From c05a65b881587ff66ef268997fa5351bc9c0b527 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 9 May 2018 22:16:30 +0200 Subject: [PATCH] expat parses (most of) races. fix canteach flag in XML. --- res/e3a/races.xml | 76 ++++++++-------- res/eressea.dtd | 190 +++++++++++++++++++++++++++++++++++++++ res/eressea/races.xml | 95 ++++++++++---------- res/races/dragon.xml | 2 +- res/races/goblin-3.xml | 2 +- res/races/halfling.xml | 2 +- src/exparse.c | 196 ++++++++++++++++++++++++++++++++++++++++- src/kernel/race.c | 15 ++-- src/kernel/race.h | 11 ++- src/kernel/race.test.c | 4 +- src/xmlreader.c | 22 ++--- 11 files changed, 498 insertions(+), 117 deletions(-) create mode 100644 res/eressea.dtd diff --git a/res/e3a/races.xml b/res/e3a/races.xml index bb70fa3df..00d34e58a 100644 --- a/res/e3a/races.xml +++ b/res/e3a/races.xml @@ -8,7 +8,7 @@ - + @@ -192,7 +192,7 @@ - + @@ -218,7 +218,7 @@ - + @@ -239,7 +239,7 @@ - + @@ -260,7 +260,7 @@ - + @@ -283,7 +283,7 @@ - + @@ -306,7 +306,7 @@ - + @@ -328,7 +328,7 @@ - + @@ -354,7 +354,7 @@ - + @@ -376,7 +376,7 @@ - + @@ -400,7 +400,7 @@ - + @@ -425,7 +425,7 @@ - + @@ -448,7 +448,7 @@ - + @@ -471,7 +471,7 @@ - + @@ -493,7 +493,7 @@ - + @@ -520,7 +520,7 @@ - + @@ -543,7 +543,7 @@ - + @@ -565,7 +565,7 @@ - + @@ -589,15 +589,15 @@ - + - + - + @@ -651,28 +651,28 @@ - + - + - + - + - + @@ -687,14 +687,14 @@ - + - + @@ -703,14 +703,14 @@ - + @@ -736,7 +736,7 @@ - + @@ -754,7 +754,7 @@ - + @@ -771,7 +771,7 @@ - + @@ -787,7 +787,7 @@ - + @@ -801,7 +801,7 @@ - + @@ -816,7 +816,7 @@ - + @@ -830,12 +830,12 @@ - + - + diff --git a/res/eressea.dtd b/res/eressea.dtd new file mode 100644 index 000000000..c58ea5f33 --- /dev/null +++ b/res/eressea.dtd @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/eressea/races.xml b/res/eressea/races.xml index 21ff5b028..eb85b6900 100644 --- a/res/eressea/races.xml +++ b/res/eressea/races.xml @@ -11,9 +11,8 @@ - + - @@ -27,7 +26,7 @@ - + @@ -60,7 +59,7 @@ - + @@ -88,7 +87,7 @@ - + @@ -115,7 +114,7 @@ - + @@ -144,7 +143,7 @@ - + @@ -173,7 +172,7 @@ - + @@ -203,7 +202,7 @@ - + @@ -237,7 +236,7 @@ - + @@ -267,7 +266,7 @@ - + @@ -299,7 +298,7 @@ - + @@ -329,7 +328,7 @@ - + @@ -358,7 +357,7 @@ - + @@ -388,7 +387,7 @@ - + @@ -417,7 +416,7 @@ - + @@ -448,7 +447,7 @@ - + @@ -479,7 +478,7 @@ - + @@ -508,7 +507,7 @@ - + @@ -540,7 +539,7 @@ - + @@ -570,16 +569,16 @@ - + - + + regaura="1.000000" weight="100" capacity="540" speed="1.000000" hp="20" damage="0d0" unarmedattack="0" unarmeddefense="0" attackmodifier="6" defensemodifier="10" scarepeasants="yes" fly="yes" walk="yes" canteach="no" invinciblenonmagic="yes"> @@ -649,28 +648,28 @@ - + - + - + - + - + @@ -684,25 +683,25 @@ - + - + - + - + @@ -712,7 +711,7 @@ - + @@ -722,7 +721,7 @@ - + @@ -733,7 +732,7 @@ - + @@ -783,7 +782,7 @@ - + @@ -925,7 +924,7 @@ - + @@ -962,7 +961,7 @@ - + @@ -970,7 +969,7 @@ - + @@ -984,7 +983,7 @@ - + @@ -1001,7 +1000,7 @@ - + @@ -1017,7 +1016,7 @@ - + @@ -1032,7 +1031,7 @@ - + @@ -1045,7 +1044,7 @@ - + @@ -1059,7 +1058,7 @@ - + @@ -1072,11 +1071,11 @@ - + - + @@ -1203,7 +1202,7 @@ - + diff --git a/res/races/dragon.xml b/res/races/dragon.xml index 7e0da2056..c9692cd9b 100644 --- a/res/races/dragon.xml +++ b/res/races/dragon.xml @@ -3,7 +3,7 @@ diff --git a/res/races/goblin-3.xml b/res/races/goblin-3.xml index 636f8cc29..9eb7ea835 100644 --- a/res/races/goblin-3.xml +++ b/res/races/goblin-3.xml @@ -7,7 +7,7 @@ speed="1.0" hp="16" damage="1d5" unarmedattack="-2" unarmeddefense="0" playerrace="yes" walk="yes" giveperson="yes" giveunit="yes" getitem="yes" equipment="yes" healing="2.0"> - + diff --git a/res/races/halfling.xml b/res/races/halfling.xml index 3628d2b35..1f326056f 100644 --- a/res/races/halfling.xml +++ b/res/races/halfling.xml @@ -1,7 +1,7 @@ - + diff --git a/src/exparse.c b/src/exparse.c index bc7218383..44455888a 100644 --- a/src/exparse.c +++ b/src/exparse.c @@ -128,7 +128,19 @@ static void handle_bad_input(parseinfo *pi, const XML_Char *el, const XML_Char * static bool handle_flag(int *flags, const XML_Char **pair, const char *names[]) { int i; for (i = 0; names[i]; ++i) { - if (xml_strcmp(pair[0], names[i]) == 0) { + const char * name = names[i]; + if (name[0] == '!') { + if (xml_strcmp(pair[0], name+1) == 0) { + if (xml_bool(pair[1])) { + *flags &= ~(1 << i); + } + else { + *flags |= (1 << i); + } + return true; + } + } + else if (xml_strcmp(pair[0], name) == 0) { if (xml_bool(pair[1])) { *flags |= (1 << i); } @@ -865,6 +877,173 @@ static void XMLCALL start_ships(parseinfo *pi, const XML_Char *el, const XML_Cha } } +static void XMLCALL start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr) { + race *rc = (race *)pi->object; + const char *flag_names[] = { + "!playerrace", "killpeasants", "scarepeasants", "!cansteal", + "moverandom", "cannotmove", "learn", "fly", "swim", "walk", + "!canlearn", "!canteach", "horse", "desert", "illusionary", + "absorbpeasants", "noheal", "noweapons", "shapeshift", + "shapeshiftany", "undead", "dragon", "coastal", "unarmedguard", + "cansail", "invisible", "shipspeed", "moveattack", "migrants", NULL }; + const char *bflag_names[] = { + "equipment", "noblock", "resistpierce", "resistcut", "resistbash", + "invinciblenonmagic", "noattack", NULL }; + const char *eflag_names[] = { + "giveperson", "giveunit", "getitem", "recruitethereal", + "recruitunlimited", "stonegolem", "irongolem", NULL }; + + if (xml_strcmp(el, "attack") == 0) { + assert(rc); + } + else if (xml_strcmp(el, "familiar") == 0) { + assert(rc); + } + else if (xml_strcmp(el, "skill") == 0) { + const XML_Char *name = NULL; + int i, speed = 0, mod = 0; + + for (i = 0; attr[i]; i += 2) { + const XML_Char *key = attr[i], *val = attr[i + 1]; + if (xml_strcmp(key, "name") == 0) { + name = val; + } + else if (xml_strcmp(key, "modifier") == 0) { + mod = xml_int(val); + } + else if (xml_strcmp(key, "speed") == 0) { + speed = xml_int(val); + } + else { + handle_bad_input(pi, el, key); + } + } + if (name) { + skill_t sk = findskill(name); + if (sk != NOSKILL) { + rc->bonus[sk] = (char)mod; + if (speed != 0) { + set_study_speed(rc, sk, speed); + } + } + } + } + else if (xml_strcmp(el, "param") == 0) { + const XML_Char *key = attr_get(attr, "name"), *val = attr_get(attr, "value"); + if (key && val) { + rc_set_param(rc, key, val); + } + } + else if (xml_strcmp(el, "ai") == 0) { + /* AI flags are cumulative to race flags. XML format is dumb */ + int i, flags = 0; + assert(rc); + for (i = 0; attr[i]; i += 2) { + const XML_Char *key = attr[i], *val = attr[i + 1]; + if (xml_strcmp(key, "splitsize") == 0) { + rc->splitsize = xml_int(val); + } + else if (xml_strcmp(key, "scare") == 0) { + rc_set_param(rc, "scare", val); + } + else if (!handle_flag(&flags, attr + i, flag_names)) { + handle_bad_input(pi, el, key); + } + } + rc->flags |= flags; + } + else if (xml_strcmp(el, "race") == 0) { + const XML_Char *name; + + name = attr_get(attr, "name"); + if (name) { + assert(!rc); + pi->object = rc = rc_get_or_create(name); + int i; + + for (i = 0; attr[i]; i += 2) { + const XML_Char *key = attr[i], *val = attr[i + 1]; + if (xml_strcmp(key, "maxaura") == 0) { + rc->maxaura = (int)(100 * xml_float(val)); + } + else if (xml_strcmp(key, "magres") == 0) { + /* specified in percent: */ + rc->magres = frac_make(xml_int(val), 100); + } + else if (xml_strcmp(key, "healing") == 0) { + rc->healing = (int)(xml_float(val) * 100); + } + else if (xml_strcmp(key, "regaura") == 0) { + rc->regaura = xml_float(val); + } + else if (xml_strcmp(key, "recruitcost") == 0) { + rc->recruitcost = xml_int(val); + } + else if (xml_strcmp(key, "maintenance") == 0) { + rc->maintenance = xml_int(val); + } + else if (xml_strcmp(key, "income") == 0) { + rc->income = xml_int(val); + } + else if (xml_strcmp(key, "weight") == 0) { + rc->weight = xml_int(val); + } + else if (xml_strcmp(key, "capacity") == 0) { + rc->capacity = xml_int(val); + } + else if (xml_strcmp(key, "speed") == 0) { + rc->speed = xml_float(val); + } + else if (xml_strcmp(key, "hp") == 0) { + rc->hitpoints = xml_int(val); + } + else if (xml_strcmp(key, "ac") == 0) { + rc->armor = xml_int(val); + } + else if (xml_strcmp(key, "damage") == 0) { + rc->def_damage = str_strdup(val); + } + else if (xml_strcmp(key, "unarmedattack") == 0) { + rc->at_default = xml_int(val); + } + else if (xml_strcmp(key, "unarmeddefense") == 0) { + rc->df_default = xml_int(val); + } + else if (xml_strcmp(key, "attackmodifier") == 0) { + rc->at_bonus = xml_int(val); + } + else if (xml_strcmp(key, "defensemodifier") == 0) { + rc->df_bonus = xml_int(val); + } + else if (xml_strcmp(key, "studyspeed") == 0) { + int study_speed = xml_int(val); + if (study_speed != 0) { + skill_t sk; + for (sk = 0; sk < MAXSKILLS; ++sk) { + set_study_speed(rc, sk, study_speed); + } + } + + } + else if (!handle_flag(&rc->flags, attr + i, flag_names)) { + if (!handle_flag(&rc->battle_flags, attr + i, bflag_names)) { + if (!handle_flag(&rc->ec_flags, attr + i, eflag_names)) { + /* we already handled the name earlier: */ + if (xml_strcmp(key, "name") != 0) { + handle_bad_input(pi, el, attr[i]); + } + } + } + } + } + } + } + else { + assert(rc); + handle_bad_input(pi, el, NULL); + } +} + static void XMLCALL start_buildings(parseinfo *pi, const XML_Char *el, const XML_Char **attr) { const char *flag_names[] = { "nodestroy", "nobuild", "unique", "decay", "magic", "namechange", "fort", "oneperturn", NULL }; if (xml_strcmp(el, "building") == 0) { @@ -979,6 +1158,9 @@ static void XMLCALL handle_start(void *data, const XML_Char *el, const XML_Char } else { switch (pi->type) { + case EXP_RACES: + start_races(pi, el, attr); + break; case EXP_BUILDINGS: start_buildings(pi, el, attr); break; @@ -1063,6 +1245,15 @@ static void end_resources(parseinfo *pi, const XML_Char *el) { } } +static void end_races(parseinfo *pi, const XML_Char *el) { + if (xml_strcmp(el, "race") == 0) { + pi->object = NULL; + } + else if (xml_strcmp(el, "races") == 0) { + pi->type = EXP_UNKNOWN; + } +} + static void end_ships(parseinfo *pi, const XML_Char *el) { ship_type *stype = (ship_type *)pi->object; if (xml_strcmp(el, "construction") == 0) { @@ -1135,6 +1326,9 @@ static void XMLCALL handle_end(void *data, const XML_Char *el) { parseinfo *pi = (parseinfo *)data; switch (pi->type) { + case EXP_RACES: + end_races(pi, el); + break; case EXP_SHIPS: end_ships(pi, el); break; diff --git a/src/kernel/race.c b/src/kernel/race.c index ac81f7f78..596f1527f 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -451,6 +451,12 @@ int rc_herb_trade(const struct race *rc) return 500; } +void set_study_speed(race *rc, skill_t sk, int modifier) { + if (!rc->study_speed) + rc->study_speed = calloc(1, MAXSKILLS); + rc->study_speed[sk] = (char)modifier; +} + const race *rc_otherrace(const race *rc) { variant *v = rc_getoption(rc, RCO_OTHER); @@ -466,18 +472,13 @@ void rc_set_param(struct race *rc, const char *key, const char *value) { if (strcmp(key, "recruit_multi") == 0) { rc->recruit_multi = atof(value); } - else if (strcmp(key, "migrants.formula") == 0) { - if (value[0] == '1') { - rc->flags |= RCF_MIGRANTS; - } - } else if (strcmp(key, "other_race")==0) { rc_setoption(rc, RCO_OTHER, value); } - else if (strcmp(key, "ai.scare")==0) { + else if (strcmp(key, "scare")==0) { rc_setoption(rc, RCO_SCARE, value); } - else if (strcmp(key, "hunger.damage")==0) { + else if (strcmp(key, "hunger_damage")==0) { rc_setoption(rc, RCO_HUNGER, value); } else if (strcmp(key, "armor.stamina")==0) { diff --git a/src/kernel/race.h b/src/kernel/race.h index 7c38eec57..51c99e0f0 100644 --- a/src/kernel/race.h +++ b/src/kernel/race.h @@ -129,7 +129,7 @@ extern "C" { int weight; int capacity; int income; - float speed; + double speed; int hitpoints; char *def_damage; int armor; @@ -229,9 +229,11 @@ extern "C" { #define RCF_CANSAIL (1<<24) /* Einheit darf Schiffe betreten */ #define RCF_INVISIBLE (1<<25) /* not visible in any report */ #define RCF_SHIPSPEED (1<<26) /* race gets +1 on shipspeed */ -#define RCF_MIGRANTS (1<<27) /* may have migrant units (human bonus) */ -#define RCF_FAMILIAR (1<<28) /* may be a familiar */ -#define RCF_ATTACK_MOVED (1<<29) /* may attack if it has moved */ +#define RCF_ATTACK_MOVED (1<<27) /* may attack if it has moved */ +#define RCF_MIGRANTS (1<<28) /* may have migrant units (human bonus) */ +#define RCF_FAMILIAR (1<<29) /* may be a familiar */ + +#define RCF_DEFAULT (RCF_NOSTEAL|RCF_CANSAIL|RCF_NOLEARN) /* Economic flags */ #define ECF_GIVEPERSON (1<<2) /* �bergibt Personen */ @@ -271,6 +273,7 @@ extern "C" { const char *raceprefix(const struct unit *u); void register_race_function(race_func, const char *); + void set_study_speed(struct race *rc, skill_t sk, int modifier); #ifdef __cplusplus } #endif diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c index dcbe26e0e..9a118bc74 100644 --- a/src/kernel/race.test.c +++ b/src/kernel/race.test.c @@ -113,9 +113,9 @@ static void test_rc_set_param(CuTest *tc) { rc_set_param(rc, "migrants.formula", "1"); CuAssertIntEquals(tc, RCF_MIGRANTS, rc->flags&RCF_MIGRANTS); CuAssertIntEquals(tc, MIGRANTS_LOG10, rc_migrants_formula(rc)); - rc_set_param(rc, "ai.scare", "400"); + rc_set_param(rc, "scare", "400"); CuAssertIntEquals(tc, 400, rc_scare(rc)); - rc_set_param(rc, "hunger.damage", "1d10+12"); + rc_set_param(rc, "hunger_damage", "1d10+12"); CuAssertStrEquals(tc, "1d10+12", rc_hungerdamage(rc)); test_teardown(); } diff --git a/src/xmlreader.c b/src/xmlreader.c index 3fd6c1b7b..b26bff66c 100644 --- a/src/xmlreader.c +++ b/src/xmlreader.c @@ -1256,7 +1256,7 @@ static void parse_ai(race * rc, xmlNodePtr node) propValue = xmlGetProp(node, BAD_CAST "scare"); if (propValue) { - rc_set_param(rc, "ai.scare", (const char *)propValue); + rc_set_param(rc, "scare", (const char *)propValue); xmlFree(propValue); } rc->splitsize = xml_ivalue(node, "splitsize", 0); @@ -1270,12 +1270,6 @@ static void parse_ai(race * rc, xmlNodePtr node) rc->flags |= RCF_ATTACK_MOVED; } -static void set_study_speed(race *rc, skill_t sk, int modifier) { - if (!rc->study_speed) - rc->study_speed = calloc(1, MAXSKILLS); - rc->study_speed[sk] = (char)modifier; -} - static int parse_races(xmlDocPtr doc) { xmlXPathContextPtr xpath = xmlXPathNewContext(doc); @@ -1319,7 +1313,7 @@ static int parse_races(xmlDocPtr doc) rc->income = xml_ivalue(node, "income", rc->income); rc->speed = (float)xml_fvalue(node, "speed", rc->speed); rc->hitpoints = xml_ivalue(node, "hp", rc->hitpoints); - rc->armor = (char)xml_ivalue(node, "ac", rc->armor); + rc->armor = xml_ivalue(node, "ac", rc->armor); study_speed_base = xml_ivalue(node, "studyspeed", 0); if (study_speed_base != 0) { for (sk = 0; sk < MAXSKILLS; ++sk) { @@ -1332,14 +1326,18 @@ static int parse_races(xmlDocPtr doc) rc->at_bonus = (char)xml_ivalue(node, "attackmodifier", rc->at_bonus); rc->df_bonus = (char)xml_ivalue(node, "defensemodifier", rc->df_bonus); + if (!xml_bvalue(node, "canteach", true)) + rc->flags |= RCF_NOTEACH; + if (!xml_bvalue(node, "cansteal", true)) + rc->flags |= RCF_NOSTEAL; + if (!xml_bvalue(node, "canlearn", true)) + rc->flags |= RCF_NOLEARN; if (!xml_bvalue(node, "playerrace", false)) { assert(rc->recruitcost == 0); rc->flags |= RCF_NPC; } if (xml_bvalue(node, "scarepeasants", false)) rc->flags |= RCF_SCAREPEASANTS; - if (!xml_bvalue(node, "cansteal", true)) - rc->flags |= RCF_NOSTEAL; if (xml_bvalue(node, "cansail", true)) rc->flags |= RCF_CANSAIL; if (xml_bvalue(node, "cannotmove", false)) @@ -1356,10 +1354,6 @@ static int parse_races(xmlDocPtr doc) rc->flags |= RCF_SWIM; if (xml_bvalue(node, "walk", false)) rc->flags |= RCF_WALK; - if (!xml_bvalue(node, "canlearn", true)) - rc->flags |= RCF_NOLEARN; - if (!xml_bvalue(node, "canteach", true)) - rc->flags |= RCF_NOTEACH; if (xml_bvalue(node, "horse", false)) rc->flags |= RCF_HORSE; if (xml_bvalue(node, "desert", false))