--- deliantra/server/server/player.C 2006/09/16 22:24:13 1.25 +++ deliantra/server/server/player.C 2008/04/17 14:06:03 1.182 @@ -1,82 +1,38 @@ /* - 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 author can be reached via e-mail to -*/ + * This file is part of Deliantra, the Roguelike Realtime MMORPG. + * + * 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 + * + * 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 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 + * 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, see . + * + * The authors can be reached via e-mail to + */ #include -#ifndef WIN32 /* ---win32 remove headers */ -# include -#endif -#ifndef __CEXTRACT__ -# include -#endif +#include #include #include #include #include #include -#include -#ifdef COZY_SERVER -extern int same_party (partylist *a, partylist *b); -#endif +#include +#include -player * -find_player (const char *plname) -{ - player *pl; - - for (pl = first_player; pl != NULL; pl = pl->next) - { - if (pl->ob != NULL && !strcmp (query_name (pl->ob), plname)) - return pl; - }; - return NULL; -} - -player * -find_player_partial_name (const char *plname) -{ - player *pl; - player *found = NULL; - size_t namelen = strlen (plname); - - for (pl = first_player; pl != NULL; pl = pl->next) - { - if ((size_t) strlen (pl->ob->name) < namelen) - continue; - - if (!strcmp (pl->ob->name, plname)) - return pl; - - if (!strncasecmp (pl->ob->name, plname, namelen)) - { - if (found) - return NULL; - - found = pl; - } - } - return found; -} +playervec players; void display_motd (const object *op) @@ -89,18 +45,20 @@ sprintf (buf, "%s/%s", settings.confdir, settings.motd); if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL) - { - return; - } + return; + motd[0] = '\0'; size = 0; - while (fgets (buf, MAX_BUF, fp) != NULL) + + while (fgets (buf, MAX_BUF, fp)) { if (*buf == '#') continue; + strncat (motd + size, buf, HUGE_BUF - size); size += strlen (buf); } + draw_ext_info (NDI_UNIQUE | NDI_GREEN, 0, op, MSG_TYPE_MOTD, MSG_SUBTYPE_NONE, motd, NULL); close_and_delete (fp, comp); } @@ -116,23 +74,26 @@ sprintf (buf, "%s/%s", settings.confdir, settings.rules); if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL) - { - return; - } + return; + rules[0] = '\0'; size = 0; - while (fgets (buf, MAX_BUF, fp) != NULL) + + while (fgets (buf, MAX_BUF, fp)) { if (*buf == '#') continue; + if (size + strlen (buf) >= HUGE_BUF) { LOG (llevDebug, "Warning, rules size is > %d bytes.\n", HUGE_BUF); break; } + strncat (rules + size, buf, HUGE_BUF - size); size += strlen (buf); } + draw_ext_info (NDI_UNIQUE | NDI_GREEN, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_RULES, rules, NULL); close_and_delete (fp, comp); } @@ -150,17 +111,21 @@ sprintf (buf, "%s/%s", settings.confdir, settings.news); if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL) return; + news[0] = '\0'; subject[0] = '\0'; size = 0; - while (fgets (buf, MAX_BUF, fp) != NULL) + + while (fgets (buf, MAX_BUF, fp)) { if (*buf == '#') continue; + if (*buf == '%') { /* send one news */ if (size > 0) - draw_ext_info_format (NDI_UNIQUE | NDI_GREEN, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS, "INFORMATION: %s\n%s", "%s\n%s", subject, news); /*send previously read news */ + draw_ext_info_format (NDI_UNIQUE | NDI_GREEN, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS, "INFORMATION: %s\n%s", (char *)"%s\n%s", subject, news); /*send previously read news */ + strcpy (subject, buf + 1); strip_endline (subject); size = 0; @@ -179,175 +144,258 @@ } draw_ext_info_format (NDI_UNIQUE | NDI_GREEN, 0, op, - MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS, "INFORMATION: %s\n%s\n", "%s\n%s", subject, news); + MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS, "INFORMATION: %s\n%s\n", (char *)"%s\n%s", subject, news); close_and_delete (fp, comp); } -int -playername_ok (const char *cp) +/* This loads the first map an puts the player on it. */ +static void +set_first_map (object *op) { - /* Don't allow - or _ as first character in the name */ - if (*cp == '-' || *cp == '_') - return 0; + op->contr->maplevel = first_map_path; + op->x = -1; + op->y = -1; +} - for (; *cp != '\0'; cp++) - if (!((*cp >= 'a' && *cp <= 'z') || (*cp >= 'A' && *cp <= 'Z')) && *cp != '-' && *cp != '_') - return 0; - return 1; +void +player::activate () +{ + if (active) + return; + + players.insert (this); + ob->remove (); + ob->map = 0; + ob->activate_recursive (); + CLEAR_FLAG (ob, FLAG_FRIENDLY); + add_friendly_object (ob); } -/* This no longer sets the player map. Also, it now updates - * all the pointers so the caller doesn't need to do that. - * Caller is responsible for setting the correct map. - */ +void +player::deactivate () +{ + if (!active) + return; -/* Redo this to do both get_player_ob and get_player. - * Hopefully this will be less bugfree and simpler. - * Returns the player structure. If 'p' is null, - * we create a new one. Otherwise, we recycle - * the one that is passed. - */ -static player * -get_player (player *p) + terminate_all_pets (ob); + remove_friendly_object (ob); + ob->deactivate_recursive (); + + if (ob->map) + maplevel = ob->map->path; + + ob->remove (); + ob->enemy = 0; // sometimes keeps an extra refcount on itself + ob->map = 0; + party = 0; + + combat_ob = ranged_ob = 0; //TODO, should be special marker, non-refcounted, not this + + players.erase (this); +} + +// connect the player with a specific client +// also changes, rationalises, and fixes some incorrect settings +void +player::connect (client *ns) { - object *op = arch_to_object (get_player_archetype (NULL)); - int i; + this->ns = ns; + ns->pl = this; + + run_on = 0; + fire_on = 0; + ob->close_container (); //TODO: client-specific + + ns->update_look = 0; + ns->look_position = 0; + + clear_los (this); + + ns->reset_stats (); - if (!p) + /* make sure he's a player -- needed because of class change. */ + ob->type = PLAYER; // we are paranoid + ob->race = ob->arch->race; + + ob->carrying = sum_weight (ob); + link_player_skills (ob); + + CLEAR_FLAG (ob, FLAG_NO_FIX_PLAYER); + + assign (title, ob->arch->object::name); + + /* if it's a dragon player, set the correct title here */ + if (is_dragon_pl (ob)) { - p = new player; + object *tmp, *abil = 0, *skin = 0; - /* This adds the player in the linked list. There is extra - * complexity here because we want to add the new player at the - * end of the list - there is in fact no compelling reason that - * that needs to be done except for things like output of - * 'who'. - */ - player *tmp = first_player; + for (tmp = ob->inv; tmp; tmp = tmp->below) + if (tmp->type == FORCE) + if (tmp->arch->archname == shstr_dragon_ability_force) + abil = tmp; + else if (tmp->arch->archname == shstr_dragon_skin_force) + skin = tmp; - while (tmp != NULL && tmp->next != NULL) - tmp = tmp->next; - if (tmp != NULL) - tmp->next = p; - else - first_player = p; + set_dragon_name (ob, abil, skin); + } - p->next = NULL; + new_draw_info (NDI_UNIQUE, 0, ob, "Welcome Back!"); + + esrv_new_player (this, ob->weight + ob->carrying); + + ob->update_stats (); + + ns->floorbox_update (); + esrv_send_inventory (ob, ob); + esrv_add_spells (this, 0); + + activate (); + + send_rules (ob); + send_news (ob); + display_motd (ob); + + INVOKE_PLAYER (CONNECT, this); + INVOKE_PLAYER (LOGIN, this); +} + +void +player::disconnect () +{ + if (ob) + { + ob->close_container (); //TODO: client-specific + ob->drop_unpaid_items (); } - /* Clears basically the entire player structure except - * for next and socket. - */ - p->clear (); + if (ns) + { + if (active) + INVOKE_PLAYER (LOGOUT, this, ARG_INT (0)); + + INVOKE_PLAYER (DISCONNECT, this); + + ns->reset_stats (); + ns->pl = 0; + ns = 0; + } + + observe = ob; + + deactivate (); +} + +// the need for this function can be explained +// by load_object not returning the object +void +player::set_object (object *op) +{ + ob = observe = op; + ob->contr = this; /* this aren't yet in archetype */ + + ob->speed = 1.0f; + ob->speed_left = 0.5f; + + ob->direction = 5; /* So player faces south */ + + ob->flag [FLAG_READY_WEAPON] = false; + ob->flag [FLAG_READY_SKILL] = false; + ob->flag [FLAG_READY_BOW] = false; + + for (object *op = ob->inv; op; op = op->below) + if (op->flag [FLAG_APPLIED]) + switch (op->type) + { + case SKILL: + ob->flag [FLAG_APPLIED] = false; + break; - /* There are some elements we want initialized to non zero value - + case WAND: + case ROD: + case HORN: + case BOW: + ranged_ob = op; + break; + + case WEAPON: + combat_ob = op; + break; + } + + ob->change_weapon (combat_ob ? combat_ob : ranged_ob); + ob->deactivate (); // change_Weapon activates, fix this better +} + +void +player::set_observe (object *op) +{ + observe = op ? op : ob; + do_los = 1; +} + +player::player () +{ + /* There are some elements we want initialised to non zero value - * we deal with that below this point. */ - p->party = NULL; - p->outputs_sync = 16; /* Every 2 seconds */ - p->outputs_count = 8; /* Keeps present behaviour */ - p->unapply = unapply_nochoice; - p->Swap_First = -1; + outputs_sync = 4; + outputs_count = 4; + unapply = unapply_nochoice; -#ifdef AUTOSAVE - p->last_save_tick = 9999999; -#endif + savebed_map = first_map_path; /* Init. respawn position */ - strcpy (p->savebed_map, first_map_path); /* Init. respawn position */ + gen_sp_armour = 10; + bowtype = bow_normal; + petmode = pet_normal; + listening = 10; + usekeys = containers; + peaceful = 1; /* default peaceful */ + do_los = 1; - op->contr = p; /* this aren't yet in archetype */ - p->ob = op; - op->speed_left = 0.5; - op->speed = 1.0; - op->direction = 5; /* So player faces south */ - op->stats.wc = 2; - op->run_away = 25; /* Then we panick... */ - p->socket.monitor_spells = 0; /* Needed because esrv_update_spells( ) gets called by roll_stats */ - - roll_stats (op); - p->state = ST_ROLL_STAT; - clear_los (op); - - p->gen_sp_armour = 10; - p->last_speed = -1; - p->shoottype = range_none; - p->bowtype = bow_normal; - p->petmode = pet_normal; - p->listening = 10; - p->usekeys = containers; - p->last_weapon_sp = -1; - p->peaceful = 1; /* default peaceful */ - p->do_los = 1; - p->explore = 0; - p->no_shout = 0; /* default can shout */ - - assign (p->title, op->arch->clone.name); - op->race = op->arch->clone.race; - - CLEAR_FLAG (op, FLAG_READY_SKILL); - - /* we need to clear these to -1 and not zero - otherwise, - * if a player quits and starts a new character, we wont - * send new values to the client, as things like exp start - * at zero. - */ - for (i = 0; i < NUM_SKILLS; i++) - { - p->last_skill_exp[i] = -1; - p->last_skill_ob[i] = NULL; - } - for (i = 0; i < NROFATTACKS; i++) - { - p->last_resist[i] = -1; - } - p->last_stats.exp = -1; - p->last_weight = (uint32) - 1; - - p->socket.update_look = 0; - p->socket.look_position = 0; - return p; + weapon_sp = 1.0f; + weapon_sp_left = 0.5f; } -/* This loads the first map an puts the player on it. */ -static void -set_first_map (object *op) +void +player::do_destroy () { - strcpy (op->contr->maplevel, first_map_path); - op->x = -1; - op->y = -1; - enter_exit (op, NULL); + disconnect (); + + attachable::do_destroy (); + + if (ob) + { + ob->destroy_inv (false); + ob->destroy (); + } + + ob = observe = 0; } -/* Tries to add player on the connection passwd in ns. +player::~player () +{ + /* Clear item stack */ + free (stack_items); +} + +/* Tries to add player on the connection passed in ns. * All we can really get in this is some settings like host and display * mode. */ - -int -add_player (NewSocket * ns) +player * +player::create () { - player *p; + player *pl = new player; - p = get_player (NULL); - p->socket = *ns; - p->socket.faces_sent = (uint8 *) malloc (p->socket.faces_sent_len * sizeof (*p->socket.faces_sent)); - if (p->socket.faces_sent == NULL) - fatal (OUT_OF_MEMORY); - memcpy (p->socket.faces_sent, ns->faces_sent, p->socket.faces_sent_len * sizeof (*p->socket.faces_sent)); - /* Needed because the socket we just copied over needs to be cleared. - * Note that this can result in a client reset if there is partial data - * on the uncoming socket. - */ - p->socket.inbuf.len = 0; - set_first_map (p->ob); - - CLEAR_FLAG (p->ob, FLAG_FRIENDLY); - add_friendly_object (p->ob); - send_rules (p->ob); - send_news (p->ob); - display_motd (p->ob); - get_name (p->ob); - return 0; + pl->set_object (arch_to_object (get_player_archetype (0))); + + pl->ob->roll_stats (); + pl->ob->stats.wc = 2; + pl->ob->run_away = 25; /* Then we panick... */ + + set_first_map (pl->ob); + + return pl; } /* @@ -358,62 +406,30 @@ archetype * get_player_archetype (archetype *at) { - archetype *start = at; + archvec::iterator i = at ? archetypes.find (at) : archetypes.begin (); for (;;) { - if (at == NULL || at->next == NULL) - at = first_archetype; - else - at = at->next; - if (at->clone.type == PLAYER) - return at; - if (at == start) - { - LOG (llevError, "No Player archetypes\n"); - exit (-1); - } + if (++i == archetypes.end ()) + i = archetypes.begin (); + else if (*i == at) + cleanup ("not a single player archetype found"); + + if ((*i)->type == PLAYER) + return *i; } } - object * get_nearest_player (object *mon) { object *op = NULL; - player *pl = NULL; objectlink *ol; unsigned lastdist; rv_vector rv; - for (ol = first_friendly_object, lastdist = 1000; ol != NULL; ol = ol->next) + for (ol = first_friendly_object, lastdist = 1000; ol; ol = ol->next) { - /* We should not find free objects on this friendly list, but it - * does periodically happen. Given that, lets deal with it. - * While unlikely, it is possible the next object on the friendly - * list is also free, so encapsulate this in a while loop. - */ - while (QUERY_FLAG (ol->ob, FLAG_FREED) || !QUERY_FLAG (ol->ob, FLAG_FRIENDLY)) - { - object *tmp = ol->ob; - - /* Can't do much more other than log the fact, because the object - * itself will have been cleared. - */ - LOG (llevDebug, "get_nearest_player: Found free/non friendly object on friendly list\n"); - ol = ol->next; - remove_friendly_object (tmp); - if (!ol) - return op; - } - - /* Remove special check for player from this. First, it looks to cause - * some crashes (ol->ob->contr not set properly?), but secondly, a more - * complicated method of state checking would be needed in any case - - * as it was, a clever player could type quit, and the function would - * skip them over while waiting for confirmation. Remove - * on_same_map check, as can_detect_enemy also does this - */ if (!can_detect_enemy (mon, ol->ob, &rv)) continue; @@ -423,18 +439,15 @@ lastdist = rv.distance; } } - for (pl = first_player; pl != NULL; pl = pl->next) - { - if (can_detect_enemy (mon, pl->ob, &rv)) - { - if (lastdist > rv.distance) - { - op = pl->ob; - lastdist = rv.distance; - } + for_all_players (pl) + if (can_detect_enemy (mon, pl->ob, &rv)) + if (lastdist > rv.distance) + { + op = pl->ob; + lastdist = rv.distance; } - } + #if 0 LOG (llevDebug, "get_nearest_player() finds player: %s\n", op ? &op->name : "(null)"); #endif @@ -463,7 +476,6 @@ */ #define MAX_SPACES 50 - /* * Returns the direction to the player, if valid. Returns 0 otherwise. * modified to verify there is a path to the player. Does this by stepping towards @@ -503,10 +515,12 @@ m = mon->map; dir = rv.direction; lastdir = firstdir = rv.direction; /* perhaps we stand next to pl, init firstdir too */ - diff = FABS (rv.distance_x) > FABS (rv.distance_y) ? FABS (rv.distance_x) : FABS (rv.distance_y); + diff = ::max (abs (rv.distance_x), abs (rv.distance_y)); + /* If we can't solve it within the search distance, return now. */ if (diff > max) return 0; + while (diff > 1 && max > 0) { lastx = x; @@ -598,17 +612,20 @@ if (!firstdir) firstdir = dir; } + if (diff <= 1) { /* Recalculate diff (distance) because we may not have actually * headed toward player for entire distance. */ get_rangevector_from_mapcoord (m, x, y, pl, &rv, 0); - diff = FABS (rv.distance_x) > FABS (rv.distance_y) ? FABS (rv.distance_x) : FABS (rv.distance_y); + diff = ::max (abs (rv.distance_x), abs (rv.distance_y)); } + if (diff > max) return 0; } + /* If we reached the max, didn't find a direction in time */ if (!max) return 0; @@ -617,14 +634,12 @@ } void -give_initial_items (object *pl, treasurelist * items) +give_initial_items (object *pl, treasurelist *items) { - object *op, *next = NULL; - - if (pl->randomitems != NULL) + if (pl->randomitems) create_treasure (items, pl, GT_STARTEQUIP | GT_ONLY_GOOD, 1, 0); - for (op = pl->inv; op; op = next) + for (object *next, *op = pl->inv; op; op = next) { next = op->below; @@ -639,14 +654,15 @@ */ if (pl->type == PLAYER) { - if ((!QUERY_FLAG (pl, FLAG_USE_ARMOUR) && - (op->type == ARMOUR || op->type == BOOTS || - op->type == CLOAK || op->type == HELMET || - op->type == SHIELD || op->type == GLOVES || - op->type == BRACERS || op->type == GIRDLE)) || (!QUERY_FLAG (pl, FLAG_USE_WEAPON) && op->type == WEAPON)) + if ((!QUERY_FLAG (pl, FLAG_USE_ARMOUR) + && + (op->type == ARMOUR || op->type == BOOTS + || op->type == CLOAK || op->type == HELMET + || op->type == SHIELD || op->type == GLOVES + || op->type == BRACERS || op->type == GIRDLE)) + || (!QUERY_FLAG (pl, FLAG_USE_WEAPON) && op->type == WEAPON)) { - remove_ob (op); - free_object (op); + op->destroy (); continue; } } @@ -667,22 +683,20 @@ if (tmp) { - remove_ob (op); - free_object (op); + op->destroy (); LOG (llevError, "give_initial_items: Removing duplicate object %s\n", &tmp->name); continue; } + if (op->nrof > 1) op->nrof = 1; } if (op->type == SPELLBOOK && op->inv) - { - CLEAR_FLAG (op->inv, FLAG_STARTEQUIP); - } + CLEAR_FLAG (op->inv, FLAG_STARTEQUIP); /* Give starting characters identified, uncursed, and undamned - * items. Just don't identify gold or silver, or it won't be + * items. Just don't identify gold or silver, or it won't be * merged properly. */ if (need_identify (op)) @@ -691,10 +705,10 @@ CLEAR_FLAG (op, FLAG_CURSED); CLEAR_FLAG (op, FLAG_DAMNED); } + if (op->type == SPELL) { - remove_ob (op); - free_object (op); + op->destroy (); continue; } else if (op->type == SKILL) @@ -703,8 +717,7 @@ op->stats.exp = 0; op->level = 1; } - /* lock all 'normal items by default */ - else + else /* lock all 'normal items by default */ SET_FLAG (op, FLAG_INV_LOCKED); } /* for loop of objects in player inv */ @@ -713,89 +726,6 @@ } void -get_name (object *op) -{ - op->contr->write_buf[0] = '\0'; - op->contr->state = ST_GET_NAME; - send_query (&op->contr->socket, 0, "What is your name?\n:"); -} - -void -get_password (object *op) -{ - op->contr->write_buf[0] = '\0'; - op->contr->state = ST_GET_PASSWORD; - send_query (&op->contr->socket, CS_QUERY_HIDEINPUT, "What is your password?\n:"); -} - -void -play_again (object *op) -{ - op->contr->state = ST_PLAY_AGAIN; - op->chosen_skill = NULL; - send_query (&op->contr->socket, CS_QUERY_SINGLECHAR, "Do you want to play again (a/q)?"); - /* a bit of a hack, but there are various places early in th - * player creation process that a user can quit (eg, roll - * stats) that isn't removing the player. Taking a quick - * look, there are many places that call play_again without - * removing the player - it probably makes more sense - * to leave it to play_again to remove the object in all - * cases. - */ - if (!QUERY_FLAG (op, FLAG_REMOVED)) - remove_ob (op); - /* Need to set this to null - otherwise, it could point to garbage, - * and draw() doesn't check to see if the player is removed, only if - * the map is null or not swapped out. - */ - op->map = NULL; -} - -int -receive_play_again (object *op, char key) -{ - if (key == 'q' || key == 'Q') - { - remove_friendly_object (op); - leave (op->contr, 0); /* ericserver will draw the message */ - return 2; - } - else if (key == 'a' || key == 'A') - { - player *pl = op->contr; - shstr name = op->name; - - op->contr = 0; - op->type = 0; - op->free (1); - pl = get_player (pl); - op = pl->ob; - add_friendly_object (op); - op->contr->password[0] = '~'; - op->name = op->name_pl = 0; - /* Lets put a space in here */ - new_draw_info (NDI_UNIQUE, 0, op, "\n"); - get_name (op); - op->name = op->name_pl = name; - set_first_map (op); - } - else - /* user pressed something else so just ask again... */ - play_again (op); - - return 0; -} - -void -confirm_password (object *op) -{ - - op->contr->write_buf[0] = '\0'; - op->contr->state = ST_CONFIRM_PASSWORD; - send_query (&op->contr->socket, CS_QUERY_HIDEINPUT, "Please type your password again.\n:"); -} - -void get_party_password (object *op, partylist *party) { if (party == NULL) @@ -803,240 +733,108 @@ LOG (llevError, "get_party_password(): tried to make player %s join a NULL party", &op->name); return; } + op->contr->write_buf[0] = '\0'; - op->contr->state = ST_GET_PARTY_PASSWORD; + op->contr->ns->state = ST_GET_PARTY_PASSWORD; op->contr->party_to_join = party; - send_query (&op->contr->socket, CS_QUERY_HIDEINPUT, "What is the password?\n:"); + send_query (op->contr->ns, CS_QUERY_HIDEINPUT, "What is the password?\n:"); } - /* This rolls four 1-6 rolls and sums the best 3 of the 4. */ -int +static int roll_stat (void) { int a[4], i, j, k; for (i = 0; i < 4; i++) - a[i] = (int) RANDOM () % 6 + 1; + a[i] = (int) rndm (6) + 1; for (i = 0, j = 0, k = 7; i < 4; i++) if (a[i] < k) k = a[i], j = i; for (i = 0, k = 0; i < 4; i++) - { - if (i != j) - k += a[i]; - } + if (i != j) + k += a[i]; + return k; } void -roll_stats (object *op) +object::roll_stats () { - int sum = 0; - int i = 0, j = 0; - int statsort[7]; + int statsort [NUM_STATS]; - do + for (;;) { - op->stats.Str = roll_stat (); - op->stats.Dex = roll_stat (); - op->stats.Int = roll_stat (); - op->stats.Con = roll_stat (); - op->stats.Wis = roll_stat (); - op->stats.Pow = roll_stat (); - op->stats.Cha = roll_stat (); - sum = op->stats.Str + op->stats.Dex + op->stats.Int + op->stats.Con + op->stats.Wis + op->stats.Pow + op->stats.Cha; - } - while (sum < 82 || sum > 116); - - /* Sort the stats so that rerolling is easier... */ - statsort[0] = op->stats.Str; - statsort[1] = op->stats.Dex; - statsort[2] = op->stats.Int; - statsort[3] = op->stats.Con; - statsort[4] = op->stats.Wis; - statsort[5] = op->stats.Pow; - statsort[6] = op->stats.Cha; + int sum = 0; + for (int i = NUM_STATS; i--; ) + sum += statsort [i] = roll_stat (); - /* a quick and dirty bubblesort? */ - do - { - if (statsort[i] < statsort[i + 1]) - { - j = statsort[i]; - statsort[i] = statsort[i + 1]; - statsort[i + 1] = j; - i = 0; - } - else - { - i++; - } + if (sum >= 82 && sum <= 116) + break; } - while (i < 6); - op->stats.Str = statsort[0]; - op->stats.Dex = statsort[1]; - op->stats.Con = statsort[2]; - op->stats.Int = statsort[3]; - op->stats.Wis = statsort[4]; - op->stats.Pow = statsort[5]; - op->stats.Cha = statsort[6]; + // Sort the stats so that rerolling is easier... + std::sort (statsort, statsort + NUM_STATS, std::greater()); + for (int i = 0; i < NUM_STATS; ++i) + stats.stat (i) = statsort [i]; - op->contr->orig_stats.Str = op->stats.Str; - op->contr->orig_stats.Dex = op->stats.Dex; - op->contr->orig_stats.Int = op->stats.Int; - op->contr->orig_stats.Con = op->stats.Con; - op->contr->orig_stats.Wis = op->stats.Wis; - op->contr->orig_stats.Pow = op->stats.Pow; - op->contr->orig_stats.Cha = op->stats.Cha; + stats.exp = 0; + stats.ac = 0; - op->level = 1; - op->stats.exp = 0; - op->stats.ac = 0; + stats.hp = stats.maxhp; + stats.sp = stats.maxsp; + stats.grace = stats.maxgrace; - op->contr->levhp[1] = 9; - op->contr->levsp[1] = 6; - op->contr->levgrace[1] = 3; - - fix_player (op); - op->stats.hp = op->stats.maxhp; - op->stats.sp = op->stats.maxsp; - op->stats.grace = op->stats.maxgrace; - op->contr->orig_stats = op->stats; -} + if (contr) + { + contr->levhp[1] = 9; + contr->levsp[1] = 6; + contr->levgrace[1] = 3; -void -Roll_Again (object *op) -{ - esrv_new_player (op->contr, 0); - send_query (&op->contr->socket, CS_QUERY_SINGLECHAR, - "[y] to roll new stats [n] to use stats\n[1-7] [1-7] to swap stats.\nRoll again (y/n/1-7)? "); + contr->orig_stats = stats; + } } void -Swap_Stat (object *op, int Swap_Second) +object::swap_stats (int a, int b) { - signed char tmp; - char buf[MAX_BUF]; + swap (contr->orig_stats.stat (a), contr->orig_stats.stat (b)); - if (op->contr->Swap_First == -1) - { - new_draw_info (NDI_UNIQUE, 0, op, "How the hell did you get here?!?!!!"); - new_draw_info (NDI_UNIQUE, 0, op, "Error in Swap_Stat code,"); - new_draw_info (NDI_UNIQUE, 0, op, "mail korg@rdt.monash.edu.au"); - return; - } + for (int i = 0; i < NUM_STATS; ++i) + stats.stat (i) = contr->orig_stats.stat (i); - tmp = get_attr_value (&op->contr->orig_stats, op->contr->Swap_First); + //TODO: the following code looks so borked and should, at the very least, + // be merged with the similar code in roll_stats + stats.ac = 0; - set_attr_value (&op->contr->orig_stats, op->contr->Swap_First, get_attr_value (&op->contr->orig_stats, Swap_Second)); + level = 1; + stats.exp = 0; + stats.ac = 0; - set_attr_value (&op->contr->orig_stats, Swap_Second, tmp); + stats.hp = stats.maxhp; + stats.sp = stats.maxsp; + stats.grace = stats.maxgrace; - sprintf (buf, "%s done\n", short_stat_name[Swap_Second]); - new_draw_info (NDI_UNIQUE, 0, op, buf); - op->stats.Str = op->contr->orig_stats.Str; - op->stats.Dex = op->contr->orig_stats.Dex; - op->stats.Con = op->contr->orig_stats.Con; - op->stats.Int = op->contr->orig_stats.Int; - op->stats.Wis = op->contr->orig_stats.Wis; - op->stats.Pow = op->contr->orig_stats.Pow; - op->stats.Cha = op->contr->orig_stats.Cha; - op->stats.ac = 0; - - op->level = 1; - op->stats.exp = 0; - op->stats.ac = 0; - - op->contr->levhp[1] = 9; - op->contr->levsp[1] = 6; - op->contr->levgrace[1] = 3; + if (contr) + { + contr->levhp[1] = 9; + contr->levsp[1] = 6; + contr->levgrace[1] = 3; - fix_player (op); - op->stats.hp = op->stats.maxhp; - op->stats.sp = op->stats.maxsp; - op->stats.grace = op->stats.maxgrace; - op->contr->orig_stats = op->stats; - op->contr->Swap_First = -1; + contr->orig_stats = stats; + } } - -/* This code has been greatly reduced, because with set_attr_value - * and get_attr_value, the stats can be accessed just numeric - * ids. stat_trans is a table that translate the number entered - * into the actual stat. It is needed because the order the stats - * are displayed in the stat window is not the same as how - * the number's access that stat. The table does that translation. - */ -int -key_roll_stat (object *op, char key) +static void +start_info (object *op) { - int keynum = key - '0'; char buf[MAX_BUF]; - static sint8 stat_trans[] = { -1, STR, DEX, CON, INT, WIS, POW, CHA }; - if (keynum > 0 && keynum <= 7) - { - if (op->contr->Swap_First == -1) - { - op->contr->Swap_First = stat_trans[keynum]; - sprintf (buf, "%s ->", short_stat_name[stat_trans[keynum]]); - new_draw_info (NDI_UNIQUE, 0, op, buf); - } - else - Swap_Stat (op, stat_trans[keynum]); - - send_query (&op->contr->socket, CS_QUERY_SINGLECHAR, ""); - return 1; - } - switch (key) - { - case 'n': - case 'N': - { - SET_FLAG (op, FLAG_WIZ); - if (op->map == NULL) - { - LOG (llevError, "Map == NULL in state 2\n"); - break; - } - -#if 0 - /* So that enter_exit will put us at startx/starty */ - op->x = -1; - - enter_exit (op, NULL); -#endif - SET_ANIMATION (op, 2); /* So player faces south */ - /* Enter exit adds a player otherwise */ - add_statbonus (op); - send_query (&op->contr->socket, CS_QUERY_SINGLECHAR, - "Now choose a character.\nPress any key to change outlook.\nPress `d' when you're pleased.\n"); - op->contr->state = ST_CHANGE_CLASS; - if (op->msg) - new_draw_info (NDI_BLUE, 0, op, op->msg); - return 0; - } - case 'y': - case 'Y': - roll_stats (op); - send_query (&op->contr->socket, CS_QUERY_SINGLECHAR, ""); - return 1; - - case 'q': - case 'Q': - play_again (op); - return 1; - - default: - send_query (&op->contr->socket, CS_QUERY_SINGLECHAR, "Yes, No, Quit or 1-6. Roll again?"); - return 0; - } - return 0; + sprintf (buf, "Welcome to Deliantra v%s!", VERSION); + new_draw_info (NDI_UNIQUE, 0, op, buf); } /* This function takes the key that is passed, and does the @@ -1045,162 +843,96 @@ * separate race and class; this actually changes the RACE, * not the class. */ - -int -key_change_class (object *op, char key) +void +player::chargen_race_done () { - int tmp_loop; + /* this must before then initial items are given */ + esrv_new_player (ob->contr, ob->weight + ob->carrying); - if (key == 'q' || key == 'Q') - { - remove_ob (op); - play_again (op); - return 0; - } - if (key == 'd' || key == 'D') - { - char buf[MAX_BUF]; + treasurelist *tl = treasurelist::find ("starting_wealth"); + if (tl) + create_treasure (tl, ob, 0, 0, 0); - /* this must before then initial items are given */ - esrv_new_player (op->contr, op->weight + op->carrying); - create_treasure (find_treasurelist ("starting_wealth"), op, 0, 0, 0); + INVOKE_PLAYER (BIRTH, ob->contr); + INVOKE_PLAYER (LOGIN, ob->contr); - INVOKE_PLAYER (BIRTH, op->contr); - INVOKE_PLAYER (LOGIN, op->contr); + ob->contr->ns->state = ST_PLAYING; - op->contr->state = ST_PLAYING; + if (ob->msg) + ob->msg = 0; - if (op->msg) - op->msg = NULL; - - /* We create this now because some of the unique maps will need it - * to save here. - */ - sprintf (buf, "%s/%s/%s", settings.localdir, settings.playerdir, &op->name); - make_path_to_file (buf); - -#ifdef AUTOSAVE - op->contr->last_save_tick = pticks; -#endif - start_info (op); - CLEAR_FLAG (op, FLAG_WIZ); - give_initial_items (op, op->randomitems); - link_player_skills (op); - esrv_send_inventory (op, op); - fix_player (op); - - /* This moves the player to a different start map, if there - * is one for this race - */ - if (*first_map_ext_path) - { - object *tmp; - char mapname[MAX_BUF]; - - snprintf (mapname, MAX_BUF - 1, "%s/%s", first_map_ext_path, &op->arch->name); - tmp = get_object (); - EXIT_PATH (tmp) = mapname; - EXIT_X (tmp) = op->x; - EXIT_Y (tmp) = op->y; - enter_exit (op, tmp); /* we don't really care if it succeeded; - * if the map isn't there, then stay on the - * default initial map */ - free_object (tmp); - } - else - { - LOG (llevDebug, "first_map_ext_path not set\n"); - } - return 0; - } - - /* Following actually changes the race - this is the default command - * if we don't match with one of the options above. + /* We create this now because some of the unique maps will need it + * to save here. */ + { + char buf[MAX_BUF]; + sprintf (buf, "%s/%s/%s", settings.localdir, settings.playerdir, &ob->name); + make_path_to_file (buf); + } + + start_info (ob); + CLEAR_FLAG (ob, FLAG_WIZ); + give_initial_items (ob, ob->randomitems); + link_player_skills (ob); + esrv_send_inventory (ob, ob); + ob->update_stats (); - tmp_loop = 0; - while (!tmp_loop) + /* This moves the player to a different start map, if there + * is one for this race + */ + if (*first_map_ext_path) { - shstr name = op->name; - int x = op->x, y = op->y; - - remove_statbonus (op); - remove_ob (op); - op->arch = get_player_archetype (op->arch); - copy_object (&op->arch->clone, op); - op->instantiate (); - op->stats = op->contr->orig_stats; - op->name = op->name_pl = name; - op->x = x; - op->y = y; - SET_ANIMATION (op, 2); /* So player faces south */ - insert_ob_in_map (op, op->map, op, 0); - assign (op->contr->title, op->arch->clone.name); - add_statbonus (op); - tmp_loop = allowed_class (op); - } - - update_object (op, UP_OBJ_FACE); - esrv_update_item (UPD_FACE, op, op); - fix_player (op); - op->stats.hp = op->stats.maxhp; - op->stats.sp = op->stats.maxsp; - op->stats.grace = 0; + object *tmp; + char mapname[MAX_BUF]; - if (op->msg) - new_draw_info (NDI_BLUE, 0, op, op->msg); - - send_query (&op->contr->socket, CS_QUERY_SINGLECHAR, "Press any key for the next race.\nPress `d' to play this race.\n"); - return 0; + snprintf (mapname, MAX_BUF - 1, "%s/%s", &first_map_ext_path, &ob->arch->archname); + tmp = object::create (); + EXIT_PATH (tmp) = mapname; + EXIT_X (tmp) = ob->x; + EXIT_Y (tmp) = ob->y; + ob->enter_exit (tmp); /* we don't really care if it succeeded; + * if the map isn't there, then stay on the + * default initial map */ + tmp->destroy (); + } + else + LOG (llevDebug, "first_map_ext_path not set\n"); } -int -key_confirm_quit (object *op, char key) +void +player::chargen_race_next () { - char buf[MAX_BUF]; + /* Following actually changes the race - this is the default command + * if we don't match with one of the options above. + */ - if (key != 'y' && key != 'Y' && key != 'q' && key != 'Q') + do { - op->contr->state = ST_PLAYING; - new_draw_info (NDI_UNIQUE, 0, op, "OK, continuing to play."); - return 1; - } - - INVOKE_PLAYER (LOGOUT, op->contr); - INVOKE_PLAYER (QUIT, op->contr); + shstr name = ob->name; + int x = ob->x, y = ob->y; - terminate_all_pets (op); - leave_map (op); - op->direction = 0; - new_draw_info_format (NDI_UNIQUE | NDI_ALL, 5, NULL, "%s quits the game.", &op->name); - - strcpy (op->contr->killer, "quit"); - check_score (op); - op->contr->party = NULL; - if (settings.set_title == TRUE) - op->contr->own_title[0] = '\0'; - - if (!QUERY_FLAG (op, FLAG_WAS_WIZ)) - { - maptile *mp, *next; - - /* We need to hunt for any per player unique maps in memory and - * get rid of them. The trailing slash in the path is intentional, - * so that players named 'Ab' won't match against players 'Abe' pathname - */ - sprintf (buf, "%s/%s/%s/", settings.localdir, settings.playerdir, &op->name); - for (mp = first_map; mp != NULL; mp = next) - { - next = mp->next; - if (!strncmp (mp->path, buf, strlen (buf))) - delete_map (mp); - } - - delete_character (op->name, 1); - } - - play_again (op); - return 1; + ob->remove_statbonus (); + ob->remove (); + ob->arch = get_player_archetype (ob->arch); + ob->arch->copy_to (ob); + ob->instantiate (); + ob->stats = ob->contr->orig_stats; + ob->name = ob->name_pl = name; + ob->x = x; + ob->y = y; + SET_ANIMATION (ob, 2); /* So player faces south */ + insert_ob_in_map (ob, ob->map, ob, 0); + assign (ob->contr->title, ob->arch->object::name); + ob->add_statbonus (); + } + while (!allowed_class (ob)); + + update_object (ob, UP_OBJ_FACE); + esrv_update_item (UPD_FACE, ob, ob); + ob->update_stats (); + ob->stats.hp = ob->stats.maxhp; + ob->stats.sp = ob->stats.maxsp; + ob->stats.grace = 0; } void @@ -1240,6 +972,7 @@ CLEAR_FLAG (op, FLAG_SCARED); return; } + get_rangevector (op, op->enemy, &rv, 0); dir = absdir (4 + rv.direction); @@ -1248,18 +981,16 @@ int m = 1 - (RANDOM () & 2); if (move_ob (op, absdir (dir + diff * m), op) || (diff == 0 && move_ob (op, absdir (dir - diff * m), op))) - { - return; - } + return; } + /* Cornered, get rid of scared */ CLEAR_FLAG (op, FLAG_SCARED); op->enemy = NULL; } - /* check_pick sees if there is stuff to be picked up/picks up stuff. - * IT returns 1 if the player should keep on moving, 0 if he should + * It returns 1 if the player should keep on moving, 0 if he should * stop. */ int @@ -1267,8 +998,8 @@ { object *tmp, *next; int stop = 0; - int j, k, wvratio; - char putstring[128], tmpstr[16]; + int wvratio; + char putstring[128]; /* if you're flying, you cna't pick up anything */ if (op->move_type & MOVE_FLYING) @@ -1276,6 +1007,9 @@ next = op->below; + int cnt = MAX_ITEM_PER_DROP; +#define CHK_PICK_PICKUP do { pick_up (op, tmp); cnt--; } while (0) + /* loop while there are items on the floor that are not marked as * destroyed */ while (next && !next->destroyed ()) @@ -1283,6 +1017,12 @@ tmp = next; next = tmp->below; + if (cnt <= 0) + { + op->failmsg ("Couldn't pickup all items at once."); + return 0; + } + if (op->destroyed ()) return 0; @@ -1292,7 +1032,7 @@ if (op->contr->search_str[0] != '\0' && settings.search_items == TRUE) { if (item_matched_string (op, tmp, op->contr->search_str)) - pick_up (op, tmp); + CHK_PICK_PICKUP; continue; } @@ -1304,35 +1044,36 @@ case 0: return 1; /* don't pick up */ case 1: - pick_up (op, tmp); + CHK_PICK_PICKUP; return 1; case 2: - pick_up (op, tmp); + CHK_PICK_PICKUP; return 0; case 3: return 0; /* stop before pickup */ case 4: - pick_up (op, tmp); + CHK_PICK_PICKUP; break; case 5: - pick_up (op, tmp); + CHK_PICK_PICKUP; stop = 1; break; case 6: - if (QUERY_FLAG (tmp, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED)) - pick_up (op, tmp); + if (QUERY_FLAG (tmp, FLAG_KNOWN_MAGICAL) + && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED)) + CHK_PICK_PICKUP; break; case 7: if (tmp->type == MONEY || tmp->type == GEM) - pick_up (op, tmp); + CHK_PICK_PICKUP; break; default: /* use value density */ if (!QUERY_FLAG (tmp, FLAG_UNPAID) && (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1))) >= op->contr->mode) - pick_up (op, tmp); + CHK_PICK_PICKUP; } } else @@ -1346,42 +1087,15 @@ &tmp->name, tmp->type, (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1)))); else sprintf (putstring, "item name: %s item type: %d weight/value: %d", - &tmp->arch->name, tmp->type, (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1)))); - new_draw_info (NDI_UNIQUE, 0, op, putstring); + &tmp->arch->archname, tmp->type, (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1)))); - sprintf (putstring, "...flags: "); - for (k = 0; k < 4; k++) - { - for (j = 0; j < 32; j++) - { - if ((tmp->flags[k] >> j) & 0x01) - { - sprintf (tmpstr, "%d ", k * 32 + j); - strcat (putstring, tmpstr); - } - } - } new_draw_info (NDI_UNIQUE, 0, op, putstring); - -#if 0 - /* print the flags too */ - for (k = 0; k < 4; k++) - { - fprintf (stderr, "%d [%d] ", k, k * 32 + 31); - for (j = 0; j < 32; j++) - { - fprintf (stderr, "%d", tmp->flags[k] >> (31 - j) & 0x01); - if (!((j + 1) % 4)) - fprintf (stderr, " "); - } - fprintf (stderr, " [%d]\n", k * 32); - } -#endif } + /* philosophy: * It's easy to grab an item type from a pile, as long as it's * generic. This takes no game-time. For more detailed pickups - * and selections, select-items shoul dbe used. This is a + * and selections, select-items should be used. This is a * grab-as-you-run type mode that's really useful for arrows for * example. * The drawback: right now it has no frontend, so you need to @@ -1424,26 +1138,21 @@ if (op->contr->mode & PU_FOOD) if (tmp->type == FOOD) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "FOOD\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_DRINK) if (tmp->type == DRINK || (tmp->type == POISON && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED))) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "DRINK\n"); + CHK_PICK_PICKUP; continue; } if (op->contr->mode & PU_POTION) if (tmp->type == POTION) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "POTION\n"); + CHK_PICK_PICKUP; continue; } @@ -1451,25 +1160,21 @@ if (op->contr->mode & PU_SPELLBOOK) if (tmp->type == SPELLBOOK) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "SPELLBOOK\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_SKILLSCROLL) if (tmp->type == SKILLSCROLL) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "SKILLSCROLL\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_READABLES) - if (tmp->type == BOOK || tmp->type == SCROLL) + if (tmp->type == BOOK || tmp->type == SCROLL || tmp->type == INSCRIBABLE) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "READABLES\n"); + CHK_PICK_PICKUP; continue; } @@ -1477,9 +1182,7 @@ if (op->contr->mode & PU_MAGIC_DEVICE) if (tmp->type == WAND || tmp->type == ROD || tmp->type == HORN) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "MAGIC_DEVICE\n"); + CHK_PICK_PICKUP; continue; } @@ -1487,9 +1190,7 @@ if (op->contr->mode & PU_MAGICAL) if (QUERY_FLAG (tmp, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED)) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "MAGICAL\n"); + CHK_PICK_PICKUP; continue; } @@ -1497,9 +1198,7 @@ { if (tmp->type == MONEY || tmp->type == GEM) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "MONEY/GEM\n"); + CHK_PICK_PICKUP; continue; } } @@ -1508,9 +1207,15 @@ if (op->contr->mode & PU_JEWELS) if (tmp->type == RING || tmp->type == AMULET) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "JEWELS\n"); + CHK_PICK_PICKUP; + continue; + } + + /* we don't forget dragon food */ + if (op->contr->mode & PU_FLESH) + if (tmp->type == FLESH) + { + CHK_PICK_PICKUP; continue; } @@ -1518,17 +1223,14 @@ if (op->contr->mode & PU_BOW) if (tmp->type == BOW) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "BOW\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_ARROW) if (tmp->type == ARROW) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "ARROW\n"); + CHK_PICK_PICKUP; continue; } @@ -1536,49 +1238,42 @@ if (op->contr->mode & PU_ARMOUR) if (tmp->type == ARMOUR) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "ARMOUR\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_HELMET) if (tmp->type == HELMET) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "HELMET\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_SHIELD) if (tmp->type == SHIELD) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "SHIELD\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_BOOTS) if (tmp->type == BOOTS) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "BOOTS\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_GLOVES) if (tmp->type == GLOVES) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "GLOVES\n"); + CHK_PICK_PICKUP; continue; } + if (op->contr->mode & PU_CLOAK) if (tmp->type == CLOAK) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "GLOVES\n"); + CHK_PICK_PICKUP; continue; } @@ -1586,9 +1281,7 @@ if (op->contr->mode & PU_MISSILEWEAPON) if (tmp->type == WEAPON && QUERY_FLAG (tmp, FLAG_IS_THROWN)) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "MISSILEWEAPON\n"); + CHK_PICK_PICKUP; continue; } @@ -1597,22 +1290,19 @@ { if (tmp->type == WEAPON && tmp->name != NULL) { - if (strstr (tmp->name, "table") == NULL && strstr (tmp->arch->name, "table") == NULL && - strstr (tmp->name, "chair") && strstr (tmp->arch->name, "chair") == NULL) + if (strstr (tmp->name, "table") == NULL && strstr (tmp->arch->archname, "table") == NULL && + strstr (tmp->name, "chair") && strstr (tmp->arch->archname, "chair") == NULL) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "WEAPON\n"); + CHK_PICK_PICKUP; continue; } } + if (tmp->type == WEAPON && tmp->name == NULL) { - if (strstr (tmp->arch->name, "table") == NULL && strstr (tmp->arch->name, "chair") == NULL) + if (strstr (tmp->arch->archname, "table") == NULL && strstr (tmp->arch->archname, "chair") == NULL) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "WEAPON\n"); + CHK_PICK_PICKUP; continue; } } @@ -1622,9 +1312,7 @@ if (op->contr->mode & PU_KEY) if (tmp->type == KEY || tmp->type == SPECIAL_KEY) { - pick_up (op, tmp); - if (0) - fprintf (stderr, "KEY\n"); + CHK_PICK_PICKUP; continue; } @@ -1639,7 +1327,7 @@ wvratio = (op->contr->mode & PU_RATIO) * 5; if ((query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1))) >= (unsigned int) wvratio) { - pick_up (op, tmp); + CHK_PICK_PICKUP; #if 0 fprintf (stderr, "HIGH WEIGHT/VALUE ["); if (tmp->name != NULL) @@ -1647,7 +1335,7 @@ fprintf (stderr, "%s", tmp->name); } else - fprintf (stderr, "%s", tmp->arch->name); + fprintf (stderr, "%s", tmp->arch->archname); fprintf (stderr, ",%d] = ", tmp->type); fprintf (stderr, "%d\n", (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1)))); #endif @@ -1656,6 +1344,7 @@ } } /* the new pickup model */ } + return !stop; } @@ -1667,14 +1356,19 @@ object * find_arrow (object *op, const char *type) { - object *tmp = NULL; + for (object *tmp = op->inv; tmp; tmp = tmp->below) + if (tmp->type == ARROW && !strcmp (&tmp->race, type)) + return splay (tmp); + + for (object *tmp = op->inv; tmp; tmp = tmp->below) + if (tmp->type == CONTAINER && QUERY_FLAG (tmp, FLAG_APPLIED) && !strcmp (&tmp->race, type)) + if (object *arrow = find_arrow (tmp, type)) + { + splay (tmp); + return arrow; + } - for (op = op->inv; op; op = op->below) - if (!tmp && op->type == CONTAINER && op->race == type && QUERY_FLAG (op, FLAG_APPLIED)) - tmp = find_arrow (op, type); - else if (op->type == ARROW && op->race == type) - return op; - return tmp; + return 0; } /* @@ -1683,7 +1377,6 @@ * of resistances. The archer is making a quick guess at what he sees down * the hall. Failing that it does it's best to pick the highest plus arrow. */ - object * find_better_arrow (object *op, object *target, const char *type, int *better) { @@ -1726,11 +1419,11 @@ for (attacknum = 0; attacknum < NROFATTACKS; attacknum++) { attacktype = 1 << attacknum; - if ((arrow->attacktype & attacktype) && (target->arch->clone.resist[attacknum]) < 0) - if (((arrow->magic + arrow->stats.dam) * (100 - target->arch->clone.resist[attacknum]) / 100) > betterby) + if ((arrow->attacktype & attacktype) && (target->arch->resist[attacknum]) < 0) + if (((arrow->magic + arrow->stats.dam) * (100 - target->arch->resist[attacknum]) / 100) > betterby) { tmp = arrow; - betterby = (arrow->magic + arrow->stats.dam) * (100 - target->arch->clone.resist[attacknum]) / 100; + betterby = (arrow->magic + arrow->stats.dam) * (100 - target->arch->resist[attacknum]) / 100; } } if ((2 + arrow->magic + arrow->stats.dam) > betterby) @@ -1759,7 +1452,6 @@ * type = bow->race * dir = fire direction */ - object * pick_arrow_target (object *op, const char *type, int dir) { @@ -1834,7 +1526,7 @@ fire_bow (object *op, object *part, object *arrow, int dir, int wc_mod, sint16 sx, sint16 sy) { object *left, *bow; - int bowspeed, mflags; + int mflags; maptile *m; if (!dir) @@ -1842,8 +1534,9 @@ new_draw_info (NDI_UNIQUE, 0, op, "You can't shoot yourself!"); return 0; } - if (op->type == PLAYER) - bow = op->contr->ranges[range_bow]; + + if (op->contr) + bow = op->current_weapon; else { for (bow = op->inv; bow; bow = bow->below) @@ -1858,21 +1551,22 @@ LOG (llevError, "Range: bow without activated bow (%s).\n", &op->name); return 0; } + + // optimisation: move object to top so we will find it quickly again + if (bow->below) + { + bow->remove (); + op->insert (bow); + } + } + if (!bow->race || !bow->skill) { new_draw_info_format (NDI_UNIQUE, 0, op, "Your %s is broken.", &bow->name); return 0; } - bowspeed = bow->stats.sp + dex_bonus[op->stats.Dex]; - - /* penalize ROF for bestarrow */ - if (op->type == PLAYER && op->contr->bowtype == bow_bestarrow) - bowspeed -= dex_bonus[op->stats.Dex] + 5; - if (bowspeed < 1) - bowspeed = 1; - if (arrow == NULL) { if ((arrow = find_arrow (op, bow->race)) == NULL) @@ -1882,14 +1576,15 @@ /* FLAG_READY_BOW will get reset if the monsters picks up some arrows */ else CLEAR_FLAG (op, FLAG_READY_BOW); + return 0; } } + mflags = get_map_flags (op->map, &m, sx, sy, &sx, &sy); if (mflags & P_OUT_OF_MAP) - { - return 0; - } + return 0; + if (GET_MAP_MOVE_BLOCK (m, sx, sy) == MOVE_FLY_LOW) { new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way."); @@ -1899,79 +1594,85 @@ /* this should not happen, but sometimes does */ if (arrow->nrof == 0) { - remove_ob (arrow); - free_object (arrow); + arrow->destroy (); return 0; } left = arrow; /* these are arrows left to the player */ arrow = get_split_ob (arrow, 1); - if (arrow == NULL) + if (!arrow) { new_draw_info_format (NDI_UNIQUE, 0, op, "You have no %s left.", &bow->race); return 0; } - set_owner (arrow, op); - arrow->skill = bow->skill; + arrow->set_owner (op); + arrow->skill = bow->skill; arrow->direction = dir; - arrow->x = sx; - arrow->y = sy; - if (op->type == PLAYER) + arrow->stats.sp = arrow->stats.wc; /* save original wc, dam, attacktype and slaying */ + arrow->stats.hp = arrow->stats.dam; + arrow->stats.grace = arrow->attacktype; + + if (arrow->slaying) + arrow->spellarg = strdup (arrow->slaying); + +#if 0 + if (player *pl = op->contr) { - op->speed_left = 0.01 - (float) FABS (op->speed) * 100 / bowspeed; - fix_player (op); - } + float speed = pl->weapon_sp; - SET_ANIMATION (arrow, arrow->direction); - arrow->stats.sp = arrow->stats.wc; /* save original wc and dam */ - arrow->stats.hp = arrow->stats.dam; - arrow->stats.grace = arrow->attacktype; - if (arrow->slaying != NULL) - arrow->spellarg = strdup_local (arrow->slaying); + /* penalize ROF for bestarrow */ + if (pl->bowtype == bow_bestarrow) + speed *= .9f; + else + speed *= 1.f + dex_bonus[op->stats.Dex] * .2f; - /* Note that this was different for monsters - they got their level - * added to the damage. I think the strength bonus is more proper. - */ + op->speed_left += speed - op->speed; + } +#endif - arrow->stats.dam += (QUERY_FLAG (bow, FLAG_NO_STRENGTH) ? 0 : dam_bonus[op->stats.Str]) + bow->stats.dam + bow->magic + arrow->magic; + SET_ANIMATION (arrow, arrow->direction); /* update the speed */ - arrow->speed = (float) ((QUERY_FLAG (bow, FLAG_NO_STRENGTH) ? - 0 : dam_bonus[op->stats.Str]) + bow->magic + arrow->magic) / 5.0 + (float) bow->stats.dam / 7.0; + arrow->speed = ((bow->flag [FLAG_NO_STRENGTH] ? 0 : dam_bonus[op->stats.Str]) + bow->magic + arrow->magic) / 5.f + + bow->stats.dam / 7.f; - if (arrow->speed < 1.0) - arrow->speed = 1.0; - update_ob_speed (arrow); + arrow->set_speed (max (arrow->speed, 2.f)); arrow->speed_left = 0; + int wc = op->stats.wc + wc_mod - arrow->magic - arrow->stats.wc; + if (op->type == PLAYER) { - arrow->stats.wc = 20 - bow->magic - arrow->magic - - (op->chosen_skill ? op->chosen_skill->level : op->level) - - dex_bonus[op->stats.Dex] - thaco_bonus[op->stats.Str] - arrow->stats.wc - bow->stats.wc + wc_mod; - arrow->level = op->chosen_skill ? op->chosen_skill->level : op->level; + wc -= dex_bonus[op->stats.Dex]; + + if (!arrow->slaying) + arrow->slaying = op->slaying; + + arrow->attacktype |= op->attacktype; } else { - arrow->stats.wc = op->stats.wc - bow->magic - arrow->magic - arrow->stats.wc + wc_mod; arrow->level = op->level; - } + arrow->stats.wc -= bow->magic; - if (arrow->attacktype == AT_PHYSICAL) - arrow->attacktype |= bow->attacktype; + if (!arrow->slaying) + arrow->slaying = bow->slaying; + + arrow->attacktype |= bow->attacktype; + } - if (bow->slaying != NULL) - arrow->slaying = bow->slaying; + wc -= arrow->level; + arrow->stats.dam = clamp (arrow->stats.dam + op->stats.dam + arrow->magic, MIN_DAM, MAX_DAM); - arrow->map = m; + arrow->stats.wc = clamp (wc, MIN_WC, MAX_WC); arrow->move_type = MOVE_FLY_LOW; arrow->move_on = MOVE_FLY_LOW | MOVE_WALK; - play_sound_map (op->map, op->x, op->y, SOUND_FIRE_ARROW); - insert_ob_in_map (arrow, m, op, 0); + op->play_sound (sound_find ("fire_arrow")); + m->insert (arrow, sx, sy, op); if (!arrow->destroyed ()) move_arrow (arrow); @@ -2001,12 +1702,13 @@ if (op->contr->bowtype == bow_bestarrow) { - ret = fire_bow (op, op, pick_arrow_target (op, op->contr->ranges[range_bow]->race, dir), dir, 0, op->x, op->y); + ret = fire_bow (op, op, pick_arrow_target (op, op->contr->ranged_ob->race, dir), dir, 0, op->x, op->y); } else if (op->contr->bowtype >= bow_n && op->contr->bowtype <= bow_nw) { if (!similar_direction (dir, op->contr->bowtype - bow_n + 1)) wcmod = -1; + ret = fire_bow (op, op, NULL, op->contr->bowtype - bow_n + 1, wcmod, op->x, op->y); } else if (op->contr->bowtype == bow_threewide) @@ -2020,43 +1722,46 @@ ret |= fire_bow (op, op, NULL, dir, 0, op->x, op->y); ret |= fire_bow (op, op, NULL, absdir (dir - 1), -5, op->x, op->y); ret |= fire_bow (op, op, NULL, absdir (dir + 1), -5, op->x, op->y); - } else { /* Simple case */ ret = fire_bow (op, op, NULL, dir, 0, op->x, op->y); } + return ret; } - /* Fires a misc (wand/rod/horn) object in 'dir'. * Broken apart from 'fire' to keep it more readable. */ void fire_misc_object (object *op, int dir) { - object *item; + object *item = op->contr->ranged_ob; - if (!op->contr->ranges[range_misc]) + if (!item) { new_draw_info (NDI_UNIQUE, 0, op, "You have range item readied."); return; } - item = op->contr->ranges[range_misc]; if (!item->inv) { LOG (llevError, "Object %s lacks a spell\n", &item->name); return; } + + if (!op->change_weapon (item)) + return; + if (item->type == WAND) { if (item->stats.food <= 0) { - play_sound_player_only (op->contr, SOUND_WAND_POOF, 0, 0); + op->contr->play_sound (sound_find ("wand_poof")); new_draw_info_format (NDI_UNIQUE, 0, op, "The %s goes poof.", query_base_name (item, 0)); + return; } } @@ -2064,11 +1769,13 @@ { if (item->stats.hp < MAX (item->inv->stats.sp, item->inv->stats.grace)) { - play_sound_player_only (op->contr, SOUND_WAND_POOF, 0, 0); + op->contr->play_sound (sound_find ("wand_poof")); + if (item->type == ROD) new_draw_info_format (NDI_UNIQUE, 0, op, "The %s whines for a while, but nothing happens.", query_base_name (item, 0)); else new_draw_info_format (NDI_UNIQUE, 0, op, "The %s needs more time to charge.", query_base_name (item, 0)); + return; } } @@ -2085,24 +1792,22 @@ if (item->arch) { CLEAR_FLAG (item, FLAG_ANIMATE); - item->face = item->arch->clone.face; - item->speed = 0; - update_ob_speed (item); + item->face = item->arch->face; + item->set_speed (0); } - if ((tmp = is_player_inv (item))) + + if ((tmp = item->in_player ())) esrv_update_item (UPD_ANIM, tmp, item); } } else if (item->type == ROD || item->type == HORN) - { - drain_rod_charge (item); - } + drain_rod_charge (item); } } /* Received a fire command for the player - go and do it. */ -void +bool fire (object *op, int dir) { int spellcost = 0; @@ -2111,53 +1816,52 @@ if (action_makes_visible (op)) make_visible (op); - switch (op->contr->shoottype) + player *pl = op->contr; + + if (pl->golem) { - case range_none: - return; + control_golem (op->contr->golem, dir); + return false; + } - case range_bow: - player_fire_bow (op, dir); - return; + object *ob = pl->ranged_ob; - case range_magic: /* Casting spells */ - spellcost = (cast_spell (op, op, dir, op->contr->ranges[range_magic], op->contr->spellparam[0] ? op->contr->spellparam : 0)); - return; + if (!ob) + return false; - case range_misc: - fire_misc_object (op, dir); - return; + if (!op->change_weapon (ob)) + return false; - case range_golem: /* Control summoned monsters from scrolls */ - if (op->contr->ranges[range_golem] == NULL || op->contr->golem_count != op->contr->ranges[range_golem]->count) - { - op->contr->ranges[range_golem] = NULL; - op->contr->shoottype = range_none; - op->contr->golem_count = 0; - } - else - control_golem (op->contr->ranges[range_golem], dir); - return; + if (op->speed_left > 0.f) + --op->speed_left; + else + return false; - case range_skill: - if (!op->chosen_skill) - { - if (op->type == PLAYER) - new_draw_info (NDI_UNIQUE, 0, op, "You have no applicable skill to use."); - return; - } - (void) do_skill (op, op, op->chosen_skill, dir, NULL); - return; - case range_builder: + switch (ob->type) + { + case BOW: + player_fire_bow (op, dir); + break; + + case SPELL: + spellcost = cast_spell (op, op, dir, ob, pl->spellparam[0] ? pl->spellparam : 0); + break; + + case BUILDER: apply_map_builder (op, dir); - return; + break; + + case SKILL: + do_skill (op, op, ob, dir, 0); + break; + default: - new_draw_info (NDI_UNIQUE, 0, op, "Illegal shoot type."); - return; + fire_misc_object (op, dir); + break; } -} - + return true; +} /* find_key * We try to find a key for the door as passed. If we find a key @@ -2169,18 +1873,17 @@ * door is the door we are trying to match against. * This function can be called recursively to search containers. */ - object * find_key (object *pl, object *container, object *door) { object *tmp, *key; /* Should not happen, but sanity checking is never bad */ - if (container->inv == NULL) - return NULL; + if (!container->inv) + return 0; /* First, lets try to find a key in the top level inventory */ - for (tmp = container->inv; tmp != NULL; tmp = tmp->below) + for (tmp = container->inv; tmp; tmp = tmp->below) { if (door->type == DOOR && tmp->type == KEY) break; @@ -2190,6 +1893,7 @@ if (tmp->slaying && tmp->type == SPECIAL_KEY && tmp->slaying == door->slaying) break; } + /* No key found - lets search inventories now */ /* If we find and use a key in an inventory, return at that time. * otherwise, if we search all the inventories and still don't find @@ -2197,18 +1901,20 @@ */ if (!tmp) { - for (tmp = container->inv; tmp != NULL; tmp = tmp->below) + for (tmp = container->inv; tmp; tmp = tmp->below) { /* No reason to search empty containers */ if (tmp->type == CONTAINER && tmp->inv) { - if ((key = find_key (pl, tmp, door)) != NULL) + if ((key = find_key (pl, tmp, door))) return key; } } + if (!tmp) return NULL; } + /* We get down here if we have found a key. Now if its in a container, * see if we actually want to use it */ @@ -2239,6 +1945,7 @@ return NULL; } } + return tmp; } @@ -2250,45 +1957,46 @@ static int player_attack_door (object *op, object *door) { - - /* If its a door, try to find a use a key. If we do destroy the door, + /* If its a door, try to find a key. If we do destroy the door, * might as well return immediately as there is nothing more to do - * otherwise, we fall through to the rest of the code. */ object *key = find_key (op, op, door); - /* IF we found a key, do some extra work */ + /* If we found a key, do some extra work */ if (key) { object *container = key->env; - play_sound_map (op->map, op->x, op->y, SOUND_OPEN_DOOR); if (action_makes_visible (op)) make_visible (op); + if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP)) spring_trap (door->inv, op); + if (door->type == DOOR) - { - hit_player (door, 9998, op, AT_PHYSICAL, 1); /* Break through the door */ - } + hit_player (door, 9998, op, AT_PHYSICAL, 1); /* Break through the door */ else if (door->type == LOCKED_DOOR) { - new_draw_info_format (NDI_UNIQUE, NDI_BROWN, op, "You open the door with the %s", query_short_name (key)); + op->statusmsg (format ("You open the door with the %s", query_short_name (key)), NDI_BROWN); remove_door2 (door); /* remove door without violence ;-) */ } + /* Do this after we print the message */ decrease_ob (key); /* Use up one of the keys */ /* Need to update the weight the container the key was in */ if (container != op) esrv_update_item (UPD_WEIGHT, op, container); + return 1; /* Nothing more to do below */ } else if (door->type == LOCKED_DOOR) { /* Might as well return now - no other way to open this */ - new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, op, door->msg); + op->failmsg (door->msg ? &door->msg : "Hmm, you miss the certain something to open this door..."); return 1; } + return 0; } @@ -2298,19 +2006,24 @@ * (taking into account confusion.) The player is also actually * going to try and move (not fire weapons). */ - -void +bool move_player_attack (object *op, int dir) { - object *tmp, *mon; - sint16 nx, ny; int on_battleground; - maptile *m; - nx = freearr_x[dir] + op->x; - ny = freearr_y[dir] + op->y; + sint16 nx = freearr_x[dir] + op->x; + sint16 ny = freearr_y[dir] + op->y; - on_battleground = op_on_battleground (op, NULL, NULL); + on_battleground = op_on_battleground (op, 0, 0); + + if (out_of_map (op->map, nx, ny)) + return false; + + if (!op->contr->braced && op->speed_left > 0.f && move_ob (op, dir, op)) + { + --op->speed_left; + return true; + } /* If braced, or can't move to the square, and it is not out of the * map, attack it. Note order of if statement is important - don't @@ -2321,178 +2034,150 @@ * quite a bit of processing. However, it probably is less than what * move_ob uses. */ - if ((op->contr->braced || !move_ob (op, dir, op)) && !out_of_map (op->map, nx, ny)) + maptile *m = op->map->xy_find (nx, ny); + + /* Go through all the objects, and find ones of interest. Only stop if + * we find a monster - that is something we know we want to attack. + * if its a door or barrel (can roll) see if there may be monsters + * on the space + */ + object *mon; + for (mon = m->at (nx, ny).bot; mon; mon = mon->above) { - if (OUT_OF_REAL_MAP (op->map, nx, ny)) - { - m = get_map_from_coord (op->map, &nx, &ny); - if (!m) - return; /* Don't think this should happen */ - } - else - m = op->map; + if ((mon->flag [FLAG_ALIVE] + || mon->type == LOCKED_DOOR + || mon->flag [FLAG_CAN_ROLL]) + && mon != op) + break; + } - if ((tmp = get_map_ob (m, nx, ny)) == NULL) - { - /* LOG(llevError,"player_move_attack: get_map_ob returns NULL, but player can not more there.\n"); */ - return; - } + if (!mon) /* This happens anytime the player tries to move */ + return false; /* into a wall */ - mon = NULL; - /* Go through all the objects, and find ones of interest. Only stop if - * we find a monster - that is something we know we want to attack. - * if its a door or barrel (can roll) see if there may be monsters - * on the space - */ - while (tmp != NULL) + mon = mon->head_ (); + + if ((mon->type == DOOR && mon->stats.hp >= 0) || (mon->type == LOCKED_DOOR)) + if (op->contr->weapon_sp_left > 0.f) + if (player_attack_door (op, mon)) { - if (tmp == op) - { - tmp = tmp->above; - continue; - } - if (QUERY_FLAG (tmp, FLAG_ALIVE)) - { - mon = tmp; - break; - } - if (tmp->type == LOCKED_DOOR || QUERY_FLAG (tmp, FLAG_CAN_ROLL)) - mon = tmp; - tmp = tmp->above; + --op->contr->weapon_sp_left; + return true; } - if (mon == NULL) /* This happens anytime the player tries to move */ - return; /* into a wall */ + /* The following deals with possibly attacking peaceful + * or friendly creatures. Basically, all players are considered + * unaggressive. If the moving player has peaceful set, then the + * object should be pushed instead of attacked. It is assumed that + * if you are braced, you will not attack friends accidently, + * and thus will not push them. + */ - if (mon->head != NULL) - mon = mon->head; + /* If the creature is a pet, push it even if the player is not + * peaceful. Our assumption is the creature is a pet if the + * player owns it and it is either friendly or unagressive. + */ + if (op->type == PLAYER + && ((mon->owner && mon->owner->contr + && same_party (mon->owner->contr->party, op->contr->party)) + || mon->owner == op) + && (QUERY_FLAG (mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG (mon, FLAG_FRIENDLY))) + { + /* If we're braced, we don't want to switch places with it */ + if (op->contr->braced) + return false; - if ((mon->type == DOOR && mon->stats.hp >= 0) || (mon->type == LOCKED_DOOR)) - if (player_attack_door (op, mon)) - return; + if (op->speed_left > 0.f) + { + --op->speed_left; - /* The following deals with possibly attacking peaceful - * or frienddly creatures. Basically, all players are considered - * unaggressive. If the moving player has peaceful set, then the - * object should be pushed instead of attacked. It is assumed that - * if you are braced, you will not attack friends accidently, - * and thus will not push them. - */ + op->play_sound (sound_find ("push_player")); + push_ob (mon, dir, op); - /* If the creature is a pet, push it even if the player is not - * peaceful. Our assumption is the creature is a pet if the - * player owns it and it is either friendly or unagressive. - */ - if ((op->type == PLAYER) -#if COZY_SERVER - && - ((get_owner (mon) && get_owner (mon)->contr - && same_party (get_owner (mon)->contr->party, op->contr->party)) || get_owner (mon) == op) -#else - && get_owner (mon) == op -#endif - && (QUERY_FLAG (mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG (mon, FLAG_FRIENDLY))) - { - /* If we're braced, we don't want to switch places with it */ - if (op->contr->braced) - return; - play_sound_map (op->map, op->x, op->y, SOUND_PUSH_PLAYER); - (void) push_ob (mon, dir, op); if (op->contr->tmp_invis || op->hide) make_visible (op); - return; + + return true; } + else + return false; + } - /* in certain circumstances, you shouldn't attack friendly - * creatures. Note that if you are braced, you can't push - * someone, but put it inside this loop so that you won't - * attack them either. - */ - if ((mon->type == PLAYER || mon->enemy != op) && - (mon->type == PLAYER || QUERY_FLAG (mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG (mon, FLAG_FRIENDLY)) && ( -#ifdef PROHIBIT_PLAYERKILL - (op->contr->peaceful - || (mon->type == PLAYER - && mon->contr-> - peaceful)) && -#else - op->contr->peaceful && -#endif - !on_battleground)) + /* in certain circumstances, you shouldn't attack friendly + * creatures. Note that if you are braced, you can't push + * someone, but put it inside this loop so that you won't + * attack them either. + */ + if ((mon->type == PLAYER || mon->enemy != op) + && (mon->type == PLAYER || QUERY_FLAG (mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG (mon, FLAG_FRIENDLY)) + && ((op->contr->peaceful + || (mon->type == PLAYER && mon->contr->peaceful)) + && !on_battleground)) + { + if (op->speed_left > 0.f) { + --op->speed_left; + if (!op->contr->braced) { - play_sound_map (op->map, op->x, op->y, SOUND_PUSH_PLAYER); - (void) push_ob (mon, dir, op); + op->play_sound (sound_find ("push_player")); + push_ob (mon, dir, op); } else - { - new_draw_info (0, 0, op, "You withhold your attack"); - } + op->statusmsg ("You withhold your attack"); + if (op->contr->tmp_invis || op->hide) make_visible (op); - } - /* If the object is a boulder or other rollable object, then - * roll it if not braced. You can't roll it if you are braced. - */ - else if (QUERY_FLAG (mon, FLAG_CAN_ROLL) && (!op->contr->braced)) + return true; + } + } + /* If the object is a boulder or other rollable object, then + * roll it if not braced. You can't roll it if you are braced. + */ + else if (QUERY_FLAG (mon, FLAG_CAN_ROLL) && (!op->contr->braced)) + { + if (op->speed_left > 0.f) { + --op->speed_left; + recursive_roll (mon, dir, op); if (action_makes_visible (op)) make_visible (op); - } - /* Any generic living creature. Including things like doors. - * Way it works is like this: First, it must have some hit points - * and be living. Then, it must be one of the following: - * 1) Not a player, 2) A player, but of a different party. Note - * that party_number -1 is no party, so attacks can still happen. - */ - - else if ((mon->stats.hp >= 0) && QUERY_FLAG (mon, FLAG_ALIVE) && - ((mon->type != PLAYER || op->contr->party == NULL || op->contr->party != mon->contr->party))) + return true; + } + } + /* Any generic living creature. Including things like doors. + * Way it works is like this: First, it must have some hit points + * and be living. Then, it must be one of the following: + * 1) Not a player, 2) A player, but of a different party. Note + * that party_number -1 is no party, so attacks can still happen. + */ + else if ((mon->stats.hp >= 0) && QUERY_FLAG (mon, FLAG_ALIVE) && + ((mon->type != PLAYER || op->contr->party == NULL || op->contr->party != mon->contr->party))) + { + if (op->contr->weapon_sp_left > 0.f && !op->flag [FLAG_WIZPASS]) { + --op->contr->weapon_sp_left; - /* If the player hasn't hit something this tick, and does - * so, give them speed boost based on weapon speed. Doing - * it here is better than process_players2, which basically - * incurred a 1 tick offset. - */ - if (!op->contr->has_hit) - { - op->speed_left += op->speed / op->contr->weapon_sp; - - op->contr->has_hit = 1; /* The last action was to hit, so use weapon_sp */ - } - - skill_attack (mon, op, 0, NULL, NULL); + skill_attack (mon, op, 0, 0, 0); - /* If attacking another player, that player gets automatic - * hitback, and doesn't loose luck either. - * Disable hitback on the battleground or if the target is - * the wiz. - */ - if (mon->type == PLAYER && mon->stats.hp >= 0 && !mon->contr->has_hit && !on_battleground && !QUERY_FLAG (mon, FLAG_WIZ)) - { - short luck = mon->stats.luck; - - mon->contr->has_hit = 1; - skill_attack (op, mon, 0, NULL, NULL); - mon->stats.luck = luck; - } if (action_makes_visible (op)) make_visible (op); + + return true; } - } /* if player should attack something */ + } + + return false; } -int +bool move_player (object *op, int dir) { int pick; - if (op->map == NULL || op->map->in_memory != MAP_IN_MEMORY) + if (!op->map || op->map->in_memory != MAP_ACTIVE) return 0; /* Sanity check: make sure dir is valid */ @@ -2502,22 +2187,24 @@ return 0; } - /* peterm: added following line */ + /* peterm: added following line */ if (QUERY_FLAG (op, FLAG_CONFUSED) && dir) - dir = absdir (dir + RANDOM () % 3 + RANDOM () % 3 - 2); + dir = absdir (dir + rndm (3) + rndm (3) - 2); op->facing = dir; if (op->hide) do_hidden_move (op); + bool retval; + if (INVOKE_PLAYER (MOVE, op->contr, ARG_INT (dir))) - /*nop */ ; + retval = RESULT_INT (0); else if (op->contr->fire_on) - fire (op, dir); + retval = fire (op, dir); else { - move_player_attack (op, dir); + retval = move_player_attack (op, dir); pick = check_pick (op); } @@ -2525,19 +2212,17 @@ * server can handle repeat firing. */ if (op->contr->fire_on || (op->contr->run_on && pick != 0)) - { - op->direction = dir; - } + op->direction = dir; else - { - op->direction = 0; - } + op->direction = 0; + /* Update how the player looks. Use the facing, so direction may * get reset to zero. This allows for full animation capabilities * for players. */ animate_object (op, op->facing); - return 0; + + return retval; } /* This is similar to handle_player, below, but is only used by the @@ -2545,105 +2230,67 @@ * This is sort of special, in that the new client/server actually uses * the new speed values for commands. * - * Returns true if there are more actions we can do. + * Returns true if there are more actions we can do. Should not do + * many actions in a row, as that would be too unfair to other + * players. */ -int +bool handle_newcs_player (object *op) { - if (op->contr->hidden) - { - op->invisible = 1000; - /* the socket code flashes the player visible/invisible - * depending on the value of invisible, so we need to - * alternate it here for it to work correctly. - */ - if (pticks & 2) - op->invisible--; - } - else if (op->invisible && !(QUERY_FLAG (op, FLAG_MAKE_INVIS))) - { - op->invisible--; - if (!op->invisible) - { - make_visible (op); - new_draw_info (NDI_UNIQUE, 0, op, "Your invisibility spell runs out."); - } - } - if (QUERY_FLAG (op, FLAG_SCARED)) { - flee_player (op); - /* If player is still scared, that is his action for this tick */ - if (QUERY_FLAG (op, FLAG_SCARED)) + if (op->speed_left > 0.f) { - op->speed_left--; - return 0; - } - } + --op->speed_left; + flee_player (op); - /* I've been seeing crashes where the golem has been destroyed, but - * the player object still points to the defunct golem. The code that - * destroys the golem looks correct, and it doesn't always happen, so - * put this in a a workaround to clean up the golem pointer. - */ - if (op->contr->ranges[range_golem] && - ((op->contr->golem_count != op->contr->ranges[range_golem]->count) || QUERY_FLAG (op->contr->ranges[range_golem], FLAG_REMOVED))) - { - op->contr->ranges[range_golem] = NULL; - op->contr->golem_count = 0; + return true; + } + else + return false; } /* call this here - we also will call this in do_ericserver, but * the players time has been increased when doericserver has been * called, so we recheck it here. */ - HandleClient (&op->contr->socket, op->contr); - if (op->speed_left < 0) - return 0; + if (op->contr->ns->handle_command ()) + return true; if (op->direction && (op->contr->run_on || op->contr->fire_on)) - { - /* All move commands take 1 tick, at least for now */ - op->speed_left--; + return move_player (op, op->direction); - /* Instead of all the stuff below, let move_player take care - * of it. Also, some of the skill stuff is only put in - * there, as well as the confusion stuff. - */ - move_player (op, op->direction); - if (op->speed_left > 0) - return 1; - else - return 0; - } - return 0; + return false; } int save_life (object *op) { - object *tmp; - if (!QUERY_FLAG (op, FLAG_LIFESAVE)) return 0; - for (tmp = op->inv; tmp != NULL; tmp = tmp->below) + for (object *tmp = op->inv; tmp; tmp = tmp->below) if (QUERY_FLAG (tmp, FLAG_APPLIED) && QUERY_FLAG (tmp, FLAG_LIFESAVE)) { - play_sound_map (op->map, op->x, op->y, SOUND_OB_EVAPORATE); + op->play_sound (sound_find ("ob_evaporate")); new_draw_info_format (NDI_UNIQUE, 0, op, "Your %s vibrates violently, then evaporates.", query_name (tmp)); + if (op->contr) esrv_del_item (op->contr, tmp->count); - remove_ob (tmp); - free_object (tmp); + + tmp->destroy (); CLEAR_FLAG (op, FLAG_LIFESAVE); + if (op->stats.hp < 0) op->stats.hp = op->stats.maxhp; + if (op->stats.food < 0) op->stats.food = 999; - fix_player (op); + + op->update_stats (); return 1; } + LOG (llevError, "Error: LIFESAVE set without applied object.\n"); CLEAR_FLAG (op, FLAG_LIFESAVE); enter_player_savebed (op); /* bring him home. */ @@ -2655,31 +2302,33 @@ * function will descend into containers. op is the object to start the search * from. */ -void -remove_unpaid_objects (object *op, object *env) +static void +drop_unpaid_items (object *op, object *env) { - object *next; - while (op) { - next = op->below; /* Make sure we have a good value, in case - * we remove object 'op' - */ + object *next = op->below; /* Make sure we have a good value, in case we remove object 'op' */ + if (QUERY_FLAG (op, FLAG_UNPAID)) { - remove_ob (op); - op->x = env->x; - op->y = env->y; if (env->type == PLAYER) esrv_del_item (env->contr, op->count); - insert_ob_in_map (op, env->map, NULL, 0); + + op->insert_at (env); } else if (op->inv) - remove_unpaid_objects (op->inv, env); + drop_unpaid_items (op->inv, env); + op = next; } } +void +object::drop_unpaid_items () +{ + if (!flag [FLAG_REMOVED]) + ::drop_unpaid_items (inv, this); +} /* * Returns pointer a static string containing gravestone text @@ -2700,12 +2349,14 @@ sprintf (buf, "%s the %s\n", &op->name, op->contr->title); else sprintf (buf, "%s\n", &op->name); + strncat (buf2, " ", 20 - strlen (buf) / 2); strcat (buf2, buf); if (op->type == PLAYER) sprintf (buf, "who was in level %d when killed\n", op->level); else sprintf (buf, "who was in level %d when died.\n\n", op->level); + strncat (buf2, " ", 20 - strlen (buf) / 2); strcat (buf2, buf); if (op->type == PLAYER) @@ -2714,14 +2365,14 @@ strncat (buf2, " ", 21 - strlen (buf) / 2); strcat (buf2, buf); } + strftime (buf, MAX_BUF, "%b %d %Y\n", localtime (&now)); strncat (buf2, " ", 20 - strlen (buf) / 2); strcat (buf2, buf); + return buf2; } - - void do_some_living (object *op) { @@ -2736,16 +2387,27 @@ const int max_sp = 1; const int max_grace = 1; - if (op->contr->outputs_sync) + if (op->contr->hidden) { - for (i = 0; i < NUM_OUTPUT_BUFS; i++) - if (op->contr->outputs[i].buf != NULL && (op->contr->outputs[i].first_update + op->contr->outputs_sync) < (uint16) pticks) - flush_output_element (op, &op->contr->outputs[i]); + op->invisible = 1000; + /* the socket code flashes the player visible/invisible + * depending on the value of invisible, so we need to + * alternate it here for it to work correctly. + */ + if (pticks & 2) + op->invisible--; } - - if (op->contr->state == ST_PLAYING) + else if (op->invisible && !(QUERY_FLAG (op, FLAG_MAKE_INVIS))) { + if (!op->invisible--) + { + make_visible (op); + new_draw_info (NDI_UNIQUE, 0, op, "Your invisibility spell runs out."); + } + } + if (op->contr->ns->state == ST_PLAYING) + { /* these next three if clauses make it possible to SLOW DOWN hp/grace/spellpoint regeneration. */ if (op->contr->gen_hp >= 0) @@ -2755,6 +2417,7 @@ gen_hp = op->stats.maxhp; rate_hp -= rate_hp / 2 * op->contr->gen_hp; } + if (op->contr->gen_sp >= 0) gen_sp = (op->contr->gen_sp + 1) * op->stats.maxsp; else @@ -2762,6 +2425,7 @@ gen_sp = op->stats.maxsp; rate_sp -= rate_sp / 2 * op->contr->gen_sp; } + if (op->contr->gen_grace >= 0) gen_grace = (op->contr->gen_grace + 1) * op->stats.maxgrace; else @@ -2770,55 +2434,13 @@ rate_grace -= rate_grace / 2 * op->contr->gen_grace; } - /* Regenerate Spell Points */ - if (op->contr->ranges[range_golem] == NULL && --op->last_sp < 0) - { - gen_sp = gen_sp * 10 / (op->contr->gen_sp_armour < 10 ? 10 : op->contr->gen_sp_armour); - if (op->stats.sp < op->stats.maxsp) - { - op->stats.sp++; - /* dms do not consume food */ - if (!QUERY_FLAG (op, FLAG_WIZ)) - { - op->stats.food--; - if (op->contr->digestion < 0) - op->stats.food += op->contr->digestion; - else if (op->contr->digestion > 0 && random_roll (0, op->contr->digestion, op, PREFER_HIGH)) - op->stats.food = last_food; - } - } - if (max_sp > 1) - { - over_sp = (gen_sp + 10) / rate_sp; - if (over_sp > 0) - { - if (op->stats.sp < op->stats.maxsp) - { - op->stats.sp += over_sp > max_sp ? max_sp : over_sp; - if (random_roll (0, rate_sp - 1, op, PREFER_LOW) > ((gen_sp + 10) % rate_sp)) - op->stats.sp--; - if (op->stats.sp > op->stats.maxsp) - op->stats.sp = op->stats.maxsp; - } - op->last_sp = 0; - } - else - { - op->last_sp = rate_sp / (gen_sp < 20 ? 30 : gen_sp + 10); - } - } - else - { - op->last_sp = rate_sp / (gen_sp < 20 ? 30 : gen_sp + 10); - } - } - /* Regenerate Grace */ /* I altered this a little - maximum grace is ony achieved through prayer -b.t. */ if (--op->last_grace < 0) { if (op->stats.grace < op->stats.maxgrace / 2) op->stats.grace++; /* no penalty in food for regaining grace */ + if (max_grace > 1) { over_grace = (gen_grace < 20 ? 30 : gen_grace + 10) / rate_grace; @@ -2840,99 +2462,144 @@ /* wearing stuff doesn't detract from grace generation. */ } - /* Regenerate Hit Points */ - if (--op->last_heal < 0) + if (op->stats.food > 0) { - if (op->stats.hp < op->stats.maxhp) + /* Regenerate Spell Points */ + if (!op->contr->golem && --op->last_sp < 0) { - op->stats.hp++; - /* dms do not consume food */ - if (!QUERY_FLAG (op, FLAG_WIZ)) + gen_sp = gen_sp * 10 / (op->contr->gen_sp_armour < 10 ? 10 : op->contr->gen_sp_armour); + + if (op->stats.sp < op->stats.maxsp) { - op->stats.food--; - if (op->contr->digestion < 0) - op->stats.food += op->contr->digestion; - else if (op->contr->digestion > 0 && random_roll (0, op->contr->digestion, op, PREFER_HIGH)) - op->stats.food = last_food; + op->stats.sp++; + + /* dms do not consume food */ + if (!QUERY_FLAG (op, FLAG_WIZ)) + { + op->stats.food--; + + if (op->contr->digestion < 0) + op->stats.food += op->contr->digestion; + else if (op->contr->digestion > 0 && random_roll (0, op->contr->digestion, op, PREFER_HIGH)) + op->stats.food = last_food; + } + } + + if (max_sp > 1) + { + over_sp = (gen_sp + 10) / rate_sp; + if (over_sp > 0) + { + if (op->stats.sp < op->stats.maxsp) + { + op->stats.sp += over_sp > max_sp ? max_sp : over_sp; + + if (random_roll (0, rate_sp - 1, op, PREFER_LOW) > ((gen_sp + 10) % rate_sp)) + op->stats.sp--; + + if (op->stats.sp > op->stats.maxsp) + op->stats.sp = op->stats.maxsp; + } + + op->last_sp = 0; + } + else + op->last_sp = rate_sp / (gen_sp < 20 ? 30 : gen_sp + 10); } + else + op->last_sp = rate_sp / (gen_sp < 20 ? 30 : gen_sp + 10); } - if (max_hp > 1) + + /* Regenerate Hit Points */ + if (--op->last_heal < 0) { - over_hp = (gen_hp < 20 ? 30 : gen_hp + 10) / rate_hp; - if (over_hp > 0) + if (op->stats.hp < op->stats.maxhp) { - op->stats.sp += over_hp + (RANDOM () % rate_hp > ((gen_hp < 20 ? 30 : gen_hp + 10) % rate_hp)) ? -1 : 0; - op->last_heal = 0; + op->stats.hp++; + + /* dms do not consume food */ + if (!QUERY_FLAG (op, FLAG_WIZ)) + { + op->stats.food--; + + if (op->contr->digestion < 0) + op->stats.food += op->contr->digestion; + else if (op->contr->digestion > 0 && random_roll (0, op->contr->digestion, op, PREFER_HIGH)) + op->stats.food = last_food; + } } - else + + if (max_hp > 1) { - op->last_heal = rate_hp / (gen_hp < 20 ? 30 : gen_hp + 10); + over_hp = (gen_hp < 20 ? 30 : gen_hp + 10) / rate_hp; + + if (over_hp > 0) + { + op->stats.sp += over_hp + (RANDOM () % rate_hp > ((gen_hp < 20 ? 30 : gen_hp + 10) % rate_hp)) ? -1 : 0; + op->last_heal = 0; + } + else + op->last_heal = rate_hp / (gen_hp < 20 ? 30 : gen_hp + 10); } - } - else - { - op->last_heal = rate_hp / (gen_hp < 20 ? 30 : gen_hp + 10); + else + op->last_heal = rate_hp / (gen_hp < 20 ? 30 : gen_hp + 10); } } /* Digestion */ if (--op->last_eat < 0) { -#ifdef COZY_SERVER - int dg = op->contr->digestion >= 0 && op->contr->digestion < 2 ? 2 : op->contr->digestion; - int bonus = dg > 0 ? dg : 0, penalty = dg < 0 ? -dg : 0; -#else - int bonus = op->contr->digestion > 0 ? op->contr->digestion : 0, penalty = op->contr->digestion < 0 ? -op->contr->digestion : 0; -#endif + int bonus = max (0, op->contr->digestion), + penalty = max (0, -op->contr->digestion); + + op->last_eat = 25 * (1 + bonus) / (max (0, op->contr->gen_hp) + penalty + 1); - if (op->contr->gen_hp > 0) - op->last_eat = 25 * (1 + bonus) / (op->contr->gen_hp + penalty + 1); - else - op->last_eat = 25 * (1 + bonus) / (penalty + 1); /* dms do not consume food */ if (!QUERY_FLAG (op, FLAG_WIZ)) op->stats.food--; } - } - - if (op->contr->state == ST_PLAYING && op->stats.food < 0 && op->stats.hp >= 0) - { - object *tmp, *flesh = NULL; - for (tmp = op->inv; tmp != NULL; tmp = tmp->below) + if (op->stats.food < 0 && op->stats.hp >= 0) { - if (!QUERY_FLAG (tmp, FLAG_UNPAID)) + object *tmp, *flesh = 0; + + for (tmp = op->inv; tmp; tmp = tmp->below) { - if (tmp->type == FOOD || tmp->type == DRINK || tmp->type == POISON) + if (!QUERY_FLAG (tmp, FLAG_UNPAID)) { - new_draw_info (NDI_UNIQUE, 0, op, "You blindly grab for a bite of food."); - manual_apply (op, tmp, 0); - if (op->stats.food >= 0 || op->stats.hp < 0) - break; - } - else if (tmp->type == FLESH) - flesh = tmp; - } /* End if paid for object */ - } /* end of for loop */ - /* If player is still starving, it means they don't have any food, so - * eat flesh instead. - */ - if (op->stats.food < 0 && op->stats.hp >= 0 && flesh) - { - new_draw_info (NDI_UNIQUE, 0, op, "You blindly grab for a bite of food."); - manual_apply (op, flesh, 0); + if (tmp->type == FOOD || tmp->type == DRINK || tmp->type == POISON) + { + new_draw_info (NDI_UNIQUE, 0, op, "You blindly grab for a bite of food."); + manual_apply (op, tmp, 0); + if (op->stats.food >= 0 || op->stats.hp < 0) + break; + } + else if (tmp->type == FLESH) + flesh = tmp; + } /* End if paid for object */ + } /* end of for loop */ + + /* If player is still starving, it means they don't have any food, so + * eat flesh instead. + */ + if (op->stats.food < 0 && op->stats.hp >= 0 && flesh) + { + new_draw_info (NDI_UNIQUE, 0, op, "You blindly grab for a bite of food."); + manual_apply (op, flesh, 0); + } } - } /* end if player is starving */ - while (op->stats.food < 0 && op->stats.hp > 0) - op->stats.food++, op->stats.hp--; + if (op->stats.food < 0) + { + op->stats.hp += op->stats.food; + op->stats.food = 0; + } - if (!op->contr->state && !QUERY_FLAG (op, FLAG_WIZ) && (op->stats.hp < 0 || op->stats.food < 0)) - kill_player (op); + if (op->stats.hp < 0 && !QUERY_FLAG (op, FLAG_WIZ)) + kill_player (op); + } } - - /* If the player should die (lack of hp, food, etc), we call this. * op is the player in jeopardy. If the player can not be saved (not * permadeath, no lifesave), this will take care of removing the player @@ -2941,17 +2608,9 @@ void kill_player (object *op) { - char buf[MAX_BUF]; int x, y; - - //int i; + char buf[MAX_BUF]; maptile *map; /* this is for resurrection */ - - /* int z; - int num_stats_lose; - int lost_a_stat; - int lose_this_stat; - int this_stat; */ int will_kill_again; archetype *at; object *tmp; @@ -2959,7 +2618,6 @@ if (save_life (op)) return; - /* If player dies on BATTLEGROUND, no stat/exp loss! For Combat-Arenas * in cities ONLY!!! It is very important that this doesn't get abused. * Look at op_on_battleground() for more info --AndreasV @@ -2971,42 +2629,36 @@ /* restore player */ at = archetype::find ("poisoning"); - tmp = present_arch_in_ob (at, op); - if (tmp) + if (object *tmp = present_arch_in_ob (at, op)) { - remove_ob (tmp); - free_object (tmp); + tmp->destroy (); new_draw_info (NDI_UNIQUE, 0, op, "Your body feels cleansed"); } at = archetype::find ("confusion"); - tmp = present_arch_in_ob (at, op); - if (tmp) + if (object *tmp = present_arch_in_ob (at, op)) { - remove_ob (tmp); - free_object (tmp); + tmp->destroy (); new_draw_info (NDI_UNIQUE, 0, tmp, "Your mind feels clearer"); } - cure_disease (op, 0); /* remove any disease */ + cure_disease (op, 0, 0); /* remove any disease */ op->stats.hp = op->stats.maxhp; if (op->stats.food <= 0) op->stats.food = 999; /* create a bodypart-trophy to make the winner happy */ - tmp = arch_to_object (archetype::find ("finger")); - if (tmp != NULL) + if (object *tmp = arch_to_object (archetype::find ("finger"))) { - sprintf (buf, "%s's finger", &op->name); - tmp->name = buf; - sprintf (buf, " This finger has been cut off %s\n" - " the %s, when he was defeated at\n level %d by %s.\n", - &op->name, op->contr->title, (int) (op->level), op->contr->killer); - tmp->msg = buf; - tmp->value = 0, tmp->material = 0, tmp->type = 0; - tmp->materialname = NULL; - tmp->x = op->x, tmp->y = op->y; - insert_ob_in_map (tmp, op->map, op, 0); + tmp->name = format ("%s's finger" , &op->name); + tmp->name_pl = format ("%s's fingers", &op->name); + tmp->msg = format ( + "This finger has been cut off of %s the %s, when he was defeated at level %d by %s.\n", + &op->name, op->contr->title, (int) (op->level), op->contr->killer + ); + tmp->value = 0, tmp->type = 0; + tmp->materialname = "organics"; + tmp->insert_at (op, tmp); } /* teleport defeated player to new destination */ @@ -3020,373 +2672,270 @@ command_kill_pets (op, 0); if (op->stats.food < 0) - { - if (op->contr->explore) - { - new_draw_info (NDI_UNIQUE, 0, op, "You would have starved, but you are"); - new_draw_info (NDI_UNIQUE, 0, op, "in explore mode, so..."); - op->stats.food = 999; - return; - } - sprintf (buf, "%s starved to death.", &op->name); - strcpy (op->contr->killer, "starvation"); - } - else - { - if (op->contr->explore) - { - new_draw_info (NDI_UNIQUE, 0, op, "You would have died, but you are"); - new_draw_info (NDI_UNIQUE, 0, op, "in explore mode, so..."); - op->stats.hp = op->stats.maxhp; - return; - } - sprintf (buf, "%s died.", &op->name); - } - play_sound_player_only (op->contr, SOUND_PLAYER_DIES, 0, 0); + strcpy (op->contr->killer, "starvation"); + + op->contr->play_sound (sound_find ("player_dies")); /* save the map location for corpse, gravestone */ - x = op->x; - y = op->y; + x = op->x; + y = op->y; map = op->map; + /* NOT_PERMADEATH code. This basically brings the character back to + * life if they are dead - it takes some exp and a random stat. + * See the config.h file for a little more in depth detail about this. + */ - if (settings.not_permadeth == TRUE) + /* Basically two ways to go - remove a stat permanently, or just + * make it depletion. This bunch of code deals with that aspect + * of death. + */ +#ifndef COZY_SERVER + if (settings.balanced_stat_loss) { - /* NOT_PERMADEATH code. This basically brings the character back to - * life if they are dead - it takes some exp and a random stat. - * See the config.h file for a little more in depth detail about this. - */ + /* If stat loss is permanent, lose one stat only. */ + /* Lower level chars don't lose as many stats because they suffer + more if they do. */ + /* Higher level characters can afford things such as potions of + restoration, or better, stat potions. So we slug them that + little bit harder. */ + /* GD */ + if (settings.stat_loss_on_death) + num_stats_lose = 1; + else + num_stats_lose = 1 + op->level / BALSL_NUMBER_LOSSES_RATIO; + } + else + num_stats_lose = 1; - /* Basically two ways to go - remove a stat permanently, or just - * make it depletion. This bunch of code deals with that aspect - * of death. - */ -#ifndef COZY_SERVER - if (settings.balanced_stat_loss) + lost_a_stat = 0; + + for (z = 0; z < num_stats_lose; z++) + { + i = RANDOM () % NUM_STATS; + + if (settings.stat_loss_on_death) { - /* If stat loss is permanent, lose one stat only. */ - /* Lower level chars don't lose as many stats because they suffer - more if they do. */ - /* Higher level characters can afford things such as potions of - restoration, or better, stat potions. So we slug them that - little bit harder. */ - /* GD */ - if (settings.stat_loss_on_death) - num_stats_lose = 1; - else - num_stats_lose = 1 + op->level / BALSL_NUMBER_LOSSES_RATIO; + /* Pick a random stat and take a point off it. Tell the player + * what he lost. + */ + change_attr_value (&(op->stats), i, -1); + check_stat_bounds (&(op->stats)); + change_attr_value (&(op->contr->orig_stats), i, -1); + check_stat_bounds (&(op->contr->orig_stats)); + new_draw_info (NDI_UNIQUE, 0, op, lose_msg[i]); + lost_a_stat = 1; } else { - num_stats_lose = 1; - } - lost_a_stat = 0; - - for (z = 0; z < num_stats_lose; z++) - { - i = RANDOM () % NUM_STATS; + /* deplete a stat */ + archetype *deparch = archetype::find ("depletion"); + object *dep; - if (settings.stat_loss_on_death) + dep = present_arch_in_ob (deparch, op); + if (!dep) { - /* Pick a random stat and take a point off it. Tell the player - * what he lost. - */ - change_attr_value (&(op->stats), i, -1); - check_stat_bounds (&(op->stats)); - change_attr_value (&(op->contr->orig_stats), i, -1); - check_stat_bounds (&(op->contr->orig_stats)); - new_draw_info (NDI_UNIQUE, 0, op, lose_msg[i]); - lost_a_stat = 1; + dep = arch_to_object (deparch); + insert_ob_in_ob (dep, op); } - else + lose_this_stat = 1; + if (settings.balanced_stat_loss) { - /* deplete a stat */ - archetype *deparch = archetype::find ("depletion"); - object *dep; - - dep = present_arch_in_ob (deparch, op); - if (!dep) - { - dep = arch_to_object (deparch); - insert_ob_in_ob (dep, op); - } - lose_this_stat = 1; - if (settings.balanced_stat_loss) - { - /* GD */ - /* Get the stat that we're about to deplete. */ - this_stat = get_attr_value (&(dep->stats), i); - if (this_stat < 0) - { - int loss_chance = 1 + op->level / BALSL_LOSS_CHANCE_RATIO; - int keep_chance = this_stat * this_stat; + /* GD */ + /* Get the stat that we're about to deplete. */ + this_stat = get_attr_value (&(dep->stats), i); + if (this_stat < 0) + { + int loss_chance = 1 + op->level / BALSL_LOSS_CHANCE_RATIO; + int keep_chance = this_stat * this_stat; - /* Yes, I am paranoid. Sue me. */ - if (keep_chance < 1) - keep_chance = 1; + /* Yes, I am paranoid. Sue me. */ + if (keep_chance < 1) + keep_chance = 1; - /* There is a maximum depletion total per level. */ - if (this_stat < -1 - op->level / BALSL_MAX_LOSS_RATIO) - { - lose_this_stat = 0; - /* Take loss chance vs keep chance to see if we - retain the stat. */ - } - else - { - if (random_roll (0, loss_chance + keep_chance - 1, op, PREFER_LOW) < keep_chance) - lose_this_stat = 0; - /* LOG(llevDebug, "Determining stat loss. Stat: %d Keep: %d Lose: %d Result: %s.\n", - this_stat, keep_chance, loss_chance, - lose_this_stat?"LOSE":"KEEP"); */ - } + /* There is a maximum depletion total per level. */ + if (this_stat < -1 - op->level / BALSL_MAX_LOSS_RATIO) + { + lose_this_stat = 0; + /* Take loss chance vs keep chance to see if we + retain the stat. */ + } + else + { + if (random_roll (0, loss_chance + keep_chance - 1, op, PREFER_LOW) < keep_chance) + lose_this_stat = 0; + /* LOG(llevDebug, "Determining stat loss. Stat: %d Keep: %d Lose: %d Result: %s.\n", + this_stat, keep_chance, loss_chance, + lose_this_stat?"LOSE":"KEEP"); */ } } + } - if (lose_this_stat) + if (lose_this_stat) + { + this_stat = get_attr_value (&(dep->stats), i); + /* We could try to do something clever like find another + * stat to reduce if this fails. But chances are, if + * stats have been depleted to -50, all are pretty low + * and should be roughly the same, so it shouldn't make a + * difference. + */ + if (this_stat >= -50) { - this_stat = get_attr_value (&(dep->stats), i); - /* We could try to do something clever like find another - * stat to reduce if this fails. But chances are, if - * stats have been depleted to -50, all are pretty low - * and should be roughly the same, so it shouldn't make a - * difference. - */ - if (this_stat >= -50) - { - change_attr_value (&(dep->stats), i, -1); - SET_FLAG (dep, FLAG_APPLIED); - new_draw_info (NDI_UNIQUE, 0, op, lose_msg[i]); - fix_player (op); - lost_a_stat = 1; - } + change_attr_value (&(dep->stats), i, -1); + SET_FLAG (dep, FLAG_APPLIED); + new_draw_info (NDI_UNIQUE, 0, op, lose_msg[i]); + op->update_stats (); + lost_a_stat = 1; } } } - /* If no stat lost, tell the player. */ - if (!lost_a_stat) - { - /* determine_god() seems to not work sometimes... why is this? - Should I be using something else? GD */ - const char *god = determine_god (op); + } + /* If no stat lost, tell the player. */ + if (!lost_a_stat) + { + /* determine_god() seems to not work sometimes... why is this? + Should I be using something else? GD */ + const char *god = determine_god (op); - if (god && (strcmp (god, "none"))) - new_draw_info_format (NDI_UNIQUE, 0, op, "For a brief " "moment you feel the holy presence of %s protecting" " you.", god); - else - new_draw_info (NDI_UNIQUE, 0, op, "For a brief moment you" " feel a holy presence protecting you."); - } + if (god && (strcmp (god, "none"))) + new_draw_info_format (NDI_UNIQUE, 0, op, "For a brief moment you feel the holy presence of %s protecting" " you.", god); + else + new_draw_info (NDI_UNIQUE, 0, op, "For a brief moment you feel a holy presence protecting you."); + } +#else + new_draw_info (NDI_UNIQUE, 0, op, "For a brief moment you" " feel a holy presence protecting you from losing yourself completely."); #endif - new_draw_info (NDI_UNIQUE, 0, op, "For a brief moment you" " feel a holy presence protecting you from losing yourself completely."); - - /* Put a gravestone up where the character 'almost' died. List the - * exp loss on the stone. - */ - tmp = arch_to_object (archetype::find ("gravestone")); - sprintf (buf, "%s's gravestone", &op->name); - tmp->name = buf; - sprintf (buf, "%s's gravestones", &op->name); - tmp->name_pl = buf; - sprintf (buf, "RIP\nHere rests the hero %s the %s,\n" "who was killed\n" "by %s.\n", &op->name, op->contr->title, op->contr->killer); - tmp->msg = buf; - tmp->x = op->x, tmp->y = op->y; - insert_ob_in_map (tmp, op->map, NULL, 0); - - /**************************************/ - /* */ - /* Subtract the experience points, */ - /* if we died cause of food, give us */ - /* food, and reset HP's... */ - /* */ - - /**************************************/ - - /* remove any poisoning and confusion the character may be suffering. */ - /* restore player */ - at = archetype::find ("poisoning"); - tmp = present_arch_in_ob (at, op); - if (tmp) - { - remove_ob (tmp); - free_object (tmp); - new_draw_info (NDI_UNIQUE, 0, op, "Your body feels cleansed"); - } - - at = archetype::find ("confusion"); - tmp = present_arch_in_ob (at, op); - if (tmp) - { - remove_ob (tmp); - free_object (tmp); - new_draw_info (NDI_UNIQUE, 0, tmp, "Your mind feels clearer"); - } - cure_disease (op, 0); /* remove any disease */ - - /*add_exp(op, (op->stats.exp * -0.20)); */ - apply_death_exp_penalty (op); - if (op->stats.food < 100) - op->stats.food = 900; - op->stats.hp = op->stats.maxhp; - op->stats.sp = MAX (op->stats.sp, op->stats.maxsp); - op->stats.grace = MAX (op->stats.grace, op->stats.maxgrace); - - /* - * Check to see if the player is in a shop. IF so, then check to see if - * the player has any unpaid items. If so, remove them and put them back - * in the map. - */ - - if (is_in_shop (op)) - remove_unpaid_objects (op->inv, op); - - /****************************************/ - /* */ - /* Move player to his current respawn- */ - /* position (usually last savebed) */ - /* */ - /****************************************/ - - enter_player_savebed (op); - - /* Save the player before inserting the force to reduce - * chance of abuse. - */ - op->contr->braced = 0; - save_player (op, 1); - - /* it is possible that the player has blown something up - * at his savebed location, and that can have long lasting - * spell effects. So first see if there is a spell effect - * on the space that might harm the player. - */ - will_kill_again = 0; - for (tmp = get_map_ob (op->map, op->x, op->y); tmp; tmp = tmp->above) - { - if (tmp->type == SPELL_EFFECT) - will_kill_again |= tmp->attacktype; - } - if (will_kill_again) - { - object *force; - int at; + /* Put a gravestone up where the character 'almost' died. List the + * exp loss on the stone. + */ + tmp = arch_to_object (archetype::find ("gravestone")); + sprintf (buf, "%s's gravestone", &op->name); + tmp->name = buf; + sprintf (buf, "%s's gravestones", &op->name); + tmp->name_pl = buf; + sprintf (buf, "RIP\nHere rests the hero %s the %s,\n" "who was killed\n" "by %s.\n", &op->name, op->contr->title, op->contr->killer); + tmp->msg = buf; + tmp->x = op->x, tmp->y = op->y; + insert_ob_in_map (tmp, op->map, NULL, 0); + + /**************************************/ + /* */ + /* Subtract the experience points, */ + /* if we died cause of food, give us */ + /* food, and reset HP's... */ + /* */ + /**************************************/ + + /* remove any poisoning and confusion the character may be suffering. */ + /* restore player */ + at = archetype::find ("poisoning"); + tmp = present_arch_in_ob (at, op); + + if (tmp) + { + tmp->destroy (); + new_draw_info (NDI_UNIQUE, 0, op, "Your body feels cleansed"); + } + + at = archetype::find ("confusion"); + tmp = present_arch_in_ob (at, op); + if (tmp) + { + tmp->destroy (); + new_draw_info (NDI_UNIQUE, 0, tmp, "Your mind feels clearer"); + } + + cure_disease (op, 0, 0); /* remove any disease */ + + /*add_exp(op, (op->stats.exp * -0.20)); */ + apply_death_exp_penalty (op); + if (op->stats.food < 100) + op->stats.food = 900; + op->stats.hp = op->stats.maxhp; + op->stats.sp = MAX (op->stats.sp, op->stats.maxsp); + op->stats.grace = MAX (op->stats.grace, op->stats.maxgrace); - force = get_archetype (FORCE_NAME); - /* 50 ticks should be enough time for the spell to abate */ - force->speed = 0.1; - force->speed_left = -5.0; - SET_FLAG (force, FLAG_APPLIED); - for (at = 0; at < NROFATTACKS; at++) - { - if (will_kill_again & (1 << at)) - force->resist[at] = 100; - } - insert_ob_in_ob (force, op); - fix_player (op); + /* + * Check to see if the player has any unpaid items. If so, remove them + * and put them back in the map. + */ + op->drop_unpaid_items (); - } + /****************************************/ + /* */ + /* Move player to his current respawn- */ + /* position (usually last savebed) */ + /* */ + /****************************************/ + + enter_player_savebed (op); + + op->contr->braced = 0; + + /* it is possible that the player has blown something up + * at his savebed location, and that can have long lasting + * spell effects. So first see if there is a spell effect + * on the space that might harm the player. + */ + will_kill_again = 0; + for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp; tmp = tmp->above) + if (tmp->type == SPELL_EFFECT) + will_kill_again |= tmp->attacktype; - new_draw_info (NDI_UNIQUE, 0, op, "YOU HAVE DIED."); - return; - } /* NOT_PERMADETH */ - else + if (will_kill_again) { - /* If NOT_PERMADETH is set, then the rest of this is not reachable. This - * should probably be embedded in an else statement. - */ + object *force; + int at; - op->contr->party = NULL; - if (settings.set_title == TRUE) - op->contr->own_title[0] = '\0'; - new_draw_info (NDI_UNIQUE | NDI_ALL, 0, NULL, buf); - check_score (op); - if (op->contr->ranges[range_golem] != NULL) - { - remove_friendly_object (op->contr->ranges[range_golem]); - remove_ob (op->contr->ranges[range_golem]); - free_object (op->contr->ranges[range_golem]); - op->contr->ranges[range_golem] = NULL; - op->contr->golem_count = 0; - } - loot_object (op); /* Remove some of the items for good */ - remove_ob (op); - op->direction = 0; - - if (!QUERY_FLAG (op, FLAG_WAS_WIZ) && op->stats.exp) - { - delete_character (op->name, 0); - if (settings.resurrection == TRUE) - { - /* save playerfile sans equipment when player dies - ** then save it as player.pl.dead so that future resurrection - ** type spells will work on them nicely - */ - delete_character (op->name, 0); - op->stats.hp = op->stats.maxhp; - op->stats.food = 999; - - /* set the location of where the person will reappear when */ - /* maybe resurrection code should fix map also */ - strcpy (op->contr->maplevel, settings.emergency_mapname); - if (op->map != NULL) - op->map = NULL; - op->x = settings.emergency_x; - op->y = settings.emergency_y; - save_player (op, 0); - op->map = map; - /* please see resurrection.c: peterm */ - dead_player (op); - } - else - delete_character (op->name, 1); - } + force = get_archetype (FORCE_NAME); + /* 50 ticks should be enough time for the spell to abate */ + force->speed = 0.1f; + force->speed_left = -5.f; + SET_FLAG (force, FLAG_APPLIED); + for (at = 0; at < NROFATTACKS; at++) + if (will_kill_again & (1 << at)) + force->resist[at] = 100; - play_again (op); + insert_ob_in_ob (force, op); + op->update_stats (); - /* peterm: added to create a corpse at deathsite. */ - tmp = arch_to_object (archetype::find ("corpse_pl")); - sprintf (buf, "%s", &op->name); - tmp->name = tmp->name_pl = buf; - tmp->level = op->level; - tmp->x = x; - tmp->y = y; - tmp->msg = gravestone_text (op); - SET_FLAG (tmp, FLAG_UNIQUE); - insert_ob_in_map (tmp, map, NULL, 0); } -} + new_draw_info (NDI_UNIQUE, 0, op, "YOU HAVE DIED."); +} void loot_object (object *op) { /* Grab and destroy some treasure */ object *tmp, *tmp2, *next; - if (op->container) - { /* close open sack first */ - esrv_apply_container (op, op->container); - } + op->close_container (); /* close open sack first */ - for (tmp = op->inv; tmp != NULL; tmp = next) + for (tmp = op->inv; tmp; tmp = next) { next = tmp->below; - if (tmp->type == EXPERIENCE || tmp->invisible) + + if (tmp->invisible) continue; - remove_ob (tmp); + + tmp->remove (); tmp->x = op->x, tmp->y = op->y; + if (tmp->type == CONTAINER) - { /* empty container to ground */ - loot_object (tmp); - } - if (!QUERY_FLAG (tmp, FLAG_UNIQUE) && (QUERY_FLAG (tmp, FLAG_STARTEQUIP) || QUERY_FLAG (tmp, FLAG_NO_DROP) || !(RANDOM () % 3))) + loot_object (tmp); /* empty container to ground */ + + if (!QUERY_FLAG (tmp, FLAG_UNIQUE) && (QUERY_FLAG (tmp, FLAG_STARTEQUIP) || QUERY_FLAG (tmp, FLAG_NO_DROP) || !(rndm (3)))) { if (tmp->nrof > 1) { tmp2 = get_split_ob (tmp, 1 + RANDOM () % (tmp->nrof - 1)); - free_object (tmp2); + tmp2->destroy (); insert_ob_in_map (tmp, op->map, NULL, 0); } else - free_object (tmp); + tmp->destroy (); } else insert_ob_in_map (tmp, op->map, NULL, 0); @@ -3398,19 +2947,16 @@ * what needs to be fixed. Refresh windows and fix speed if anything * was changed. */ - void fix_weight (void) { - player *pl; - - for (pl = first_player; pl != NULL; pl = pl->next) + for_all_players (pl) { int old = pl->ob->carrying, sum = sum_weight (pl->ob); if (old == sum) continue; - fix_player (pl->ob); + pl->ob->update_stats (); LOG (llevDebug, "Fixed inventory in %s (%d -> %d)\n", &pl->ob->name, old, sum); } } @@ -3418,19 +2964,15 @@ void fix_luck (void) { - player *pl; - - for (pl = first_player; pl != NULL; pl = pl->next) - if (!pl->ob->contr->state) - change_luck (pl->ob, 0); + for_all_players (pl) + if (!pl->ob->contr->ns->state) + pl->ob->change_luck (0); } - /* cast_dust() - handles op throwing objects of type 'DUST'. * This is much simpler in the new spell code - we basically * just treat this as any other spell casting object. */ - void cast_dust (object *op, object *throw_ob, int dir) { @@ -3461,9 +3003,7 @@ cast_spell (op, throw_ob, dir, spob, NULL); - if (!QUERY_FLAG (throw_ob, FLAG_REMOVED)) - remove_ob (throw_ob); - free_object (throw_ob); + throw_ob->destroy (); } void @@ -3471,27 +3011,22 @@ { op->hide = 0; op->invisible = 0; + if (op->type == PLAYER) { op->contr->tmp_invis = 0; op->contr->invis_race = 0; } - update_object (op, UP_OBJ_FACE); + + update_object (op, UP_OBJ_CHANGE); } int is_true_undead (object *op) { - object *tmp = NULL; - - if (QUERY_FLAG (&op->arch->clone, FLAG_UNDEAD)) + if (QUERY_FLAG (op->arch, FLAG_UNDEAD)) return 1; - if (op->type == PLAYER) - for (tmp = op->inv; tmp; tmp = tmp->below) - if (tmp->type == EXPERIENCE && tmp->stats.Wis) - if (QUERY_FLAG (tmp, FLAG_UNDEAD)) - return 1; return 0; } @@ -3499,7 +3034,6 @@ * the hideability of this object. Positive levels * indicate greater hideability. */ - int hideability (object *ob) { @@ -3519,13 +3053,14 @@ level = -(10 + (2 * ob->map->darkness)); /* scan through all nearby squares for terrain to hide in */ - for (i = 0, x = ob->x, y = ob->y; i < 9; i++, x = ob->x + freearr_x[i], y = ob->y + freearr_y[i]) + for (i = 0, x = ob->x, y = ob->y; + i <= SIZEOFFREE1; + i++, x = ob->x + freearr_x[i], y = ob->y + freearr_y[i]) { mflag = get_map_flags (ob->map, NULL, x, y, NULL, NULL); if (mflag & P_OUT_OF_MAP) - { - continue; - } + continue; + if (mflag & P_BLOCKSVIEW) /* something to hide near! */ level += 2; else /* open terrain! */ @@ -3543,7 +3078,6 @@ * AND, for players, if they move into a ridiculously unhideable * spot (surrounded by clear terrain in broad daylight). -b.t. */ - void do_hidden_move (object *op) { @@ -3557,19 +3091,19 @@ /* its *extremely* hard to run and sneak/hide at the same time! */ if (op->type == PLAYER && op->contr->run_on) - { - if (!skop || num >= skop->level) - { - new_draw_info (NDI_UNIQUE, 0, op, "You ran too much! You are no longer hidden!"); - make_visible (op); - return; - } - else - num += 20; - } + if (!skop || num >= skop->level) + { + new_draw_info (NDI_UNIQUE, 0, op, "You ran too much! You are no longer hidden!"); + make_visible (op); + return; + } + else + num += 20; + num += op->map->difficulty; hide = hideability (op); /* modify by terrain hidden level */ num -= hide; + if ((op->type == PLAYER && hide < -10) || ((op->invisible -= num) <= 0)) { make_visible (op); @@ -3577,9 +3111,7 @@ new_draw_info (NDI_UNIQUE, 0, op, "You moved out of hiding! You are visible!"); } else if (op->type == PLAYER && skop) - { - change_exp (op, calc_skill_exp (op, NULL, skop), skop->skill, 0); - } + change_exp (op, calc_skill_exp (op, NULL, skop), skop->skill, 0); } /* determine if who is standing near a hostile creature. */ @@ -3616,7 +3148,7 @@ if (OB_TYPE_MOVE_BLOCK (who, GET_MAP_MOVE_BLOCK (m, x, y))) continue; - for (tmp = get_map_ob (m, x, y); tmp; tmp = tmp->above) + for (tmp = GET_MAP_OB (m, x, y); tmp; tmp = tmp->above) { if ((player ||friendly) &&QUERY_FLAG (tmp, FLAG_MONSTER) && !QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE)) return 1; @@ -3637,14 +3169,13 @@ * a pile (say a coin under a table would return "viewable" * by this routine). Another question, should we be * concerned with the direction the player is looking - * in? Realistically, most of use cant see stuff behind + * in? Realistically, most of us can't see stuff behind * our backs...on the other hand, does the "facing" direction - * imply the way your head, or body is facing? Its possible + * imply the way your head, or body is facing? It's possible * for them to differ. Sigh, this fctn could get a bit more complex. * -b.t. * This function is now map tiling safe. */ - int player_can_view (object *pl, object *op) { @@ -3656,36 +3187,37 @@ LOG (llevError, "player_can_view() called for non-player object\n"); return -1; } + if (!pl || !op) return 0; - if (op->head) - { - op = op->head; - } + op = op->head_ (); + get_rangevector (pl, op, &rv, 0x1); /* starting with the 'head' part, lets loop * through the object and find if it has any - * part that is in the los array but isnt on + * part that is in the los array but isn't on * a blocked los square. * we use the archetype to figure out offsets. */ while (op) { - dx = rv.distance_x + op->arch->clone.x; - dy = rv.distance_y + op->arch->clone.y; + dx = rv.distance_x + op->arch->x; + dy = rv.distance_y + op->arch->y; /* only the viewable area the player sees is updated by LOS * code, so we need to restrict ourselves to that range of values * for any meaningful values. */ - if (FABS (dx) <= (pl->contr->socket.mapx / 2) && - FABS (dy) <= (pl->contr->socket.mapy / 2) && - !pl->contr->blocked_los[dx + (pl->contr->socket.mapx / 2)][dy + (pl->contr->socket.mapy / 2)]) + if (abs (dx) <= (pl->contr->ns->mapx / 2) && + abs (dy) <= (pl->contr->ns->mapy / 2) && + !pl->contr->blocked_los[dx + (pl->contr->ns->mapx / 2)][dy + (pl->contr->ns->mapy / 2)]) return 1; + op = op->more; } + return 0; } @@ -3698,7 +3230,6 @@ int action_makes_visible (object *op) { - if (op->invisible && QUERY_FLAG (op, FLAG_ALIVE)) { if (QUERY_FLAG (op, FLAG_MAKE_INVIS)) @@ -3714,6 +3245,7 @@ return 1; } } + return 0; } @@ -3728,42 +3260,44 @@ int op_on_battleground (object *op, int *x, int *y) { - object *tmp; - /* A battleground-tile needs the following attributes to be valid: * is_floor 1 (has to be the FIRST floor beneath the player's feet), * name="battleground", no_pick 1, type=58 (type BATTLEGROUND) * and the exit-coordinates sp/hp must both be > 0. * => The intention here is to prevent abuse of the battleground- * feature (like pickable or hidden battleground tiles). */ - for (tmp = op->below; tmp != NULL; tmp = tmp->below) + for (object *tmp = op->below; tmp; tmp = tmp->below) { if (QUERY_FLAG (tmp, FLAG_IS_FLOOR)) { - if (QUERY_FLAG (tmp, FLAG_NO_PICK) && - strcmp (tmp->name, "battleground") == 0 && tmp->type == BATTLEGROUND && EXIT_X (tmp) && EXIT_Y (tmp)) + if (QUERY_FLAG (tmp, FLAG_NO_PICK) + && tmp->type == BATTLEGROUND + && tmp->name == shstr_battleground + && EXIT_X (tmp) && EXIT_Y (tmp)) { - /*before we assign the exit, check if this is a teambattle */ + /* before we assign the exit, check if this is a teambattle */ if (EXIT_ALT_X (tmp) && EXIT_ALT_Y (tmp) && EXIT_PATH (tmp)) { - object *invtmp; - - for (invtmp = op->inv; invtmp != NULL; invtmp = invtmp->below) + for (object *invtmp = op->inv; invtmp; invtmp = invtmp->below) { - if (invtmp->type == FORCE && invtmp->slaying && !strcmp (EXIT_PATH (tmp), invtmp->slaying)) + if (invtmp->type == FORCE && invtmp->slaying && tmp->slaying == invtmp->slaying) { - if (x != NULL && y != NULL) + if (x && y) *x = EXIT_ALT_X (tmp), *y = EXIT_ALT_Y (tmp); + return 1; } } } - if (x != NULL && y != NULL) + + if (x && y) *x = EXIT_X (tmp), *y = EXIT_Y (tmp); + return 1; } } } + /* If we got here, did not find a battleground */ return 0; } @@ -3789,27 +3323,27 @@ /* get the appropriate treasurelist */ if (atnr == ATNR_FIRE) - trlist = find_treasurelist ("dragon_ability_fire"); + trlist = treasurelist::find (shstr_dragon_ability_fire); else if (atnr == ATNR_COLD) - trlist = find_treasurelist ("dragon_ability_cold"); + trlist = treasurelist::find (shstr_dragon_ability_cold); else if (atnr == ATNR_ELECTRICITY) - trlist = find_treasurelist ("dragon_ability_elec"); + trlist = treasurelist::find (shstr_dragon_ability_elec); else if (atnr == ATNR_POISON) - trlist = find_treasurelist ("dragon_ability_poison"); + trlist = treasurelist::find (shstr_dragon_ability_poison); if (trlist == NULL || who->type != PLAYER) return; for (i = 0, tr = trlist->items; tr != NULL && i < level - 1; tr = tr->next, i++); - if (tr == NULL || tr->item == NULL) + if (!tr || !tr->item) { /* LOG(llevDebug, "-> no more treasure for %s\n", change_resist_msg[atnr]); */ return; } /* everything seems okay - now bring on the gift: */ - item = &(tr->item->clone); + item = tr->item; if (item->type == SPELL) { @@ -3878,8 +3412,10 @@ object *skin; /* first get the dragon skin force */ - for (skin = who->inv; skin != NULL && strcmp (skin->arch->name, "dragon_skin_force") != 0; skin = skin->below); - if (skin == NULL) + for (skin = who->inv; skin && !(skin->arch->archname == shstr_dragon_skin_force); skin = skin->below) + ; + + if (!skin) return; /* adding new spellpath attunements */ @@ -3934,17 +3470,51 @@ void player_unready_range_ob (player *pl, object *ob) { - rangetype i; + if (pl->ob->current_weapon == ob) + pl->ob->current_weapon = 0; - for (i = (rangetype) 0; i < range_size; i = (rangetype) ((int) i + 1)) - { - if (pl->ranges[i] == ob) - { - pl->ranges[i] = NULL; - if (pl->shoottype == i) - { - pl->shoottype = range_none; - } - } - } + if (pl->combat_ob == ob) + pl->combat_ob = 0; + + if (pl->ranged_ob == ob) + pl->ranged_ob = 0; +} + +sint8 +player::visibility_at (maptile *map, int x, int y) const +{ + if (!ns) + return 0; + + int dx, dy; + if (!adjacent_map (map, ns->current_map, &dx, &dy)) + return 0; + + x += dx - ns->current_x + ns->mapx / 2; + y += dy - ns->current_y + ns->mapy / 2; + + if (!IN_RANGE_EXC (x, 0, ns->mapx) || !IN_RANGE_EXC (y, 0, ns->mapy)) + return 0; + + return 100 - blocked_los [x][y]; +} + +void +player::infobox (const char *title, const char *msg, int color) +{ + send_msg (color | NDI_DEF | NDI_REPLY | NDI_CLEAR, title, msg); +} + +void +player::statusmsg (const char *msg, int color) +{ + send_msg (color | NDI_REPLY, INFO_CHANNEL, msg); } + +void +player::failmsg (const char *msg, int color) +{ + play_sound (sound_find ("generic_failure")); + statusmsg (msg, color); +} +