forked from github/server
86c0a65184
requires that alliancejoin and alliancekick are called at some point in the code.
3067 lines
61 KiB
C
3067 lines
61 KiB
C
/* vi: set ts=2:
|
|
*
|
|
* Eressea PB(E)M host Copyright (C) 1998-2000
|
|
* 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-pbem.de)
|
|
* 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ö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 "eressea.h"
|
|
|
|
/* attributes includes */
|
|
#include <attributes/reduceproduction.h>
|
|
#include <attributes/otherfaction.h>
|
|
#include <attributes/racename.h>
|
|
#include <attributes/gm.h>
|
|
|
|
/* kernel includes */
|
|
#include "message.h"
|
|
#include "curse.h"
|
|
#include "spell.h"
|
|
#include "names.h"
|
|
#include "faction.h"
|
|
#include "item.h"
|
|
#include "border.h"
|
|
#include "plane.h"
|
|
#include "alchemy.h"
|
|
#include "unit.h"
|
|
#include "save.h"
|
|
#include "magic.h"
|
|
#include "building.h"
|
|
#include "battle.h"
|
|
#include "race.h"
|
|
#include "pool.h"
|
|
#include "region.h"
|
|
#include "unit.h"
|
|
#include "skill.h"
|
|
#include "objtypes.h"
|
|
#include "ship.h"
|
|
#include "karma.h"
|
|
#include "group.h"
|
|
|
|
/* util includes */
|
|
#include <base36.h>
|
|
#include <event.h>
|
|
#include <umlaut.h>
|
|
#include <translation.h>
|
|
#include <crmessage.h>
|
|
#include <sql.h>
|
|
#include <xml.h>
|
|
|
|
#include <modules/xecmd.h>
|
|
|
|
/* libc includes */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <message.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
/* exported variables */
|
|
region *regions;
|
|
faction *factions;
|
|
settings global;
|
|
char buf[BUFSIZE + 1];
|
|
FILE *logfile;
|
|
FILE *updatelog;
|
|
const struct race * new_race[MAXRACES];
|
|
|
|
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;
|
|
}
|
|
|
|
const char *directions[MAXDIRECTIONS+2] =
|
|
{
|
|
"northwest",
|
|
"northeast",
|
|
"east",
|
|
"southeast",
|
|
"southwest",
|
|
"west",
|
|
"",
|
|
"pause"
|
|
};
|
|
|
|
const char *gr_prefix[3] = {
|
|
"einem",
|
|
"einer",
|
|
"einem"
|
|
};
|
|
|
|
const char *parameters[MAXPARAMS] =
|
|
{
|
|
"LOCALE",
|
|
"ALLES",
|
|
"BAUERN",
|
|
"BURG",
|
|
"EINHEIT",
|
|
"PRIVAT",
|
|
"HINTEN",
|
|
"KOMMANDO",
|
|
"KRÄUTER",
|
|
"NICHT",
|
|
"NÄCHSTER",
|
|
"PARTEI",
|
|
"ERESSEA",
|
|
"PERSONEN",
|
|
"REGION",
|
|
"SCHIFF",
|
|
"SILBER",
|
|
"STRAßEN",
|
|
"TEMPORÄRE",
|
|
"FLIEHE",
|
|
"GEBÄUDE",
|
|
"GIB", /* Für HELFE */
|
|
"KÄMPFE",
|
|
"BEWACHE",
|
|
"ZAUBER",
|
|
"PAUSE",
|
|
"VORNE",
|
|
"AGGRESSIV",
|
|
"DEFENSIV",
|
|
"STUFE",
|
|
"HELFE",
|
|
"FREMDES",
|
|
"AURA",
|
|
"UM",
|
|
"BEISTAND",
|
|
"GNADE",
|
|
"HINTER",
|
|
"VOR",
|
|
"ANZAHL",
|
|
"GEGENSTÄNDE",
|
|
"TRÄNKE",
|
|
"GRUPPE",
|
|
"PARTEITARNUNG",
|
|
"BÄUME",
|
|
"XEPOTION",
|
|
"XEBALLOON",
|
|
"XELAEN"
|
|
};
|
|
|
|
|
|
const char *keywords[MAXKEYWORDS] =
|
|
{
|
|
"//",
|
|
"BANNER",
|
|
"ARBEITEN",
|
|
"ATTACKIEREN",
|
|
"BIETEN",
|
|
"BEKLAUEN",
|
|
"BELAGERE",
|
|
"BENENNEN",
|
|
"BENUTZEN",
|
|
"BESCHREIBEN",
|
|
"BETRETEN",
|
|
"BEWACHEN",
|
|
"BOTSCHAFT",
|
|
"ENDE",
|
|
"FAHREN",
|
|
"NUMMER",
|
|
"FOLGEN",
|
|
"FORSCHEN",
|
|
"GIB",
|
|
"HELFEN",
|
|
"KÄMPFEN",
|
|
"KAMPFZAUBER",
|
|
"KAUFEN",
|
|
"KONTAKTIEREN",
|
|
"LEHREN",
|
|
"LERNEN",
|
|
"LIEFERE",
|
|
"MACHEN",
|
|
"NACH",
|
|
"PASSWORT",
|
|
"REGION",
|
|
"REKRUTIEREN",
|
|
"RESERVIEREN",
|
|
"ROUTE",
|
|
"SABOTIEREN",
|
|
"OPTION",
|
|
"SPIONIEREN",
|
|
"STIRB",
|
|
"TARNEN",
|
|
"TRANSPORTIEREN",
|
|
"TREIBEN",
|
|
"UNTERHALTEN",
|
|
"VERKAUFEN",
|
|
"VERLASSEN",
|
|
"VERGESSEN",
|
|
"ZAUBERE",
|
|
"ZEIGEN",
|
|
"ZERSTÖREN",
|
|
"ZÜCHTEN",
|
|
"DEFAULT",
|
|
"REPORT",
|
|
"URSPRUNG",
|
|
"EMAIL",
|
|
"MEINUNG",
|
|
"MAGIEGEBIET",
|
|
"PIRATERIE",
|
|
"NEUSTART",
|
|
"GRUPPE",
|
|
"OPFERE",
|
|
"BETEN",
|
|
"SORTIEREN",
|
|
"JIHAD",
|
|
"GM",
|
|
"INFO",
|
|
#ifdef USE_UGROUPS
|
|
"JOINVERBAND",
|
|
"LEAVEVERBAND",
|
|
#endif
|
|
"PRÄFIX",
|
|
"SYNONYM",
|
|
"PFLANZEN",
|
|
"WERWESEN",
|
|
"XONTORMIA"
|
|
};
|
|
|
|
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",
|
|
"SILBERPOOL",
|
|
"STATISTIK",
|
|
"DEBUG",
|
|
"ZIPPED",
|
|
"ZEITUNG", /* Option hat Sonderbehandlung! */
|
|
"MATERIALPOOL",
|
|
"ADRESSEN",
|
|
"BZIP2",
|
|
"PUNKTE",
|
|
"SHOWSKCHANGE"
|
|
};
|
|
|
|
int
|
|
max_skill(const faction * f, skill_t sk)
|
|
{
|
|
int m = INT_MAX;
|
|
|
|
switch (sk) {
|
|
case SK_MAGIC:
|
|
m = MAXMAGICIANS;
|
|
if (old_race(f->race) == RC_ELF) m += 1;
|
|
m += fspecial(f, FS_MAGOCRACY) * 2;
|
|
break;
|
|
case SK_ALCHEMY:
|
|
m = MAXALCHEMISTS;
|
|
break;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
char * g_basedir;
|
|
char * g_resourcedir;
|
|
|
|
const char *
|
|
basepath(void)
|
|
{
|
|
if (g_basedir) return g_basedir;
|
|
return ".";
|
|
}
|
|
|
|
const char *
|
|
resourcepath(void)
|
|
{
|
|
static char zText[MAX_PATH];
|
|
if (g_resourcedir) return g_resourcedir;
|
|
return strcat(strcpy(zText, basepath()), "/res");
|
|
}
|
|
|
|
int
|
|
count_all_money(const region * r)
|
|
{
|
|
const unit *u;
|
|
int m = rmoney(r);
|
|
|
|
for (u = r->units; u; u = u->next)
|
|
m += get_money(u);
|
|
|
|
return m;
|
|
}
|
|
|
|
int
|
|
count_skill(faction * f, skill_t sk)
|
|
{
|
|
int n = 0;
|
|
region *r;
|
|
unit *u;
|
|
region *last = lastregion(f);
|
|
|
|
for (r = firstregion(f); r != last; r = r->next)
|
|
for (u = r->units; u; u = u->next)
|
|
if (u->faction == f && has_skill(u, sk))
|
|
if (!is_familiar(u)) n += u->number;
|
|
|
|
return n;
|
|
}
|
|
|
|
int
|
|
shipcapacity (const ship * sh)
|
|
{
|
|
int i;
|
|
|
|
/* sonst ist construction:: size nicht ship_type::maxsize */
|
|
assert(!sh->type->construction || sh->type->construction->improvement==NULL);
|
|
|
|
if (sh->type->construction && sh->size!=sh->type->construction->maxsize)
|
|
return 0;
|
|
|
|
#ifdef SHIPDAMAGE
|
|
i = ((sh->size * DAMAGE_SCALE - sh->damage) / DAMAGE_SCALE)
|
|
* sh->type->cargo / sh->size;
|
|
i += ((sh->size * DAMAGE_SCALE - sh->damage) % DAMAGE_SCALE)
|
|
* sh->type->cargo / (sh->size*DAMAGE_SCALE);
|
|
#else
|
|
i = sh->type->cargo;
|
|
#endif
|
|
return i;
|
|
}
|
|
|
|
int max_unique_id;
|
|
int quiet = 0;
|
|
|
|
FILE *debug;
|
|
|
|
int
|
|
shipspeed (ship * sh, const unit * u)
|
|
{
|
|
int k = sh->type->range;
|
|
static const curse_type * stormwind_ct, * nodrift_ct;
|
|
static boolean init;
|
|
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 (old_race(u->faction->race) == RC_AQUARIAN
|
|
&& old_race(u->race) == RC_AQUARIAN)
|
|
k += 1;
|
|
#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 min (k, MAXSPEED);
|
|
}
|
|
|
|
/* erwartete Anzahl Einheiten x 2 */
|
|
#define UMAXHASH 199999
|
|
unit *unithash[UMAXHASH];
|
|
|
|
void
|
|
uhash (unit * u)
|
|
{
|
|
assert(!u->nexthash || !"unit ist bereits gehasht");
|
|
u->nexthash = unithash[u->no % UMAXHASH];
|
|
unithash[u->no % UMAXHASH] = u;
|
|
}
|
|
|
|
void
|
|
uunhash (unit * u)
|
|
{
|
|
unit ** x = &(unithash[u->no % UMAXHASH]);
|
|
while (*x && *x!=u) x = &(*x)->nexthash;
|
|
assert(*x || !"unit nicht gefunden");
|
|
*x = u->nexthash;
|
|
u->nexthash=NULL;
|
|
}
|
|
|
|
unit *
|
|
ufindhash (int i)
|
|
{
|
|
unit * u = unithash[i % UMAXHASH];
|
|
while (u && u->no!=i) u = u->nexthash;
|
|
return u;
|
|
}
|
|
|
|
#define FMAXHASH 2047
|
|
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
|
|
stripfaction (faction * f)
|
|
{
|
|
#ifdef OLD_MESSAGES
|
|
free_messages(f->msgs);
|
|
#else
|
|
/* TODO: inhalt auch löschen */
|
|
if (f->msgs) free(f->msgs);
|
|
if (f->battles) free(f->battles);
|
|
#endif
|
|
/* TODO: free msgs */
|
|
freestrlist(f->mistakes);
|
|
freelist(f->allies);
|
|
free(f->email);
|
|
free(f->banner);
|
|
free(f->passw);
|
|
free(f->override);
|
|
free(f->name);
|
|
#ifndef FAST_REGION
|
|
vset_destroy(&f->regions);
|
|
#endif
|
|
while (f->attribs) a_remove (&f->attribs, f->attribs);
|
|
freelist(f->ursprung);
|
|
funhash(f);
|
|
}
|
|
|
|
void
|
|
stripunit(unit * u)
|
|
{
|
|
free(u->name);
|
|
free(u->display);
|
|
free(u->thisorder);
|
|
free(u->lastorder);
|
|
freestrlist(u->orders);
|
|
freestrlist(u->botschaften);
|
|
if(u->skills) free(u->skills);
|
|
while (u->items) {
|
|
item * it = u->items->next;
|
|
u->items->next = NULL;
|
|
i_free(u->items);
|
|
u->items = it;
|
|
}
|
|
while (u->attribs) a_remove (&u->attribs, u->attribs);
|
|
}
|
|
|
|
void
|
|
verify_data(void)
|
|
{
|
|
#ifndef NDEBUG
|
|
int lf = -1;
|
|
faction *f;
|
|
unit *u;
|
|
int mage, alchemist;
|
|
|
|
puts(" - Überprü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 > 50000 || u->number < 0) {
|
|
if (lf != f->no) {
|
|
lf = f->no;
|
|
printf("Partei %s:\n", factionid(f));
|
|
}
|
|
log_warning(("Einheit %s hat %d Personen\n", unitid(u), u->number));
|
|
}
|
|
}
|
|
if (f->no != 0 && ((mage > 3 && old_race(f->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 (rand() % old < new_value)
|
|
t++;
|
|
|
|
return t;
|
|
}
|
|
|
|
int
|
|
change_hitpoints (unit * u, int value)
|
|
{
|
|
int hp = u->hp;
|
|
|
|
hp += value;
|
|
|
|
/* Jede Person benötigt mindestens 1 HP */
|
|
if (hp < u->number){
|
|
if (hp < 0){ /* Einheit tot */
|
|
hp = 0;
|
|
}
|
|
scale_number(u, hp);
|
|
}
|
|
u->hp = hp;
|
|
return hp;
|
|
}
|
|
|
|
int
|
|
atoip(const char *s)
|
|
{
|
|
int n;
|
|
|
|
n = atoi (s);
|
|
|
|
if (n < 0)
|
|
n = 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
void
|
|
scat (const char *s)
|
|
{
|
|
strncat (buf, s, BUFSIZE - strlen (buf));
|
|
}
|
|
|
|
void
|
|
icat (int n)
|
|
{
|
|
char s[12];
|
|
|
|
sprintf (s, "%d", n);
|
|
scat (s);
|
|
}
|
|
|
|
|
|
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
|
|
effstealth(const unit * u)
|
|
{
|
|
int e, es;
|
|
|
|
/* Auf dem Ozean keine Tarnung! */
|
|
if (u->region->terrain == T_OCEAN) return 0;
|
|
|
|
e = effskill(u, SK_STEALTH);
|
|
|
|
es = u_geteffstealth(u);
|
|
if (es >=0 && es < e) return es;
|
|
return e;
|
|
}
|
|
|
|
int
|
|
eff_stealth (const unit * u, const region * r)
|
|
{
|
|
int e, es;
|
|
|
|
e = eff_skill (u, SK_STEALTH, r);
|
|
|
|
es = u_geteffstealth(u);
|
|
if (es >=0 && es < e) return es;
|
|
return e;
|
|
}
|
|
|
|
void
|
|
scale_number (unit * u, int n)
|
|
{
|
|
skill_t sk;
|
|
const attrib * a;
|
|
int remain;
|
|
|
|
if (n == u->number) return;
|
|
if (n && u->number) {
|
|
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 ((rand() % u->number) < remain)
|
|
++u->hp; /* Nachkommastellen */
|
|
} else {
|
|
remain = 0;
|
|
u->hp = 0;
|
|
}
|
|
for (a = a_find(u->attribs, &at_effect);a;a=a->nexttype) {
|
|
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 ((rand() % u->number) < remain)
|
|
++snew; /* Nachkommastellen */
|
|
}
|
|
data->value = snew;
|
|
}
|
|
for (sk = 0; sk < MAXSKILLS; sk++) {
|
|
if (n==0 || u->number == 0) {
|
|
remove_skill(u, sk);
|
|
}
|
|
}
|
|
|
|
set_number(u, n);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef ALLIANCES
|
|
int
|
|
allied(const unit * u, const faction * f2, int mode)
|
|
{
|
|
if (u->faction->alliance==f2->alliance) return mode;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
isallied(const plane * pl, const faction * f, const faction * f2, int mode)
|
|
{
|
|
unused(pl);
|
|
if (f->alliance==f2->alliance) return mode;
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
/* f hat zu f2 HELFE mode gesetzt */
|
|
int
|
|
isallied(const plane * pl, const faction * f, const faction * f2, int mode)
|
|
{
|
|
ally *sf;
|
|
attrib * a;
|
|
|
|
if (f == f2) return mode;
|
|
if (f2==NULL) return 0;
|
|
|
|
a = a_find(f->attribs, &at_gm);
|
|
while (a) {
|
|
plane * p = (plane*)a->data.v;
|
|
if (p==pl) return mode;
|
|
a=a->next;
|
|
}
|
|
|
|
if (pl && pl->flags & PFL_FRIENDLY) return mode;
|
|
if (mode != HELP_GIVE && pl && (pl->flags & PFL_NOALLIANCES)) return 0;
|
|
for (sf = f->allies; sf; sf = sf->next)
|
|
if (sf->faction == f2)
|
|
return (sf->status & mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alliance(const ally * sf, const faction * f, int mode)
|
|
{
|
|
while (sf) {
|
|
if (sf->faction == f)
|
|
return sf->status & mode;
|
|
sf = sf->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Die Gruppe von Einheit u hat helfe zu f2 gesetzt. */
|
|
int
|
|
allied(const unit * u, const faction * f2, int mode)
|
|
{
|
|
ally * sf;
|
|
const attrib * a;
|
|
plane * pl;
|
|
|
|
if (u->faction == f2) return mode;
|
|
if (u->faction == NULL || f2==NULL) return 0;
|
|
|
|
sf = u->faction->allies;
|
|
pl = getplane(u->region);
|
|
|
|
if (pl && pl->flags & PFL_FRIENDLY) return mode;
|
|
|
|
/* if f2 is a gm in this plane, everyone has an auto-help to it */
|
|
a = a_find(f2->attribs, &at_gm);
|
|
while (a) {
|
|
plane * p = (plane*)a->data.v;
|
|
if (p==pl) return mode;
|
|
a=a->next;
|
|
}
|
|
|
|
if (mode != HELP_GIVE && pl && (pl->flags & PFL_NOALLIANCES))
|
|
return 0;
|
|
a = a_find(u->attribs, &at_group);
|
|
if (a) sf = ((group*)a->data.v)->allies;
|
|
return alliance(sf, f2, mode);
|
|
}
|
|
#endif
|
|
|
|
boolean
|
|
seefaction(const faction * f, const region * r, const unit * u, int modifier)
|
|
{
|
|
if (((f == u->faction) || !fval(u, FL_PARTEITARNUNG)) && 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 n;
|
|
boolean cansee = false;
|
|
unit *u2;
|
|
|
|
if (u->faction == f || omniscient(f)) {
|
|
return true;
|
|
} else if (old_race(u->race) == RC_SPELL) {
|
|
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;
|
|
}
|
|
}
|
|
n = eff_stealth(u, r) - modifier;
|
|
for (u2 = r->units; u2; u2 = u2->next) {
|
|
if (u2->faction == f) {
|
|
int o;
|
|
|
|
if (getguard(u) || usiege(u) || u->building || u->ship) {
|
|
cansee = true;
|
|
break;
|
|
}
|
|
if (invisible(u) >= u->number
|
|
&& !get_item(u2, I_AMULET_OF_TRUE_SEEING))
|
|
continue;
|
|
|
|
o = eff_skill(u2, SK_OBSERVATION, r);
|
|
#if NIGHTEYES
|
|
if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT)
|
|
o = NIGHT_EYE_TALENT;
|
|
#endif
|
|
if (o >= n) {
|
|
cansee = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cansee;
|
|
}
|
|
|
|
#if 0
|
|
boolean
|
|
cansee(faction * f, region * r, 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 */
|
|
{
|
|
boolean cansee = false;
|
|
unit *u2;
|
|
#if FAST_CANSEE
|
|
static region * lastr = NULL;
|
|
static faction * lastf = NULL;
|
|
static int maxskill = INT_MIN;
|
|
#endif
|
|
int n = 0;
|
|
boolean ring = false;
|
|
if (u->faction == f || omniscient(f)) return true;
|
|
#if FAST_CANSEE /* buggy */
|
|
if (lastr==r && lastf==f) {
|
|
n = eff_stealth(u, r) - modifier;
|
|
if (n<=maxskill) return true;
|
|
}
|
|
else {
|
|
lastf=f;
|
|
lastr=r;
|
|
maxskill=INT_MIN;
|
|
}
|
|
#endif
|
|
if (old_race(u->race) == RC_SPELL || u->number == 0) return false;
|
|
else {
|
|
boolean xcheck = false;
|
|
int o = INT_MIN;
|
|
ring = (boolean)(ring || (invisible(u) >= u->number));
|
|
n = n || eff_stealth(u, r) - modifier;
|
|
for (u2 = r->units; u2; u2 = u2->next) {
|
|
if (u2->faction == f) {
|
|
|
|
if (!xcheck && (getguard(u) || usiege(u) || u->building || u->ship)) {
|
|
cansee = true;
|
|
break;
|
|
}
|
|
else
|
|
xcheck = true;
|
|
if (ring && !get_item(u2, I_AMULET_OF_TRUE_SEEING))
|
|
continue;
|
|
|
|
o = eff_skill(u2, SK_OBSERVATION, r);
|
|
#if NIGHTEYES
|
|
if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT)
|
|
o = NIGHT_EYE_TALENT;
|
|
#endif
|
|
if (o >= n) {
|
|
cansee = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#if FAST_CANSEE
|
|
maxskill = o;
|
|
#endif
|
|
}
|
|
return cansee;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
boolean cansee = false;
|
|
unit *u2;
|
|
if (old_race(u->race) == RC_SPELL || u->number == 0) return false;
|
|
else if (u->faction == f) cansee = true;
|
|
else {
|
|
|
|
n = eff_stealth(u, r) - modifier;
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next){
|
|
if (u2->faction == f) {
|
|
int o;
|
|
|
|
if (getguard(u) || usiege(u) || u->building || u->ship) {
|
|
cansee = true;
|
|
break;
|
|
}
|
|
if (invisible(u) >= u->number
|
|
&& !get_item(u2, I_AMULET_OF_TRUE_SEEING))
|
|
continue;
|
|
|
|
o = eff_skill(u2, SK_OBSERVATION, r);
|
|
|
|
#if NIGHTEYES
|
|
if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT)
|
|
o = NIGHT_EYE_TALENT;
|
|
#endif
|
|
if (o >= n) {
|
|
cansee = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (getguard(u) || usiege(u) || u->building || u->ship) {
|
|
cansee = true;
|
|
}
|
|
}
|
|
return cansee;
|
|
}
|
|
|
|
#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ürzt:\n%s\n",
|
|
(int)maxlen, s));
|
|
strnzcpy(buffer, s, maxlen);
|
|
return buffer;
|
|
}
|
|
return s;
|
|
}
|
|
#endif
|
|
|
|
attrib_type at_lighthouse = {
|
|
"lighthouse"
|
|
/* Rest ist NULL; temporä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)
|
|
{
|
|
region * r = lh->region;
|
|
int d = (int)log10(lh->size) + 1;
|
|
int x, y;
|
|
static const struct building_type * bt_lighthouse;
|
|
if (!bt_lighthouse) bt_lighthouse = bt_find("lighthouse");
|
|
assert(bt_lighthouse);
|
|
|
|
if (lh->type!=bt_lighthouse) return;
|
|
|
|
for (x=-d;x<=d;++x) {
|
|
for (y=-d;y<=d;++y) {
|
|
attrib * a;
|
|
region * r2 = findregion(x+r->x, y+r->y);
|
|
if (rterrain(r2)!=T_OCEAN) continue;
|
|
if (!r2 || distance(r, r2) > d) continue;
|
|
a = a_find(r2->attribs, &at_lighthouse);
|
|
while (a) {
|
|
building * b = (building*)a->data.v;
|
|
if (b==lh) break;
|
|
a=a->nexttype;
|
|
}
|
|
if (!a) {
|
|
a = a_add(&r2->attribs, a_new(&at_lighthouse));
|
|
a->data.v = (void*)lh;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
count_all(const faction * f)
|
|
{
|
|
int n = 0;
|
|
unit *u;
|
|
for (u=f->units;u;u=u->nextF)
|
|
if (playerrace(u->race)) {
|
|
n += u->number;
|
|
assert(f==u->faction);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int
|
|
count_maxmigrants(const faction * f)
|
|
{
|
|
int x = 0;
|
|
if (old_race(f->race) == RC_HUMAN) {
|
|
x = (int)(log10(count_all(f) / 50.0) * 20);
|
|
if (x < 0) x = 0;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
/* GET STR, I zur Eingabe von Daten liest diese aus dem Buffer, der beim ersten
|
|
* Aufruf inititialisiert wird? */
|
|
|
|
char *
|
|
igetstrtoken (const char *s1)
|
|
{
|
|
int i;
|
|
static const char *s;
|
|
static char lbuf[DISPLAYSIZE + 1];
|
|
|
|
if (s1) s = s1;
|
|
while (*s == ' ')
|
|
s++;
|
|
i = 0;
|
|
|
|
while (*s && *s != ' ' && i < DISPLAYSIZE) {
|
|
lbuf[i] = (*s);
|
|
|
|
/* Hier wird space_replacement wieder in space zurueck
|
|
* verwandelt, ausser wenn es nach einem escape_char kommt. Im
|
|
* letzteren Fall wird escape_char durch space_replacement
|
|
* ersetzt, statt den aktuellen char einfach dran zu haengen. */
|
|
|
|
if (*s == SPACE_REPLACEMENT) {
|
|
if (i > 0 && lbuf[i - 1] == ESCAPE_CHAR)
|
|
lbuf[--i] = SPACE_REPLACEMENT;
|
|
else
|
|
lbuf[i] = ' ';
|
|
}
|
|
i++;
|
|
s++;
|
|
}
|
|
|
|
lbuf[i] = 0;
|
|
return lbuf;
|
|
}
|
|
|
|
char *
|
|
getstrtoken (void)
|
|
{
|
|
return igetstrtoken (0);
|
|
}
|
|
|
|
int
|
|
geti (void)
|
|
{
|
|
return atoip (getstrtoken ());
|
|
}
|
|
|
|
/* GET KEYWORD, SKILL, ITEM, SPELL benutzen FINDSTR - welche Item um Item eine
|
|
* Liste durchsucht.
|
|
*
|
|
* FIND wird immer von GET aufgerufen. GET braucht keine Parameter, IGET braucht
|
|
* einen String aus dem gelesen wird. In FIND stehen dann listen etc drinnen.
|
|
* FIND kann man auch allein verwenden, wenn der string _nur_ noch das gesuchte
|
|
* object enthaelt. Steht noch weitere info darin, sollte man GET verwenden,
|
|
* bzw. GETI wenn die info am Anfang eines neuen Stringes steht. */
|
|
|
|
int
|
|
findstr(const char **v, const char *s, unsigned char n)
|
|
{
|
|
int i;
|
|
size_t ss = strlen(s);
|
|
if (!ss)
|
|
return -1;
|
|
for (i = 0; i != n; i++)
|
|
if (!strncasecmp(s, v[i], ss))
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
enum {
|
|
UT_NONE,
|
|
UT_PARAM,
|
|
UT_ITEM,
|
|
UT_BUILDING,
|
|
UT_HERB,
|
|
UT_POTION,
|
|
UT_MAX
|
|
};
|
|
|
|
static struct lstr {
|
|
const struct locale * lang;
|
|
struct tnode tokens[UT_MAX];
|
|
struct tnode skillnames;
|
|
struct tnode keywords;
|
|
struct tnode races;
|
|
struct tnode directions;
|
|
struct tnode options;
|
|
struct lstr * next;
|
|
} * lstrs;
|
|
|
|
static struct lstr *
|
|
get_lnames(const struct locale * lang)
|
|
{
|
|
static struct lstr * lnames = NULL;
|
|
static const struct locale * lastlang = NULL;
|
|
|
|
if (lastlang!=lang || lnames==NULL) {
|
|
lnames = lstrs;
|
|
while (lnames && lnames->lang!=lang) lnames = lnames->next;
|
|
if (lnames==NULL) {
|
|
lnames = calloc(sizeof(struct lstr), 1);
|
|
lnames->lang = lang;
|
|
lnames->next = lstrs;
|
|
lstrs = lnames;
|
|
}
|
|
}
|
|
return lnames;
|
|
}
|
|
|
|
const struct race *
|
|
findrace(const char * s, const struct locale * lang)
|
|
{
|
|
struct lstr * lnames = get_lnames(lang);
|
|
const struct race * rc;
|
|
|
|
if (findtoken(&lnames->races, s, (void **)&rc)==E_TOK_SUCCESS) {
|
|
return rc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
findoption(const char *s, const struct locale * lang)
|
|
{
|
|
struct lstr * lnames = get_lnames(lang);
|
|
int dir;
|
|
|
|
if (findtoken(&lnames->options, s, (void**)&dir)==E_TOK_SUCCESS) {
|
|
return (direction_t)dir;
|
|
}
|
|
return NODIRECTION;
|
|
}
|
|
|
|
skill_t
|
|
findskill(const char *s, const struct locale * lang)
|
|
{
|
|
struct lstr * lnames = get_lnames(lang);
|
|
int i;
|
|
|
|
if (findtoken(&lnames->skillnames, s, (void**)&i)==E_TOK_NOMATCH) return NOSKILL;
|
|
return (skill_t)i;
|
|
}
|
|
|
|
keyword_t
|
|
findkeyword(const char *s, const struct locale * lang)
|
|
{
|
|
struct lstr * lnames = get_lnames(lang);
|
|
int i;
|
|
#ifdef AT_PERSISTENT
|
|
if (*s == '@') s++;
|
|
#endif
|
|
if (findtoken(&lnames->keywords, s, (void**)&i)==E_TOK_NOMATCH) return NOKEYWORD;
|
|
if (global.disabled[i]) return NOKEYWORD;
|
|
return (keyword_t) i;
|
|
}
|
|
|
|
keyword_t
|
|
igetkeyword (const char *s, const struct locale * lang)
|
|
{
|
|
return findkeyword (igetstrtoken (s), lang);
|
|
}
|
|
|
|
keyword_t
|
|
getkeyword (const struct locale * lang)
|
|
{
|
|
return findkeyword (getstrtoken (), lang);
|
|
}
|
|
|
|
param_t
|
|
findparam(const char *s, const struct locale * lang)
|
|
{
|
|
struct lstr * lnames = get_lnames(lang);
|
|
const building_type * btype;
|
|
|
|
int i;
|
|
if (findtoken(&lnames->tokens[UT_PARAM], s, (void**)&i)==E_TOK_NOMATCH) {
|
|
btype = findbuildingtype(s, lang);
|
|
if (btype!=NULL) return (param_t) P_BUILDING;
|
|
return NOPARAM;
|
|
}
|
|
return (param_t)i;
|
|
}
|
|
|
|
param_t
|
|
igetparam (const char *s, const struct locale *lang)
|
|
{
|
|
return findparam (igetstrtoken (s), lang);
|
|
}
|
|
param_t
|
|
getparam (const struct locale * lang)
|
|
{
|
|
return findparam (getstrtoken (), lang);
|
|
}
|
|
|
|
#ifdef FUZZY_BASE36
|
|
extern int fuzzy_hits;
|
|
boolean enable_fuzzy = false;
|
|
#endif /* FUZZY_BASE36 */
|
|
|
|
faction *
|
|
findfaction (int n)
|
|
{
|
|
faction * f;
|
|
|
|
f = ffindhash(n);
|
|
if (f) return f;
|
|
for (f = factions; f; f = f->next)
|
|
if (f->no == n) {
|
|
fhash(f);
|
|
return f;
|
|
}
|
|
#ifdef FUZZY_BASE36
|
|
if(enable_fuzzy) {
|
|
n = atoi(itoa36(n));
|
|
if (n) {
|
|
f = ffindhash(n);
|
|
if (f) return f;
|
|
for (f = factions; f; f = f->next) {
|
|
if (f->no == n) {
|
|
fhash(f);
|
|
return (f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* FUZZY_BASE36 */
|
|
/* Gibt komische Seiteneffekte hier! */
|
|
/* if (n==MONSTER_FACTION) return makemonsters(); */
|
|
return NULL;
|
|
}
|
|
|
|
faction *
|
|
getfaction (void)
|
|
{
|
|
return findfaction (getid());
|
|
}
|
|
|
|
faction *
|
|
findfaction_unique_id (int unique_id)
|
|
{
|
|
faction *f;
|
|
|
|
for (f = factions; f; f = f->next)
|
|
if (f->unique_id == unique_id) {
|
|
return f;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
return findunitg(n, NULL);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
char *s;
|
|
|
|
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(s);
|
|
}
|
|
|
|
/* exported symbol */
|
|
boolean getunitpeasants;
|
|
unit *
|
|
getunitg(const region * r, const faction * f)
|
|
{
|
|
int n;
|
|
getunitpeasants = 0;
|
|
|
|
n = read_unitid(f, r);
|
|
|
|
if (n == 0) {
|
|
getunitpeasants = 1;
|
|
return NULL;
|
|
}
|
|
|
|
if (n < 0) return 0;
|
|
|
|
return findunit(n);
|
|
}
|
|
|
|
unit *
|
|
getunit(const region * r, const faction * f)
|
|
{
|
|
int n;
|
|
unit *u2;
|
|
getunitpeasants = 0;
|
|
|
|
n = read_unitid(f, r);
|
|
|
|
if (n == 0) {
|
|
getunitpeasants = 1;
|
|
return NULL;
|
|
}
|
|
|
|
if (n < 0) return 0;
|
|
|
|
for (u2 = r->units; u2; u2 = u2->next) {
|
|
if (u2->no == n && !fval(u2, UFL_ISNEW)) {
|
|
return u2;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
/* - String Listen --------------------------------------------- */
|
|
|
|
strlist *
|
|
makestrlist (const char *s)
|
|
{
|
|
strlist *S;
|
|
S = malloc(sizeof(strlist));
|
|
S->s = strdup(s);
|
|
S->next = NULL;
|
|
return S;
|
|
}
|
|
|
|
void
|
|
addstrlist (strlist ** SP, const char *s)
|
|
{
|
|
addlist(SP, makestrlist(s));
|
|
}
|
|
|
|
void
|
|
freestrlist (strlist * s)
|
|
{
|
|
strlist *q, *p = s;
|
|
while (p) {
|
|
q = p->next;
|
|
free(p->s);
|
|
free(p);
|
|
p = q;
|
|
}
|
|
}
|
|
|
|
/* - Meldungen und Fehler ------------------------------------------------- */
|
|
|
|
boolean nomsg = false;
|
|
|
|
/* - Namen der Strukturen -------------------------------------- */
|
|
typedef char name[OBJECTIDSIZE + 1];
|
|
static name idbuf[8];
|
|
static int nextbuf = 0;
|
|
|
|
char *
|
|
estring(const char *s)
|
|
{
|
|
char *buf = idbuf[(++nextbuf) % 8];
|
|
char *r;
|
|
|
|
strcpy(buf,s);
|
|
r = buf;
|
|
|
|
while(*buf) {
|
|
if(*buf == ' ') {
|
|
*buf = '~';
|
|
}
|
|
buf++;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
char *
|
|
cstring(const char *s)
|
|
{
|
|
char *buf = idbuf[(++nextbuf) % 8];
|
|
char *r;
|
|
|
|
strcpy(buf,s);
|
|
r = buf;
|
|
|
|
while(*buf) {
|
|
if(*buf == '~') {
|
|
*buf = ' ';
|
|
}
|
|
buf++;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
const char *
|
|
regionid(const region * r)
|
|
{
|
|
char *buf = idbuf[(++nextbuf) % 8];
|
|
|
|
if (!r) {
|
|
strcpy(buf, "(Chaos)");
|
|
} else {
|
|
sprintf(buf, "\\r(%d,%d)", r->x, r->y);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
buildingname (const building * b)
|
|
{
|
|
char *buf = idbuf[(++nextbuf) % 8];
|
|
|
|
sprintf(buf, "%s (%s)", strcheck(b->name, NAMESIZE), itoa36(b->no));
|
|
return buf;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const char *
|
|
unitname(const unit * u)
|
|
{
|
|
char *ubuf = idbuf[(++nextbuf) % 8];
|
|
sprintf(ubuf, "%s (%s)", strcheck(u->name, NAMESIZE), itoa36(u->no));
|
|
return ubuf;
|
|
}
|
|
|
|
char *
|
|
xunitid(const unit *u)
|
|
{
|
|
char *buf = idbuf[(++nextbuf) % 8];
|
|
sprintf(buf, "%s in %s", unitname(u), regionid(u->region));
|
|
return buf;
|
|
}
|
|
|
|
/* -- Erschaffung neuer Einheiten ------------------------------ */
|
|
|
|
extern faction * dfindhash(int i);
|
|
|
|
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 + (rand() % 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 + (rand() % 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;
|
|
}
|
|
|
|
void
|
|
createunitid(unit *u, int id)
|
|
{
|
|
if (id<=0 || id > MAX_UNIT_NR || ufindhash(id) || dfindhash(id) || forbiddenid(id))
|
|
u->no = newunitid();
|
|
else
|
|
u->no = id;
|
|
uhash(u);
|
|
}
|
|
|
|
unit *
|
|
createunit(region * r, faction * f, int number, const struct race * rc)
|
|
{
|
|
return create_unit(r, f, number, rc, 0, NULL, NULL);
|
|
}
|
|
|
|
attrib_type at_creator = {
|
|
"creator"
|
|
/* Rest ist NULL; temporäres, nicht alterndes Attribut */
|
|
};
|
|
|
|
unit *
|
|
create_unit(region * r, faction * f, int number, const struct race *urace, int id, const char * dname, unit *creator)
|
|
{
|
|
unit * u = calloc(1, sizeof(unit));
|
|
|
|
assert(f->alive);
|
|
u_setfaction(u, f);
|
|
set_string(&u->thisorder, "");
|
|
set_string(&u->lastorder, LOC(u->faction->locale, "defaultorder"));
|
|
u_seteffstealth(u, -1);
|
|
u->race = urace;
|
|
u->irace = urace;
|
|
|
|
set_number(u, number);
|
|
|
|
/* die nummer der neuen einheit muss vor name_unit generiert werden,
|
|
* da der default name immer noch 'Nummer u->no' ist */
|
|
createunitid(u, id);
|
|
|
|
/* zuerst in die Region setzen, da zb Drachennamen den Regionsnamen
|
|
* enthalten */
|
|
move_unit(u, r, NULL);
|
|
|
|
/* u->race muss bereits gesetzt sein, wird für default-hp gebraucht */
|
|
/* u->region auch */
|
|
u->hp = unit_max_hp(u) * number;
|
|
|
|
if (!dname) {
|
|
name_unit(u);
|
|
}
|
|
else set_string(&u->name, dname);
|
|
set_string(&u->display, "");
|
|
|
|
/* Nicht zu der Einheitenzahl zählen sollten auch alle Monster. Da
|
|
* aber auf die MAXUNITS nur in MACHE TEMP geprüft wird, ist es egal */
|
|
if(!fval(u->race, RCF_UNDEAD)) {
|
|
f->no_units++;
|
|
}
|
|
|
|
if (creator) {
|
|
attrib * a;
|
|
|
|
/* erbt Kampfstatus */
|
|
u->status = creator->status;
|
|
|
|
/* erbt Gebäude/Schiff*/
|
|
if (creator->region==r) {
|
|
u->building = creator->building;
|
|
u->ship = creator->ship;
|
|
}
|
|
|
|
/* Temps von parteigetarnten Einheiten sind wieder parteigetarnt */
|
|
if (fval(creator, FL_PARTEITARNUNG))
|
|
fset(u, FL_PARTEITARNUNG);
|
|
|
|
/* Daemonentarnung */
|
|
set_racename(&u->attribs, get_racename(creator->attribs));
|
|
if (fval(u->race, RCF_SHAPESHIFT) && fval(creator->race, RCF_SHAPESHIFT)) {
|
|
u->irace = creator->irace;
|
|
}
|
|
|
|
/* Gruppen */
|
|
a = a_find(creator->attribs, &at_group);
|
|
if (a) {
|
|
group * g = (group*)a->data.v;
|
|
a_add(&u->attribs, a_new(&at_group))->data.v = g;
|
|
}
|
|
a = a_find(creator->attribs, &at_otherfaction);
|
|
if (a) {
|
|
a_add(&u->attribs, make_otherfaction(get_otherfaction(a)));
|
|
}
|
|
|
|
a = a_add(&u->attribs, a_new(&at_creator));
|
|
a->data.v = creator;
|
|
}
|
|
/* Monster sind grundsätzlich parteigetarnt */
|
|
if(f->no <= 0) fset(u, FL_PARTEITARNUNG);
|
|
|
|
return u;
|
|
}
|
|
|
|
/* Setzt Default Befehle -------------------------------------- */
|
|
|
|
boolean
|
|
idle (faction * f)
|
|
{
|
|
return (boolean) (f ? false : true);
|
|
}
|
|
|
|
|
|
int
|
|
maxworkingpeasants(const struct region * r)
|
|
{
|
|
#if GROWING_TREES
|
|
int i = production(r) * MAXPEASANTS_PER_AREA
|
|
- ((rtrees(r,2)+rtrees(r,1)/2) * TREESIZE);
|
|
#else
|
|
int i = production(r) * MAXPEASANTS_PER_AREA - rtrees(r) * TREESIZE;
|
|
#endif
|
|
return max(i, 0);
|
|
}
|
|
|
|
boolean
|
|
check_leuchtturm(region * r, faction * f)
|
|
{
|
|
attrib * a;
|
|
|
|
if (rterrain(r) != T_OCEAN) return false;
|
|
|
|
for (a = a_find(r->attribs, &at_lighthouse);a;a=a->nexttype) {
|
|
building *b = (building *)a->data.v;
|
|
region *r2 = b->region;
|
|
|
|
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;
|
|
|
|
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 */
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
region *
|
|
lastregion (faction * f)
|
|
{
|
|
region *r = f->last;
|
|
if (!r && f->units) {
|
|
for (r = firstregion(f); r; r = r->next) {
|
|
plane * p = rplane(r);
|
|
unit *u;
|
|
attrib *ru;
|
|
for (u = r->units; u; u = u->next) {
|
|
if (u->faction == f) {
|
|
f->last = r->next;
|
|
break;
|
|
}
|
|
}
|
|
if (f->last == r->next)
|
|
continue;
|
|
for (ru = a_find(r->attribs, &at_travelunit); ru; ru = ru->nexttype) {
|
|
u = (unit*)ru->data.v;
|
|
if (u->faction == f) {
|
|
f->last = r->next;
|
|
break;
|
|
}
|
|
}
|
|
if (f->last == r->next)
|
|
continue;
|
|
if (check_leuchtturm(r, f))
|
|
f->last = r->next;
|
|
if (p && is_watcher(p, f)) {
|
|
f->last = r->next;
|
|
}
|
|
}
|
|
}
|
|
return f->last;
|
|
}
|
|
|
|
region *
|
|
firstregion (faction * f)
|
|
{
|
|
region *r;
|
|
if (f->first || !f->units)
|
|
return f->first;
|
|
|
|
for (r = regions; r; r = r->next) {
|
|
attrib *ru;
|
|
unit *u;
|
|
plane * p = rplane(r);
|
|
for (u = r->units; u; u = u->next) {
|
|
if (u->faction == f) {
|
|
f->first = r;
|
|
return r;
|
|
}
|
|
}
|
|
if (f->first == r->next)
|
|
continue;
|
|
for (ru = a_find(r->attribs, &at_travelunit); ru; ru = ru->nexttype) {
|
|
u = (unit*)ru->data.v;
|
|
if (u->faction == f) {
|
|
f->first = r;
|
|
return r;
|
|
}
|
|
}
|
|
if (check_leuchtturm(r, f)) {
|
|
f->first = r;
|
|
return r;
|
|
}
|
|
if (p && is_watcher(p, f)) {
|
|
f->first = r;
|
|
return r;
|
|
}
|
|
}
|
|
f->first = regions;
|
|
return regions;
|
|
}
|
|
|
|
void ** blk_list[1024];
|
|
int list_index;
|
|
int blk_index;
|
|
|
|
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;
|
|
}
|
|
|
|
const char *
|
|
findorder(const unit * u, const char * cmd)
|
|
{
|
|
strlist * o;
|
|
char * c;
|
|
for (o=u->orders;o;o=o->next) {
|
|
if (!strcmp(cmd, o->s)) {
|
|
if (o->s==cmd)
|
|
return cmd;
|
|
return o->s;
|
|
}
|
|
}
|
|
c = strdup(cmd);
|
|
gc_add(c);
|
|
return c;
|
|
}
|
|
|
|
void
|
|
use_birthdayamulet(region * r, unit * magician, strlist * cmdstrings)
|
|
{
|
|
region *tr;
|
|
direction_t d;
|
|
|
|
unused(cmdstrings);
|
|
unused(magician);
|
|
|
|
for(d=0;d<MAXDIRECTIONS;d++) {
|
|
tr = rconnect(r, d);
|
|
if(tr) addmessage(tr, 0, "Miiauuuuuu...", MSG_MESSAGE, ML_IMPORTANT);
|
|
}
|
|
|
|
tr = r;
|
|
addmessage(r, 0, "Miiauuuuuu...", MSG_MESSAGE, ML_IMPORTANT);
|
|
}
|
|
|
|
typedef struct t_umlaut {
|
|
const char *txt;
|
|
int id;
|
|
int typ;
|
|
} t_umlaut;
|
|
|
|
/* Hier sind zum einen Umlaut-Versionen von Schlüsselworten, aber auch
|
|
* oftmals Umlaut-umschriebene Worte, weil im "normalen" Source die
|
|
* Umlaut-Version steht, damit die im Report erscheint.
|
|
* WICHTIG: "setenv LANG en_US" sonst ist ä != Ä
|
|
*/
|
|
|
|
#if 0
|
|
static const t_umlaut umlaut[] = {
|
|
/* Parameter */
|
|
{ "Straßen", P_ROAD, UT_PARAM },
|
|
/* Gegenstände - alternative Namen */
|
|
{ "Eisenbarren", I_IRON, UT_ITEM },
|
|
{ "Holzstamm", I_WOOD, UT_ITEM },
|
|
{ "Holzstämme", I_WOOD, UT_ITEM },
|
|
{ "Stämme", I_WOOD, UT_ITEM },
|
|
{ "Stamm", I_WOOD, UT_ITEM },
|
|
{ "Quader", I_STONE, UT_ITEM },
|
|
{ "Steinquader", I_STONE, UT_ITEM },
|
|
{ "Langbogen", I_LONGBOW, UT_ITEM },
|
|
{ "Langbögen", I_LONGBOW, UT_ITEM },
|
|
{ "Hemden", I_CHAIN_MAIL, UT_ITEM },
|
|
{ "Panzer", I_PLATE_ARMOR, UT_ITEM },
|
|
{ "Gewürze", I_SPICES, UT_ITEM },
|
|
{ "Öle", I_OIL, UT_ITEM },
|
|
{ "Sehens", I_AMULET_OF_TRUE_SEEING, UT_ITEM },
|
|
{ "Heilung", I_AMULET_OF_HEALING, UT_ITEM },
|
|
{ "Unsichtbarkeit", I_RING_OF_INVISIBILITY, UT_ITEM },
|
|
{ "Macht", I_RING_OF_POWER, UT_ITEM },
|
|
#ifdef COMPATIBILITY
|
|
{ "Einhornaugen", I_EYE_OF_HORAX, UT_ITEM },
|
|
{ "Reisekristall", I_TELEPORTCRYSTAL, UT_ITEM },
|
|
{ "Dunkelheit", I_AMULET_OF_DARKNESS, UT_ITEM },
|
|
#endif
|
|
{ "Kopf", I_DRAGONHEAD, UT_ITEM },
|
|
{ "Köpfe", I_DRAGONHEAD, UT_ITEM },
|
|
{ "Keuschheitsamulett", I_CHASTITY_BELT, UT_ITEM },
|
|
{ "Zweihänder", I_GREATSWORD, UT_ITEM },
|
|
{ "Axt", I_AXE, UT_ITEM },
|
|
{ "Äxte", I_AXE, UT_ITEM },
|
|
{ "Treffens", I_AMULETT_DES_TREFFENS, UT_ITEM },
|
|
{ "Flinkfingerring", I_RING_OF_NIMBLEFINGER, UT_ITEM },
|
|
{ NULL, 0, 0 }
|
|
};
|
|
#endif
|
|
|
|
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ü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 lstr * lnames = get_lnames(lang);
|
|
for (i=0; dirs[i].direction!=NODIRECTION;++i) {
|
|
addtoken(&lnames->directions, LOC(lang, dirs[i].name), (void*)dirs[i].direction);
|
|
}
|
|
}
|
|
|
|
direction_t
|
|
finddirection(const char *s, const struct locale * lang)
|
|
{
|
|
struct lstr * lnames = get_lnames(lang);
|
|
int dir;
|
|
|
|
if (findtoken(&lnames->directions, s, (void**)&dir)==E_TOK_SUCCESS) {
|
|
return (direction_t)dir;
|
|
}
|
|
return NODIRECTION;
|
|
}
|
|
|
|
static void
|
|
init_tokens(const struct locale * lang)
|
|
{
|
|
struct lstr * lnames = get_lnames(lang);
|
|
int i;
|
|
const struct race * rc;
|
|
|
|
init_directions(&lnames->directions, lang);
|
|
for (rc=races;rc;rc=rc->next) {
|
|
addtoken(&lnames->races, LOC(lang, rc_name(rc, 1)), (void*)rc);
|
|
addtoken(&lnames->races, LOC(lang, rc_name(rc, 0)), (void*)rc);
|
|
}
|
|
for (i=0;i!=MAXPARAMS;++i)
|
|
addtoken(&lnames->tokens[UT_PARAM], LOC(lang, parameters[i]), (void*)i);
|
|
for (i=0;i!=MAXSKILLS;++i)
|
|
addtoken(&lnames->skillnames, skillname((skill_t)i, lang), (void*)i);
|
|
for (i=0;i!=MAXKEYWORDS;++i)
|
|
addtoken(&lnames->keywords, LOC(lang, keywords[i]), (void*)i);
|
|
for (i=0;i!=MAXOPTIONS;++i)
|
|
addtoken(&lnames->options, LOC(lang, options[i]), (void*)i);
|
|
#if 0
|
|
for (i=0;umlaut[i].txt;++i)
|
|
addtoken(&lnames->tokens[umlaut[i].typ], umlaut[i].txt, (void*)umlaut[i].id);
|
|
#endif
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
static void
|
|
read_strings(FILE * F)
|
|
{
|
|
char rbuf[8192];
|
|
while (fgets(rbuf, sizeof(rbuf), F)) {
|
|
char * b = rbuf;
|
|
locale * lang;
|
|
char * key = b;
|
|
char * language;
|
|
const char * k;
|
|
|
|
if (rbuf[0]=='#') continue;
|
|
rbuf[strlen(rbuf)-1] = 0; /* \n weg */
|
|
while (*b && *b!=';') ++b;
|
|
if (!*b) continue;
|
|
*b++ = 0;
|
|
language = b;
|
|
while (*b && *b!=';') ++b;
|
|
*b++ = 0;
|
|
lang = find_locale(language);
|
|
if (!lang) lang = make_locale(language);
|
|
k = locale_getstring(lang, key);
|
|
if (k) {
|
|
log_warning(("Trying to register %s[%s]=\"%s\", already have \"%s\"\n", key, language, k, b));
|
|
} else locale_setstring(lang, key, b);
|
|
}
|
|
}
|
|
|
|
const char * messages[] = {
|
|
"%s/%s/strings.xml",
|
|
"%s/%s/messages.xml",
|
|
NULL
|
|
};
|
|
|
|
const char * strings[] = {
|
|
"%s/%s/strings.txt",
|
|
NULL
|
|
};
|
|
|
|
const char * locales[] = {
|
|
"de", "en",
|
|
NULL
|
|
};
|
|
|
|
static int read_xml(const char * filename, struct xml_stack *stack);
|
|
|
|
static int
|
|
parse_tagbegin(struct xml_stack *stack)
|
|
{
|
|
const xml_tag * tag = stack->tag;
|
|
if (strcmp(tag->name, "include")==0) {
|
|
const char * filename = xml_value(tag, "file");
|
|
if (filename) {
|
|
return read_xml(filename, stack);
|
|
} else {
|
|
log_printf("required tag 'file' missing from include");
|
|
return XML_USERERROR;
|
|
}
|
|
} else if (strcmp(tag->name, "game")==0) {
|
|
const char * welcome = xml_value(tag, "welcome");
|
|
const char * name = xml_value(tag, "name");
|
|
int maxunits = xml_ivalue(tag, "units");
|
|
if (welcome!=NULL) {
|
|
global.welcomepath = strdup(welcome);
|
|
}
|
|
if (name!=NULL) {
|
|
global.gamename = strdup(name);
|
|
}
|
|
if (maxunits!=0) {
|
|
global.maxunits = maxunits;
|
|
}
|
|
} else if (strcmp(tag->name, "order")==0) {
|
|
const char * name = xml_value(tag, "name");
|
|
if (xml_bvalue(tag, "disable")) {
|
|
int k;
|
|
for (k=0;k!=MAXKEYWORDS;++k) {
|
|
if (strncmp(keywords[k], name, strlen(name))==0) {
|
|
global.disabled[k]=1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return XML_OK;
|
|
}
|
|
|
|
static xml_callbacks xml_eressea = {
|
|
parse_tagbegin, NULL, NULL
|
|
};
|
|
|
|
static int
|
|
read_xml(const char * filename, xml_stack * stack)
|
|
{
|
|
char zText[80];
|
|
FILE * F;
|
|
int i;
|
|
sprintf(zText, "%s/%s", resourcepath(), filename);
|
|
F = fopen(zText, "r+");
|
|
if (F==NULL) {
|
|
log_printf("could not open %s: %s\n", zText, strerror(errno));
|
|
return XML_USERERROR;
|
|
}
|
|
|
|
i = xml_read(F, stack);
|
|
fclose(F);
|
|
return i;
|
|
}
|
|
|
|
int
|
|
init_data(const char * filename)
|
|
{
|
|
int l;
|
|
|
|
xml_register(&xml_eressea, "eressea", 0);
|
|
xml_register(&xml_eressea, "eressea include", XML_CB_IGNORE);
|
|
|
|
l = read_xml(filename, NULL);
|
|
if (l) return l;
|
|
|
|
/* old stuff, for removal: */
|
|
for (l=0;locales[l];++l) {
|
|
char zText[MAX_PATH];
|
|
int i;
|
|
for (i=0;strings[i];++i) {
|
|
FILE * F;
|
|
sprintf(zText, strings[i], resourcepath(), locales[l]);
|
|
F = fopen(zText, "r+");
|
|
if (F) {
|
|
read_strings(F);
|
|
fclose(F);
|
|
} else {
|
|
sprintf(buf, "fopen(%s): ", zText);
|
|
perror(buf);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
init_locales(void)
|
|
{
|
|
int l;
|
|
for (l=0;locales[l];++l) {
|
|
const struct locale * lang = find_locale(locales[l]);
|
|
if (lang) init_tokens(lang);
|
|
}
|
|
}
|
|
|
|
/* TODO: soll hier weg */
|
|
extern building_type bt_caldera;
|
|
extern attrib_type at_traveldir_new;
|
|
|
|
attrib_type at_germs = {
|
|
"germs",
|
|
DEFAULT_INIT,
|
|
DEFAULT_FINALIZE,
|
|
DEFAULT_AGE,
|
|
DEFAULT_WRITE,
|
|
DEFAULT_READ,
|
|
ATF_UNIQUE
|
|
};
|
|
|
|
void
|
|
attrib_init(void)
|
|
{
|
|
/* Alle speicherbaren Attribute müssen hier registriert werden */
|
|
at_register(&at_unitdissolve);
|
|
at_register(&at_traveldir_new);
|
|
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_bauernblut);
|
|
at_register(&at_countdown);
|
|
at_register(&at_showitem);
|
|
at_register(&at_curse);
|
|
at_register(&at_cursewall);
|
|
|
|
at_register(&at_seenspell);
|
|
at_register(&at_reportspell);
|
|
at_register(&at_deathcloud);
|
|
|
|
/* neue REGION-Attribute */
|
|
at_register(&at_direction);
|
|
at_register(&at_moveblock);
|
|
#if AT_SALARY
|
|
at_register(&at_salary);
|
|
#endif
|
|
at_register(&at_horseluck);
|
|
at_register(&at_peasantluck);
|
|
at_register(&at_deathcount);
|
|
at_register(&at_chaoscount);
|
|
at_register(&at_woodcount);
|
|
at_register(&at_road);
|
|
|
|
/* neue UNIT-Attribute */
|
|
at_register(&at_alias);
|
|
at_register(&at_siege);
|
|
at_register(&at_target);
|
|
at_register(&at_potion);
|
|
at_register(&at_potionuser);
|
|
at_register(&at_contact);
|
|
at_register(&at_effect);
|
|
at_register(&at_private);
|
|
|
|
at_register(&at_icastle);
|
|
at_register(&at_guard);
|
|
at_register(&at_lighthouse);
|
|
at_register(&at_group);
|
|
at_register(&at_faction_special);
|
|
at_register(&at_prayer_timeout);
|
|
at_register(&at_prayer_effect);
|
|
at_register(&at_wyrm);
|
|
at_register(&at_building_generic_type);
|
|
|
|
/* border-typen */
|
|
register_bordertype(&bt_noway);
|
|
register_bordertype(&bt_fogwall);
|
|
register_bordertype(&bt_wall);
|
|
register_bordertype(&bt_illusionwall);
|
|
register_bordertype(&bt_firewall);
|
|
register_bordertype(&bt_wisps);
|
|
register_bordertype(&bt_road);
|
|
register_bordertype(&bt_questportal);
|
|
|
|
at_register(&at_jihad);
|
|
at_register(&at_skillmod);
|
|
#if GROWING_TREES
|
|
at_register(&at_germs);
|
|
#endif
|
|
at_register(&at_laen); /* required for old datafiles */
|
|
at_register(&at_xontormiaexpress); /* required for old datafiles */
|
|
}
|
|
|
|
void
|
|
kernel_init(void)
|
|
{
|
|
char zBuffer[MAX_PATH];
|
|
skill_init();
|
|
attrib_init();
|
|
translation_init();
|
|
init_messages();
|
|
|
|
if (!turn) turn = lastturn();
|
|
if (turn == 0)
|
|
srand(time((time_t *) NULL));
|
|
else
|
|
srand(turn);
|
|
|
|
sprintf(zBuffer, "%s/patch-%d.sql", datapath(), turn);
|
|
sql_init(zBuffer);
|
|
}
|
|
|
|
/*********************/
|
|
/* at_guard */
|
|
/*********************/
|
|
attrib_type at_guard = {
|
|
"guard",
|
|
DEFAULT_INIT,
|
|
DEFAULT_FINALIZE,
|
|
DEFAULT_AGE,
|
|
DEFAULT_WRITE,
|
|
DEFAULT_READ,
|
|
ATF_UNIQUE
|
|
};
|
|
|
|
void
|
|
setguard(unit * u, unsigned int flags)
|
|
{
|
|
/* setzt die guard-flags der Einheit */
|
|
attrib * a = a_find(u->attribs, &at_guard);
|
|
if(flags == GUARD_NONE) {
|
|
if(a) a_remove(&u->attribs, a);
|
|
return;
|
|
}
|
|
if (!a) a = a_add(&u->attribs, a_new(&at_guard));
|
|
a->data.i = (int)flags;
|
|
}
|
|
|
|
unsigned int
|
|
getguard(const unit * u)
|
|
{
|
|
attrib *a;
|
|
|
|
if(u->region->terrain == T_OCEAN) return GUARD_NONE;
|
|
a = a_find(u->attribs, &at_guard);
|
|
if (a) return (unsigned int)a->data.i;
|
|
return GUARD_NONE;
|
|
}
|
|
|
|
#ifndef HAVE_STRDUP
|
|
char *
|
|
strdup(const char *s)
|
|
{
|
|
return strcpy((char*)malloc(sizeof(char)*(strlen(s)+1)), s);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
remove_empty_factions(boolean writedropouts)
|
|
{
|
|
faction **fp, *f3;
|
|
FILE *dofp = NULL;
|
|
char zText[MAX_PATH];
|
|
sprintf(zText, "%s/dropouts.%d", basepath(), turn);
|
|
|
|
if (writedropouts) dofp = fopen(zText, "w");
|
|
|
|
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->alive == 0 && f->no != MONSTER_FACTION) {
|
|
ursprung * ur = f->ursprung;
|
|
while (ur && ur->id!=0) ur=ur->next;
|
|
if (!quiet) printf("\t%s\n", factionname(f));
|
|
|
|
/* Einfach in eine Datei schreiben und später vermailen */
|
|
|
|
if (dofp) fprintf(dofp, "%s %s %d %d %d\n", f->email, LOC(default_locale, rc_name(f->race, 0)), f->age, ur?ur->x:0, ur?ur->y:0);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
fprintf(sqlstream, "UPDATE subscriptions set status='DEAD' where "
|
|
"faction='%s' and game=%d\n;", itoa36(f->no), GAME_ID);
|
|
|
|
*fp = f->next;
|
|
/* stripfaction(f);
|
|
* free(f);
|
|
* Wir können die nicht löschen, weil sie evtl. noch in attributen
|
|
* referenziert sind ! */
|
|
}
|
|
else fp = &(*fp)->next;
|
|
}
|
|
|
|
if (dofp) fclose(dofp);
|
|
}
|
|
|
|
void
|
|
remove_empty_units_in_region(region *r)
|
|
{
|
|
unit **up = &r->units;
|
|
|
|
while (*up) {
|
|
unit * u = *up;
|
|
#ifdef MAXAGE
|
|
faction * f = u->faction;
|
|
if (f->age > MAXAGE) set_number(u, 0);
|
|
#endif
|
|
if ((u->number <= 0 && u->race != new_race[RC_SPELL])
|
|
|| (u->age <= 0 && u->race == new_race[RC_SPELL])
|
|
|| u->number < 0) {
|
|
destroy_unit(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);
|
|
}
|
|
}
|
|
|
|
int *used_faction_ids = NULL;
|
|
int no_used_faction_ids = 0;
|
|
|
|
static int
|
|
_cmp_int(const void *i1, const void *i2)
|
|
{
|
|
if(i2==NULL)
|
|
return(*(int*)i1);
|
|
return (*(int*)i1 - *(int *)i2);
|
|
}
|
|
|
|
void
|
|
register_faction_id(int id)
|
|
{
|
|
no_used_faction_ids++;
|
|
used_faction_ids = realloc(used_faction_ids, no_used_faction_ids*sizeof(int));
|
|
used_faction_ids[no_used_faction_ids-1] = id;
|
|
if(no_used_faction_ids > 1)
|
|
qsort(used_faction_ids, (size_t)no_used_faction_ids, sizeof(int), _cmp_int);
|
|
}
|
|
|
|
boolean
|
|
faction_id_is_unused(int id)
|
|
{
|
|
#if 0
|
|
if(used_faction_ids==NULL)
|
|
return(true);
|
|
return (boolean)(bsearch(&id, used_faction_ids, no_used_faction_ids,
|
|
sizeof(int), _cmp_int) == NULL);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
int
|
|
weight(const unit * u)
|
|
{
|
|
int w, n = 0, in_bag = 0;
|
|
int faerie_level;
|
|
|
|
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;
|
|
}
|
|
|
|
faerie_level = fspecial(u->faction, FS_FAERIE);
|
|
if (faerie_level) {
|
|
n += (u->number * u->race->weight)/(1+faerie_level);
|
|
} else {
|
|
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
|
|
init_used_faction_ids(void)
|
|
{
|
|
faction *f;
|
|
|
|
no_used_faction_ids = 0;
|
|
for(f = factions; f; f = f->next) {
|
|
register_faction_id(f->no);
|
|
}
|
|
}
|
|
|
|
|
|
unit *
|
|
make_undead_unit(region * r, faction * f, int n, const struct race * rc)
|
|
{
|
|
unit *u;
|
|
|
|
u = createunit(r, f, n, rc);
|
|
set_string(&u->lastorder, "");
|
|
name_unit(u);
|
|
fset(u, UFL_ISNEW);
|
|
return u;
|
|
}
|
|
|
|
void
|
|
guard(unit * u, unsigned int mask)
|
|
{
|
|
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;
|
|
}
|
|
setguard(u, flags & mask);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
int
|
|
lifestyle(const unit * u)
|
|
{
|
|
static plane * astralspace = NULL;
|
|
int need = u->number * u->race->maintenance;
|
|
|
|
if (!astralspace) {
|
|
astralspace = getplanebyname("Astralraum");
|
|
}
|
|
|
|
/* Keinen Unterhalt im Astralraum. */
|
|
if (getplane(u->region) == astralspace)
|
|
return 0;
|
|
|
|
if(u->region->planep && fval(u->region->planep, PFL_NOFEED))
|
|
return 0;
|
|
|
|
if(fspecial(u->faction, FS_REGENERATION))
|
|
need += 1;
|
|
if(fspecial(u->faction, FS_ADMINISTRATOR))
|
|
need += 1;
|
|
if(fspecial(u->faction, FS_WYRM) && old_race(u->race) == RC_WYRM)
|
|
need *= 500;
|
|
|
|
return need;
|
|
}
|
|
|
|
void
|
|
hunger(unit * u, int need)
|
|
{
|
|
region * r = u->region;
|
|
int dead = 0, hpsub = 0;
|
|
int hp = u->hp / u->number;
|
|
int lspp = lifestyle(u)/u->number;
|
|
|
|
if(lspp <= 0) return;
|
|
|
|
while(need > 0) {
|
|
int dam = old_race(u->race)==RC_HALFLING?15+rand()%14:(13+rand()%12);
|
|
if(dam >= hp) {
|
|
++dead;
|
|
} else {
|
|
hpsub += dam;
|
|
}
|
|
need -= lspp;
|
|
}
|
|
|
|
if (dead) {
|
|
/* Gestorbene aus der Einheit nehmen,
|
|
* Sie bekommen keine Beerdingung. */
|
|
add_message(&u->faction->msgs, new_message(u->faction,
|
|
"starvation%u:unit%r:region%i:dead%i:live", u, r, dead, u->number-dead));
|
|
|
|
scale_number(u, u->number - dead);
|
|
deathcounts(r, dead);
|
|
}
|
|
if(hpsub > 0) {
|
|
/* Jetzt die Schäden der nicht gestorbenen abziehen. */
|
|
u->hp -= hpsub;
|
|
/* Meldung nur, wenn noch keine für Tote generiert. */
|
|
if (dead == 0) {
|
|
/* Durch unzureichende Ernährung wird %s geschwächt */
|
|
add_message(&u->faction->msgs, new_message(u->faction,
|
|
"malnourish%u:unit%r:region", u, r));
|
|
}
|
|
}
|
|
if(dead || hpsub) {
|
|
fset(u, FL_HUNGER);
|
|
}
|
|
}
|
|
|
|
void
|
|
plagues(region * r, boolean ismagic)
|
|
{
|
|
double prob;
|
|
int peasants;
|
|
int i;
|
|
int gestorben;
|
|
/* Vermeidung von DivByZero */
|
|
double mwp = max(maxworkingpeasants(r), 1);
|
|
|
|
/* Seuchenwahrscheinlichkeit in % */
|
|
|
|
prob = pow((double) rpeasants(r) /
|
|
(mwp * (((double)wage(r,NULL,false)) / 10.0) * 1.3), 4.0)
|
|
* (double) SEUCHE;
|
|
|
|
if (rand() % 100 >= (int)prob && !ismagic) return;
|
|
|
|
peasants = rpeasants(r);
|
|
for (i = peasants; i != 0; i--) {
|
|
if (rand() % 100 < SEUCHENOPFER) {
|
|
if (rand() % 100 < HEILCHANCE && rmoney(r) >= HEILKOSTEN) {
|
|
rsetmoney(r, rmoney(r) - HEILKOSTEN);
|
|
} else {
|
|
peasants--;
|
|
}
|
|
}
|
|
}
|
|
|
|
gestorben = rpeasants(r) - peasants;
|
|
|
|
if (gestorben > 0) {
|
|
message * msg = add_message(&r->msgs, msg_message("pest", "dead", gestorben));
|
|
msg_release(msg);
|
|
deathcounts(r, gestorben);
|
|
}
|
|
rsetpeasants(r, peasants);
|
|
}
|
|
|
|
/* Lohn bei den einzelnen Burgstufen für Normale Typen, Orks, Bauern,
|
|
* Modifikation für Städter. */
|
|
|
|
#if LARGE_CASTLES
|
|
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 */
|
|
};
|
|
#else
|
|
static const int wagetable[7][4] = {
|
|
{10, 10, 11, -5}, /* Baustelle */
|
|
{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 */
|
|
};
|
|
#endif
|
|
|
|
int
|
|
wage(const region *r, const unit *u, boolean img)
|
|
/* Gibt Arbeitslohn für entsprechende Rasse zurück, oder für
|
|
* die Bauern wenn ra == NORACE. */
|
|
{
|
|
building *b = largestbuilding(r, img);
|
|
int esize = 0;
|
|
curse * c;
|
|
int wage;
|
|
attrib *a;
|
|
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) esize = buildingeffsize(b, img);
|
|
|
|
if (u) {
|
|
wage = wagetable[esize][u->race == new_race[RC_ORC] || u->race == new_race[RC_SNOTLING] || u->race == new_race[RC_URUK]];
|
|
if (fspecial(u->faction, FS_URBAN)) {
|
|
wage += wagetable[esize][3];
|
|
}
|
|
} else {
|
|
if (rterrain(r) == T_OCEAN) {
|
|
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));
|
|
}
|
|
|
|
/* 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 wage;
|
|
}
|
|
|
|
int
|
|
fwage(const region *r, const faction *f, boolean img)
|
|
{
|
|
building *b = largestbuilding(r, img);
|
|
int esize = 0;
|
|
int wage;
|
|
attrib *a;
|
|
curse * c;
|
|
|
|
if (b) esize = buildingeffsize(b, img);
|
|
|
|
if (f) {
|
|
wage = wagetable[esize][old_race(f->race) == RC_ORC];
|
|
if (fspecial(f, FS_URBAN)) {
|
|
wage += wagetable[esize][3];
|
|
}
|
|
} else {
|
|
if (rterrain(r) == T_OCEAN) {
|
|
wage = 11;
|
|
} else if (fval(r, RF_ORCIFIED)) {
|
|
wage = wagetable[esize][1];
|
|
} else {
|
|
wage = wagetable[esize][2];
|
|
}
|
|
wage += curse_geteffect(get_curse(r->attribs, ct_find("blessedharvest")));
|
|
}
|
|
|
|
/* 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 */
|
|
c = get_curse(r->attribs, ct_find("drought"));
|
|
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 wage;
|
|
}
|
|
|
|
|
|
|
|
region *
|
|
findspecialdirection(const region *r, char *token)
|
|
{
|
|
attrib *a;
|
|
spec_direction *d;
|
|
|
|
for (a = a_find(r->attribs, &at_direction);a;a=a->nexttype) {
|
|
d = (spec_direction *)(a->data.v);
|
|
|
|
if(strncasecmp(d->keyword, token, strlen(token)) == 0) {
|
|
return findregion(d->x, d->y);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
region *
|
|
movewhere(region * r, const unit *u)
|
|
{
|
|
direction_t d;
|
|
char *token;
|
|
region * r2;
|
|
|
|
token = getstrtoken();
|
|
|
|
d = finddirection(token, u->faction->locale);
|
|
if (d == D_PAUSE)
|
|
return r;
|
|
|
|
if (d == NODIRECTION)
|
|
return findspecialdirection(r, token);
|
|
|
|
#if 0 /* NOT here! make a temporary attribute for this and move it into travel() */
|
|
if (is_cursed(r->attribs, C_REGCONF, 0)) {
|
|
if (rand()%100 < get_curseeffect(r->attribs, C_REGCONF, 0)) {
|
|
if(u->wants < 0) u->wants--;
|
|
else if(u->wants > 0) u->wants++;
|
|
else u->wants = rand()%2==0?1:-1;
|
|
}
|
|
}
|
|
|
|
if (u->ship && is_cursed(u->ship->attribs, C_DISORIENTATION, 0)) {
|
|
if (rand()%100 < get_curseeffect(r->attribs, C_DISORIENTATION, 0)) {
|
|
if(u->wants < 0) u->wants--;
|
|
else if(u->wants > 0) u->wants++;
|
|
else u->wants = rand()%20?1:-1;
|
|
}
|
|
}
|
|
d = (direction_t)((d + u->wants + MAXDIRECTIONS) % MAXDIRECTIONS);
|
|
#endif
|
|
|
|
if (!rconnect(r, d)) {
|
|
#if USE_CREATION
|
|
makeblock(r->x + delta_x[d], r->y + delta_y[d], 1);
|
|
printf("* Fehler! Region (%d,%d) hatte seine Nachbarn "
|
|
"(%d,%d) noch nicht generiert!\n", r->x, r->y,
|
|
r->x + delta_x[d], r->y + delta_y[d]);
|
|
#else
|
|
add_message(&u->faction->msgs,
|
|
msg_message("moveblocked", "unit direction", u, d));
|
|
return NULL;
|
|
#endif
|
|
}
|
|
r2 = rconnect(r, d);
|
|
|
|
if (!r2) {
|
|
printf("* Fehler! Region (%d,%d) hatte seine Nachbarn "
|
|
"(%d,%d) nicht gefunden!", r->x, r->y,
|
|
r->x + delta_x[d], r->y + delta_y[d]);
|
|
return 0;
|
|
}
|
|
|
|
if (move_blocked(u, r, d) == true) {
|
|
add_message(&u->faction->msgs,
|
|
msg_message("moveblocked", "unit direction", u, d));
|
|
return NULL;
|
|
}
|
|
|
|
/* r2 enthält nun die existierende Zielregion - ihre Nachbarn sollen
|
|
* auch schon alle existieren. Dies erleichtert das Umherschauen bei
|
|
* den Reports! */
|
|
|
|
#if USE_CREATION
|
|
for (d = 0; d != MAXDIRECTIONS; d++)
|
|
if (!rconnect(r2, d))
|
|
makeblock(r2->x + delta_x[d], r2->y + delta_y[d], 1);
|
|
#endif
|
|
|
|
return r2;
|
|
}
|
|
|
|
boolean
|
|
move_blocked(const unit * u, const region *r, direction_t dir)
|
|
{
|
|
region * r2 = NULL;
|
|
border * b;
|
|
if (dir<MAXDIRECTIONS) r2 = rconnect(r, dir);
|
|
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;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
add_income(unit * u, int type, int want, int qty)
|
|
{
|
|
if (want==INT_MAX) want = qty;
|
|
add_message(&u->faction->msgs, new_message(u->faction, "income%u:unit%r:region%i:mode%i:wanted%i:amount",
|
|
u, u->region, type, want, qty));
|
|
}
|
|
|
|
int weeks_per_month;
|
|
int months_per_year;
|
|
|
|
int
|
|
month(int offset)
|
|
{
|
|
int t = turn - FIRST_TURN + offset;
|
|
int year, r, month;
|
|
|
|
if (t<0) t = turn;
|
|
|
|
year = t/(months_per_year * weeks_per_month) + 1;
|
|
r = t - (year-1) * months_per_year * weeks_per_month;
|
|
month = r/weeks_per_month;
|
|
|
|
return month;
|
|
}
|
|
|
|
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, FL_OWNER)) {
|
|
unit * nu = *ubegin;
|
|
insert=ubegin;
|
|
if (nu!=u && nu->building==u->building && fval(nu, FL_OWNER)) {
|
|
log_error(("[reorder_owners] %s hat mehrere Besitzer mit FL_OWNER.\n", buildingname(nu->building)));
|
|
freset(nu, FL_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, FL_OWNER)) {
|
|
log_warning(("[reorder_owners] Einheit %s war Besitzer von nichts.\n", unitname(u)));
|
|
freset(u, FL_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, FL_OWNER)) {
|
|
unit * nu = *ubegin;
|
|
insert = ubegin;
|
|
if (nu!=u && nu->ship==u->ship && fval(nu, FL_OWNER)) {
|
|
log_error(("[reorder_owners] %s hat mehrere Besitzer mit FL_OWNER.\n", shipname(nu->ship)));
|
|
freset(nu, FL_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 (n==0 || !playerrace(u->race)) return 0;
|
|
learn_skill(u, sk, PRODUCEEXP/30.0);
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|