server-1.12/common/arch.c

759 lines
22 KiB
C

/*
* static char *rcsid_arch_c =
* "$Id: arch.c 11578 2009-02-23 22:02:27Z lalo $";
*/
/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The authors can be reached via e-mail at crossfire-devel@real-time.com
*/
/**
* @file arch.c
* All archetype-related functions.
* @note
* The naming of these functions is really poor - they are all
* pretty much named '.._arch_...', but they may more may not
* return archetypes. Some make the arch_to_object call, and thus
* return an object. Perhaps those should be called 'archob' functions
* to denote they return an object derived from the archetype.
* MSW 2003-04-29
* @todo
* make the functions use the same order for parameters (type first, then name, or the opposite).
*/
#include <global.h>
#include <loader.h>
/** If set, does a little timing on the archetype load. */
#define TIME_ARCH_LOAD 0
static void add_arch(archetype *at);
static archetype *arch_table[ARCHTABLE];
static int arch_cmp = 0; /**< How many strcmp's */
static int arch_search = 0; /**< How many searches */
int arch_init; /**< True if doing arch initialization */
static void load_archetypes(void);
/**
* This function retrieves an archetype given the name that appears
* during the game (for example, "writing pen" instead of "stylus").
* It does not use the hashtable system, but browse the whole archlist each time.
* I suggest not to use it unless you really need it because of performance issue.
* It is currently used by scripting extensions (create-object).
* Params:
* @param name
* the name we're searching for (ex: "writing pen")
* @return
* the archetype found or NULL if nothing was found
*/
archetype *find_archetype_by_object_name(const char *name) {
archetype *at;
const char *tmp;
if (name == NULL)
return (archetype *)NULL;
tmp = add_string(name);
for (at = first_archetype; at != NULL; at = at->next) {
if (at->clone.name == tmp) {
free_string(tmp);
return at;
}
}
free_string(tmp);
return NULL;
}
/**
* This function retrieves an archetype by type and name that appears during
* the game. It is basically the same as find_archetype_by_object_name()
* except that it considers only items of the given type.
* @param type
* item type we're searching
* @param name
* the name we're searching for (ex: "writing pen")
*/
archetype *find_archetype_by_object_type_name(int type, const char *name) {
archetype *at;
if (name == NULL)
return NULL;
for (at = first_archetype; at != NULL; at = at->next) {
if (at->clone.type == type && strcmp(at->clone.name, name) == 0)
return at;
}
return NULL;
}
/**
* Retrieves an archetype by skill name and type.
* This is a lot like the other get_archetype_ functions, with different options.
* @param skill
* skill to search for. Must not be NULL.
* @param type
* item type to search for. -1 means that it doesn't matter.
* @return
* matching archetype, or NULL if none found.
*/
archetype *get_archetype_by_skill_name(const char *skill, int type) {
archetype *at;
if (skill == NULL)
return NULL;
for (at = first_archetype; at != NULL; at = at->next) {
if (((type == -1) || (type == at->clone.type))
&& (at->clone.skill) && (!strcmp(at->clone.skill, skill)))
return at;
}
return NULL;
}
/**
* Retrieves an archetype by type and subtype.
* Similiar to other get_archetype_ functions. This returns the first archetype
* that matches both the type and subtype. type and subtype
* can be -1 to say ignore, but in this case, the match it does
* may not be very useful. This function is most useful when
* subtypes are known to be unique for a particular type
* (eg, skills)
* @param type
* object type to search for. -1 means any
* @param subtype
* object subtype to search for. -1 means any
* @return
* matching archetype, or NULL if none found.
*/
archetype *get_archetype_by_type_subtype(int type, int subtype) {
archetype *at;
for (at = first_archetype; at != NULL; at = at->next) {
if (((type == -1) || (type == at->clone.type))
&& (subtype == -1 || subtype == at->clone.subtype))
return at;
}
return NULL;
}
/**
* Creates an object given the name that appears during the game
* (for example, "writing pen" instead of "stylus").
* @param name
* the name we're searching for (ex: "writing pen"), must not be null
* @return
* a corresponding object if found; a singularity object if not found.
* @note
* Note by MSW - it appears that it takes the full name and keeps
* shortening it until it finds a match. I re-wrote this so that it
* doesn't malloc it each time - not that this function is used much,
* but it otherwise had a big memory leak.
*/
object *create_archetype_by_object_name(const char *name) {
archetype *at;
char tmpname[MAX_BUF];
size_t i;
strncpy(tmpname, name, MAX_BUF-1);
tmpname[MAX_BUF-1] = 0;
for (i = strlen(tmpname); i > 0; i--) {
tmpname[i] = 0;
at = find_archetype_by_object_name(tmpname);
if (at != NULL) {
return arch_to_object(at);
}
}
return create_singularity(name);
}
/**
* Initialises the internal linked list of archetypes (read from file).
* Then the global ::empty_archetype pointer is initialised.
* Can be called multiple times, will just return.
*/
void init_archetypes(void) {
if (first_archetype != NULL) /* Only do this once */
return;
arch_init = 1;
load_archetypes();
arch_init = 0;
empty_archetype = find_archetype("empty_archetype");
/* init_blocksview();*/
}
/**
* Stores debug-information about how efficient the hashtable
* used for archetypes has been in the static errmsg array.
*/
void arch_info(object *op) {
draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG,
"%d searches and %d strcmp()'s",
"%d searches and %d strcmp()'s",
arch_search, arch_cmp);
}
/**
* Initialise the hashtable used by the archetypes.
*/
void clear_archetable(void) {
memset((void *)arch_table, 0, ARCHTABLE*sizeof(archetype *));
}
/**
* An alternative way to init the hashtable which is slower, but _works_...
*/
static void init_archetable(void) {
archetype *at;
LOG(llevDebug, " Setting up archetable...\n");
for (at = first_archetype; at != NULL; at = (at->more == NULL) ? at->next : at->more)
add_arch(at);
LOG(llevDebug, "done\n");
}
/**
* Dumps an archetype to buffer.
*
* @param at
* archetype to dump. Must not be NULL.
* @param sb
* buffer that will contain dumped information.
*/
void dump_arch(archetype *at, StringBuffer *sb) {
dump_object(&at->clone, sb);
}
/**
* Dumps _all_ archetypes to debug-level output.
* If you run crossfire with debug, and enter DM-mode, you can trigger
* this with the "dumpallarchetypes" command.
*/
void dump_all_archetypes(void) {
archetype *at;
for (at = first_archetype; at != NULL; at = (at->more == NULL) ? at->next : at->more) {
StringBuffer *sb;
char *diff;
sb = stringbuffer_new();
dump_arch(at, sb);
diff = stringbuffer_finish(sb);
LOG(llevDebug, "%s\n", diff);
free(diff);
}
}
/**
* Frees archetype.
*
* @param at
* archetype to free. Pointer becomes invalid after the call.
*/
void free_arch(archetype *at) {
if (at->name)
free_string(at->name);
if (at->clone.name)
free_string(at->clone.name);
if (at->clone.name_pl)
free_string(at->clone.name_pl);
if (at->clone.title)
free_string(at->clone.title);
if (at->clone.race)
free_string(at->clone.race);
if (at->clone.slaying)
free_string(at->clone.slaying);
if (at->clone.msg)
free_string(at->clone.msg);
if (at->clone.discrete_damage)
free(at->clone.discrete_damage);
free_key_values(&at->clone);
free(at);
}
/**
* Frees all memory allocated to archetypes.
* After calling this, it's possible to call again init_archetypes() to reload data.
*/
void free_all_archs(void) {
archetype *at, *next;
int i = 0;
for (at = first_archetype; at != NULL; at = next) {
if (at->more)
next = at->more;
else
next = at->next;
free_arch(at);
i++;
}
first_archetype = NULL;
/* Reset the hashtable */
clear_archetable();
LOG(llevDebug, "Freed %d archetypes\n", i);
}
/**
* Allocates, initialises and returns the pointer to an archetype structure.
* @return
* new archetype structure, will never be NULL.
* @note
* this function will call fatal() if it can't allocate memory.
*/
archetype *get_archetype_struct(void) {
archetype *new;
new = (archetype *)CALLOC(1, sizeof(archetype));
if (new == NULL)
fatal(OUT_OF_MEMORY);
new->next = NULL;
new->name = NULL;
new->clone.other_arch = NULL;
new->clone.name = NULL;
new->clone.name_pl = NULL;
new->clone.title = NULL;
new->clone.race = NULL;
new->clone.slaying = NULL;
new->clone.msg = NULL;
clear_object(&new->clone); /* to initial state other also */
CLEAR_FLAG((&new->clone), FLAG_FREED); /* This shouldn't matter, since copy_object() */
SET_FLAG((&new->clone), FLAG_REMOVED); /* doesn't copy these flags... */
new->head = NULL;
new->more = NULL;
new->clone.arch = new;
return new;
}
/**
* Reads/parses the archetype-file, and copies into a linked list
* of archetype-structures.
* Called through load_archetypes()
*
* Will discard object in archetypes, those are handled by second_arch_pass().
*
* @param fp
* opened file descriptor which will be used to read the archetypes.
*/
static void first_arch_pass(FILE *fp) {
object *op;
archetype *at, *head = NULL, *last_more = NULL;
int i, first = 2;
op = get_object();
op->arch = first_archetype = at = get_archetype_struct();
while ((i = load_object(fp, op, first, 0))) {
first = 0;
copy_object(op, &at->clone);
at->clone.speed_left = (float)(-0.1);
/* copy the body_info to the body_used - this is only really
* need for monsters, but doesn't hurt to do it for everything.
* by doing so, when a monster is created, it has good starting
* values for the body_used info, so when items are created
* for it, they can be properly equipped.
*/
memcpy(&at->clone.body_used, &op->body_info, sizeof(op->body_info));
switch (i) {
case LL_NORMAL: /* A new archetype, just link it with the previous */
if (last_more != NULL)
last_more->next = at;
if (head != NULL)
head->next = at;
head = last_more = at;
at->tail_x = 0;
at->tail_y = 0;
break;
case LL_MORE: /* Another part of the previous archetype, link it correctly */
at->head = head;
at->clone.head = &head->clone;
if (last_more != NULL) {
last_more->more = at;
last_more->clone.more = &at->clone;
}
last_more = at;
/* Set FLAG_MONSTER throughout parts if head has it */
if (QUERY_FLAG(&head->clone, FLAG_MONSTER)) {
SET_FLAG(&at->clone, FLAG_MONSTER);
}
/* If this multipart image is still composed of individual small
* images, don't set the tail_.. values. We can't use them anyways,
* and setting these to zero makes the map sending to the client much
* easier as just looking at the head, we know what to do.
*/
if (at->clone.face != head->clone.face) {
head->tail_x = 0;
head->tail_y = 0;
} else {
if (at->clone.x > head->tail_x)
head->tail_x = at->clone.x;
if (at->clone.y > head->tail_y)
head->tail_y = at->clone.y;
}
break;
}
at = get_archetype_struct();
clear_object(op);
op->arch = at;
}
free_object(op);
op->arch = NULL; /* arch is checked for temporary archetypes if not NULL. */
free(at);
}
/**
* Reads the archetype file once more, and links all pointers between
* archetypes and treasure lists. Must be called after first_arch_pass().
*
* This also handles putting items in inventory when defined in archetype.
*
* @param fp
* file fron which to read. Won't be rewinded.
*/
static void second_arch_pass(FILE *fp) {
char buf[MAX_BUF], *variable = buf, *argument, *cp;
archetype *at = NULL, *other;
object *inv;
while (fgets(buf, MAX_BUF, fp) != NULL) {
if (*buf == '#')
continue;
if ((argument = strchr(buf, ' ')) != NULL) {
*argument = '\0', argument++;
cp = argument+strlen(argument)-1;
while (isspace(*cp)) {
*cp = '\0';
cp--;
}
}
if (!strcmp("Object", variable)) {
if ((at = find_archetype(argument)) == NULL)
LOG(llevError, "Warning: failed to find arch %s\n", argument);
} else if (!strcmp("other_arch", variable)) {
if (at != NULL && at->clone.other_arch == NULL) {
if ((other = find_archetype(argument)) == NULL)
LOG(llevError, "Warning: failed to find other_arch %s\n", argument);
else if (at != NULL)
at->clone.other_arch = other;
}
} else if (!strcmp("randomitems", variable)) {
if (at != NULL) {
treasurelist *tl = find_treasurelist(argument);
if (tl == NULL)
LOG(llevError, "Failed to link treasure to arch (%s): %s\n", at->name, argument);
else
at->clone.randomitems = tl;
}
} else if (!strcmp("arch", variable)) {
inv = create_archetype(argument);
load_object(fp, inv, LO_LINEMODE, 0);
if (at) {
insert_ob_in_ob(inv, &at->clone);
/*LOG(llevDebug, "Put %s in %s\n", inv->name, at->clone.name);*/
} else {
LOG(llevError, "Got an arch %s not inside an Object.\n", argument);
free_object(inv);
}
}
}
}
#ifdef DEBUG
/**
* Check all generators have the other_arch set.
* @note
* doesn't work anymore, as generators can use their inventory.
*/
void check_generators(void) {
archetype *at;
for (at = first_archetype; at != NULL; at = at->next)
if (QUERY_FLAG(&at->clone, FLAG_GENERATOR) && at->clone.other_arch == NULL)
LOG(llevError, "Warning: %s is generator but lacks other_arch.\n", at->name);
}
#endif
/**
* Loads all archetypes and treasures.
* First initialises the archtype hash-table (init_archetable()).
* Reads and parses the archetype file (with the first and second-pass
* functions).
* Then initialises treasures by calling load_treasures().
*/
static void load_archetypes(void) {
FILE *fp;
char filename[MAX_BUF];
int comp;
#if TIME_ARCH_LOAD
struct timeval tv1, tv2;
#endif
snprintf(filename, sizeof(filename), "%s/%s", settings.datadir, settings.archetypes);
LOG(llevDebug, "Reading archetypes from %s...\n", filename);
if ((fp = open_and_uncompress(filename, 0, &comp)) == NULL) {
LOG(llevError, " Can't open archetype file.\n");
return;
}
clear_archetable();
LOG(llevDebug, " arch-pass 1...\n");
#if TIME_ARCH_LOAD
GETTIMEOFDAY(&tv1);
#endif
first_arch_pass(fp);
#if TIME_ARCH_LOAD
{
int sec, usec;
GETTIMEOFDAY(&tv2);
sec = tv2.tv_sec-tv1.tv_sec;
usec = tv2.tv_usec-tv1.tv_usec;
if (usec < 0) {
usec += 1000000;
sec--;
}
LOG(llevDebug, "Load took %d.%06d seconds\n", sec, usec);
}
#endif
LOG(llevDebug, " done\n");
init_archetable();
warn_archetypes = 1;
/* do a close and reopen instead of a rewind - necessary in case the
* file has been compressed.
*/
close_and_delete(fp, comp);
fp = open_and_uncompress(filename, 0, &comp);
LOG(llevDebug, " loading treasure...\n");
load_treasures();
LOG(llevDebug, " done\n");
LOG(llevDebug, "arch-pass 2...\n");
second_arch_pass(fp);
LOG(llevDebug, " done\n");
#ifdef DEBUG
check_generators();
#endif
close_and_delete(fp, comp);
LOG(llevDebug, " done\n");
}
/**
* Creates and returns a new object which is a copy of the given archetype.
* This function returns NULL if given a NULL pointer, else an object.
* @param at
* archetype from which to get an object.
* @return
* object of specified type.
* @note
* get_object() will either allocate memory or call fatal(), so returned value
* is never NULL.
*/
object *arch_to_object(archetype *at) {
object *op;
if (at == NULL) {
if (warn_archetypes)
LOG(llevError, "Couldn't find archetype.\n");
return NULL;
}
op = get_object();
copy_object_with_inv(&at->clone, op);
op->arch = at;
return op;
}
/**
* Creates a dummy object. This function is called by get_archetype()
* if it fails to find the appropriate archetype.
* Thus get_archetype() will be guaranteed to always return
* an object, and never NULL.
* @param name
* name to give the dummy object.
* @return
* object of specified name. It fill have the ::FLAG_NO_PICK flag set.
* @note
* get_object() will either allocate memory or call fatal(), so returned value
* is never NULL.
*/
object *create_singularity(const char *name) {
object *op;
char buf[MAX_BUF];
snprintf(buf, sizeof(buf), "%s (%s)", ARCH_SINGULARITY, name);
op = get_object();
op->name = add_string(buf);
op->name_pl = add_string(buf);
SET_FLAG(op, FLAG_NO_PICK);
return op;
}
/**
* Finds which archetype matches the given name, and returns a new
* object containing a copy of the archetype.
* @param name
* archetype name
* @return
* object of specified archetype, or a singularity. Will never be NULL.
* @todo
* replace with object_create_arch() which is multi-part aware.
*/
object *create_archetype(const char *name) {
archetype *at;
at = find_archetype(name);
if (at == NULL)
return create_singularity(name);
return arch_to_object(at);
}
/**
* Hash-function used by the arch-hashtable.
* @param str
* archetype name
* @param tablesize
* size of the hash table
* @return
* hash of the archetype name
*/
static unsigned long
hasharch(const char *str, int tablesize) {
unsigned long hash = 0;
int i = 0;
const char *p;
/* use the one-at-a-time hash function, which supposedly is
* better than the djb2-like one used by perl5.005, but
* certainly is better then the bug used here before.
* see http://burtleburtle.net/bob/hash/doobs.html
*/
for (p = str; i < MAXSTRING && *p; p++, i++) {
hash += *p;
hash += hash<<10;
hash ^= hash>>6;
}
hash += hash<<3;
hash ^= hash>>11;
hash += hash<<15;
return hash%tablesize;
}
/**
* Finds, using the hashtable, which archetype matches the given name. Will not LOG() if not found.
* @return
* pointer to the found archetype, otherwise NULL.
* @see find_archetype()
*/
archetype *try_find_archetype(const char *name) {
archetype *at;
unsigned long index;
if (name == NULL)
return (archetype *)NULL;
index = hasharch(name, ARCHTABLE);
arch_search++;
for (;;) {
at = arch_table[index];
if (at == NULL) {
return NULL;
}
arch_cmp++;
if (!strcmp(at->name, name))
return at;
if (++index >= ARCHTABLE)
index = 0;
}
}
/**
* Finds, using the hashtable, which archetype matches the given name. Will LOG() if not found.
* @return
* pointer to the found archetype, otherwise NULL.
* @see try_find_archetype
* @todo replace by try_find_archetype() when suitable and trash ::warn_archetypes.
*/
archetype *find_archetype(const char *name) {
archetype *at;
if (name == NULL)
return (archetype *)NULL;
at = try_find_archetype(name);
if (at == NULL && warn_archetypes)
LOG(llevError, "Couldn't find archetype %s\n", name);
return at;
}
/**
* Adds an archetype to the hashtable.
* Will call fatal() if archetype table is too small to contain archetypes.
*/
static void add_arch(archetype *at) {
unsigned long index = hasharch(at->name, ARCHTABLE), org_index = index;
for (;;) {
if (arch_table[index] == NULL) {
arch_table[index] = at;
return;
}
if (++index == ARCHTABLE)
index = 0;
if (index == org_index)
fatal(ARCHTABLE_TOO_SMALL);
}
}
/**
* Create a full object using the given archetype.
* This instanciate not only the archetype but also
* all linked archetypes in case of multisquare archetype.
* @param at
* archetype to instanciate. Must not be NULL.
* @return
* pointer to head of instance.
* @note
* will never return NULL.
*/
object *object_create_arch(archetype *at) {
object *op, *prev = NULL, *head = NULL;
while (at) {
op = arch_to_object(at);
op->x = at->clone.x;
op->y = at->clone.y;
if (head)
op->head = head, prev->more = op;
if (!head)
head = op;
prev = op;
at = at->more;
}
return (head);
}
/*** end of arch.c ***/