2010-08-08 08:05:54 +02:00
|
|
|
|
/*
|
|
|
|
|
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
|
|
|
|
|
Katja Zedel <katze@felidae.kn-bremen.de
|
|
|
|
|
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
|
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
|
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
#include <platform.h>
|
|
|
|
|
#include <kernel/config.h>
|
|
|
|
|
|
|
|
|
|
/* attributes includes */
|
|
|
|
|
#include <attributes/reduceproduction.h>
|
|
|
|
|
#include <attributes/gm.h>
|
|
|
|
|
|
|
|
|
|
/* kernel includes */
|
|
|
|
|
#include "alliance.h"
|
|
|
|
|
#include "alchemy.h"
|
|
|
|
|
#include "battle.h"
|
|
|
|
|
#include "connection.h"
|
|
|
|
|
#include "building.h"
|
|
|
|
|
#include "calendar.h"
|
|
|
|
|
#include "curse.h"
|
|
|
|
|
#include "faction.h"
|
|
|
|
|
#include "group.h"
|
|
|
|
|
#include "item.h"
|
|
|
|
|
#include "magic.h"
|
|
|
|
|
#include "message.h"
|
|
|
|
|
#include "move.h"
|
|
|
|
|
#include "names.h"
|
|
|
|
|
#include "objtypes.h"
|
|
|
|
|
#include "order.h"
|
|
|
|
|
#include "plane.h"
|
|
|
|
|
#include "pool.h"
|
|
|
|
|
#include "race.h"
|
|
|
|
|
#include "region.h"
|
|
|
|
|
#include "save.h"
|
|
|
|
|
#include "ship.h"
|
|
|
|
|
#include "skill.h"
|
|
|
|
|
#include "terrain.h"
|
|
|
|
|
#include "unit.h"
|
|
|
|
|
|
|
|
|
|
/* util includes */
|
|
|
|
|
#include <util/attrib.h>
|
|
|
|
|
#include <util/base36.h>
|
|
|
|
|
#include <util/crmessage.h>
|
|
|
|
|
#include <util/encoding.h>
|
|
|
|
|
#include <util/event.h>
|
|
|
|
|
#include <util/functions.h>
|
|
|
|
|
#include <util/language.h>
|
|
|
|
|
#include <util/log.h>
|
|
|
|
|
#include <util/lists.h>
|
|
|
|
|
#include <util/parser.h>
|
|
|
|
|
#include <util/rand.h>
|
|
|
|
|
#include <util/rng.h>
|
|
|
|
|
#include <util/sql.h>
|
|
|
|
|
#include <util/translation.h>
|
|
|
|
|
#include <util/umlaut.h>
|
|
|
|
|
#include <util/bsdstring.h>
|
|
|
|
|
#include <util/unicode.h>
|
|
|
|
|
|
|
|
|
|
#include <iniparser/iniparser.h>
|
|
|
|
|
|
|
|
|
|
/* libc includes */
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#define PTRIES 0 /* it turns out they are slow :-( */
|
|
|
|
|
#if PTRIES
|
|
|
|
|
#include <util/patricia.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
struct settings global = {
|
|
|
|
|
"Eressea", /* gamename */
|
|
|
|
|
};
|
|
|
|
|
FILE *logfile;
|
|
|
|
|
FILE *updatelog;
|
|
|
|
|
const struct race * new_race[MAXRACES];
|
|
|
|
|
boolean sqlpatch = false;
|
|
|
|
|
boolean battledebug = false;
|
|
|
|
|
int turn = 0;
|
|
|
|
|
|
|
|
|
|
#if XECMD_MODULE
|
|
|
|
|
attrib_type at_xontormiaexpress = {
|
|
|
|
|
"xontormiaexpress",
|
|
|
|
|
DEFAULT_INIT,
|
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
|
DEFAULT_AGE,
|
|
|
|
|
a_writeint,
|
|
|
|
|
a_readint,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
NewbieImmunity(void) {
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "NewbieImmunity", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
IsImmune(const faction * f)
|
|
|
|
|
{
|
|
|
|
|
return !fval(f, FFL_NPC) && f->age < NewbieImmunity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
MaxAge(void) {
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "MaxAge", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ally_flag(const char * s, int help_mask)
|
|
|
|
|
{
|
|
|
|
|
if ((help_mask&HELP_MONEY) && strcmp(s, "money")==0) return HELP_MONEY;
|
|
|
|
|
if ((help_mask&HELP_FIGHT) && strcmp(s, "fight")==0) return HELP_FIGHT;
|
|
|
|
|
if ((help_mask&HELP_GIVE) && strcmp(s, "give")==0) return HELP_GIVE;
|
|
|
|
|
if ((help_mask&HELP_GUARD) && strcmp(s, "guard")==0) return HELP_GUARD;
|
|
|
|
|
if ((help_mask&HELP_FSTEALTH) && strcmp(s, "stealth")==0) return HELP_FSTEALTH;
|
|
|
|
|
if ((help_mask&HELP_TRAVEL) && strcmp(s, "travel")==0) return HELP_TRAVEL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
ExpensiveMigrants(void)
|
|
|
|
|
{
|
|
|
|
|
int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "study.expensivemigrants", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
/** Specifies automatic alliance modes.
|
|
|
|
|
* If this returns a value then the bits set are immutable between alliance
|
|
|
|
|
* partners (faction::alliance) and cannot be changed with the HELP command.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
AllianceAuto(void)
|
|
|
|
|
{
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
const char * str = get_param(global.parameters, "alliance.auto");
|
|
|
|
|
value = 0;
|
|
|
|
|
if (str!=NULL) {
|
|
|
|
|
char * sstr = strdup(str);
|
|
|
|
|
char * tok = strtok(sstr, " ");
|
|
|
|
|
while (tok) {
|
|
|
|
|
value |= ally_flag(tok, -1);
|
|
|
|
|
tok = strtok(NULL, " ");
|
|
|
|
|
}
|
|
|
|
|
free(sstr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return value & HelpMask();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Limits the available help modes
|
|
|
|
|
* The bitfield returned by this function specifies the available help modes
|
|
|
|
|
* in this game (so you can, for example, disable HELP GIVE globally).
|
|
|
|
|
* Disabling a status will disable the command sequence entirely (order parsing
|
|
|
|
|
* uses this function).
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
HelpMask(void)
|
|
|
|
|
{
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
const char * str = get_param(global.parameters, "rules.help.mask");
|
|
|
|
|
value = 0;
|
|
|
|
|
if (str!=NULL) {
|
|
|
|
|
char * sstr = strdup(str);
|
|
|
|
|
char * tok = strtok(sstr, " ");
|
|
|
|
|
while (tok) {
|
|
|
|
|
value |= ally_flag(tok, -1);
|
|
|
|
|
tok = strtok(NULL, " ");
|
|
|
|
|
}
|
|
|
|
|
free(sstr);
|
|
|
|
|
} else {
|
|
|
|
|
value = HELP_ALL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AllianceRestricted(void)
|
|
|
|
|
{
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
const char * str = get_param(global.parameters, "alliance.restricted");
|
|
|
|
|
value = 0;
|
|
|
|
|
if (str!=NULL) {
|
|
|
|
|
char * sstr = strdup(str);
|
|
|
|
|
char * tok = strtok(sstr, " ");
|
|
|
|
|
while (tok) {
|
|
|
|
|
value |= ally_flag(tok, -1);
|
|
|
|
|
tok = strtok(NULL, " ");
|
|
|
|
|
}
|
|
|
|
|
free(sstr);
|
|
|
|
|
}
|
|
|
|
|
value &= HelpMask();
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
LongHunger(const struct unit * u) {
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (u!=NULL) {
|
|
|
|
|
if (!fval(u, UFL_HUNGER)) return false;
|
|
|
|
|
#ifdef NEW_DAEMONHUNGER_RULE
|
|
|
|
|
if (u->race==new_race[RC_DAEMON]) return false;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "hunger.long", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
SkillCap(skill_t sk) {
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (sk==SK_MAGIC) return 0; /* no caps on magic */
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "skill.maxlevel", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
NMRTimeout(void) {
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "nmr.timeout", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
race_t
|
|
|
|
|
old_race(const struct race * rc)
|
|
|
|
|
{
|
|
|
|
|
race_t i;
|
|
|
|
|
for (i=0;i!=MAXRACES;++i) {
|
|
|
|
|
if (new_race[i]==rc) return i;
|
|
|
|
|
}
|
|
|
|
|
return NORACE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
helpmode helpmodes[] = {
|
|
|
|
|
{ "all", HELP_ALL },
|
|
|
|
|
{ "money", HELP_MONEY },
|
|
|
|
|
{ "fight", HELP_FIGHT },
|
|
|
|
|
{ "observe", HELP_OBSERVE },
|
|
|
|
|
{ "give", HELP_GIVE },
|
|
|
|
|
{ "guard", HELP_GUARD },
|
|
|
|
|
{ "stealth", HELP_FSTEALTH },
|
|
|
|
|
{ "travel", HELP_TRAVEL },
|
|
|
|
|
{ NULL, 0 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *directions[MAXDIRECTIONS+2] =
|
|
|
|
|
{
|
|
|
|
|
"northwest",
|
|
|
|
|
"northeast",
|
|
|
|
|
"east",
|
|
|
|
|
"southeast",
|
|
|
|
|
"southwest",
|
|
|
|
|
"west",
|
|
|
|
|
"",
|
|
|
|
|
"pause"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Returns the English name of the race, which is what the database uses.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
dbrace(const struct race * rc)
|
|
|
|
|
{
|
|
|
|
|
static char zText[32];
|
|
|
|
|
char * zPtr = zText;
|
|
|
|
|
|
|
|
|
|
/* the english names are all in ASCII, so we don't need to worry about UTF8 */
|
|
|
|
|
strcpy(zText, (const char*)LOC(find_locale("en"), rc_name(rc, 0)));
|
|
|
|
|
while (*zPtr) {
|
|
|
|
|
*zPtr = (char)(toupper(*zPtr));
|
|
|
|
|
++zPtr;
|
|
|
|
|
}
|
|
|
|
|
return zText;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *parameters[MAXPARAMS] =
|
|
|
|
|
{
|
|
|
|
|
"LOCALE",
|
|
|
|
|
"ALLES",
|
|
|
|
|
"JEDEM",
|
|
|
|
|
"BAUERN",
|
|
|
|
|
"BURG",
|
|
|
|
|
"EINHEIT",
|
|
|
|
|
"PRIVAT",
|
|
|
|
|
"HINTEN",
|
|
|
|
|
"KOMMANDO",
|
|
|
|
|
"KRAEUTER",
|
|
|
|
|
"NICHT",
|
|
|
|
|
"NAECHSTER",
|
|
|
|
|
"PARTEI",
|
|
|
|
|
"ERESSEA",
|
|
|
|
|
"PERSONEN",
|
|
|
|
|
"REGION",
|
|
|
|
|
"SCHIFF",
|
|
|
|
|
"SILBER",
|
|
|
|
|
"STRASSEN",
|
|
|
|
|
"TEMPORAERE",
|
|
|
|
|
"FLIEHE",
|
|
|
|
|
"GEBAEUDE",
|
|
|
|
|
"GIB", /* F<>r HELFE */
|
|
|
|
|
"KAEMPFE",
|
|
|
|
|
"DURCHREISE",
|
|
|
|
|
"BEWACHE",
|
|
|
|
|
"ZAUBER",
|
|
|
|
|
"PAUSE",
|
|
|
|
|
"VORNE",
|
|
|
|
|
"AGGRESSIV",
|
|
|
|
|
"DEFENSIV",
|
|
|
|
|
"STUFE",
|
|
|
|
|
"HELFE",
|
|
|
|
|
"FREMDES",
|
|
|
|
|
"AURA",
|
|
|
|
|
"UM",
|
|
|
|
|
"BEISTAND",
|
|
|
|
|
"GNADE",
|
|
|
|
|
"HINTER",
|
|
|
|
|
"VOR",
|
|
|
|
|
"ANZAHL",
|
|
|
|
|
"GEGENSTAENDE",
|
|
|
|
|
"TRAENKE",
|
|
|
|
|
"GRUPPE",
|
|
|
|
|
"PARTEITARNUNG",
|
|
|
|
|
"BAEUME",
|
|
|
|
|
"XEPOTION",
|
|
|
|
|
"XEBALLOON",
|
|
|
|
|
"XELAEN",
|
|
|
|
|
"ALLIANZ"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *keywords[MAXKEYWORDS] =
|
|
|
|
|
{
|
|
|
|
|
"//",
|
|
|
|
|
"BANNER",
|
|
|
|
|
"ARBEITEN",
|
|
|
|
|
"ATTACKIEREN",
|
|
|
|
|
"BEKLAUEN",
|
|
|
|
|
"BELAGERE",
|
|
|
|
|
"BENENNEN",
|
|
|
|
|
"BENUTZEN",
|
|
|
|
|
"BESCHREIBEN",
|
|
|
|
|
"BETRETEN",
|
|
|
|
|
"BEWACHEN",
|
|
|
|
|
"BOTSCHAFT",
|
|
|
|
|
"ENDE",
|
|
|
|
|
"FAHREN",
|
|
|
|
|
"NUMMER",
|
|
|
|
|
"KRIEG",
|
|
|
|
|
"FRIEDEN",
|
|
|
|
|
"FOLGEN",
|
|
|
|
|
"FORSCHEN",
|
|
|
|
|
"GIB",
|
|
|
|
|
"HELFEN",
|
|
|
|
|
"KAEMPFEN",
|
|
|
|
|
"KAMPFZAUBER",
|
|
|
|
|
"KAUFEN",
|
|
|
|
|
"KONTAKTIEREN",
|
|
|
|
|
"LEHREN",
|
|
|
|
|
"LERNEN",
|
|
|
|
|
"LIEFERE",
|
|
|
|
|
"MACHEN",
|
|
|
|
|
"NACH",
|
|
|
|
|
"PASSWORT",
|
|
|
|
|
"REKRUTIEREN",
|
|
|
|
|
"RESERVIEREN",
|
|
|
|
|
"ROUTE",
|
|
|
|
|
"SABOTIEREN",
|
|
|
|
|
"OPTION",
|
|
|
|
|
"SPIONIEREN",
|
|
|
|
|
"STIRB",
|
|
|
|
|
"TARNEN",
|
|
|
|
|
"TRANSPORTIEREN",
|
|
|
|
|
"TREIBEN",
|
|
|
|
|
"UNTERHALTEN",
|
|
|
|
|
"VERKAUFEN",
|
|
|
|
|
"VERLASSEN",
|
|
|
|
|
"VERGESSEN",
|
|
|
|
|
"ZAUBERE",
|
|
|
|
|
"ZEIGEN",
|
|
|
|
|
"ZERSTOEREN",
|
|
|
|
|
"ZUECHTEN",
|
|
|
|
|
"DEFAULT",
|
|
|
|
|
"URSPRUNG",
|
|
|
|
|
"EMAIL",
|
|
|
|
|
"PIRATERIE",
|
|
|
|
|
"NEUSTART",
|
|
|
|
|
"GRUPPE",
|
|
|
|
|
"OPFERE",
|
|
|
|
|
"BETEN",
|
|
|
|
|
"SORTIEREN",
|
|
|
|
|
"JIHAD",
|
|
|
|
|
"GM",
|
|
|
|
|
"INFO",
|
|
|
|
|
"PRAEFIX",
|
|
|
|
|
"PFLANZEN",
|
|
|
|
|
"WERWESEN",
|
|
|
|
|
"XONTORMIA",
|
|
|
|
|
"ALLIANZ",
|
|
|
|
|
"BEANSPRUCHEN",
|
|
|
|
|
"PROMOTION",
|
|
|
|
|
"BEZAHLEN",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *report_options[MAX_MSG] =
|
|
|
|
|
{
|
|
|
|
|
"Kampf",
|
|
|
|
|
"Ereignisse",
|
|
|
|
|
"Bewegung",
|
|
|
|
|
"Einkommen",
|
|
|
|
|
"Handel",
|
|
|
|
|
"Produktion",
|
|
|
|
|
"Orkvermehrung",
|
|
|
|
|
"Zauber",
|
|
|
|
|
"",
|
|
|
|
|
""
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *message_levels[ML_MAX] =
|
|
|
|
|
{
|
|
|
|
|
"Wichtig",
|
|
|
|
|
"Debug",
|
|
|
|
|
"Fehler",
|
|
|
|
|
"Warnungen",
|
|
|
|
|
"Infos"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *options[MAXOPTIONS] =
|
|
|
|
|
{
|
|
|
|
|
"AUSWERTUNG",
|
|
|
|
|
"COMPUTER",
|
|
|
|
|
"ZUGVORLAGE",
|
|
|
|
|
NULL,
|
|
|
|
|
"STATISTIK",
|
|
|
|
|
"DEBUG",
|
|
|
|
|
"ZIPPED",
|
|
|
|
|
"ZEITUNG", /* Option hat Sonderbehandlung! */
|
|
|
|
|
NULL,
|
|
|
|
|
"ADRESSEN",
|
|
|
|
|
"BZIP2",
|
|
|
|
|
"PUNKTE",
|
|
|
|
|
"SHOWSKCHANGE",
|
|
|
|
|
"XML"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
allied_skillcount(const faction * f, skill_t sk)
|
|
|
|
|
{
|
|
|
|
|
int num = 0;
|
|
|
|
|
alliance * a = f_get_alliance(f);
|
|
|
|
|
faction_list * members = a->members;
|
|
|
|
|
while (members!=NULL) {
|
|
|
|
|
num += count_skill(members->data, sk);
|
|
|
|
|
members=members->next;
|
|
|
|
|
}
|
|
|
|
|
return num;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
allied_skilllimit(const faction * f, skill_t sk)
|
|
|
|
|
{
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "alliance.skilllimit", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
init_maxmagicians(struct attrib *a)
|
|
|
|
|
{
|
|
|
|
|
a->data.i = MAXMAGICIANS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static attrib_type at_maxmagicians = {
|
|
|
|
|
"maxmagicians",
|
|
|
|
|
init_maxmagicians,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
a_writeint,
|
|
|
|
|
a_readint,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
init_npcfaction(struct attrib *a)
|
|
|
|
|
{
|
|
|
|
|
a->data.i = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static attrib_type at_npcfaction = {
|
|
|
|
|
"npcfaction",
|
|
|
|
|
init_npcfaction,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
a_writeint,
|
|
|
|
|
a_readint,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
max_magicians(const faction * f)
|
|
|
|
|
{
|
|
|
|
|
int m = MAXMAGICIANS;
|
|
|
|
|
attrib * a;
|
|
|
|
|
|
|
|
|
|
if ((a = a_find(f->attribs, &at_maxmagicians)) != NULL) {
|
|
|
|
|
m = a->data.i;
|
|
|
|
|
}
|
|
|
|
|
if (f->race == new_race[RC_ELF]) ++m;
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
skill_limit(faction * f, skill_t sk)
|
|
|
|
|
{
|
|
|
|
|
int m = INT_MAX;
|
|
|
|
|
int al = allied_skilllimit(f, sk);
|
|
|
|
|
if (al>0) {
|
|
|
|
|
if (sk!=SK_ALCHEMY && sk!=SK_MAGIC) return INT_MAX;
|
|
|
|
|
if (f_get_alliance(f)) {
|
|
|
|
|
int ac = listlen(f->alliance->members); /* number of factions */
|
|
|
|
|
int fl = (al+ac-1)/ac; /* faction limit, rounded up */
|
|
|
|
|
/* the faction limit may not be achievable because it would break the alliance-limit */
|
|
|
|
|
int sc = al - allied_skillcount(f, sk);
|
|
|
|
|
if (sc<=0) return 0;
|
|
|
|
|
return fl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
switch (sk) {
|
|
|
|
|
case SK_MAGIC:
|
|
|
|
|
m = max_magicians(f);
|
|
|
|
|
break;
|
|
|
|
|
case SK_ALCHEMY:
|
|
|
|
|
m = MAXALCHEMISTS;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
count_skill(faction * f, skill_t sk)
|
|
|
|
|
{
|
|
|
|
|
int n = 0;
|
|
|
|
|
unit *u;
|
|
|
|
|
|
|
|
|
|
for (u = f->units; u; u = u->nextF) {
|
|
|
|
|
if (has_skill(u, sk)) {
|
|
|
|
|
if (!is_familiar(u)) n += u->number;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int verbosity = 0;
|
|
|
|
|
|
|
|
|
|
FILE *debug;
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ShipSpeedBonus(const unit * u)
|
|
|
|
|
{
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (level==-1) {
|
|
|
|
|
level = get_param_int(global.parameters, "movement.shipspeed.skillbonus", 0);
|
|
|
|
|
}
|
|
|
|
|
if (level>0) {
|
|
|
|
|
ship * sh = u->ship;
|
|
|
|
|
int skl = effskill(u, SK_SAILING);
|
|
|
|
|
int minsk = (sh->type->cptskill+1)/2;
|
|
|
|
|
return (skl-minsk)/level;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
shipspeed(const ship * sh, const unit * u)
|
|
|
|
|
{
|
|
|
|
|
double k = sh->type->range;
|
|
|
|
|
static const curse_type * stormwind_ct, * nodrift_ct;
|
|
|
|
|
static boolean init;
|
|
|
|
|
attrib *a;
|
|
|
|
|
curse *c;
|
|
|
|
|
|
|
|
|
|
if (!init) {
|
|
|
|
|
init = true;
|
|
|
|
|
stormwind_ct = ct_find("stormwind");
|
|
|
|
|
nodrift_ct = ct_find("nodrift");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(u->ship==sh);
|
|
|
|
|
assert(sh->type->construction->improvement==NULL); /* sonst ist construction::size nicht ship_type::maxsize */
|
|
|
|
|
if (sh->size!=sh->type->construction->maxsize) return 0;
|
|
|
|
|
|
|
|
|
|
if( curse_active(get_curse(sh->attribs, stormwind_ct)))
|
|
|
|
|
k *= 2;
|
|
|
|
|
if( curse_active(get_curse(sh->attribs, nodrift_ct)))
|
|
|
|
|
k += 1;
|
|
|
|
|
|
|
|
|
|
if (u->faction->race == u->race) {
|
|
|
|
|
/* race bonus for this faction? */
|
|
|
|
|
if (fval(u->race, RCF_SHIPSPEED)) {
|
|
|
|
|
k += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
k += ShipSpeedBonus(u);
|
|
|
|
|
|
|
|
|
|
a = a_find(sh->attribs, &at_speedup);
|
|
|
|
|
while (a != NULL && a->type==&at_speedup) {
|
|
|
|
|
k += a->data.sa[0];
|
|
|
|
|
a = a->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c = get_curse(sh->attribs, ct_find("shipspeedup"));
|
|
|
|
|
while(c) {
|
|
|
|
|
k += curse_geteffect(c);
|
|
|
|
|
c = c->nexthash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef SHIPSPEED
|
|
|
|
|
k *= SHIPSPEED;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef SHIPDAMAGE
|
|
|
|
|
if (sh->damage) k = (k * (sh->size * DAMAGE_SCALE - sh->damage) + sh->size * DAMAGE_SCALE- 1) / (sh->size*DAMAGE_SCALE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return (int)k;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define FMAXHASH 2039
|
|
|
|
|
faction * factionhash[FMAXHASH];
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
fhash(faction * f)
|
|
|
|
|
{
|
|
|
|
|
int index = f->no % FMAXHASH;
|
|
|
|
|
f->nexthash = factionhash[index];
|
|
|
|
|
factionhash[index] = f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
funhash(faction * f)
|
|
|
|
|
{
|
|
|
|
|
int index = f->no % FMAXHASH;
|
|
|
|
|
faction ** fp = factionhash+index;
|
|
|
|
|
while (*fp && (*fp)!=f) fp = &(*fp)->nexthash;
|
|
|
|
|
*fp = f->nexthash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static faction *
|
|
|
|
|
ffindhash(int no)
|
|
|
|
|
{
|
|
|
|
|
int index = no % FMAXHASH;
|
|
|
|
|
faction * f = factionhash[index];
|
|
|
|
|
while (f && f->no!=no) f = f->nexthash;
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
verify_data(void)
|
|
|
|
|
{
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
int lf = -1;
|
|
|
|
|
faction *f;
|
|
|
|
|
unit *u;
|
|
|
|
|
int mage, alchemist;
|
|
|
|
|
|
|
|
|
|
if (verbosity>=1) puts(" - <20>berpr<70>fe Daten auf Korrektheit...");
|
|
|
|
|
|
|
|
|
|
list_foreach(faction, factions, f) {
|
|
|
|
|
mage = 0;
|
|
|
|
|
alchemist = 0;
|
|
|
|
|
for (u=f->units;u;u=u->nextF) {
|
|
|
|
|
if (eff_skill(u, SK_MAGIC, u->region)) {
|
|
|
|
|
mage += u->number;
|
|
|
|
|
}
|
|
|
|
|
if (eff_skill(u, SK_ALCHEMY, u->region))
|
|
|
|
|
alchemist += u->number;
|
|
|
|
|
if (u->number > UNIT_MAXSIZE) {
|
|
|
|
|
if (lf != f->no) {
|
|
|
|
|
lf = f->no;
|
|
|
|
|
log_stdio(stdout, "Partei %s:\n", factionid(f));
|
|
|
|
|
}
|
|
|
|
|
log_warning(("Einheit %s hat %d Personen\n", unitid(u), u->number));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (f->no != 0 && ((mage > 3 && f->race != new_race[RC_ELF]) || mage > 4))
|
|
|
|
|
log_error(("Partei %s hat %d Magier.\n", factionid(f), mage));
|
|
|
|
|
if (alchemist > 3)
|
|
|
|
|
log_error(("Partei %s hat %d Alchemisten.\n", factionid(f), alchemist));
|
|
|
|
|
}
|
|
|
|
|
list_next(f);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
distribute(int old, int new_value, int n)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int t;
|
|
|
|
|
assert(new_value <= old);
|
|
|
|
|
|
|
|
|
|
if (old == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
t = (n / old) * new_value;
|
|
|
|
|
for (i = (n % old); i; i--)
|
|
|
|
|
if (rng_int() % old < new_value)
|
|
|
|
|
t++;
|
|
|
|
|
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
change_hitpoints (unit * u, int value)
|
|
|
|
|
{
|
|
|
|
|
int hp = u->hp;
|
|
|
|
|
|
|
|
|
|
hp += value;
|
|
|
|
|
|
|
|
|
|
/* Jede Person ben<65>tigt mindestens 1 HP */
|
|
|
|
|
if (hp < u->number){
|
|
|
|
|
if (hp < 0){ /* Einheit tot */
|
|
|
|
|
hp = 0;
|
|
|
|
|
}
|
|
|
|
|
scale_number(u, hp);
|
|
|
|
|
}
|
|
|
|
|
u->hp = hp;
|
|
|
|
|
return hp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
atoip(const char *s)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
n = atoi (s);
|
|
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
|
n = 0;
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
region *
|
|
|
|
|
findunitregion (const unit * su)
|
|
|
|
|
{
|
|
|
|
|
#ifndef SLOW_REGION
|
|
|
|
|
return su->region;
|
|
|
|
|
#else
|
|
|
|
|
region *r;
|
|
|
|
|
const unit *u;
|
|
|
|
|
|
|
|
|
|
for (r = regions; r; r = r->next) {
|
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
if (su == u) {
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This should never happen */
|
|
|
|
|
assert (!"Die unit wurde nicht gefunden");
|
|
|
|
|
|
|
|
|
|
return (region *) NULL;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
effskill(const unit * u, skill_t sk)
|
|
|
|
|
{
|
|
|
|
|
return eff_skill(u, sk, u->region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
eff_stealth(const unit * u, const region * r)
|
|
|
|
|
{
|
|
|
|
|
int e = 0;
|
|
|
|
|
|
|
|
|
|
/* Auf Schiffen keine Tarnung! */
|
|
|
|
|
if (!u->ship && skill_enabled[SK_STEALTH]) {
|
|
|
|
|
e = eff_skill (u, SK_STEALTH, r);
|
|
|
|
|
|
|
|
|
|
if (fval(u, UFL_STEALTH)) {
|
|
|
|
|
int es = u_geteffstealth(u);
|
|
|
|
|
if (es >=0 && es < e) return es;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
unit_has_cursed_item(unit *u)
|
|
|
|
|
{
|
|
|
|
|
item * itm = u->items;
|
|
|
|
|
while (itm) {
|
|
|
|
|
if (fval(itm->type, ITF_CURSED) && itm->number>0) return true;
|
|
|
|
|
itm=itm->next;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
init_gms(void)
|
|
|
|
|
{
|
|
|
|
|
faction * f;
|
|
|
|
|
|
|
|
|
|
for (f=factions;f;f=f->next) {
|
|
|
|
|
const attrib * a = a_findc(f->attribs, &at_gm);
|
|
|
|
|
|
|
|
|
|
if (a!=NULL) fset(f, FFL_GM);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
autoalliance(const plane * pl, const faction * sf, const faction * f2)
|
|
|
|
|
{
|
|
|
|
|
static boolean init = false;
|
|
|
|
|
if (!init) {
|
|
|
|
|
init_gms();
|
|
|
|
|
init = true;
|
|
|
|
|
}
|
|
|
|
|
if (pl && (pl->flags & PFL_FRIENDLY)) return HELP_ALL;
|
|
|
|
|
/* if f2 is a gm in this plane, everyone has an auto-help to it */
|
|
|
|
|
if (fval(f2, FFL_GM)) {
|
|
|
|
|
attrib * a = a_find(f2->attribs, &at_gm);
|
|
|
|
|
|
|
|
|
|
while (a) {
|
|
|
|
|
const plane * p = (const plane*)a->data.v;
|
|
|
|
|
if (p==pl) return HELP_ALL;
|
|
|
|
|
a=a->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (f_get_alliance(sf)!=NULL && AllianceAuto()) {
|
|
|
|
|
if (sf->alliance==f2->alliance) return AllianceAuto();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ally_mode(const ally * sf, int mode)
|
|
|
|
|
{
|
|
|
|
|
if (sf==NULL) return 0;
|
|
|
|
|
return sf->status & mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
alliedgroup(const struct plane * pl, const struct faction * f,
|
|
|
|
|
const struct faction * f2, const struct ally * sf, int mode)
|
|
|
|
|
{
|
|
|
|
|
while (sf && sf->faction!=f2) sf=sf->next;
|
|
|
|
|
if (sf==NULL) {
|
|
|
|
|
mode = mode & autoalliance(pl, f, f2);
|
|
|
|
|
}
|
|
|
|
|
mode = ally_mode(sf, mode) | (mode & autoalliance(pl, f, f2));
|
|
|
|
|
if (AllianceRestricted()) {
|
|
|
|
|
if (a_findc(f->attribs, &at_npcfaction)) {
|
|
|
|
|
return mode;
|
|
|
|
|
}
|
|
|
|
|
if (a_findc(f2->attribs, &at_npcfaction)) {
|
|
|
|
|
return mode;
|
|
|
|
|
}
|
|
|
|
|
if (f->alliance!=f2->alliance) {
|
|
|
|
|
mode &= ~AllianceRestricted();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
alliedfaction(const struct plane * pl, const struct faction * f,
|
|
|
|
|
const struct faction * f2, int mode)
|
|
|
|
|
{
|
|
|
|
|
return alliedgroup(pl, f, f2, f->allies, mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Die Gruppe von Einheit u hat helfe zu f2 gesetzt. */
|
|
|
|
|
int
|
|
|
|
|
alliedunit(const unit * u, const faction * f2, int mode)
|
|
|
|
|
{
|
|
|
|
|
ally * sf;
|
|
|
|
|
int automode;
|
|
|
|
|
|
|
|
|
|
assert(u->region); /* the unit should be in a region, but it's possible that u->number==0 (TEMP units) */
|
|
|
|
|
if (u->faction == f2) return mode;
|
|
|
|
|
if (u->faction != NULL && f2!=NULL) {
|
|
|
|
|
plane * pl;
|
|
|
|
|
|
|
|
|
|
if (mode&HELP_FIGHT) {
|
|
|
|
|
if ((u->flags&UFL_DEFENDER) || (u->faction->flags&FFL_DEFENDER)) {
|
|
|
|
|
faction * owner = region_get_owner(u->region);
|
|
|
|
|
/* helps the owner of the region */
|
|
|
|
|
if (owner==f2) {
|
|
|
|
|
return HELP_FIGHT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pl = rplane(u->region);
|
|
|
|
|
automode = mode & autoalliance(pl, u->faction, f2);
|
|
|
|
|
|
|
|
|
|
if (pl!=NULL && (pl->flags & PFL_NOALLIANCES))
|
|
|
|
|
mode = (mode & automode) | (mode & HELP_GIVE);
|
|
|
|
|
|
|
|
|
|
sf = u->faction->allies;
|
|
|
|
|
if (fval(u, UFL_GROUP)) {
|
|
|
|
|
const attrib * a = a_findc(u->attribs, &at_group);
|
|
|
|
|
if (a!=NULL) sf = ((group*)a->data.v)->allies;
|
|
|
|
|
}
|
|
|
|
|
return alliedgroup(pl, u->faction, f2, sf, mode);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
seefaction(const faction * f, const region * r, const unit * u, int modifier)
|
|
|
|
|
{
|
|
|
|
|
if (((f == u->faction) || !fval(u, UFL_ANON_FACTION)) && cansee(f, r, u, modifier))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
cansee(const faction * f, const region * r, const unit * u, int modifier)
|
|
|
|
|
/* r kann != u->region sein, wenn es um durchreisen geht */
|
|
|
|
|
/* und es muss niemand aus f in der region sein, wenn sie vom Turm
|
|
|
|
|
* erblickt wird */
|
|
|
|
|
{
|
|
|
|
|
int stealth, rings;
|
|
|
|
|
unit *u2 = r->units;
|
|
|
|
|
static const item_type * itype_grail;
|
|
|
|
|
static boolean init;
|
|
|
|
|
|
|
|
|
|
if (!init) {
|
|
|
|
|
init = true;
|
|
|
|
|
itype_grail = it_find("grail");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->faction == f || omniscient(f)) {
|
|
|
|
|
return true;
|
|
|
|
|
} else if (fval(u->race, RCF_INVISIBLE)) {
|
|
|
|
|
return false;
|
|
|
|
|
} else if (u->number == 0) {
|
|
|
|
|
attrib *a = a_find(u->attribs, &at_creator);
|
|
|
|
|
if (a) { /* u is an empty temporary unit. In this special case
|
|
|
|
|
we look at the creating unit. */
|
|
|
|
|
u = (unit *)a->data.v;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (leftship(u)) return true;
|
|
|
|
|
if (itype_grail!=NULL && i_get(u->items, itype_grail)) return true;
|
|
|
|
|
|
|
|
|
|
while (u2 && u2->faction != f) u2 = u2->next;
|
|
|
|
|
if (u2==NULL) return false;
|
|
|
|
|
|
|
|
|
|
/* simple visibility, just gotta have a unit in the region to see 'em */
|
|
|
|
|
if (is_guard(u, GUARD_ALL)!=0 || usiege(u) || u->building || u->ship) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rings = invisible(u, NULL);
|
|
|
|
|
stealth = eff_stealth(u, r) - modifier;
|
|
|
|
|
|
|
|
|
|
while (u2) {
|
|
|
|
|
if (rings<u->number || invisible(u, u2) < u->number) {
|
|
|
|
|
if (skill_enabled[SK_PERCEPTION]) {
|
|
|
|
|
int observation = eff_skill(u2, SK_PERCEPTION, r);
|
|
|
|
|
|
|
|
|
|
if (observation >= stealth) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* find next unit in our faction */
|
|
|
|
|
do {
|
|
|
|
|
u2=u2->next;
|
|
|
|
|
} while (u2 && u2->faction != f);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
cansee_unit(const unit * u, const unit * target, int modifier)
|
|
|
|
|
/* target->region kann != u->region sein, wenn es um durchreisen geht */
|
|
|
|
|
{
|
|
|
|
|
if (fval(target->race, RCF_INVISIBLE) || target->number == 0) return false;
|
|
|
|
|
else if (target->faction == u->faction) return true;
|
|
|
|
|
else {
|
|
|
|
|
int n, rings, o;
|
|
|
|
|
|
|
|
|
|
if (is_guard(target, GUARD_ALL)!=0 || usiege(target) || target->building || target->ship) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = eff_stealth(target, target->region) - modifier;
|
|
|
|
|
rings = invisible(target, NULL);
|
|
|
|
|
if (rings==0 && n<=0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rings && invisible(target, u) >= target->number) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (skill_enabled[SK_PERCEPTION]) {
|
|
|
|
|
o = eff_skill(u, SK_PERCEPTION, target->region);
|
|
|
|
|
if (o >= n) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
cansee_durchgezogen(const faction * f, const region * r, const unit * u, int modifier)
|
|
|
|
|
/* r kann != u->region sein, wenn es um durchreisen geht */
|
|
|
|
|
/* und es muss niemand aus f in der region sein, wenn sie vom Turm
|
|
|
|
|
* erblickt wird */
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
unit *u2;
|
|
|
|
|
|
|
|
|
|
if (fval(u->race, RCF_INVISIBLE) || u->number == 0) return false;
|
|
|
|
|
else if (u->faction == f) return true;
|
|
|
|
|
else {
|
|
|
|
|
int rings;
|
|
|
|
|
|
|
|
|
|
if (is_guard(u, GUARD_ALL)!=0 || usiege(u) || u->building || u->ship) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = eff_stealth(u, r) - modifier;
|
|
|
|
|
rings = invisible(u, NULL);
|
|
|
|
|
if (rings==0 && n<=0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next){
|
|
|
|
|
if (u2->faction == f) {
|
|
|
|
|
int o;
|
|
|
|
|
|
|
|
|
|
if (rings && invisible(u, u2) >= u->number) continue;
|
|
|
|
|
|
|
|
|
|
o = eff_skill(u2, SK_PERCEPTION, r);
|
|
|
|
|
|
|
|
|
|
if (o >= n) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
const char *
|
|
|
|
|
strcheck (const char *s, size_t maxlen)
|
|
|
|
|
{
|
|
|
|
|
static char buffer[16 * 1024];
|
|
|
|
|
if (strlen(s) > maxlen) {
|
|
|
|
|
assert(maxlen < 16 * 1024);
|
|
|
|
|
log_warning(("[strcheck] String wurde auf %d Zeichen verk<72>rzt:\n%s\n",
|
|
|
|
|
(int)maxlen, s));
|
|
|
|
|
strlcpy(buffer, s, maxlen);
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static attrib_type at_lighthouse = {
|
|
|
|
|
"lighthouse"
|
|
|
|
|
/* Rest ist NULL; tempor<6F>res, nicht alterndes Attribut */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* update_lighthouse: call this function whenever the size of a lighthouse changes
|
|
|
|
|
* it adds temporary markers to the surrounding regions.
|
|
|
|
|
* The existence of markers says nothing about the quality of the observer in
|
|
|
|
|
* the lighthouse, for this may change more frequently.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
update_lighthouse(building * lh)
|
|
|
|
|
{
|
|
|
|
|
static boolean init_lighthouse = false;
|
|
|
|
|
static const struct building_type * bt_lighthouse = 0;
|
|
|
|
|
|
|
|
|
|
if (!init_lighthouse) {
|
|
|
|
|
bt_lighthouse = bt_find("lighthouse");
|
|
|
|
|
if (bt_lighthouse==NULL) return;
|
|
|
|
|
init_lighthouse = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lh->type==bt_lighthouse) {
|
|
|
|
|
region * r = lh->region;
|
|
|
|
|
int d = (int)log10(lh->size) + 1;
|
|
|
|
|
int x;
|
|
|
|
|
|
|
|
|
|
if (lh->size>0) {
|
|
|
|
|
r->flags |= RF_LIGHTHOUSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (x=-d;x<=d;++x) {
|
|
|
|
|
int y;
|
|
|
|
|
for (y=-d;y<=d;++y) {
|
|
|
|
|
attrib * a;
|
|
|
|
|
region * r2;
|
|
|
|
|
int px = r->x+x, py = r->y+y;
|
|
|
|
|
pnormalize(&px, &py, rplane(r));
|
|
|
|
|
r2 = findregion(px, py);
|
|
|
|
|
if (r2==NULL) continue;
|
|
|
|
|
if (!fval(r2->terrain, SEA_REGION)) continue;
|
|
|
|
|
if (distance(r, r2) > d) continue;
|
|
|
|
|
a = a_find(r2->attribs, &at_lighthouse);
|
|
|
|
|
while (a && a->type==&at_lighthouse) {
|
|
|
|
|
building * b = (building*)a->data.v;
|
|
|
|
|
if (b==lh) break;
|
|
|
|
|
a = a->next;
|
|
|
|
|
}
|
|
|
|
|
if (!a) {
|
|
|
|
|
a = a_add(&r2->attribs, a_new(&at_lighthouse));
|
|
|
|
|
a->data.v = (void*)lh;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
count_all(const faction * f)
|
|
|
|
|
{
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
int n = 0;
|
|
|
|
|
unit *u;
|
|
|
|
|
for (u=f->units;u;u=u->nextF) {
|
|
|
|
|
if (playerrace(u->race)) {
|
|
|
|
|
n += u->number;
|
|
|
|
|
assert(f==u->faction);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (f->num_people != n) {
|
|
|
|
|
log_error(("# of people in %s is != num_people: %d should be %d.\n",
|
|
|
|
|
factionid(f), f->num_people, n));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return f->num_people;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
count_migrants (const faction * f)
|
|
|
|
|
{
|
|
|
|
|
unit *u = f->units;
|
|
|
|
|
int n = 0;
|
|
|
|
|
while (u) {
|
|
|
|
|
assert(u->faction == f);
|
|
|
|
|
if (u->race != f->race && u->race != new_race[RC_ILLUSION] && u->race != new_race[RC_SPELL]
|
|
|
|
|
&& !!playerrace(u->race) && !(is_cursed(u->attribs, C_SLAVE, 0)))
|
|
|
|
|
{
|
|
|
|
|
n += u->number;
|
|
|
|
|
}
|
|
|
|
|
u = u->nextF;
|
|
|
|
|
}
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
count_maxmigrants(const faction * f)
|
|
|
|
|
{
|
|
|
|
|
static int migrants = -1;
|
|
|
|
|
|
|
|
|
|
if (migrants<0) {
|
|
|
|
|
migrants = get_param_int(global.parameters, "rules.migrants", INT_MAX);
|
|
|
|
|
}
|
|
|
|
|
if (migrants==INT_MAX) {
|
|
|
|
|
int x = 0;
|
|
|
|
|
if (f->race == new_race[RC_HUMAN]) {
|
|
|
|
|
int nsize = count_all(f);
|
|
|
|
|
if (nsize>0) {
|
|
|
|
|
x = (int)(log10(nsize / 50.0) * 20);
|
|
|
|
|
if (x < 0) x = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
return migrants;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
init_tokens(const struct order * ord)
|
|
|
|
|
{
|
|
|
|
|
char * cmd = getcommand(ord);
|
|
|
|
|
init_tokens_str(cmd, cmd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
parse(keyword_t kword, int (*dofun)(unit *, struct order *), boolean thisorder)
|
|
|
|
|
{
|
|
|
|
|
region *r;
|
|
|
|
|
|
|
|
|
|
for (r = regions; r; r = r->next) {
|
|
|
|
|
unit **up = &r->units;
|
|
|
|
|
while (*up) {
|
|
|
|
|
unit * u = *up;
|
|
|
|
|
order ** ordp = &u->orders;
|
|
|
|
|
if (thisorder) ordp = &u->thisorder;
|
|
|
|
|
while (*ordp) {
|
|
|
|
|
order * ord = *ordp;
|
|
|
|
|
if (get_keyword(ord) == kword) {
|
|
|
|
|
if (dofun(u, ord)!=0) break;
|
|
|
|
|
if (u->orders==NULL) break;
|
|
|
|
|
}
|
|
|
|
|
if (thisorder) break;
|
|
|
|
|
if (*ordp==ord) ordp=&ord->next;
|
|
|
|
|
}
|
|
|
|
|
if (*up==u) up=&u->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
igetstrtoken(const char * initstr)
|
|
|
|
|
{
|
|
|
|
|
if (initstr!=NULL) {
|
|
|
|
|
init_tokens_str(initstr, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return getstrtoken();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
getuint (void)
|
|
|
|
|
{
|
|
|
|
|
return atoip((const char *)getstrtoken());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
getint (void)
|
|
|
|
|
{
|
|
|
|
|
return atoi((const char *)getstrtoken());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct race *
|
|
|
|
|
findrace(const char * s, const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
struct tnode * tokens = get_translations(lang, UT_RACES);
|
|
|
|
|
variant token;
|
|
|
|
|
|
|
|
|
|
assert(lang);
|
|
|
|
|
if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) {
|
|
|
|
|
return (const struct race *)token.v;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
findoption(const char *s, const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
struct tnode * tokens = get_translations(lang, UT_OPTIONS);
|
|
|
|
|
variant token;
|
|
|
|
|
|
|
|
|
|
if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) {
|
|
|
|
|
return (direction_t)token.i;
|
|
|
|
|
}
|
|
|
|
|
return NODIRECTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if PTRIES
|
|
|
|
|
static struct trie_node * ptries[UT_MAX][4];
|
|
|
|
|
|
|
|
|
|
static struct trie_node **
|
|
|
|
|
get_ptrie(const struct locale * lang, int type)
|
|
|
|
|
{
|
|
|
|
|
int index = (strcmp(locale_name(lang), "de")==0);
|
|
|
|
|
return &(ptries[type][index]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
umlaut_substitution(const char * ip, char * op, size_t outlen)
|
|
|
|
|
{
|
|
|
|
|
#define UMAX 7
|
|
|
|
|
static struct replace {
|
|
|
|
|
ucs4_t ucs;
|
|
|
|
|
const char str[3];
|
|
|
|
|
} replace[UMAX] = {
|
|
|
|
|
/* match lower-case (!) umlauts and others to transcriptions */
|
|
|
|
|
{ 223, "ss"}, /* szlig */
|
|
|
|
|
{ 228, "ae"}, /* auml */
|
|
|
|
|
{ 229, "aa"}, /* norsk */
|
|
|
|
|
{ 230, "ae"}, /* norsk */
|
|
|
|
|
{ 246, "oe"}, /* ouml */
|
|
|
|
|
{ 248, "oe"}, /* norsk */
|
|
|
|
|
{ 252, "ue"}, /* uuml */
|
|
|
|
|
};
|
|
|
|
|
int subs = 0;
|
|
|
|
|
while (*ip) {
|
|
|
|
|
ucs4_t ucs = *ip;
|
|
|
|
|
size_t size = 1;
|
|
|
|
|
size_t cpsize = 1;
|
|
|
|
|
|
|
|
|
|
if (ucs & 0x80) {
|
|
|
|
|
int ret = unicode_utf8_to_ucs4(&ucs, ip, &size);
|
|
|
|
|
if (ret!=0) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
cpsize = size;
|
|
|
|
|
if (ucs >= replace[0].ucs && ucs <= replace[UMAX-1].ucs) {
|
|
|
|
|
int i;
|
|
|
|
|
for (i=0;i!=UMAX;++i) {
|
|
|
|
|
if (replace[i].ucs==ucs) {
|
|
|
|
|
cpsize = 0;
|
|
|
|
|
memcpy(op, replace[i].str, 2);
|
|
|
|
|
op+=2;
|
|
|
|
|
++subs;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (cpsize) {
|
|
|
|
|
if (cpsize>outlen) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
memcpy(op, ip, cpsize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ip += size;
|
|
|
|
|
op += cpsize;
|
|
|
|
|
outlen -= cpsize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (outlen<=0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
*op = 0;
|
|
|
|
|
return subs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ptrie_find(struct trie_node *ptrie, const char * key, void * data, size_t size)
|
|
|
|
|
{
|
|
|
|
|
trie_node * node = trie_find_prefix(ptrie, key);
|
|
|
|
|
if (node) {
|
|
|
|
|
void * result = trie_getdata(node);
|
|
|
|
|
memcpy(data, result, size);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ptrie_insert(struct trie_node **ptrie, const char * name, void * data, size_t size)
|
|
|
|
|
{
|
|
|
|
|
char converted[256];
|
|
|
|
|
char simple[256];
|
|
|
|
|
int ret = unicode_utf8_tolower(converted, 256, name);
|
|
|
|
|
if (ret==0) {
|
|
|
|
|
int subs = umlaut_substitution(converted, simple, sizeof(simple));
|
|
|
|
|
if (subs>0) {
|
|
|
|
|
trie_insert(ptrie, simple, data, size);
|
|
|
|
|
}
|
|
|
|
|
trie_insert(ptrie, converted, data, size);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
skill_t
|
|
|
|
|
findskill(const char *s, const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
#if PTRIES
|
|
|
|
|
char lowercase[256];
|
|
|
|
|
int res = unicode_utf8_tolower(lowercase, sizeof(lowercase), s);
|
|
|
|
|
if (res==0) {
|
|
|
|
|
trie_node ** ptrie = get_ptrie(lang, UT_SKILLS);
|
|
|
|
|
skill_t sk;
|
|
|
|
|
int result = ptrie_find(*ptrie, lowercase, &sk, sizeof(sk));
|
|
|
|
|
if (result==0) return sk;
|
|
|
|
|
}
|
|
|
|
|
return NOSKILL;
|
|
|
|
|
#else
|
|
|
|
|
struct tnode * tokens = get_translations(lang, UT_SKILLS);
|
|
|
|
|
variant token;
|
|
|
|
|
|
|
|
|
|
if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) return NOSKILL;
|
|
|
|
|
return (skill_t)token.i;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyword_t
|
|
|
|
|
findkeyword(const char *s, const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
struct tnode * tokens = get_translations(lang, UT_KEYWORDS);
|
|
|
|
|
variant token;
|
|
|
|
|
|
|
|
|
|
if (*s == '@') s++;
|
|
|
|
|
if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) return NOKEYWORD;
|
|
|
|
|
if (global.disabled[token.i]) return NOKEYWORD;
|
|
|
|
|
return (keyword_t) token.i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
param_t
|
|
|
|
|
findparam(const char *s, const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
struct tnode * tokens = get_translations(lang, UT_PARAMS);
|
|
|
|
|
variant token;
|
|
|
|
|
|
|
|
|
|
if (findtoken(tokens, s, &token)==E_TOK_NOMATCH) {
|
|
|
|
|
const building_type * btype = findbuildingtype(s, lang);
|
|
|
|
|
if (btype!=NULL) return (param_t) P_GEBAEUDE;
|
|
|
|
|
return NOPARAM;
|
|
|
|
|
}
|
|
|
|
|
if (token.i==P_BUILDING) return P_GEBAEUDE;
|
|
|
|
|
return (param_t)token.i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
param_t
|
|
|
|
|
getparam (const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
return findparam (getstrtoken (), lang);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
faction *
|
|
|
|
|
findfaction (int n)
|
|
|
|
|
{
|
|
|
|
|
faction * f = ffindhash(n);
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
faction *
|
|
|
|
|
getfaction (void)
|
|
|
|
|
{
|
|
|
|
|
return findfaction (getid());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unit *
|
|
|
|
|
findunitr (const region * r, int n)
|
|
|
|
|
{
|
|
|
|
|
unit *u;
|
|
|
|
|
|
|
|
|
|
/* findunit regional! */
|
|
|
|
|
|
|
|
|
|
for (u = r->units; u; u = u->next)
|
|
|
|
|
if (u->no == n)
|
|
|
|
|
return u;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unit *findunit(int n)
|
|
|
|
|
{
|
|
|
|
|
if (n <= 0) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return ufindhash(n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unit *
|
|
|
|
|
findunitg (int n, const region * hint)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* Abfangen von Syntaxfehlern. */
|
|
|
|
|
if (n <= 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* findunit global! */
|
|
|
|
|
hint = 0;
|
|
|
|
|
return ufindhash(n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unit *
|
|
|
|
|
getnewunit (const region * r, const faction * f)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
n = getid();
|
|
|
|
|
|
|
|
|
|
return findnewunit (r, f, n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
read_newunitid (const faction * f, const region * r)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
unit *u2;
|
|
|
|
|
n = getid();
|
|
|
|
|
if (n == 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
u2 = findnewunit(r, f, n);
|
|
|
|
|
if (u2) return u2->no;
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
read_unitid (const faction * f, const region * r)
|
|
|
|
|
{
|
|
|
|
|
const char * s = getstrtoken();
|
|
|
|
|
|
|
|
|
|
/* Da s nun nur einen string enthaelt, suchen wir ihn direkt in der
|
|
|
|
|
* paramliste. machen wir das nicht, dann wird getnewunit in s nach der
|
|
|
|
|
* nummer suchen, doch dort steht bei temp-units nur "temp" drinnen! */
|
|
|
|
|
|
|
|
|
|
switch (findparam(s, f->locale)) {
|
|
|
|
|
case P_TEMP:
|
|
|
|
|
return read_newunitid(f, r);
|
|
|
|
|
}
|
|
|
|
|
if (!s || *s == 0)
|
|
|
|
|
return -1;
|
|
|
|
|
return atoi36((const char *)s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* exported symbol */
|
|
|
|
|
boolean getunitpeasants;
|
|
|
|
|
unit *
|
|
|
|
|
getunitg(const region * r, const faction * f)
|
|
|
|
|
{
|
|
|
|
|
int n = read_unitid(f, r);
|
|
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
getunitpeasants = 1;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
getunitpeasants = 0;
|
|
|
|
|
if (n < 0) return 0;
|
|
|
|
|
|
|
|
|
|
return findunit(n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unit *
|
|
|
|
|
getunit(const region * r, const faction * f)
|
|
|
|
|
{
|
|
|
|
|
int n = read_unitid(f, r);
|
|
|
|
|
unit *u2;
|
|
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
getunitpeasants = 1;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
getunitpeasants = 0;
|
|
|
|
|
if (n < 0) return 0;
|
|
|
|
|
|
|
|
|
|
u2 = findunit(n);
|
|
|
|
|
if (u2!=NULL && u2->region==r) {
|
|
|
|
|
/* there used to be a 'u2->flags & UFL_ISNEW || u2->number>0' condition
|
|
|
|
|
* here, but it got removed because of a bug that made units disappear:
|
|
|
|
|
* http://eressea.upb.de/mantis/bug_view_page.php?bug_id=0000172
|
|
|
|
|
*/
|
|
|
|
|
return u2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* - String Listen --------------------------------------------- */
|
|
|
|
|
void
|
|
|
|
|
addstrlist (strlist ** SP, const char *s)
|
|
|
|
|
{
|
|
|
|
|
strlist * slist = malloc(sizeof(strlist));
|
|
|
|
|
slist->next = NULL;
|
|
|
|
|
slist->s = strdup(s);
|
|
|
|
|
addlist(SP, slist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
freestrlist (strlist * s)
|
|
|
|
|
{
|
|
|
|
|
strlist *q, *p = s;
|
|
|
|
|
while (p) {
|
|
|
|
|
q = p->next;
|
|
|
|
|
free(p->s);
|
|
|
|
|
free(p);
|
|
|
|
|
p = q;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* - Meldungen und Fehler ------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
boolean lomem = false;
|
|
|
|
|
|
|
|
|
|
/* - Namen der Strukturen -------------------------------------- */
|
|
|
|
|
typedef char name[OBJECTIDSIZE+1];
|
|
|
|
|
static name idbuf[8];
|
|
|
|
|
static int nextbuf = 0;
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
estring_i(char *ibuf)
|
|
|
|
|
{
|
|
|
|
|
char *p = ibuf;
|
|
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
|
if (isxspace(*(unsigned*)p) == ' ') {
|
|
|
|
|
*p = '~';
|
|
|
|
|
}
|
|
|
|
|
++p;
|
|
|
|
|
}
|
|
|
|
|
return ibuf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
estring(const char *s)
|
|
|
|
|
{
|
|
|
|
|
char *ibuf = idbuf[(++nextbuf) % 8];
|
|
|
|
|
|
|
|
|
|
strlcpy(ibuf, s, sizeof(name));
|
|
|
|
|
return estring_i(ibuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
cstring_i(char *ibuf)
|
|
|
|
|
{
|
|
|
|
|
char *p = ibuf;
|
|
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
|
if (*p == '~') {
|
|
|
|
|
*p = ' ';
|
|
|
|
|
}
|
|
|
|
|
++p;
|
|
|
|
|
}
|
|
|
|
|
return ibuf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
cstring(const char *s)
|
|
|
|
|
{
|
|
|
|
|
char *ibuf = idbuf[(++nextbuf) % 8];
|
|
|
|
|
|
|
|
|
|
strlcpy(ibuf, s, sizeof(name));
|
|
|
|
|
return cstring_i(ibuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
building *
|
|
|
|
|
largestbuilding(const region * r, cmp_building_cb cmp_gt, boolean imaginary)
|
|
|
|
|
{
|
|
|
|
|
building *b, *best = NULL;
|
|
|
|
|
|
|
|
|
|
for (b = rbuildings(r); b; b = b->next) {
|
|
|
|
|
if (cmp_gt(b, best)<=0) continue;
|
|
|
|
|
if (!imaginary) {
|
|
|
|
|
const attrib * a = a_find(b->attribs, &at_icastle);
|
|
|
|
|
if (a) continue;
|
|
|
|
|
}
|
|
|
|
|
best = b;
|
|
|
|
|
}
|
|
|
|
|
return best;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
write_unitname(const unit * u, char * buffer, size_t size)
|
|
|
|
|
{
|
|
|
|
|
snprintf((char*)buffer, size, "%s (%s)", (const char*)u->name, itoa36(u->no));
|
|
|
|
|
buffer[size-1] = 0;
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
unitname(const unit * u)
|
|
|
|
|
{
|
|
|
|
|
char *ubuf = idbuf[(++nextbuf) % 8];
|
|
|
|
|
return write_unitname(u, ubuf, sizeof(name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* -- Erschaffung neuer Einheiten ------------------------------ */
|
|
|
|
|
|
|
|
|
|
extern faction * dfindhash(int i);
|
|
|
|
|
|
|
|
|
|
static const char* forbidden[] = { "t", "te", "tem", "temp", NULL };
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
forbiddenid(int id)
|
|
|
|
|
{
|
|
|
|
|
static int * forbid = NULL;
|
|
|
|
|
static size_t len;
|
|
|
|
|
size_t i;
|
|
|
|
|
if (id<=0) return 1;
|
|
|
|
|
if (!forbid) {
|
|
|
|
|
while (forbidden[len]) ++len;
|
|
|
|
|
forbid = calloc(len, sizeof(int));
|
|
|
|
|
for (i=0;i!=len;++i) {
|
|
|
|
|
forbid[i] = strtol(forbidden[i], NULL, 36);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i=0;i!=len;++i) if (id==forbid[i]) return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ID's f<>r Einheiten und Zauber */
|
|
|
|
|
int
|
|
|
|
|
newunitid(void)
|
|
|
|
|
{
|
|
|
|
|
int random_unit_no;
|
|
|
|
|
int start_random_no;
|
|
|
|
|
random_unit_no = 1 + (rng_int() % MAX_UNIT_NR);
|
|
|
|
|
start_random_no = random_unit_no;
|
|
|
|
|
|
|
|
|
|
while (ufindhash(random_unit_no) || dfindhash(random_unit_no)
|
|
|
|
|
|| cfindhash(random_unit_no)
|
|
|
|
|
|| forbiddenid(random_unit_no))
|
|
|
|
|
{
|
|
|
|
|
random_unit_no++;
|
|
|
|
|
if (random_unit_no == MAX_UNIT_NR + 1) {
|
|
|
|
|
random_unit_no = 1;
|
|
|
|
|
}
|
|
|
|
|
if (random_unit_no == start_random_no) {
|
|
|
|
|
random_unit_no = (int) MAX_UNIT_NR + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return random_unit_no;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
newcontainerid(void)
|
|
|
|
|
{
|
|
|
|
|
int random_no;
|
|
|
|
|
int start_random_no;
|
|
|
|
|
|
|
|
|
|
random_no = 1 + (rng_int() % MAX_CONTAINER_NR);
|
|
|
|
|
start_random_no = random_no;
|
|
|
|
|
|
|
|
|
|
while (findship(random_no) || findbuilding(random_no)) {
|
|
|
|
|
random_no++;
|
|
|
|
|
if (random_no == MAX_CONTAINER_NR + 1) {
|
|
|
|
|
random_no = 1;
|
|
|
|
|
}
|
|
|
|
|
if (random_no == start_random_no) {
|
|
|
|
|
random_no = (int) MAX_CONTAINER_NR + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return random_no;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unit *
|
|
|
|
|
createunit(region * r, faction * f, int number, const struct race * rc)
|
|
|
|
|
{
|
|
|
|
|
assert(rc);
|
|
|
|
|
return create_unit(r, f, number, rc, 0, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
idle (faction * f)
|
|
|
|
|
{
|
|
|
|
|
return (boolean) (f ? false : true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
maxworkingpeasants(const struct region * r)
|
|
|
|
|
{
|
|
|
|
|
int i = production(r) * MAXPEASANTS_PER_AREA
|
|
|
|
|
- ((rtrees(r,2)+rtrees(r,1)/2) * TREESIZE);
|
|
|
|
|
return MAX(i, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lighthouse_range(const building * b, const faction * f)
|
|
|
|
|
{
|
|
|
|
|
int d = 0;
|
|
|
|
|
if (fval(b, BLD_WORKING) && b->size >= 10) {
|
|
|
|
|
int maxd = (int)log10(b->size) + 1;
|
|
|
|
|
|
|
|
|
|
if (skill_enabled[SK_PERCEPTION]) {
|
|
|
|
|
region * r = b->region;
|
|
|
|
|
int c = 0;
|
|
|
|
|
unit *u;
|
|
|
|
|
for (u = r->units; u; u = u->next) {
|
|
|
|
|
if (u->building == b) {
|
|
|
|
|
c += u->number;
|
|
|
|
|
if (c > buildingcapacity(b)) break;
|
|
|
|
|
if (f==NULL || u->faction == f) {
|
|
|
|
|
int sk = eff_skill(u, SK_PERCEPTION, r) / 3;
|
|
|
|
|
d = MAX(d, sk);
|
|
|
|
|
d = MIN(maxd, d);
|
|
|
|
|
if (d==maxd) break;
|
|
|
|
|
}
|
|
|
|
|
} else if (c) break; /* first unit that's no longer in the house ends the search */
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* E3A rule: no perception req'd */
|
|
|
|
|
return maxd;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
check_leuchtturm(region * r, faction * f)
|
|
|
|
|
{
|
|
|
|
|
attrib * a;
|
|
|
|
|
|
|
|
|
|
if (!fval(r->terrain, SEA_REGION)) return false;
|
|
|
|
|
|
|
|
|
|
for (a = a_find(r->attribs, &at_lighthouse);a && a->type==&at_lighthouse;a=a->next) {
|
|
|
|
|
building *b = (building *)a->data.v;
|
|
|
|
|
|
|
|
|
|
assert(b->type == bt_find("lighthouse"));
|
|
|
|
|
if (fval(b, BLD_WORKING) && b->size >= 10) {
|
|
|
|
|
int maxd = (int)log10(b->size) + 1;
|
|
|
|
|
|
|
|
|
|
if (skill_enabled[SK_PERCEPTION]) {
|
|
|
|
|
region *r2 = b->region;
|
|
|
|
|
unit *u;
|
|
|
|
|
int c = 0;
|
|
|
|
|
int d = 0;
|
|
|
|
|
|
|
|
|
|
for (u = r2->units; u; u = u->next) {
|
|
|
|
|
if (u->building == b) {
|
|
|
|
|
c += u->number;
|
|
|
|
|
if (c > buildingcapacity(b)) break;
|
|
|
|
|
if (f==NULL || u->faction == f) {
|
|
|
|
|
if (!d) d = distance(r, r2);
|
|
|
|
|
if (maxd < d) break;
|
|
|
|
|
if (eff_skill(u, SK_PERCEPTION, r) >= d * 3) return true;
|
|
|
|
|
}
|
|
|
|
|
} else if (c) break; /* first unit that's no longer in the house ends the search */
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* E3A rule: no perception req'd */
|
|
|
|
|
return maxd;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
region *
|
|
|
|
|
lastregion (faction * f)
|
|
|
|
|
{
|
|
|
|
|
#ifdef SMART_INTERVALS
|
|
|
|
|
unit * u = f->units;
|
|
|
|
|
region *r = f->last;
|
|
|
|
|
|
|
|
|
|
if (u==NULL) return NULL;
|
|
|
|
|
if (r!=NULL) return r->next;
|
|
|
|
|
|
|
|
|
|
/* it is safe to start in the region of the first unit. */
|
|
|
|
|
f->last = u->region;
|
|
|
|
|
/* if regions have indices, we can skip ahead: */
|
|
|
|
|
for (u=u->nextF; u!=NULL; u=u->nextF) {
|
|
|
|
|
r = u->region;
|
|
|
|
|
if (r->index > f->last->index) f->last = r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* we continue from the best region and look for travelthru etc. */
|
|
|
|
|
for (r = f->last->next; r; r = r->next) {
|
|
|
|
|
plane * p = rplane(r);
|
|
|
|
|
|
|
|
|
|
/* search the region for travelthru-attributes: */
|
|
|
|
|
if (fval(r, RF_TRAVELUNIT)) {
|
|
|
|
|
attrib * ru = a_find(r->attribs, &at_travelunit);
|
|
|
|
|
while (ru && ru->type==&at_travelunit) {
|
|
|
|
|
u = (unit*)ru->data.v;
|
|
|
|
|
if (u->faction == f) {
|
|
|
|
|
f->last = r;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ru = ru->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (f->last == r) continue;
|
|
|
|
|
if (check_leuchtturm(r, f))
|
|
|
|
|
f->last = r;
|
|
|
|
|
if (p && is_watcher(p, f)) {
|
|
|
|
|
f->last = r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return f->last->next;
|
|
|
|
|
#else
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
region *
|
|
|
|
|
firstregion (faction * f)
|
|
|
|
|
{
|
|
|
|
|
#ifdef SMART_INTERVALS
|
|
|
|
|
region *r = f->first;
|
|
|
|
|
|
|
|
|
|
if (f->units==NULL) return NULL;
|
|
|
|
|
if (r!=NULL) return r;
|
|
|
|
|
|
|
|
|
|
return f->first = regions;
|
|
|
|
|
#else
|
|
|
|
|
return regions;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ** blk_list[1024];
|
|
|
|
|
int list_index;
|
|
|
|
|
int blk_index;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gc_done(void)
|
|
|
|
|
{
|
|
|
|
|
int i, k;
|
|
|
|
|
for (i=0;i!=list_index;++i)
|
|
|
|
|
{
|
|
|
|
|
for (k=0;k!=1024;++k) free(blk_list[i][k]);
|
|
|
|
|
free(blk_list[i]);
|
|
|
|
|
}
|
|
|
|
|
for (k=0;k!=blk_index;++k) free(blk_list[list_index][k]);
|
|
|
|
|
free(blk_list[list_index]);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
gc_add(void * p)
|
|
|
|
|
{
|
|
|
|
|
if (blk_index==0) {
|
|
|
|
|
blk_list[list_index] = (void**)malloc(1024 * sizeof(void*));
|
|
|
|
|
}
|
|
|
|
|
blk_list[list_index][blk_index] = p;
|
|
|
|
|
blk_index = (blk_index+1) % 1024;
|
|
|
|
|
if (!blk_index) ++ list_index;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
init_directions(tnode * root, const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
/* mit dieser routine kann man mehrere namen f<>r eine direction geben,
|
|
|
|
|
* das ist f<EFBFBD>r die hexes ideal. */
|
|
|
|
|
const struct {
|
|
|
|
|
const char* name;
|
|
|
|
|
int direction;
|
|
|
|
|
} dirs [] = {
|
|
|
|
|
{ "dir_ne", D_NORTHEAST},
|
|
|
|
|
{ "dir_nw", D_NORTHWEST},
|
|
|
|
|
{ "dir_se", D_SOUTHEAST},
|
|
|
|
|
{ "dir_sw", D_SOUTHWEST},
|
|
|
|
|
{ "dir_east", D_EAST},
|
|
|
|
|
{ "dir_west", D_WEST},
|
|
|
|
|
{ "northeast", D_NORTHEAST},
|
|
|
|
|
{ "northwest", D_NORTHWEST},
|
|
|
|
|
{ "southeast", D_SOUTHEAST},
|
|
|
|
|
{ "southwest", D_SOUTHWEST},
|
|
|
|
|
{ "east", D_EAST },
|
|
|
|
|
{ "west",D_WEST },
|
|
|
|
|
{ "PAUSE", D_PAUSE },
|
|
|
|
|
{ NULL, NODIRECTION}
|
|
|
|
|
};
|
|
|
|
|
int i;
|
|
|
|
|
struct tnode * tokens = get_translations(lang, UT_DIRECTIONS);
|
|
|
|
|
|
|
|
|
|
for (i=0; dirs[i].direction!=NODIRECTION;++i) {
|
|
|
|
|
variant token;
|
|
|
|
|
token.i = dirs[i].direction;
|
|
|
|
|
addtoken(tokens, LOC(lang, dirs[i].name), token);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
direction_t
|
|
|
|
|
finddirection(const char *s, const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
struct tnode * tokens = get_translations(lang, UT_DIRECTIONS);
|
|
|
|
|
variant token;
|
|
|
|
|
|
|
|
|
|
if (findtoken(tokens, s, &token)==E_TOK_SUCCESS) {
|
|
|
|
|
return (direction_t)token.i;
|
|
|
|
|
}
|
|
|
|
|
return NODIRECTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
init_locale(const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
variant var;
|
|
|
|
|
int i;
|
|
|
|
|
const struct race * rc;
|
|
|
|
|
struct tnode * tokens;
|
|
|
|
|
const terrain_type * terrain;
|
|
|
|
|
#if PTRIES
|
|
|
|
|
trie_node ** ptrie;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
tokens = get_translations(lang, UT_MAGIC);
|
|
|
|
|
if (tokens) {
|
|
|
|
|
const char * str = get_param(global.parameters, "rules.magic.playerschools");
|
|
|
|
|
char * sstr, * tok;
|
|
|
|
|
if (str==NULL) {
|
|
|
|
|
str = "gwyrrd illaun draig cerddor tybied";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sstr = strdup(str);
|
|
|
|
|
tok = strtok(sstr, " ");
|
|
|
|
|
while (tok) {
|
|
|
|
|
for (i=0;i!=MAXMAGIETYP;++i) {
|
|
|
|
|
if (strcmp(tok, magic_school[i])==0) break;
|
|
|
|
|
}
|
|
|
|
|
assert(i!=MAXMAGIETYP);
|
|
|
|
|
var.i = i;
|
|
|
|
|
addtoken(tokens, LOC(lang, mkname("school", tok)), var);
|
|
|
|
|
tok = strtok(NULL, " ");
|
|
|
|
|
}
|
|
|
|
|
free(sstr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tokens = get_translations(lang, UT_DIRECTIONS);
|
|
|
|
|
init_directions(tokens, lang);
|
|
|
|
|
|
|
|
|
|
tokens = get_translations(lang, UT_RACES);
|
|
|
|
|
for (rc=races;rc;rc=rc->next) {
|
|
|
|
|
var.v = (void*)rc;
|
|
|
|
|
addtoken(tokens, LOC(lang, rc_name(rc, 1)), var);
|
|
|
|
|
addtoken(tokens, LOC(lang, rc_name(rc, 0)), var);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tokens = get_translations(lang, UT_PARAMS);
|
|
|
|
|
for (i=0;i!=MAXPARAMS;++i) {
|
|
|
|
|
var.i = i;
|
|
|
|
|
addtoken(tokens, LOC(lang, parameters[i]), var);
|
|
|
|
|
}
|
|
|
|
|
#if PTRIES
|
|
|
|
|
ptrie = get_ptrie(lang, UT_SKILLS);
|
|
|
|
|
for (i=0;i!=MAXSKILLS;++i) {
|
|
|
|
|
skill_t sk = (skill_t)i;
|
|
|
|
|
const char * skname = skillname(sk, lang);
|
|
|
|
|
if (skname!=NULL) {
|
|
|
|
|
ptrie_insert(ptrie, skname, &sk, sizeof(sk));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
tokens = get_translations(lang, UT_SKILLS);
|
|
|
|
|
for (i=0;i!=MAXSKILLS;++i) {
|
|
|
|
|
const char * skname = skillname((skill_t)i, lang);
|
|
|
|
|
if (skname!=NULL) {
|
|
|
|
|
var.i = i;
|
|
|
|
|
addtoken(tokens, skname, var);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
tokens = get_translations(lang, UT_KEYWORDS);
|
|
|
|
|
for (i=0;i!=MAXKEYWORDS;++i) {
|
|
|
|
|
var.i = i;
|
|
|
|
|
if (keywords[i]) addtoken(tokens, LOC(lang, keywords[i]), var);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tokens = get_translations(lang, UT_OPTIONS);
|
|
|
|
|
for (i=0;i!=MAXOPTIONS;++i) {
|
|
|
|
|
var.i = i;
|
|
|
|
|
if (options[i]) addtoken(tokens, LOC(lang, options[i]), var);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tokens = get_translations(lang, UT_TERRAINS);
|
|
|
|
|
for (terrain=terrains();terrain!=NULL;terrain=terrain->next) {
|
|
|
|
|
var.v = (void*)terrain;
|
|
|
|
|
addtoken(tokens, LOC(lang, terrain->_name), var);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct param {
|
|
|
|
|
struct param * next;
|
|
|
|
|
char * name;
|
|
|
|
|
char * data;
|
|
|
|
|
} param;
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
getid(void)
|
|
|
|
|
{
|
|
|
|
|
const char * str = (const char *)getstrtoken();
|
|
|
|
|
int i = atoi36(str);
|
|
|
|
|
if (i<0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
get_param(const struct param * p, const char * key)
|
|
|
|
|
{
|
|
|
|
|
while (p!=NULL) {
|
|
|
|
|
if (strcmp(p->name, key)==0) return p->data;
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
get_param_int(const struct param * p, const char * key, int def)
|
|
|
|
|
{
|
|
|
|
|
while (p!=NULL) {
|
|
|
|
|
if (strcmp(p->name, key)==0) return atoi(p->data);
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
return def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char * g_datadir;
|
|
|
|
|
const char *
|
|
|
|
|
datapath(void)
|
|
|
|
|
{
|
|
|
|
|
static char zText[MAX_PATH];
|
|
|
|
|
if (g_datadir) return g_datadir;
|
|
|
|
|
return strcat(strcpy(zText, basepath()), "/data");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
set_datapath(const char * path)
|
|
|
|
|
{
|
|
|
|
|
g_datadir = path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char * g_reportdir;
|
|
|
|
|
const char *
|
|
|
|
|
reportpath(void)
|
|
|
|
|
{
|
|
|
|
|
static char zText[MAX_PATH];
|
|
|
|
|
if (g_reportdir) return g_reportdir;
|
|
|
|
|
return strcat(strcpy(zText, basepath()), "/reports");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
set_reportpath(const char * path)
|
|
|
|
|
{
|
|
|
|
|
g_reportdir = path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char * g_basedir;
|
|
|
|
|
const char *
|
|
|
|
|
basepath(void)
|
|
|
|
|
{
|
|
|
|
|
if (g_basedir) return g_basedir;
|
|
|
|
|
return ".";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
set_basepath(const char * path)
|
|
|
|
|
{
|
|
|
|
|
g_basedir = path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
get_param_flt(const struct param * p, const char * key, float def)
|
|
|
|
|
{
|
|
|
|
|
while (p!=NULL) {
|
|
|
|
|
if (strcmp(p->name, key)==0) return (float)atof(p->data);
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
return def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
set_param(struct param ** p, const char * key, const char * data)
|
|
|
|
|
{
|
|
|
|
|
++global.cookie;
|
|
|
|
|
while (*p!=NULL) {
|
|
|
|
|
if (strcmp((*p)->name, key)==0) {
|
|
|
|
|
free((*p)->data);
|
|
|
|
|
(*p)->data = strdup(data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
p=&(*p)->next;
|
|
|
|
|
}
|
|
|
|
|
*p = malloc(sizeof(param));
|
|
|
|
|
(*p)->name = strdup(key);
|
|
|
|
|
(*p)->data = strdup(data);
|
|
|
|
|
(*p)->next = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
kernel_done(void)
|
|
|
|
|
{
|
|
|
|
|
/* calling this function releases memory assigned to static variables, etc.
|
|
|
|
|
* calling it is optional, e.g. a release server will most likely not do it.
|
|
|
|
|
*/
|
|
|
|
|
translation_done();
|
|
|
|
|
gc_done();
|
|
|
|
|
sql_done();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char * localenames[] = {
|
|
|
|
|
"de", "en",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
init_locales(void)
|
|
|
|
|
{
|
|
|
|
|
int l;
|
|
|
|
|
for (l=0;localenames[l];++l) {
|
|
|
|
|
const struct locale * lang = find_locale(localenames[l]);
|
|
|
|
|
if (lang) init_locale(lang);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: soll hier weg */
|
|
|
|
|
extern struct attrib_type at_shiptrail;
|
|
|
|
|
|
|
|
|
|
attrib_type at_germs = {
|
|
|
|
|
"germs",
|
|
|
|
|
DEFAULT_INIT,
|
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
|
DEFAULT_AGE,
|
|
|
|
|
a_writeshorts,
|
|
|
|
|
a_readshorts,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*********************/
|
|
|
|
|
/* at_guard */
|
|
|
|
|
/*********************/
|
|
|
|
|
attrib_type at_guard = {
|
|
|
|
|
"guard",
|
|
|
|
|
DEFAULT_INIT,
|
|
|
|
|
DEFAULT_FINALIZE,
|
|
|
|
|
DEFAULT_AGE,
|
|
|
|
|
a_writeint,
|
|
|
|
|
a_readint,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
setstatus(struct unit * u, int status)
|
|
|
|
|
{
|
|
|
|
|
assert(status>=ST_AGGRO && status<=ST_FLEE);
|
|
|
|
|
if (u->status!=status) {
|
|
|
|
|
u->status = (status_t)status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
setguard(unit * u, unsigned int flags)
|
|
|
|
|
{
|
|
|
|
|
/* setzt die guard-flags der Einheit */
|
|
|
|
|
attrib * a = NULL;
|
|
|
|
|
assert(flags==0 || !fval(u, UFL_MOVED));
|
|
|
|
|
assert(flags==0 || u->status<ST_FLEE);
|
|
|
|
|
if (fval(u, UFL_GUARD)) {
|
|
|
|
|
a = a_find(u->attribs, &at_guard);
|
|
|
|
|
}
|
|
|
|
|
if (flags == GUARD_NONE) {
|
|
|
|
|
freset(u, UFL_GUARD);
|
|
|
|
|
if (a) a_remove(&u->attribs, a);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fset(u, UFL_GUARD);
|
|
|
|
|
fset(u->region, RF_GUARDED);
|
|
|
|
|
if ((int)flags==guard_flags(u)) {
|
|
|
|
|
if (a) a_remove(&u->attribs, a);
|
|
|
|
|
} else {
|
|
|
|
|
if (!a) a = a_add(&u->attribs, a_new(&at_guard));
|
|
|
|
|
a->data.i = (int)flags;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
getguard(const unit * u)
|
|
|
|
|
{
|
|
|
|
|
attrib * a;
|
|
|
|
|
|
|
|
|
|
assert((u->building && fval(u, UFL_OWNER)) || fval(u, UFL_GUARD) || !"you're doing it wrong! check is_guard first");
|
|
|
|
|
a = a_find(u->attribs, &at_guard);
|
|
|
|
|
if (a) {
|
|
|
|
|
return (unsigned int)a->data.i;
|
|
|
|
|
}
|
|
|
|
|
return guard_flags(u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef HAVE_STRDUP
|
|
|
|
|
char *
|
|
|
|
|
strdup(const char *s)
|
|
|
|
|
{
|
|
|
|
|
return strcpy((char*)malloc(sizeof(char)*(strlen(s)+1)), s);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
remove_empty_factions(void)
|
|
|
|
|
{
|
|
|
|
|
faction **fp, *f3;
|
|
|
|
|
|
|
|
|
|
for (fp = &factions; *fp;) {
|
|
|
|
|
faction * f = *fp;
|
|
|
|
|
/* monster (0) werden nicht entfernt. alive kann beim readgame
|
|
|
|
|
* () auf 0 gesetzt werden, wenn monsters keine einheiten mehr
|
|
|
|
|
* haben. */
|
|
|
|
|
if ((f->units==NULL || f->alive == 0) && !is_monsters(f)) {
|
|
|
|
|
ursprung * ur = f->ursprung;
|
|
|
|
|
while (ur && ur->id!=0) ur=ur->next;
|
|
|
|
|
if (verbosity>=2) log_stdio(stdout, "\t%s\n", factionname(f));
|
|
|
|
|
|
|
|
|
|
/* Einfach in eine Datei schreiben und sp<73>ter vermailen */
|
|
|
|
|
|
|
|
|
|
if (updatelog) fprintf(updatelog, "dropout %s\n", itoa36(f->no));
|
|
|
|
|
|
|
|
|
|
for (f3 = factions; f3; f3 = f3->next) {
|
|
|
|
|
ally * sf;
|
|
|
|
|
group * g;
|
|
|
|
|
ally ** sfp = &f3->allies;
|
|
|
|
|
while (*sfp) {
|
|
|
|
|
sf = *sfp;
|
|
|
|
|
if (sf->faction == f || sf->faction == NULL) {
|
|
|
|
|
*sfp = sf->next;
|
|
|
|
|
free(sf);
|
|
|
|
|
}
|
|
|
|
|
else sfp = &(*sfp)->next;
|
|
|
|
|
}
|
|
|
|
|
for (g = f3->groups; g; g=g->next) {
|
|
|
|
|
sfp = &g->allies;
|
|
|
|
|
while (*sfp) {
|
|
|
|
|
sf = *sfp;
|
|
|
|
|
if (sf->faction == f || sf->faction == NULL) {
|
|
|
|
|
*sfp = sf->next;
|
|
|
|
|
free(sf);
|
|
|
|
|
}
|
|
|
|
|
else sfp = &(*sfp)->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (f->subscription) {
|
|
|
|
|
sql_print(("UPDATE subscriptions set status='DEAD' where id=%u;\n",
|
|
|
|
|
f->subscription));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*fp = f->next;
|
|
|
|
|
funhash(f);
|
|
|
|
|
free_faction(f);
|
|
|
|
|
free(f);
|
|
|
|
|
}
|
|
|
|
|
else fp = &(*fp)->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
remove_empty_units_in_region(region *r)
|
|
|
|
|
{
|
|
|
|
|
unit **up = &r->units;
|
|
|
|
|
|
|
|
|
|
while (*up) {
|
|
|
|
|
unit * u = *up;
|
|
|
|
|
|
|
|
|
|
if (u->number) {
|
|
|
|
|
faction * f = u->faction;
|
|
|
|
|
if (f==NULL || !f->alive) {
|
|
|
|
|
set_number(u, 0);
|
|
|
|
|
}
|
|
|
|
|
if (MaxAge()>0) {
|
|
|
|
|
if ((!fval(f, FFL_NOTIMEOUT) && f->age > MaxAge())) {
|
|
|
|
|
set_number(u, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((u->number == 0 && u->race != new_race[RC_SPELL]) || (u->age <= 0 && u->race == new_race[RC_SPELL])) {
|
|
|
|
|
remove_unit(up, u);
|
|
|
|
|
}
|
|
|
|
|
if (*up==u) up=&u->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
remove_empty_units(void)
|
|
|
|
|
{
|
|
|
|
|
region *r;
|
|
|
|
|
|
|
|
|
|
for (r = regions; r; r = r->next) {
|
|
|
|
|
remove_empty_units_in_region(r);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
faction_id_is_unused(int id)
|
|
|
|
|
{
|
|
|
|
|
return findfaction(id)==NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
weight(const unit * u)
|
|
|
|
|
{
|
|
|
|
|
int w, n = 0, in_bag = 0;
|
|
|
|
|
|
|
|
|
|
item * itm;
|
|
|
|
|
for (itm=u->items;itm;itm=itm->next) {
|
|
|
|
|
w = itm->type->weight * itm->number;
|
|
|
|
|
n += w;
|
|
|
|
|
if( !fval(itm->type, ITF_BIG))
|
|
|
|
|
in_bag += w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n += u->number * u->race->weight;
|
|
|
|
|
|
|
|
|
|
w = get_item(u, I_BAG_OF_HOLDING) * BAGCAPACITY;
|
|
|
|
|
if( w > in_bag )
|
|
|
|
|
w = in_bag;
|
|
|
|
|
n -= w;
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
make_undead_unit(unit * u)
|
|
|
|
|
{
|
|
|
|
|
free_orders(&u->orders);
|
|
|
|
|
name_unit(u);
|
|
|
|
|
fset(u, UFL_ISNEW);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int guard_flags(const unit * u)
|
|
|
|
|
{
|
|
|
|
|
unsigned int flags = GUARD_CREWS | GUARD_LANDING | GUARD_TRAVELTHRU | GUARD_TAX;
|
|
|
|
|
#if GUARD_DISABLES_PRODUCTION == 1
|
|
|
|
|
flags |= GUARD_PRODUCE;
|
|
|
|
|
#endif
|
|
|
|
|
#if GUARD_DISABLES_RECRUIT == 1
|
|
|
|
|
flags |= GUARD_RECRUIT;
|
|
|
|
|
#endif
|
|
|
|
|
switch (old_race(u->race)) {
|
|
|
|
|
case RC_ELF:
|
|
|
|
|
if (u->faction->race != u->race) break;
|
|
|
|
|
/* else fallthrough */
|
|
|
|
|
case RC_TREEMAN:
|
|
|
|
|
flags |= GUARD_TREES;
|
|
|
|
|
break;
|
|
|
|
|
case RC_IRONKEEPER:
|
|
|
|
|
flags = GUARD_MINING;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
guard(unit * u, unsigned int mask)
|
|
|
|
|
{
|
|
|
|
|
unsigned int flags = guard_flags(u);
|
|
|
|
|
setguard(u, flags & mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
besieged(const unit * u)
|
|
|
|
|
{
|
|
|
|
|
/* belagert kann man in schiffen und burgen werden */
|
|
|
|
|
return (u && !global.disabled[K_BESIEGE]
|
|
|
|
|
&& u->building && u->building->besieged
|
|
|
|
|
&& u->building->besieged >= u->building->size * SIEGEFACTOR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lifestyle(const unit * u)
|
|
|
|
|
{
|
|
|
|
|
int need;
|
|
|
|
|
plane * pl;
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
if (gamecookie!=global.cookie) {
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_monsters(u->faction)) return 0;
|
|
|
|
|
|
|
|
|
|
need = maintenance_cost(u);
|
|
|
|
|
|
|
|
|
|
pl = rplane(u->region);
|
|
|
|
|
if (pl && fval(pl, PFL_NOFEED))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return need;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean has_horses(const struct unit * u)
|
|
|
|
|
{
|
|
|
|
|
item * itm = u->items;
|
|
|
|
|
for (;itm;itm=itm->next) {
|
|
|
|
|
if (itm->type->flags&ITF_ANIMAL) return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
hunger(int number, unit * u)
|
|
|
|
|
{
|
|
|
|
|
region * r = u->region;
|
|
|
|
|
int dead = 0, hpsub = 0;
|
|
|
|
|
int hp = u->hp / u->number;
|
|
|
|
|
static const char * damage = 0;
|
|
|
|
|
static const char * rcdamage = 0;
|
|
|
|
|
static const race * rc = 0;
|
|
|
|
|
|
|
|
|
|
if (!damage) {
|
|
|
|
|
damage = get_param(global.parameters, "hunger.damage");
|
|
|
|
|
if (damage==NULL) damage = "1d12+12";
|
|
|
|
|
}
|
|
|
|
|
if (rc!=u->race) {
|
|
|
|
|
rcdamage = get_param(u->race->parameters, "hunger.damage");
|
|
|
|
|
rc = u->race;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (number--) {
|
|
|
|
|
int dam = dice_rand(rcdamage?rcdamage:damage);
|
|
|
|
|
if (dam >= hp) {
|
|
|
|
|
++dead;
|
|
|
|
|
} else {
|
|
|
|
|
hpsub += dam;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dead) {
|
|
|
|
|
/* Gestorbene aus der Einheit nehmen,
|
|
|
|
|
* Sie bekommen keine Beerdingung. */
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("starvation",
|
|
|
|
|
"unit region dead live", u, r, dead, u->number-dead));
|
|
|
|
|
|
|
|
|
|
scale_number(u, u->number - dead);
|
|
|
|
|
deathcounts(r, dead);
|
|
|
|
|
}
|
|
|
|
|
if (hpsub > 0) {
|
|
|
|
|
/* Jetzt die Sch<63>den der nicht gestorbenen abziehen. */
|
|
|
|
|
u->hp -= hpsub;
|
|
|
|
|
/* Meldung nur, wenn noch keine f<>r Tote generiert. */
|
|
|
|
|
if (dead == 0) {
|
|
|
|
|
/* Durch unzureichende Ern<72>hrung wird %s geschw<68>cht */
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("malnourish",
|
|
|
|
|
"unit region", u, r));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (dead || hpsub);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
plagues(region * r, boolean ismagic)
|
|
|
|
|
{
|
|
|
|
|
int peasants;
|
|
|
|
|
int i;
|
|
|
|
|
int dead = 0;
|
|
|
|
|
|
|
|
|
|
/* Seuchenwahrscheinlichkeit in % */
|
|
|
|
|
|
|
|
|
|
if (!ismagic) {
|
|
|
|
|
double mwp = MAX(maxworkingpeasants(r), 1);
|
|
|
|
|
double prob = pow(rpeasants(r) / (mwp * wage(r, NULL, NULL, turn) * 0.13), 4.0)
|
|
|
|
|
* PLAGUE_CHANCE;
|
|
|
|
|
|
|
|
|
|
if (rng_double() >= prob) return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
peasants = rpeasants(r);
|
|
|
|
|
dead = (int)(0.5F + PLAGUE_VICTIMS * peasants);
|
|
|
|
|
for (i = dead; i != 0; i--) {
|
|
|
|
|
if (rng_double() < PLAGUE_HEALCHANCE && rmoney(r) >= PLAGUE_HEALCOST) {
|
|
|
|
|
rsetmoney(r, rmoney(r) - PLAGUE_HEALCOST);
|
|
|
|
|
} else {
|
|
|
|
|
--dead;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dead > 0) {
|
|
|
|
|
message * msg = add_message(&r->msgs, msg_message("pest", "dead", dead));
|
|
|
|
|
msg_release(msg);
|
|
|
|
|
deathcounts(r, dead);
|
|
|
|
|
rsetpeasants(r, peasants - dead);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lohn bei den einzelnen Burgstufen f<>r Normale Typen, Orks, Bauern,
|
|
|
|
|
* Modifikation f<EFBFBD>r St<EFBFBD>dter. */
|
|
|
|
|
|
|
|
|
|
static const int wagetable[7][4] = {
|
|
|
|
|
{10, 10, 11, -7}, /* Baustelle */
|
|
|
|
|
{10, 10, 11, -5}, /* Handelsposten */
|
|
|
|
|
{11, 11, 12, -3}, /* Befestigung */
|
|
|
|
|
{12, 11, 13, -1}, /* Turm */
|
|
|
|
|
{13, 12, 14, 0}, /* Burg */
|
|
|
|
|
{14, 12, 15, 1}, /* Festung */
|
|
|
|
|
{15, 13, 16, 2} /* Zitadelle */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cmp_wage(const struct building * b, const building * a)
|
|
|
|
|
{
|
|
|
|
|
static const struct building_type * bt_castle;
|
|
|
|
|
if (!bt_castle) bt_castle = bt_find("castle");
|
|
|
|
|
if (b->type==bt_castle) {
|
|
|
|
|
if (!a) return 1;
|
|
|
|
|
if (b->size>a->size) return 1;
|
|
|
|
|
if (b->size==a->size) return 0;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean is_owner_building(const struct building * b)
|
|
|
|
|
{
|
|
|
|
|
region * r = b->region;
|
|
|
|
|
if (b->type->taxes && r->land && r->land->ownership) {
|
|
|
|
|
unit * u = building_owner(b);
|
|
|
|
|
return u && u->faction == r->land->ownership->owner;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cmp_taxes(const building * b, const building * a)
|
|
|
|
|
{
|
|
|
|
|
faction * f = region_get_owner(b->region);
|
|
|
|
|
if (b->type->taxes) {
|
|
|
|
|
unit * u = building_owner(b);
|
|
|
|
|
if (!u) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (a) {
|
|
|
|
|
int newsize = buildingeffsize(b, false);
|
|
|
|
|
double newtaxes = b->type->taxes(b, newsize);
|
|
|
|
|
int oldsize = buildingeffsize(a, false);
|
|
|
|
|
double oldtaxes = a->type->taxes(a, oldsize);
|
|
|
|
|
|
|
|
|
|
if (newtaxes<oldtaxes) return -1;
|
|
|
|
|
else if (newtaxes>oldtaxes) return 1;
|
|
|
|
|
else if (b->size<a->size) return -1;
|
|
|
|
|
else if (b->size>a->size) return 1;
|
|
|
|
|
else {
|
|
|
|
|
if (u && u->faction==f) {
|
|
|
|
|
u = building_owner(a);
|
|
|
|
|
if (u && u->faction==f) return -1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cmp_current_owner(const building * b, const building * a)
|
|
|
|
|
{
|
|
|
|
|
faction * f = region_get_owner(b->region);
|
|
|
|
|
|
|
|
|
|
assert(rule_region_owners());
|
|
|
|
|
if (f && b->type->taxes) {
|
|
|
|
|
unit * u = building_owner(b);
|
|
|
|
|
if (!u || u->faction!=f) return -1;
|
|
|
|
|
if (a) {
|
|
|
|
|
int newsize = buildingeffsize(b, false);
|
|
|
|
|
double newtaxes = b->type->taxes(b, newsize);
|
|
|
|
|
int oldsize = buildingeffsize(a, false);
|
|
|
|
|
double oldtaxes = a->type->taxes(a, oldsize);
|
|
|
|
|
|
|
|
|
|
if (newtaxes!=oldtaxes) return (newtaxes>oldtaxes)?1:-1;
|
|
|
|
|
if (newsize!=oldsize) return newsize-oldsize;
|
|
|
|
|
return (b->size-a->size);
|
|
|
|
|
} else {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_stealth_faction(void)
|
|
|
|
|
{
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
static int rule = -1;
|
|
|
|
|
if (rule<0 || gamecookie!=global.cookie) {
|
|
|
|
|
rule = get_param_int(global.parameters, "rules.stealth.faction", 1);
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
assert(rule>=0);
|
|
|
|
|
}
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_region_owners(void)
|
|
|
|
|
{
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
static int rule = -1;
|
|
|
|
|
if (rule<0 || gamecookie!=global.cookie) {
|
|
|
|
|
rule = get_param_int(global.parameters, "rules.region_owners", 0);
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
assert(rule>=0);
|
|
|
|
|
}
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_auto_taxation(void)
|
|
|
|
|
{
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
static int rule = -1;
|
|
|
|
|
if (rule<0 || gamecookie!=global.cookie) {
|
|
|
|
|
rule = get_param_int(global.parameters, "rules.economy.taxation", TAX_ORDER);
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
assert(rule>=0);
|
|
|
|
|
}
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_blessed_harvest(void)
|
|
|
|
|
{
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
static int rule = -1;
|
|
|
|
|
if (rule<0 || gamecookie!=global.cookie) {
|
|
|
|
|
rule = get_param_int(global.parameters, "rules.magic.blessed_harvest", HARVEST_WORK);
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
assert(rule>=0);
|
|
|
|
|
}
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_alliance_limit(void)
|
|
|
|
|
{
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
static int rule = -1;
|
|
|
|
|
if (rule<0 || gamecookie!=global.cookie) {
|
|
|
|
|
rule = get_param_int(global.parameters, "rules.limit.alliance", 0);
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
assert(rule>=0);
|
|
|
|
|
}
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_faction_limit(void)
|
|
|
|
|
{
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
static int rule = -1;
|
|
|
|
|
if (rule<0 || gamecookie!=global.cookie) {
|
|
|
|
|
rule = get_param_int(global.parameters, "rules.limit.faction", 0);
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
assert(rule>=0);
|
|
|
|
|
}
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_transfermen(void)
|
|
|
|
|
{
|
|
|
|
|
static int gamecookie = -1;
|
|
|
|
|
static int rule = -1;
|
|
|
|
|
if (rule<0 || gamecookie!=global.cookie) {
|
|
|
|
|
rule = get_param_int(global.parameters, "rules.transfermen", 1);
|
|
|
|
|
gamecookie = global.cookie;
|
|
|
|
|
assert(rule>=0);
|
|
|
|
|
}
|
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
default_wage(const region *r, const faction * f, const race * rc, int in_turn)
|
|
|
|
|
{
|
|
|
|
|
building *b = largestbuilding(r, &cmp_wage, false);
|
|
|
|
|
int esize = 0;
|
|
|
|
|
curse * c;
|
|
|
|
|
double wage;
|
|
|
|
|
attrib *a;
|
|
|
|
|
const building_type *artsculpture_type = bt_find("artsculpture");
|
|
|
|
|
static const curse_type * drought_ct, * blessedharvest_ct;
|
|
|
|
|
static boolean init;
|
|
|
|
|
|
|
|
|
|
if (!init) {
|
|
|
|
|
init = true;
|
|
|
|
|
drought_ct = ct_find("drought");
|
|
|
|
|
blessedharvest_ct = ct_find("blessedharvest");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (b!=NULL) {
|
|
|
|
|
/* TODO: this reveals imaginary castles */
|
|
|
|
|
esize = buildingeffsize(b, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (f!=NULL) {
|
|
|
|
|
int index = 0;
|
|
|
|
|
if (rc==new_race[RC_ORC] || rc==new_race[RC_SNOTLING]) {
|
|
|
|
|
index = 1;
|
|
|
|
|
}
|
|
|
|
|
wage = wagetable[esize][index];
|
|
|
|
|
} else {
|
|
|
|
|
if (is_mourning(r, in_turn)) {
|
|
|
|
|
wage = 10;
|
|
|
|
|
} else if (fval(r->terrain, SEA_REGION)) {
|
|
|
|
|
wage = 11;
|
|
|
|
|
} else if (fval(r, RF_ORCIFIED)) {
|
|
|
|
|
wage = wagetable[esize][1];
|
|
|
|
|
} else {
|
|
|
|
|
wage = wagetable[esize][2];
|
|
|
|
|
}
|
|
|
|
|
if (rule_blessed_harvest()==HARVEST_WORK) {
|
|
|
|
|
/* E1 rules */
|
|
|
|
|
wage += curse_geteffect(get_curse(r->attribs, blessedharvest_ct));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Artsculpture: Income +5 */
|
|
|
|
|
for(b=r->buildings; b; b=b->next) {
|
|
|
|
|
if(b->type == artsculpture_type) {
|
|
|
|
|
wage += 5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Godcurse: Income -10 */
|
|
|
|
|
if (curse_active(get_curse(r->attribs, ct_find("godcursezone")))) {
|
|
|
|
|
wage = MAX(0,wage-10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Bei einer D<>rre verdient man nur noch ein Viertel */
|
|
|
|
|
if (drought_ct) {
|
|
|
|
|
c = get_curse(r->attribs, drought_ct);
|
|
|
|
|
if (curse_active(c)) wage /= curse_geteffect(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = a_find(r->attribs, &at_reduceproduction);
|
|
|
|
|
if (a) wage = (wage * a->data.sa[0])/100;
|
|
|
|
|
|
|
|
|
|
return (int)wage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
minimum_wage(const region *r, const faction * f, const race * rc, int in_turn)
|
|
|
|
|
{
|
|
|
|
|
if (f && rc) {
|
|
|
|
|
return rc->maintenance;
|
|
|
|
|
}
|
|
|
|
|
return default_wage(r, f, rc, in_turn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Gibt Arbeitslohn f<>r entsprechende Rasse zur<75>ck, oder f<>r
|
|
|
|
|
* die Bauern wenn f == NULL. */
|
|
|
|
|
int
|
|
|
|
|
wage(const region *r, const faction * f, const race * rc, int in_turn)
|
|
|
|
|
{
|
|
|
|
|
if (global.functions.wage) {
|
|
|
|
|
return global.functions.wage(r, f, rc, in_turn);
|
|
|
|
|
}
|
|
|
|
|
return default_wage(r, f, rc, in_turn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define MAINTENANCE 10
|
|
|
|
|
int
|
|
|
|
|
maintenance_cost(const struct unit * u)
|
|
|
|
|
{
|
|
|
|
|
if (u==NULL) return MAINTENANCE;
|
|
|
|
|
if (global.functions.maintenance) {
|
|
|
|
|
int retval = global.functions.maintenance(u);
|
|
|
|
|
if (retval>=0) return retval;
|
|
|
|
|
}
|
|
|
|
|
return u->race->maintenance * u->number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
message *
|
|
|
|
|
movement_error(unit * u, const char * token, order * ord, int error_code)
|
|
|
|
|
{
|
|
|
|
|
direction_t d;
|
|
|
|
|
switch (error_code) {
|
|
|
|
|
case E_MOVE_BLOCKED:
|
|
|
|
|
d = finddirection(token, u->faction->locale);
|
|
|
|
|
return msg_message("moveblocked", "unit direction", u, d);
|
|
|
|
|
case E_MOVE_NOREGION:
|
|
|
|
|
return msg_feedback(u, ord, "unknowndirection", "dirname", token);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
movewhere(const unit *u, const char * token, region * r, region** resultp)
|
|
|
|
|
{
|
|
|
|
|
region * r2;
|
|
|
|
|
direction_t d;
|
|
|
|
|
|
|
|
|
|
if (*token == '\0') {
|
|
|
|
|
*resultp = NULL;
|
|
|
|
|
return E_MOVE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d = finddirection(token, u->faction->locale);
|
|
|
|
|
switch (d) {
|
|
|
|
|
case D_PAUSE:
|
|
|
|
|
*resultp = r;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NODIRECTION:
|
|
|
|
|
r2 = find_special_direction(r, token, u->faction->locale);
|
|
|
|
|
if (r2==NULL) {
|
|
|
|
|
return E_MOVE_NOREGION;
|
|
|
|
|
}
|
|
|
|
|
*resultp = r2;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
r2 = rconnect(r, d);
|
|
|
|
|
if (r2==NULL || move_blocked(u, r, r2)) {
|
|
|
|
|
return E_MOVE_BLOCKED;
|
|
|
|
|
}
|
|
|
|
|
*resultp = r2;
|
|
|
|
|
}
|
|
|
|
|
return E_MOVE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
move_blocked(const unit * u, const region *r, const region *r2)
|
|
|
|
|
{
|
|
|
|
|
connection * b;
|
|
|
|
|
curse * c;
|
|
|
|
|
static const curse_type * fogtrap_ct = NULL;
|
|
|
|
|
|
|
|
|
|
if (r2==NULL) return true;
|
|
|
|
|
b = get_borders(r, r2);
|
|
|
|
|
while (b) {
|
|
|
|
|
if (b->type->block && b->type->block(b, u, r)) return true;
|
|
|
|
|
b = b->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fogtrap_ct==NULL) fogtrap_ct = ct_find("fogtrap");
|
|
|
|
|
c = get_curse(r->attribs, fogtrap_ct);
|
|
|
|
|
if (curse_active(c)) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
add_income(unit * u, int type, int want, int qty)
|
|
|
|
|
{
|
|
|
|
|
if (want==INT_MAX) want = qty;
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("income", "unit region mode wanted amount",
|
|
|
|
|
u, u->region, type, want, qty));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
reorder_units(region * r)
|
|
|
|
|
{
|
|
|
|
|
unit ** unext = &r->units;
|
|
|
|
|
|
|
|
|
|
if (r->buildings) {
|
|
|
|
|
building * b = r->buildings;
|
|
|
|
|
while (*unext && b) {
|
|
|
|
|
unit ** ufirst = unext; /* where the first unit in the building should go */
|
|
|
|
|
unit ** umove = unext; /* a unit we consider moving */
|
|
|
|
|
unit * owner = NULL;
|
|
|
|
|
while (*umove) {
|
|
|
|
|
unit * u = *umove;
|
|
|
|
|
if (u->number && u->building==b) {
|
|
|
|
|
unit ** uinsert = unext;
|
|
|
|
|
if (fval(u, UFL_OWNER)) {
|
|
|
|
|
uinsert = ufirst;
|
|
|
|
|
owner = u;
|
|
|
|
|
}
|
|
|
|
|
if (umove!=uinsert) {
|
|
|
|
|
*umove = u->next;
|
|
|
|
|
u->next = *uinsert;
|
|
|
|
|
*uinsert = u;
|
|
|
|
|
} else {
|
|
|
|
|
/* no need to move, skip ahead */
|
|
|
|
|
umove = &u->next;
|
|
|
|
|
}
|
|
|
|
|
if (unext==uinsert) {
|
|
|
|
|
/* we have a new well-placed unit. jump over it */
|
|
|
|
|
unext = &u->next;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
umove = &u->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!owner && ufirst!=unext) {
|
|
|
|
|
owner = *ufirst;
|
|
|
|
|
fset(owner, UFL_OWNER);
|
|
|
|
|
}
|
|
|
|
|
b = b->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r->ships) {
|
|
|
|
|
ship * sh = r->ships;
|
|
|
|
|
/* first, move all units up that are not on ships */
|
|
|
|
|
unit ** umove = unext; /* a unit we consider moving */
|
|
|
|
|
while (*umove) {
|
|
|
|
|
unit * u = *umove;
|
|
|
|
|
if (u->number && !u->ship) {
|
|
|
|
|
if (umove!=unext) {
|
|
|
|
|
*umove = u->next;
|
|
|
|
|
u->next = *unext;
|
|
|
|
|
*unext = u;
|
|
|
|
|
} else {
|
|
|
|
|
/* no need to move, skip ahead */
|
|
|
|
|
umove = &u->next;
|
|
|
|
|
}
|
|
|
|
|
/* we have a new well-placed unit. jump over it */
|
|
|
|
|
unext = &u->next;
|
|
|
|
|
} else {
|
|
|
|
|
umove = &u->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (*unext && sh) {
|
|
|
|
|
unit ** ufirst = unext; /* where the first unit in the building should go */
|
|
|
|
|
unit ** umove = unext; /* a unit we consider moving */
|
|
|
|
|
unit * owner = NULL;
|
|
|
|
|
while (*umove) {
|
|
|
|
|
unit * u = *umove;
|
|
|
|
|
if (u->number && u->ship==sh) {
|
|
|
|
|
unit ** uinsert = unext;
|
|
|
|
|
if (fval(u, UFL_OWNER)) {
|
|
|
|
|
uinsert = ufirst;
|
|
|
|
|
owner = u;
|
|
|
|
|
}
|
|
|
|
|
if (umove!=uinsert) {
|
|
|
|
|
*umove = u->next;
|
|
|
|
|
u->next = *uinsert;
|
|
|
|
|
*uinsert = u;
|
|
|
|
|
} else {
|
|
|
|
|
/* no need to move, skip ahead */
|
|
|
|
|
umove = &u->next;
|
|
|
|
|
}
|
|
|
|
|
if (unext==uinsert) {
|
|
|
|
|
/* we have a new well-placed unit. jump over it */
|
|
|
|
|
unext = &u->next;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
umove = &u->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!owner && ufirst!=unext) {
|
|
|
|
|
owner = *ufirst;
|
|
|
|
|
fset(owner, UFL_OWNER);
|
|
|
|
|
}
|
|
|
|
|
sh = sh->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
produceexp(struct unit * u, skill_t sk, int n)
|
|
|
|
|
{
|
|
|
|
|
if (global.producexpchance>0.0F) {
|
|
|
|
|
if (n==0 || !playerrace(u->race)) return 0;
|
|
|
|
|
learn_skill(u, sk, global.producexpchance);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lovar(double xpct_x2)
|
|
|
|
|
{
|
|
|
|
|
int n = (int)(xpct_x2 * 500)+1;
|
|
|
|
|
if (n==0) return 0;
|
|
|
|
|
return (rng_int() % n + rng_int() % n)/1000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
has_limited_skills (const struct unit * u)
|
|
|
|
|
{
|
|
|
|
|
if (has_skill(u, SK_MAGIC) || has_skill(u, SK_ALCHEMY) ||
|
|
|
|
|
has_skill(u, SK_TACTICS) || has_skill(u, SK_HERBALISM) ||
|
|
|
|
|
has_skill(u, SK_SPY)) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
attrib_init(void)
|
|
|
|
|
{
|
|
|
|
|
/* Alle speicherbaren Attribute m<>ssen hier registriert werden */
|
|
|
|
|
at_register(&at_shiptrail);
|
|
|
|
|
at_register(&at_familiar);
|
|
|
|
|
at_register(&at_familiarmage);
|
|
|
|
|
at_register(&at_clone);
|
|
|
|
|
at_register(&at_clonemage);
|
|
|
|
|
at_register(&at_eventhandler);
|
|
|
|
|
at_register(&at_stealth);
|
|
|
|
|
at_register(&at_mage);
|
|
|
|
|
at_register(&at_countdown);
|
|
|
|
|
at_register(&at_curse);
|
|
|
|
|
|
|
|
|
|
at_register(&at_seenspell);
|
|
|
|
|
|
|
|
|
|
/* neue REGION-Attribute */
|
|
|
|
|
at_register(&at_direction);
|
|
|
|
|
at_register(&at_moveblock);
|
|
|
|
|
at_register(&at_deathcount);
|
|
|
|
|
at_register(&at_chaoscount);
|
|
|
|
|
at_register(&at_woodcount);
|
|
|
|
|
|
|
|
|
|
/* neue UNIT-Attribute */
|
|
|
|
|
at_register(&at_siege);
|
|
|
|
|
at_register(&at_effect);
|
|
|
|
|
at_register(&at_private);
|
|
|
|
|
|
|
|
|
|
at_register(&at_icastle);
|
|
|
|
|
at_register(&at_guard);
|
|
|
|
|
at_register(&at_group);
|
|
|
|
|
|
|
|
|
|
at_register(&at_building_generic_type);
|
|
|
|
|
at_register(&at_maxmagicians);
|
|
|
|
|
at_register(&at_npcfaction);
|
|
|
|
|
|
|
|
|
|
/* connection-typen */
|
|
|
|
|
register_bordertype(&bt_noway);
|
|
|
|
|
register_bordertype(&bt_fogwall);
|
|
|
|
|
register_bordertype(&bt_wall);
|
|
|
|
|
register_bordertype(&bt_illusionwall);
|
|
|
|
|
register_bordertype(&bt_road);
|
|
|
|
|
register_bordertype(&bt_questportal);
|
|
|
|
|
|
|
|
|
|
register_function((pf_generic)&minimum_wage, "minimum_wage");
|
|
|
|
|
|
|
|
|
|
at_register(&at_germs);
|
|
|
|
|
#if XECMD_MODULE
|
|
|
|
|
at_register(&at_xontormiaexpress); /* required for old datafiles */
|
|
|
|
|
#endif
|
|
|
|
|
at_register(&at_speedup);
|
|
|
|
|
at_register(&at_building_action);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
kernel_init(void)
|
|
|
|
|
{
|
|
|
|
|
char zBuffer[MAX_PATH];
|
|
|
|
|
attrib_init();
|
|
|
|
|
translation_init();
|
|
|
|
|
|
|
|
|
|
if (sqlpatch) {
|
|
|
|
|
sprintf(zBuffer, "%s/patch-%d.sql", datapath(), turn);
|
|
|
|
|
sql_init(zBuffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
order *
|
|
|
|
|
default_order(const struct locale * lang)
|
|
|
|
|
{
|
|
|
|
|
return parse_order(locale_string(lang, "defaultorder"), lang);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
entertainmoney(const region *r)
|
|
|
|
|
{
|
|
|
|
|
double n;
|
|
|
|
|
|
|
|
|
|
if (is_cursed(r->attribs, C_DEPRESSION, 0)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = rmoney(r) / ENTERTAINFRACTION;
|
|
|
|
|
|
|
|
|
|
if (is_cursed(r->attribs, C_GENEROUS, 0)) {
|
|
|
|
|
n *= get_curseeffect(r->attribs, C_GENEROUS, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (int)n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rule_give(void)
|
|
|
|
|
{
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "rules.give", GIVE_DEFAULT);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int markets_module(void)
|
|
|
|
|
{
|
|
|
|
|
static int value = -1;
|
|
|
|
|
if (value<0) {
|
|
|
|
|
value = get_param_int(global.parameters, "modules.markets", 0);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** releases all memory associated with the game state.
|
|
|
|
|
* call this function before calling read_game() to load a new game
|
|
|
|
|
* if you have a previously loaded state in memory.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
free_gamedata(void)
|
|
|
|
|
{
|
|
|
|
|
free_units();
|
|
|
|
|
free_regions();
|
|
|
|
|
free_borders();
|
|
|
|
|
|
|
|
|
|
while (alliances) {
|
|
|
|
|
alliance * al = alliances;
|
|
|
|
|
alliances = al->next;
|
|
|
|
|
free_alliance(al);
|
|
|
|
|
}
|
|
|
|
|
while (factions) {
|
|
|
|
|
faction * f = factions;
|
|
|
|
|
factions = f->next;
|
|
|
|
|
funhash(f);
|
|
|
|
|
free_faction(f);
|
|
|
|
|
free(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (planes) {
|
|
|
|
|
plane * pl = planes;
|
|
|
|
|
planes = planes->next;
|
|
|
|
|
free(pl->name);
|
|
|
|
|
free(pl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (global.attribs) {
|
|
|
|
|
a_remove(&global.attribs, global.attribs);
|
|
|
|
|
}
|
|
|
|
|
++global.cookie; /* readgame() already does this, but sjust in case */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
load_inifile(dictionary * d)
|
|
|
|
|
{
|
|
|
|
|
const char * reportdir = reportpath();
|
|
|
|
|
const char * datadir = datapath();
|
|
|
|
|
const char * basedir = basepath();
|
|
|
|
|
const char * str;
|
|
|
|
|
|
|
|
|
|
assert(d);
|
|
|
|
|
|
|
|
|
|
str = iniparser_getstring(d, "eressea:base", basedir);
|
|
|
|
|
if (str!=basedir) set_basepath(str);
|
|
|
|
|
str = iniparser_getstring(d, "eressea:report", reportdir);
|
|
|
|
|
if (str!=reportdir) set_reportpath(str);
|
|
|
|
|
str = iniparser_getstring(d, "eressea:data", datadir);
|
|
|
|
|
if (str!=datadir) set_datapath(str);
|
|
|
|
|
|
|
|
|
|
lomem = iniparser_getint(d, "eressea:lomem", lomem)?1:0;
|
|
|
|
|
|
|
|
|
|
str = iniparser_getstring(d, "eressea:encoding", NULL);
|
|
|
|
|
if (str) enc_gamedata = get_encoding_by_name(str);
|
|
|
|
|
|
|
|
|
|
verbosity = iniparser_getint(d, "eressea:verbose", 2);
|
|
|
|
|
sqlpatch = iniparser_getint(d, "eressea:sqlpatch", false);
|
|
|
|
|
battledebug = iniparser_getint(d, "eressea:debug", battledebug)?1:0;
|
|
|
|
|
|
|
|
|
|
str = iniparser_getstring(d, "eressea:locales", "de,en");
|
|
|
|
|
make_locales(str);
|
|
|
|
|
|
|
|
|
|
/* excerpt from [config] (the rest is used in bindings.c) */
|
|
|
|
|
game_name = iniparser_getstring(d, "config:game", game_name);
|
|
|
|
|
|
|
|
|
|
global.inifile = d;
|
|
|
|
|
}
|