summaryrefslogtreecommitdiff
path: root/battle.c
diff options
context:
space:
mode:
Diffstat (limited to 'battle.c')
-rw-r--r--battle.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/battle.c b/battle.c
new file mode 100644
index 0000000..e18ebb1
--- /dev/null
+++ b/battle.c
@@ -0,0 +1,517 @@
+#include <SDL/SDL.h>
+#include "structures.h"
+#include "constantes.h"
+
+#include "ai.h"
+#include "blits.h"
+
+#include "character.h"
+
+#include "priv_entries.h"
+
+static inline void highlight_current_character(struct team_t *team)
+{
+ struct chr_t *chr = &team->chrs[team->chr_cur];
+
+ chr->surf = chr->red_surf;
+}
+
+static inline void unhighlight_prev_character(struct team_t *team)
+{
+ struct chr_t *chr = &team->chrs[team->chr_cur];
+
+ chr->surf = chr->def_surf;
+}
+
+static struct chr_t *find_next_team_member(const struct chr_t *current)
+{
+ for (int i = current->idx + 1; i < current->team->chr_cnt; ++i) {
+ struct chr_t *chr = &current->team->chrs[i];
+
+ if (chr->alive)
+ return chr;
+ }
+
+ for (int i = 0; i <= current->idx; ++i) {
+ struct chr_t *chr = &current->team->chrs[i];
+
+ if (chr->alive)
+ return chr;
+ }
+
+ return NULL;
+}
+
+static struct chr_t *find_prev_team_member(const struct chr_t *current)
+{
+ for (int i = current->idx - 1; i >= 0; --i) {
+ struct chr_t *chr = &current->team->chrs[i];
+
+ if (chr->alive)
+ return chr;
+ }
+
+ for (int i = current->team->chr_cnt - 1; i >= current->idx; ++i) {
+ struct chr_t *chr = &current->team->chrs[i];
+
+ if (chr->alive)
+ return chr;
+ }
+
+ return NULL;
+}
+
+static struct chr_t *get_first_alive_character(const struct team_t *team)
+{
+ for (int i = 0; i < team->chr_cnt; ++i) {
+ if (team->chrs[i].alive)
+ return &team->chrs[i];
+ }
+
+ return NULL;
+}
+
+/* function called after an action has been performed */
+static void update_current_character(struct team_t *t1, struct team_t *t2,
+ struct team_t **playing)
+{
+ struct team_t *current;
+ struct chr_t *next;
+
+ unhighlight_prev_character(*playing);
+
+ current = *playing;
+
+ next = find_next_team_member(&current->chrs[current->chr_cur]);
+
+ if (next == NULL) {
+ /* if our team is dead */
+ current = *playing = (current == t1) ? t2 : t1;
+
+ if (!current->chrs[current->chr_cur].alive) {
+ next = find_next_team_member(&current->chrs[current->chr_cur]);
+
+ /* if both team are dead */
+ if (next == NULL)
+ return;
+
+ current->chr_cur = next->idx;
+ }
+ } else if (next->idx > current->chr_cur) {
+ /* we still have some players to use */
+ current->chr_cur = next->idx;
+ } else {
+ current->chr_cur = next->idx;
+
+ current = *playing = (current == t1) ? t2 : t1;
+
+ if (!current->chrs[current->chr_cur].alive) {
+ next = find_next_team_member(&current->chrs[current->chr_cur]);
+
+ if (next == NULL) {
+ current = *playing = (current == t1) ? t2 : t1;
+ } else {
+ current->chr_cur = next->idx;
+ }
+ }
+ }
+
+ highlight_current_character(*playing);
+}
+
+/* TODO the code of this function should be split in different functions */
+ static struct target_t
+select_default_target(const struct action_params_t *params,
+ enum target_type_t type)
+{
+ struct team_t *enemy_team;
+ struct target_t target;
+
+ if (type & (TARGET_SELF | TARGET_SINGLE_ALLY)) {
+ target.is_chr = true;
+ target.chr = params->src;
+
+ return target;
+ }
+
+ enemy_team = (params->src->team == params->t1) ? params->t2 : params->t1;
+
+ if (type & TARGET_SINGLE_ENEMY) {
+ target.chr = get_first_alive_character(enemy_team);
+
+ if (target.chr != NULL) {
+ target.is_chr = true;
+ return target;
+ }
+ }
+
+ if (type & TARGET_TEAM_ALLY) {
+ target.is_chr = false;
+ target.team = params->src->team;
+ return target;
+ }
+
+ if (type & TARGET_TEAM_ENEMY) {
+ target.is_chr = false;
+ target.team = enemy_team;
+ return target;
+ }
+
+ abort();
+}
+
+enum selection_change_t {
+ SELECTION_CHANGE_LEFT = 0x1,
+ SELECTION_CHANGE_RIGHT = SELECTION_CHANGE_LEFT << 0x1,
+
+ SELECTION_CHANGE_HORIZ = SELECTION_CHANGE_LEFT | SELECTION_CHANGE_RIGHT,
+
+ SELECTION_CHANGE_UP = SELECTION_CHANGE_RIGHT << 0x1,
+ SELECTION_CHANGE_DOWN = SELECTION_CHANGE_UP << 0x1,
+
+ SELECTION_CHANGE_VERT = SELECTION_CHANGE_UP | SELECTION_CHANGE_DOWN,
+
+ SELECTION_CHANGE_MODE = SELECTION_CHANGE_DOWN << 0x1,
+};
+
+static int
+change_selection_vert(bool up, struct target_t *target)
+{
+ struct chr_t *(*find)(const struct chr_t *);
+ struct chr_t *new_selection;
+
+ /* UP or DOWN when selecting a team does nothing */
+ if (!target->is_chr)
+ return -1;
+
+ find = (up) ? find_prev_team_member : find_next_team_member;
+
+ new_selection = (*find)(target->chr);
+
+ if (new_selection->idx == target->chr->idx)
+ return -1;
+
+ target->chr = new_selection;
+
+ return 0;
+}
+
+static int change_selection_horiz(const struct action_params_t *params,
+ enum target_type_t type, struct target_t *target)
+{
+ enum target_type_t filter_ally, filter_enemy;
+ struct team_t *team;
+
+ if (target->is_chr) {
+ filter_ally = TARGET_SINGLE_ALLY;
+ filter_enemy = TARGET_SINGLE_ENEMY;
+
+ team = (target->chr->team == params->t1) ? params->t2 : params->t1;
+ } else {
+ filter_ally = TARGET_TEAM_ALLY;
+ filter_enemy = TARGET_TEAM_ENEMY;
+
+ team = (target->team == params->t1) ? params->t2 : params->t1;
+ }
+
+ if (params->src->team == team && !(type & filter_ally))
+ return -1;
+
+ if (params->src->team != team && !(type & filter_enemy))
+ return -1;
+
+ if (target->is_chr) {
+ struct chr_t *new_chr;
+
+ new_chr = get_first_alive_character(team);
+
+ if (new_chr == NULL)
+ return -1;
+
+ target->chr = new_chr;
+ } else {
+ target->team = team;
+ }
+ return 0;
+}
+
+static int change_selection_mode(const struct action_params_t *params,
+ int type, struct target_t *target)
+{
+ enum target_type_t filter_ally, filter_enemy;
+ struct team_t *team;
+
+ if (target->is_chr) {
+ filter_ally = TARGET_TEAM_ALLY;
+ filter_enemy = TARGET_TEAM_ENEMY;
+
+ team = target->chr->team;
+ } else {
+ filter_ally = TARGET_SINGLE_ALLY | TARGET_SELF;
+ filter_enemy = TARGET_SINGLE_ENEMY;
+
+ team = target->team;
+ }
+
+ if (team == params->src->team && !(type & filter_ally))
+ return -1;
+
+ if (team != params->src->team && !(type & filter_enemy))
+ return -1;
+
+ if (target->is_chr) {
+ target->is_chr = false;
+ target->team = team;
+ } else {
+ target->is_chr = true;
+ target->chr = (params->src->team == team) ?
+ params->src : get_first_alive_character(team);
+ }
+
+ return 0;
+}
+
+static int change_selection(const struct action_params_t *params,
+ enum target_type_t type, enum selection_change_t change,
+ struct target_t *target)
+{
+ int ret;
+
+ /* the target is only `self' so cannot move */
+ if (type == TARGET_SELF)
+ return -1;
+
+ if (change & SELECTION_CHANGE_VERT) {
+ ret = change_selection_vert(change & SELECTION_CHANGE_UP, target);
+ } else if (change & SELECTION_CHANGE_HORIZ) {
+ ret = change_selection_horiz(params, type, target);
+ } else if (change == SELECTION_CHANGE_MODE) {
+ ret = change_selection_mode(params, type, target);
+ } else {
+ abort();
+ }
+
+ if (ret < 0)
+ return -1;
+
+ update_selected_target(params->surfaces, params->positions, target);
+ SDL_Flip(params->surfaces->screen);
+
+ return 0;
+}
+
+static enum action_state_t
+select_target(struct action_params_t *params, const struct action_t *action)
+{
+ SURFACES *surfaces = params->surfaces;
+ POSITIONS *positions = params->positions;
+ /* select our own character because he exists no matter what */
+ struct target_t target = select_default_target(params, action->target);
+ SDL_Event event;
+
+ update_selected_target(surfaces, positions, &target);
+ SDL_Flip(surfaces->Pecran);
+
+ while (SDL_PollEvent(&event));
+ for (;;) {
+ SDL_WaitEvent(&event);
+
+ if (event.type != SDL_KEYDOWN)
+ continue;
+
+ switch (event.key.keysym.sym) {
+ case SDLK_ESCAPE:
+ case SDLK_a:
+ update_selected_target(surfaces, positions, NULL);
+ SDL_Flip(surfaces->Pecran);
+ return ACTION_CANCELED;
+ case SDLK_TAB:
+ case SDLK_d:
+ change_selection(params, action->target,
+ SELECTION_CHANGE_MODE, &target);
+ break;
+ case SDLK_UP:
+ case SDLK_k:
+ change_selection(params, action->target, SELECTION_CHANGE_UP,
+ &target);
+ break;
+ case SDLK_DOWN:
+ case SDLK_j:
+ change_selection(params, action->target,
+ SELECTION_CHANGE_DOWN, &target);
+ break;
+ case SDLK_LEFT:
+ case SDLK_h:
+ case SDLK_RIGHT:
+ case SDLK_l:
+ change_selection(params, action->target,
+ SELECTION_CHANGE_HORIZ, &target);
+ break;
+ case SDLK_RETURN:
+ case SDLK_f:
+ update_selected_target(surfaces, positions, NULL);
+
+ (*action->f)(surfaces, positions, params->src, &target,
+ action->data);
+
+ return ACTION_PERFORMED;
+ default:
+ break;
+ }
+ }
+}
+
+static enum action_state_t dig_entry(const struct entry_t *entries,
+ int cnt_entries, struct action_params_t *params)
+{
+ SDL_Event event;
+ const struct entry_t *target;
+ int selection = 0;
+
+ update_list_entries(params->surfaces, params->positions, entries,
+ cnt_entries, selection);
+
+ while (SDL_PollEvent(&event));
+ for (;;) {
+ SDL_WaitEvent(&event);
+
+ if (event.type != SDL_KEYDOWN)
+ continue;
+
+ switch (event.key.keysym.sym) {
+ case SDLK_a:
+ case SDLK_ESCAPE:
+ return ACTION_CANCELED;
+ case SDLK_k:
+ case SDLK_UP:
+ selection = (selection > 0) ? selection - 1 : cnt_entries - 1;
+ update_list_entries(params->surfaces, params->positions,
+ entries, cnt_entries, selection);
+ break;
+ case SDLK_j:
+ case SDLK_DOWN:
+ selection = (selection < cnt_entries - 1) ? selection + 1 : 0;
+ update_list_entries(params->surfaces, params->positions,
+ entries, cnt_entries, selection);
+ break;
+ case SDLK_f:
+ case SDLK_RETURN:
+ target = &entries[selection];
+ if (!target->children_cnt) {
+ enum action_state_t state;
+
+ update_list_entries(params->surfaces, params->positions,
+ entries, cnt_entries, -1);
+
+ state = select_target(params, &target->action);
+
+ if (state == ACTION_PERFORMED)
+ return ACTION_PERFORMED;
+ } else {
+ if (dig_entry(target->children, target->children_cnt,
+ params) == ACTION_PERFORMED)
+ {
+ return ACTION_PERFORMED;
+ }
+ }
+
+ update_list_entries(params->surfaces, params->positions,
+ entries, cnt_entries, selection);
+ default:
+ break;
+ }
+ }
+}
+
+static enum action_state_t
+character_play_turn(struct action_params_t *params)
+{
+ params->src->defensive = false;
+
+ if (!params->src->team->cpu) {
+ return dig_entry(action_entries_g, countof(action_entries_g), params);
+ }
+
+ ai_play_turn(params);
+
+ return ACTION_PERFORMED;
+}
+
+static void hook_post_action(struct action_params_t *params)
+{
+ struct chr_t *chr = params->src;
+
+ if (chr->alive && chr->poisoned) {
+ int damages = chr->max_hp / 4;
+
+ damage_target_hp(params->surfaces, params->positions, chr, damages);
+
+ SDL_Flip(params->surfaces->screen);
+
+ SDL_Delay(1000);
+ }
+}
+
+static bool is_battle_over(const struct team_t *t1, const struct team_t *t2)
+{
+ if (get_first_alive_character(t1) == NULL) {
+ return true;
+ }
+
+ return get_first_alive_character(t2) == NULL;
+}
+
+int battle(SURFACES *surfaces, POSITIONS *positions,
+ struct team_t *t1, struct team_t *t2)
+{
+ struct team_t *playing_team;
+
+ SDL_BlitSurface(surfaces->background, NULL, surfaces->screen, NULL);
+
+ /* compute whether the allies or the enemies should begin */
+ playing_team = (rand() % 2) ? t1 : t2;
+
+ highlight_current_character(playing_team);
+
+ blit_team(surfaces, t1);
+ blit_team(surfaces, t2);
+
+ SDL_Flip(surfaces->screen);
+
+ while (!is_battle_over(t1, t2)) {
+ struct action_params_t params = {
+ .surfaces = surfaces,
+ .positions = positions,
+
+ .t1 = t1,
+ .t2 = t2,
+
+ .src = &playing_team->chrs[playing_team->chr_cur],
+ };
+
+ switch (character_play_turn(&params)) {
+ case ACTION_CANCELED:
+ return -1;
+ case ACTION_PERFORMED:
+
+ SDL_Flip(surfaces->screen);
+
+ SDL_Delay(1000);
+
+ SDL_BlitSurface(surfaces->background, &positions->degats,
+ surfaces->screen, &positions->degats);
+
+ SDL_Flip(surfaces->screen);
+
+ hook_post_action(&params);
+
+ update_current_character(t1, t2, &playing_team);
+ blit_team(surfaces, t1);
+ blit_team(surfaces, t2);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}