# HG changeset patch # User David Demelier # Date 1610031176 -3600 # Node ID 63d9fb56c609d3498133fb76ebf554a37ceb47a0 # Parent cc0f02ae9005de2bcdb048e44a788c31d7033df9 rpg: rework selection diff -r cc0f02ae9005 -r 63d9fb56c609 doc/docs/dev/api/core/util.md --- a/doc/docs/dev/api/core/util.md Thu Jan 07 15:50:01 2021 +0100 +++ b/doc/docs/dev/api/core/util.md Thu Jan 07 15:52:56 2021 +0100 @@ -45,7 +45,7 @@ ### util_nrand -Returns a random number between `lower` and `upper` (included). +Returns a random number between `lower` and `upper` (excluded). ```c unsigned int diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/CMakeLists.txt --- a/libmlk-adventure/CMakeLists.txt Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-adventure/CMakeLists.txt Thu Jan 07 15:52:56 2021 +0100 @@ -41,6 +41,8 @@ ${libadventure_SOURCE_DIR}/adventure/mapscene/mapscene.h ${libadventure_SOURCE_DIR}/adventure/molko.c ${libadventure_SOURCE_DIR}/adventure/molko.h + ${libadventure_SOURCE_DIR}/adventure/spell/fire-minor.c + ${libadventure_SOURCE_DIR}/adventure/spell/fire-minor.h ${libadventure_SOURCE_DIR}/adventure/state/battle.c ${libadventure_SOURCE_DIR}/adventure/state/battle.h ${libadventure_SOURCE_DIR}/adventure/state/continue.c diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/adventure/action/spawner.c --- a/libmlk-adventure/adventure/action/spawner.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-adventure/adventure/action/spawner.c Thu Jan 07 15:52:56 2021 +0100 @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -51,13 +52,24 @@ struct battle *bt; bt = alloc_new0(sizeof (*bt)); - bt->enemies[0].ch = &character_black_cat; + + bt->enemies[0].ch = alloc_dup(&character_black_cat, sizeof (character_black_cat)); bt->enemies[0].x = 400; bt->enemies[0].y = 50; + bt->enemies[1].ch = alloc_dup(&character_black_cat, sizeof (character_black_cat)); + bt->enemies[1].x = 200; + bt->enemies[1].y = 100; + bt->inventory = &molko.inventory; - for (size_t i = 0; i < TEAM_MEMBER_MAX; ++i) - bt->team[i].ch = molko.team.members[i]; + for (size_t i = 0; i < TEAM_MEMBER_MAX; ++i) { + if (molko.team.members[i]) { + bt->team[i].ch = alloc_dup(molko.team.members[i], sizeof (*molko.team.members[i])); + character_reset(bt->team[i].ch); + bt->team[i].ch->hp = bt->team[i].ch->hpmax; + bt->team[i].ch->mp = bt->team[i].ch->mpmax; + } + } molko_fight(bt); } diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/adventure/assets.c --- a/libmlk-adventure/adventure/assets.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-adventure/adventure/assets.c Thu Jan 07 15:52:56 2021 +0100 @@ -44,7 +44,8 @@ SPRITE(ASSETS_SPRITE_CHARACTER_BLACK_CAT, "images/black-cat.png", 123, 161), SPRITE(ASSETS_SPRITE_CHARACTER_NETH, "sprites/john-walk.png", 256, 256), SPRITE(ASSETS_SPRITE_CHARACTER_NETH_SWORD, "sprites/john-sword.png", 256, 256), - SPRITE(ASSETS_SPRITE_FACES, "sprites/faces.png", 144, 144) + SPRITE(ASSETS_SPRITE_FACES, "sprites/faces.png", 144, 144), + SPRITE(ASSETS_SPRITE_EXPLOSION, "sprites/explosion.png", 256, 256) }; static struct { diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/adventure/assets.h --- a/libmlk-adventure/adventure/assets.h Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-adventure/adventure/assets.h Thu Jan 07 15:52:56 2021 +0100 @@ -37,6 +37,9 @@ ASSETS_SPRITE_CHARACTER_NETH_SWORD, ASSETS_SPRITE_FACES, + /* Animations. */ + ASSETS_SPRITE_EXPLOSION, + ASSETS_SPRITE_NUM }; diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/adventure/character/neth.c --- a/libmlk-adventure/adventure/character/neth.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-adventure/adventure/character/neth.c Thu Jan 07 15:52:56 2021 +0100 @@ -21,6 +21,8 @@ #include #include +#include + #include "neth.h" static void @@ -42,5 +44,8 @@ [CHARACTER_SPRITE_NORMAL] = &assets_sprites[ASSETS_SPRITE_CHARACTER_NETH], [CHARACTER_SPRITE_SWORD] = &assets_sprites[ASSETS_SPRITE_CHARACTER_NETH_SWORD] }, + .spells = { + &spell_fire_minor + }, .reset = reset }; diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/adventure/molko.c --- a/libmlk-adventure/adventure/molko.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-adventure/adventure/molko.c Thu Jan 07 15:52:56 2021 +0100 @@ -50,6 +50,9 @@ #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 + +#include "character/neth.h" + static jmp_buf panic_buf; struct molko molko; @@ -92,6 +95,9 @@ game_switch(state_splashscreen_ne(), true); #else game_switch(state_mainmenu_new(), true); + molko.team.members[0] = &character_neth; + molko.team.members[1] = &character_neth; + molko_teleport("maps/map-world.map", -1, -1); #endif } diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/adventure/spell/fire-minor.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-adventure/adventure/spell/fire-minor.c Thu Jan 07 15:52:56 2021 +0100 @@ -0,0 +1,130 @@ +/* + * fire-minor.c -- minor fire + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "fire-minor.h" + +struct rendering { + struct battle *battle; + struct battle_entity *source; + struct battle_entity *target; + struct action action; + struct animation animation; +}; + +static bool +update(struct action *act, unsigned int ticks) +{ + struct rendering *rdr = act->data; + + return animation_update(&rdr->animation, ticks); +} + +static void +draw(struct action *act) +{ + const struct rendering *rdr = act->data; + + animation_draw(&rdr->animation, rdr->target->x, rdr->target->y); +} + +static void +end(struct action *act) +{ + struct rendering *rdr = act->data; + float base; + + /* Compute damage. */ + /* TODO: move this into a general maths computation. */ + /* TODO: move min/max limits outside. */ + base = util_nrand(50, 70); + base += base * (maths_scale(rdr->source->ch->atk + rdr->source->ch->atkbonus, 0, 1000, 0, 100) / 100); + + /* Reduce damage taken. */ + base -= base * (maths_scale(rdr->target->ch->atk + rdr->target->ch->atkbonus, 0, 1000, 0, 100) / 200); + base = base < 0 ? 0 : base; + + /* TODO: add battle_damage function*/ + rdr->target->ch->hp = fmax(rdr->target->ch->hp - base, rdr->target->ch->hp); + battle_indicator_hp(rdr->battle, rdr->target->ch, base); +} + +static void +finish(struct action *act) +{ + free(act->data); +} + +static void +select(const struct battle *bt, struct selection *slt) +{ + slt->index_side = 0; + + selection_first(slt, bt); +} + +static void +action(struct battle *bt, struct character *owner, const struct selection *slt) +{ + struct rendering *rdr; + + /* Action. */ + rdr = alloc_new0(sizeof (*rdr)); + rdr->action.data = rdr; + rdr->action.update = update; + rdr->action.draw = draw; + rdr->action.end = end; + rdr->action.finish = finish; + + /* Battle and target. */ + rdr->battle = bt; + rdr->source = battle_find(bt, owner); + rdr->target = &bt->enemies[slt->index_character]; + + /* Animation. */ + rdr->animation.delay = 10; + rdr->animation.sprite = &assets_sprites[ASSETS_SPRITE_EXPLOSION]; + animation_start(&rdr->animation); + + action_stack_add(&bt->actions[0], &rdr->action); +} + +const struct spell spell_fire_minor = { + .name = N_("Fire Minor"), + .description = N_("A small amount of fire balls"), + .mp = 10, + .type = SPELL_TYPE_FIRE, + .select_kind = SELECTION_KIND_ONE, + .select_side = SELECTION_SIDE_ENEMY, + .select = select, + .action = action +}; diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-adventure/adventure/spell/fire-minor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-adventure/adventure/spell/fire-minor.h Thu Jan 07 15:52:56 2021 +0100 @@ -0,0 +1,24 @@ +/* + * fire-minor.h -- minor fire + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MOLKO_ADVENTURE_FIRE_MINOR_H +#define MOLKO_ADVENTURE_FIRE_MINOR_H + +extern const struct spell spell_fire_minor; + +#endif /* !MOLKO_ADVENTURE_FIRE_MINOR_H */ diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-data/maps/map-world.json --- a/libmlk-data/maps/map-world.json Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-data/maps/map-world.json Thu Jan 07 15:52:56 2021 +0100 @@ -63,7 +63,7 @@ { "name":"exec", "type":"string", - "value":"spawner|800|1000" + "value":"spawner|1|2" }], "rotation":0, "type":"", diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/CMakeLists.txt --- a/libmlk-rpg/CMakeLists.txt Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/CMakeLists.txt Thu Jan 07 15:52:56 2021 +0100 @@ -75,6 +75,7 @@ ${libmlk-rpg_SOURCE_DIR}/rpg/rpg_p.h ${libmlk-rpg_SOURCE_DIR}/rpg/save.c ${libmlk-rpg_SOURCE_DIR}/rpg/save.h + ${libmlk-rpg_SOURCE_DIR}/rpg/selection.c ${libmlk-rpg_SOURCE_DIR}/rpg/selection.h ${libmlk-rpg_SOURCE_DIR}/rpg/spell.c ${libmlk-rpg_SOURCE_DIR}/rpg/spell.h diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/battle-state-check.c --- a/libmlk-rpg/rpg/battle-state-check.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/battle-state-check.c Thu Jan 07 15:52:56 2021 +0100 @@ -53,7 +53,6 @@ return true; fade->alpha -= 10; - texture_set_alpha_mod(ch->sprites[CHARACTER_SPRITE_NORMAL]->texture, fade->alpha); } return false; @@ -63,8 +62,11 @@ fadeout_draw(struct action *act) { const struct fadeout *fade = act->data; + struct sprite *sprite = fade->ch->sprites[CHARACTER_SPRITE_NORMAL]; - sprite_draw(fade->ch->sprites[CHARACTER_SPRITE_NORMAL], 0, 0, fade->x, fade->y); + texture_set_alpha_mod(sprite->texture, fade->alpha); + sprite_draw(sprite, 0, 0, fade->x, fade->y); + texture_set_alpha_mod(sprite->texture, 255); } static void diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/battle-state-menu.c --- a/libmlk-rpg/rpg/battle-state-menu.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/battle-state-menu.c Thu Jan 07 15:52:56 2021 +0100 @@ -35,17 +35,12 @@ static void open_attack(struct battle *bt) { - unsigned int selection = 0; + struct selection slt = { + .allowed_sides = SELECTION_SIDE_ENEMY + }; - /* Find first enemy to attack. */ - for (size_t i = 0; i < BATTLE_ENEMY_MAX; ++i) { - if (character_ok(bt->enemies[i].ch)) { - selection = i; - break; - } - } - - battle_state_selection(bt, SELECTION_ENEMY_ONE, selection); + selection_first(&slt, bt); + battle_state_selection(bt, &slt); } static void diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/battle-state-selection.c --- a/libmlk-rpg/rpg/battle-state-selection.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/battle-state-selection.c Thu Jan 07 15:52:56 2021 +0100 @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -35,28 +36,49 @@ struct select { struct battle_state state; - enum selection type; - unsigned int selection; + struct selection slt; }; static void -select_adj_in(struct select *select, const struct battle_entity entities[], size_t entitiesz, int step) +attack(struct select *select, struct battle *bt) { - assert(select->selection != (unsigned int)-1); + struct character *target; + + if (select->slt.index_side == 0) + target = bt->enemies[select->slt.index_character].ch; + else + target = bt->team[select->slt.index_character].ch; + + battle_attack(bt, bt->order_cur->ch, target); +} - unsigned int newselection = select->selection; +static void +cast(struct select *select, struct battle *bt) +{ + struct character *source = bt->order_cur->ch; + const struct spell *spell = source->spells[bt->bar.sub_grid.selected]; + + battle_cast(bt, source, spell, &select->slt); +} + +static void +select_adj_in(struct select *select, const struct battle_entity *entities, size_t entitiesz, int step) +{ + assert(select->slt.index_character != (unsigned int)-1); + + unsigned int newselection = select->slt.index_character; if (step < 0) { while (newselection > 0) { if (character_ok(entities[--newselection].ch)) { - select->selection = newselection; + select->slt.index_character = newselection; break; } } } else { while (newselection < entitiesz) { if (character_ok(entities[++newselection].ch)) { - select->selection = newselection; + select->slt.index_character = newselection; break; } } @@ -66,17 +88,10 @@ static void select_adj(struct select *select, const struct battle *bt, int step) { - switch (select->type) { - case SELECTION_TEAM_ONE: - case SELECTION_TEAM_COMBINED: + if (select->slt.index_side == 0) + select_adj_in(select, bt->enemies, UTIL_SIZE(bt->enemies), step); + else select_adj_in(select, bt->team, UTIL_SIZE(bt->team), step); - break; - case SELECTION_ENEMY_ONE: - case SELECTION_ENEMY_COMBINED: - select_adj_in(select, bt->enemies, UTIL_SIZE(bt->enemies), step); - default: - break; - } } static void @@ -85,33 +100,49 @@ assert(ev->type == EVENT_KEYDOWN); struct select *select = st->data; - struct character *source = bt->order_cur->ch; - const struct spell *sp = source->spells[bt->bar.sub_grid.selected]; switch (ev->key.key) { case KEY_ESCAPE: - battle_state_sub(bt); + switch (bt->bar.menu) { + case BATTLE_BAR_MENU_MAGIC: + case BATTLE_BAR_MENU_OBJECTS: + battle_state_sub(bt); + break; + default: + battle_state_menu(bt); + break; + } break; case KEY_ENTER: switch (bt->bar.menu) { case BATTLE_BAR_MENU_ATTACK: - battle_attack(bt, source, bt->enemies[select->selection].ch); + attack(select, bt); break; case BATTLE_BAR_MENU_MAGIC: - battle_cast(bt, source, sp, select->selection); + cast(select, bt); break; default: break; } break; case KEY_LEFT: + if (select->slt.allowed_sides & SELECTION_SIDE_ENEMY) + select->slt.index_side = 0; + break; + case KEY_RIGHT: + if (select->slt.allowed_sides & SELECTION_SIDE_TEAM) + select->slt.index_side = 1; + break; case KEY_UP: select_adj(select, bt, -1); break; - case KEY_RIGHT: case KEY_DOWN: select_adj(select, bt, +1); break; + case KEY_TAB: + if (select->slt.allowed_kinds == SELECTION_KIND_BOTH) + select->slt.index_character = -1; + break; default: break; } @@ -138,21 +169,16 @@ static void draw_cursors(const struct battle_state *st, - const struct battle *bt, - const struct battle_entity entities[], - size_t entitiesz) + const struct battle *bt, + const struct battle_entity *entities, + size_t entitiesz) { - const struct select *select = st->data; + for (size_t i = 0; i < entitiesz; ++i) { + const struct battle_entity *et = &entities[i]; - if (select->selection == (unsigned int)-1) { - for (size_t i = 0; i < entitiesz; ++i) { - const struct battle_entity *et = &entities[i]; - - if (character_ok(et->ch)) - draw_cursor(bt, et); - } - } else - draw_cursor(bt, &entities[select->selection]); + if (character_ok(et->ch)) + draw_cursor(bt, et); + } } static void @@ -174,24 +200,19 @@ { const struct select *select = st->data; - switch (select->type) { - case SELECTION_SELF: - draw_cursor(bt, bt->order_cur); - break; - case SELECTION_ENEMY_ALL: - case SELECTION_ENEMY_ONE: - case SELECTION_ENEMY_COMBINED: - draw_cursors(st, bt, bt->enemies, UTIL_SIZE(bt->enemies)); - break; - case SELECTION_TEAM_ALL: - case SELECTION_TEAM_ONE: - case SELECTION_TEAM_COMBINED: - draw_cursors(st, bt, bt->team, UTIL_SIZE(bt->team)); - break; - default: - break; + if (select->slt.index_character == -1) { + /* All selected. */ + if (select->slt.index_side == 0) + draw_cursors(st, bt, bt->enemies, UTIL_SIZE(bt->enemies)); + else + draw_cursors(st, bt, bt->team, UTIL_SIZE(bt->team)); + } else { + /* Select one. */ + if (select->slt.index_side == 0) + draw_cursor(bt, &bt->enemies[select->slt.index_character]); + else + draw_cursor(bt, &bt->team[select->slt.index_character]); } - } static void @@ -203,9 +224,7 @@ } void -battle_state_selection(struct battle *bt, - enum selection type, - unsigned int selection) +battle_state_selection(struct battle *bt, const struct selection *slt) { assert(bt); @@ -214,12 +233,11 @@ if (!(select = alloc_new0(sizeof (*select)))) panic(); - select->type = type; - select->selection = selection; select->state.data = select; select->state.handle = handle; select->state.draw = draw; select->state.finish = finish; + memcpy(&select->slt, slt, sizeof (*slt)); battle_switch(bt, &select->state); } diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/battle-state-sub.c --- a/libmlk-rpg/rpg/battle-state-sub.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/battle-state-sub.c Thu Jan 07 15:52:56 2021 +0100 @@ -36,46 +36,18 @@ { const struct character *ch = bt->order_cur->ch; const struct spell *sp = ch->spells[bt->bar.sub_grid.selected]; - unsigned int selection = 0; + struct selection slt = {0}; /* Don't forget to reset the gridmenu state. */ gridmenu_reset(&bt->bar.sub_grid); - if (!sp || sp->mp > (unsigned int)(ch->mp)) + if (bt->bar.sub_grid.selected > CHARACTER_SPELL_MAX) + return; + if (!(sp = ch->spells[bt->bar.sub_grid.selected]) || sp->mp > (unsigned int)(ch->mp)) return; - /* - * When starting the selection state we need to initialize the first - * selection depending on the spell selection type. For example, if the - * spell require to select exactly one enemy, we need to find the first - * one in the battle that is not NULL. - */ - switch (sp->selection) { - case SELECTION_SELF: - case SELECTION_TEAM_COMBINED: - case SELECTION_TEAM_ONE: - selection = bt->order_cur - bt->team; - break; - case SELECTION_TEAM_ALL: - case SELECTION_ENEMY_ALL: - selection = -1; - break; - case SELECTION_ENEMY_COMBINED: - case SELECTION_ENEMY_ONE: - /* Find first available. */ - for (size_t i = 0; i < BATTLE_ENEMY_MAX; ++i) { - if (character_ok(bt->enemies[i].ch)) { - selection = i; - break; - } - } - break; - default: - selection = 0; - break; - } - - battle_state_selection(bt, sp->selection, selection); + spell_select(sp, bt, &slt); + battle_state_selection(bt, &slt); /* A cursor should be present. */ if (!sprite_ok(BATTLE_THEME(bt)->sprites[THEME_SPRITE_CURSOR])) diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/battle-state.h --- a/libmlk-rpg/rpg/battle-state.h Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/battle-state.h Thu Jan 07 15:52:56 2021 +0100 @@ -86,9 +86,7 @@ battle_state_opening(struct battle *bt); void -battle_state_selection(struct battle *bt, - enum selection type, - unsigned int selection); +battle_state_selection(struct battle *bt, const struct selection *slt); void battle_state_sub(struct battle *bt); diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/battle.c --- a/libmlk-rpg/rpg/battle.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/battle.c Thu Jan 07 15:52:56 2021 +0100 @@ -290,7 +290,7 @@ battle_cast(struct battle *bt, struct character *source, const struct spell *spell, - unsigned int selection) + const struct selection *selection) { assert(bt); assert(source); @@ -314,12 +314,18 @@ battle_order(bt); bt->order_cur = bt->order[bt->order_curindex = 0]; } else { - /* End of turn. */ - if (++bt->order_curindex >= BATTLE_ENTITY_MAX || !bt->order[bt->order_curindex]) { + for (++bt->order_curindex; bt->order_curindex < BATTLE_ENTITY_MAX; ++bt->order_curindex) { + if (battle_entity_ok(bt->order[bt->order_curindex])) { + bt->order_cur = bt->order[bt->order_curindex]; + break; + } + } + + /* End of "turn". */ + if (bt->order_curindex >= BATTLE_ENTITY_MAX) { battle_order(bt); bt->order_cur = bt->order[bt->order_curindex = 0]; - } else - bt->order_cur = bt->order[bt->order_curindex]; + } } /* Change state depending on the kind of entity. */ diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/battle.h --- a/libmlk-rpg/rpg/battle.h Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/battle.h Thu Jan 07 15:52:56 2021 +0100 @@ -38,6 +38,7 @@ struct character; struct inventory; struct music; +struct selection; struct spell; struct theme; @@ -100,7 +101,7 @@ battle_cast(struct battle *bt, struct character *source, const struct spell *spell, - unsigned int selection); + const struct selection *slt); void battle_indicator_hp(struct battle *bt, const struct character *target, unsigned int amount); diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/selection.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/rpg/selection.c Thu Jan 07 15:52:56 2021 +0100 @@ -0,0 +1,84 @@ +/* + * selection.c -- kind of selection + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "battle.h" +#include "character.h" +#include "selection.h" + +static void +random(struct selection *slt, const struct battle *bt, const struct battle_entity *entities, size_t entitiesz) +{ + struct { + const struct battle_entity *entity; + size_t position; + } table[BATTLE_ENTITY_MAX] = {0}; + + size_t tablesz = 0; + + /* + * Merge the list of valid entities into the table to select a random + * one. + */ + for (size_t i = 0; i < entitiesz; ++i) { + if (battle_entity_ok(&entities[i])) { + table[tablesz].entity = &entities[i]; + table[tablesz++].position = i; + } + } + + slt->index_character = table[util_nrand(0, tablesz)].position; +} + +static void +first(struct selection *slt, const struct battle *bt, const struct battle_entity *entities, size_t entitiesz) +{ + for (size_t i = 0; i < entitiesz; ++i) { + if (battle_entity_ok(&entities[i])) { + slt->index_character = i; + break; + } + } +} + +void +selection_first(struct selection *slt, const struct battle *bt) +{ + assert(slt); + assert(bt); + + if (slt->index_side == 0) + first(slt, bt, bt->enemies, BATTLE_ENEMY_MAX); + else + first(slt, bt, bt->team, BATTLE_TEAM_MAX); +} + +void +selection_random(struct selection *slt, const struct battle *bt) +{ + assert(slt); + assert(bt); + + if (slt->index_side == 0) + random(slt, bt, bt->enemies, BATTLE_ENEMY_MAX); + else + random(slt, bt, bt->team, BATTLE_TEAM_MAX); +} diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/selection.h --- a/libmlk-rpg/rpg/selection.h Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/selection.h Thu Jan 07 15:52:56 2021 +0100 @@ -19,17 +19,36 @@ #ifndef MOLKO_RPG_SELECTION_H #define MOLKO_RPG_SELECTION_H -/** - * \brief Kind of selection. - */ -enum selection { - SELECTION_SELF, /*!< Owner only. */ - SELECTION_TEAM_ONE, /*!< One member of the team. */ - SELECTION_TEAM_ALL, /*!< All members of the team. */ - SELECTION_TEAM_COMBINED, /*!< One or all members of the team. */ - SELECTION_ENEMY_ONE, /*!< One enemy. */ - SELECTION_ENEMY_ALL, /*!< All enemies. */ - SELECTION_ENEMY_COMBINED /*!< One or all enemies. */ +struct battle; + +enum selection_kind { + SELECTION_KIND_SELF, + SELECTION_KIND_ONE, + SELECTION_KIND_ALL, + SELECTION_KIND_BOTH +}; + +enum selection_side { + /* Which side allowed (can be both). */ + SELECTION_SIDE_TEAM = (1 << 0), + SELECTION_SIDE_ENEMY = (1 << 1) }; +struct selection { + enum selection_kind allowed_kinds; + enum selection_side allowed_sides; + + /* Character index in battle entity array. */ + unsigned int index_character; + + /* Side index (0 = enemy, 1 = team). */ + unsigned int index_side; +}; + +void +selection_first(struct selection *, const struct battle *); + +void +selection_random(struct selection *, const struct battle *); + #endif /* !MOLKO_RPG_SELECTION_H */ diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/spell.c --- a/libmlk-rpg/rpg/spell.c Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/spell.c Thu Jan 07 15:52:56 2021 +0100 @@ -21,21 +21,32 @@ #include "spell.h" void -spell_action(const struct spell *s, struct battle *bt, struct character *owner, unsigned int selection) +spell_select(const struct spell *s, const struct battle *bt, struct selection *slt) +{ + assert(s && s->select); + assert(bt); + assert(slt); + + s->select(bt, slt); +} + +void +spell_action(const struct spell *s, struct battle *bt, struct character *owner, const struct selection *slt) { assert(s && s->action); assert(bt); assert(owner); + assert(slt); - s->action(bt, owner, selection); + s->action(bt, owner, slt); } void -spell_use(struct spell *s, struct character *owner, int selection) +spell_use(struct spell *s, struct character *owner, const struct selection *slt) { assert(s && s->use); assert(owner); + assert(slt); - if (s->use) - s->use(owner, selection); + s->use(owner, slt); } diff -r cc0f02ae9005 -r 63d9fb56c609 libmlk-rpg/rpg/spell.h --- a/libmlk-rpg/rpg/spell.h Thu Jan 07 15:50:01 2021 +0100 +++ b/libmlk-rpg/rpg/spell.h Thu Jan 07 15:52:56 2021 +0100 @@ -28,81 +28,39 @@ struct character; struct battle; +struct selection; -/** - * \brief Kind of spell. - */ enum spell_type { - SPELL_TYPE_NEUTRAL, /*!< No type. */ - SPELL_TYPE_FIRE, /*!< Fire (affected by attack). */ - SPELL_TYPE_WIND, /*!< Wind (affected by agility). */ - SPELL_TYPE_WATER, /*!< Water (affected by luck). */ - SPELL_TYPE_EARTH, /*!< Earth (affected by defense). */ - SPELL_TYPE_CHAOS, /*!< Chaotic. */ - SPELL_TYPE_HOLY, /*!< Holy. */ - SPELL_TYPE_TIME /*!< Chrono. */ + SPELL_TYPE_NEUTRAL, + SPELL_TYPE_FIRE, + SPELL_TYPE_WIND, + SPELL_TYPE_WATER, + SPELL_TYPE_EARTH, + SPELL_TYPE_CHAOS, + SPELL_TYPE_HOLY, + SPELL_TYPE_TIME }; -/** - * \brief Spell structure. - * - * A spell is a magical object owned by a character and can be used in a battle - * and/or outside of a battle. It costs a certain amount of magic points and is - * typed into a category (earth, fire, etc, …). - * - * A spell can select one character or all. - */ struct spell { - const char *name; /*!< (+&) Spell name. */ - const char *description; /*!< (+&) Long description. */ - unsigned int mp; /*!< (+) Number of MP required. */ - enum spell_type type; /*!< (+) Kind of spell. */ - enum selection selection; /*!< (+) Kind of selection. */ + const char *name; + const char *description; + unsigned int mp; + enum spell_type type; + enum selection_kind select_kind; + enum selection_side select_side; - /** - * (+) Execute the spell in a battle. - * - * \param bt the current battle - * \param owner the spell owner - * \param selection the selection (-1 == all) - */ - void (*action)(struct battle *bt, struct character *owner, unsigned int selection); - - /** - * (+) Use the spell outside of a battle. - * - * This function is optional. - * - * \param owner the spell owner - * \param selection the selection flags - */ - void (*use)(struct character *owner, int selection); + void (*select)(const struct battle *, struct selection *); + void (*action)(struct battle *, struct character *, const struct selection *); + void (*use)(struct character *, const struct selection *); }; -/** - * Cast this spell within a battle. - * - * \pre s != NULL && s->action - * \pre bt != NULL - * \pre owner != NULL - * \param s the spell - * \param bt the battle - * \param owner the owner - * \param selection the selection (index or -1 for all) - */ +void +spell_select(const struct spell *s, const struct battle *bt, struct selection *slt); + void -spell_action(const struct spell *s, struct battle *bt, struct character *owner, unsigned int selection); +spell_action(const struct spell *s, struct battle *bt, struct character *owner, const struct selection *slt); -/** - * Use this spell immediately. - * - * \pre s != NULL && s->use - * \pre owner != NULL - * \param s the spell - * \param owner the owner - * \param selection the selection flags - */ void -spell_use(struct spell *s, struct character *owner, int selection); +spell_use(struct spell *s, struct character *owner, const struct selection *slt); #endif /* !MOLKO_RPG_SPELL_H */