server/src/tools/echeck.c
2001-02-05 21:17:14 +00:00

4672 lines
90 KiB
C

/* vi: set ts=2 ai sw=2:
*
* $Id: echeck.c,v 1.5 2001/02/05 21:17:14 faroul Exp $
*
* Eressea PB(E)M host Copyright (C) 1997-2000
* Enno Rehling (rehling@usa.net)
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@gmx.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 is freeware. It may not be sold or used commercially.
* Please send any changes or bugfixes to the authors.
*
* Eressea PBeM Order Syntax Checker
*/
#ifdef _MSC_VER
# pragma warning (disable: 4711)
#endif
#define MAINVERSION "3"
#define MINVERSION "11"
#define PATCHLEVEL "0"
#ifndef DEFAULT_PATH
# if defined(unix)
# define DEFAULT_PATH "/usr/local/lib/echeck:."
# define PATH_DELIM ":"
# else
# define DEFAULT_PATH NULL
# define PATH_DELIM ";"
# endif
#endif
#define VERSION MAINVERSION"."MINVERSION"."PATCHLEVEL
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#ifdef DMALLOC
# include <dmalloc.h>
#endif
#if macintosh
# include <console.h> /* macintosh console handler zur eingabe von parametern
added 15.6.00 chartus */
# define UMLAUTE
#endif
enum {
UT_NONE,
UT_PARAM,
UT_ITEM,
UT_SKILL,
UT_KEYWORD,
UT_BUILDING,
UT_HERB,
UT_POTION,
UT_RACE,
UT_MAX
};
#if __STDC__
/* strdup ist nicht ANSI */
# if !(defined __USE_SVID) && !(defined __USE_BSD) && !(defined __USE_XOPEN_EXTENDED)
/* ... ist aber unter Linux trotzdem definiert. */
# define strdup(s) (strcpy((char*)malloc(sizeof(char)*strlen(s)+1), s))
# endif
#endif
#ifdef AMIGA
const char version[]="\0$VER: ECheck V"VERSION", "__DATE__"\0";
#endif
#ifdef _MSC_VER
# include <crtdbg.h>
#endif
#ifndef BOOL_DEFINED
# define BOOL_DEFINED
# ifndef bool
# define bool char
# define true ((bool)1)
# define false ((bool)0)
# endif
#endif
#ifdef WIN32
# define strncasecmp _strnicmp
# define strcasecmp _stricmp
#endif
#if defined(AMIGA) || defined(UMLAUTE)
/* ohne brauchbares locale.h wird das nix bei Umlauten */
int
a_isspace(char c) {
return (c==' ' || c=='\t' || c=='\f');
}
char
a_tolower(char c) {
switch (c) {
case 'Ä': return 'ä';
case 'Ö': return 'ö';
case 'Ü': return 'ü';
default: return (char) tolower(c);
}
}
int
a_strncasecmp(const char *a, const char *b, size_t s) {
int i,j;
if (!a || !b || !*a || !*b || strlen(b) < s)
return -1;
do {
i=*a++ & 0xDF; /* 1101 1111 - Groß/Kleinschreibung weglassen */
j=*b++ & 0xDF;
} while (i-j==0 && *a && *b && --s);
return i-j;
}
# define strncasecmp(x,y,n) a_strncasecmp(x,y,n)
# define strcasecmp(x,y) a_strncasecmp(x,y,strlen(x))
# ifdef tolower
# undef tolower
# endif
# define tolower(x) a_tolower(x)
#else
# include <locale.h>
# define a_isspace(c) isspace(c)
#endif
#define SPACE_REPLACEMENT '~'
#define SPACE ' '
#define ESCAPE_CHAR '\\'
#define COMMENT_CHAR ';'
#define MARGIN 78
#define RECRUIT_COST 50
#define INDENT_FACTION 0
#define INDENT_UNIT 2
#define INDENT_ORDERS 4
#define INDENT_NEW_ORDERS 6
#ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
# define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#define BUFSIZE 8192
#define MAXLINE 4096
#define DISPLAYSIZE 4095
#define NAMESIZE 127
FILE *ERR, *OUT=0;
int line_no, /* count line number */
MAXSPELLS,
MAXITEMS;
char echo_it=0, /* option: echo input lines */
no_comment=-3, /* Keine Infos in [] hinter EINHEIT */
show_warnings=4, /* option: print warnings (levels) */
warnings_cl=0, /* -w auf der Kommandozeile gegeben */
warn_off=0, /* ECHECK NOWARN */
use_stderr=0, /* option: use stderr for errors etc */
brief=0, /* option: don't list errors */
ignore_NameMe=0, /* option: ignoriere NameMe-Kommentare ;; */
piping=0, /* option: wird output als pipe-input benutzt? */
empiria=0, /* option: Empiria-Checking */
lohn=10, /* Lohn für Arbeit - je Region zu setzen */
silberpool=0, /* option: Silberpool-Verwaltung */
line_start=0, /* option: Beginn der Zeilenzählung */
noship=0,
noroute=0,
nolost=0,
has_version=0,
at_cmd=0,
attack_warning=0,
compile=0; /* option: compiler-style warnings */
int error_count=0, /* counter: errors */
warning_count=0; /* counter: warnings */
char order_buf[BUFSIZE], /* current order line */
checked_buf[BUFSIZE], /* checked order line */
message_buf[BUFSIZE], /* messages are composed here */
warn_buf[BUFSIZE], /* warnings are composed here */
indent, next_indent, /* indent index */
does_default=0, /* Ist DEFAULT aktiv? */
befehle_ende, /* EOF der Befehlsdatei */
*filename;
int rec_cost=RECRUIT_COST,
this_command,
this_unit, /* wird von getaunit gesetzt */
Rx, Ry; /* Koordinaten der aktuellen Region */
const char *path;
FILE *F;
/* ------------------------------------------------------------- */
enum {
K_WORK,
K_ATTACK,
K_STEAL,
K_BESIEGE,
K_NAME,
K_USE,
K_DISPLAY,
K_ENTER,
K_GUARD,
K_MAIL,
K_END,
K_DRIVE,
K_FOLLOW,
K_RESEARCH,
K_GIVE,
K_ALLY,
K_STATUS,
K_COMBAT,
K_BUY,
K_CONTACT,
K_TEACH,
K_STUDY,
K_LIEFERE,
K_MAKE,
K_MOVE,
K_PASSWORD,
K_RECRUIT,
K_REPORT,
K_OPTION,
K_SPY,
K_SETSTEALTH,
K_TRANSPORT,
K_QUIT,
K_TAX,
K_TURMZAUBER,
K_ENTERTAIN,
K_SELL,
K_LEAVE,
K_CAST,
K_RESHOW,
K_DESTROY,
K_VERGESSE,
K_BIETE,
K_DEFAULT,
K_COMMENT,
K_ROUTE,
K_SABOTIERE,
K_ZUECHTE,
K_URSPRUNG,
K_EMAIL,
K_RESERVE,
K_BANNER,
K_MEINUNG,
K_NUMMER,
K_MAGIEGEBIET,
K_PIRATERIE,
K_NEUSTART,
K_GRUPPE,
K_SORTIERE,
/* Empiria only: */
K_SEND,
K_FIND,
K_ADDRESS,
MAXKEYWORDS
};
static char *keywords[MAXKEYWORDS]={
"Arbeiten",
"Attackieren",
"Beklauen",
"Belagere",
"Benennen",
"Benutzen",
"Beschreiben",
"Betreten",
"Bewachen",
"Botschaft",
"Ende",
"Fahren",
"Folgen",
"Forschen",
"Gib",
"Helfen",
"Kämpfen",
"Kampfzauber",
"Kaufen",
"Kontaktieren",
"Lehren",
"Lernen",
"Liefere",
"Machen",
"Nach",
"Passwort",
"Rekrutieren",
"Report",
"Option",
"Spionieren",
"Tarnen",
"Transportieren",
"Stirb",
"Treiben",
"Turmzauber",
"Unterhalten",
"Verkaufen",
"Verlassen",
"Zaubere",
"Zeigen",
"Zerstören",
"Vergessen",
"Bieten",
"Default",
"//",
"Route",
"Sabotieren",
"Züchten",
"Ursprung",
"Email",
"Reservieren",
"Banner",
"Meinung",
"Nummer",
"Magiegebiet",
"Piraterie",
"Neustart",
"Gruppe",
"Sortiere",
/* Empiria only: */
"Sende",
"Finde",
"Adresse"
};
static char *magiegebiet[]={
"Illaun",
"Tybied",
"Cerddor",
"Gwyrrd",
"Draig"
};
enum {
P_ALLES,
P_PEASANT,
P_CASTLE,
P_GEBAEUDE,
P_UNIT,
P_FLEE,
P_BEHIND,
P_VORNE,
P_CONTROL,
P_HERBS,
P_NOT,
P_NEXT,
P_FACTION,
P_PERSON,
P_REGION,
P_SHIP,
P_SILVER,
P_ROAD,
P_TEMP,
P_PRIVAT,
P_KAEMPFE,
P_OBSERVE,
P_GIVE,
P_GUARD,
P_WARN,
P_SPELLBOOK,
P_STUFE,
P_HORSE,
P_FREMD,
MAXPARAMS
};
static char *parameters[MAXPARAMS]={
"Alles",
"Bauern",
"Burg",
"Gebäude",
"Einheit",
"Fliehe",
"Hinten",
"Vorne",
"Kommando",
"Kräuter",
"Nicht",
"Nächster",
"Partei",
"Personen",
"Region",
"Schiff",
"Silber",
"Straßen",
"Temp",
"Privat",
"Kämpfe",
"Wahrnehmung",
"Gib",
"Bewache",
"Warnung",
"Zauberbuch",
"Stufe", /* Eressea-Zauber */
"Pferde", /* ZÜCHTE PFERDE */
"Fremdes"
};
static char *reports[]={ /* Fehler und Meldungen im Report */
"Kampf",
"Ereignisse",
"Bewegung",
"Einkommen",
"Handel",
"Produktion",
"Orkvermehrung",
"Alles"
};
static const int MAXREPORTS=sizeof(reports)/sizeof(reports[0]);
static char *message_levels[]={
"Wichtig",
"Debug",
"Fehler",
"Warnungen",
"Infos"
};
static const int ML_MAX=sizeof(message_levels)/sizeof(message_levels[0]);
static char *options[]={
"Auswertung",
"Computer",
"Statistik",
"Zipped",
"Merian",
"Zugvorlage", /* ab hier Eressea only */
"Silberpool",
"Materialpool",
"Adressen",
"Bzip2"
};
static const int MAXOPTIONS=sizeof(options)/sizeof(options[0]);
enum {
D_NORTH,
D_SOUTH,
D_EAST,
D_WEST,
D_PAUSE,
D_NORTHEAST,
D_NORTHWEST,
D_SOUTHEAST,
D_SOUTHWEST,
MAXDIRECTIONS
};
static char *directions[MAXDIRECTIONS]={
"Norden",
"Sueden", /* Empiria hat keine Umlaute! */
"Osten",
"Westen",
"Pause",
"Nordosten",
"Nordwesten",
"Südosten",
"Südwesten"
};
static char *Rassen[]={
"Menschen",
"Zwerge",
"Elfen",
"Orks",
"Dämonen",
"Meermenschen",
"Halblinge",
"Trolle",
"Goblins",
"Katzen",
"Insekten"
};
static const int R_MAX=sizeof(Rassen)/sizeof(Rassen[0]);
static char *shiptypes[]={
"Boot",
"Langboot",
"Drachenschiff",
"Karavelle",
"Trireme"
};
static const int MAXSHIPS=sizeof(shiptypes)/sizeof(shiptypes[0]);
enum {
POT_FAST,
POT_STRONG,
POT_TREES,
POT_DOMORE,
POT_HEILWASSER,
POT_BAUERNBLUT,
POT_WISE,
POT_FOOL,
POT_WARMTH,
POT_HORSE,
POT_BERSERK,
POT_PEOPLE,
POT_WAHRHEIT,
POT_MACHT,
POT_HEAL,
MAXPOTIONS
};
enum {
H_PLAIN_1,
H_PLAIN_2,
H_PLAIN_3,
H_FOREST_1,
H_FOREST_2,
H_FOREST_3,
H_SWAMP_1,
H_SWAMP_2,
H_SWAMP_3,
H_DESERT_1,
H_DESERT_2,
H_DESERT_3,
H_HIGHLAND_1,
H_HIGHLAND_2,
H_HIGHLAND_3,
H_MOUNTAIN_1,
H_MOUNTAIN_2,
H_MOUNTAIN_3,
H_GLACIER_1,
H_GLACIER_2,
H_GLACIER_3,
MAXHERBS
};
static char *herbdata[2][MAXHERBS]={
{
"Flachwurz",
"Würziger Wagemut",
"Eulenauge",
"Grüner Spinnerich",
"Blauer Baumringel",
"Elfenlieb",
"Gurgelkraut",
"Knotiger Saugwurz",
"Blasenmorchel",
"Wasserfinder",
"Kakteenschwitz",
"Sandfäule",
"Windbeutel",
"Fjordwuchs",
"Alraune",
"Steinbeißer",
"Spaltwachs",
"Höhlenglimm",
"Eisblume",
"Weißer Wüterich",
"Schneekristall"
},
{
"Flachwurze",
"Würzige Wagemute",
"Eulenaugen",
"Grüne Spinneriche",
"Blaue Baumringel",
"Elfenlieb",
"Gurgelkräuter",
"Knotige Saugwurze",
"Blasenmorcheln",
"Wasserfinder",
"Kakteenschwitze",
"Sandfäulen",
"Windbeutel",
"Fjordwuchse",
"Alraunen",
"Steinbeißer",
"Spaltwachse",
"Höhlenglimme",
"Eisblumen",
"Weiße Wüteriche",
"Schneekristalle"
}
};
typedef struct _names {
char *txt;
struct _names *next;
} t_names;
typedef struct _item {
t_names *name;
struct _item *next;
} t_item;
t_item *itemdata=NULL;
typedef struct t_spell {
char *name;
int kosten;
char typ;
struct t_spell *next;
} t_spell;
t_spell *spells=NULL;
typedef struct _skills {
char *name;
int kosten;
struct _skills *next;
} t_skills;
t_skills *skilldata=NULL;
enum {
I_BALM,
I_SPICES,
I_JEWELERY,
I_MYRRH,
I_OIL,
I_SILK,
I_INCENSE
};
#define isluxury(i) (i<=I_INCENSE)
#define SP_ZAUBER 1
#define SP_KAMPF 2
#define SP_POST 4
#define SP_PRAE 8
#define SP_BATTLE 14 /* 2+4+8 */
#define SP_ALL 15
#define MAXPOTIONS 15
char* potionnames[2][MAXPOTIONS]={
{
/* Stufe 1: */
"Siebenmeilentee",
"Goliathwasser",
"Wasser des Lebens",
"Trank der Wahrheit",
/* Stufe 2: */
"Schaffenstrunk",
"Wundsalbe",
"Bauernblut",
/* Stufe 3: */
"Gehirnschmalz",
"Dumpfbackenbrot",
"Nestwärme",
"Pferdeglück",
"Berserkerblut",
/* Stufe 4: */
"Bauernlieb",
"Elixier der Macht",
"Heiltrank"
}, {
/* Stufe 1: */
"Siebenmeilentees",
"Goliathwasser",
"Wasser des Lebens",
"Tränke der Wahrheit",
/* Stufe 2: */
"Schaffenstrünke",
"Wundsalben",
"Bauernblut",
/* Stufe 3: */
"Gehirnschmalz",
"Dumpfbackenbrote",
"Nestwärme",
"Pferdeglück",
"Berserkerblut",
/* Stufe 4: */
"Bauernlieb",
"Elixiere der Macht",
"Heiltränke"
}
};
static char* buildingtypes[]={
"Burg",
"Leuchtturm",
"Bergwerk",
"Steinbruch",
"Hafen",
"Akademie", /* wird in inittokens() für Empiria rausgefiltert */
"Magierturm",
"Schmiede",
"Sägewerk",
"Pferdezucht",
"Monument",
"Damm",
"Karawanserei",
"Tunnel",
"Taverne",
"Universität", /* wird in inittokens() für Eressea rausgefiltert */
NULL
};
enum {
POSSIBLE,
NECESSARY
};
/* --------------------------------------------------------------------- */
typedef struct list {
struct list *next;
} list;
typedef struct t_region {
struct t_region *next;
int personen, geld, x, y, line_no, reserviert;
char *name;
} t_region;
typedef struct unit {
struct unit *next;
int no;
int people;
int money;
int reserviert;
int unterhalt;
int line_no;
int temp;
int ship; /* Nummer unseres Schiffes; ship<0: ich bin owner */
char lives, hasmoved;
t_region *region;
int newx, newy;
int transport; /* hier steht drin, welche Einheit mich TRANSPORTIEREn wird */
int drive; /* hier steht drin, welche Einheit mich TRANSPORTIEREn soll */
/* wenn drive!=0, muß transport==drive sein, sonst irgendwo Tippfehler */
int start_of_orders_line;
int long_order_line;
char *start_of_orders;
char *long_order;
char *order;
int schueler;
int lehrer;
int lernt;
int spell; /* Bit-Map: 2^Spell-Typ */
} unit;
typedef struct teach {
struct teach *next;
unit *teacher;
unit *student;
} teach;
teach *teachings=NULL;
unit *units=NULL,
*order_unit, /* Die Einheit, die gerade dran ist */
*mother_unit, /* Die Einheit, die MACHE TEMP macht */
*cmd_unit; /* Die Einheit, die gerade angesprochen wird, z.B. mit GIB */
t_region *Regionen=NULL;
typedef struct tnode {
char c;
bool leaf;
int id;
struct tnode * nexthash;
struct tnode * next[32];
} tnode;
tnode tokens[UT_MAX];
/* Prototypes */
unit * find_unit(int i, int t);
unit * newunit(int n, int t);
void *
cmalloc(int n) {
void *p;
if (n==0)
n=1;
p=calloc(n,1);
if (p==NULL) {
puts("\007 * Kein freier Speicher mehr *");
exit(1);
}
return p;
}
int
Pow(int p) {
if (!p)
return 1;
else
return 2<<(p-1);
}
char nulls[]="\0\0\0\0\0\0\0\0";
const char*
itob(int i) {
char *dst;
int b=i;
if (i==0) return "0";
if (empiria) {
static char s[8];
sprintf(s, "%d", i);
return s;
}
dst=nulls+6;
while (b>0) {
int x=b%36;
b/=36; dst--;
if (x<10)
*dst=(char)('0'+x);
else
*dst=(char)('a'+(x-10));
}
return dst;
}
#define scat(X) strcat(checked_buf, X)
#define Scat(X) scat(" ");scat(X)
void
qcat(char *s) {
char *x;
x=strchr(s, ' ');
if (x != NULL && does_default==0)
scat(" \"");
else
scat(" ");
scat(s);
if (x != NULL && does_default==0)
scat("\"");
}
void
icat(int n) {
static char s[10];
sprintf(s, " %d", n);
scat(s);
}
void
bcat(int n) {
if (empiria)
icat(n);
else {
static char s[10];
sprintf(s, " %s", itob(n));
scat(s);
}
}
char *
ItemName(int i, int plural) {
static int ino=0;
static t_item *item=NULL, *it=NULL;
int x;
if (!it) item=it=itemdata;
if (i!=ino) {
x=i;
if (i<ino) it=itemdata;
else i-=ino;
ino=x;
while (0<i-- && it) it=it->next;
if (!it) {
fprintf(ERR,"Fehler: Item %d (%d) nicht gefunden!\n",ino,i);
exit(123);
}
item=it;
}
if (plural)
return item->name->next->txt;
return item->name->txt;
}
FILE *
path_fopen(const char *path, const char *file, const char *mode) {
FILE *f;
char *pathw = strdup(path);
char *token;
char buf[4096];
token = strtok(pathw, PATH_DELIM);
while(token) {
sprintf(buf, "%s/%s", token, file);
f = fopen(buf, mode);
if(f) {
free(pathw);
return f;
}
token = strtok(NULL, PATH_DELIM);
}
free(pathw);
return NULL;
}
void
readspells(void) {
char *s,*x;
t_spell *it, *in;
char *file;
F = path_fopen(path, "zauber.txt", "r");
if (!F) {
fprintf(ERR, "Kann Datei 'zauber.txt' nicht lesen.\n"
"Suchpfad ist '%s'\n", file, path);
return;
}
it=in=spells;
for (;;) {
do {
s=fgets(order_buf, MAXLINE, F);
} while (s && (*s=='#' || *s=='\n')); /* Leer- und Kommentarzeilen überlesen */
if (!s) {
fclose(F);
return;
}
it=cmalloc(sizeof(t_spell));
x=strchr(s,';');
if (!x) x=strchr(s,',');
if (x) *x=0;
else {
x=strchr(s,'\n');
if (x) *x=0;
x=NULL;
}
it->name=strdup(s);
if (x) {
s=(char *)(x+1);
while (a_isspace(*s)) s++;
it->kosten=atoi(s);
x=strchr(s,';');
if (!x) x=strchr(s,',');
if (x) {
s=(char *)(x+1);
it->typ=(char)Pow(atoi(s));
}
}
if (in) in->next=it;
in=it;
if (!spells) spells=in;
} /* Abbruch oben */
}
void
readskills(void) {
char *s,*x;
t_skills *it, *in;
char *file;
F = path_fopen(path, "talente.txt", "r");
if (!F) {
fprintf(ERR, "Kann Datei 'talente.txt' nicht lesen.\n"
"Suchpfad ist '%s'\n", file, path);
return;
}
it=in=skilldata;
for (;;) {
do {
s=fgets(order_buf, MAXLINE, F);
} while (s && (*s=='#' || *s=='\n')); /* Leer- und Kommentarzeilen überlesen */
if (!s) {
fclose(F);
return;
}
it=cmalloc(sizeof(t_skills));
x=strchr(s,';');
if (!x) x=strchr(s,',');
if (x) *x=0;
else {
x=strchr(s,'\n');
if (x) *x=0;
x=NULL;
}
it->name=strdup(s);
if (x) {
s=(char *)(x+1);
while (a_isspace(*s)) s++;
it->kosten=atoi(s);
}
if (in) in->next=it;
in=it;
if (!skilldata) skilldata=in;
} /* Abbruch oben */
}
void
readitems(void) {
char *s,*x;
t_item *it, *in;
t_names *n, *nn;
char *file;
F = path_fopen(path, "items.txt", "r");
if(!F) {
fprintf(ERR, "Kann Datei 'items.txt' nicht lesen.\n"
"Suchpfad ist '%s'\n", file, path);
return;
}
it=in=itemdata;
for (;;) {
do {
s=fgets(order_buf, MAXLINE, F);
} while (s && (*s=='#' || *s=='\n')); /* Leer- und Kommentarzeilen überlesen */
if (!s) {
fclose(F);
return;
}
it=cmalloc(sizeof(t_item));
nn=NULL;
x=s;
do {
n=cmalloc(sizeof(t_names));
x=strchr(s,';');
if (x) *x=0;
else {
x=strchr(s,'\n');
if (x) *x=0;
x=NULL;
}
n->txt=strdup(s);
if (x) {
s=(char *)(x+1);
while (a_isspace(*s)) s++;
}
if (nn) nn->next=n;
nn=n;
if (!it->name) it->name=nn;
} while (x && *s);
if (in) in->next=it;
in=it;
if (!itemdata) itemdata=in;
} /* Abbruch oben */
}
void
porder(void) {
int i;
if (echo_it) {
if (does_default != 2)
for (i=0; i != indent; i++)
putc(' ',OUT);
if (at_cmd)
putc('@',OUT);
at_cmd=0;
if (does_default>0)
fprintf(OUT,"%s%s",checked_buf, does_default==2 ? "\"\n" : "");
else
{
fputs(checked_buf,OUT);
putc('\n',OUT);
}
}
checked_buf[0]=0;
if (next_indent != indent)
indent=next_indent;
}
char *
wrap(char *s) {
int i, j, k;
strcpy(message_buf,s);
/* i zählt die Länge des Strings s ab. j zählt die Länge der Zeile ab.
Ist der Rand erreicht, wird k auf i gesetzt, und rückwärts eine
Leerstelle gesucht, die man mit einem '\n' ersetzen könnte. */
for (i=0, j=25; s[i]; i++, j++) { /* j=25: "Warnung zur Zeile xyz:" */
if (j==MARGIN) {
for (k=i; !a_isspace(s[k]) && k>0; k--);
/* findet man eine Leerstelle, wird sie durch '\n' ersetzt: */
if (k>0) {
message_buf[k]='\n';
j=i-k;
}
} else
/* Hat man das letzte mal keine Leerstelle gefunden, so reagiert man
einfach auf die nächste Leerstelle, ohne nach vorwärts zu suchen
(das hat man ja schon einmal gemacht) */
if (j>MARGIN && a_isspace(s[i])) {
message_buf[i]='\n';
j=0;
}
}
return message_buf;
}
void
Error(char *s, int l, char *o) {
error_count++;
if (!brief) {
if (!compile)
fprintf(ERR, "Fehler in Zeile %d: %s.\n `%s'\n\n", l, wrap(s), o);
else
fprintf(ERR, "%s(%d): Fehler: %s. `%s'\n", filename, l, s, o);
}
}
#define anerror(s) Error(s,line_no,order_buf)
int
btoi(char *s) {
char *x=s;
int i=0;
if (!(*s)) return 0;
while(isalnum(*s)) s++;
if (s) *s=0;
s=x;
if (strlen(s)>(size_t)(4+empiria)) {
sprintf(message_buf,"Zahl '%s' ist zu groß. Stattdessen 1 benutzt",s);
anerror(message_buf);
return 1;
}
#ifdef HAVE_STRTOL
i=(int)(strtol(x, NULL, empiria ? 10 : 36));
#else
if (empiria)
i=atoi(s);
else {
while (isalnum(*s)) {
i*=36;
if (isupper(*s)) i+=(*s)-'A'+10;
else if (islower(*s)) i+=(*s)-'a'+10;
else if (isdigit(*s)) i+=(*s)-'0';
++s;
}
}
#endif
return i;
}
const char *
uid(unit *u) {
static char *bf=NULL;
if (!bf) bf=cmalloc(18);
sprintf(bf,"%s%s", u->temp!=0 ? "TEMP " : "", itob(u->no));
return bf;
}
const char *
Uid(int i) {
static char *bf=NULL;
unit *u;
u=find_unit(i,0);
if (!u) u=find_unit(i,1);
if (!u) {
sprintf(warn_buf,"Konnte Einheit %s nicht ermitteln",itob(i));
Error(warn_buf,line_no,"<interner Check>");
u=newunit(-1,0);
}
if (!bf) bf=cmalloc(18);
sprintf(bf,"%s%s", u->temp!=0 ? "TEMP " : "", itob(u->no));
return bf;
}
void
warn(char *s, int line_no, char level) {
if (warn_off) return;
if (level>show_warnings) return;
warning_count++;
if (show_warnings && !brief) {
if (!compile)
fprintf(ERR, "Warnung zur Zeile %d: %s\n", line_no, wrap(s));
else
fprintf(ERR, "%s(%d): Warnung (%d): %s\n", filename, line_no, level, s);
}
}
void
warning(char *s, int l, char *o, char level) {
if (warn_off) return;
if (level>show_warnings) return;
warning_count++;
if (show_warnings && !brief) {
if (!compile)
fprintf(ERR, "Warnung zur Zeile %d: %s.\n `%s'\n\n", l, wrap(s), o);
else
fprintf(ERR, "%s(%d): Warnung (%d): %s. `%s'\n", filename, l, level, s, o);
}
}
#define awarning(s,level) warning(s,line_no,order_buf,level)
void
checkstring(char *s, size_t l, int type) {
if (l>0 && strlen(s)>l) {
sprintf(warn_buf, "Text zu lang (max. %d)", (int)(l));
awarning(warn_buf,2);
}
if (s[0]==0 && type==NECESSARY)
awarning("Kein Text",1);
strncpy(warn_buf, s, l);
if (echo_it && s[0]) {
if (strlen(s) + strlen(checked_buf) > MARGIN) {
qcat(wrap(s));
} else
qcat(s);
}
}
/* - Speicher und Listen Funktionen ---------------------------- */
#define addlist(l,p) (p->next=*l, *l=p)
int current_temp_no;
/* Enthält die TEMP No, falls eine TEMP Einheit angesprochen wurde. */
int from_temp_unit_no;
/* Enthält die TEMP No, falls Befehle von einer TEMP Einheit gelesen werden. */
#define val(x) (x!=0)
unit *
find_unit(int i, int t) {
unit *u;
for (u=units; u; u=u->next)
if ((i < 0 && u->no < 0) || (u->no==i && val(u->temp)==val(t)))
break;
return u;
}
t_region *
addregion(int x, int y, int pers) {
static t_region *r=NULL;
if (!r || r->x != x || r->y != y) {
for (r=Regionen; r; r=r->next)
if (x==r->x && y==r->y)
break;
}
if (!r) {
r=cmalloc(sizeof(t_region));
r->x=x;
r->y=y;
r->personen=pers;
r->geld=0;
r->reserviert=0;
r->name=strdup("Region");
r->line_no=line_no;
r->next=Regionen;
Regionen=r;
} else {
r->personen+=pers;
}
return r;
}
void
addteach(unit *teacher, unit *student) {
teach *t, **teachlist=&teachings;
for (t=teachings; t; t=t->next) {
if (t->student==student) {
if (!teacher) /* Aufruf durch Schüler, aber Lehrer hat schon Struktur angelegt */
return;
if (t->teacher==teacher) /* kann eigentlich nicht vorkommen, aber egal */
return;
if (t->teacher==NULL) { /* Schüler hat Struct angelegt (mit unbek. Lehrer),
wir tragen jetzt nur noch den Lehrer nach */
t->teacher=teacher;
return;
}
}
}
t=cmalloc(sizeof(teach));
t->next=NULL;
t->teacher=teacher;
t->student=student;
addlist(teachlist, t);
}
unit *
newunit(int n, int t) {
unit *u=find_unit(n,t), **up=&units;
if (!u) {
u=cmalloc(sizeof(unit));
u->no=n;
u->line_no=line_no;
u->order=strdup(order_buf);
u->region=addregion(Rx, Ry, 0);
u->newx=Rx;
u->newy=Ry;
u->temp=t;
addlist(up, u);
}
if (u->temp<0) {
sprintf(warn_buf, "`TEMP %s' wird in Region %d,%d und Region %d,%d "
"(Zeile %d) gebraucht", itob(u->no), Rx, Ry, u->newx, u->newy,
u->start_of_orders_line);
anerror(warn_buf);
u->long_order_line=0;
}
if (u->temp<42)
u->temp=t;
if (t)
current_temp_no=n;
else
current_temp_no=0;
return u;
}
int
atoip(char *s) {
int n;
n=atoi(s);
if (n<0)
n=0;
return n;
}
char *
igetstr(char *s1) {
int i;
static char *s;
static char buf[BUFSIZE];
if (s1)
s=s1;
while (s[0]==SPACE)
s++;
for (i=0; s[0] && s[0] != SPACE && i < sizeof(buf); i++, s++) {
buf[i]=s[0];
if (s[0]==SPACE_REPLACEMENT) {
if (i>0 && buf[i-1]==ESCAPE_CHAR)
buf[--i]=SPACE_REPLACEMENT;
else
buf[i]=SPACE;
}
}
buf[i]=0;
return buf;
}
#define getstr() igetstr(NULL)
#define getb() btoi(igetstr(NULL))
#define geti() atoi(igetstr(NULL))
#define getI() empiria?atoi(igetstr(NULL)):getb()
int
findstr(char **v, const char *s, int max) {
int i;
size_t ss=strlen(s);
if (!s[0])
return -1;
for (i=0; i < max; i++)
if (v[i] && strncasecmp(s, v[i], ss)==0)
return i;
return -1;
}
int
findtoken(const char *str, int type) {
tnode *tk = &tokens[type];
if (*str=='@') {
str++;
at_cmd=1;
} else
at_cmd=0;
while (*str) {
char c=(char)tolower(*str);
int index=((unsigned char)c)%32;
tk=tk->next[index];
while (tk && tk->c!=c) tk=tk->nexthash;
++str;
if (!tk) return -1;
}
if (tk->id>=0) return tk->id;
else return -1;
}
int
findparam(const char *s) {
int p;
p=findtoken(s, UT_PARAM);
if (p != -1)
return p;
p=findtoken(s, UT_BUILDING);
if (p != -1)
return P_GEBAEUDE;
return -1;
}
int
isparam(char *s, int i, char print) {
if (i != findparam(s))
return 0;
if (print) {
Scat(parameters[i]);
}
return 1;
}
t_spell *
findspell(char *s) {
t_spell *sp;
if (!s[0] || !spells)
return NULL;
for (sp=spells; sp; sp=sp->next)
if (sp->name && !strncasecmp(sp->name, s, strlen(s)))
return sp;
return NULL;
}
t_skills *
findskill(char *s) {
int i;
t_skills *sk=skilldata;
i=findtoken(s, UT_SKILL);
if (i<0)
return NULL;
while (i--) sk=sk->next;
return sk;
}
#define findherb(s) findtoken(s, UT_HERB)
#define findpotion(s) findtoken(s, UT_POTION)
#define finditem(s) findtoken(s, UT_ITEM)
#define findbuildingtype(s) findtoken(s, UT_BUILDING)
#define findreport(s) findstr(reports,s,MAXREPORTS)
#define findoption(s) findstr(options,s,MAXOPTIONS)
#define findshiptype(s) findstr(shiptypes,s,MAXSHIPS);
#define getskill() findskill(getstr())
#define getparam() findparam(getstr())
#define igetparam(s) findparam(igetstr(s))
int
finddirection(char *s) {
#define MAXDIRNAMES 13
char* dirs[MAXDIRNAMES]={
"NW",
"NO",
"Nordwesten",
"Nordosten",
"Suedosten",
"Südosten",
"Suedwesten",
"Südwesten",
"SO",
"SW",
"Pause",
"Osten",
"Westen"
/* mit dieser routine kann man mehrere namen für eine direction geben,
* das ist für die hexes ideal. */
};
static const int dir_xref[MAXDIRNAMES]={
D_NORTHWEST,
D_NORTHEAST,
D_NORTHWEST,
D_NORTHEAST,
D_SOUTHEAST,
D_SOUTHEAST,
D_SOUTHWEST,
D_SOUTHWEST,
D_SOUTHEAST,
D_SOUTHWEST,
D_PAUSE,
D_EAST,
D_WEST
};
int uc;
if (!*s)
return -2;
if (empiria)
return findstr(directions, s, 4);
else {
if (strcmp(s,"//")==0) /* "NACH NW NO NO // nach Xontormia" ist erlaubt */
return -2;
uc=findstr(dirs, s, MAXDIRNAMES);
}
if (uc != -1)
return dir_xref[uc];
return uc;
}
#define getdirection() finddirection(getstr())
int
getoption(void) {
int i=findoption(getstr());
if (empiria && i>5) /* Option ist Eressea only */
return -1;
return i;
}
#define findkeyword(s) findtoken(s, UT_KEYWORD)
#define igetkeyword(s) findkeyword(igetstr(s))
char *
getbuf(void) {
char lbuf[MAXLINE];
bool cont = false;
bool quote = false;
bool report = false;
char * cp = warn_buf;
lbuf[MAXLINE-1] = '@';
do {
bool eatwhite = true;
bool start = true;
char *end;
char *bp = fgets(lbuf, MAXLINE, F);
if (!bp) return NULL;
end=bp+strlen(bp);
if (*(end-1)=='\n') {
line_no++;
*(--end)=0;
} else {
/* wenn die Zeile länger als erlaubt war, wird der Rest weggeworfen: */
while (bp && !lbuf[MAXLINE-1] && lbuf[MAXLINE-2]!='\n')
bp=fgets(warn_buf, 1024, F);
sprintf(warn_buf, "%.30s", lbuf);
Error("Zeile zu lang", line_no, warn_buf);
bp=lbuf;
}
cont=false;
while (cp!=warn_buf+MAXLINE && bp!=lbuf+MAXLINE && *bp) {
if (a_isspace(*bp)) {
if (eatwhite) {
do { ++bp; } while (bp!=lbuf+MAXLINE && a_isspace(*bp));
if (!quote && !start) *(cp++)=' ';
} else {
do {
*(cp++)=SPACE_REPLACEMENT;
++bp;
} while (cp!=warn_buf+MAXLINE && bp!=lbuf+MAXLINE && a_isspace(*bp));
}
} else {
cont=false;
if (*bp=='"') {
quote=(bool)!quote;
eatwhite=true;
} else {
if (*bp=='\\') cont=true;
else
#if !defined(AMIGA) && !defined(UMLAUTE)
if (!iscntrl(*bp)) {
#else
if ((unsigned char)(*bp)>32) {
#endif
*(cp++) = *bp;
eatwhite = (bool)!quote;
}
}
++bp;
}
start=false;
}
if (cp==warn_buf+MAXLINE) {
--cp;
if (!report) {
report=true;
sprintf(lbuf, "%.30s", warn_buf);
Error("Zeile zu lang", line_no, lbuf);
}
}
*cp=0;
} while (cont || cp==warn_buf);
if (quote)
Error("Fehlende \"",line_no,lbuf);
return warn_buf;
}
void
get_order(void) {
char *buf;
bool ok=false;
while (!befehle_ende && !ok) {
buf=getbuf();
if (buf) {
if (buf[0]==COMMENT_CHAR && buf[1]==COMMENT_CHAR) {
if (ignore_NameMe) line_no--;
continue;
}
strcpy(order_buf,buf);
ok=true;
} else
befehle_ende=1;
}
}
void
getafaction(char *s) {
int i;
if (empiria) i=atoi(s);
else i=btoi(s);
if (!s[0])
anerror("Partei fehlt");
else {
if (!i)
awarning("Partei 0 verwendet",1);
icat(i);
}
}
int
getmoreunits(bool partei) {
char *s;
int i, count, temp;
unit *u;
count=0;
for (;;) {
s=getstr();
if (!(*s))
break;
if (partei) {
if (empiria)
i=atoi(s);
else
i=btoi(s);
if (i<1) {
sprintf(warn_buf,"Partei '%s' ungültig",s);
anerror(warn_buf);
} else
bcat(i);
} else {
if (findparam(s)==P_TEMP) {
scat(" TEMP");
temp=1;
s=getstr();
} else
temp=0;
i=btoi(s);
if (!i) {
sprintf(warn_buf,"Einheit '%s' geht hier nicht",s);
anerror(warn_buf);
} else {
bcat(i);
if (!does_default) {
u=newunit(i,temp);
addteach(order_unit, u);
}
}
}
count++;
}
return count;
}
int
getaunit(int type) {
char *s, is_temp=0;
int i;
current_temp_no=0;
cmd_unit=NULL;
s=getstr();
switch (findparam(s)) {
case P_PEASANT:
Scat(parameters[P_PEASANT]);
this_unit=0;
return 2;
case P_TEMP:
scat(" TEMP");
s=getstr();
current_temp_no=i=btoi(s);
is_temp=1;
break;
default:
i=btoi(s);
break;
}
if (type==42) { /* Nur Test, ob eine Einheit kommt, weil das ein Fehler ist */
if (i)
return 42;
return 0;
}
this_unit=i;
if (s[0]==0) {
if (type==NECESSARY)
anerror("Einheit fehlt");
return 0;
}
/* wenn in s etwas drinnen steht, aber i==0 ist, gilt es! */
if (s[0] && i==0) {
if (empiria) awarning("Einheit 0 verwendet",2);
/* Bei Empiria kann man sich leichter vertippen; base36 erlaubt mehr */
scat("0");
return 2; /* Einheit 0==Bauern! */
}
cmd_unit=newunit(i,is_temp); /* Die Unit schon machen, wegen TEMP-Check */
bcat(i);
if (is_temp)
return 3;
return 1;
}
void
copy_unit(unit *from, unit *to) {
to->no=from->no;
to->people=from->people;
to->money=from->money;
to->line_no=from->line_no;
to->temp=from->temp;
to->lives=from->lives;
to->start_of_orders_line=from->start_of_orders_line;
to->long_order_line=from->long_order_line;
if (from->start_of_orders_line)
to->start_of_orders=strdup(from->start_of_orders);
if (from->long_order_line)
to->long_order=strdup(from->long_order);
if (from->order)
to->order=strdup(from->order);
to->lernt=from->lernt;
}
char *
netaddress(char *s) {
char *s1, *s2;
if (strncasecmp(s, "internet:", 9)!=0)
return 0;
s1=strchr(s, '@');
if (!s1)
return 0;
while (s1 > s && *s1 && (isalnum(*s1) || *s1=='@' || *s1=='-' ||
*s1=='.' || *s1=='_'))
s1--;
s1++;
s2=s1;
while (*s2 && (isalnum(*s2) || *s2=='@' || *s2=='-' || *s2=='.' || *s2=='_'))
s2++;
*s2=0;
if (s2-s1>DISPLAYSIZE) {
anerror("Adresse zu lang");
return 0;
}
return s1;
}
void
checkemail(void) {
char *s, *addr;
scat(keywords[K_EMAIL]);
addr=getstr();
checkstring(addr, DISPLAYSIZE, NECESSARY);
if (!addr) {
awarning("eMail bitte mit EMAIL setzen",3);
scat("; eMail bitte mit EMAIL setzen!");
return;
}
s=strchr(addr, '@');
if (!s) {
anerror("Ungültige eMail-Adresse");
return;
}
scat("; Zustellung an ");
scat(addr);
}
void
checkaddr(void) {
char *s, *addr;
scat(keywords[K_ADDRESS]);
s=getstr();
checkstring(s, DISPLAYSIZE, NECESSARY);
addr=netaddress(s);
if (!addr) {
strcpy(warn_buf,"Adresse enthält keine eMail mehr, bitte mit EMAIL setzen");
awarning(warn_buf,1);
scat("; eMail bitte mit EMAIL setzen");
} else {
scat("; Zustellung an ");
scat(addr);
}
}
/* Check auf langen Befehl usw. - aus ACheck.c 4.1 */
void
end_unit_orders(void) {
if (!order_unit) /* Für die ersten Befehle der ersten Einheit. */
return;
if (order_unit->lives>0 && !order_unit->long_order_line && order_unit->people>0) {
sprintf(warn_buf, "Einheit %s hat keinen langen Befehl bekommen",
uid(order_unit));
warning(warn_buf, order_unit->start_of_orders_line,
order_unit->start_of_orders,2);
}
}
void
orders_for_unit(int i, unit *u) {
unit *t;
char *k, *j, *e;
int s;
end_unit_orders();
mother_unit=order_unit=u;
if (u->start_of_orders_line) {
sprintf(warn_buf, "Einheit %s hat schon in Zeile %d Befehle bekommen. ",
uid(u), u->start_of_orders_line);
do {
i++; if (i<1) i=1;
u=newunit(i, 0);
} while (u->start_of_orders_line);
strcat(warn_buf,"Benutze stattdessen Einheit ");
strcat(warn_buf, itob(i));
awarning(warn_buf,1);
order_unit=u;
}
u->start_of_orders=strdup(order_buf);
u->start_of_orders_line=line_no;
u->lives=1;
if (no_comment>0)
return;
k=strchr(order_buf,'[');
if (!k) {
awarning("Kann Kommentar zu Personen nicht auswerten",4);
no_comment++;
return;
}
k++;
while (!atoi(k)) { /* Hier ist eine [ im Namen;
0 Personen ist nicht in der Zugvorlage */
k=strchr(k,'[');
if (!k) {
awarning("Kann Kommentar zu Personen nicht auswerten",4);
no_comment++;
return;
}
k++;
}
e=strchr(k,']');
u->people+=atoi(k);
j=strchr(k,',');
if (!j) j=strchr(k,';');
if (!j || j>e) {
awarning("Kann Kommentar zu Silber nicht auswerten",4);
no_comment++;
return;
}
while (!isdigit(j[0])) j++;
u->money+=atoi(j);
j=strchr(k,'U');
if (j && j<e) { /* Muß ein Gebäude unterhalten */
j++;
if (isdigit(j[0]))
u->unterhalt=atoi(j);
}
if (!empiria) {
j=strchr(k,'I');
if (j && j<e) { /* I wie Illusion, auch Untote - eben alles, was nix frißt und
keinen langen Befehl braucht */
u->lives=-1;
}
j=strchr(k,'s');
if (j && j<e) { /* Ist auf einem Schiff */
j++;
if (u->ship>=0) /* hat sonst schon ein Schiffskommando! */
u->ship=btoi(j);
}
j=strchr(k,'S');
if (j && j<e) { /* Ist Kapitän auf einem Schiff */
j++;
s=-btoi(j);
for (t=units; t; t=t->next) /* vielleicht hat schon eine Einheit durch */
if (t->ship==s) { /* BETRETE das Kommando; das ist ja falsch */
t->ship=-s;
break;
}
u->ship=s;
}
}
addregion(Rx, Ry, u->people);
}
void
orders_for_temp_unit(unit *u) {
if (u->start_of_orders_line && u->region==Regionen) {
/* in Regionen steht die aktuelle Region drin */
sprintf (warn_buf, "`TEMP %s' wurde in Zeile %d schon gebraucht",
itob(u->no), u->start_of_orders_line);
anerror (warn_buf);
/* return; Trotzdem kein return, damit die Befehle ordnungsgemäß
zugeordnet werden! */
}
u->line_no=line_no;
u->lives=1;
if (u->order)
free(u->order);
u->order=strdup(order_buf);
if (u->start_of_orders)
free(u->start_of_orders);
u->start_of_orders=strdup(order_buf);
u->start_of_orders_line=line_no;
mother_unit=order_unit;
order_unit=u;
}
void
long_order(void) {
char *s, *q;
int i;
if (order_unit->long_order_line) {
if (!empiria) {
s=strdup(order_unit->long_order);
q=strchr(s,' ');
if (q) *q=0; /* Den Befehl extrahieren */
i=findkeyword(s);
switch (i) {
case K_CAST:
if (this_command==i)
return;
/* ZAUBERE ist zwar kein langer Befehl, aber man darf auch
* keine anderen langen haben - darum ist bei denen long_order() */
case K_SELL:
case K_BUY:
if (this_command==K_SELL || this_command==K_BUY)
return;
/* Es sind mehrere VERKAUFE (und rein theoretisch auch KAUFE)
* pro Einheit möglich */
}
if ((i==K_FOLLOW && this_command!=K_FOLLOW) ||
(i!=K_FOLLOW && this_command==K_FOLLOW))
return; /* FOLGE ist nur Trigger */
}
if (strlen(order_unit->long_order) > DISPLAYSIZE+NAMESIZE)
order_unit->long_order[DISPLAYSIZE+NAMESIZE]=0;
/* zu lange Befehle kappen */
sprintf(warn_buf, "Einheit %s hat schon einen langen Befehl in"
" Zeile %d (`%s')", uid(order_unit),
order_unit->long_order_line, order_unit->long_order);
awarning(warn_buf,1);
} else {
order_unit->long_order=strdup(order_buf);
order_unit->long_order_line=line_no;
}
}
void
checknaming(void) {
int i;
char *s;
scat(keywords[K_NAME]);
i=findparam(getstr());
s=getstr();
if (!empiria && i==P_FREMD) {
if (i==P_REGION) {
sprintf(warn_buf,"BENENNE FREMD nicht mit %s",parameters[i]);
anerror(warn_buf);
} else
Scat(parameters[P_FREMD]);
i=findparam(s);
s=getstr();
}
if (strchr(s,'('))
awarning("Namen dürfen keine Klammern enthalten",1);
switch (i) {
case -1:
anerror("Objekt nicht erkannt");
break;
case P_UNIT:
case P_FACTION:
case P_GEBAEUDE:
case P_CASTLE:
case P_SHIP:
case P_REGION:
Scat(parameters[i]);
checkstring(s, NAMESIZE, NECESSARY);
break;
default:
anerror("Objekt kann nicht umbenannt werden");
}
}
void
checkdisplay(void) {
int i;
scat(keywords[K_DISPLAY]);
i=findparam(getstr());
switch (i) {
case -1:
anerror("Objekt nicht erkannt");
break;
case P_REGION:
case P_UNIT:
case P_GEBAEUDE:
case P_CASTLE:
case P_SHIP:
case P_PRIVAT:
Scat(parameters[i]);
checkstring(getstr(), 0, POSSIBLE);
break;
default:
anerror("Objekt kann nicht beschrieben werden");
}
}
void
check_leave(void) {
unit *t,*T=NULL;
int s;
s=order_unit->ship;
order_unit->ship=0;
if (s<0) { /* wir waren Kapitän, neuen suchen */
for (t=units; t; t=t->next)
if (t->ship==-s) /* eine Unit auf dem selben Schiff */
T=t; /* in ECheck sind die Units down->top, darum */
if (T) /* die letzte der Liste==erste Unit im Programm */
T->ship=s;
}
}
void
checkenter(void) {
int i,n;
unit *u;
scat(keywords[K_ENTER]);
i=findparam(getstr());
switch (i) {
case -1:
anerror("Objekt nicht erkannt");
return;
case P_GEBAEUDE:
case P_CASTLE:
case P_SHIP:
Scat(parameters[i]);
n=getI();
if (!n) {
anerror("Nummer des Objektes fehlt");
return;
} else
bcat(n);
break;
default:
anerror("Objekt kann nicht betreten werden");
return;
}
check_leave();
if (i==P_SHIP) {
order_unit->ship=n;
for (u=units; u; u=u->next) /* ggf. Kommando geben */
if (u->ship==-n) /* da hat einer schon das Kommando */
return;
order_unit->ship=-n;
}
}
int
getaspell(char *s, char spell_typ, unit *u, int turmzauber) {
t_spell *sp;
int p;
if (!empiria) {
if (*s=='[' || *s=='<') {
anerror("[ ] und < > dürfen nicht mit eingegeben werden!");
return 0;
}
if (findparam(s) == P_REGION) {
scat(parameters[P_REGION]);
s=getstr();
if (*s) {
p=atoi(s);
icat(p);
s=getstr();
if (*s) {
p=atoi(s);
icat(p);
} else {
anerror("Fehler bei Regions-Koordinaten");
return 0;
}
} else {
anerror("Fehler bei REGION-Parametern");
return 0;
}
s=getstr();
}
if (findparam(s) == P_STUFE) {
scat(parameters[P_STUFE]);
s=getstr();
if (!*s || atoi(s)<1) {
anerror("Fehler bei STUFE-Parameter");
return 0;
}
p=atoi(s);
icat(p);
s=getstr();
}
}
sp=findspell(s);
if (!sp) {
if (u) { /* sonst ist das der Test von GIB */
if (show_warnings > 0) /* nicht bei -w0 */
anerror("Zauberspruch nicht erkannt");
if (*s>='0' && *s<='9')
anerror("Parameter STUFE oder REGION fehlt");
qcat(s);
}
return 0;
}
qcat(sp->name);
if (!(sp->typ & spell_typ)) {
sprintf(warn_buf, "\"%s\" ist %sein Kampfzauber", sp->name,
(sp->typ & SP_ZAUBER) ? "k" : "");
if (show_warnings > 0) /* nicht bei -w0 */
anerror(warn_buf);
} else {
if (u && (sp->typ & SP_BATTLE) && (u->spell & sp->typ)) {
sprintf(warn_buf, "Einheit %s hat schon einen ",uid(u));
switch (sp->typ) {
case SP_POST:
strcat(warn_buf,"Post-");
break;
case SP_PRAE:
strcat(warn_buf,"Prä-");
break;
}
strcat(warn_buf,"Kampfzauber gesetzt");
if (show_warnings > 0) /* nicht bei -w0 */
awarning(warn_buf,1);
}
if (u) {
p=sp->typ*turmzauber;
u->spell |= p;
u->money-=sp->kosten;
u->reserviert-=sp->kosten;
}
}
do {
s=getstr();
if (*s) Scat(s);
} while (*s); /* restliche Parameter ohne Check ausgeben */
return 1;
}
void
checkgiving(int key) {
char *s;
int i, n;
scat(keywords[key]);
getaunit(NECESSARY);
s=getstr();
if (!getaspell(s, SP_ALL, NULL, 0)
&& !isparam(s, P_CONTROL,1)
&& !isparam(s, P_HERBS,1)
&& !isparam(s, P_SPELLBOOK,1)
&& !isparam(s, P_UNIT,1)) {
n=atoi(s);
if (n<1) {
if (!empiria && findparam(s)==P_ALLES) { /* GIB xx ALLES wasauchimmer */
n=-1;
Scat(parameters[P_ALLES]);
} else {
anerror("Anzahl Gegenstände/Personen/Silber fehlt");
n=1;
}
}
if (n>0) icat(n);
s=getstr();
if (!(*s) && n<0 && !empiria) { /* GIB xx ALLES */
if (cmd_unit) {
n=order_unit->money-order_unit->reserviert;
cmd_unit->money+=n;
cmd_unit->reserviert+=n;
}
order_unit->money=order_unit->reserviert;
return;
}
if (!(*s)) {
anerror("Was übergeben?");
return;
}
i=findparam(s);
switch (i) {
case P_PERSON:
Scat(parameters[i]);
if (n<0) n=order_unit->people;
if (cmd_unit)
cmd_unit->people+=n;
order_unit->people-=n;
if (order_unit->people<0 && no_comment<1 && !does_default) {
sprintf(warn_buf,"Einheit %s hat evtl. zu wenig Personen", uid(order_unit));
awarning(warn_buf,4);
}
break;
case P_SILVER:
Scat(parameters[i]);
if (n<0) n=order_unit->money-order_unit->reserviert;
if (cmd_unit) {
cmd_unit->money+=n;
cmd_unit->reserviert+=n;
}
order_unit->money-=n;
if (order_unit->money<0 && no_comment<1 && !does_default) {
sprintf(warn_buf,"Einheit %s hat evtl. zu wenig Silber",
uid(order_unit));
awarning(warn_buf,4);
}
break;
default:
i=finditem(s);
if (i < 0) {
i=findherb(s);
if (i < 0) {
i=findpotion(s);
if (i >= 0) {
if (piping) {
strcpy(warn_buf,potionnames[n!=1][i]);
s=strchr(warn_buf, ' ');
if (s) *s=0;
Scat(warn_buf);
} else {
qcat(potionnames[n!=1][i]);
}
} else {
awarning("Objekt nicht erkannt",1);
}
} else {
if (piping) {
strcpy(warn_buf,herbdata[n!=1][i]);
s=strchr(warn_buf, ' ');
if (s) *s=0;
Scat(warn_buf);
} else {
qcat(herbdata[n!=1][i]);
}
}
} else {
if (piping) {
strcpy(warn_buf,ItemName(i,n!=1));
s=strchr(warn_buf, ' ');
if (s) *s=0;
Scat(warn_buf);
} else {
qcat(ItemName(i,n!=1));
}
}
break;
}
} else if (findparam(s)==P_CONTROL) {
if (order_unit->ship && !does_default) {
if (order_unit->ship>0) {
sprintf(warn_buf,"Einheit %s hat evtl. nicht das Kommando"
" über Schiff %d",uid(order_unit),order_unit->ship);
awarning(warn_buf,4);
} else if (cmd_unit) {
if (cmd_unit->ship!=0 && abs(cmd_unit->ship) != -order_unit->ship) {
sprintf(warn_buf,"Einheit %s ist evtl. nicht auf Schiff %d sondern"
" auf Schiff %d", uid(cmd_unit), -order_unit->ship, abs(cmd_unit->ship));
awarning(warn_buf,4);
}
cmd_unit->ship=order_unit->ship;
}
order_unit->ship=-order_unit->ship;
} else if (order_unit->unterhalt) {
if (cmd_unit)
cmd_unit->unterhalt=order_unit->unterhalt;
order_unit->unterhalt=0;
}
}
}
void getluxuries(int cmd) {
char *s;
int n, i;
s=getstr();
n=atoi(s);
if (n<1) {
if (!empiria && findparam(s)==P_ALLES) {
if (cmd==K_BUY) {
anerror("KAUFE geht nicht mit ALLES");
return;
} else
scat(" ALLE");
} else {
anerror("Anzahl Luxusgüter fehlt");
}
n=1;
}
icat(n);
s=getstr();
i=finditem(s);
if (!isluxury(i))
anerror("Kein Luxusgut");
else {
Scat(ItemName(i,n!=1));
if (cmd==K_BUY) { /* Silber abziehen; nur Grundpreis! */
switch (i) {
case I_BALM:
i=4; break;
case I_SPICES:
i=5; break;
case I_JEWELERY:
i=7; break;
case I_MYRRH:
i=5; break;
case I_OIL:
i=3; break;
case I_SILK:
i=6; break;
case I_INCENSE:
i=4; break;
}
order_unit->money-=i*n;
order_unit->reserviert-=i*n;
}
}
}
void
checkmake(void) {
int i, j=0, k=0;
char *s;
scat(keywords[K_MAKE]);
s=getstr();
if (s[0]>='0' && s[0]<='9') { /* MACHE anzahl "Gegenstand" */
j=atoi(s);
if (j==0)
awarning("Anzahl 0 macht keinen Sinn",2);
s=getstr();
}
i=findparam(s);
switch (i) {
case P_HERBS:
if (j) {
if (empiria)
anerror("Anzahl hier nicht möglich");
else
icat(j);
}
scat(" Kräuter");
long_order();
return;
case P_TEMP:
if (j)
anerror("Anzahl hier nicht möglich");
next_indent=INDENT_NEW_ORDERS;
scat(" TEMP");
j=getb();
if (!j)
anerror("Keine TEMP-Nummer");
else {
unit *u;
bcat(j);
if (!empiria) {
s=getstr();
if (*s) qcat(s);
}
from_temp_unit_no=j;
u=newunit(j,42);
if (u->ship==0)
u->ship=abs(order_unit->ship);
orders_for_temp_unit(u);
}
return;
case P_SHIP:
if (j>0) icat(j);
k=getI();
Scat(parameters[i]);
if (k) bcat(k);
long_order();
return;
case P_ROAD:
if (j>0) icat(j);
Scat(parameters[i]);
k=getdirection();
if (k<0)
anerror("MACHE STRASSE <richtung>");
else
Scat(directions[k]);
long_order();
return;
}
i=findshiptype(s);
if (i != -1) {
qcat(shiptypes[i]);
long_order();
getI();
return;
}
i=finditem(s);
if (!isluxury (i)) {
if (j)
icat(j);
if (piping) {
strcpy(warn_buf,ItemName(i,1));
s=strchr(warn_buf, ' ');
if (s) *s=0;
Scat(warn_buf);
} else {
qcat(ItemName(i,1));
}
long_order();
return;
}
i=findpotion(s);
if (i != -1) {
if (j)
awarning("Anzahl hier nicht möglich",2);
if (piping) {
strcpy(warn_buf,potionnames[1][i]);
s=strchr(warn_buf, ' ');
if (s) *s=0;
Scat(warn_buf);
} else {
qcat(potionnames[1][i]);
}
long_order();
return;
}
i=findbuildingtype(s);
if (i != -1) {
if (j) icat(j);
qcat(buildingtypes[i]);
long_order();
k=getI();
if (k) bcat(k);
return;
}
/* Man kann MACHE ohne Parameter verwenden, wenn man in einer Burg oder
einem Schiff ist. Ist aber trotzdem eine Warnung wert. */
if (s[0])
anerror("Sowas kann man nicht machen");
else
awarning("Einheit muß in einer Burg, in einem Gebäude"
" oder auf einem Schiff sein",4);
long_order();
/* es kam ja eine Meldung - evtl. kennt ECheck das nur nicht? */
}
void
checkdirections(int key) {
int i, count=0, x, y, sx, sy, rx, ry;
rx=sx=x=Rx; ry=sy=y=Ry;
scat(keywords[key]);
do {
i=getdirection();
if (i<=-1 || (empiria && i >= D_PAUSE) || (!empiria && i < D_EAST)) {
if (i==-2) break;
anerror("Richtung nicht erkannt");
} else {
if (key==K_ROUTE && i==D_PAUSE && count==0)
awarning("ROUTE beginnt mit PAUSE",2);
if (key==K_MOVE && i==D_PAUSE)
anerror("NACH geht nicht mit PAUSE");
else {
Scat(directions[i]);
count++;
if (!noship && order_unit->ship==0 && key!=K_ROUTE && count==4) {
sprintf(warn_buf,"Einheit %s bewegt sich evtl. zu weit",
uid(order_unit));
awarning(warn_buf,4);
}
switch (i) {
case D_NORTHEAST:
y++;
break;
case D_NORTHWEST:
y++; x--;
break;
case D_SOUTHEAST:
y--; x++;
break;
case D_SOUTHWEST:
y--;
break;
case D_EAST:
x++;
break;
case D_WEST:
x--;
break;
case D_PAUSE:
if (rx==sx && ry==sy) {
rx=x; ry=y; /* ROUTE: ersten Abschnitt merken für Silber verschieben */
}
break;
}
}
}
} while (i >= 0);
if (!count)
anerror("Richtung nicht erkannt");
if (key==K_ROUTE && !noroute && (sx!=x || sy!=y)) {
sprintf(warn_buf, "ROUTE ist kein Kreis; (%d,%d) -> (%d,%d)",sx,sy,x,y);
awarning(warn_buf,4);
}
if (!does_default) {
if (order_unit->hasmoved) {
sprintf(warn_buf, "Einheit %s hat sich schon bewegt", uid(order_unit));
anerror(warn_buf);
return;
}
order_unit->hasmoved=2; /* 2: selber bewegt */
if (key==K_ROUTE) {
order_unit->newx=rx;
order_unit->newy=ry;
} else {
order_unit->newx=x;
order_unit->newy=y;
}
}
}
void
check_sabotage(void) {
if (getparam() != P_SHIP) {
anerror("Bislang gibt es nur SABOTIERE SCHIFF");
return;
}
Scat(parameters[P_SHIP]);
return;
}
void
checkmail(void) {
char *s;
scat(keywords[K_MAIL]);
s=getstr();
if (strcasecmp(s, "an")==0)
s=getstr();
switch (findparam(s)) {
case P_FACTION:
Scat(parameters[P_FACTION]);
break;
case P_REGION:
Scat(parameters[P_REGION]);
break;
/* implizit case P_UNIT: */
default:
Scat(parameters[P_UNIT]);
bcat(getb());
return;
}
s=getstr();
checkstring(s, 0, NECESSARY);
}
void
reserve(void) {
char *s;
int i,n;
scat(keywords[K_RESERVE]);
if (from_temp_unit_no) {
anerror("TEMP-Einheiten können nichts reservieren! Bitte GIB benutzen");
return;
}
n=geti();
if (n==0) {
anerror("RESERVIERE 0 xxx macht keinen Sinn");
return;
}
icat(n);
s=getstr();
if (!(*s)) {
anerror("RESERVIERE was?");
return;
}
i=finditem(s);
if (i>=0) {
Scat(ItemName(i,n!=1));
return;
}
if (findparam(s)==P_SILVER) {
Scat("Silber");
order_unit->region->reserviert+=n;
order_unit->reserviert+=n;
return;
}
i=findpotion(s);
if (i>=0) {
Scat(potionnames[n!=1][i]);
return;
}
i=findherb(s);
if (i>=0) {
Scat(herbdata[n=!1][i]);
return;
}
}
void
check_ally(void) {
int i;
char *s;
scat(keywords[K_ALLY]);
getafaction(getstr());
s=getstr();
i=findparam(s);
switch (i) {
case P_GIVE:
case P_KAEMPFE:
case P_SILVER:
case P_OBSERVE:
case P_GUARD:
case P_ALLES:
case P_NOT:
Scat(parameters[i]);
break;
default:
anerror("Helfe-Status falsch");
return;
}
s=getstr();
if (findparam(s)==P_NOT) {
Scat(parameters[P_NOT]);
}
}
int
studycost(t_skills *talent) {
if (does_default)
return 0;
if (talent->kosten<0) {
int i;
/* Lerne Magie [gebiet] 2000 -> Lernkosten=2000 Silber */
char *s=getstr();
i=findstr(magiegebiet,s,5);
if (i>=0) {
fprintf(ERR,"Magiegebiet '%s' gewählt",magiegebiet[i]);
i=geti();
} else
i=atoi(s);
if (i<100) {
i=200;
awarning("Lernkosten mit 200 Silber angenommen",2);
}
return i;
}
return talent->kosten;
}
void
check_meinung(void) {
int i;
scat(keywords[K_MEINUNG]);
i=geti();
if (i <= 0)
anerror("Nummer der Umfrage muß größer 0 sein");
icat(i);
i=geti();
if (i <= 0)
anerror("Nummer der Meinung muß größer 0 sein");
icat(i);
checkstring(getstr(),DISPLAYSIZE,POSSIBLE);
}
void
check_comment(void) {
char *s;
int m;
s=getstr();
if (strncasecmp(s,"ECHECK",6))
return;
s=getstr();
if (strncasecmp(s,"VERSION",7)==0) {
fprintf(ERR, "%s\n", order_buf);
return;
}
if (strncasecmp(s,"NOWARN", 6)==0) { /* Warnungen für nächste Zeile aus */
warn_off=2;
return;
}
if (strncasecmp(s,"LOHN",4)==0) { /* LOHN für Arbeit */
m=geti();
lohn=(char)max(10,m);
return;
}
if (strncasecmp(s,"ROUT",4)==0) { /* ROUTe */
noroute=(char)(1-noroute);
return;
}
if (strncasecmp(s,"KOMM",4)==0) { /* KOMMando */
m=geti();
if (!m) {
m=order_unit->ship;
if (!m)
m=rand();
}
order_unit->ship=-abs(m);
return;
}
if (strncasecmp(s,"EMPTY",5)==0) {
order_unit->money=0;
order_unit->reserviert=0;
return;
}
if (strncasecmp(s,"NACH",4)==0) {
order_unit->hasmoved=1;
order_unit->newx=geti();
order_unit->newy=geti();
return;
}
m=atoi(s);
if (m) {
if (order_unit) {
order_unit->money+=m;
order_unit->reserviert+=m;
}
return;
}
}
void
check_money(bool do_move) { /* do_move=true: vor der Bewegung, anschließend */
unit *u, *t; /* Bewegung ausführen, damit das Silber bewegt wird */
t_region *r;
int i,x,y,um;
u=find_unit(-1,0);
if (u) { /* Unit -1 leeren */
u->people=u->money=u->unterhalt=u->reserviert=0;
u->lives=-1;
}
if (do_move) {
for (u=units; u; u=u->next) { /* Check TEMP und leere Einheiten */
if (u->lives<1) /* fremde Einheit oder Untot/Illusion */
continue; /* auslassen, weil deren Geldbedarf nicht zählt */
if (u->temp && abs(u->temp)!=42) {
sprintf(warn_buf, "Einheit TEMP %s wurde nicht mit "
"MACHE TEMP generiert", itob(u->no));
Error(warn_buf, u->line_no, u->order);
}
if (u->people<0) {
sprintf(warn_buf,"Einheit %s hat %d Personen!", uid(u), u->people);
warn(warn_buf, u->line_no, 3);
}
if (u->people==0 && ((!nolost && !u->temp && u->money>0) || u->temp)) {
if (u->temp) {
if (u->money>0)
sprintf(warn_buf,"Einheit TEMP %s hat nicht rekrutiert und keine Personen"
" bekommen! Sie verliert Silber und evtl. Gegenstände", itob(u->no));
else
sprintf(warn_buf,"Einheit TEMP %s hat nicht rekrutiert und keine Personen"
" bekommen", itob(u->no));
warn(warn_buf,u->line_no,2);
} else if (no_comment<=0) {
sprintf(warn_buf,"Einheit %s verliert Silber und evtl. Gegenstände", uid(u));
warn(warn_buf,u->line_no,3);
}
}
}
}
if (Regionen && no_comment<1) {
if (silberpool && do_move) {
for (u=units; u; u=u->next) {
u->region->geld+=u->money;
/* Reservierung < Silber der Unit? Silber von anderen Units holen */
if (u->reserviert > u->money) {
i=u->reserviert-u->money;
for (t=units; t && i>0; t=t->next) {
if (t->region!=u->region || t==u) continue;
um=min(i,t->money-t->reserviert);
if (um>0) {
u->money+=um;
i-=um;
t->money-=um;
}
}
}
}
}
if (do_move)
for (r=Regionen; r; r=r->next) {
if (r->reserviert>0 && r->reserviert>r->geld) { /* nur explizit mit RESERVIERE */
sprintf(warn_buf, "In %s (%d,%d) wurde mehr Silber reserviert (%d)"
" als vorhanden (%d).", r->name, r->x, r->y, r->reserviert, r->geld);
warn(warn_buf, r->line_no, 3);
}
}
for (u=units; u; u=u->next) { /* fehlendes Silber aus dem Silberpool nehmen */
if (do_move && u->unterhalt) {
u->money-=u->unterhalt;
u->reserviert-=u->unterhalt;
}
if (u->money<0 && silberpool) {
for (t=units; t && u->money<0; t=t->next) {
if (t->region!=u->region || t==u) continue;
um=min(-u->money,t->money-t->reserviert);
if (um>0) {
u->money+=um;
u->reserviert+=um; /* das so erworbene Silber muß auch reserviert sein */
t->money-=um;
}
}
}
if (u->money<0) {
sprintf(warn_buf,"Einheit %s hat %s%d Silber!",
uid(u),do_move?"vor den Einnahmen ":"", u->money);
warn(warn_buf, u->line_no, 3);
if (u->unterhalt) {
if (do_move)
sprintf(warn_buf,"Einheit %s braucht noch %d Silber, "
"um ein Gebäude zu unterhalten.", uid(u), -u->money);
else
sprintf(warn_buf,"Einheit %s kann ein Gebäude nicht erhalten,"
" es fehlen noch %d Silber!", uid(u), -u->money);
warn(warn_buf, u->line_no, 1);
}
}
}
}
if (Regionen)
for (r=Regionen; r; r=r->next)
r->geld=0; /* gleich wird Geld bei den Einheiten bewegt, darum den Pool
* leeren und nach der Bewegung nochmal füllen */
if (!do_move) /* Ebenso wird es in check_living nochmal neu eingezahlt */
return;
if (!Regionen) /* ohne Regionen Bewegung nicht sinnvoll */
return;
for (u=units; u; u=u->next) { /* Bewegen vormerken */
if (u->lives<1) /* fremde Einheit oder Untot/Illusion */
continue;
if (u->hasmoved>1) {
if (!noship && u->ship>0) {
sprintf(warn_buf,"Einheit %s will Schiff %d bewegen und"
" hat evtl. kein Kommando", uid(u), u->ship);
warn(warn_buf,u->line_no,4);
}
i=-u->ship;
if (i>0) { /* wir sind Kapitän; alle Einheiten auf dem Schiff auch bewegen */
x=u->newx; y=u->newy;
for (t=units; t; t=t->next) {
if (t->ship==i) {
if (t->hasmoved>1) { /* schon bewegt! */
sprintf(warn_buf, "Einheit %s auf Schiff %d hat sich schon bewegt",
uid(t),i);
Error(warn_buf,t->line_no,t->long_order);
}
t->hasmoved=1;
t->newx=x;
t->newy=y;
}
}
}
}
}
for (u=units; u; u=u->next) { /* Bewegen ausführen */
if (u->lives<1) /* fremde Einheit oder Untot/Illusion */
continue;
if (u->transport && u->drive && u->drive != u->transport) {
sprintf(checked_buf,"Einheit %s wird von Einheit %s transportiert, "
"fährt aber mit ", uid(u), Uid(u->transport));
scat(Uid(u->drive));
warning(checked_buf,u->line_no,u->long_order,1);
continue;
}
if (u->drive) { /* FAHRE; in u->transport steht die transportierende Einheit */
if (u->hasmoved) {
sprintf(warn_buf,"Einheit %s hat sich bereits bewegt", uid(u));
Error(warn_buf, u->line_no, u->long_order);
}
if (u->transport==0) {
t=find_unit(u->drive,0);
if (!t) t=find_unit(u->drive,1);
if (t && t->lives) {
sprintf(warn_buf,"Einheit %s fährt mit Einheit %s, "
"diese transportiert aber nicht",uid(u),Uid(u->drive));
Error(warn_buf,u->line_no,u->long_order);
} else { /* unbekannte Einheit -> unbekanntes Ziel */
u->hasmoved=1;
u->newx=-9999;
u->newy=-9999;
}
} else {
t=find_unit(u->transport,0);
if (!t) t=find_unit(u->transport,1);
/* muß es geben, hat ja schließlich u->transport gesetzt */
u->hasmoved=1;
u->newx=t->newx;
u->newy=t->newy;
}
} else if (u->transport) {
t=find_unit(u->transport,0);
if (!t) t=find_unit(u->transport,1);
if (t && t->lives && t->drive != u->no) {
sprintf(warn_buf, "Einheit %s transportiert Einheit %s, "
"diese fährt aber nicht",Uid(u->transport),uid(u));
Error(warn_buf,u->line_no,u->long_order);
}
}
if (u->hasmoved) { /* NACH, gefahren oder auf Schiff */
addregion(u->region->x, u->region->y, -(u->people));
u->region=addregion(u->newx, u->newy, u->people);
if (u->region->line_no==line_no) /* line_no => NÄCHSTER */
u->region->line_no=u->line_no;
}
}
}
void
check_living(void) {
unit *u;
t_region *r;
/* Für die Nahrungsversorgung ist auch ohne Silberpool alles Silber der
* Region zuständig */
for (u=units; u; u=u->next) { /* Silber der Einheiten in den Silberpool "einzahlen" */
if (u->lives<1) /* jetzt nach der Umverteilung von Silber */
continue;
u->region->geld+=u->money; /* jetzt wird reserviertes Silber nicht festgehalten */
}
for (r=Regionen; r; r=r->next) {
for (u=units; u; u=u->next)
if (u->region==r && u->lives>0)
r->geld-=u->people*10;
if (r->geld<0) {
sprintf(warn_buf, "Das Silber in %s (%d,%d) reicht evtl. nicht"
" zum Leben; es fehlen %d Silber.", r->name, r->x, r->y, -(r->geld));
warn(warn_buf, r->line_no, 4);
}
}
}
void
remove_temp(void) {
/* Markiert das TEMP-Flag von Einheiten der letzten Region
* -> falsche TEMP-Nummern bei GIB oder so fallen auf */
unit *u;
for (u=units; u; u=u->next)
u->temp=-u->temp;
}
void
check_teachings(void) {
teach *t;
unit *u;
int n;
for (t=teachings; t; t=t->next) {
t->student->schueler=t->student->people;
if (t->teacher) {
t->teacher->lehrer=t->teacher->people*10;
if (t->teacher->lehrer==0) {
sprintf(warn_buf, "Einheit %s hat 0 Personen und lehrt Einheit ",
uid(t->teacher));
strcat(warn_buf, uid(t->student));
strcat(warn_buf, ".");
warn(warn_buf, t->teacher->line_no, 4);
}
if (t->student->schueler==0 && t->student->lives>0) {
sprintf(warn_buf, "Einheit %s hat 0 Personen und wird von Einheit ",
uid(t->student));
strcat(warn_buf, uid(t->teacher));
strcat(warn_buf, " gelehrt.");
warn(warn_buf, t->student->line_no, 4);
}
}
}
for (t=teachings; t; t=t->next) {
if (t->teacher==NULL || t->student->lives<1) { /* lernt ohne Lehrer bzw. */
t->student->schueler=0; /* keine eigene Einheit */
continue;
}
if (!t->student->lernt) {
if (t->student->temp ) /* unbekannte TEMP-Einheit, wird eh schon angemeckert */
continue;
sprintf(warn_buf,"Einheit %s wird von Einheit ", uid(t->student));
strcat(warn_buf,uid(t->teacher)); /* uid() hat ein static char*, das
im sprintf() dann zweimal das selbe ergibt */
strcat(warn_buf," gelehrt, lernt aber nicht.");
warn(warn_buf, t->student->line_no, 2);
t->student->schueler=0;
continue;
}
n=min(t->teacher->lehrer, t->student->schueler);
t->teacher->lehrer-=n;
t->student->schueler-=n;
}
for (u=units; u; u=u->next) {
if (u->lives<1) continue;
if (u->lehrer>0) {
sprintf(warn_buf,"Einheit %s kann noch %d Schüler lehren.",
uid(u), u->lehrer);
warn(warn_buf, u->line_no, 5);
}
if (u->schueler>0) {
sprintf(warn_buf,"Einheit %s kann noch %d Lehrer gebrauchen.",
uid(u), (u->schueler+9)/10);
warn(warn_buf, u->line_no, 5);
}
}
}
void
checkanorder(char *Orders) {
int i, x;
char *s;
t_skills *sk;
unit *u;
s=strchr(Orders,';');
if (s)
*s=0; /* Ggf. Kommentar kappen */
if (Orders[0]==0)
return; /* Dies war eine Kommentarzeile */
strcpy(order_buf, Orders);
i=igetkeyword(order_buf);
this_command=i;
switch (i) {
case -1:
anerror("Befehl nicht erkannt");
return;
case K_ADDRESS:
if (empiria)
checkaddr();
else
anerror("Statt ADRESSE bitte EMAIL und BANNER benutzen");
break;
case K_NUMMER:
if (empiria)
anerror("Befehl nicht erkannt");
else {
scat(keywords[K_NUMMER]);
i=getparam();
if (!(i==P_UNIT || i==P_SHIP || i==P_GEBAEUDE || i==P_CASTLE
|| i==P_FACTION)) {
anerror("NUMMER SCHIFF, NUMMER BURG, NUMMER PARTEI oder NUMMER EINHEIT");
break;
}
Scat(parameters[i]);
s=getstr();
if (strncasecmp(s,"TEMP",strlen(s))==0)
anerror("Nummer nicht erlaubt");
else
Scat(s);
}
break;
case K_MEINUNG:
if (empiria)
anerror("Befehl nicht erkannt");
else
check_meinung();
break;
case K_BANNER:
if (empiria)
anerror("Befehl nicht erkannt");
else {
scat(keywords[K_BANNER]);
checkstring(getstr(),DISPLAYSIZE,NECESSARY);
}
break;
case K_EMAIL:
if (empiria)
anerror("Befehl nicht erkannt");
else
checkemail();
break;
case K_URSPRUNG:
if (empiria)
anerror("Befehl nicht erkannt");
else {
scat(keywords[K_URSPRUNG]);
s=getstr();
if (*s) {
x=atoi(s);
icat(x);
s=getstr();
if (*s) {
x=atoi(s);
icat(x);
} else
anerror("Beide Koordinaten müssen angegeben werden");
}
}
break;
case K_USE:
scat(keywords[K_USE]);
s=getstr();
i=findpotion(s);
if (i<0)
anerror("Unbekannter Trank");
else {
Scat(potionnames[1][i]);
}
break;
case K_MAIL:
checkmail();
break;
case K_WORK:
scat(keywords[K_WORK]);
long_order();
if (!does_default)
order_unit->money+=lohn*order_unit->people;
break;
case K_ATTACK:
scat(keywords[K_ATTACK]);
if (getaunit(NECESSARY)==3)
anerror("TEMP-Einheiten kann man nicht ATTACKIEREn");
if (!attack_warning) {
/* damit längere Angriffe nicht in Warnungs-Tiraden ausarten */
awarning("Längere Kämpfe schließen den langen Befehl aus",5);
attack_warning=1;
}
if (getaunit(42)==42) {
strcpy(warn_buf,"Pro Einheit muß ein ATTACKIERE-Befehl gegeben werden");
anerror(warn_buf);
}
break;
case K_BESIEGE:
scat(keywords[K_BESIEGE]);
i=getI();
if (!i)
anerror("Nummer der Burg fehlt");
else
bcat(i);
long_order();
break;
case K_NAME:
checknaming();
break;
case K_ZUECHTE:
scat(keywords[K_ZUECHTE]);
if (!empiria) {
i=getparam();
if (i==P_HERBS || i==P_HORSE)
scat(parameters[i]);
else
anerror("ZÜCHTE PFERDE oder ZÜCHTE KRÄUTER");
}
long_order();
break;
case K_STEAL:
scat(keywords[K_STEAL]);
getaunit(NECESSARY);
long_order();
break;
case K_DISPLAY:
checkdisplay();
break;
case K_GUARD:
scat(keywords[K_GUARD]);
s=getstr();
if (findparam(s)==P_NOT) {
Scat(parameters[P_NOT]);
}
break;
case K_END:
if (from_temp_unit_no==0)
awarning("ENDE ohne MACHE TEMP",2);
scat(keywords[K_END]);
indent=next_indent=INDENT_ORDERS;
end_unit_orders();
from_temp_unit_no=current_temp_no=0;
if (mother_unit) {
order_unit=mother_unit;
mother_unit=NULL;
}
break;
case K_FIND:
if (!empiria)
awarning("FINDE wurde gegen OPTION ADRESSEN ersetzt",1);
else {
scat(keywords[K_FIND]);
s=getstr();
if (findparam(s)==P_ALLES) {
Scat(parameters[P_ALLES]);
} else
getafaction(s);
}
break;
case K_RESEARCH:
scat(keywords[K_RESEARCH]);
i=getparam();
if (i==P_HERBS) {
scat(parameters[P_HERBS]); /* momentan nur FORSCHE KRÄUTER */
} else
anerror("Es gibt nur FORSCHE KRÄUTER");
long_order();
break;
case K_SETSTEALTH:
scat(keywords[K_SETSTEALTH]);
s=getstr();
i=findstr(Rassen, s, R_MAX);
if (i >= 0) {
Scat(Rassen[i]);
break;
}
i=findparam(s);
if (i==P_FACTION) {
Scat(parameters[i]);
s=getstr();
if (*s) {
if (findparam(s)!=P_NOT)
anerror("Falscher Parameter");
else
Scat(parameters[P_NOT]);
}
break;
}
if (isdigit(s[0])) {
i=atoip(s);
icat(i);
break;
}
awarning("TARNE ohne Parameter",5);
break;
case K_GIVE:
case K_LIEFERE:
if (this_command==K_LIEFERE && !empiria)
{
awarning("LIEFERE ist obsolet, bitte @GIB benutzen",3);
i=K_GIVE;
at_cmd=1;
}
checkgiving(i);
break;
case K_BIETE:
if (empiria) {
scat(keywords[K_BIETE]);
if (getaunit(NECESSARY) > 1 || current_temp_no) {
sprintf(warn_buf,
"%s geht nur mit normalen Einheiten", keywords[K_BIETE]);
anerror(warn_buf);
break;
}
i=geti();
if (i<1)
anerror("Geldgebot fehlt");
icat(i);
long_order();
} else
anerror("Befehl nicht erkannt");
break;
case K_ALLY:
check_ally();
break;
case K_STATUS:
scat(keywords[K_STATUS]);
s=getstr();
i=findparam(s);
switch (i) {
case P_NOT:
case P_BEHIND:
case P_FLEE:
case P_VORNE:
Scat(parameters[i]);
break;
default:
if (*s) {
if (findkeyword(s)==K_ALLY) {
Scat(keywords[K_ALLY]);
s=getstr();
if (findparam(s)==P_NOT) {
Scat(parameters[P_NOT]);
break;
}
}
Scat(s);
anerror("Falscher Kampfstatus");
} else
Scat(parameters[P_VORNE]);
}
break;
case K_COMBAT:
scat(keywords[K_COMBAT]);
s=getstr();
getaspell(s, SP_KAMPF | SP_POST | SP_PRAE, order_unit, 1);
break;
case K_BUY:
case K_SELL:
scat(keywords[i]);
getluxuries(i);
long_order();
break;
case K_CONTACT:
scat(keywords[K_CONTACT]);
getaunit(NECESSARY);
break;
case K_SPY:
scat(keywords[K_SPY]);
i=getb();
if (!i)
anerror("Einheit fehlt");
else
bcat(i);
long_order();
break;
case K_TEACH:
scat(keywords[K_TEACH]);
if (!getmoreunits(false))
anerror("Wen lehren?");
long_order();
break;
case K_VERGESSE:
scat(keywords[K_VERGESSE]);
sk=getskill();
if (!sk)
anerror("Talent nicht erkannt");
else {
Scat(sk->name);
}
break;
case K_STUDY:
scat(keywords[K_STUDY]);
sk=getskill();
if (!sk)
anerror("Talent nicht erkannt");
else {
Scat(sk->name);
if (!empiria && strcasecmp(sk->name,"Magie")==0)
if (order_unit->people>1)
anerror("Magiereinheiten dürfen nur eine Person haben");
}
if (sk && !does_default) {
x=studycost(sk)*order_unit->people;
if (x) {
order_unit->money-=x;
order_unit->reserviert-=x;
}
addteach(NULL, order_unit);
order_unit->lernt=1;
}
long_order();
break;
case K_MAKE:
checkmake();
break;
case K_SABOTIERE:
scat(keywords[K_SABOTIERE]);
check_sabotage();
long_order();
break;
case K_PASSWORD:
scat(keywords[K_PASSWORD]);
s=getstr();
if (!s[0])
awarning("Passwort gelöscht",0);
else
checkstring(s, NAMESIZE, POSSIBLE);
break;
case K_RECRUIT:
scat(keywords[K_RECRUIT]);
i=geti();
if (i) {
icat(i);
if (from_temp_unit_no)
u=newunit(from_temp_unit_no, 1);
else
u=order_unit;
if (does_default)
break;
u->money-=i*rec_cost;
u->reserviert-=i*rec_cost;
u->people+=i;
addregion(Rx, Ry, i);
} else
anerror("Anzahl Rekruten fehlt");
break;
case K_QUIT:
scat(keywords[K_QUIT]);
s=getstr();
if (!s[0])
anerror("Kein Passwort angeben");
else
checkstring(s, NAMESIZE, POSSIBLE);
awarning("Achtung! STIRB gegeben! Die Partei wird aufgelöst!",0);
break;
case K_TAX:
scat(keywords[K_TAX]);
i=(geti()/10)*10;
/* Steuern werden in Kontingenten von 10 Silber eingetrieben. */
if (i)
icat(i);
else
i=20*order_unit->people;
while (*igetstr(NULL));
long_order();
if (!does_default)
order_unit->money+=i;
break;
case K_ENTERTAIN:
scat(keywords[K_ENTERTAIN]);
i=geti();
if (!does_default) {
if (!i)
i=20*order_unit->people;
order_unit->money+=i;
}
long_order();
break;
case K_ENTER:
checkenter();
break;
case K_LEAVE:
scat(keywords[K_LEAVE]);
check_leave();
break;
case K_ROUTE:
if (empiria) {
anerror("Befehl nicht erkannt");
break;
}
case K_MOVE:
checkdirections(i);
long_order();
break;
case K_FOLLOW:
scat(keywords[K_FOLLOW]);
if (empiria) {
getaunit(NECESSARY);
long_order();
} else {
s=getstr();
if (s) {
i=findparam(s);
if (i==P_UNIT) {
Scat(parameters[i]);
getaunit(NECESSARY);
} else if (i==P_SHIP) {
Scat(parameters[i]);
s=getstr();
x=atoi(s);
if (x<0 || x>9999) {
sprintf(warn_buf,"Schiffsnr. %s ungültig",s);
anerror(warn_buf);
} else
Scat(s);
} else
anerror("FOLGE EINHEIT xx, FOLGE SCHIFF xx oder FOLGE");
}
}
break;
case K_REPORT:
if (empiria)
anerror("Befehl nicht erkannt");
else {
scat(keywords[K_REPORT]);
s=getstr();
i=findreport(s);
if (i==-1) {
if (strncasecmp(s, "ZEIGEN", strlen(s)))
anerror("Unbekannte Report-Option");
else
scat(" ZEIGEN");
break;
}
Scat(reports[i]);
s=getstr();
i=findstr(message_levels, s, ML_MAX);
if (i==-1) {
anerror("falscher Ausgabe-Level");
break;
}
Scat(message_levels[i]);
}
break;
case K_SEND:
if (!empiria)
awarning("SENDE wurde in OPTION umbenannt",4);
case K_OPTION:
if (i==K_OPTION && empiria) {
anerror("Befehl nicht erkannt");
break;
} else {
if (empiria)
scat(keywords[K_SEND]);
else
scat(keywords[K_OPTION]);
}
i=getoption();
if (i<0) {
anerror("Option unbekannt");
break;
}
Scat(options[i]);
i=getparam();
if (i==P_WARN) {
Scat(parameters[i]);
i=getparam();
}
if (i==P_NOT) {
Scat(parameters[i]);
}
break;
case K_CAST:
scat(keywords[K_CAST]);
s=getstr();
getaspell(s, SP_ZAUBER, order_unit, 1);
long_order();
break;
case K_TURMZAUBER:
if (empiria) {
scat(keywords[K_TURMZAUBER]);
s=getstr();
getaspell(s, SP_ZAUBER, order_unit, 256);
} else
anerror("Befehl nicht erkannt");
break;
case K_RESHOW:
scat(keywords[K_RESHOW]);
s=getstr();
Scat(s);
break;
case K_DESTROY:
scat(keywords[K_DESTROY]);
break;
case K_DRIVE:
scat(keywords[K_DRIVE]);
if (getaunit(NECESSARY)==2)
anerror("Einheit 0 bzw. Bauern geht hier nicht");
else if (!does_default)
order_unit->drive=this_unit;
long_order();
break;
case K_TRANSPORT:
scat(keywords[K_TRANSPORT]);
getaunit(NECESSARY);
if (!does_default) {
if (cmd_unit) cmd_unit->transport=order_unit->no;
else awarning("Kann zu transportierende Einheit nicht ermitteln",3);
}
if (getaunit(42)==42)
anerror("Pro Einheit muß ein TRANSPORTIERE-Befehl gegeben werden");
break;
case K_PIRATERIE:
if (empiria)
anerror("Befehl nicht erkannt");
else
getmoreunits(true);
break;
case K_MAGIEGEBIET:
if (empiria)
anerror("Befehl nicht erkannt");
else {
s=getstr();
i=findstr(magiegebiet,s,5);
if (i<0) {
sprintf(warn_buf,"Magiegebiet '%s' gibt es nicht",s);
anerror(warn_buf);
} else {
scat(keywords[K_MAGIEGEBIET]);
Scat(magiegebiet[i]);
}
}
break;
case K_DEFAULT:
if (empiria)
anerror("Befehl nicht erkannt");
else {
scat(keywords[K_DEFAULT]);
scat(" \"");
u=newunit(-1,0);
copy_unit(order_unit, u);
if (order_unit->start_of_orders)
free(order_unit->start_of_orders);
if (order_unit->long_order)
free(order_unit->long_order);
if (order_unit->order)
free(order_unit->order);
order_unit->long_order_line=0;
order_unit->start_of_orders_line=0;
order_unit->temp=0;
/* der DEFAULT gilt ja erst nächste Runde! */
s=getstr();
does_default=1;
porder();
checkanorder(s);
does_default=2;
while (getstr()[0]);
copy_unit(u, order_unit);
u->no=-1;
u->long_order_line=0;
u->start_of_orders_line=0;
u->temp=0;
}
break;
case K_COMMENT:
if (empiria)
anerror("Befehl nicht erkannt");
else {
check_comment();
scat(Orders);
}
break;
case K_RESERVE:
if (empiria)
anerror("Befehl nicht erkannt");
else
reserve();
break;
case K_NEUSTART:
if (empiria)
anerror("Befehl nicht erkannt");
else {
i=findstr(Rassen, getstr(), R_MAX);
if (i<0) {
anerror("Unbekannte Rasse");
break;
} else
Scat(Rassen[i]);
s=getstr();
if (!*s) {
anerror("Passwort fehlt");
break;
} else
qcat(s);
awarning("NEUSTART befohlen!",0);
}
break;
case K_GRUPPE:
if (empiria)
anerror("Befehl nicht erkannt");
else {
s=getstr();
if (*s)
Scat(s);
}
break;
case K_SORTIERE:
if (empiria)
anerror("Befehl nicht erkannt");
else {
s=getstr();
if (*s) {
if (strncasecmp(s, "VOR", strlen(s))==0 ||
strncasecmp(s, "HINTER", strlen(s))==0) {
Scat(s);
i=getaunit(NECESSARY);
if (i==1 || i==3) /* normale oder TEMP-Einheit: ok */
break;
}
}
anerror("SORTIERE VOR oder HINTER <einheit>");
}
break;
default:
anerror("Befehl nicht erkannt");
}
if (does_default != 1) {
porder();
does_default=0;
}
}
void
readaunit(void) {
int i;
unit *u;
i=getb();
if (i==0) {
anerror("Keine Einheitsnummer");
get_order();
return;
}
u=newunit(i, 0);
u->line_no=line_no;
/* Sonst ist die Zeilennummer die Zeile, wo die Einheit zuerst von
* einer anderen Einheit angesprochen wurde... */
orders_for_unit(i,u);
bcat(i);
indent=INDENT_UNIT;
next_indent=INDENT_ORDERS;
porder();
from_temp_unit_no=0;
for (;;) {
get_order();
if (befehle_ende)
return;
/* Erst wenn wir sicher sind, daß kein Befehl eingegeben wurde, checken
* wir, ob nun eine neue Einheit oder ein neuer Spieler drankommt */
if (igetkeyword(order_buf)==-1) {
if (order_buf[0]==';') {
check_comment();
continue;
}
else switch (igetparam(order_buf)) {
case P_UNIT:
case P_FACTION:
case P_NEXT:
case P_REGION:
if (from_temp_unit_no != 0) {
sprintf(warn_buf,"TEMP-Einheit %s wird nicht durch ENDE abgeschlossen",
itob(from_temp_unit_no));
awarning(warn_buf, 2);
from_temp_unit_no=0;
}
return;
}
}
if (order_buf[0])
checkanorder(order_buf);
}
}
int
readafaction(void) {
int i;
char *s;
if (line_start==1)
line_no=1;
i=getI();
if (i) {
bcat(i);
s=getstr();
if (s[0]==0) {
anerror("Kein Passwort");
} else if (strcmp(s, "hier_passwort_eintragen") == 0) {
anerror("Nicht das korrekte Passwort eingesetzt");
}
qcat(s);
} else
anerror("Keine Parteinummer");
indent=next_indent=INDENT_FACTION;
porder();
return i;
}
void
help(const char *s) {
fprintf(ERR, "ECheck (Version "VERSION", "__DATE__
"), Zug-Checker für Eressea - Freeware!\n\n"
" Benutzung: %s [Optionen] Befehlsdatei\n"
/***** Vorsicht, TABs! Neue Texte mit Tab-Width=8 rein *****/
" - Verwendet stdin anstelle einer Eingabedatei.\n"
" -b unterdrückt Warnungen und Fehler (brief)\n"
" -q erwartet keine Angaben zu Personen/Silber in [] bei EINHEIT\n"
" -rnnn Legt Rekrutierungskosten auf nnn Silber fest\n"
" -c schreibt die Warnungen und Fehler in einer Compiler-ähnlichen Form\n"
" -e schreibt die geprüfte Datei auf stdout, Fehler nach stderr\n"
" -E schreibt die geprüfte Datei auf stdout, Fehler nach stdout\n"
" -ofile schreibt die geprüfte Datei in die Datei 'file'\n"
" -Ofile schreibt Fehler in die Datei 'file'\n"
" -h zeigt diese kleine Hilfe an\n"
" -s verwendet stderr für Warnungen, Fehler etc., nicht stdout\n"
" -p verkürzt einige Ausgaben für piping\n"
" -l simuliert Silberpool-Funktion (nicht bei Empiria)\n"
" -n zählt NameMe-Kommentare (;;) nicht als Zeile\n"
" -m Empiria-Checking\n"
" -noxxx Keine xxx-Warnungen. xxx kann sein:\n"
" ship Einheit steuert Schiff und hat evtl. kein Kommando\n"
" route kein Check auf zyklisches ROUTE\n"
" lost Einheit verliert Silber und Gegenstände\n"
" -w[n] Warnungen der Stufe n (default: 4=alle Warnungen)\n"
" -x Zeilenzählung ab PARTEI statt Dateianfang\n"
" -Ppfad Pfadangabe für items.txt, talente.txt und zauber.txt\n"
" -vm.l Mainversion.Level - für Test, ob richtige ECheck-Version\n"
, s);
}
void
check_options(int argc, char *argv[], char dostop, char command_line) {
int i;
char *x;
for (i=1; i != argc; i++) {
if (argv[i][0]=='-'
#ifdef WIN32
|| argv[i][0]=='/'
#endif
) {
switch (argv[i][1]) {
case 'P':
if (dostop && strlen(argv[i])>2) {
/* bei Optionen via "; ECHECK" nicht mehr machen */
if (argv[i][2]==0) { /* -P path */
i++;
if (argv[i])
path=strdup((char *)(argv[i]+2));
else {
fputs("Leere Pfad-Angabe ungültig\n",stderr);
exit(0);
}
} else
path=strdup((char *)(argv[i]+2));
}
break;
case 'v':
if (argv[i][2]==0) { /* -v version */
i++;
if (!argv[i])
break;
}
has_version=1;
x=strchr(argv[i],'.');
if (x) {
*x=0;
if (strcmp(MAINVERSION,(char *)(argv[i]+2))==0) {
*x='.';
x++;
if (show_warnings>1 && strcmp(MINVERSION,x)!=0)
fprintf(stderr,"Warnung, falsche ECheck-Version: %s\n",argv[i]+2);
break;
}
}
break;
case 'b':
brief=1;
break;
case 'l':
silberpool=1;
break;
case 'm':
empiria=1;
break;
case 'q':
no_comment=1;
noship=1; nolost=1; noroute=1;
break;
case 'r':
if (argv[i][2]==0) { /* -r nnn */
i++;
if (argv[i])
rec_cost=atoi(argv[i]);
else
fprintf(stderr,"Fehlende Rekrutierungskosten, auf %d gesetzt",rec_cost);
} else
rec_cost=atoi(argv[i]+2);
break;
case 'c':
compile=1;
break;
case 'E':
if (dostop) { /* bei Optionen via "; ECHECK" nicht mehr machen */
echo_it=1;
OUT=stdout;
ERR=stdout;
}
break;
case 'e':
if (dostop) { /* bei Optionen via "; ECHECK" nicht mehr machen */
echo_it=1;
OUT=stdout;
ERR=stderr;
}
break;
case 'O':
if (dostop) { /* bei Optionen via "; ECHECK" nicht mehr machen */
if (argv[i][2]==0) { /* "-o file" */
i++;
x=argv[i];
} else /* "-ofile" */
x=argv[i]+2;
if (!x) {
fputs("Keine Datei für Fehler-Texte, stderr benutzt\n",stderr);
ERR=stderr;
break;
}
ERR=fopen(x,"w");
if (!ERR) {
fprintf(stderr,"Kann Datei '%s' nicht schreiben:\n %s",
x,strerror(errno));
exit(0);
}
}
break;
case 'o':
if (dostop) { /* bei Optionen via "; ECHECK" nicht mehr machen */
if (argv[i][2]==0) { /* "-o file" */
i++;
x=argv[i];
} else /* "-ofile" */
x=argv[i]+2;
echo_it=1;
if (!x) {
fputs("Leere Datei für geprüfte Datei, stdout benutzt\n",stderr);
OUT=stdout;
break;
}
OUT=fopen(x,"w");
if (!OUT) {
fprintf(stderr,"Kann Datei '%s' nicht schreiben:\n %s",
x,strerror(errno));
exit(0);
}
}
break;
case 'p':
piping=1;
break;
case 'x':
line_start=1;
break;
case 'w':
if (command_line == 1 || warnings_cl == 0) {
if (argv[i][2])
show_warnings=(char)atoi(argv[i]+2);
else {
if (argv[i+1] && isdigit(*argv[i+1])) {
i++;
show_warnings=atoi(argv[i]);
} else
show_warnings=0;
}
}
if (command_line==1)
warnings_cl=1;
break;
case 's':
if (dostop) /* bei Optionen via "; ECHECK" nicht mehr machen */
ERR=stderr;
break;
case 'n':
if (strlen(argv[i])>2) {
if (argv[i][3]==0) { /* -no xxx */
i++;
x=argv[i];
} else
x=argv[i]+3;
if (!x) {
fputs("-no ???\n",stderr);
break;
}
switch (*x) {
case 's':
noship=1;
break;
case 'r':
noroute=1;
break;
case 'l':
nolost=1;
break;
}
} else
ignore_NameMe=1;
break;
case '?':
case 'h':
if (dostop) { /* bei Optionen via "; ECHECK" nicht mehr machen */
help(argv[0]);
exit(1);
}
break;
default:
if (argv[i][1]) {
fprintf(ERR, "Option '%s' unbekannt.\n", argv[i]);
if (dostop) /* Nicht stoppen, wenn dies die Parameter aus der
Datei selbst sind! */
exit(10);
}
}
}
}
if (empiria && silberpool) {
fprintf(ERR,"Empiria hat keinen Silberpool\n");
silberpool=0;
}
}
void
parse_options(char *p, char dostop) {
char *argv[10], **ap=argv, *vl, argc=0;
vl=strtok(p, " \t,");
do {
*ap++=vl;
argc++;
} while ((vl=strtok(NULL, " \t,"))!=NULL);
*ap=0;
check_options(argc, argv, dostop, 0);
}
void
check_OPTION(void) {
get_order();
if (befehle_ende) return;
if (strncmp(order_buf, "From ", 5)==0) { /* es ist eine Mail */
do { /* Bis zur Leerzeile suchen -> Mailheader zu Ende */
fgets(order_buf, BUFSIZE, F);
} while (order_buf[0] != '\n' && !feof(F));
if (feof(F)) {
befehle_ende=1;
return;
}
get_order();
}
if (befehle_ende) return;
if (order_buf[0]==COMMENT_CHAR)
do {
if (strlen(order_buf)>9) {
if (strncasecmp(order_buf, "; OPTION", 8)==0 ||
strncasecmp(order_buf, "; ECHECK", 8)==0) {
parse_options((char *)(order_buf+2),0);
/* "; " überspringen; zeigt dann auf "OPTION" */
} else if (strncasecmp(order_buf, "; VERSION", 9)==0)
fprintf(ERR, "%s\n", order_buf);
}
get_order();
if (befehle_ende) return;
} while (order_buf[0]==COMMENT_CHAR);
}
void
process_order_file(int *faction_count, int *unit_count) {
int f=0, next=0;
t_region *r;
unit *u;
char *x;
line_no=befehle_ende=0;
check_OPTION();
if (befehle_ende) /* dies war wohl eine Datei ohne Befehle */
return;
Rx=Ry=-10000;
/* Auffinden der ersten Partei, und danach abarbeiten bis zur letzten Partei. */
while (!befehle_ende) {
switch (igetparam(order_buf)) {
case P_REGION:
if (Regionen)
remove_temp();
attack_warning=0;
if (echo_it)
{
fputs(order_buf,OUT);
putc('\n',OUT);
}
x=getstr();
if (*x) {
Rx=atoi(x);
x=strchr(order_buf, ',');
if (!x) {
x=strchr(order_buf, ' ');
if (x) x=strchr(++x, ' '); /* 2. Space ist Trenner? */
else x=getstr();
}
if (x && *x)
Ry=atoi(++x);
else
Ry=-10000;
} else
Rx=-10000;
if (Rx<-9999 || Ry<-9999)
awarning("REGION fehlerhaft",0);
r=addregion(Rx, Ry, 0);
r->line_no=line_no;
x=strchr(order_buf, ';');
if (x) {
x++;
while (a_isspace(*x)) x++;
if(r->name) free(r->name);
r->name=strdup(x);
x=strchr(r->name,'\n');
if (x)
*x=0;
} else {
if(r->name) free(r->name);
r->name=strdup("");
}
get_order();
break;
case P_FACTION:
if (f && !next)
awarning("NÄCHSTER fehlt",0);
scat(parameters[P_FACTION]);
befehle_ende=0;
f=readafaction();
fprintf(ERR, "Befehle für Partei %s gefunden.\n", itob(f));
check_OPTION(); /* Nach PARTEI auf "; OPTION" bzw. "; ECHECK" testen */
if (befehle_ende) return;
fprintf(ERR, "Rekrutierungskosten auf %d Silber gesetzt, "
"Warning Level %d.\n", rec_cost, show_warnings);
if (silberpool)
fputs("Silberpool", ERR);
else if (empiria) /* Empipria hat keinen Silberpool */
fputs("Empiria-Modus", ERR);
if (empiria || silberpool)
fputs(" aktiviert.", ERR);
fputs("\n\n", ERR);
if (!has_version)
fputs("Hinweis: es wurde keine ECheck-Version angegeben (-v"
MAINVERSION"."MINVERSION")\n",ERR);
(*faction_count)++;
next=0;
break;
case P_UNIT:
if (f) {
scat(parameters[P_UNIT]);
readaunit();
(*unit_count)++;
} else
get_order();
break;
/* Falls in readunit abgebrochen wird, steht dort entweder eine neue
Partei, eine neue Einheit oder das File-Ende. Das switch wird erneut
durchlaufen, und die entsprechende Funktion aufgerufen. Man darf
order_buf auf alle Fälle nicht überschreiben! Bei allen anderen
Einträgen hier muß order_buf erneut gefüllt werden, da die
betreffende Information in nur einer Zeile steht, und nun die
nächste gelesen werden muß. */
case P_NEXT:
f=0;
scat(parameters[P_NEXT]);
indent=next_indent=INDENT_FACTION;
porder();
next=1;
check_money(true); /* Check für Lerngeld, Handel usw.;
* true: dann Bewegung ausführen */
if (Regionen) {
check_money(false); /* Silber nochmal in den Pool, fehlendes aus Pool */
check_living(); /* Ernährung mit allem Silber der Region */
}
check_teachings();
while (Regionen) {
r=Regionen->next;
if (Regionen->name)
free(Regionen->name);
free(Regionen);
Regionen=r;
}
while (units) {
u=units->next;
if (units->start_of_orders)
free(units->start_of_orders);
if (units->long_order)
free(units->long_order);
if (units->order)
free(units->order);
free(units);
units=u;
}
Regionen=(t_region *)NULL;
units=(unit *)NULL;
default:
if (order_buf[0]==';') {
check_comment();
} else {
if (f && order_buf[0])
awarning("Wird von keiner Einheit ausgeführt",1);
}
get_order();
}
} /* end while !befehle_ende */
if (igetparam(order_buf)==P_NEXT) /* diese Zeile wurde ggf. gelesen und dann kam */
next=1; /* EOF -> kein Check mehr, next=0... */
if (f && !next)
anerror("NÄCHSTER fehlt");
}
void
addtoken(tnode *root, const char *str, int id) {
static char buf[1024];
static struct replace {
char c;
char *str;
} replace[] = {
{'ä', "ae"},
{'Ä', "ae"},
{'ö', "oe"},
{'Ö', "oe"},
{'ü', "ue"},
{'Ü', "ue"},
{'ß', "ss"},
{ 0, 0}
};
if (root->id>=0 && root->id!=id && !root->leaf) root->id=-1;
if (!*str) {
root->id = id;
root->leaf=1;
} else {
char c = (char)tolower(*str);
int index = ((unsigned char)c) % 32;
int i=0;
tnode * tk = root->next[index];
if (root->id<0) root->id = id;
while (tk && tk->c != c) tk = tk->nexthash;
if (!tk) {
tk = calloc(1, sizeof(tnode));
tk->id = -1;
tk->c = c;
tk->nexthash=root->next[index];
root->next[index] = tk;
}
addtoken(tk, str+1, id);
while (replace[i].str) {
if (*str==replace[i].c) {
strcat(strcpy(buf, replace[i].str), str+1);
addtoken(root, buf, id);
break;
}
++i;
}
}
}
void
inittokens(void) {
int i, k;
t_item *it;
t_names *n;
t_skills *s;
for (i=0,it=itemdata; it; it=it->next,++i)
for (n=it->name; n; n=n->next)
addtoken(&tokens[UT_ITEM], n->txt, i);
for (i=0; i!=MAXPARAMS; ++i)
addtoken(&tokens[UT_PARAM], parameters[i], i);
for (i=0, s=skilldata; s; s=s->next,++i)
addtoken(&tokens[UT_SKILL], s->name, i);
for (i=0; i!=MAXKEYWORDS; ++i)
addtoken(&tokens[UT_KEYWORD], keywords[i], i);
k=0;
for (i=0; buildingtypes[i]; ++i) {
if (!empiria && strcmp(buildingtypes[i],"Universität")==0) {
k++;
continue;
}
if (empiria && strcmp(buildingtypes[i],"Akademie")==0) {
k++;
continue;
}
addtoken(&tokens[UT_BUILDING], buildingtypes[i], i-k);
}
for (i=0; i!=MAXHERBS; ++i)
for (k=0; k!=2; ++k)
addtoken(&tokens[UT_HERB], herbdata[k][i], i);
for (i=0; i!=MAXPOTIONS; ++i)
for (k=0;k!=2;++k)
addtoken(&tokens[UT_POTION], potionnames[k][i], i);
}
int
main(int argc, char *argv[]) {
int i, faction_count=0, unit_count=0;
#if !defined(AMIGA) && !defined(UMLAUTE)
setlocale(LC_CTYPE, "");
#endif
#if macintosh
argc=ccommand(&argv); /* consolenabruf der parameter fuer macintosh
added 15.6.00 chartus*/
#endif
/* Path-Handling */
path = getenv("ECHECKPATH");
if(path == NULL) {
path = DEFAULT_PATH;
}
ERR=stdout;
if (argc <= 1) {
help(argv[0]);
return 0;
}
if (argc>1)
check_options(argc, argv, 1, 1);
fprintf(ERR, "ECheck (Version "VERSION
", "__DATE__"), Zug-Checker für Eressea - Freeware!\n\n");
filename=getenv("ECHECKOPTS");
if (filename) parse_options(filename,1);
readitems();
readspells();
readskills();
if (!skilldata || !spells) {
fputs("Die Dateien 'talente.txt' und 'items.txt' müssen vorhanden sein\n",ERR);
return 5;
}
inittokens();
F=stdin;
for (i=1; i<argc; i++)
if (argv[i][0] != '-') {
F=fopen(argv[i], "r");
if (!F) {
fprintf(ERR, "Kann Datei `%s' nicht lesen.\n", argv[i]);
return 2;
} else {
filename=argv[i];
fprintf(ERR, "Verarbeite Datei `%s'.\n", argv[i]);
process_order_file(&faction_count, &unit_count);
}
}
/* Falls es keine input Dateien gab, ist F immer noch auf stdin gesetzt */
if (F==stdin)
process_order_file(&faction_count, &unit_count);
fprintf(ERR, "\nEs wurden Befehle für %d %s und %d %s gelesen.\n",
faction_count, faction_count != 1 ? "Parteien" : "Partei",
unit_count, unit_count != 1 ? "Einheiten" : "Einheit");
if (unit_count==0) {
fputs("\nBitte überprüfe, ob Du die Befehle korrekt eingesandt hast.\n"
"Beachte dabei besonders, daß die Befehle nicht als HTML, Word-Dokument\n"
"oder als Attachment (Anlage) eingeschickt werden dürfen.\n", ERR);
return -42;
}
if (!error_count && !warning_count && faction_count && unit_count)
fputs("Die Befehle scheinen in Ordnung zu sein.\n", ERR);
if (error_count>1)
fprintf(ERR, "Es wurden %d Fehler", error_count);
else if (error_count==1)
fputs("Es wurde ein Fehler", ERR);
if (warning_count) {
if (error_count)
fputs(" und", ERR);
else
fputs("Es wurde", ERR);
}
if (warning_count>1) {
if (!error_count)
fputs("n", ERR);
fprintf(ERR," %d Warnungen", warning_count);
} else if (warning_count==1)
fputs(" eine Warnung", ERR);
if (warning_count || error_count)
fputs(" entdeckt.\n", ERR);
#ifdef AMIGA
if (error_count>0)
return 10; /* FAIL beim AMIGA */
if (warning_count>0)
return 5; /* WARN beim AMIGA */
#endif
return 0;
}