server/src/common/kernel/eressea.c

2987 lines
61 KiB
C
Raw Normal View History

2001-01-25 10:37:55 +01:00
/* vi: set ts=2:
*
* Eressea PB(E)M host Copyright (C) 1998-2003
2001-01-25 10:37:55 +01:00
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
* Enno Rehling (enno@eressea.de)
2001-01-25 10:37:55 +01:00
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*
* based on:
*
* Atlantis v1.0 13 September 1993 Copyright 1993 by Russell Wallace
* Atlantis v1.7 Copyright 1996 by Alex Schr<EFBFBD>der
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
* This program may not be sold or used commercially without prior written
* permission from the authors.
*/
#include <config.h>
#include <kernel/eressea.h>
2001-01-25 10:37:55 +01:00
/* attributes includes */
#include <attributes/reduceproduction.h>
#include <attributes/gm.h>
/* kernel includes */
#include "alliance.h"
2004-06-21 18:45:27 +02:00
#include "alchemy.h"
#include "battle.h"
#include "border.h"
#include "building.h"
#include "calendar.h"
#include "curse.h"
2001-01-25 10:37:55 +01:00
#include "faction.h"
2004-06-21 18:45:27 +02:00
#include "group.h"
2001-01-25 10:37:55 +01:00
#include "item.h"
2004-06-21 18:45:27 +02:00
#include "karma.h"
2001-01-25 10:37:55 +01:00
#include "magic.h"
2004-06-21 18:45:27 +02:00
#include "message.h"
2007-08-05 14:26:46 +02:00
#include "move.h"
2004-06-21 18:45:27 +02:00
#include "names.h"
#include "objtypes.h"
#include "order.h"
#include "plane.h"
2001-01-25 10:37:55 +01:00
#include "pool.h"
2004-06-21 18:45:27 +02:00
#include "race.h"
2001-01-25 10:37:55 +01:00
#include "region.h"
2004-06-21 18:45:27 +02:00
#include "save.h"
2001-01-25 10:37:55 +01:00
#include "ship.h"
2004-06-21 18:45:27 +02:00
#include "skill.h"
#include "terrain.h"
2004-06-21 18:45:27 +02:00
#include "unit.h"
2001-01-25 10:37:55 +01:00
/* util includes */
2007-06-20 02:34:02 +02:00
#include <util/attrib.h>
#include <util/base36.h>
#include <util/crmessage.h>
#include <util/event.h>
#include <util/functions.h>
#include <util/log.h>
2007-06-20 02:34:02 +02:00
#include <util/lists.h>
#include <util/parser.h>
#include <util/rng.h>
#include <util/sql.h>
#include <util/translation.h>
#include <util/umlaut.h>
#include <util/xml.h>
#include <util/bsdstring.h>
#include <util/unicode.h>
/* libxml includes */
#include <libxml/tree.h>
#include <libxml/xpath.h>
2001-01-25 10:37:55 +01:00
/* 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>
2001-01-25 10:37:55 +01:00
2008-04-25 17:46:39 +02:00
#define PTRIES 0 /* it turns out they are slow :-( */
#if PTRIES
#include <util/patricia.h>
#endif
2001-01-25 10:37:55 +01:00
/* exported variables */
region *regions;
2001-01-25 10:37:55 +01:00
faction *factions;
settings global;
FILE *logfile;
FILE *updatelog;
const struct race * new_race[MAXRACES];
2002-10-13 13:08:04 +02:00
boolean sqlpatch = false;
int turn;
#if XECMD_MODULE
attrib_type at_xontormiaexpress = {
"xontormiaexpress",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
a_writeint,
a_readint,
ATF_UNIQUE
};
#endif
2005-01-08 00:25:38 +01:00
int
NewbieImmunity(void) {
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "NewbieImmunity");
value = str?atoi(str):0;
}
return value;
}
static int
MaxAge(void) {
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "MaxAge");
value = str?atoi(str):0;
}
return value;
}
static int
ally_flag(const char * s)
{
if (strcmp(s, "money")==0) return HELP_MONEY;
if (strcmp(s, "fight")==0) return HELP_FIGHT;
if (strcmp(s, "give")==0) return HELP_GIVE;
if (strcmp(s, "guard")==0) return HELP_GUARD;
if (strcmp(s, "stealth")==0) return HELP_FSTEALTH;
if (strcmp(s, "travel")==0) return HELP_TRAVEL;
return 0;
}
boolean
ExpensiveMigrants(void)
{
int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "study.expensivemigrants");
value = str?atoi(str):0;
}
return value;
}
int
AllianceAuto(void)
{
static int value = -1;
if (value<0) {
2004-06-27 20:56:03 +02:00
const char * str = get_param(global.parameters, "alliance.auto");
value = 0;
if (str!=NULL) {
char * sstr = strdup(str);
2004-06-27 20:56:03 +02:00
char * tok = strtok(sstr, " ");
while (tok) {
value |= ally_flag(tok);
tok = strtok(NULL, " ");
}
2004-06-27 20:56:03 +02:00
free(sstr);
}
}
return value;
}
int
AllianceRestricted(void)
{
static int value = -1;
if (value<0) {
2004-06-27 20:56:03 +02:00
const char * str = get_param(global.parameters, "alliance.restricted");
value = 0;
if (str!=NULL) {
char * sstr = strdup(str);
2004-06-27 20:56:03 +02:00
char * tok = strtok(sstr, " ");
while (tok) {
value |= ally_flag(tok);
tok = strtok(NULL, " ");
}
2004-06-27 20:56:03 +02:00
free(sstr);
}
}
return value;
}
int
2004-08-03 17:34:26 +02:00
LongHunger(const struct unit * u) {
static int value = -1;
2004-08-03 17:34:26 +02:00
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) {
const char * str = get_param(global.parameters, "hunger.long");
value = str?atoi(str):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) {
const char * str = get_param(global.parameters, "skill.maxlevel");
value = str?atoi(str):0;
}
return value;
}
boolean
TradeDisabled(void) {
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "trade.disabled");
value = str?atoi(str):0;
}
return value;
}
int
NMRTimeout(void) {
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "nmr.timeout");
value = str?atoi(str):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;
}
2001-01-25 10:37:55 +01:00
const char *directions[MAXDIRECTIONS+2] =
{
"northwest",
"northeast",
"east",
"southeast",
"southwest",
"west",
"",
"pause"
2001-01-25 10:37:55 +01:00
};
/** Returns the English name of the race, which is what the database uses.
*/
2002-11-02 18:24:41 +01:00
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;
2002-11-02 18:24:41 +01:00
}
2001-01-25 10:37:55 +01:00
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"
2001-01-25 10:37:55 +01:00
};
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",
"MEINUNG",
"MAGIEGEBIET",
"PIRATERIE",
"NEUSTART",
"GRUPPE",
"OPFERE",
"BETEN",
"SORTIEREN",
"JIHAD",
"GM",
"INFO",
"PRAEFIX",
"PFLANZEN",
"WERWESEN",
"XONTORMIA",
2004-08-03 17:34:26 +02:00
"ALLIANZ",
"BEANSPRUCHEN",
"PROMOTION",
2001-01-25 10:37:55 +01:00
};
const char *report_options[MAX_MSG] =
{
"Kampf",
"Ereignisse",
"Bewegung",
"Einkommen",
"Handel",
"Produktion",
"Orkvermehrung",
"Zauber",
"",
""
2001-01-25 10:37:55 +01:00
};
const char *message_levels[ML_MAX] =
{
"Wichtig",
"Debug",
"Fehler",
"Warnungen",
"Infos"
2001-01-25 10:37:55 +01:00
};
const char *options[MAXOPTIONS] =
{
"AUSWERTUNG",
"COMPUTER",
"ZUGVORLAGE",
NULL,
"STATISTIK",
"DEBUG",
"ZIPPED",
"ZEITUNG", /* Option hat Sonderbehandlung! */
NULL,
"ADRESSEN",
"BZIP2",
"PUNKTE",
"SHOWSKCHANGE",
"XML"
2001-01-25 10:37:55 +01:00
};
static int
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
allied_skillcount(const faction * f, skill_t sk)
{
int num = 0;
alliance * a = f->alliance;
faction_list * members = a->members;
while (members!=NULL) {
num += count_skill(members->data, sk);
members=members->next;
}
return num;
}
static int
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
allied_skilllimit(const faction * f, skill_t sk)
2001-01-25 10:37:55 +01:00
{
static int value = -1;
if (value<0) {
const char * str = get_param(global.parameters, "alliance.skilllimit");
value = str?atoi(str):0;
}
return value;
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
}
2001-01-25 10:37:55 +01:00
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;
#if KARMA_MODULE
m += fspecial(f, FS_MAGOCRACY) * 2;
#endif /* KARMA_MODULE */
return m;
}
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
int
max_skill(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->alliance!=NULL) {
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;
}
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
}
switch (sk) {
case SK_MAGIC:
m = max_magicians(f);
break;
case SK_ALCHEMY:
m = MAXALCHEMISTS;
break;
}
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
return m;
2001-01-25 10:37:55 +01:00
}
char * g_basedir;
char * g_resourcedir;
2001-01-25 10:37:55 +01:00
const char *
basepath(void)
{
if (g_basedir) return g_basedir;
return ".";
2001-01-25 10:37:55 +01:00
}
const char *
resourcepath(void)
{
static char zText[MAX_PATH];
if (g_resourcedir) return g_resourcedir;
return strcat(strcpy(zText, basepath()), "/res");
}
2001-01-25 10:37:55 +01:00
int
count_skill(faction * f, skill_t sk)
2001-01-25 10:37:55 +01:00
{
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;
2001-01-25 10:37:55 +01:00
}
int quiet = 0;
FILE *debug;
int
shipspeed (const ship * sh, const unit * u)
2001-01-25 10:37:55 +01:00
{
int 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;
2001-01-25 10:37:55 +01:00
if( curse_active(get_curse(sh->attribs, stormwind_ct)))
k *= 2;
if( curse_active(get_curse(sh->attribs, nodrift_ct)))
k += 1;
2001-01-25 10:37:55 +01:00
if (u->faction->race == new_race[RC_AQUARIAN]
&& u->race == new_race[RC_AQUARIAN]) {
k += 1;
}
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;
}
2001-01-25 10:37:55 +01:00
#ifdef SHIPSPEED
k *= SHIPSPEED;
2001-01-25 10:37:55 +01:00
#endif
#ifdef SHIPDAMAGE
if (sh->damage) k = (k * (sh->size * DAMAGE_SCALE - sh->damage) + sh->size * DAMAGE_SCALE- 1) / (sh->size*DAMAGE_SCALE);
2001-01-25 10:37:55 +01:00
#endif
return k;
2001-01-25 10:37:55 +01:00
}
#define FMAXHASH 2039
2001-01-25 10:37:55 +01:00
faction * factionhash[FMAXHASH];
void
2001-01-25 10:37:55 +01:00
fhash(faction * f)
{
int index = f->no % FMAXHASH;
f->nexthash = factionhash[index];
factionhash[index] = f;
2001-01-25 10:37:55 +01:00
}
void
2001-01-25 10:37:55 +01:00
funhash(faction * f)
{
int index = f->no % FMAXHASH;
faction ** fp = factionhash+index;
while (*fp && (*fp)!=f) fp = &(*fp)->nexthash;
*fp = f->nexthash;
2001-01-25 10:37:55 +01:00
}
static faction *
ffindhash(int no)
{
int index = no % FMAXHASH;
faction * f = factionhash[index];
while (f && f->no!=no) f = f->nexthash;
return f;
2001-01-25 10:37:55 +01:00
}
/* ----------------------------------------------------------------------- */
void
stripfaction (faction * f)
{
2005-05-08 02:38:21 +02:00
/* TODO: inhalt auch l<>schen */
if (f->msgs) free_messagelist(f->msgs);
while (f->battles) {
2006-01-29 02:55:36 +01:00
struct bmsg * bm = f->battles;
f->battles = bm->next;
if (bm->msgs) free_messagelist(bm->msgs);
free(bm);
2005-05-08 02:38:21 +02:00
}
freelist(f->allies);
free(f->email);
free(f->banner);
free(f->passw);
free(f->override);
free(f->name);
while (f->attribs) a_remove (&f->attribs, f->attribs);
i_freeall(&f->items);
2005-05-08 02:38:21 +02:00
freelist(f->ursprung);
funhash(f);
2001-01-25 10:37:55 +01:00
}
void
verify_data(void)
2001-01-25 10:37:55 +01:00
{
#ifndef NDEBUG
int lf = -1;
faction *f;
unit *u;
int mage, alchemist;
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);
2001-01-25 10:37:55 +01:00
#endif
}
int
distribute(int old, int new_value, int n)
2001-01-25 10:37:55 +01:00
{
int i;
int t;
assert(new_value <= old);
2001-01-25 10:37:55 +01:00
if (old == 0)
return 0;
2001-01-25 10:37:55 +01:00
t = (n / old) * new_value;
for (i = (n % old); i; i--)
if (rng_int() % old < new_value)
t++;
2001-01-25 10:37:55 +01:00
return t;
2001-01-25 10:37:55 +01:00
}
int
change_hitpoints (unit * u, int value)
{
int hp = u->hp;
2001-01-25 10:37:55 +01:00
hp += value;
2001-01-25 10:37:55 +01:00
/* 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;
2001-01-25 10:37:55 +01:00
}
unsigned int
atoip(const char *s)
2001-01-25 10:37:55 +01:00
{
int n;
2001-01-25 10:37:55 +01:00
n = atoi (s);
2001-01-25 10:37:55 +01:00
if (n < 0)
n = 0;
2001-01-25 10:37:55 +01:00
return n;
2001-01-25 10:37:55 +01:00
}
region *
findunitregion (const unit * su)
{
#ifndef SLOW_REGION
return su->region;
2001-01-25 10:37:55 +01:00
#else
region *r;
const unit *u;
2001-01-25 10:37:55 +01:00
for (r = regions; r; r = r->next) {
for (u = r->units; u; u = u->next) {
if (su == u) {
return r;
}
}
}
2001-01-25 10:37:55 +01:00
/* This should never happen */
assert (!"Die unit wurde nicht gefunden");
2001-01-25 10:37:55 +01:00
return (region *) NULL;
2001-01-25 10:37:55 +01:00
#endif
}
int
2001-01-25 10:37:55 +01:00
effskill(const unit * u, skill_t sk)
{
return eff_skill(u, sk, u->region);
2001-01-25 10:37:55 +01:00
}
int
eff_stealth(const unit * u, const region * r)
2001-01-25 10:37:55 +01:00
{
int e;
2001-01-25 10:37:55 +01:00
/* Auf Schiffen keine Tarnung! */
if (u->ship) return 0;
2001-01-25 10:37:55 +01:00
e = eff_skill (u, SK_STEALTH, r);
2001-01-25 10:37:55 +01:00
if (fval(u, UFL_STEALTH)) {
int es = u_geteffstealth(u);
if (es >=0 && es < e) return es;
}
return e;
2001-01-25 10:37:55 +01:00
}
void
scale_number (unit * u, int n)
{
skill_t sk;
const attrib * a;
int remain;
if (n == u->number) return;
if (n && u->number>0) {
int full;
remain = ((u->hp%u->number) * (n % u->number)) % u->number;
full = u->hp/u->number; /* wieviel kriegt jede person mindestens */
u->hp = full * n + (u->hp-full*u->number) * n / u->number;
assert(u->hp>=0);
if ((rng_int() % u->number) < remain)
++u->hp; /* Nachkommastellen */
} else {
remain = 0;
u->hp = 0;
}
if (u->number>0) {
for (a = a_find(u->attribs, &at_effect);a && a->type==&at_effect;a=a->next) {
effect_data * data = (effect_data *)a->data.v;
int snew = data->value / u->number * n;
if (n) {
remain = data->value - snew / n * u->number;
snew += remain * n / u->number;
remain = (remain * n) % u->number;
if ((rng_int() % u->number) < remain)
++snew; /* Nachkommastellen */
}
data->value = snew;
}
}
if (u->number==0 || n==0) {
for (sk = 0; sk < MAXSKILLS; sk++) {
remove_skill(u, sk);
}
}
2001-01-25 10:37:55 +01:00
set_number(u, n);
2001-01-25 10:37:55 +01:00
}
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;
2001-01-25 10:37:55 +01:00
}
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
2002-09-02 22:36:12 +02:00
autoalliance(const plane * pl, const faction * sf, const faction * f2)
2001-01-25 10:37:55 +01:00
{
2004-02-22 02:19:07 +01:00
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;
}
}
2004-08-03 17:34:26 +02:00
if (sf->alliance!=NULL && AllianceAuto()) {
if (sf->alliance==f2->alliance) return AllianceAuto();
}
return 0;
2001-01-25 10:37:55 +01:00
}
2002-09-02 23:10:53 +02:00
static int
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
ally_mode(const ally * sf, int mode)
2002-09-02 23:10:53 +02:00
{
if (sf==NULL) return 0;
return sf->status & mode;
2002-09-02 23:10:53 +02:00
}
int
alliedgroup(const struct plane * pl, const struct faction * f,
const struct faction * f2, const struct ally * sf, int mode)
2002-09-02 23:10:53 +02:00
{
while (sf && sf->faction!=f2) sf=sf->next;
if (sf==NULL) {
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
mode = mode & autoalliance(pl, f, f2);
}
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
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();
}
magicskillboost -- implementiert. Kann nur einmal pro Partei verwendet werden, und nur von einem Magier. Müssen wir erklären. Amulett des wahren Sehens und Ring der Unsichtbarkeit geben lediglich einen Bonus auf Wahrnehmung (+2) und Tarnung (+4). -- das define war nicht aktiviert Halblinge haben Armbrustschiessen +0 (statt +1). -- spearates Rassenfile für WDW Es gibt spezielle Regeln für Allianzen, und der HELFE und KONTAKTIERE Befehl sind eingeschränkt. Konkret kann kein HELP_FIGHT HELP_GUARD HELP_FSTEALTH HELP_MONEY gegeben werden (die anderen sind erlaubt). -- mit #defeine ALLIES_ONLY definiert Jede Allianz kann bis zu 15 Magier und 15 Alchemisten haben. Jede einzelne Partei der Allianz kann dabei nicht mehr als 15/Anzahl_Parteien (aufgerundet) Magier bzw. Alchemisten haben, und die Gesamtsumme darf 15 nicht übersteigen. -- mit #define ALLIANCE_LIMITS gemacht. Die Startgeschenke (Personen, Silber, ...) werden pro Allianz, nicht pro Spieler verteilt. Größere Allianzen bekommen also weniger pro Spieler. -- Nochmal geändert: Die Allianzen kriegen jede 168 Personen zum Start, weil sich das gut aufteilen lässt. Das wird auf 28 Einheiten pro Partei gesplittet, jede Einheit hat eines der Talente, außer der Starteinheit, die hat den magicskillboost. Einige Skills kommen öfter vor als andere, das ist nicht einfach vermeidbar. Sollte aber auch wurscht sein, es geht primär darum, lehren zu können. Es gibt ein Einheitenlimit von 1000 Einheiten pro Allianz. -- die Regel sparen wir uns einfach mal.
2003-12-14 22:45:47 +01:00
}
return mode;
2002-09-02 23:10:53 +02:00
}
2002-09-02 22:36:12 +02:00
int
alliedfaction(const struct plane * pl, const struct faction * f,
const struct faction * f2, int mode)
2001-01-25 10:37:55 +01:00
{
return alliedgroup(pl, f, f2, f->allies, mode);
2001-01-25 10:37:55 +01:00
}
/* Die Gruppe von Einheit u hat helfe zu f2 gesetzt. */
2001-01-25 10:37:55 +01:00
int
2002-09-02 22:36:12 +02:00
alliedunit(const unit * u, const faction * f2, int mode)
2001-01-25 10:37:55 +01:00
{
2005-12-02 21:23:54 +01:00
ally * sf;
const plane * pl = u->region->planep;
2005-12-02 21:23:54 +01:00
int automode;
2001-01-25 10:37:55 +01:00
2005-12-02 21:23:54 +01:00
if (u->faction == f2) return mode;
if (u->faction == NULL || f2==NULL) return 0;
2001-01-25 10:37:55 +01:00
2005-12-02 21:23:54 +01:00
automode = mode & autoalliance(pl, u->faction, f2);
2001-01-25 10:37:55 +01:00
2005-12-02 21:23:54 +01:00
if (pl!=NULL && (pl->flags & PFL_NOALLIANCES))
mode = (mode & automode) | (mode & HELP_GIVE);
2001-02-18 13:30:10 +01:00
2005-12-02 21:23:54 +01:00
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;
}
2002-12-14 17:19:05 +01:00
return alliedgroup(pl, u->faction, f2, sf, mode);
2001-01-25 10:37:55 +01:00
}
boolean
seefaction(const faction * f, const region * r, const unit * u, int modifier)
2001-01-25 10:37:55 +01:00
{
if (((f == u->faction) || !fval(u, UFL_PARTEITARNUNG)) && cansee(f, r, u, modifier))
return true;
return false;
2001-01-25 10:37:55 +01:00
}
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 */
2001-01-25 10:37:55 +01:00
{
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");
}
2001-01-25 10:37:55 +01:00
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;
}
}
2005-01-07 00:37:43 +01:00
if (leftship(u)) return true;
2005-01-07 00:37:43 +01:00
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;
2001-01-25 10:37:55 +01:00
/* simple visibility, just gotta have a unit in the region to see 'em */
if (getguard(u) || usiege(u) || u->building || u->ship) {
return true;
}
stealth = eff_stealth(u, r) - modifier;
rings = invisible(u, NULL);
while (u2) {
if (rings<u->number || invisible(u, u2) < u->number) {
int observation = eff_skill(u2, SK_OBSERVATION, r);
#ifdef NIGHTEYES
if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT)
observation = NIGHT_EYE_TALENT;
2001-01-25 10:37:55 +01:00
#endif
if (observation >= stealth) {
return true;
}
}
/* find next unit in our faction */
do {
u2=u2->next;
} while (u2 && u2->faction != f);
}
return false;
2001-01-25 10:37:55 +01:00
}
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 (getguard(target) || 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;
}
o = eff_skill(u, SK_OBSERVATION, target->region);
#ifdef NIGHTEYES
if (u->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT)
o = NIGHT_EYE_TALENT;
#endif
if (o >= n) {
return true;
}
}
return false;
}
2001-01-25 10:37:55 +01:00
boolean
cansee_durchgezogen(const faction * f, const region * r, const unit * u, int modifier)
2001-01-25 10:37:55 +01:00
/* 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;
2001-01-25 10:37:55 +01:00
if (getguard(u) || 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;
}
2001-01-25 10:37:55 +01:00
for (u2 = r->units; u2; u2 = u2->next){
if (u2->faction == f) {
int o;
2001-01-25 10:37:55 +01:00
if (rings && invisible(u, u2) >= u->number) continue;
2001-01-25 10:37:55 +01:00
o = eff_skill(u2, SK_OBSERVATION, r);
2001-01-25 10:37:55 +01:00
#ifdef NIGHTEYES
if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT)
o = NIGHT_EYE_TALENT;
2001-01-25 10:37:55 +01:00
#endif
if (o >= n) {
return true;
}
}
}
}
return false;
2001-01-25 10:37:55 +01:00
}
#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;
2001-01-25 10:37:55 +01:00
}
#endif
static attrib_type at_lighthouse = {
"lighthouse"
/* Rest ist NULL; tempor<6F>res, nicht alterndes Attribut */
2001-01-25 10:37:55 +01:00
};
/* 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;
short d = (short)log10(lh->size) + 1;
short x;
if (lh->size>0) {
r->flags |= RF_LIGHTHOUSE;
}
for (x=-d;x<=d;++x) {
short y;
for (y=-d;y<=d;++y) {
attrib * a;
region * r2 = findregion(x+r->x, y+r->y);
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;
}
}
}
}
2001-01-25 10:37:55 +01:00
}
int
2001-02-05 08:23:17 +01:00
count_all(const faction * f)
2001-01-25 10:37:55 +01:00
{
#ifndef NDEBUG
int n = 0;
unit *u;
2004-08-03 17:34:26 +02:00
for (u=f->units;u;u=u->nextF) {
if (playerrace(u->race)) {
n += u->number;
assert(f==u->faction);
}
2004-08-03 17:34:26 +02:00
}
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));
2005-03-06 14:53:13 +01:00
return n;
}
#endif
return f->num_people;
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
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;
2004-06-21 18:45:27 +02:00
}
2001-01-25 10:37:55 +01:00
int
2001-02-05 08:23:17 +01:00
count_maxmigrants(const faction * f)
2001-01-25 10:37:55 +01:00
{
2005-04-25 19:44:19 +02:00
int x = 0;
2005-05-27 23:04:13 +02:00
if (f->race == new_race[RC_HUMAN]) {
2005-04-25 19:44:19 +02:00
int nsize = count_all(f);
if (nsize>0) {
x = (int)(log10(nsize / 50.0) * 20);
if (x < 0) x = 0;
}
}
return x;
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
void
init_tokens(const struct order * ord)
{
char * cmd = getcommand(ord);
init_tokens_str(cmd, cmd);
2004-06-21 18:45:27 +02:00
}
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;
2004-06-21 18:45:27 +02:00
}
if (thisorder) break;
if (*ordp==ord) ordp=&ord->next;
}
if (*up==u) up=&u->next;
}
}
}
const char *
igetstrtoken(const char * initstr)
2004-06-21 18:45:27 +02:00
{
if (initstr!=NULL) {
init_tokens_str(initstr, NULL);
2004-06-21 18:45:27 +02:00
}
return getstrtoken();
2001-01-25 10:37:55 +01:00
}
unsigned int
getuint (void)
2001-01-25 10:37:55 +01:00
{
return atoip((const char *)getstrtoken());
2001-01-25 10:37:55 +01:00
}
int
getint (void)
2001-01-25 10:37:55 +01:00
{
return atoi((const char *)getstrtoken());
2001-01-25 10:37:55 +01:00
}
const struct race *
findrace(const char * s, const struct locale * lang)
{
struct tnode * tokens = get_translations(lang, UT_RACES);
variant token;
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
2001-01-25 10:37:55 +01:00
skill_t
findskill(const char *s, const struct locale * lang)
2001-01-25 10:37:55 +01:00
{
#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
2001-01-25 10:37:55 +01:00
}
keyword_t
findkeyword(const char *s, const struct locale * lang)
2001-01-25 10:37:55 +01:00
{
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;
2001-01-25 10:37:55 +01:00
}
param_t
findparam(const char *s, const struct locale * lang)
2001-01-25 10:37:55 +01:00
{
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;
2004-03-08 19:39:34 +01:00
}
if (token.i==P_BUILDING) return P_GEBAEUDE;
return (param_t)token.i;
2001-01-25 10:37:55 +01:00
}
param_t
getparam (const struct locale * lang)
2001-01-25 10:37:55 +01:00
{
return findparam (getstrtoken (), lang);
2001-01-25 10:37:55 +01:00
}
faction *
findfaction (int n)
{
faction * f = ffindhash(n);
return f;
2001-01-25 10:37:55 +01:00
}
faction *
getfaction (void)
{
return findfaction (getid());
2001-01-25 10:37:55 +01:00
}
unit *
findunitr (const region * r, int n)
{
unit *u;
2001-01-25 10:37:55 +01:00
/* findunit regional! */
2001-01-25 10:37:55 +01:00
for (u = r->units; u; u = u->next)
if (u->no == n)
return u;
2001-01-25 10:37:55 +01:00
return 0;
2001-01-25 10:37:55 +01:00
}
unit *findunit(int n)
{
if (n <= 0) {
return NULL;
}
return ufindhash(n);
2001-01-25 10:37:55 +01:00
}
unit *
2001-04-26 19:41:06 +02:00
findunitg (int n, const region * hint)
2001-01-25 10:37:55 +01:00
{
/* Abfangen von Syntaxfehlern. */
if (n <= 0)
return NULL;
2001-01-25 10:37:55 +01:00
/* findunit global! */
hint = 0;
return ufindhash(n);
2001-01-25 10:37:55 +01:00
}
unit *
2001-04-26 19:41:06 +02:00
getnewunit (const region * r, const faction * f)
2001-01-25 10:37:55 +01:00
{
int n;
n = getid();
2001-01-25 10:37:55 +01:00
return findnewunit (r, f, n);
2001-01-25 10:37:55 +01:00
}
static int
2001-04-26 19:41:06 +02:00
read_newunitid (const faction * f, const region * r)
2001-01-25 10:37:55 +01:00
{
int n;
unit *u2;
n = getid();
if (n == 0)
return -1;
2001-01-25 10:37:55 +01:00
u2 = findnewunit(r, f, n);
if (u2) return u2->no;
2001-01-25 10:37:55 +01:00
return -1;
2001-01-25 10:37:55 +01:00
}
int
2001-04-26 19:41:06 +02:00
read_unitid (const faction * f, const region * r)
2001-01-25 10:37:55 +01:00
{
const char * s = getstrtoken();
2001-01-25 10:37:55 +01:00
/* 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! */
2001-01-25 10:37:55 +01:00
switch (findparam(s, f->locale)) {
case P_TEMP:
return read_newunitid(f, r);
}
if (!s || *s == 0)
return -1;
return atoi36((const char *)s);
2001-01-25 10:37:55 +01:00
}
/* exported symbol */
boolean getunitpeasants;
unit *
2001-04-26 19:41:06 +02:00
getunitg(const region * r, const faction * f)
{
int n = read_unitid(f, r);
2001-04-26 19:41:06 +02:00
if (n == 0) {
getunitpeasants = 1;
return NULL;
}
getunitpeasants = 0;
if (n < 0) return 0;
2001-04-26 19:41:06 +02:00
return findunit(n);
2001-04-26 19:41:06 +02:00
}
unit *
getunit(const region * r, const faction * f)
2001-01-25 10:37:55 +01:00
{
2004-07-09 16:06:27 +02:00
int n = read_unitid(f, r);
2004-06-13 19:10:00 +02:00
unit *u2;
2004-06-13 19:10:00 +02:00
if (n == 0) {
getunitpeasants = 1;
return NULL;
2004-06-13 19:10:00 +02:00
}
getunitpeasants = 0;
2004-06-13 19:10:00 +02:00
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;
2004-06-13 19:10:00 +02:00
}
2001-01-25 10:37:55 +01:00
return NULL;
2001-01-25 10:37:55 +01:00
}
2004-06-21 18:45:27 +02:00
/* - String Listen --------------------------------------------- */
2001-01-25 10:37:55 +01:00
void
addstrlist (strlist ** SP, const char *s)
{
2004-06-21 18:45:27 +02:00
strlist * slist = malloc(sizeof(strlist));
slist->next = NULL;
slist->s = strdup(s);
addlist(SP, slist);
2001-01-25 10:37:55 +01:00
}
void
freestrlist (strlist * s)
{
strlist *q, *p = s;
while (p) {
q = p->next;
free(p->s);
free(p);
p = q;
}
2001-01-25 10:37:55 +01:00
}
/* - Meldungen und Fehler ------------------------------------------------- */
boolean lomem = false;
2001-01-25 10:37:55 +01:00
/* - Namen der Strukturen -------------------------------------- */
typedef char name[OBJECTIDSIZE+1];
2001-01-25 10:37:55 +01:00
static name idbuf[8];
static int nextbuf = 0;
char *
estring_i(char *ibuf)
{
char *p = ibuf;
while (*p) {
if (isspace(*(unsigned*)p) == ' ') {
*p = '~';
}
++p;
}
return ibuf;
}
2001-01-25 10:37:55 +01:00
char *
estring(const char *s)
{
char *ibuf = idbuf[(++nextbuf) % 8];
2001-01-25 10:37:55 +01:00
strlcpy(ibuf, s, sizeof(name));
return estring_i(ibuf);
}
2001-01-25 10:37:55 +01:00
char *
cstring_i(char *ibuf)
{
char *p = ibuf;
while (*p) {
if (*p == '~') {
*p = ' ';
}
++p;
}
return ibuf;
2001-01-25 10:37:55 +01:00
}
char *
cstring(const char *s)
{
char *ibuf = idbuf[(++nextbuf) % 8];
2001-01-25 10:37:55 +01:00
strlcpy(ibuf, s, sizeof(name));
return cstring_i(ibuf);
2001-01-25 10:37:55 +01:00
}
building *
largestbuilding (const region * r, boolean img)
{
static const building_type * btype = NULL;
building *b, *best = NULL;
if (!btype) btype = bt_find("castle"); /* TODO: parameter der funktion? */
/* durch die verw. von '>' statt '>=' werden die aelteren burgen
* bevorzugt. */
for (b = rbuildings(r); b; b = b->next) {
if (b->type!=btype) {
if (img) {
const attrib * a = a_find(b->attribs, &at_icastle);
if (!a) continue;
if (a->data.v != btype) continue;
} else continue;
}
if (best==NULL || b->size > best->size)
best = b;
}
return best;
2001-01-25 10:37:55 +01:00
}
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 *
2001-01-25 10:37:55 +01:00
unitname(const unit * u)
{
char *ubuf = idbuf[(++nextbuf) % 8];
return write_unitname(u, ubuf, sizeof(name));
2001-01-25 10:37:55 +01:00
}
/* -- Erschaffung neuer Einheiten ------------------------------ */
extern faction * dfindhash(int i);
static const char* forbidden[] = { "t", "te", "tem", "temp", NULL };
2001-01-25 10:37:55 +01:00
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;
2001-01-25 10:37:55 +01:00
}
/* 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;
2001-01-25 10:37:55 +01:00
}
int
newcontainerid(void)
{
int random_no;
int start_random_no;
2001-01-25 10:37:55 +01:00
random_no = 1 + (rng_int() % MAX_CONTAINER_NR);
start_random_no = random_no;
2001-01-25 10:37:55 +01:00
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;
2001-01-25 10:37:55 +01:00
}
unit *
createunit(region * r, faction * f, int number, const struct race * rc)
{
assert(rc);
return create_unit(r, f, number, rc, 0, NULL, NULL);
}
2001-01-25 10:37:55 +01:00
boolean
idle (faction * f)
{
return (boolean) (f ? false : true);
2001-01-25 10:37:55 +01:00
}
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);
2001-01-25 10:37:55 +01:00
}
unit_list *
get_lighthouses(const region * r)
{
attrib * a;
unit_list * ulist = NULL;
if (!fval(r->terrain, SEA_REGION)) return NULL;
for (a = a_find(r->attribs, &at_lighthouse);a && a->type==&at_lighthouse;a=a->next) {
building *b = (building *)a->data.v;
region *r2 = b->region;
if (fval(b, BLD_WORKING) && b->size >= 10) {
boolean c = false;
unit *u;
int d = distance(r, r2);
int maxd = (int)log10(b->size) + 1;
if (maxd < d) break;
for (u = r2->units; u; u = u->next) {
if (u->building == b) {
c = true;
if (c > buildingcapacity(b)) break;
if (eff_skill(u, SK_OBSERVATION, r) >= d * 3) {
unitlist_insert(&ulist, u);
}
} else if (c) break; /* first unit that's no longer in the house ends the search */
}
}
}
return ulist;
}
int
lighthouse_range(const building * b, const faction * f)
{
int d = 0;
if (fval(b, BLD_WORKING) && b->size >= 10) {
region * r = b->region;
unit *u;
int maxd = (int)log10(b->size) + 1;
int c = 0;
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_OBSERVATION, 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 */
}
}
return d;
}
2001-01-25 10:37:55 +01:00
boolean
check_leuchtturm(region * r, faction * f)
{
attrib * a;
2001-01-25 10:37:55 +01:00
if (!fval(r->terrain, SEA_REGION)) return false;
2001-01-25 10:37:55 +01:00
for (a = a_find(r->attribs, &at_lighthouse);a && a->type==&at_lighthouse;a=a->next) {
building *b = (building *)a->data.v;
region *r2 = b->region;
2001-01-25 10:37:55 +01:00
assert(b->type == bt_find("lighthouse"));
if (fval(b, BLD_WORKING) && b->size >= 10) {
int c = 0;
unit *u;
int d = 0;
int maxd = (int)log10(b->size) + 1;
2001-01-25 10:37:55 +01:00
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_OBSERVATION, r) >= d * 3) return true;
}
} else if (c) break; /* first unit that's no longer in the house ends the search */
}
}
}
2001-01-25 10:37:55 +01:00
return false;
2001-01-25 10:37:55 +01:00
}
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
2001-01-25 10:37:55 +01:00
}
region *
firstregion (faction * f)
{
#ifdef SMART_INTERVALS
region *r = f->first;
2001-01-25 10:37:55 +01:00
if (f->units==NULL) return NULL;
if (r!=NULL) return r;
return f->first = regions;
#else
return regions;
#endif
2001-01-25 10:37:55 +01:00
}
void ** blk_list[1024];
int list_index;
int blk_index;
static void
2001-01-25 10:37:55 +01:00
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]);
2001-01-25 10:37:55 +01:00
}
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;
2001-01-25 10:37:55 +01:00
}
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;
}
2001-01-25 10:37:55 +01:00
static void
2004-06-21 18:45:27 +02:00
init_locale(const struct locale * lang)
2001-01-25 10:37:55 +01:00
{
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);
for (i=0;i!=MAXMAGIETYP;++i) {
var.i = i;
addtoken(tokens, LOC(lang, mkname("school", magietypen[i])), var);
}
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) {
if (i!=SK_TRADE || !TradeDisabled()) {
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) {
if (i!=SK_TRADE || !TradeDisabled()) {
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;
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;
}
void
set_param(struct param ** p, const char * key, const char * data)
{
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;
2001-01-25 10:37:55 +01:00
}
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();
skill_done();
gc_done();
sql_done();
}
2002-09-02 22:36:12 +02:00
const char * localenames[] = {
"de", "en",
NULL
};
int
init_data(const char * filename)
{
int l;
char zText[80];
sprintf(zText, "%s/%s", resourcepath(), filename);
l = read_xml(zText);
if (l) return l;
if (turn<first_turn) turn = first_turn;
return 0;
}
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 */
2007-06-20 02:34:02 +02:00
extern struct attrib_type at_shiptrail;
attrib_type at_germs = {
"germs",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
a_writeshorts,
a_readshorts,
ATF_UNIQUE
};
2001-01-25 10:37:55 +01:00
/*********************/
/* at_guard */
/*********************/
attrib_type at_guard = {
"guard",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
a_writeint,
a_readint,
ATF_UNIQUE
2001-01-25 10:37:55 +01:00
};
void
setstatus(struct unit * u, int status)
{
assert(status>=ST_AGGRO && status<=ST_FLEE);
if (u->status!=status) {
2007-02-12 08:29:07 +01:00
u->status = (status_t)status;
if (u->status==ST_FLEE) {
setguard(u, GUARD_NONE);
}
}
}
2001-01-25 10:37:55 +01:00
void
setguard(unit * u, unsigned int flags)
{
2005-06-12 12:56:18 +02:00
/* 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);
}
2005-06-12 12:56:18 +02:00
if (flags == GUARD_NONE) {
freset(u, UFL_GUARD);
if (a) a_remove(&u->attribs, a);
return;
2005-06-12 12:56:18 +02:00
}
fset(u, UFL_GUARD);
fset(u->region, RF_GUARDED);
2005-06-12 12:56:18 +02:00
if (!a) a = a_add(&u->attribs, a_new(&at_guard));
a->data.i = (int)flags;
2001-01-25 10:37:55 +01:00
}
unsigned int
getguard(const unit * u)
{
if (fval(u->region->terrain, SEA_REGION)) return GUARD_NONE;
if (fval(u, UFL_GUARD)) {
attrib * a = a_find(u->attribs, &at_guard);
if (a) return (unsigned int)a->data.i;
}
return GUARD_NONE;
2001-01-25 10:37:55 +01:00
}
#ifndef HAVE_STRDUP
char *
strdup(const char *s)
{
return strcpy((char*)malloc(sizeof(char)*(strlen(s)+1)), s);
2001-01-25 10:37:55 +01:00
}
#endif
void
remove_empty_factions(boolean writedropouts)
2001-01-25 10:37:55 +01:00
{
faction **fp, *f3;
FILE *dofp = NULL;
char zText[MAX_PATH];
sprintf(zText, "%s/dropouts.%d", basepath(), turn);
2001-01-28 09:50:46 +01:00
if (writedropouts) dofp = fopen(zText, "w");
if (dofp) {
const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf };
fwrite(utf8_bom, 1, 3, dofp);
}
2001-01-25 10:37:55 +01:00
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) && f->no != MONSTER_FACTION) {
ursprung * ur = f->ursprung;
while (ur && ur->id!=0) ur=ur->next;
if (!quiet) log_stdio(stdout, "\t%s\n", factionname(f));
2001-01-25 10:37:55 +01:00
/* Einfach in eine Datei schreiben und sp<73>ter vermailen */
2001-01-25 10:37:55 +01:00
if (dofp) {
fprintf(dofp, "%3d %s %s %s\n",
f->age, LOC(default_locale, rc_name(f->race, 0)), f->email,
factionname(f));
}
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) {
2006-12-02 11:10:59 +01:00
sql_print(("UPDATE subscriptions set status='DEAD' where id=%u;\n",
f->subscription));
}
*fp = f->next;
}
else fp = &(*fp)->next;
}
2001-01-25 10:37:55 +01:00
if (dofp) fclose(dofp);
2001-01-25 10:37:55 +01:00
}
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])) {
2006-11-13 09:50:03 +01:00
if (u->faction!=NULL) destroy_unit(u);
if (u->number==0) remove_unit(u);
}
if (*up==u) up=&u->next;
}
2001-01-25 10:37:55 +01:00
}
void
remove_empty_units(void)
{
region *r;
2001-01-25 10:37:55 +01:00
for (r = regions; r; r = r->next) {
remove_empty_units_in_region(r);
}
2001-01-25 10:37:55 +01:00
}
boolean
faction_id_is_unused(int id)
{
return findfaction(id)==NULL && id!=MONSTER_FACTION;
2001-01-25 10:37:55 +01:00
}
int
weight(const unit * u)
{
int w, n = 0, in_bag = 0;
int faerie_level = 0;
2001-01-25 10:37:55 +01:00
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;
}
2001-01-25 10:37:55 +01:00
#if KARMA_MODULE
faerie_level = fspecial(u->faction, FS_FAERIE);
#endif /* KARMA_MODULE */
if (faerie_level) {
n += (u->number * u->race->weight)/(1+faerie_level);
} else {
n += u->number * u->race->weight;
}
2001-01-25 10:37:55 +01:00
w = get_item(u, I_BAG_OF_HOLDING) * BAGCAPACITY;
if( w > in_bag )
w = in_bag;
n -= w;
2001-01-25 10:37:55 +01:00
return n;
2001-01-25 10:37:55 +01:00
}
void
make_undead_unit(unit * u)
2001-01-25 10:37:55 +01:00
{
#ifdef LASTORDER
set_order(&u->lastorder, NULL);
#endif
free_orders(&u->orders);
name_unit(u);
fset(u, UFL_ISNEW);
2001-01-25 10:37:55 +01:00
}
void
guard(unit * u, unsigned int mask)
{
int flags = GUARD_CREWS | GUARD_LANDING | GUARD_TRAVELTHRU | GUARD_TAX;
2002-04-21 19:44:17 +02:00
#if GUARD_DISABLES_PRODUCTION == 1
flags |= GUARD_PRODUCE;
2002-04-21 19:44:17 +02:00
#endif
#if GUARD_DISABLES_RECRUIT == 1
flags |= GUARD_RECRUIT;
2002-04-21 19:44:17 +02:00
#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;
}
setguard(u, flags & mask);
2001-01-25 10:37:55 +01:00
}
int
besieged(const unit * u)
{
/* belagert kann man in schiffen und burgen werden */
return (u
&& u->building && u->building->besieged
&& u->building->besieged >= u->building->size * SIEGEFACTOR);
2001-01-25 10:37:55 +01:00
}
int
lifestyle(const unit * u)
{
static plane * astralspace = NULL;
int need;
if (u->faction->no == MONSTER_FACTION) return 0;
need = maintenance_cost(u);
2001-01-25 10:37:55 +01:00
if (!astralspace) {
astralspace = getplanebyname("Astralraum");
}
2001-01-25 10:37:55 +01:00
if(u->region->planep && fval(u->region->planep, PFL_NOFEED))
return 0;
2001-01-25 10:37:55 +01:00
#if KARMA_MODULE
if(fspecial(u->faction, FS_REGENERATION))
need += 1;
if(fspecial(u->faction, FS_ADMINISTRATOR))
need += 1;
if(fspecial(u->faction, FS_WYRM) && u->race == new_race[RC_WYRM])
need *= 500;
#endif /* KARMA_MODULE */
return need;
2001-01-25 10:37:55 +01:00
}
boolean
hunger(int number, unit * u)
2001-01-25 10:37:55 +01:00
{
region * r = u->region;
int dead = 0, hpsub = 0;
int hp = u->hp / u->number;
while (number--) {
int dam = u->race==new_race[RC_HALFLING]?15+rng_int()%14:(13+rng_int()%12);
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));
2001-01-25 10:37:55 +01:00
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);
2001-01-25 10:37:55 +01:00
}
void
plagues(region * r, boolean ismagic)
{
int peasants;
int i;
int dead = 0;
2001-01-25 10:37:55 +01:00
/* Seuchenwahrscheinlichkeit in % */
2001-01-25 10:37:55 +01:00
if (!ismagic) {
double mwp = max(maxworkingpeasants(r), 1);
double prob = pow(rpeasants(r) / (mwp * wage(r, NULL, NULL) * 0.13), 4.0)
2007-06-20 02:34:02 +02:00
* PLAGUE_CHANCE;
if (rng_double() >= prob) return;
}
2001-01-25 10:37:55 +01:00
peasants = rpeasants(r);
2007-06-20 02:34:02 +02:00
dead = (int)(0.5F + PLAGUE_VICTIMS * peasants);
for (i = dead; i != 0; i--) {
2007-06-20 02:34:02 +02:00
if (rng_double() < PLAGUE_HEALCHANCE && rmoney(r) >= PLAGUE_HEALCOST) {
rsetmoney(r, rmoney(r) - PLAGUE_HEALCOST);
} else {
--dead;
}
}
2001-01-25 10:37:55 +01:00
if (dead > 0) {
message * msg = add_message(&r->msgs, msg_message("pest", "dead", dead));
msg_release(msg);
deathcounts(r, dead);
rsetpeasants(r, peasants - dead);
}
2001-01-25 10:37:55 +01:00
}
/* 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 */
2001-01-25 10:37:55 +01:00
};
static int
default_wage(const region *r, const faction * f, const race * rc)
{
building *b = largestbuilding(r, false);
int esize = 0;
curse * c;
int wage;
attrib *a;
const building_type *artsculpture_type = bt_find("artsculpture");
static const curse_type * drought_ct, * blessedharvest_ct;
static boolean init;
2004-05-14 12:07:36 +02:00
if (!init) {
init = true;
drought_ct = ct_find("drought");
blessedharvest_ct = ct_find("blessedharvest");
}
2001-01-25 10:37:55 +01:00
if (b!=NULL) {
/* TODO: this reveals imaginary castles */
esize = buildingeffsize(b, false);
}
2001-01-25 10:37:55 +01:00
if (f!=NULL) {
int index = 0;
if (rc==new_race[RC_ORC] || rc==new_race[RC_URUK] || rc==new_race[RC_SNOTLING]) {
index = 1;
}
wage = wagetable[esize][index];
#if KARMA_MODULE
if (fspecial(f, FS_URBAN)) {
wage += wagetable[esize][3];
}
#endif /* KARMA_MODULE */
} else {
if (fval(r->terrain, SEA_REGION)) {
wage = 11;
} else if (fval(r, RF_ORCIFIED)) {
wage = wagetable[esize][1];
} else {
wage = wagetable[esize][2];
}
wage += curse_geteffect(get_curse(r->attribs, blessedharvest_ct));
}
2001-01-25 10:37:55 +01:00
2004-05-14 12:07:36 +02:00
/* 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);
}
2001-01-25 10:37:55 +01:00
a = a_find(r->attribs, &at_reduceproduction);
if (a) wage = (wage * a->data.sa[0])/100;
2001-01-25 10:37:55 +01:00
return wage;
2001-01-25 10:37:55 +01:00
}
/* 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)
{
if (global.functions.wage) {
return global.functions.wage(r, f, rc);
}
return default_wage(r, f, rc);
}
#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)
2001-01-25 10:37:55 +01:00
{
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;
}
2001-01-25 10:37:55 +01:00
int
movewhere(const unit *u, const char * token, region * r, region** resultp)
{
region * r2;
direction_t d;
2001-01-25 10:37:55 +01:00
if (*token == '\0') {
*resultp = NULL;
return E_MOVE_OK;
}
d = finddirection(token, u->faction->locale);
switch (d) {
case D_PAUSE:
*resultp = r;
break;
2001-01-25 10:37:55 +01:00
case NODIRECTION:
r2 = find_special_direction(r, token, u->faction->locale);
if (r2==NULL) {
return E_MOVE_NOREGION;
}
*resultp = r2;
break;
2001-01-25 10:37:55 +01:00
default:
r2 = rconnect(r, d);
if (r2==NULL || move_blocked(u, r, r2)) {
return E_MOVE_BLOCKED;
}
*resultp = r2;
}
return E_MOVE_OK;
2001-01-25 10:37:55 +01:00
}
boolean
move_blocked(const unit * u, const region *r, const region *r2)
2001-01-25 10:37:55 +01:00
{
border * 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;
2001-01-25 10:37:55 +01:00
}
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));
2001-01-25 10:37:55 +01:00
}
void
reorder_owners(region * r)
{
unit ** up=&r->units, ** useek;
building * b=NULL;
ship * sh=NULL;
#ifndef NDEBUG
size_t len = listlen(r->units);
#endif
for (b = r->buildings;b;b=b->next) {
unit ** ubegin = up;
unit ** uend = up;
useek = up;
while (*useek) {
unit * u = *useek;
if (u->building==b) {
unit ** insert;
if (fval(u, UFL_OWNER)) {
unit * nu = *ubegin;
insert = ubegin;
if (nu!=u && nu->building==u->building && fval(nu, UFL_OWNER)) {
log_error(("[reorder_owners] %s hat mehrere Besitzer mit UFL_OWNER.\n", buildingname(nu->building)));
freset(nu, UFL_OWNER);
}
}
else insert = uend;
if (insert != useek) {
*useek = u->next; /* raus aus der liste */
u->next = *insert;
*insert = u;
}
if (insert==uend) uend=&u->next;
}
if (*useek==u) useek = &u->next;
}
up = uend;
}
useek=up;
while (*useek) {
unit * u = *useek;
assert(!u->building);
if (u->ship==NULL) {
if (fval(u, UFL_OWNER)) {
log_warning(("[reorder_owners] Einheit %s war Besitzer von nichts.\n", unitname(u)));
freset(u, UFL_OWNER);
}
if (useek!=up) {
*useek = u->next; /* raus aus der liste */
u->next = *up;
*up = u;
}
up = &u->next;
}
if (*useek==u) useek = &u->next;
}
for (sh = r->ships;sh;sh=sh->next) {
unit ** ubegin = up;
unit ** uend = up;
useek = up;
while (*useek) {
unit * u = *useek;
if (u->ship==sh) {
unit ** insert;
if (fval(u, UFL_OWNER)) {
unit * nu = *ubegin;
insert = ubegin;
if (nu!=u && nu->ship==u->ship && fval(nu, UFL_OWNER)) {
log_error(("[reorder_owners] %s hat mehrere Besitzer mit UFL_OWNER.\n", shipname(nu->ship)));
freset(nu, UFL_OWNER);
}
}
else insert = uend;
if (insert!=useek) {
*useek = u->next; /* raus aus der liste */
u->next = *insert;
*insert = u;
}
if (insert==uend) uend=&u->next;
}
if (*useek==u) useek = &u->next;
}
up = uend;
}
#ifndef NDEBUG
assert(len==listlen(r->units));
#endif
}
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
teure_talente (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);
#if KARMA_MODULE
at_register(&at_faction_special);
at_register(&at_prayer_timeout);
at_register(&at_wyrm);
at_register(&at_jihad);
#endif /* KARMA_MODULE */
at_register(&at_building_generic_type);
at_register(&at_maxmagicians);
at_register(&at_npcfaction);
/* border-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);
at_register(&at_germs);
#if XECMD_MODULE
at_register(&at_xontormiaexpress); /* required for old datafiles */
2004-02-20 10:59:15 +01:00
#endif
at_register(&at_speedup);
at_register(&at_building_action);
}
void
kernel_init(void)
{
char zBuffer[MAX_PATH];
skill_init();
attrib_init();
translation_init();
if (!turn) turn = lastturn();
rng_init(turn?turn:(int)time(0));
if (sqlpatch) {
sprintf(zBuffer, "%s/patch-%d.sql", datapath(), turn);
sql_init(zBuffer);
}
}
2004-06-21 18:45:27 +02:00
order *
default_order(const struct locale * lang)
{
return parse_order(locale_string(lang, "defaultorder"), lang);
2004-06-21 18:45:27 +02:00
}
int
entertainmoney(const region *r)
{
int 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 n;
}