diff --git a/src/battle.c b/src/battle.c index 19f30e59c..5075b247b 100644 --- a/src/battle.c +++ b/src/battle.c @@ -439,7 +439,7 @@ static int get_row(const side * s, int row, const side * vs) return result; } -static int get_unitrow(const fighter * af, const side * vs) +int get_unitrow(const fighter * af, const side * vs) { int row = statusrow(af->status); if (vs == NULL) { @@ -1589,8 +1589,7 @@ static troop select_opponent(battle * b, troop at, int mindist, int maxdist) return dt; } -selist *fighters(battle * b, const side * vs, int minrow, int maxrow, - int mask) +selist *select_fighters(battle * b, const side * vs, int mask, select_fun cb, void *cbdata) { side *s; selist *fightervp = 0; @@ -1613,8 +1612,7 @@ selist *fighters(battle * b, const side * vs, int minrow, int maxrow, assert(mask == (FS_HELP | FS_ENEMY) || !"invalid alliance state"); } for (fig = s->fighters; fig; fig = fig->next) { - int row = get_unitrow(fig, vs); - if (row >= minrow && row <= maxrow) { + if (cb(vs, fig, cbdata)) { selist_push(&fightervp, fig); } } @@ -1623,6 +1621,26 @@ selist *fighters(battle * b, const side * vs, int minrow, int maxrow, return fightervp; } +struct selector { + int minrow; + int maxrow; +}; + +static bool select_row(const side *vs, const fighter *fig, void *cbdata) +{ + struct selector *sel = (struct selector *)cbdata; + int row = get_unitrow(fig, vs); + return (row >= sel->minrow && row <= sel->maxrow); +} + +selist *fighters(battle * b, const side * vs, int minrow, int maxrow, int mask) +{ + struct selector sel; + sel.maxrow = maxrow; + sel.minrow = minrow; + return select_fighters(b, vs, mask, select_row, &sel); +} + static void report_failed_spell(struct battle * b, struct unit * mage, const struct spell *sp) { message *m = msg_message("battle::spell_failed", "unit spell", mage, sp); diff --git a/src/battle.h b/src/battle.h index 4e20d00d8..3c86c2c8d 100644 --- a/src/battle.h +++ b/src/battle.h @@ -229,15 +229,17 @@ extern "C" { fighter * get_fighter(battle * b, const struct unit * u); /* END battle interface */ - extern void do_battles(void); + void do_battles(void); /* for combat spells and special attacks */ enum { SELECT_ADVANCE = 0x1, SELECT_DISTANCE = 0x2, SELECT_FIND = 0x4 }; enum { ALLY_SELF, ALLY_ANY }; - extern troop select_enemy(struct fighter *af, int minrow, int maxrow, + int get_unitrow(const fighter * af, const side * vs); + + troop select_enemy(struct fighter *af, int minrow, int maxrow, int select); - extern troop select_ally(struct fighter *af, int minrow, int maxrow, + troop select_ally(struct fighter *af, int minrow, int maxrow, int allytype); int count_enemies(struct battle *b, const struct fighter *af, @@ -246,21 +248,25 @@ extern "C" { int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, union variant *magres); bool terminate(troop dt, troop at, int type, const char *damage, bool missile); - extern void message_all(battle * b, struct message *m); - extern int hits(troop at, troop dt, weapon * awp); - extern void damage_building(struct battle *b, struct building *bldg, + void message_all(battle * b, struct message *m); + int hits(troop at, troop dt, weapon * awp); + void damage_building(struct battle *b, struct building *bldg, int damage_abs); + + typedef bool(*select_fun)(const struct side *vs, const struct fighter *fig, void *cbdata); + struct selist *select_fighters(struct battle *b, const struct side *vs, int mask, select_fun cb, void *cbdata); struct selist *fighters(struct battle *b, const struct side *vs, int minrow, int maxrow, int mask); + int count_allies(const struct side *as, int minrow, int maxrow, int select, int allytype); - extern bool helping(const struct side *as, const struct side *ds); - extern void rmfighter(fighter * df, int i); - extern struct fighter *select_corpse(struct battle *b, struct fighter *af); - extern int statusrow(int status); - extern void drain_exp(struct unit *u, int d); - extern void kill_troop(troop dt); - extern void remove_troop(troop dt); /* not the same as the badly named rmtroop */ + bool helping(const struct side *as, const struct side *ds); + void rmfighter(fighter * df, int i); + struct fighter *select_corpse(struct battle *b, struct fighter *af); + int statusrow(int status); + void drain_exp(struct unit *u, int d); + void kill_troop(troop dt); + void remove_troop(troop dt); /* not the same as the badly named rmtroop */ bool is_attacker(const fighter * fig); struct battle *make_battle(struct region * r); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index ab5f72f35..75daf2417 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -981,7 +981,7 @@ void move_unit(unit * u, region * r, unit ** ulist) /* ist mist, aber wegen nicht skalierender attribute notwendig: */ #include "alchemy.h" -void transfermen(unit * u, unit * dst, int n) +void clone_men(unit * u, unit * dst, int n) { const attrib *a; int hp = u->hp; @@ -1074,7 +1074,6 @@ void transfermen(unit * u, unit * dst, int n) transfer_curse(u, dst, n); } } - scale_number(u, u->number - n); if (dst) { set_number(dst, dst->number + n); hp -= u->hp; @@ -1100,6 +1099,12 @@ void transfermen(unit * u, unit * dst, int n) } } +void transfermen(unit * u, unit * dst, int n) +{ + clone_men(u, dst, n); + scale_number(u, u->number - n); +} + struct building *inside_building(const struct unit *u) { if (!u->building) { diff --git a/src/kernel/unit.h b/src/kernel/unit.h index 8ae13c5e2..beea74ee7 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -165,7 +165,8 @@ extern "C" { void set_level(struct unit *u, skill_t id, int level); int get_level(const struct unit *u, skill_t id); - extern void transfermen(struct unit *src, struct unit *dst, int n); + void transfermen(struct unit *src, struct unit *dst, int n); + void clone_men(struct unit *src, struct unit *dst, int n); /* like transfer, but do not subtract from src */ int eff_skill(const struct unit *u, const struct skill *sv, const struct region *r); int effskill_study(const struct unit *u, skill_t sk, const struct region *r); diff --git a/src/spells/combatspells.c b/src/spells/combatspells.c index 08f38c8ea..1f3ad47e4 100644 --- a/src/spells/combatspells.c +++ b/src/spells/combatspells.c @@ -256,6 +256,17 @@ static void scramble_fighters(selist * ql) } } +static bool select_armed(const side *vs, const fighter *fig, void *cbdata) +{ + int row = get_unitrow(fig, vs); + + UNUSED_ARG(cbdata); + if (row >= FIGHT_ROW && row < BEHIND_ROW) { + return fig->alive > 0 && fig->weapons; + } + return false; +} + /* Rosthauch */ int sp_combatrosthauch(struct castorder * co) { @@ -275,52 +286,42 @@ int sp_combatrosthauch(struct castorder * co) return 0; } - fgs = fighters(b, fi->side, FIGHT_ROW, BEHIND_ROW - 1, FS_ENEMY); + fgs = select_fighters(b, fi->side, FS_ENEMY, select_armed, NULL); scramble_fighters(fgs); - for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { + for (qi = 0, ql = fgs; force>0 && ql; selist_advance(&ql, &qi, 1)) { fighter *df = (fighter *)selist_get(ql, qi); + int w; - if (df->alive == 0) - continue; - if (force <= 0) - break; - - /* da n MIN(force, x), sollte force maximal auf 0 sinken */ - assert(force >= 0); - - if (df->weapons) { - int w; - for (w = 0; df->weapons[w].type != NULL; ++w) { - weapon *wp = df->weapons; - int n = MIN(force, wp->used); - if (n) { - requirement *mat = wp->type->itype->construction->materials; - bool iron = false; - while (mat && mat->number > 0) { - if (mat->rtype == get_resourcetype(R_IRON)) { - iron = true; - break; - } - mat++; + for (w = 0; df->weapons[w].type != NULL; ++w) { + weapon *wp = df->weapons; + int n = MIN(force, wp->used); + if (n) { + requirement *mat = wp->type->itype->construction->materials; + bool iron = false; + while (mat && mat->number > 0) { + if (mat->rtype == get_resourcetype(R_IRON)) { + iron = true; + break; } - if (iron) { - int p; - force -= n; - wp->used -= n; - k += n; - i_change(&df->unit->items, wp->type->itype, -n); - for (p = 0; n && p != df->unit->number; ++p) { - if (df->person[p].missile == wp) { - df->person[p].missile = NULL; - --n; - } + mat++; + } + if (iron) { + int p; + force -= n; + wp->used -= n; + k += n; + i_change(&df->unit->items, wp->type->itype, -n); + for (p = 0; n && p != df->unit->number; ++p) { + if (df->person[p].missile == wp) { + df->person[p].missile = NULL; + --n; } - for (p = 0; n && p != df->unit->number; ++p) { - if (df->person[p].melee == wp) { - df->person[p].melee = NULL; - --n; - } + } + for (p = 0; n && p != df->unit->number; ++p) { + if (df->person[p].melee == wp) { + df->person[p].melee = NULL; + --n; } } } @@ -889,6 +890,13 @@ int sp_strong_wall(struct castorder * co) return level; } +static bool select_alive(const side *vs, const fighter *fig, void *cbdata) +{ + UNUSED_ARG(vs); + UNUSED_ARG(cbdata); + return fig->alive > 0; +} + /** Spells: chaosrow / song of confusion. * German Title: 'Gesang der Verwirrung' */ @@ -915,7 +923,7 @@ int sp_chaosrow(struct castorder * co) power = chaosrow ? (power * 40) : get_force(power, 5); - fgs = fighters(b, fi->side, FIGHT_ROW, NUMROWS, FS_ENEMY); + fgs = select_fighters(b, fi->side, FS_ENEMY, select_alive, NULL); scramble_fighters(fgs); for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { @@ -977,9 +985,18 @@ int sp_chaosrow(struct castorder * co) return level; } +static bool select_afraid(const side *vs, const fighter *fig, void *cbdata) +{ + int row = get_unitrow(fig, vs); + UNUSED_ARG(cbdata); + if (row >= FIGHT_ROW && row <= AVOID_ROW) { + return fig->alive + fig->run.number < fig->unit->number; + } + return false; +} + /* Gesang der Furcht (Kampfzauber) */ /* Panik (Pr�kampfzauber) */ - int flee_spell(struct castorder * co, int strength) { fighter * fi = co->magician.fig; @@ -994,23 +1011,20 @@ int flee_spell(struct castorder * co, int strength) int force; force = (int)get_force(power, strength); - if (!count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE | SELECT_FIND)) { + if (force<=0 || !count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE | SELECT_FIND)) { msg = msg_message("sp_flee_effect_0", "mage spell", mage, sp); message_all(b, msg); msg_release(msg); return 0; } - fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY); + fgs = select_fighters(b, fi->side, FS_ENEMY, select_afraid, NULL); scramble_fighters(fgs); - for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { + for (qi = 0, ql = fgs; force > 0 && ql; selist_advance(&ql, &qi, 1)) { fighter *df = (fighter *)selist_get(ql, qi); - for (n = 0; n != df->alive; ++n) { - if (force < 0) - break; - + for (n = 0; force > 0 && n != df->alive; ++n) { if (df->person[n].flags & FL_PANICED) { /* bei SPL_SONG_OF_FEAR m�glich */ df->person[n].attack -= 1; --force; @@ -1556,8 +1570,8 @@ int sp_healing(struct castorder * co) message *msg; bool use_item = has_ao_healing(mage); - /* bis zu 11 Personen pro Stufe (einen HP m�ssen sie ja noch - * haben, sonst w�ren sie tot) k�nnen geheilt werden */ + /* bis zu 11 Personen pro Stufe (einen HP muessen sie ja noch + * haben, sonst waeren sie tot) koennen geheilt werden */ if (use_item) { healhp *= 2; @@ -1589,6 +1603,19 @@ int sp_healing(struct castorder * co) return level; } +static bool select_hero(const side *vs, const fighter *fig, void *cbdata) +{ + UNUSED_ARG(cbdata); + + if (playerrace(u_race(fig->unit))) { + int row = get_unitrow(fig, vs); + if (row >= FIGHT_ROW && row <= AVOID_ROW) { + return fig->alive + fig->run.number < fig->unit->number; + } + } + return false; +} + int sp_undeadhero(struct castorder * co) { fighter * fi = co->magician.fig; @@ -1603,80 +1630,69 @@ int sp_undeadhero(struct castorder * co) int force = (int)get_force(power, 0); double c = 0.50 + 0.02 * power; - /* Liste aus allen K�mpfern */ - fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY | FS_HELP); + /* Liste aus allen Kaempfern */ + fgs = select_fighters(b, fi->side, FS_ENEMY | FS_HELP, select_hero, NULL); scramble_fighters(fgs); - for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { + for (qi = 0, ql = fgs; ql && force>0; selist_advance(&ql, &qi, 1)) { fighter *df = (fighter *)selist_get(ql, qi); unit *du = df->unit; + int j = 0; - if (force <= 0) - break; + /* Wieviele Untote koennen wir aus dieser Einheit wecken? */ + for (n = df->alive + df->run.number; force>0 && n != du->number; n++) { + if (chance(c)) { + ++j; + --force; + } + } - /* keine Monster */ - if (!playerrace(u_race(du))) - continue; + if (j > 0) { + item **ilist; + unit *u = + create_unit(r, mage->faction, 0, get_race(RC_UNDEAD), 0, unit_getname(du), + du); - if (df->alive + df->run.number < du->number) { - int j = 0; + /* new units gets some stats from old unit */ - /* Wieviele Untote k�nnen wir aus dieser Einheit wecken? */ - for (n = df->alive + df->run.number; n != du->number; n++) { - if (chance(c)) { - ++j; - if (--force <= 0) - break; + if (du->display) { + unit_setinfo(u, du->display); + } + else { + unit_setinfo(u, NULL); + } + setstatus(u, du->status); + setguard(u, false); + for (ilist = &du->items; *ilist;) { + item *itm = *ilist; + int loot = itm->number * j / du->number; + if (loot != itm->number) { + int split = itm->number * j % du->number; + if (split > 0 && (rng_int() % du->number) < split) { + ++loot; + } + } + i_change(&u->items, itm->type, loot); + i_change(ilist, itm->type, -loot); + if (*ilist == itm) { + ilist = &itm->next; } } - if (j > 0) { - item **ilist; - unit *u = - create_unit(r, mage->faction, 0, get_race(RC_UNDEAD), 0, unit_getname(du), - du); - - /* new units gets some stats from old unit */ - - if (du->display) { - unit_setinfo(u, du->display); - } - else { - unit_setinfo(u, NULL); - } - setstatus(u, du->status); - setguard(u, false); - for (ilist = &du->items; *ilist;) { - item *itm = *ilist; - int loot = itm->number * j / du->number; - if (loot != itm->number) { - int split = itm->number * j % du->number; - if (split > 0 && (rng_int() % du->number) < split) { - ++loot; - } - } - i_change(&u->items, itm->type, loot); - i_change(ilist, itm->type, -loot); - if (*ilist == itm) { - ilist = &itm->next; - } - } - - /* inherit stealth from magician */ - if (mage->flags & UFL_ANON_FACTION) { - u->flags |= UFL_ANON_FACTION; - } - - /* transfer dead people to new unit, set hitpoints to those of old unit */ - transfermen(du, u, j); - u->hp = u->number * unit_max_hp(du); - assert(j <= df->side->casualties); - df->side->casualties -= j; - df->side->dead -= j; - - /* counting total number of undead */ - undead += j; + /* inherit stealth from magician */ + if (mage->flags & UFL_ANON_FACTION) { + u->flags |= UFL_ANON_FACTION; } + + /* transfer dead people to new unit, set hitpoints to those of old unit */ + transfermen(du, u, j); + u->hp = u->number * unit_max_hp(du); + assert(j <= df->side->casualties); + df->side->casualties -= j; + df->side->dead -= j; + + /* counting total number of undead */ + undead += j; } } selist_free(fgs); diff --git a/tests/runtests.bat b/tests/runtests.bat index b6f70768f..95ac17be4 100644 --- a/tests/runtests.bat +++ b/tests/runtests.bat @@ -3,6 +3,7 @@ IF EXIST ..\build-vs10 SET BUILD=..\build-vs10\eressea\Debug IF EXIST ..\build-vs11 SET BUILD=..\build-vs11\eressea\Debug IF EXIST ..\build-vs12 SET BUILD=..\build-vs12\eressea\Debug IF EXIST ..\build-vs14 SET BUILD=..\build-vs14\eressea\Debug +REM IF EXIST ..\build-vs15 SET BUILD=..\build-vs15\eressea\Debug SET SERVER=%BUILD%\eressea.exe %BUILD%\test_eressea.exe %SERVER% ..\scripts\run-tests.lua