Merge pull request #694 from ennorehling/feature/2326-undeadhero

BUG 2326: counting undead heroes in battle summary
This commit is contained in:
Enno Rehling 2017-05-28 12:49:46 +02:00 committed by GitHub
commit 383866c44b
6 changed files with 181 additions and 134 deletions

View File

@ -439,7 +439,7 @@ static int get_row(const side * s, int row, const side * vs)
return result; 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); int row = statusrow(af->status);
if (vs == NULL) { if (vs == NULL) {
@ -1589,8 +1589,7 @@ static troop select_opponent(battle * b, troop at, int mindist, int maxdist)
return dt; return dt;
} }
selist *fighters(battle * b, const side * vs, int minrow, int maxrow, selist *select_fighters(battle * b, const side * vs, int mask, select_fun cb, void *cbdata)
int mask)
{ {
side *s; side *s;
selist *fightervp = 0; 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"); assert(mask == (FS_HELP | FS_ENEMY) || !"invalid alliance state");
} }
for (fig = s->fighters; fig; fig = fig->next) { for (fig = s->fighters; fig; fig = fig->next) {
int row = get_unitrow(fig, vs); if (cb(vs, fig, cbdata)) {
if (row >= minrow && row <= maxrow) {
selist_push(&fightervp, fig); selist_push(&fightervp, fig);
} }
} }
@ -1623,6 +1621,26 @@ selist *fighters(battle * b, const side * vs, int minrow, int maxrow,
return fightervp; 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) 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); message *m = msg_message("battle::spell_failed", "unit spell", mage, sp);

View File

@ -229,15 +229,17 @@ extern "C" {
fighter * get_fighter(battle * b, const struct unit * u); fighter * get_fighter(battle * b, const struct unit * u);
/* END battle interface */ /* END battle interface */
extern void do_battles(void); void do_battles(void);
/* for combat spells and special attacks */ /* for combat spells and special attacks */
enum { SELECT_ADVANCE = 0x1, SELECT_DISTANCE = 0x2, SELECT_FIND = 0x4 }; enum { SELECT_ADVANCE = 0x1, SELECT_DISTANCE = 0x2, SELECT_FIND = 0x4 };
enum { ALLY_SELF, ALLY_ANY }; 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); 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 allytype);
int count_enemies(struct battle *b, const struct fighter *af, 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); 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 terminate(troop dt, troop at, int type, const char *damage,
bool missile); bool missile);
extern void message_all(battle * b, struct message *m); void message_all(battle * b, struct message *m);
extern int hits(troop at, troop dt, weapon * awp); int hits(troop at, troop dt, weapon * awp);
extern void damage_building(struct battle *b, struct building *bldg, void damage_building(struct battle *b, struct building *bldg,
int damage_abs); 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, struct selist *fighters(struct battle *b, const struct side *vs,
int minrow, int maxrow, int mask); int minrow, int maxrow, int mask);
int count_allies(const struct side *as, int minrow, int maxrow, int count_allies(const struct side *as, int minrow, int maxrow,
int select, int allytype); int select, int allytype);
extern bool helping(const struct side *as, const struct side *ds); bool helping(const struct side *as, const struct side *ds);
extern void rmfighter(fighter * df, int i); void rmfighter(fighter * df, int i);
extern struct fighter *select_corpse(struct battle *b, struct fighter *af); struct fighter *select_corpse(struct battle *b, struct fighter *af);
extern int statusrow(int status); int statusrow(int status);
extern void drain_exp(struct unit *u, int d); void drain_exp(struct unit *u, int d);
extern void kill_troop(troop dt); void kill_troop(troop dt);
extern void remove_troop(troop dt); /* not the same as the badly named rmtroop */ void remove_troop(troop dt); /* not the same as the badly named rmtroop */
bool is_attacker(const fighter * fig); bool is_attacker(const fighter * fig);
struct battle *make_battle(struct region * r); struct battle *make_battle(struct region * r);

View File

@ -981,7 +981,7 @@ void move_unit(unit * u, region * r, unit ** ulist)
/* ist mist, aber wegen nicht skalierender attribute notwendig: */ /* ist mist, aber wegen nicht skalierender attribute notwendig: */
#include "alchemy.h" #include "alchemy.h"
void transfermen(unit * u, unit * dst, int n) void clone_men(unit * u, unit * dst, int n)
{ {
const attrib *a; const attrib *a;
int hp = u->hp; int hp = u->hp;
@ -1074,7 +1074,6 @@ void transfermen(unit * u, unit * dst, int n)
transfer_curse(u, dst, n); transfer_curse(u, dst, n);
} }
} }
scale_number(u, u->number - n);
if (dst) { if (dst) {
set_number(dst, dst->number + n); set_number(dst, dst->number + n);
hp -= u->hp; 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) struct building *inside_building(const struct unit *u)
{ {
if (!u->building) { if (!u->building) {

View File

@ -165,7 +165,8 @@ extern "C" {
void set_level(struct unit *u, skill_t id, int level); void set_level(struct unit *u, skill_t id, int level);
int get_level(const struct unit *u, skill_t id); 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 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); int effskill_study(const struct unit *u, skill_t sk, const struct region *r);

View File

@ -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 */ /* Rosthauch */
int sp_combatrosthauch(struct castorder * co) int sp_combatrosthauch(struct castorder * co)
{ {
@ -275,52 +286,42 @@ int sp_combatrosthauch(struct castorder * co)
return 0; 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); 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); fighter *df = (fighter *)selist_get(ql, qi);
int w;
if (df->alive == 0) for (w = 0; df->weapons[w].type != NULL; ++w) {
continue; weapon *wp = df->weapons;
if (force <= 0) int n = MIN(force, wp->used);
break; if (n) {
requirement *mat = wp->type->itype->construction->materials;
/* da n MIN(force, x), sollte force maximal auf 0 sinken */ bool iron = false;
assert(force >= 0); while (mat && mat->number > 0) {
if (mat->rtype == get_resourcetype(R_IRON)) {
if (df->weapons) { iron = true;
int w; break;
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++;
} }
if (iron) { mat++;
int p; }
force -= n; if (iron) {
wp->used -= n; int p;
k += n; force -= n;
i_change(&df->unit->items, wp->type->itype, -n); wp->used -= n;
for (p = 0; n && p != df->unit->number; ++p) { k += n;
if (df->person[p].missile == wp) { i_change(&df->unit->items, wp->type->itype, -n);
df->person[p].missile = NULL; for (p = 0; n && p != df->unit->number; ++p) {
--n; 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) { for (p = 0; n && p != df->unit->number; ++p) {
df->person[p].melee = NULL; if (df->person[p].melee == wp) {
--n; df->person[p].melee = NULL;
} --n;
} }
} }
} }
@ -889,6 +890,13 @@ int sp_strong_wall(struct castorder * co)
return level; 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. /** Spells: chaosrow / song of confusion.
* German Title: 'Gesang der Verwirrung' * German Title: 'Gesang der Verwirrung'
*/ */
@ -915,7 +923,7 @@ int sp_chaosrow(struct castorder * co)
power = chaosrow ? (power * 40) : get_force(power, 5); 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); scramble_fighters(fgs);
for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) {
@ -977,9 +985,18 @@ int sp_chaosrow(struct castorder * co)
return level; 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) */ /* Gesang der Furcht (Kampfzauber) */
/* Panik (Pr<50>kampfzauber) */ /* Panik (Pr<50>kampfzauber) */
int flee_spell(struct castorder * co, int strength) int flee_spell(struct castorder * co, int strength)
{ {
fighter * fi = co->magician.fig; fighter * fi = co->magician.fig;
@ -994,23 +1011,20 @@ int flee_spell(struct castorder * co, int strength)
int force; int force;
force = (int)get_force(power, strength); 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); msg = msg_message("sp_flee_effect_0", "mage spell", mage, sp);
message_all(b, msg); message_all(b, msg);
msg_release(msg); msg_release(msg);
return 0; 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); 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); fighter *df = (fighter *)selist_get(ql, qi);
for (n = 0; n != df->alive; ++n) { for (n = 0; force > 0 && n != df->alive; ++n) {
if (force < 0)
break;
if (df->person[n].flags & FL_PANICED) { /* bei SPL_SONG_OF_FEAR m<>glich */ if (df->person[n].flags & FL_PANICED) { /* bei SPL_SONG_OF_FEAR m<>glich */
df->person[n].attack -= 1; df->person[n].attack -= 1;
--force; --force;
@ -1556,8 +1570,8 @@ int sp_healing(struct castorder * co)
message *msg; message *msg;
bool use_item = has_ao_healing(mage); bool use_item = has_ao_healing(mage);
/* bis zu 11 Personen pro Stufe (einen HP m<EFBFBD>ssen sie ja noch /* bis zu 11 Personen pro Stufe (einen HP muessen sie ja noch
* haben, sonst w<EFBFBD>ren sie tot) k<EFBFBD>nnen geheilt werden */ * haben, sonst waeren sie tot) koennen geheilt werden */
if (use_item) { if (use_item) {
healhp *= 2; healhp *= 2;
@ -1589,6 +1603,19 @@ int sp_healing(struct castorder * co)
return level; 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) int sp_undeadhero(struct castorder * co)
{ {
fighter * fi = co->magician.fig; fighter * fi = co->magician.fig;
@ -1603,80 +1630,69 @@ int sp_undeadhero(struct castorder * co)
int force = (int)get_force(power, 0); int force = (int)get_force(power, 0);
double c = 0.50 + 0.02 * power; double c = 0.50 + 0.02 * power;
/* Liste aus allen K<EFBFBD>mpfern */ /* Liste aus allen Kaempfern */
fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY | FS_HELP); fgs = select_fighters(b, fi->side, FS_ENEMY | FS_HELP, select_hero, NULL);
scramble_fighters(fgs); 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); fighter *df = (fighter *)selist_get(ql, qi);
unit *du = df->unit; unit *du = df->unit;
int j = 0;
if (force <= 0) /* Wieviele Untote koennen wir aus dieser Einheit wecken? */
break; for (n = df->alive + df->run.number; force>0 && n != du->number; n++) {
if (chance(c)) {
++j;
--force;
}
}
/* keine Monster */ if (j > 0) {
if (!playerrace(u_race(du))) item **ilist;
continue; 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) { /* new units gets some stats from old unit */
int j = 0;
/* Wieviele Untote k<>nnen wir aus dieser Einheit wecken? */ if (du->display) {
for (n = df->alive + df->run.number; n != du->number; n++) { unit_setinfo(u, du->display);
if (chance(c)) { }
++j; else {
if (--force <= 0) unit_setinfo(u, NULL);
break; }
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) { /* inherit stealth from magician */
item **ilist; if (mage->flags & UFL_ANON_FACTION) {
unit *u = u->flags |= UFL_ANON_FACTION;
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;
} }
/* 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); selist_free(fgs);

View File

@ -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-vs11 SET BUILD=..\build-vs11\eressea\Debug
IF EXIST ..\build-vs12 SET BUILD=..\build-vs12\eressea\Debug IF EXIST ..\build-vs12 SET BUILD=..\build-vs12\eressea\Debug
IF EXIST ..\build-vs14 SET BUILD=..\build-vs14\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 SET SERVER=%BUILD%\eressea.exe
%BUILD%\test_eressea.exe %BUILD%\test_eressea.exe
%SERVER% ..\scripts\run-tests.lua %SERVER% ..\scripts\run-tests.lua