server/src/common/kernel/border.c
2005-01-03 21:28:57 +00:00

534 lines
12 KiB
C
Raw Blame History

/* vi: set ts=2:
*
*
* Eressea PB(E)M host Copyright (C) 1998-2003
* 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)
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
*/
#include <config.h>
#include "eressea.h"
#include "border.h"
#include "unit.h"
#include "region.h"
/* libc includes */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
extern boolean incomplete_data;
unsigned int nextborder = 0;
border * borders[BMAXHASH];
border_type * bordertypes;
void
free_borders(void)
{
int i;
for (i=0;i!=BMAXHASH;++i) {
borders[i] = NULL;
}
}
border *
find_border(unsigned int id)
{
int key;
for (key=0;key!=BMAXHASH;key++) {
border * bhash;
for (bhash=borders[key];bhash!=NULL;bhash=bhash->nexthash) {
border * b;
for (b=bhash;b;b=b->next) {
if (b->id==id) return b;
}
}
}
return NULL;
}
void *
resolve_borderid(void * id) {
return (void*)find_border((unsigned int)id);
}
static border **
get_borders_i(const region * r1, const region * r2)
{
border ** bp;
int key = region_hashkey(r1);
int k2 = region_hashkey(r2);
key = min(k2, key) % BMAXHASH;
bp = &borders[key];
while (*bp) {
border * b = *bp;
if ((b->from==r1 && b->to==r2) || (b->from==r2 && b->to==r1)) break;
bp = &b->nexthash;
}
return bp;
}
border *
get_borders(const region * r1, const region * r2)
{
border ** bp = get_borders_i(r1, r2);
return *bp;
}
border *
new_border(border_type * type, region * from, region * to)
{
border ** bp = get_borders_i(from, to);
border * b = calloc(1, sizeof(struct border));
while (*bp) bp = &(*bp)->next;
*bp = b;
b->type = type;
b->from = from;
b->to = to;
b->id = ++nextborder;
if (type->init) type->init(b);
return b;
}
void
erase_border(border * b)
{
border ** bp = get_borders_i(b->from, b->to);
attrib ** ap = &b->attribs;
while (*ap) a_remove(&b->attribs, *ap);
assert(*bp!=NULL || !"error: border is not registered");
if (*bp==b) {
/* it is the first in the list, so it is in the nexthash list */
if (b->next) {
*bp = b->next;
(*bp)->nexthash = b->nexthash;
} else {
*bp = b->nexthash;
}
} else {
while (*bp && *bp != b) {
bp = &(*bp)->next;
}
assert(*bp==b || !"error: border is not registered");
*bp = b->next;
}
if (b->type->destroy) b->type->destroy(b);
free(b);
}
void
register_bordertype(border_type * type)
{
border_type ** btp = &bordertypes;
while (*btp && *btp!=type) btp = &(*btp)->next;
if (*btp) return;
*btp = type;
}
border_type *
find_bordertype(const char * name)
{
border_type * bt = bordertypes;
while (bt && strcmp(bt->__name, name)) bt = bt->next;
return bt;
}
void
b_read(border * b, FILE *f)
{
assert(sizeof(int)==sizeof(b->data));
fscanf(f, "%x ", (unsigned int*)&b->data);
}
void
b_write(const border * b, FILE *f)
{
assert(sizeof(int)==sizeof(b->data));
fprintf(f, "%x ", (int)b->data);
}
boolean b_transparent(const border * b, const struct faction * f) { unused(b); unused(f); return true; }
boolean b_opaque(const border * b, const struct faction * f) { unused(b); unused(f); return false; }
boolean b_blockall(const border * b, const unit * u, const region * r) { unused(u); unused(r); unused(b); return true; }
boolean b_blocknone(const border * b, const unit * u, const region * r) { unused(u); unused(r); unused(b); return false; }
boolean b_rvisible(const border * b, const region * r) { return (boolean)(b->to==r || b->from==r); }
boolean b_fvisible(const border * b, const struct faction * f, const region * r) { unused(r); unused(f); unused(b); return true; }
boolean b_uvisible(const border * b, const unit * u) { unused(u); unused(b); return true; }
boolean b_rinvisible(const border * b, const region * r) { unused(r); unused(b); return false; }
boolean b_finvisible(const border * b, const struct faction * f, const region * r) { unused(r); unused(f); unused(b); return false; }
boolean b_uinvisible(const border * b, const unit * u) { unused(u); unused(b); return false; }
/*********************/
/* at_countdown */
/*********************/
static int
a_agecountdown(attrib * a)
{
a->data.i = max(a->data.i-1, 0);
return a->data.i;
}
attrib_type at_countdown = {
"countdown",
DEFAULT_INIT,
DEFAULT_FINALIZE,
a_agecountdown,
DEFAULT_WRITE,
DEFAULT_READ
};
void
age_borders(void)
{
border_list * deleted = NULL;
int i;
for (i=0;i!=BMAXHASH;++i) {
border * bhash = borders[i];
for (;bhash;bhash=bhash->nexthash) {
border * b = bhash;
for (;b;b=b->next) {
attrib ** ap = &b->attribs;
while (*ap) {
attrib * a = *ap;
if (a->type->age && a->type->age(a)==0) {
if (a->type == &at_countdown) {
border_list * bnew = malloc(sizeof(border_list));
bnew->next = deleted;
bnew->data = b;
deleted = bnew;
break;
}
a_remove(&b->attribs, a);
}
else ap=&a->next;
}
}
}
}
while (deleted) {
border_list * blist = deleted->next;
border * b = deleted->data;
erase_border(b);
free(deleted);
deleted = blist;
}
}
/********
* implementation of a couple of borders. this shouldn't really be in here, so
* let's keep it separate from the more general stuff above
********/
#include "faction.h"
static const char *
b_namewall(const border * b, const region * r, const struct faction * f, int gflags)
{
unused(f);
unused(r);
unused(b);
if (gflags & GF_ARTICLE) return "eine Wand";
return "Wand";
}
border_type bt_wall = {
"wall",
b_opaque,
NULL, /* init */
NULL, /* destroy */
b_read, /* read */
b_write, /* write */
b_blockall, /* block */
b_namewall, /* name */
b_rvisible, /* rvisible */
b_fvisible, /* fvisible */
b_uvisible, /* uvisible */
};
border_type bt_noway = {
"noway",
b_transparent,
NULL, /* init */
NULL, /* destroy */
b_read, /* read */
b_write, /* write */
b_blockall, /* block */
NULL, /* name */
b_rinvisible, /* rvisible */
b_finvisible, /* fvisible */
b_uinvisible, /* uvisible */
};
static const char *
b_namefogwall(const border * b, const region * r, const struct faction * f, int gflags)
{
unused(f);
unused(b);
unused(r);
if (gflags & GF_ARTICLE) return "eine Nebelwand";
return "Nebelwand";
}
static boolean
b_blockfogwall(const border * b, const unit * u, const region * r)
{
unused(b);
unused(r);
if (!u) return true;
return (boolean)(effskill(u, SK_OBSERVATION) > 4); /* Das ist die alte Nebelwand */
}
border_type bt_fogwall = {
"fogwall",
b_transparent, /* transparent */
NULL, /* init */
NULL, /* destroy */
b_read, /* read */
b_write, /* write */
b_blockfogwall, /* block */
b_namefogwall, /* name */
b_rvisible, /* rvisible */
b_fvisible, /* fvisible */
b_uvisible, /* uvisible */
};
static const char *
b_nameillusionwall(const border * b, const region * r, const struct faction * f, int gflags)
{
/* TODO: f->locale bestimmt die Sprache */
int fno = (int)b->data;
unused(b);
unused(r);
if (gflags & GF_ARTICLE) return (f && fno==f->no)?"eine Illusionswand":"eine Wand";
return (f && fno==f->no)?"Illusionswand":"Wand";
}
border_type bt_illusionwall = {
"illusionwall",
b_opaque,
NULL, /* init */
NULL, /* destroy */
b_read, /* read */
b_write, /* write */
b_blocknone, /* block */
b_nameillusionwall, /* name */
b_rvisible, /* rvisible */
b_fvisible, /* fvisible */
b_uvisible, /* uvisible */
};
/***
* special quest door
***/
boolean b_blockquestportal(const border * b, const unit * u, const region * r) {
if((int)b->data > 0) return true;
return false;
}
static const char *
b_namequestportal(const border * b, const region * r, const struct faction * f, int gflags)
{
/* TODO: f->locale bestimmt die Sprache */
int lock = (int)b->data;
unused(b);
unused(r);
if (gflags & GF_ARTICLE) {
if(lock > 0) {
return "ein gewaltiges verschlossenes Tor";
} else {
return "ein gewaltiges offenes Tor";
}
} else {
if(lock > 0) {
return "gewaltiges verschlossenes Tor";
} else {
return "gewaltiges offenes Tor";
}
}
}
border_type bt_questportal = {
"questportal",
b_opaque,
NULL, /* init */
NULL, /* destroy */
b_read, /* read */
b_write, /* write */
b_blockquestportal, /* block */
b_namequestportal, /* name */
b_rvisible, /* rvisible */
b_fvisible, /* fvisible */
b_uvisible, /* uvisible */
};
/***
* roads. meant to replace the old at_road or r->road attribute
***/
static const char *
b_nameroad(const border * b, const region * r, const struct faction * f, int gflags)
{
region * r2 = (r==b->to)?b->from:b->to;
int rval = (int)b->data;
int local = (r==b->to)?(rval & 0xFFFF):((rval>>16) & 0xFFFF);
static char buffer[64];
unused(f);
if (gflags & GF_ARTICLE) {
if (!(gflags & GF_DETAILED)) strcpy(buffer, "eine Stra<72>e");
else if (terrain[rterrain(r)].roadreq<=local) {
int remote = (r!=b->to)?(rval & 0xFFFF):((rval>>16) & 0xFFFF);
if (terrain[rterrain(r2)].roadreq<=remote) {
strcpy(buffer, "eine Stra<72>e");
} else {
strcpy(buffer, "eine unvollst<73>ndige Stra<72>e");
}
} else {
int percent = max(1, 100*local/terrain[rterrain(r)].roadreq);
if (local) {
sprintf(buffer, "eine zu %d%% vollendete Stra<72>e", percent);
} else {
strcpy(buffer, "ein Stra<72>enanschlu<6C>");
}
}
}
else if (gflags & GF_PLURAL) return "Stra<EFBFBD>en";
else return "Stra<EFBFBD>e";
return buffer;
}
static void
b_readroad(border * b, FILE *f)
{
int x, y;
fscanf(f, "%d %d ", &x, &y);
b->data=(void*)((x<<16) | y);
}
static void
b_writeroad(const border * b, FILE *f)
{
int x, y;
x = (int)b->data;
y = x & 0xFFFF;
x = (x >> 16) & 0xFFFF;
fprintf(f, "%d %d ", x, y);
}
static boolean
b_validroad(const border * b)
{
int x = (int)b->data;
if (x==0) return false;
return true;
}
static boolean
b_rvisibleroad(const border * b, const region * r)
{
int x = (int)b->data;
x = (r==b->to)?(x & 0xFFFF):((x>>16) & 0xFFFF);
if (x==0) return false;
return (boolean)(b->to==r || b->from==r);
}
border_type bt_road = {
"road",
b_transparent,
NULL, /* init */
NULL, /* destroy */
b_readroad, /* read */
b_writeroad, /* write */
b_blocknone, /* block */
b_nameroad, /* name */
b_rvisibleroad, /* rvisible */
b_finvisible, /* fvisible */
b_uinvisible, /* uvisible */
b_validroad /* valid */
};
void
write_borders(FILE * f)
{
int i;
for (i=0;i!=BMAXHASH;++i) {
border * bhash;
for (bhash=borders[i];bhash;bhash=bhash->nexthash) {
border * b;
for (b=bhash;b!=NULL;b=b->next) {
if (b->type->valid && !b->type->valid(b)) continue;
fprintf(f, "%s %d %d %d %d %d ", b->type->__name, b->id, b->from->x, b->from->y, b->to->x, b->to->y);
if (b->type->write) b->type->write(b, f);
#if RELEASE_VERSION>BORDER_VERSION
a_write(f, b->attribs);
#endif
putc('\n', f);
}
}
}
fputs("end ", f);
}
void
read_borders(FILE * f)
{
for (;;) {
int fx, fy, tx, ty;
unsigned int bid = 0;
char zText[32];
border * b;
region * from, * to;
border_type * type;
fscanf(f, "%s", zText);
if (!strcmp(zText, "end")) break;
fscanf(f, "%u %d %d %d %d", &bid, &fx, &fy, &tx, &ty);
type = find_bordertype(zText);
assert(type || !"border type not registered");
from = findregion(fx, fy);
if (from==NULL) {
if (!incomplete_data) log_error(("border for unknown region %d,%d\n", fx, fy));
from = new_region(fx, fy);
}
to = findregion(tx, ty);
if (to==NULL) {
if (!incomplete_data) log_error(("border for unknown region %d,%d\n", tx, ty));
to = new_region(tx, ty);
}
if (to==from) {
direction_t dir = (direction_t) (rand() % MAXDIRECTIONS);
region * r = rconnect(from, dir);
log_error(("[read_borders] invalid %s in %s\n", type->__name, regionname(from, NULL)));
if (r!=NULL) to = r;
}
b = new_border(type, from, to);
nextborder--; /* new_border erh<72>ht den Wert */
b->id = bid;
assert(bid<=nextborder);
if (type->read) type->read(b, f);
a_read(f, &b->attribs);
}
}