--- deliantra/server/common/treasure.C 2007/04/16 15:41:26 1.45 +++ deliantra/server/common/treasure.C 2008/04/02 11:13:52 1.70 @@ -1,25 +1,24 @@ /* - * CrossFire, A Multiplayer game for X-windows + * This file is part of Deliantra, the Roguelike Realtime MMORPG. * - * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team - * Copyright (C) 2002 Mark Wedel & Crossfire Development Team - * Copyright (C) 1992 Frank Tore Johansen + * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Deliantra team + * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team + * Copyright (©) 1992,2007 Frank Tore Johansen * - * This program is free software; you can redistribute it and/or modify + * Deliantra 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 + * the Free Software Foundation, either version 3 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 + * 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 + * along with this program. If not, see . + * + * The authors can be reached via e-mail to */ /* TREASURE_DEBUG does some checking on the treasurelists after loading. @@ -56,28 +55,6 @@ static tl_map_t tl_map; /* - * Initialize global archtype pointers: - */ -void -init_archetype_pointers () -{ - int prev_warn = warn_archetypes; - - warn_archetypes = 1; - - if (ring_arch == NULL) - ring_arch = archetype::find ("ring"); - if (amulet_arch == NULL) - amulet_arch = archetype::find ("amulet"); - if (staff_arch == NULL) - staff_arch = archetype::find ("staff"); - if (crown_arch == NULL) - crown_arch = archetype::find ("crown"); - - warn_archetypes = prev_warn; -} - -/* * Searches for the given treasurelist */ treasurelist * @@ -130,29 +107,46 @@ tl->total_chance = 0; } +#ifdef TREASURE_DEBUG +/* recursived checks the linked list. Treasurelist is passed only + * so that the treasure name can be printed out + */ +static void +check_treasurelist (const treasure *t, const treasurelist * tl) +{ + if (t->chance >= 100 && t->next_yes && (t->next || t->next_no)) + LOG (llevError, "Treasurelist %s has element that has 100%% generation, next_yes field as well as next or next_no\n", &tl->name); + + if (t->next) + check_treasurelist (t->next, tl); + + if (t->next_yes) + check_treasurelist (t->next_yes, tl); + + if (t->next_no) + check_treasurelist (t->next_no, tl); +} +#endif + /* * Reads the lib/treasure file from disk, and parses the contents * into an internal treasure structure (very linked lists) */ static treasure * -load_treasure (object_thawer &f) +read_treasure (object_thawer &f) { treasure *t = new treasure; - int value; - nroftreasures++; + f.next (); for (;;) { - coroapi::cede_every (10); - - f.next (); + coroapi::cede_to_tick (); switch (f.kw) { case KW_arch: - if (!(t->item = archetype::find (f.get_str ()))) - LOG (llevError, "%s:%d treasure references unknown archetype '%s', skipping.\n", f.name, f.linenum, f.get_str ()); + t->item = archetype::get (f.get_str ()); break; case KW_list: f.get (t->name); break; @@ -163,124 +157,62 @@ case KW_nrof: f.get (t->nrof); break; case KW_magic: f.get (t->magic); break; - case KW_yes: t->next_yes = load_treasure (f); break; - case KW_no: t->next_no = load_treasure (f); break; + case KW_yes: t->next_yes = read_treasure (f); continue; + case KW_no: t->next_no = read_treasure (f); continue; case KW_end: + f.next (); return t; case KW_more: - t->next = load_treasure (f); + t->next = read_treasure (f); return t; default: - if (!f.parse_error ("treasure list")) - return t; // error + if (!f.parse_error ("treasurelist", t->name)) + return 0; return t; } - } -} - -#ifdef TREASURE_DEBUG -/* recursived checks the linked list. Treasurelist is passed only - * so that the treasure name can be printed out - */ -static void -check_treasurelist (const treasure *t, const treasurelist * tl) -{ - if (t->chance >= 100 && t->next_yes && (t->next || t->next_no)) - LOG (llevError, "Treasurelist %s has element that has 100%% generation, next_yes field as well as next or next_no\n", &tl->name); - - if (t->next) - check_treasurelist (t->next, tl); - if (t->next_yes) - check_treasurelist (t->next_yes, tl); - - if (t->next_no) - check_treasurelist (t->next_no, tl); + f.next (); + } } -#endif /* - * Opens LIBDIR/treasure and reads all treasure-declarations from it. * Each treasure is parsed with the help of load_treasure(). */ -bool -load_treasure_file (const char *filename) +treasurelist * +treasurelist::read (object_thawer &f) { - treasure *t; - int comp, line = 0; - - object_thawer f (filename); - - if (!f) - { - LOG (llevError, "Can't open treasure file.\n"); - return false; - } + assert (f.kw == KW_treasure || f.kw == KW_treasureone); - f.next (); + bool one = f.kw == KW_treasureone; + treasurelist *tl = treasurelist::get (f.get_str ()); + clear (tl); + tl->items = read_treasure (f); + if (!tl->items) + return 0; - for (;;) + /* This is a one of the many items on the list should be generated. + * Add up the chance total, and check to make sure the yes & no + * fields of the treasures are not being used. + */ + if (one) { - switch (f.kw) + for (treasure *t = tl->items; t; t = t->next) { - case KW_treasure: - case KW_treasureone: + if (t->next_yes || t->next_no) { - bool one = f.kw == KW_treasureone; - treasurelist *tl = treasurelist::get (f.get_str ()); - - clear (tl); - tl->items = load_treasure (f); - - if (!tl->items) - return false; - - /* This is a one of the many items on the list should be generated. - * Add up the chance total, and check to make sure the yes & no - * fields of the treasures are not being used. - */ - if (one) - { - for (t = tl->items; t; t = t->next) - { -#ifdef TREASURE_DEBUG - if (t->next_yes || t->next_no) - { - LOG (llevError, "Treasure %s is one item, but on treasure %s\n", &tl->name, t->item ? &t->item->name : &t->name); - LOG (llevError, " the next_yes or next_no field is set\n"); - } -#endif - tl->total_chance += t->chance; - } - } + LOG (llevError, "Treasure %s is one item, but on treasure %s\n", &tl->name, t->item ? &t->item->archname : &t->name); + LOG (llevError, " the next_yes or next_no field is set\n"); } - break; - case KW_EOF: -#ifdef TREASURE_DEBUG - /* Perform some checks on how valid the treasure data actually is. - * verify that list transitions work (ie, the list that it is supposed - * to transition to exists). Also, verify that at least the name - * or archetype is set for each treasure element. - */ - for (treasurelist *tl = first_treasurelist; tl; tl = tl->next) - check_treasurelist (tl->items, tl); -#endif - return true; - - default: - if (!f.parse_error ("treasure lists")) - return false; - - break; + tl->total_chance += t->chance; } - - f.next (); } + + return tl; } /* @@ -297,17 +229,29 @@ static void put_treasure (object *op, object *creator, int flags) { - object *tmp; - - /* Bit of a hack - spells should never be put onto the map. The entire - * treasure stuff is a problem - there is no clear idea of knowing - * this is the original object, or if this is an object that should be created - * by another object. - */ - if (flags & GT_ENVIRONMENT && op->type != SPELL) + if (flags & GT_ENVIRONMENT) { - SET_FLAG (op, FLAG_OBJ_ORIGINAL); - op->insert_at (creator, creator, INS_NO_MERGE | INS_NO_WALK_ON); + /* Bit of a hack - spells should never be put onto the map. The entire + * treasure stuff is a problem - there is no clear idea of knowing + * this is the original object, or if this is an object that should be created + * by another object. + */ + //TODO: flag such as objects... as such (no drop, anybody?) + if (op->type == SPELL) + { + op->destroy (); + return; + } + + op->expand_tail (); + + if (op->blocked (creator->map, creator->x, creator->y)) + op->destroy (); + else + { + SET_FLAG (op, FLAG_OBJ_ORIGINAL); + op->insert_at (creator, creator, INS_NO_MERGE | INS_NO_WALK_ON); + } } else { @@ -316,8 +260,9 @@ if ((flags & GT_APPLY) && QUERY_FLAG (creator, FLAG_MONSTER)) monster_check_apply (creator, op); - if ((flags & GT_UPDATE_INV) && (tmp = creator->in_player ())) - esrv_send_item (tmp, op); + if (flags & GT_UPDATE_INV) + if (object *tmp = creator->in_player ()) + esrv_send_item (tmp, op); } } @@ -349,11 +294,14 @@ if (t->name) { if (difficulty >= t->magic) - create_treasure (treasurelist::find (t->name), op, flag, difficulty, tries); + if (treasurelist *tl = treasurelist::find (t->name)) + create_treasure (tl, op, flag, difficulty, tries); + else + LOG (llevError, "create_all_treasures: undefined reference to treasurelist '%s'.\n", &t->name); } else { - if (t->item && (t->item->clone.invisible != 0 || !(flag & GT_INVISIBLE))) + if (t->item && (t->item->invisible != 0 || !(flag & GT_INVISIBLE))) { object *tmp = arch_to_object (t->item); @@ -410,7 +358,7 @@ else if (t->nrof) create_one_treasure (tl, op, flag, difficulty, tries); } - else if (t->item && (t->item->clone.invisible != 0 || flag != GT_INVISIBLE)) + else if (t->item && (t->item->invisible != 0 || flag != GT_INVISIBLE)) { if (object *tmp = arch_to_object (t->item)) { @@ -424,6 +372,12 @@ } } +void +object::create_treasure (treasurelist *tl, int flags) +{ + ::create_treasure (tl, this, flags, map ? map->difficulty : 0); +} + /* This calls the appropriate treasure creation function. tries is passed * to determine how many list transitions or attempts to create treasure * have been made. It is really in place to prevent infinite loops with @@ -444,6 +398,15 @@ return; } + if (op->flag [FLAG_TREASURE_ENV]) + { + // do not generate items when there already is something above the object + if (op->flag [FLAG_IS_FLOOR] && op->above) + return; + + flag |= GT_ENVIRONMENT; + } + if (tl->total_chance) create_one_treasure (tl, op, flag, difficulty, tries); else @@ -460,13 +423,13 @@ { difficulty = clamp (difficulty, 1, settings.max_level); - object *ob = object::create (), *tmp; + object *ob = object::create (); create_treasure (tl, ob, 0, difficulty, 0); /* Don't want to free the object we are about to return */ - tmp = ob->inv; - if (tmp != NULL) + object *tmp = ob->inv; + if (tmp) tmp->remove (); if (ob->inv) @@ -484,7 +447,6 @@ */ static int difftomagic_list[DIFFLEVELS][MAXMAGIC + 1] = { - // chance of magic difficulty // +0 +1 +2 +3 +4 {95, 2, 2, 1, 0}, // 1 @@ -520,34 +482,27 @@ { 0, 0, 0, 0, 100}, // 31 }; - /* calculate the appropriate level for wands staves and scrolls. * This code presumes that op has had its spell object created (in op->inv) * * elmex Wed Aug 9 17:44:59 CEST 2006: * Removed multiplicator, too many high-level items were generated on low-difficulty maps. */ - int level_for_item (const object *op, int difficulty) { - int olevel = 0; - if (!op->inv) { LOG (llevError, "level_for_item: Object %s has no inventory!\n", &op->name); return 0; } - olevel = (int) (op->inv->level + (double) difficulty * (1 - drand48 () * drand48 () * 2)); + int olevel = op->inv->level + int (difficulty * (1. - rndm () * rndm () * 2.)); if (olevel <= 0) - olevel = rndm (1, MIN (op->inv->level, 1)); - - if (olevel > MAXLEVEL) - olevel = MAXLEVEL; + olevel = rndm (1, op->inv->level); - return olevel; + return min (olevel, MAXLEVEL); } /* @@ -613,11 +568,11 @@ if (op->arch) { if (op->type == ARMOUR) - ARMOUR_SPEED (op) = (ARMOUR_SPEED (&op->arch->clone) * (100 + magic * 10)) / 100; + ARMOUR_SPEED (op) = (ARMOUR_SPEED (op->arch) * (100 + magic * 10)) / 100; if (magic < 0 && !(rndm (3))) /* You can't just check the weight always */ magic = (-magic); - op->weight = (op->arch->clone.weight * (100 - magic * 10)) / 100; + op->weight = (op->arch->weight * (100 - magic * 10)) / 100; } else { @@ -689,7 +644,7 @@ case 4: case 5: case 6: - set_attr_value (&op->stats, r, (signed char) (bonus + get_attr_value (&op->stats, r))); + op->stats.stat (r) += bonus; break; case 7: @@ -847,10 +802,7 @@ if (op->randomitems && op->type != SPELL) { - create_treasure (op->randomitems, op, flags, difficulty, 0); - if (!op->inv) - LOG (llevDebug, "fix_generated_item: Unable to generate treasure for %s\n", op->debug_desc ()); - + create_treasure (op->randomitems, op, flags & ~GT_ENVIRONMENT, difficulty, 0); /* So the treasure doesn't get created again */ op->randomitems = 0; } @@ -1178,7 +1130,7 @@ static artifactlist * get_empty_artifactlist (void) { - return salloc0 (); + return salloc0 (); } /* @@ -1187,7 +1139,7 @@ static artifact * get_empty_artifact (void) { - return salloc0 (); + return salloc0 (); } /* @@ -1268,10 +1220,10 @@ for (i = 0; i < depth; i++) fprintf (logfile, " "); - if (t->item && t->item->clone.type == FLESH) - fprintf (logfile, "%s's %s\n", name, &t->item->clone.name); + if (t->item && t->item->type == FLESH) + fprintf (logfile, "%s's %s\n", name, &t->item->object::name); else - fprintf (logfile, "%s\n", &t->item->clone.name); + fprintf (logfile, "%s\n", &t->item->object::name); } if (t->next_yes) @@ -1309,12 +1261,12 @@ found = 0; fprintf (logfile, "\n"); - for (at = first_archetype; at != NULL; at = at->next) - if (!strcasecmp (at->clone.name, name) && at->clone.title == NULL) + for_all_archetypes (at) + if (!strcasecmp (at->object::name, name) && at->title == NULL) { - fprintf (logfile, "treasures for %s (arch: %s)\n", &at->clone.name, &at->name); - if (at->clone.randomitems != NULL) - dump_monster_treasure_rec (at->clone.name, at->clone.randomitems->items, 1); + fprintf (logfile, "treasures for %s (arch: %s)\n", &at->object::name, &at->archname); + if (at->randomitems != NULL) + dump_monster_treasure_rec (at->object::name, at->randomitems->items, 1); else fprintf (logfile, "(nothing)\n"); @@ -1348,18 +1300,13 @@ if (!f) return; - f.next (); - for (;;) { switch (f.kw) { case KW_allowed: if (!art) - { - art = get_empty_artifact (); - nrofartifacts++; - } + art = get_empty_artifact (); { if (!strcmp (f.get_str (), "all")) @@ -1369,8 +1316,6 @@ do { - nrofallowedstr++; - if ((next = strchr (cp, ','))) *next++ = '\0'; @@ -1395,6 +1340,8 @@ case KW_object: { art->item = object::create (); + f.get (art->item->name); + f.next (); if (!art->item->parse_kv (f)) LOG (llevError, "Init_Artifacts: Could not load object.\n"); @@ -1430,6 +1377,8 @@ done: for (al = first_artifactlist; al; al = al->next) { + al->total_chance = 0; + for (art = al->items; art; art = art->next) { if (!art->chance) @@ -1445,12 +1394,10 @@ LOG (llevDebug, "done.\n"); } - /* * Used in artifact generation. The bonuses of the first object * is modified by the bonuses of the second object. */ - void add_abilities (object *op, object *change) { @@ -1465,7 +1412,7 @@ } for (i = 0; i < NUM_STATS; i++) - change_attr_value (&(op->stats), i, get_attr_value (&(change->stats), i)); + change_attr_value (&(op->stats), i, change->stats.stat (i)); op->attacktype |= change->attacktype; op->path_attuned |= change->path_attuned; @@ -1639,14 +1586,15 @@ } static int -legal_artifact_combination (object *op, artifact * art) +legal_artifact_combination (object *op, artifact *art) { int neg, success = 0; linked_char *tmp; const char *name; - if (art->allowed == (linked_char *) NULL) + if (!art->allowed) return 1; /* Ie, "all" */ + for (tmp = art->allowed; tmp; tmp = tmp->next) { #ifdef TREASURE_VERBOSE @@ -1658,7 +1606,7 @@ name = tmp->name, neg = 0; /* If we match name, then return the opposite of 'neg' */ - if (!strcmp (name, op->name) || (op->arch && !strcmp (name, op->arch->name))) + if (!strcmp (name, op->name) || (op->arch && !strcmp (name, op->arch->archname))) return !neg; /* Set success as true, since if the match was an inverse, it means @@ -1667,6 +1615,7 @@ else if (neg) success = 1; } + return success; } @@ -1813,7 +1762,6 @@ } /* special_potion() - so that old potion code is still done right. */ - int special_potion (object *op) {