759 lines
22 KiB
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 ***/
|