From 43bf73a01b203039436a8013643419de70afb20b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 14 May 2009 20:19:13 +0000 Subject: [PATCH] - recruiting secondary races - some archetype changes - new looting rules --- src/common/gamecode/archetype.c | 14 ++++ src/common/gamecode/archetype.h | 3 + src/common/gamecode/creport.c | 2 +- src/common/gamecode/economy.c | 135 ++++++++++++++++++++++--------- src/common/gamecode/economy.h | 1 - src/common/gamecode/laws.c | 32 +++++++- src/common/gamecode/report.c | 2 +- src/common/kernel/battle.c | 120 +++++++++++++++++++++------ src/common/kernel/battle.h | 7 +- src/common/kernel/building.c | 43 +++++----- src/common/kernel/eressea.c | 12 +-- src/common/kernel/move.c | 2 +- src/common/kernel/race.h | 71 ++++++++-------- src/common/kernel/region.c | 9 ++- src/common/kernel/region.h | 8 +- src/common/kernel/save.c | 34 ++++++++ src/common/kernel/version.h | 3 +- src/common/kernel/xmlreader.c | 51 +++++++----- src/common/settings-eressea.h | 2 + src/common/spells/combatspells.c | 47 ++--------- src/eressea/lua/region.cpp | 4 +- src/eressea/tolua/helpers.c | 34 ++++++++ 22 files changed, 433 insertions(+), 203 deletions(-) diff --git a/src/common/gamecode/archetype.c b/src/common/gamecode/archetype.c index 4a14b91b9..6e21eeb1a 100644 --- a/src/common/gamecode/archetype.c +++ b/src/common/gamecode/archetype.c @@ -13,6 +13,7 @@ #include #include #include +#include /* libxml includes */ #include @@ -83,6 +84,7 @@ parse_archetypes(xmlDocPtr doc) int i; for (i=0;i!=nodes->nodeNr;++i) { xmlNodePtr node = nodes->nodeTab[i]; + xmlNodePtr child; archetype * arch = calloc(1, sizeof(archetype)); xmlXPathObjectPtr sub; @@ -109,6 +111,18 @@ parse_archetypes(xmlDocPtr doc) arch->size = xml_ivalue(node, "cost", 0); + for (child=node->children;child;child=child->next) { + if (strcmp((const char *)child->name, "function")==0) { + xmlChar * propName = xmlGetProp(child, BAD_CAST "name"); + xmlChar * propValue = xmlGetProp(child, BAD_CAST "value"); + if (strcmp((const char *)propName, "create")) { + pf_generic foo = get_function((const char *)propValue); + arch->exec = (archetype_function)foo; + } + xmlFree(propValue); + xmlFree(propName); + } + } xpath->node = node; sub = xmlXPathEvalExpression(BAD_CAST "allow|deny", xpath); if (sub->nodesetval && sub->nodesetval->nodeNr) { diff --git a/src/common/gamecode/archetype.h b/src/common/gamecode/archetype.h index f500794bc..cf5187fa3 100644 --- a/src/common/gamecode/archetype.h +++ b/src/common/gamecode/archetype.h @@ -23,6 +23,8 @@ extern "C" { char * value; } rule; + typedef int (*archetype_function)(struct unit * u, const struct archetype *, int); + typedef struct archetype { struct archetype * next; char * name[2]; @@ -31,6 +33,7 @@ extern "C" { struct equipment * equip; struct construction * ctype; struct rule * rules; + archetype_function exec; } archetype; extern const struct archetype * find_archetype(const char * s, const struct locale * lang); diff --git a/src/common/gamecode/creport.c b/src/common/gamecode/creport.c index d58102843..785c3edba 100644 --- a/src/common/gamecode/creport.c +++ b/src/common/gamecode/creport.c @@ -1109,7 +1109,7 @@ cr_output_region(FILE * F, report_context * ctx, seen_region * sr) if (sr->mode!=see_unit) fprintf(F, "\"%s\";visibility\n", visibility[sr->mode]); { - faction * owner = get_region_owner(r); + faction * owner = region_get_owner(r); if (owner) { fprintf(F, "%d;owner\n", owner->no); } diff --git a/src/common/gamecode/economy.c b/src/common/gamecode/economy.c index ffe63fece..9220165a2 100644 --- a/src/common/gamecode/economy.c +++ b/src/common/gamecode/economy.c @@ -436,6 +436,20 @@ expandrecruit(region * r, request * recruitorders) assert(recruitorders==NULL); } +static int +recruit_cost(const faction * f, const race * rc) +{ + if (is_monsters(f) || f->race==rc) { + return rc->recruitcost; + } else { + const char * str = get_param(f->race->parameters, "other_race"); + if (str && strcmp(rc->_name[0], str)==0) { + return get_param_int(f->race->parameters, "other_cost", -1); + } + } + return -1; +} + static void recruit(unit * u, struct order * ord, request ** recruitorders) { @@ -446,16 +460,26 @@ recruit(unit * u, struct order * ord, request ** recruitorders) int recruitcost; const faction * f = u->faction; const struct race * rc = f->race; + const char * str; init_tokens(ord); skip_token(); n = getuint(); - if (is_monsters(f)) { - /* Monster dürfen REKRUTIERE 15 dracoid machen */ - const char * str = getstrtoken(); + + str = getstrtoken(); + if (str) { + /* Monster dürfen REKRUTIERE 15 dracoid machen + * also: secondary race */ rc = findrace(str, f->locale); - if (rc==NULL) rc = f->race; + recruitcost = recruit_cost(f, rc); + if ((u->number!=0 && rc!=f->race) || rc==NULL || recruitcost<0) { + rc = f->race; + recruitcost = recruit_cost(f, f->race); + } + } else { + recruitcost = recruit_cost(f, f->race); } + assert(recruitcost>=0); #if GUARD_DISABLES_RECRUIT /* this is a very special case because the recruiting unit may be empty @@ -507,7 +531,6 @@ recruit(unit * u, struct order * ord, request ** recruitorders) } } - recruitcost = rc->recruitcost; if (recruitcost) { pl = getplane(r); if (pl && fval(pl, PFL_NORECRUITS)) { @@ -1066,12 +1089,31 @@ maintain_buildings(region * r, boolean crash) } } +#define RECRUIT_MERGE 1 +#define RECRUIT_CLASSIC 2 +#define RECRUIT_ARCHETYPES 4 +static int rules_recruit = -1; + static int recruit_archetype(unit * u, order * ord) { + boolean merge = (u->number>0); int want; const char * s; + if (rules_recruit<0) { + rules_recruit = 0; + if (get_param_int(global.parameters, "recruit.allow_merge", 1)) { + rules_recruit |= RECRUIT_MERGE; + } + if (get_param_int(global.parameters, "recruit.classic", 1)) { + rules_recruit |= RECRUIT_CLASSIC; + } + if (get_param_int(global.parameters, "recruit.archetype", 0)) { + rules_recruit |= RECRUIT_ARCHETYPES; + } + } + init_tokens(ord); skip_token(); want = getuint(); @@ -1081,7 +1123,7 @@ recruit_archetype(unit * u, order * ord) const archetype * arch = find_archetype(s, u->faction->locale); attrib * a = NULL; - if (u->number>0) { + if ((rules_recruit&RECRUIT_MERGE)==0 && merge) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "unit_must_be_new", "")); /* TODO: error message */ return 0; @@ -1146,15 +1188,29 @@ recruit_archetype(unit * u, order * ord) n = build(u, arch->ctype, 0, n); if (n>0) { - set_number(u, n); - equip_unit(u, arch->equip); - u->hp = n * unit_max_hp(u); - if (arch->size) { - if (a==NULL) a = a_add(&u->building->attribs, a_new(&at_recruit)); - a->data.i += n*arch->size; + unit * u2; + if (merge) { + u2 = create_unit(u->region, u->faction, 0, u->race, 0, 0, u); + } else { + u2 = u; + } + if (arch->exec) { + n = arch->exec(u2, arch, n); + } + else { + set_number(u2, n); + equip_unit(u2, arch->equip); + u2->hp = n * unit_max_hp(u2); + if (arch->size) { + if (a==NULL) a = a_add(&u->building->attribs, a_new(&at_recruit)); + a->data.i += n*arch->size; + } } ADDMSG(&u->faction->msgs, msg_message("recruit_archetype", "unit amount archetype", u, n, arch->name[n==1])); + if (u!=u2 && u->race==u2->race) { + transfermen(u2, u, u2->number); + } return n; } else switch(n) { case ENOMATERIALS: @@ -1173,24 +1229,20 @@ recruit_archetype(unit * u, order * ord) return -1; } -int -recruit_classic(void) +static void recruit_init(void) { - static int value = -1; - if (value<0) { - value = get_param_int(global.parameters, "recruit.classic", 1); + if (rules_recruit<0) { + rules_recruit = 0; + if (get_param_int(global.parameters, "recruit.allow_merge", 1)) { + rules_recruit |= RECRUIT_MERGE; + } } - return value; } -int -recruit_archetypes(void) +int recruit_archetypes(void) { - static int value = -1; - if (value<0) { - value = get_param_int(global.parameters, "recruit.archetypes", 0); - } - return value; + if (rules_recruit<0) recruit_init(); + return (rules_recruit&RECRUIT_ARCHETYPES)!=0; } void @@ -1232,22 +1284,23 @@ economics(region *r) } /* RECRUIT orders */ + if (rules_recruit<0) recruit_init(); for (u = r->units; u; u = u->next) { order * ord; - if (!recruit_classic()) { - if (u->number>0) continue; - } - for (ord = u->orders; ord; ord = ord->next) { - if (get_keyword(ord) == K_RECRUIT) { - if (recruit_archetypes()) { - if (recruit_archetype(u, ord)>=0) { - continue; + + if ((rules_recruit&RECRUIT_MERGE) || u->number==0) { + for (ord = u->orders; ord; ord = ord->next) { + if (get_keyword(ord) == K_RECRUIT) { + if (rules_recruit&RECRUIT_ARCHETYPES) { + if (recruit_archetype(u, ord)>=0) { + continue; + } } + if (rules_recruit&RECRUIT_CLASSIC) { + recruit(u, ord, &recruitorders); + } + break; } - if (recruit_classic()) { - recruit(u, ord, &recruitorders); - } - break; } } } @@ -3147,8 +3200,9 @@ peasant_taxes(region * r) building * b; int money; int maxsize; + int morale; - f = get_region_owner(r); + f = region_get_owner(r); if (f==NULL) return; money = rmoney(r); @@ -3161,8 +3215,9 @@ peasant_taxes(region * r) if (u==NULL || u->faction!=f) return; maxsize = buildingeffsize(b, false); - if (maxsize > r->land->morale) { - maxsize = r->land->morale; + morale = region_get_morale(r); + if (maxsize > morale) { + maxsize = morale; } if (maxsize>0) { diff --git a/src/common/gamecode/economy.h b/src/common/gamecode/economy.h index bba522715..06d3b2958 100644 --- a/src/common/gamecode/economy.h +++ b/src/common/gamecode/economy.h @@ -54,7 +54,6 @@ void maintain_buildings(struct region * r, boolean crash); extern void add_spende(struct faction * f1, struct faction * f2, int betrag, struct region * r); extern int make_cmd(struct unit * u, struct order * ord); extern void split_allocations(struct region * r); -extern int recruit_classic(void); extern int recruit_archetypes(void); #ifdef __cplusplus diff --git a/src/common/gamecode/laws.c b/src/common/gamecode/laws.c index 5d12d285a..0eb3271bd 100644 --- a/src/common/gamecode/laws.c +++ b/src/common/gamecode/laws.c @@ -187,7 +187,7 @@ get_food(region *r) { unit *u; int peasantfood = rpeasants(r)*10; - faction * owner = get_region_owner(r); + faction * owner = region_get_owner(r); /* 1. Versorgung von eigenen Einheiten. Das vorhandene Silber * wird zunächst so auf die Einheiten aufgeteilt, dass idealerweise @@ -2914,9 +2914,16 @@ age_building(building * b) return b; } +static void age_region(region * r) +{ + a_age(&r->attribs); + handle_event(r->attribs, "timer", r); +} + static void ageing(void) { + const building_type * bt_castle = bt_find("castle"); faction *f; region *r; @@ -2960,9 +2967,9 @@ ageing(void) building ** bp; unit ** up; ship ** sp; + building * blargest = NULL; - a_age(&r->attribs); - handle_event(r->attribs, "timer", r); + age_region(r); /* Einheiten */ for (up=&r->units;*up;) { @@ -2985,7 +2992,24 @@ ageing(void) building * b = *bp; b = age_building(b); - if (b==*bp) bp = &(*bp)->next; + if (b==*bp) { + if (b->type==bt_castle) { + if (blargest==NULL || b->size>blargest->size) { + blargest = b; + } + } + bp = &(*bp)->next; + } + if (blargest) { + /* region owners update? */ + faction * f = region_get_owner(r); + unit * u = buildingowner(r, blargest); + if (u==NULL) { + region_set_owner(r, NULL, turn); + } else if (u->faction!=f) { + region_set_owner(r, f, turn); + } + } } } } diff --git a/src/common/gamecode/report.c b/src/common/gamecode/report.c index 0c38a30a6..448a3bba2 100644 --- a/src/common/gamecode/report.c +++ b/src/common/gamecode/report.c @@ -996,7 +996,7 @@ describe(FILE * F, const seen_region * sr, faction * f) } { - const faction * owner = get_region_owner(r); + const faction * owner = region_get_owner(r); if (owner!=NULL) { bytes = snprintf(bufp, size, " Die Region ist im Besitz von %s.", factionname(owner)); diff --git a/src/common/kernel/battle.c b/src/common/kernel/battle.c index b9959a3fa..99b1ab9d0 100644 --- a/src/common/kernel/battle.c +++ b/src/common/kernel/battle.c @@ -117,11 +117,16 @@ const troop no_troop = {0, 0}; static int max_turns = 0; static int damage_rules = 0; +static int loot_rules = 0; static int skill_formula = 0; #define FORMULA_ORIG 0 #define FORMULA_NEW 1 +#define LOOT_MONSTERS (1<<0) +#define LOOT_SELF (1<<1) /* code is mutually exclusive with LOOT_OTHERS */ +#define LOOT_OTHERS (1<<2) + #define DAMAGE_CRITICAL (1<<0) #define DAMAGE_MELEE_BONUS (1<<1) #define DAMAGE_MISSILE_BONUS (1<<2) @@ -132,6 +137,7 @@ static int skill_formula = 0; static void static_rules(void) { + loot_rules = get_param_int(global.parameters, "rules.combat.loot", LOOT_MONSTERS|LOOT_OTHERS); /* new formula to calculate to-hit-chance */ skill_formula = get_param_int(global.parameters, "rules.combat.skill_formula", FORMULA_ORIG); /* maximum number of combat turns */ @@ -1279,16 +1285,17 @@ count_side(const side * s, const side * vs, int minrow, int maxrow, int select) * troops that are still alive, not those that are still fighting although * dead. */ int -count_allies(const side * as, int minrow, int maxrow, int select) +count_allies(const side * as, int minrow, int maxrow, int select, int allytype) { battle *b = as->battle; - side *s; + side *ds; int count = 0; - for (s=b->sides;s!=b->sides+b->nsides;++s) { - if (!helping(as, s)) continue; - count += count_side(s, NULL, minrow, maxrow, select); - if (count>0 && (select&SELECT_FIND)) break; + for (ds=b->sides;ds!=b->sides+b->nsides;++ds) { + if ((allytype==ALLY_ANY && helping(as, ds)) || (allytype==ALLY_SELF && as->faction==ds->faction)) { + count += count_side(ds, NULL, minrow, maxrow, select); + if (count>0 && (select&SELECT_FIND)) break; + } } return count; } @@ -1933,12 +1940,14 @@ attacks_per_round(troop t) return t.fighter->person[t.index].speed; } - -#define HERO_SPEED 10 static void make_heroes(battle * b) { side * s; + static int hero_speed = 0; + if (hero_speed==0) { + hero_speed = get_param_int(global.parameters, "rules.combat.herospeed", 10); + } for (s=b->sides;s!=b->sides+b->nsides;++s) { fighter * fig; for (fig=s->fighters;fig;fig=fig->next) { @@ -1947,7 +1956,7 @@ make_heroes(battle * b) int i; assert(playerrace(u->race)); for (i=0;i!=u->number;++i) { - fig->person[i].speed += (HERO_SPEED-1); + fig->person[i].speed += (hero_speed-1); } } } @@ -2252,7 +2261,42 @@ make_side(battle * b, const faction * f, const group * g, unsigned int flags, co return s1; } -void +troop +select_ally(fighter * af, int minrow, int maxrow, int allytype) +{ + side *as = af->side; + battle *b = as->battle; + side * ds; + int allies = count_allies(as, minrow, maxrow, SELECT_ADVANCE, allytype); + + if (!allies) { + return no_troop; + } + allies = rng_int() % allies; + + for (ds=b->sides;ds!=b->sides+b->nsides;++ds) { + if ((allytype==ALLY_ANY && helping(as, ds)) || (allytype==ALLY_SELF && as->faction==ds->faction)) { + fighter * df; + for (df=ds->fighters; df; df=df->next) { + int dr = get_unitrow(df, NULL); + if (dr >= minrow && dr <= maxrow) { + if (df->alive - df->removed > allies) { + troop dt; + assert(allies>=0); + dt.index = allies; + dt.fighter = df; + return dt; + } + allies -= df->alive; + } + } + } + } + assert(!"we should never have gotten here"); + return no_troop; +} + +static void loot_items(fighter * corpse) { unit * u = corpse->unit; @@ -2271,27 +2315,55 @@ loot_items(fighter * corpse) int loot = maxloot/i; if (loot>0) { - int maxrow = BEHIND_ROW; - int lootchance = 50 + b->keeploot; + fighter *fig = NULL; + int looting = 0; + int maxrow = 0; - if (itm->type->flags & (ITF_CURSED|ITF_NOTLOST)) maxrow = LAST_ROW; itm->number -= loot; maxloot -= loot; - if (maxrow == LAST_ROW || rng_int() % 100 < lootchance) { - fighter *fig = select_enemy(corpse, FIGHT_ROW, maxrow, 0).fighter; - if (fig) { - item * l = fig->loot; - while (l && l->type!=itm->type) l=l->next; - if (!l) { - l = calloc(sizeof(item), 1); - l->next = fig->loot; - fig->loot = l; - l->type = itm->type; + if (is_monsters(u->faction) && (loot_rules & LOOT_MONSTERS)) { + looting = 1; + } else if (loot_rules&LOOT_OTHERS) { + looting = 1; + } else if (loot_rules&LOOT_SELF) { + looting = 2; + } + if (looting) { + if (itm->type->flags & (ITF_CURSED|ITF_NOTLOST)) { + maxrow = LAST_ROW; + } else { + int lootchance = 50 + b->keeploot; + if (rng_int() % 100 < lootchance) { + maxrow = BEHIND_ROW; } - l->number += loot; } } + if (maxrow>0) { + if (looting==1) { + fig = select_enemy(corpse, FIGHT_ROW, maxrow, 0).fighter; + } else if (looting==2) { + fig = select_ally(corpse, FIGHT_ROW, LAST_ROW, ALLY_SELF).fighter; + } else if (itm->type->flags & (ITF_CURSED|ITF_NOTLOST)) { + /* we absolutely, positively must have somebody loot this thing */ + fig = select_enemy(corpse, FIGHT_ROW, maxrow, 0).fighter; + if (!fig) { + fig = select_ally(corpse, FIGHT_ROW, LAST_ROW, ALLY_SELF).fighter; + } + } + } + + if (fig) { + item * l = fig->loot; + while (l && l->type!=itm->type) l=l->next; + if (!l) { + l = calloc(sizeof(item), 1); + l->next = fig->loot; + fig->loot = l; + l->type = itm->type; + } + l->number += loot; + } } } } diff --git a/src/common/kernel/battle.h b/src/common/kernel/battle.h index 8d0304fda..1ef180c8e 100644 --- a/src/common/kernel/battle.h +++ b/src/common/kernel/battle.h @@ -231,17 +231,20 @@ extern "C" { extern void do_battle(struct region * r); - /* for combar spells and special attacks */ + /* 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 select); + extern troop select_ally(struct fighter * af, int minrow, int maxrow, int allytype); + extern int count_enemies(struct battle * b, const struct fighter * af, int minrow, int maxrow, int select); extern boolean terminate(troop dt, troop at, int type, const char *damage, boolean 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, int damage_abs); extern struct cvector * fighters(struct battle *b, const struct side * vs, int minrow, int maxrow, int mask); - extern int count_allies(const struct side * as, int minrow, int maxrow, int select); + extern int count_allies(const struct side * as, int minrow, int maxrow, int select, int allytype); extern int get_unitrow(const struct fighter * af, const struct side * vs); extern boolean helping(const struct side * as, const struct side * ds); extern void rmfighter(fighter *df, int i); diff --git a/src/common/kernel/building.c b/src/common/kernel/building.c index de59838d5..bbc10f5da 100644 --- a/src/common/kernel/building.c +++ b/src/common/kernel/building.c @@ -523,33 +523,36 @@ free_buildings(void) extern struct attrib_type at_icastle; +/** returns the building's build stage (NOT size in people). + * only makes sense for castles or similar buildings with multiple + * stages */ int buildingeffsize(const building * b, boolean img) { - int i = b->size, n = 0; - const construction * cons; - static const struct building_type * bt_castle; - if (!bt_castle) bt_castle = bt_find("castle"); - assert(bt_castle); + int i = b->size, n = 0; + const construction * cons; + static const struct building_type * bt_castle; + if (!bt_castle) bt_castle = bt_find("castle"); + assert(bt_castle); - if (b==NULL) return 0; + if (b==NULL) return 0; - if (b->type!=bt_castle) { - if (img) { - const attrib * a = a_find(b->attribs, &at_icastle); - if (!a || a->data.v != bt_castle) return 0; - } else return 0; - } - cons = bt_castle->construction; - assert(cons); + if (b->type!=bt_castle) { + if (img) { + const attrib * a = a_find(b->attribs, &at_icastle); + if (!a || a->data.v != bt_castle) return 0; + } else return 0; + } + cons = bt_castle->construction; + assert(cons); - while (cons && cons->maxsize != -1 && i>=cons->maxsize) { - i-=cons->maxsize; - cons = cons->improvement; - ++n; - } + while (cons && cons->maxsize != -1 && i>=cons->maxsize) { + i-=cons->maxsize; + cons = cons->improvement; + ++n; + } - return n; + return n; } const char * diff --git a/src/common/kernel/eressea.c b/src/common/kernel/eressea.c index 71f16e2e4..9c532a3fc 100644 --- a/src/common/kernel/eressea.c +++ b/src/common/kernel/eressea.c @@ -583,7 +583,7 @@ int verbosity = 0; FILE *debug; int -shipspeed (const ship * sh, const unit * u) +shipspeed(const ship * sh, const unit * u) { int k = sh->type->range; static const curse_type * stormwind_ct, * nodrift_ct; @@ -606,9 +606,11 @@ shipspeed (const ship * sh, const unit * u) if( curse_active(get_curse(sh->attribs, nodrift_ct))) k += 1; - if (u->faction->race == new_race[RC_AQUARIAN] - && u->race == new_race[RC_AQUARIAN]) { - k += 1; + if (u->faction->race == u->race) { + /* race bonus for this faction? */ + if (u->race == new_race[RC_AQUARIAN]) { + k += 1; + } } a = a_find(sh->attribs, &at_speedup); @@ -2098,8 +2100,6 @@ get_param_int(const struct param * p, const char * key, int def) return def; } - - void set_param(struct param ** p, const char * key, const char * data) { diff --git a/src/common/kernel/move.c b/src/common/kernel/move.c index 1ae9573dc..784e6fc70 100644 --- a/src/common/kernel/move.c +++ b/src/common/kernel/move.c @@ -198,7 +198,7 @@ static boolean entrance_allowed(const struct unit * u, const struct region * r) { #ifdef REGIONOWNERS - faction * owner = get_region_owner(r); + faction * owner = region_get_owner(r); if (owner == NULL || u->faction == owner) return true; if (alliedfaction(r->planep, owner, u->faction, HELP_TRAVEL)) return true; return false; diff --git a/src/common/kernel/race.h b/src/common/kernel/race.h index 5dcddfd8b..4f16d84c0 100644 --- a/src/common/kernel/race.h +++ b/src/common/kernel/race.h @@ -50,44 +50,47 @@ typedef struct att { int flags; } att; +struct param; + typedef struct race { - const char *_name[4]; /* neu: name[4]völker */ - float magres; - float maxaura; /* Faktor auf Maximale Aura */ - float regaura; /* Faktor auf Regeneration */ - int recruitcost; - int maintenance; - int splitsize; - int weight; - int capacity; - float speed; + struct param * parameters; + const char *_name[4]; /* neu: name[4]völker */ + float magres; + float maxaura; /* Faktor auf Maximale Aura */ + float regaura; /* Faktor auf Regeneration */ + int recruitcost; + int maintenance; + int splitsize; + int weight; + int capacity; + float speed; float aggression; /* chance that a monster will attack */ - int hitpoints; - const char *def_damage; - char armor; - char at_default; /* Angriffsskill Unbewaffnet (default: -2)*/ - char df_default; /* Verteidigungsskill Unbewaffnet (default: -2)*/ - char at_bonus; /* Verändert den Angriffsskill (default: 0)*/ - char df_bonus; /* Verändert den Verteidigungskill (default: 0)*/ - const spell * precombatspell; - struct att attack[10]; - char bonus[MAXSKILLS]; - boolean __remove_me_nonplayer; - int flags; - int battle_flags; - int ec_flags; - race_t oldfamiliars[MAXMAGIETYP]; + int hitpoints; + const char *def_damage; + char armor; + char at_default; /* Angriffsskill Unbewaffnet (default: -2)*/ + char df_default; /* Verteidigungsskill Unbewaffnet (default: -2)*/ + char at_bonus; /* Verändert den Angriffsskill (default: 0)*/ + char df_bonus; /* Verändert den Verteidigungskill (default: 0)*/ + const spell * precombatspell; + struct att attack[10]; + char bonus[MAXSKILLS]; + boolean __remove_me_nonplayer; + int flags; + int battle_flags; + int ec_flags; + race_t oldfamiliars[MAXMAGIETYP]; - const char *(*generate_name) (const struct unit *); - const char *(*describe) (const struct unit *, const struct locale *); - void (*age)(struct unit *u); - boolean (*move_allowed)(const struct region *, const struct region *); - struct item * (*itemdrop)(const struct race *, int size); - void (*init_familiar)(struct unit *); + const char *(*generate_name) (const struct unit *); + const char *(*describe) (const struct unit *, const struct locale *); + void (*age)(struct unit *u); + boolean (*move_allowed)(const struct region *, const struct region *); + struct item * (*itemdrop)(const struct race *, int size); + void (*init_familiar)(struct unit *); - const struct race * familiars[MAXMAGIETYP]; - struct attrib * attribs; - struct race * next; + const struct race * familiars[MAXMAGIETYP]; + struct attrib * attribs; + struct race * next; } race; typedef struct race_list { diff --git a/src/common/kernel/region.c b/src/common/kernel/region.c index 98c280f9c..4cf030b62 100644 --- a/src/common/kernel/region.c +++ b/src/common/kernel/region.c @@ -1326,7 +1326,7 @@ r_addmessage(struct region * r, const struct faction * viewer, struct message * } struct faction * -get_region_owner(const struct region * r) +region_get_owner(const struct region * r) { if (r->land && r->land->ownership) { return r->land->ownership->owner; @@ -1335,7 +1335,7 @@ get_region_owner(const struct region * r) } void -set_region_owner(struct region * r, struct faction * owner, int turn) +region_set_owner(struct region * r, struct faction * owner, int turn) { if (r->land && r->land->ownership) { r->land->ownership->owner = owner; @@ -1371,3 +1371,8 @@ region_getname(const region * r) { } return ""; } + +int region_get_morale(const region * r) +{ + return r->land->morale; +} \ No newline at end of file diff --git a/src/common/kernel/region.h b/src/common/kernel/region.h index 62041e64a..87e30b042 100644 --- a/src/common/kernel/region.h +++ b/src/common/kernel/region.h @@ -79,7 +79,7 @@ typedef struct land_region { } * demands; const struct item_type * herbtype; short herbs; - unsigned short morale; + short morale; int trees[3]; /* 0 -> seeds, 1 -> shoots, 2 -> trees */ int horses; int peasants; @@ -232,8 +232,8 @@ extern const short delta_y[MAXDIRECTIONS]; direction_t dir_invert(direction_t dir); int production(const struct region *r); -void set_region_owner(struct region * r, struct faction * owner, int turn); -struct faction * get_region_owner(const struct region * r); +void region_set_owner(struct region * r, struct faction * owner, int turn); +struct faction * region_get_owner(const struct region * r); struct region * r_connect(const struct region *, direction_t dir); #ifdef FAST_CONNECT @@ -244,6 +244,8 @@ struct region * r_connect(const struct region *, direction_t dir); void free_regions(void); +int region_get_morale(const region * r); + void write_region_reference(const struct region * r, struct storage * store); variant read_region_reference(struct storage * store); int resolve_region_coor(variant id, void * address); diff --git a/src/common/kernel/save.c b/src/common/kernel/save.c index 07bb22e2e..938d2d39d 100644 --- a/src/common/kernel/save.c +++ b/src/common/kernel/save.c @@ -588,6 +588,31 @@ write_items(struct storage * store, item *ilist) store->w_tok(store, "end"); } +static void +write_owner(struct storage * store, region_owner *owner) +{ + if (owner) { + store->w_int(store, owner->since_turn); + write_faction_reference(owner->owner, store); + } else { + store->w_int(store, -1); + } +} + +static void +read_owner(struct storage * store, region_owner **powner) +{ + int since_turn = store->r_int(store); + if (since_turn>=0) { + region_owner * owner = malloc(sizeof(region_owner)); + owner->since_turn = since_turn; + read_reference(&owner->owner, store, read_faction_reference, resolve_faction); + *powner = owner; + } else { + *powner = 0; + } +} + int lastturn(void) { @@ -990,6 +1015,10 @@ readregion(struct storage * store, short x, short y) if (store->version>=REGIONITEMS_VERSION) { read_items(store, &r->land->items); } + if (store->version>=REGIONOWNER_VERSION) { + r->land->morale = (short)store->r_int(store); + read_owner(store, &r->land->ownership); + } } a_read(store, &r->attribs); @@ -1046,6 +1075,11 @@ writeregion(struct storage * store, const region * r) #if RELEASE_VERSION>=REGIONITEMS_VERSION write_items(store, r->land->items); store->w_brk(store); +#endif +#if RELEASE_VERSION>=REGIONOWNER_VERSION + store->w_int(store, r->land->morale); + write_owner(store, r->land->ownership); + store->w_brk(store); #endif } a_write(store, r->attribs); diff --git a/src/common/kernel/version.h b/src/common/kernel/version.h index d220d70d4..cdc89d70a 100644 --- a/src/common/kernel/version.h +++ b/src/common/kernel/version.h @@ -60,6 +60,7 @@ #define NOZEROIDS_VERSION 330 /* zero is not a valid ID for anything (including factions) */ #define NOBORDERATTRIBS_VERSION 331 /* border::attribs has been moved to userdata */ #define UIDHASH_VERSION 332 /* borders use the region.uid to store */ +#define REGIONOWNER_VERSION 333 /* regions have owners and morale */ #define MIN_VERSION CURSETYPE_VERSION /* minimal datafile we support */ -#define RELEASE_VERSION UIDHASH_VERSION /* current datafile */ +#define RELEASE_VERSION REGIONOWNER_VERSION /* current datafile */ diff --git a/src/common/kernel/xmlreader.c b/src/common/kernel/xmlreader.c index 725f86e66..433170d6a 100644 --- a/src/common/kernel/xmlreader.c +++ b/src/common/kernel/xmlreader.c @@ -293,7 +293,7 @@ parse_buildings(xmlDocPtr doc) } assert(propValue!=NULL); if (strcmp((const char*)propValue, "name")==0) { - btype->name = (const char * (*)(const struct building_type*, int size))fun; + btype->name = (const char * (*)(const struct building_type*, int))fun; } else if (strcmp((const char*)propValue, "init")==0) { btype->init = (void (*)(struct building_type*))fun; } else { @@ -1487,6 +1487,28 @@ parse_spells(xmlDocPtr doc) return 0; } +static void +parse_param(struct param ** params, xmlNodePtr node) +{ + xmlChar * propName = xmlGetProp(node, BAD_CAST "name"); + xmlChar * propValue = xmlGetProp(node, BAD_CAST "value"); + + set_param(params, (const char*)propName, (const char*)propValue); + + xmlFree(propName); + xmlFree(propValue); +} + +static void +parse_ai(race * rc, xmlNodePtr node) +{ + rc->splitsize = xml_ivalue(node, "splitsize", 0); + rc->aggression = (float)xml_fvalue(node, "aggression", 0.04); + if (xml_bvalue(node, "killpeasants", false)) rc->flags |= RCF_KILLPEASANTS; + if (xml_bvalue(node, "moverandom", false)) rc->flags |= RCF_MOVERANDOM; + if (xml_bvalue(node, "learn", false)) rc->flags |= RCF_LEARN; +} + static int parse_races(xmlDocPtr doc) { @@ -1500,6 +1522,7 @@ parse_races(xmlDocPtr doc) nodes = races->nodesetval; for (i=0;i!=nodes->nodeNr;++i) { xmlNodePtr node = nodes->nodeTab[i]; + xmlNodePtr child; xmlChar * propValue; race * rc; xmlXPathObjectPtr result; @@ -1573,19 +1596,13 @@ parse_races(xmlDocPtr doc) if (xml_bvalue(node, "resistpierce", false)) rc->battle_flags |= BF_RES_PIERCE; if (xml_bvalue(node, "canattack", true)) rc->battle_flags |= BF_CANATTACK; - /* reading eressea/races/race/ai */ - xpath->node = node; - result = xmlXPathEvalExpression(BAD_CAST "ai", xpath); - for (k=0;k!=result->nodesetval->nodeNr;++k) { - xmlNodePtr node = result->nodesetval->nodeTab[k]; - - rc->splitsize = xml_ivalue(node, "splitsize", 0); - rc->aggression = (float)xml_fvalue(node, "aggression", 0.04); - if (xml_bvalue(node, "killpeasants", false)) rc->flags |= RCF_KILLPEASANTS; - if (xml_bvalue(node, "moverandom", false)) rc->flags |= RCF_MOVERANDOM; - if (xml_bvalue(node, "learn", false)) rc->flags |= RCF_LEARN; + for (child=node->children;child;child=child->next) { + if (strcmp((const char *)child->name, "ai")==0) { + parse_ai(rc, child); + } else if (strcmp((const char *)child->name, "param")==0) { + parse_param(&rc->parameters, child); + } } - xmlXPathFreeObject(result); /* reading eressea/races/race/skill */ xpath->node = node; @@ -2043,13 +2060,7 @@ parse_main(xmlDocPtr doc) nodes = result->nodesetval; for (i=0;i!=nodes->nodeNr;++i) { xmlNodePtr node = nodes->nodeTab[i]; - xmlChar * propName = xmlGetProp(node, BAD_CAST "name"); - xmlChar * propValue = xmlGetProp(node, BAD_CAST "value"); - - set_param(&global.parameters, (const char*)propName, (const char*)propValue); - - xmlFree(propName); - xmlFree(propValue); + parse_param(&global.parameters, node); } xmlXPathFreeObject(result); diff --git a/src/common/settings-eressea.h b/src/common/settings-eressea.h index 844f6372c..f7f9cf9a7 100644 --- a/src/common/settings-eressea.h +++ b/src/common/settings-eressea.h @@ -61,3 +61,5 @@ #else # define BINDINGS_TOLUA /* new default */ #endif + +#undef REGIONOWNERS /* (WIP) region-owner uses HELP_TRAVEL to control entry to region */ diff --git a/src/common/spells/combatspells.c b/src/common/spells/combatspells.c index 5d5cec4ef..ef32e6f9b 100644 --- a/src/common/spells/combatspells.c +++ b/src/common/spells/combatspells.c @@ -385,41 +385,6 @@ sp_sleep(fighter * fi, int level, double power, spell * sp) } -static troop -select_ally(fighter * af, int minrow, int maxrow) -{ - side *as = af->side; - battle *b = as->battle; - side * ds; - int allies = count_allies(as, minrow, maxrow, SELECT_ADVANCE); - - if (!allies) { - return no_troop; - } - allies = rng_int() % allies; - - for (ds=b->sides;ds!=b->sides+b->nsides;++ds) { - if (helping(as, ds)) { - fighter * df; - for (df=ds->fighters; df; df=df->next) { - int dr = get_unitrow(df, NULL); - if (dr >= minrow && dr <= maxrow) { - if (df->alive - df->removed > allies) { - troop dt; - assert(allies>=0); - dt.index = allies; - dt.fighter = df; - return dt; - } - allies -= df->alive; - } - } - } - } - assert(!"we should never have gotten here"); - return no_troop; -} - int sp_speed(fighter * fi, int level, double power, spell * sp) { @@ -431,13 +396,13 @@ sp_speed(fighter * fi, int level, double power, spell * sp) force = lovar(power * power * 5); - allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); + allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE, ALLY_ANY); /* maximal 2*allies Versuche ein Opfer zu finden, ansonsten bestände * die Gefahr eine Endlosschleife*/ allies *= 2; while (force && allies) { - troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW); + troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW, ALLY_ANY); fighter *df = dt.fighter; --allies; @@ -1074,13 +1039,13 @@ sp_hero(fighter * fi, int level, double power, spell * sp) force = MAX(1, (int)power); } - allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE); + allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW, SELECT_ADVANCE, ALLY_ANY); /* maximal 2*allies Versuche ein Opfer zu finden, ansonsten bestände * die Gefahr eine Endlosschleife*/ allies *= 2; while (force && allies) { - troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW); + troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW, ALLY_ANY); fighter *df = dt.fighter; --allies; @@ -1126,13 +1091,13 @@ sp_berserk(fighter * fi, int level, double power, spell * sp) force = (int)power; } - allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE); + allies = count_allies(fi->side, FIGHT_ROW, BEHIND_ROW-1, SELECT_ADVANCE, ALLY_ANY); /* maximal 2*allies Versuche ein Opfer zu finden, ansonsten bestände * die Gefahr eine Endlosschleife*/ allies *= 2; while (force && allies) { - troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW-1); + troop dt = select_ally(fi, FIGHT_ROW, BEHIND_ROW-1, ALLY_ANY); fighter *df = dt.fighter; --allies; diff --git a/src/eressea/lua/region.cpp b/src/eressea/lua/region.cpp index 32c8a3812..84d603953 100644 --- a/src/eressea/lua/region.cpp +++ b/src/eressea/lua/region.cpp @@ -63,12 +63,12 @@ region_getterrain(const region * r) { static void lua_region_setowner(region * r, faction * f) { - set_region_owner(r, f, turn); + region_set_owner(r, f, turn); } static faction * lua_region_getowner(const region * r) { - return get_region_owner(r); + return region_get_owner(r); } static void diff --git a/src/eressea/tolua/helpers.c b/src/eressea/tolua/helpers.c index f6d6937d2..5694ffc26 100644 --- a/src/eressea/tolua/helpers.c +++ b/src/eressea/tolua/helpers.c @@ -27,6 +27,8 @@ without prior permission by the authors of Eressea. #include #include +#include + #include #include @@ -435,11 +437,43 @@ lua_useitem(struct unit * u, const struct item_type * itype, int amount, struct return result; } +static int +lua_recruit(struct unit * u, const struct archetype * arch, int amount) +{ + lua_State * L = (lua_State *)global.vm_state; + int result = 0; + char fname[64]; + snprintf(fname, sizeof(fname), "recruit_%s", arch->name[0]); + + lua_pushstring(L, fname); + lua_rawget(L, LUA_GLOBALSINDEX); + if (lua_isfunction(L, 1)) { + tolua_pushusertype(L, (void *)u, "unit"); + tolua_pushnumber(L, (lua_Number)amount); + + if (lua_pcall(L, 2, 1, 0)!=0) { + const char* error = lua_tostring(L, -1); + log_error(("use(%s) calling '%s': %s.\n", + unitname(u), fname, error)); + lua_pop(L, 1); + } else { + result = (int)lua_tonumber(L, -1); + lua_pop(L, 1); + } + } else { + log_error(("use(%s) calling '%s': not a function.\n", + unitname(u), fname)); + lua_pop(L, 1); + } + return result; +} + void register_tolua_helpers(void) { at_building_action.age = lc_age; + register_function((pf_generic)&lua_recruit, "lua_recruit"); register_function((pf_generic)&lua_callspell, "lua_castspell"); register_function((pf_generic)&lua_initfamiliar, "lua_initfamiliar"); register_item_use(&lua_useitem, "lua_useitem");