diff options
Diffstat (limited to 'battle.c')
-rw-r--r-- | battle.c | 517 |
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 = ¤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(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(¶ms)) { + 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(¶ms); + + update_current_character(t1, t2, &playing_team); + blit_team(surfaces, t1); + blit_team(surfaces, t2); + break; + default: + break; + } + } + return 0; +} |