--- deliantra/server/common/loader.C 2006/12/26 08:54:59 1.30 +++ deliantra/server/common/loader.C 2007/02/10 01:52:25 1.57 @@ -1,35 +1,195 @@ /* - CrossFire, A Multiplayer game for X-windows - - Copyright (C) 2002 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, A Multiplayer game for X-windows + * + * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team + * Copyright (C) 2002 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 + */ /* Eneq(@csd.uu.se): Added weight-modifiers in environment of objects. sub/add_weight will transcend the environment updating the carrying variable. */ - #include #include #include +///////////////////////////////////////////////////////////////////////////// + +loader_base::~loader_base () +{ +} + +// the base class warns about and skips everything +archetype * +loader_base::get_arch (const char *name) +{ + LOG (llevError, "%s: found archetype definition '%s', which is not allowed in files of this type.\n", + filename, name); + + return new archetype; +} + +object * +loader_base::get_object (const char *name) +{ + LOG (llevError, "%s: found object definition '%s', which is not allowed in files of this type.\n", + filename, name); + + return object::create (); +} + +player * +loader_base::get_player () +{ + LOG (llevError, "%s: found player definition, which is not allowed in files of this type.\n", + filename); + + return player::create (); +} + +region * +loader_base::get_region (const char *name) +{ + LOG (llevError, "%s: found region definition '%s', which is not allowed in files of this type.\n", + filename, name); + + return new region; +} + +facetile * +loader_base::get_face (const char *name) +{ + LOG (llevError, "%s: found face definition '%s', which is not allowed in files of this type.\n", + filename, name); + + return new facetile; +} + +treasurelist * +loader_base::get_treasure (const char *name, bool one) +{ + LOG (llevError, "%s: found treasure definition '%s', which is not allowed in files of this type.\n", + filename, name); + + return new treasurelist;//D +} + +animation * +loader_base::get_animation (const char *name) +{ + LOG (llevError, "%s: found animation definition '%s', which is not allowed in files of this type.\n", + filename, name); + + return new animation; +} + +void +loader_base::put_arch (archetype *arch) +{ + delete arch; +} + +void +loader_base::put_object (object *op) +{ + op->destroy (); +} + +void +loader_base::put_player (player *pl) +{ + delete pl; +} + +void +loader_base::put_region (region *region) +{ + delete region; +} + +void +loader_base::put_face (facetile *face) +{ + delete face; +} + +void +loader_base::put_treasure (treasurelist *treasure) +{ + delete treasure; +} + +void +loader_base::put_animation (animation *anim) +{ + delete anim; +} + +///////////////////////////////////////////////////////////////////////////// + +bool loader_base::parse (object_thawer &thawer) +{ + for (;;) + { + keyword kw = thawer.get_kv (); + + switch (kw) + { + case KW_region: + { + region *rgn = get_region (thawer.get_str ()); + + if (!parse_region (thawer, rgn)) + { + delete rgn; + return false; + } + + put_region (rgn); + } + break; + + case KW_EOF: + return true; + + default: + if (!thawer.parse_error (kw, "resource file")) + return false; + } + } +} + +bool loader_base::load (const char *filename) +{ + this->filename = filename; + + object_thawer fp (filename); + + if (!fp) + return false; + + return parse (fp); +} + +///////////////////////////////////////////////////////////////////////////// + /* Maps the MOVE_* values to names */ static const char *const move_name[] = { "walk", "fly_low", "fly_high", "swim", "boat", NULL }; @@ -263,11 +423,9 @@ static void check_loaded_object (object *op) { - int ip; - - /* We do some specialized handling to handle legacy cases of name_pl. + /* We do some specialised handling to handle legacy cases of name_pl. * If the object doesn't have a name_pl, we just use the object name - - * this isn't perfect (things won't be properly pluralized), but works to + * this isn't perfect (things won't be properly pluralised), but works to * that degree (5 heart is still quite understandable). But the case we * also have to catch is if this object is not using the normal name for * the object. In that case, we also want to use the loaded name. @@ -275,7 +433,7 @@ * information (appear as just 'hearts' and not 'goblins heart') */ if (op->arch && op->name != op->arch->clone.name && op->name_pl == op->arch->clone.name_pl) - op->name_pl = NULL; + op->name_pl = 0; if (!op->name_pl) op->name_pl = op->name; @@ -288,6 +446,7 @@ else set_materialname (op, 5, NULL); } + /* only do these when program is first run - a bit * excessive to do this at every run - most of this is * really just to catch any errors - program will still run, but @@ -296,25 +455,22 @@ if ((op->type == WEAPON || op->type == BOW) && arch_init) { if (!op->skill) - { - LOG (llevError, "Weapon %s lacks a skill.\n", op->debug_desc ()); - } + LOG (llevError, "Weapon %s lacks a skill.\n", op->debug_desc ()); else if ((!strcmp (op->skill, "one handed weapons") && op->body_info[1] != -1) || (!strcmp (op->skill, "two handed weapons") && op->body_info[1] != -2)) - { - LOG (llevError, "weapon %s arm usage does not match skill: %d, %s\n", op->debug_desc (), op->body_info[1], &op->skill); - } + LOG (llevError, "weapon %s arm usage does not match skill: %d, %s\n", op->debug_desc (), op->body_info[1], &op->skill); } /* We changed last_heal to gen_sp_armour, which is what it * really does for many objects. Need to catch any in maps * that may have an old value. */ - if ((op->type == WEAPON) || - (op->type == ARMOUR) || (op->type == HELMET) || - (op->type == SHIELD) || (op->type == RING) || - (op->type == BOOTS) || (op->type == GLOVES) || - (op->type == AMULET) || (op->type == GIRDLE) || (op->type == BRACERS) || (op->type == CLOAK)) + if (op->type == WEAPON + || op->type == ARMOUR || op->type == HELMET + || op->type == SHIELD || op->type == RING + || op->type == BOOTS || op->type == GLOVES + || op->type == AMULET || op->type == GIRDLE + || op->type == BRACERS || op->type == CLOAK) { if (op->last_heal) { @@ -322,19 +478,18 @@ op->gen_sp_armour = op->last_heal; op->last_heal = 0; } - if (editor) - ip = 0; - else - ip = calc_item_power (op, 0); + + int ip = calc_item_power (op, 0); + /* Legacy objects from before item power was in the game */ if (!op->item_power && ip) { if (ip > 3) - { - LOG (llevDebug, "Object %s had no item power, using %d\n", op->debug_desc (), ip); - } + LOG (llevDebug, "Object %s had no item power, using %d\n", op->debug_desc (), ip); + op->item_power = ip; } + /* Check for possibly bogus values. Has to meet both these criteria - * something that has item_power 1 is probably just fine if our calculated * value is 1 or 2 - these values are small enough that hard to be precise. @@ -342,32 +497,26 @@ * but not the second one. */ if (ip > 2 * op->item_power && ip > (op->item_power + 3)) - { - LOG (llevDebug, "Object %s seems to have too low item power? %d > %d\n", op->debug_desc (), ip, op->item_power); - } - + LOG (llevDebug, "Object %s seems to have too low item power? %d > %d\n", op->debug_desc (), ip, op->item_power); } + /* Old spellcasting object - need to load in the appropiate object */ if ((op->type == ROD || op->type == WAND || op->type == SCROLL || op->type == HORN || op->type == FIREWALL || /* POTIONS and ALTARS don't always cast spells, but if they do, update them */ ((op->type == POTION || op->type == ALTAR) && op->stats.sp)) && !op->inv && !arch_init) { - object *tmp; - /* Fireall is bizarre in that spell type was stored in dam. Rest are 'normal' * in that spell was stored in sp. */ - tmp = get_archetype (spell_mapping[op->type == FIREWALL ? op->stats.dam : op->stats.sp]); + object *tmp = get_archetype (spell_mapping[op->type == FIREWALL ? op->stats.dam : op->stats.sp]); insert_ob_in_ob (tmp, op); op->randomitems = NULL; /* So another spell isn't created for this object */ } - /* spellbooks & runes use slaying. But not to arch name, but to spell name */ + /* spellbooks & runes use slaying. But not to arch name, but to spell name */ if ((op->type == SPELLBOOK || op->type == RUNE) && op->slaying && !op->inv && !arch_init) { - object *tmp; - - tmp = get_archetype_by_object_name (op->slaying); + object *tmp = get_archetype_by_object_name (op->slaying); insert_ob_in_ob (tmp, op); op->randomitems = NULL; /* So another spell isn't created for this object */ /* without this, value is all screwed up */ @@ -377,7 +526,10 @@ if (QUERY_FLAG (op, FLAG_MONSTER)) { if (op->stats.hp > op->stats.maxhp) - LOG (llevDebug, "Monster %s has hp set higher than maxhp (%d>%d)\n", op->debug_desc (), op->stats.hp, op->stats.maxhp); + { + LOG (llevDebug, "Monster %s has hp set higher than maxhp (%d>%d)\n", op->debug_desc (), op->stats.hp, op->stats.maxhp); + op->stats.maxhp = op->stats.hp; + } /* The archs just need to be updated for this */ if (op->move_type == 0) @@ -410,9 +562,7 @@ op->stats.maxhp = 0; } else - { - op->move_type = MOVE_WALK; - } + op->move_type = MOVE_WALK; } } @@ -553,15 +703,21 @@ object *tmp; - if (arch != NULL) + if (arch) tmp = arch_to_object (arch); else { tmp = object::create (); /* record the name of the broken object */ + //TODO: but do not log??? tmp->name = str; } + // decativate the object we, as we are still going + // to read the speed value. Objects should be activated + // when the map, object or player is completly loaded. + tmp->deactivate (); + parse_object (tmp, thawer, map_flags); if (tmp->arch) @@ -603,7 +759,11 @@ op->arch = archetype::find (str); if (op->arch) - op->arch->clone.copy_to (op); + { + op->arch->clone.copy_to (op); + // copy_to activates, this should be fixed properly + op->deactivate (); + } else if (!arch_init) /* record the name of the broken object */ op->name = str; @@ -617,20 +777,13 @@ case KW_animation: { - const char *str = thawer.get_str (); + CLEAR_FLAG (op, FLAG_ANIMATE); + op->animation_id = 0; - if (!str) - { - op->animation_id = 0; - CLEAR_FLAG (op, FLAG_ANIMATE); - } - else - { - op->animation_id = find_animation (str); - SET_FLAG (op, FLAG_ANIMATE); - } + const char *str = thawer.get_str (); + if (str && (op->animation_id = find_animation (str))) + SET_FLAG (op, FLAG_ANIMATE); } - break; case KW_last_heal: thawer.get (op->last_heal); break; @@ -640,16 +793,13 @@ case KW_speed_left: thawer.get (op->speed_left); break; case KW_speed: - { - float speed; - thawer.get (speed); - op->set_speed (speed); - - //TODO: maybe do in check_object - if (!(map_flags & MAP_STYLE)) - if (op->speed < 0) - op->speed_left = op->speed_left - RANDOM () % 100 / 100.0; - } + thawer.get (op->speed); + + //TODO: maybe do in check_object + // removed check for style maps + if (op->speed < 0) + op->speed_left = op->speed_left - rndm (); + break; case KW_slow_move: @@ -708,7 +858,7 @@ case KW_magic: thawer.get (op->magic); break; case KW_state: thawer.get (op->state); break; case KW_move_slow_penalty: thawer.get (op->move_slow_penalty); break; - case KW_material: thawer.get (op->material); break; + case KW_material: thawer.get (op->materials); break; //TODO: nuke case KW_materialname: thawer.get (op->materialname); break; /* These are the new values */ @@ -812,16 +962,17 @@ break; case KW_friendly: - GET_FLAG (op, FLAG_FRIENDLY); - //TODO: move to check_object or so - if (op->type != PLAYER && QUERY_FLAG (op, FLAG_FRIENDLY)) - add_friendly_object (op); + if (thawer.get_sint32 ()) + if (op->type != PLAYER) + add_friendly_object (op); + break; case KW_monster: GET_FLAG (op, FLAG_MONSTER); break; case KW_neutral: GET_FLAG (op, FLAG_NEUTRAL); break; case KW_no_attack: GET_FLAG (op, FLAG_NO_ATTACK); break; case KW_no_damage: GET_FLAG (op, FLAG_NO_DAMAGE); break; + case KW_obj_original: GET_FLAG (op, FLAG_OBJ_ORIGINAL); break; case KW_generator: GET_FLAG (op, FLAG_GENERATOR); break; case KW_use_content_on_gen: GET_FLAG (op, FLAG_CONTENT_ON_GEN); break; case KW_is_thrown: GET_FLAG (op, FLAG_IS_THROWN); break; @@ -902,6 +1053,7 @@ case KW_one_hit: GET_FLAG (op, FLAG_ONE_HIT); break; case KW_berserk: GET_FLAG (op, FLAG_BERSERK); break; case KW_is_buildable: GET_FLAG (op, FLAG_IS_BUILDABLE); break; + case KW_destroy_on_death: GET_FLAG (op, FLAG_DESTROY_ON_DEATH); break; case KW_armour: thawer.get (op->resist[ATNR_PHYSICAL]); break; case KW_resist_physical: thawer.get (op->resist[ATNR_PHYSICAL]); break; @@ -978,33 +1130,29 @@ case KW_randomitems: op->randomitems = find_treasurelist (thawer.get_str ()); + //if (!op->randomitems) + // LOG (llevError, "%s uses unknown randomitems '%s'.\n", op->debug_desc (), thawer.get_str ()); break; case KW_msg: - { - thawer.get_ml (KW_endmsg, op->msg); - //TODO: allow longer messages - if (strlen (op->msg) >= HUGE_BUF) - { - LOG (llevDebug, "\n\tError message length >= %d: %d\n>%.80s<\n", HUGE_BUF, strlen (op->msg), &op->msg); - op->msg = "ERROR, please report: string too long, winged.\n"; - } - } + thawer.get_ml (KW_endmsg, op->msg); + //TODO: allow longer messages + if (strlen (op->msg) >= HUGE_BUF) + { + LOG (llevDebug, "\tError message length >= %d: %d\n>%.80s<\n", HUGE_BUF, strlen (op->msg), &op->msg); + op->msg = "ERROR, please report: string too long, winged.\n"; + } break; case KW_lore: - { - shstr msgbuf; - - thawer.get_ml (KW_endlore, op->lore); - //TODO: allow longer messages - /* Just print a warning so we can be reasonably safe - * about not overflowing the buffer. - */ - if (strlen (op->lore) > (HUGE_BUF / 2)) - LOG (llevDebug, "\n\tWarning lore length > %d (max allowed=%d): %d\n>%.80s<\n", - HUGE_BUF / 2, HUGE_BUF, strlen (op->lore), &op->lore); - } + thawer.get_ml (KW_endlore, op->lore); + //TODO: allow longer messages + /* Just print a warning so we can be reasonably safe + * about not overflowing the buffer. + */ + if (strlen (op->lore) > (HUGE_BUF / 2)) + LOG (llevDebug, "\tWarning lore length > %d (max allowed=%d): %d\n>%.80s<\n", + HUGE_BUF / 2, HUGE_BUF, strlen (op->lore), &op->lore); break; case KW_editable: @@ -1031,19 +1179,13 @@ return LL_EOF; case KW_ERROR: - set_ob_key_value (op, thawer.line, thawer.last_value, true); - //printf ("addkv(%s,%s)\n", thawer.line, thawer.get_str());//D - - //TODO: skip line, ugly, factor parsing better - if (thawer.last_value) - thawer.line = thawer.last_value; - - thawer.line += strlen (thawer.line) + 1; - + set_ob_key_value (op, thawer.last_keyword, thawer.last_value, true); + //fprintf (stderr, "addkv(%s,%s)\n", thawer.last_keyword, thawer.last_value);//D break; default: - LOG (llevError, "UNSUPPORTED KEYWORD IN MAP: \"%s\", bug in normaliser. skipping.\n", keyword_str[kw]); + if (!thawer.parse_error (kw, "object", op->name)) + return false; break; } } @@ -1066,6 +1208,8 @@ int load_object (object_thawer &fp, object *op, int map_flags) { + coroapi::cede_every (1000); // cede once in a while + return parse_object (op, fp, map_flags); } @@ -1273,7 +1417,7 @@ /* 100 */ KW_no_attack, KW_no_damage, - KW_NULL, + KW_obj_original, KW_NULL, KW_activate_on_push, KW_activate_on_release, @@ -1282,6 +1426,8 @@ KW_NULL, KW_is_buildable, /* 110 */ + KW_destroy_on_death, + KW_NULL, }; int i; @@ -1289,7 +1435,7 @@ /* This saves the key/value lists. We do it first so that any * keys that match field names will be overwritten by the loader. */ - for (key_value *my_field = op->key_values; my_field != NULL; my_field = my_field->next) + for (key_value *my_field = op->key_values; my_field; my_field = my_field->next) { /* Find the field in the opposing member. */ key_value *arch_field = get_ob_key_link (tmp, my_field->key); @@ -1383,7 +1529,7 @@ CMP_OUT (path_attuned); CMP_OUT (path_repelled); CMP_OUT (path_denied); - CMP_OUT (material); + CMP_OUT2 (material, materials);//TODO: nuke CMP_OUT (materialname); CMP_OUT (value); CMP_OUT (carrying); @@ -1467,6 +1613,6 @@ save_object (fp, tmp, flag); fp.put (op); - fprintf (fp, "end\n"); + fp.put (KW_end); }