#include "battle.h" #include "rpg.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 = ¤t->team->chrs[i]; if (chr->alive) return chr; } for (int i = 0; i <= current->idx; ++i) { struct chr_t *chr = ¤t->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 = ¤t->team->chrs[i]; if (chr->alive) return chr; } for (int i = current->team->chr_cnt - 1; i >= current->idx; ++i) { struct chr_t *chr = ¤t->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(¤t->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(¤t->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(¤t->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_init(struct battle_t *battle, struct team_t *t1, struct team_t *t2) { battle->t1 = t1; battle->t2 = t2; battle->background = rpg_g.surfaces.background; return 0; } void battle_close(struct battle_t *battle) { (void) battle; } int battle_run(struct battle_t *battle) { struct team_t *t1 = battle->t1; struct team_t *t2 = battle->t2; struct team_t *playing_team; SDL_BlitSurface(battle->background, NULL, rpg_g.screen, NULL); /* compute whether the allies or the enemies should begin */ playing_team = (rand() % 2) ? t1 : t2; highlight_current_character(playing_team); blit_team(t1); blit_team(t2); SDL_Flip(rpg_g.screen); while (!is_battle_over(t1, t2)) { struct action_params_t params = { .surfaces = &rpg_g.surfaces, .positions = &rpg_g.positions, .t1 = t1, .t2 = t2, .src = &playing_team->chrs[playing_team->chr_cur], }; switch (character_play_turn(¶ms)) { case ACTION_CANCELED: return -1; case ACTION_PERFORMED: SDL_Flip(rpg_g.screen); SDL_Delay(1000); SDL_BlitSurface(battle->background, &rpg_g.positions.degats, rpg_g.screen, &rpg_g.positions.degats); SDL_Flip(rpg_g.screen); hook_post_action(¶ms); update_current_character(t1, t2, &playing_team); blit_team(t1); blit_team(t2); break; default: break; } } return 0; }