Archetypes (WIP): "RECRUIT 15 knight"

creates XML-configurable pre-skilled units for game variants with faster reinforcements.
Speeding up locale-string lookup a little bit, and making it more unified for different classes of strings (so new ones are easily added).
This commit is contained in:
Enno Rehling 2007-05-28 16:03:48 +00:00
parent 0fedaf43b2
commit b27da8c056
21 changed files with 773 additions and 160 deletions

View File

@ -0,0 +1,124 @@
#include <config.h>
#include "eressea.h"
#include "archetype.h"
/* kernel includes */
#include <kernel/equipment.h>
#include <kernel/building.h>
#include <kernel/xmlkernel.h>
#include <kernel/xmlreader.h>
/* util includes */
#include <util/umlaut.h>
#include <util/language.h>
#include <util/xml.h>
/* libxml includes */
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/encoding.h>
/* libc includes */
#include <string.h>
#include <assert.h>
static struct archetype * archetypes;
const struct archetype *
find_archetype(const char * s, const struct locale * lang)
{
struct tnode * tokens = get_translations(lang, UT_ARCHETYPES);
variant token;
if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) {
return (const struct archetype *)token.v;
}
return NULL;
}
void
register_archetype(archetype * arch)
{
arch->next = archetypes;
archetypes = arch;
}
const archetype *
get_archetype(const char * name)
{
const archetype * arch = archetypes;
for (;arch;arch=arch->next) {
if (strcmp(name, arch->name)==0) {
return arch;
}
}
return NULL;
}
void
init_archetypes(void)
{
const struct locale * lang = locales;
for (;lang;lang=nextlocale(lang)) {
variant var;
archetype * arch = archetypes;
struct tnode * tokens = get_translations(lang, UT_ARCHETYPES);
for (;arch;arch=arch->next) {
var.v = arch;
addtoken(tokens, LOC(lang, arch->name), var);
}
}
}
static int
parse_archetypes(xmlDocPtr doc)
{
xmlXPathContextPtr xpath = xmlXPathNewContext(doc);
xmlXPathObjectPtr result = xmlXPathEvalExpression(BAD_CAST "/eressea/archetypes/archetype", xpath);
xmlNodeSetPtr nodes = result->nodesetval;
xmlChar * property;
if (nodes && nodes->nodeNr>0) {
xmlNodePtr node = nodes->nodeTab[0];
archetype * arch = calloc(1, sizeof(archetype));
xmlXPathObjectPtr sub;
property = xmlGetProp(node, BAD_CAST "name");
assert(property!=NULL);
arch->name = strdup((const char *)property);
xmlFree(property);
property = xmlGetProp(node, BAD_CAST "equip");
if (property!=NULL) {
arch->equip = get_equipment((const char*)property);
xmlFree(property);
} else {
arch->equip = get_equipment(arch->name);
}
property = xmlGetProp(node, BAD_CAST "building");
if (property!=NULL) {
arch->btype = bt_find((const char*)property);
xmlFree(property);
}
arch->size = xml_ivalue(node, "cost", 1);
xpath->node = node;
sub = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
if (sub->nodesetval) {
xml_readconstruction(xpath, sub->nodesetval, &arch->ctype);
}
xmlXPathFreeObject(sub);
}
xmlXPathFreeObject(result);
xmlXPathFreeContext(xpath);
return 0;
}
void
register_archetypes(void)
{
xml_register_callback(parse_archetypes);
}

View File

@ -0,0 +1,40 @@
/* vi: set ts=2:
+-------------------+
| | Enno Rehling <enno@eressea.de>
| Eressea PBEM host | Christian Schlittchen <corwin@amber.kn-bremen.de>
| (c) 1998 - 2007 | Katja Zedel <katze@felidae.kn-bremen.de>
| | Henning Peters <faroul@beyond.kn-bremen.de>
+-------------------+
This program may not be used, modified or distributed
without prior permission by the authors of Eressea.
*/
#ifndef H_GC_ARCHETYPE
#define H_GC_ARCHETYPE
#ifdef __cplusplus
extern "C" {
#endif
typedef struct archetype {
char * name;
int size;
struct building_type * btype;
struct equipment * equip;
struct construction * ctype;
struct archetype * next;
} archetype;
extern const struct archetype * find_archetype(const char * s, const struct locale * lang);
extern void init_archetypes(void);
extern const struct archetype * get_archetype(const char * name);
extern void register_archetype(struct archetype * arch);
extern void register_archetypes(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -26,11 +26,13 @@
/* gamecode includes */ /* gamecode includes */
#include "laws.h" #include "laws.h"
#include "randenc.h" #include "randenc.h"
#include "archetype.h"
/* kernel includes */ /* kernel includes */
#include <kernel/alchemy.h> #include <kernel/alchemy.h>
#include <kernel/building.h> #include <kernel/building.h>
#include <kernel/calendar.h> #include <kernel/calendar.h>
#include <kernel/equipment.h>
#include <kernel/faction.h> #include <kernel/faction.h>
#include <kernel/give.h> #include <kernel/give.h>
#include <kernel/item.h> #include <kernel/item.h>
@ -1053,6 +1055,80 @@ maintain_buildings(region * r, boolean crash)
} }
} }
static int
recruit_archetype(unit * u, order * ord)
{
int n, id;
const char * s;
init_tokens(ord);
skip_token();
n = geti();
s = getstrtoken();
id = getid();
if (n>0 && s && s[0]) {
const archetype * arch = find_archetype(s, u->faction->locale);
if (u->number>0) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "unit_must_be_new", ""));
/* TODO: error message */
return 0;
}
if (arch==NULL) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "unknown_archetype", "name", s));
/* TODO: error message */
return 0;
}
if (arch->btype && u->building->type!=arch->btype) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "unit_must_be_in_building", "type", arch->btype));
/* TODO: error message */
return 0;
}
n = build(u, arch->ctype, 0, n);
if (n>0) {
scale_number(u, n);
equip_unit(u, arch->equip);
return n;
} else switch(n) {
case ENOMATERIALS:
ADDMSG(&u->faction->msgs, msg_materials_required(u, ord, arch->ctype));
return 0;
case ELOWSKILL:
case ENEEDSKILL:
/* no skill, or not enough skill points to build */
cmistake(u, ord, 50, MSG_PRODUCE);
return 0;
default:
assert(!"unhandled return value from build() in recruit_archetype");
}
return 0;
}
return -1;
}
static int
recruit_classic(void)
{
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "recruit.classic");
value = str?atoi(str):1;
}
return value;
}
static int
recruit_archetypes(void)
{
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "recruit.archetypes");
value = str?atoi(str):0;
}
return value;
}
void void
economics(region *r) economics(region *r)
@ -1095,9 +1171,19 @@ economics(region *r)
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
order * ord; order * ord;
if (!recruit_classic()) {
if (u->number>0) continue;
}
for (ord = u->orders; ord; ord = ord->next) { for (ord = u->orders; ord; ord = ord->next) {
if (get_keyword(ord) == K_RECRUIT) { if (get_keyword(ord) == K_RECRUIT) {
if (recruit_archetypes()) {
if (recruit_archetype(u, ord)>=0) {
continue;
}
}
if (recruit_classic()) {
recruit(u, ord, &recruitorders); recruit(u, ord, &recruitorders);
}
break; break;
} }
} }
@ -1142,27 +1228,9 @@ manufacture(unit * u, const item_type * itype, int want)
sk, minskill, itype->rtype, 1)); sk, minskill, itype->rtype, 1));
return; return;
case ENOMATERIALS: case ENOMATERIALS:
/* something missing from the list of materials */ ADDMSG(&u->faction->msgs, msg_materials_required(u, u->thisorder, itype->construction));
strcpy(buf, "Dafür braucht man mindestens:");
{
int c, n;
const construction * cons = itype->construction;
char * ch = buf+strlen(buf);
assert(cons);
for (c=0;cons->materials[c].number; c++) {
requirement * m = cons->materials+c;
if (c!=0)
strcat(ch++, ",");
n = m->number / cons->reqsize;
sprintf(ch, " %d %s", n?n:1,
locale_string(u->faction->locale,
resourcename(m->rtype, m->number!=1)));
ch = ch+strlen(ch);
}
mistake(u, u->thisorder, buf, MSG_PRODUCE);
return; return;
} }
}
if (n>0) { if (n>0) {
i_change(&u->items, itype, n); i_change(&u->items, itype, n);
if (want==INT_MAX) want = n; if (want==INT_MAX) want = n;
@ -1571,26 +1639,8 @@ create_potion(unit * u, const potion_type * ptype, int want)
break; break;
case ENOMATERIALS: case ENOMATERIALS:
/* something missing from the list of materials */ /* something missing from the list of materials */
strcpy(buf, "Dafür braucht man mindestens:"); ADDMSG(&u->faction->msgs, msg_materials_required(u, u->thisorder, ptype->itype->construction));
{
int c, n;
const construction * cons = ptype->itype->construction;
char * ch = buf+strlen(buf);
assert(cons);
for (c=0;cons->materials[c].number; c++) {
const requirement * m = cons->materials+c;
if (c!=0)
strcat(ch++, ",");
n = m->number / cons->reqsize;
sprintf(ch, " %d %s", n?n:1,
LOC(u->faction->locale,
resourcename(m->rtype, m->number!=1)));
ch = ch+strlen(ch);
}
strcat(ch,".");
mistake(u, u->thisorder, buf, MSG_PRODUCE);
return; return;
}
break; break;
default: default:
i_change(&u->items, ptype->itype, built); i_change(&u->items, ptype->itype, built);

View File

@ -277,6 +277,9 @@
<Filter <Filter
Name="Header" Name="Header"
Filter="*.h"> Filter="*.h">
<File
RelativePath=".\archetype.h">
</File>
<File <File
RelativePath=".\creation.h"> RelativePath=".\creation.h">
</File> </File>
@ -308,6 +311,9 @@
RelativePath=".\xmlreport.h"> RelativePath=".\xmlreport.h">
</File> </File>
</Filter> </Filter>
<File
RelativePath=".\archetype.c">
</File>
<File <File
RelativePath=".\creation.c"> RelativePath=".\creation.c">
</File> </File>

View File

@ -608,17 +608,20 @@ int
build(unit * u, const construction * ctype, int completed, int want) build(unit * u, const construction * ctype, int completed, int want)
{ {
const construction * type = ctype; const construction * type = ctype;
int skills; /* number of skill points remainig */ int skills = INT_MAX; /* number of skill points remainig */
int dm = get_effect(u, oldpotiontype[P_DOMORE]); int basesk = 0;
int made = 0; int made = 0;
int basesk, effsk;
assert(u->number);
if (want<=0) return 0; if (want<=0) return 0;
if (type==NULL) return 0; if (type==NULL) return 0;
if (type->improvement==NULL && completed==type->maxsize) if (type->improvement==NULL && completed==type->maxsize)
return ECOMPLETE; return ECOMPLETE;
if (type->skill!=NOSKILL) {
int effsk;
int dm = get_effect(u, oldpotiontype[P_DOMORE]);
assert(u->number);
basesk = effskill(u, type->skill); basesk = effskill(u, type->skill);
if (basesk==0) return ENEEDSKILL; if (basesk==0) return ENEEDSKILL;
@ -644,6 +647,7 @@ build(unit * u, const construction * ctype, int completed, int want)
change_effect(u, oldpotiontype[P_DOMORE], -dm); change_effect(u, oldpotiontype[P_DOMORE], -dm);
skills += dm * effsk; skills += dm * effsk;
} }
}
for (;want>0 && skills>0;) { for (;want>0 && skills>0;) {
int c, n; int c, n;
@ -764,6 +768,23 @@ build(unit * u, const construction * ctype, int completed, int want)
return made; return made;
} }
message *
msg_materials_required(unit * u, order * ord, const construction * ctype)
{
/* something missing from the list of materials */
int c;
resource * reslist = NULL;
for (c=0;ctype->materials[c].number; ++c) {
resource * res = malloc(sizeof(resource));
res->number = ctype->materials[c].number / ctype->reqsize;
res->type = ctype->materials[c].rtype;
res->next = reslist;
reslist = res;
}
return msg_feedback(u, ord, "build_required", "required", reslist);
}
int int
maxbuild(const unit * u, const construction * cons) maxbuild(const unit * u, const construction * cons)
/* calculate maximum size that can be built from available material */ /* calculate maximum size that can be built from available material */
@ -791,7 +812,7 @@ build_building(unit * u, const building_type * btype, int want, order * ord)
{ {
region * r = u->region; region * r = u->region;
boolean newbuilding = false; boolean newbuilding = false;
int c, built = 0, id; int built = 0, id;
building * b = NULL; building * b = NULL;
/* einmalige Korrektur */ /* einmalige Korrektur */
static char buffer[8 + IDSIZE + 1 + NAMESIZE + 1]; static char buffer[8 + IDSIZE + 1 + NAMESIZE + 1];
@ -874,22 +895,9 @@ build_building(unit * u, const building_type * btype, int want, order * ord)
/* the building is already complete */ /* the building is already complete */
cmistake(u, ord, 4, MSG_PRODUCE); cmistake(u, ord, 4, MSG_PRODUCE);
return; return;
case ENOMATERIALS: { case ENOMATERIALS:
/* something missing from the list of materials */ ADDMSG(&u->faction->msgs, msg_materials_required(u, ord, btype->construction));
const construction * cons = btype->construction;
resource * reslist = NULL;
assert(cons);
for (c=0;cons->materials[c].number; ++c) {
resource * res = malloc(sizeof(resource));
res->number = cons->materials[c].number / cons->reqsize;
res->type = cons->materials[c].rtype;
res->next = reslist;
reslist = res;
}
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "build_required",
"required", reslist));
return; return;
}
case ELOWSKILL: case ELOWSKILL:
case ENEEDSKILL: case ENEEDSKILL:
/* no skill, or not enough skill points to build */ /* no skill, or not enough skill points to build */

View File

@ -79,7 +79,7 @@ void sunhash(struct ship * sh);
extern int build(struct unit * u, const construction * ctype, int completed, int want); extern int build(struct unit * u, const construction * ctype, int completed, int want);
extern int maxbuild(const struct unit *u, const construction *cons); extern int maxbuild(const struct unit *u, const construction *cons);
extern struct message * msg_materials_required(struct unit * u, struct order * ord, const struct construction * ctype);
/** error messages that build may return: */ /** error messages that build may return: */
#define ELOWSKILL -1 #define ELOWSKILL -1
#define ENEEDSKILL -2 #define ENEEDSKILL -2

View File

@ -1489,53 +1489,13 @@ findstr(const char **v, const char *s, unsigned char n)
return -1; return -1;
} }
enum {
UT_NONE,
UT_PARAM,
UT_ITEM,
UT_BUILDING,
UT_HERB,
UT_POTION,
UT_MAX
};
static struct lstr {
const struct locale * lang;
struct tnode tokens[UT_MAX];
struct tnode skillnames;
struct tnode keywords;
struct tnode races;
struct tnode directions;
struct tnode options;
struct lstr * next;
} * lstrs;
static struct lstr *
get_lnames(const struct locale * lang)
{
static struct lstr * lnames = NULL;
static const struct locale * lastlang = NULL;
if (lastlang!=lang || lnames==NULL) {
lnames = lstrs;
while (lnames && lnames->lang!=lang) lnames = lnames->next;
if (lnames==NULL) {
lnames = calloc(sizeof(struct lstr), 1);
lnames->lang = lang;
lnames->next = lstrs;
lstrs = lnames;
}
}
return lnames;
}
const struct race * const struct race *
findrace(const char * s, const struct locale * lang) findrace(const char * s, const struct locale * lang)
{ {
struct lstr * lnames = get_lnames(lang); struct tnode * tokens = get_translations(lang, UT_RACES);
variant token; variant token;
if (findtoken(&lnames->races, s, &token)==E_TOK_SUCCESS) { if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) {
return (const struct race *)token.v; return (const struct race *)token.v;
} }
return NULL; return NULL;
@ -1544,10 +1504,10 @@ findrace(const char * s, const struct locale * lang)
int int
findoption(const char *s, const struct locale * lang) findoption(const char *s, const struct locale * lang)
{ {
struct lstr * lnames = get_lnames(lang); struct tnode * tokens = get_translations(lang, UT_OPTIONS);
variant token; variant token;
if (findtoken(&lnames->options, s, &token)==E_TOK_SUCCESS) { if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) {
return (direction_t)token.i; return (direction_t)token.i;
} }
return NODIRECTION; return NODIRECTION;
@ -1556,22 +1516,23 @@ findoption(const char *s, const struct locale * lang)
skill_t skill_t
findskill(const char *s, const struct locale * lang) findskill(const char *s, const struct locale * lang)
{ {
struct lstr * lnames = get_lnames(lang); struct tnode * tokens = get_translations(lang, UT_SKILLS);
variant token; variant token;
if (findtoken(&lnames->skillnames, s, &token)==E_TOK_NOMATCH) return NOSKILL; if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) return NOSKILL;
return (skill_t)token.i; return (skill_t)token.i;
} }
keyword_t keyword_t
findkeyword(const char *s, const struct locale * lang) findkeyword(const char *s, const struct locale * lang)
{ {
struct lstr * lnames = get_lnames(lang); struct tnode * tokens = get_translations(lang, UT_KEYWORDS);
variant token; variant token;
#ifdef AT_PERSISTENT #ifdef AT_PERSISTENT
if (*s == '@') s++; if (*s == '@') s++;
#endif #endif
if (findtoken(&lnames->keywords, s, &token)==E_TOK_NOMATCH) return NOKEYWORD; if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) return NOKEYWORD;
if (global.disabled[token.i]) return NOKEYWORD; if (global.disabled[token.i]) return NOKEYWORD;
return (keyword_t) token.i; return (keyword_t) token.i;
} }
@ -1579,12 +1540,11 @@ findkeyword(const char *s, const struct locale * lang)
param_t param_t
findparam(const char *s, const struct locale * lang) findparam(const char *s, const struct locale * lang)
{ {
struct lstr * lnames = get_lnames(lang); struct tnode * tokens = get_translations(lang, UT_PARAMS);
const building_type * btype;
variant token; variant token;
if (findtoken(&lnames->tokens[UT_PARAM], s, &token)==E_TOK_NOMATCH) { if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) {
btype = findbuildingtype(s, lang); const building_type * btype = findbuildingtype(s, lang);
if (btype!=NULL) return (param_t) P_GEBAEUDE; if (btype!=NULL) return (param_t) P_GEBAEUDE;
return NOPARAM; return NOPARAM;
} }
@ -1943,6 +1903,11 @@ createunit(region * r, faction * f, int number, const struct race * rc)
return create_unit(r, f, number, rc, 0, NULL, NULL); return create_unit(r, f, number, rc, 0, NULL, NULL);
} }
/** creates a new unit.
*
* @param dname: name, set to NULL to get a default.
* @param creator: unit to inherit stealth, group, building, ship, etc. from
*/
unit * unit *
create_unit(region * r, faction * f, int number, const struct race *urace, int id, const char * dname, unit *creator) create_unit(region * r, faction * f, int number, const struct race *urace, int id, const char * dname, unit *creator)
{ {
@ -2026,7 +1991,7 @@ create_unit(region * r, faction * f, int number, const struct race *urace, int i
a->data.v = creator; a->data.v = creator;
} }
/* Monster sind grundsätzlich parteigetarnt */ /* Monster sind grundsätzlich parteigetarnt */
if(f->no <= 0) fset(u, UFL_PARTEITARNUNG); if (f->no <= 0) fset(u, UFL_PARTEITARNUNG);
return u; return u;
} }
@ -2299,21 +2264,22 @@ init_directions(tnode * root, const struct locale * lang)
{ NULL, NODIRECTION} { NULL, NODIRECTION}
}; };
int i; int i;
struct lstr * lnames = get_lnames(lang); struct tnode * tokens = get_translations(lang, UT_DIRECTIONS);
for (i=0; dirs[i].direction!=NODIRECTION;++i) { for (i=0; dirs[i].direction!=NODIRECTION;++i) {
variant token; variant token;
token.i = dirs[i].direction; token.i = dirs[i].direction;
addtoken(&lnames->directions, LOC(lang, dirs[i].name), token); addtoken(tokens, LOC(lang, dirs[i].name), token);
} }
} }
direction_t direction_t
finddirection(const char *s, const struct locale * lang) finddirection(const char *s, const struct locale * lang)
{ {
struct lstr * lnames = get_lnames(lang); struct tnode * tokens = get_translations(lang, UT_DIRECTIONS);
variant token; variant token;
if (findtoken(&lnames->directions, s, &token)==E_TOK_SUCCESS) { if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) {
return (direction_t)token.i; return (direction_t)token.i;
} }
return NODIRECTION; return NODIRECTION;
@ -2322,37 +2288,48 @@ finddirection(const char *s, const struct locale * lang)
static void static void
init_locale(const struct locale * lang) init_locale(const struct locale * lang)
{ {
struct lstr * lnames = get_lnames(lang);
variant var; variant var;
int i; int i;
const struct race * rc; const struct race * rc;
struct tnode * tokens;
init_directions(&lnames->directions, lang); tokens = get_translations(lang, UT_DIRECTIONS);
init_directions(tokens, lang);
tokens = get_translations(lang, UT_RACES);
for (rc=races;rc;rc=rc->next) { for (rc=races;rc;rc=rc->next) {
var.v = (void*)rc; var.v = (void*)rc;
addtoken(&lnames->races, LOC(lang, rc_name(rc, 1)), var); addtoken(tokens, LOC(lang, rc_name(rc, 1)), var);
addtoken(&lnames->races, LOC(lang, rc_name(rc, 0)), var); addtoken(tokens, LOC(lang, rc_name(rc, 0)), var);
} }
tokens = get_translations(lang, UT_PARAMS);
for (i=0;i!=MAXPARAMS;++i) { for (i=0;i!=MAXPARAMS;++i) {
var.i = i; var.i = i;
addtoken(&lnames->tokens[UT_PARAM], LOC(lang, parameters[i]), var); addtoken(tokens, LOC(lang, parameters[i]), var);
} }
tokens = get_translations(lang, UT_SKILLS);
for (i=0;i!=MAXSKILLS;++i) { for (i=0;i!=MAXSKILLS;++i) {
if (i!=SK_TRADE || !TradeDisabled()) { if (i!=SK_TRADE || !TradeDisabled()) {
const char * skname = skillname((skill_t)i, lang); const char * skname = skillname((skill_t)i, lang);
if (skname!=NULL) { if (skname!=NULL) {
var.i = i; var.i = i;
addtoken(&lnames->skillnames, skname, var); addtoken(tokens, skname, var);
} }
} }
} }
tokens = get_translations(lang, UT_KEYWORDS);
for (i=0;i!=MAXKEYWORDS;++i) { for (i=0;i!=MAXKEYWORDS;++i) {
var.i = i; var.i = i;
addtoken(&lnames->keywords, LOC(lang, keywords[i]), var); addtoken(tokens, LOC(lang, keywords[i]), var);
} }
tokens = get_translations(lang, UT_OPTIONS);
for (i=0;i!=MAXOPTIONS;++i) { for (i=0;i!=MAXOPTIONS;++i) {
var.i = i; var.i = i;
addtoken(&lnames->options, LOC(lang, options[i]), var); addtoken(tokens, LOC(lang, options[i]), var);
} }
} }

View File

@ -406,6 +406,9 @@
<File <File
RelativePath=".\unit.c"> RelativePath=".\unit.c">
</File> </File>
<File
RelativePath=".\xmlkernel.h">
</File>
<File <File
RelativePath=".\xmlreader.c"> RelativePath=".\xmlreader.c">
</File> </File>

View File

@ -0,0 +1,28 @@
/* vi: set ts=2:
+-------------------+
| | Enno Rehling <enno@eressea.de>
| Eressea PBEM host | Christian Schlittchen <corwin@amber.kn-bremen.de>
| (c) 1998 - 2007 | Katja Zedel <katze@felidae.kn-bremen.de>
| | Henning Peters <faroul@beyond.kn-bremen.de>
+-------------------+
This program may not be used, modified or distributed
without prior permission by the authors of Eressea.
*/
#ifndef H_KRNL_XML
#define H_KRNL_XML
#ifdef __cplusplus
extern "C" {
#endif
#include <libxml/xpath.h>
extern void xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, struct construction ** consPtr);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -159,13 +159,13 @@ xml_readrequirements(xmlNodePtr * nodeTab, int nodeNr, requirement ** reqArray)
} }
} }
static void void
xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr * nodeTab, int nodeNr, construction ** consPtr) xml_readconstruction(xmlXPathContextPtr xpath, xmlNodeSetPtr nodeSet, construction ** consPtr)
{ {
xmlNodePtr pushNode = xpath->node; xmlNodePtr pushNode = xpath->node;
int k; int k;
for (k=0;k!=nodeNr;++k) { for (k=0;k!=nodeSet->nodeNr;++k) {
xmlNodePtr node = nodeTab[k]; xmlNodePtr node = nodeSet->nodeTab[k];
xmlChar * property; xmlChar * property;
construction * con; construction * con;
xmlXPathObjectPtr req; xmlXPathObjectPtr req;
@ -176,10 +176,13 @@ xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr * nodeTab, int nodeNr,
consPtr = &con->improvement; consPtr = &con->improvement;
property = xmlGetProp(node, BAD_CAST "skill"); property = xmlGetProp(node, BAD_CAST "skill");
assert(property!=NULL); if (property!=NULL) {
con->skill = sk_find((const char*)property); con->skill = sk_find((const char*)property);
assert(con->skill!=NOSKILL); assert(con->skill!=NOSKILL);
xmlFree(property); xmlFree(property);
} else {
con->skill = NOSKILL;
}
con->maxsize = xml_ivalue(node, "maxsize", -1); con->maxsize = xml_ivalue(node, "maxsize", -1);
con->minskill = xml_ivalue(node, "minskill", -1); con->minskill = xml_ivalue(node, "minskill", -1);
@ -207,7 +210,6 @@ xml_readconstruction(xmlXPathContextPtr xpath, xmlNodePtr * nodeTab, int nodeNr,
} }
xmlXPathFreeObject(req); xmlXPathFreeObject(req);
} }
xpath->node = pushNode; xpath->node = pushNode;
} }
@ -277,7 +279,7 @@ parse_buildings(xmlDocPtr doc)
/* reading eressea/buildings/building/construction */ /* reading eressea/buildings/building/construction */
xpath->node = node; xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval->nodeTab, result->nodesetval->nodeNr, &btype->construction); xml_readconstruction(xpath, result->nodesetval, &btype->construction);
xmlXPathFreeObject(result); xmlXPathFreeObject(result);
if (gamecode_enabled) { if (gamecode_enabled) {
@ -494,7 +496,7 @@ parse_ships(xmlDocPtr doc)
/* reading eressea/ships/ship/construction */ /* reading eressea/ships/ship/construction */
xpath->node = node; xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval->nodeTab, result->nodesetval->nodeNr, &st->construction); xml_readconstruction(xpath, result->nodesetval, &st->construction);
xmlXPathFreeObject(result); xmlXPathFreeObject(result);
/* reading eressea/ships/ship/coast */ /* reading eressea/ships/ship/coast */
@ -754,7 +756,7 @@ xml_readitem(xmlXPathContextPtr xpath, resource_type * rtype)
/* reading item/construction */ /* reading item/construction */
xpath->node = node; xpath->node = node;
result = xmlXPathEvalExpression(BAD_CAST "construction", xpath); result = xmlXPathEvalExpression(BAD_CAST "construction", xpath);
xml_readconstruction(xpath, result->nodesetval->nodeTab, result->nodesetval->nodeNr, &itype->construction); xml_readconstruction(xpath, result->nodesetval, &itype->construction);
xmlXPathFreeObject(result); xmlXPathFreeObject(result);
/* reading item/weapon */ /* reading item/weapon */
@ -1206,7 +1208,7 @@ parse_equipment(xmlDocPtr doc)
xmlXPathContextPtr xpath = xmlXPathNewContext(doc); xmlXPathContextPtr xpath = xmlXPathNewContext(doc);
xmlXPathObjectPtr xpathRaces; xmlXPathObjectPtr xpathRaces;
/* reading eressea/races/race */ /* reading eressea/equipment/set */
xpathRaces = xmlXPathEvalExpression(BAD_CAST "/eressea/equipment/set", xpath); xpathRaces = xmlXPathEvalExpression(BAD_CAST "/eressea/equipment/set", xpath);
if (xpathRaces->nodesetval) { if (xpathRaces->nodesetval) {
xmlNodeSetPtr nsetRaces = xpathRaces->nodesetval; xmlNodeSetPtr nsetRaces = xpathRaces->nodesetval;

View File

@ -2,7 +2,7 @@
+-------------------+ +-------------------+
| | Enno Rehling <enno@eressea.de> | | Enno Rehling <enno@eressea.de>
| Eressea PBEM host | Christian Schlittchen <corwin@amber.kn-bremen.de> | Eressea PBEM host | Christian Schlittchen <corwin@amber.kn-bremen.de>
| (c) 1998 - 2004 | Katja Zedel <katze@felidae.kn-bremen.de> | (c) 1998 - 2007 | Katja Zedel <katze@felidae.kn-bremen.de>
| | Henning Peters <faroul@beyond.kn-bremen.de> | | Henning Peters <faroul@beyond.kn-bremen.de>
+-------------------+ +-------------------+

View File

@ -17,6 +17,7 @@
#include "log.h" #include "log.h"
#include "goodies.h" #include "goodies.h"
#include "umlaut.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -44,6 +45,8 @@ find_locale(const char * name)
return l; return l;
} }
static int nextlocaleindex = 0;
locale * locale *
make_locale(const char * name) make_locale(const char * name)
{ {
@ -57,6 +60,8 @@ make_locale(const char * name)
l->hashkey = hkey; l->hashkey = hkey;
l->name = strdup(name); l->name = strdup(name);
l->next = locales; l->next = locales;
l->index = nextlocaleindex++;
assert(nextlocaleindex<=MAXLOCALES);
locales = l; locales = l;
if (default_locale==NULL) default_locale = l; if (default_locale==NULL) default_locale = l;
return l; return l;
@ -219,3 +224,22 @@ nextlocale(const struct locale * lang)
{ {
return lang->next; return lang->next;
} }
typedef struct lstr {
tnode tokens[UT_MAX];
} lstr;
static lstr lstrs[MAXLOCALES];
struct tnode *
get_translations(const struct locale * lang, int index)
{
static struct lstr * lnames = NULL;
static const struct locale * lastlang = NULL;
assert(lang->index<MAXLOCALES || "you have to increase MAXLOCALES and recompile");
if (lang->index<MAXLOCALES) {
return lstrs[lang->index].tokens+index;
}
return lstrs[0].tokens+index;
}

View File

@ -42,6 +42,19 @@ extern struct locale * default_locale;
extern struct locale * locales; extern struct locale * locales;
extern struct locale * nextlocale(const struct locale * lang); extern struct locale * nextlocale(const struct locale * lang);
enum {
UT_PARAMS,
UT_KEYWORDS,
UT_SKILLS,
UT_RACES,
UT_OPTIONS,
UT_DIRECTIONS,
UT_ARCHETYPES,
UT_MAX
};
struct tnode * get_translations(const struct locale * lang, int index);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -5,6 +5,7 @@
* feel that you need to include it, it's a sure sign that you're trying to * feel that you need to include it, it's a sure sign that you're trying to
* do something BAD. */ * do something BAD. */
#define MAXLOCALES 3
#define SMAXHASH 2048 #define SMAXHASH 2048
typedef struct locale_str { typedef struct locale_str {
unsigned int hashkey; unsigned int hashkey;
@ -14,6 +15,7 @@ typedef struct locale_str {
} locale_str; } locale_str;
typedef struct locale { typedef struct locale {
int index;
struct locale * next; struct locale * next;
unsigned int hashkey; unsigned int hashkey;
const char * name; const char * name;

View File

@ -54,6 +54,7 @@
#endif #endif
/* gamecode includes */ /* gamecode includes */
#include <gamecode/archetype.h>
#include <gamecode/economy.h> #include <gamecode/economy.h>
#include <gamecode/items.h> #include <gamecode/items.h>
#include <gamecode/laws.h> #include <gamecode/laws.h>
@ -223,6 +224,7 @@ game_init(void)
register_ships(); register_ships();
register_itemfunctions(); register_itemfunctions();
register_spells(); register_spells();
register_archetypes();
#ifdef DUNGEON_MODULE #ifdef DUNGEON_MODULE
register_dungeon(); register_dungeon();
#endif #endif
@ -245,6 +247,7 @@ game_init(void)
init_locales(); init_locales();
/* init_resources(); must be done inside the xml-read, because requirements use items */ /* init_resources(); must be done inside the xml-read, because requirements use items */
init_archetypes();
init_attributes(); init_attributes();
init_races(); init_races();
init_itemtypes(); init_itemtypes();

115
src/res/rts.xml Normal file
View File

@ -0,0 +1,115 @@
<?xml version="1.0"?>
<eressea xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="messages.xml"/>
<!-- Localization -->
<xi:include href="de/strings.xml"/>
<xi:include href="en/strings.xml"/>
<xi:include href="resources.xml"/>
<xi:include href="spoils.xml"/>
<xi:include href="races.xml"/>
<xi:include href="prefixes.xml"/>
<xi:include href="ships.xml"/>
<xi:include href="rts/buildings.xml"/>
<xi:include href="rts/units.xml"/>
<xi:include href="eressea/calendar.xml"/>
<xi:include href="equipment.xml"/>
<xi:include href="spells.xml"/>
<xi:include href="terrains.xml"/>
<xi:include href="dungeons.xml"/>
<game name="Eressea" welcome="eressea" learningbydoing="0.0">
<!-- Game specific settings -->
<order name="MEINUNG" disable="yes"/>
<order name="MAGIEGEBIET" disable="yes"/>
<order name="LEHREN" disable="yes"/>
<order name="SPIONIEREN" disable="yes"/>
<order name="SABOTIEREN" disable="yes"/>
<order name="ARBEITEN" disable="yes"/>
<order name="KRIEG" disable="yes"/>
<order name="FRIEDEN" disable="yes"/>
<order name="FORSCHEN" disable="yes"/>
<order name="TARNEN" disable="yes"/>
<order name="TREIBEN" disable="yes"/>
<order name="UNTERHALTEN" disable="yes"/>
<order name="KAUFEN" disable="yes"/>
<order name="VERKAUFEN" disable="yes"/>
<order name="ZUECHTEN" disable="yes"/>
<order name="NEUSTART" disable="yes"/>
<order name="OPFERE" disable="yes"/>
<order name="LIEFERE" disable="yes"/>
<order name="BETEN" disable="yes"/>
<order name="JIHAD" disable="yes"/>
<order name="GM" disable="yes"/>
<order name="INFO" disable="yes"/>
<order name="WERWESEN" disable="yes"/>
<order name="ALLIANZ" disable="yes"/>
<order name="XONTORMIA" disable="yes"/>
<order name="SYNONYM" disable="yes"/>
<skill name="alchemy" enable="true"/>
<skill name="crossbow" enable="true"/>
<skill name="mining" enable="true"/>
<skill name="bow" enable="true"/>
<skill name="building" enable="true"/>
<skill name="trade" enable="false"/>
<skill name="forestry" enable="true"/>
<skill name="catapult" enable="true"/>
<skill name="herbalism" enable="true"/>
<skill name="magic" enable="true"/>
<skill name="training" enable="true"/>
<skill name="riding" enable="true"/>
<skill name="armorer" enable="true"/>
<skill name="shipcraft" enable="true"/>
<skill name="melee" enable="true"/>
<skill name="sailing" enable="true"/>
<skill name="polearm" enable="true"/>
<skill name="espionage" enable="false"/>
<skill name="quarrying" enable="true"/>
<skill name="roadwork" enable="true"/>
<skill name="tactics" enable="false"/>
<skill name="stealth" enable="false"/>
<skill name="entertainment" enable="false"/>
<skill name="weaponsmithing" enable="true"/>
<skill name="cartmaking" enable="true"/>
<skill name="perception" enable="false"/>
<skill name="taxation" enable="false"/>
<skill name="stamina" enable="true"/>
<skill name="unarmed" enable="true"/>
<param name="recruit.classic" value="0"/>
<param name="recruit.archetypes" value="1"/>
<param name="study.newskills" value="false"/>
<param name="entertain.base" value="0"/>
<param name="entertain.perlevel" value="20"/>
<param name="nmr.timeout" value="4"/>
<param name="nmr.removenewbie" value="10"/>
<param name="GiveRestriction" value="3"/>
<param name="hunger.long" value="1"/>
<param name="rules.check_overload" value="0"/>
</game>
<xi:include href="eressea/de/strings.xml"/>
<xi:include href="eressea/en/strings.xml"/>
<xi:include href="eressea/races.xml"/>
<xi:include href="eressea/items.xml"/>
<xi:include href="eressea/artrewards.xml"/>
<xi:include href="eressea/dungeons.xml"/>
<xi:include href="eressea/temple.xml"/>
<strings>
<string name="mailto">
<text locale="de">eressea-server@eressea.upb.de</text>
<text locale="en">eressea-server@eressea.upb.de</text>
</string>
<string name="newbie_info_1">
<text locale="de">Bitte denke daran, deine Befehle mit dem Betreff
ERESSEA BEFEHLE an eressea-server@eressea.upb.de zu senden.</text>
<text locale="en">Remember to send your orders to
eressea-server@eressea.upb.de with the subject ERESSEA ORDERS.</text>
</string>
<string name="mailcmd">
<text locale="de">ERESSEA BEFEHLE</text>
<text locale="en">ERESSEA ORDERS</text>
</string>
</strings>
</eressea>

38
src/res/rts/buildings.xml Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<buildings>
<building name="castle" capacity="1">
<function name="name" value="castle_name"/>
<construction skill="building" minskill="1" maxsize="2" reqsize="1">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="1" maxsize="8" reqsize="1">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="2" maxsize="40" reqsize="1">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="3" maxsize="200" reqsize="1">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="4" maxsize="1000" reqsize="1">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="5" maxsize="5000" reqsize="1">
<requirement type="stone" quantity="1"/>
</construction>
<construction skill="building" minskill="6" reqsize="1">
<requirement type="stone" quantity="1"/>
</construction>
</building>
<building name="barracks">
<construction skill="building" minskill="1" maxsize="10" reqsize="1">
<requirement type="stone" quantity="1"/>
<requirement type="log" quantity="1"/>
<requirement type="money" quantity="100"/>
</construction>
</building>
</buildings>

27
src/res/rts/equipment.xml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<equipment>
<!-- archetypes -->
<set name="pikeman">
<skill name="polearm" level="1"/>
<skill name="stamina" level="1"/>
</set>
<set name="swordsman">
<skill name="melee" level="1"/>
<skill name="stamina" level="1"/>
</set>
<set name="knight">
<skill name="melee" level="3"/>
<skill name="polearm" level="3"/>
<skill name="stamina" level="3"/>
<skill name="riding" level="3"/>
</set>
<set name="craftsman">
<skill name="building" level="1"/>
</set>
</equipment>

30
src/res/rts/units.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<archetypes>
<archetype name="swordsman" building="barracks" cost="10">
<construction>
<requirement type="money" quantity="100"/>
</construction>
</building>
<archetype name="pikeman" building="barracks" cost="10">
<construction>
<requirement type="money" quantity="100"/>
</construction>
</building>
<archetype name="knight" building="barracks" cost="100">
<construction>
<requirement type="money" quantity="1000"/>
<requirement type="laen" quantity="1"/>
</construction>
</building>
<archetype name="craftsman" building="castle" cost="10">
<construction>
<requirement type="money" quantity="100"/>
</construction>
</building>
</archetypes>

24
src/rts.txt Normal file
View File

@ -0,0 +1,24 @@
Aenderungen fuer Eressea RTS:
* Befehle abgeschafft:
ARBEITEN, LEHREN, SPIONIEREN, SABOTIEREN, KRIEG, FRIEDEN, FORSCHEN, TARNEN, TREIBEN, UNTERHALTEN, KAUFEN, VERKAUFEN, ZUECHTEN, LIEFERE, MEINUNG, MAGIEGEBIET, NEUSTART, OPFERE, BETEN, JIHAD, INFO, GM, WERWESEN, ALLIANZ, XONTORMIA, SYNONYM
* Learning by doing abgeschafft.
* Skills disabled:
espionage, entertainment, taxation, tactics, stealth, trade, perception
* Alle Gebaeude entfernt, mit Ausnahme von Burgen
* Keine NPC-Monster
* Einheiten koennen ueber ihre Grundskills hinaus keine neuen Skills lernen
Offene Fragen:
* Soll es eine Maximalanzahl Einheiten geben?

99
src/scripts/rts-run.lua Normal file
View File

@ -0,0 +1,99 @@
function loadscript(name)
local script = scriptpath .. "/" .. name
print("- loading " .. script)
if pcall(dofile, script)==0 then
print("Could not load " .. script)
end
end
function change_locales()
-- local localechange = { }
local localechange = { de = { "bLub" } }
for loc, flist in localechange do
for index, name in flist do
f = get_faction(atoi36(name))
if f ~= nil then
f.locale = loc
print("LOCALECHANGE ", f, loc)
end
end
end
end
function run_scripts()
scripts = {
"spells.lua",
"extensions.lua",
"familiars.lua",
"write_emails.lua"
}
for index in scripts do
loadscript(scripts[index])
end
end
function process(orders)
-- initialize starting equipment for new players
equipment_setitem("new_faction", "log", "30");
equipment_setitem("new_faction", "stone", "30");
equipment_setitem("new_faction", "money", "4200");
file = "" .. get_turn()
if read_game(file)~=0 then
print("could not read game")
return -1
end
init_summary()
-- run the turn:
if read_orders(orders) ~= 0 then
print("could not read " .. orders)
return -1
end
run_scripts()
plan_monsters()
local nmrs = get_nmrs(1)
if nmrs >= 70 then
print("Shit. More than 70 factions with 1 NMR (" .. nmrs .. ")")
write_summary()
return -1
end
print (nmrs .. " Factions with 1 NMR")
process_orders()
-- post-turn updates:
update_guards()
update_scores()
change_locales()
-- use newfactions file to place out new players
autoseed(basepath .. "/newfactions", false)
write_passwords()
write_reports()
write_emails()
write_summary()
file = "" .. get_turn()
if write_game(file)~=0 then
print("could not write game")
return -1
end
end
--
-- main body of script
--
-- orderfile: contains the name of the orders.
if orderfile==nil then
print "you must specify an orderfile"
else
process(orderfile)
end