--- deliantra/server/server/player.C 2007/03/18 03:05:40 1.109 +++ deliantra/server/server/player.C 2007/11/08 19:43:27 1.174 @@ -1,29 +1,27 @@ /* - * CrossFire, A Multiplayer game - * - * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team - * Copyright (C) 2002 Mark Wedel & Crossfire Development Team - * Copyright (C) 1992 Frank Tore Johansen - * - * This program is free software; you can redistribute it and/or modify + * 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 2 of the License, or + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * The author can be reached via e-mail to + * along with this program. If not, see . + * + * The authors can be reached via e-mail to */ #include -#include #include #include #include @@ -160,19 +158,6 @@ } void -player::enter_map () -{ - object *tmp = object::create (); - - EXIT_PATH (tmp) = maplevel; - EXIT_X (tmp) = ob->x; - EXIT_Y (tmp) = ob->y; - ob->enter_exit (tmp); - - tmp->destroy (); -} - -void player::activate () { if (active) @@ -184,7 +169,6 @@ ob->activate_recursive (); CLEAR_FLAG (ob, FLAG_FRIENDLY); add_friendly_object (ob); - enter_map (); } void @@ -196,19 +180,22 @@ terminate_all_pets (ob); remove_friendly_object (ob); ob->deactivate_recursive (); - maplevel = ob->map->path; + + if (ob->map) + maplevel = ob->map->path; + ob->remove (); + ob->enemy = 0; // sometimes keeps an extra refcount on itself ob->map = 0; party = 0; - // for weird reasons, this is often "ob", keeping a circular reference - ranges [range_skill] = 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 changed, rationalises, and fixes some incorrect settings +// also changes, rationalises, and fixes some incorrect settings void player::connect (client *ns) { @@ -222,37 +209,31 @@ ns->update_look = 0; ns->look_position = 0; - clear_los (ob); + clear_los (this); ns->reset_stats (); /* make sure he's a player -- needed because of class change. */ ob->type = PLAYER; // we are paranoid - ob->race = ob->arch->clone.race; - - if (!legal_range (ob, shoottype)) - shoottype = range_none; + 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->clone.name); + assign (title, ob->arch->object::name); /* if it's a dragon player, set the correct title here */ if (is_dragon_pl (ob)) { object *tmp, *abil = 0, *skin = 0; - shstr_cmp dragon_ability_force ("dragon_ability_force"); - shstr_cmp dragon_skin_force ("dragon_skin_force"); - for (tmp = ob->inv; tmp; tmp = tmp->below) if (tmp->type == FORCE) - if (tmp->arch->name == dragon_ability_force) + if (tmp->arch->archname == shstr_dragon_ability_force) abil = tmp; - else if (tmp->arch->name == dragon_skin_force) + else if (tmp->arch->archname == shstr_dragon_skin_force) skin = tmp; set_dragon_name (ob, abil, skin); @@ -263,8 +244,8 @@ esrv_new_player (this, ob->weight + ob->carrying); ob->update_stats (); - ns->floorbox_update (); + ns->floorbox_update (); esrv_send_inventory (ob, ob); esrv_add_spells (this, 0); @@ -281,6 +262,12 @@ void player::disconnect () { + if (ob) + { + ob->close_container (); //TODO: client-specific + ob->drop_unpaid_items (); + } + if (ns) { if (active) @@ -290,11 +277,10 @@ ns->reset_stats (); ns->pl = 0; - this->ns = 0; + ns = 0; } - if (ob) - ob->close_container (); //TODO: client-specific + observe = ob; deactivate (); } @@ -304,12 +290,46 @@ void player::set_object (object *op) { - ob = op; + ob = observe = op; ob->contr = this; /* this aren't yet in archetype */ - ob->speed_left = 0.5; - ob->speed = 1.0; - ob->direction = 5; /* So player faces south */ + 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; + + 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); +} + +void +player::set_observe (object *op) +{ + observe = op ? op : ob; + do_los = 1; } player::player () @@ -317,20 +337,22 @@ /* There are some elements we want initialised to non zero value - * we deal with that below this point. */ - outputs_sync = 6; /* Every 2 seconds */ - outputs_count = 10; /* Keeps present behaviour */ + outputs_sync = 4; + outputs_count = 4; unapply = unapply_nochoice; savebed_map = first_map_path; /* Init. respawn position */ gen_sp_armour = 10; - shoottype = range_none; bowtype = bow_normal; petmode = pet_normal; listening = 10; usekeys = containers; peaceful = 1; /* default peaceful */ do_los = 1; + + weapon_sp = 1.0f; + weapon_sp_left = 0.5f; } void @@ -345,6 +367,8 @@ ob->destroy_inv (false); ob->destroy (); } + + ob = observe = 0; } player::~player () @@ -381,23 +405,17 @@ 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 (++i == archetypes.end ()) + i = archetypes.begin (); + else if (*i == at) + cleanup ("not a single player archetype found"); - if (at == start) - { - LOG (llevError, "No Player archetypes\n"); - exit (-1); - } + if ((*i)->type == PLAYER) + return *i; } } @@ -411,33 +429,6 @@ 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 '%s' on friendly list\n", - tmp->debug_desc ()); - 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; @@ -773,12 +764,12 @@ void object::roll_stats () { - int statsort [7]; + int statsort [NUM_STATS]; for (;;) { int sum = 0; - for (int i = 7; i--; ) + for (int i = NUM_STATS; i--; ) sum += statsort [i] = roll_stat (); if (sum >= 82 && sum <= 116) @@ -786,15 +777,10 @@ } // Sort the stats so that rerolling is easier... - std::sort (statsort, statsort + 7, std::greater()); + std::sort (statsort, statsort + NUM_STATS, std::greater()); - stats.Str = statsort[0]; - stats.Dex = statsort[1]; - stats.Con = statsort[2]; - stats.Int = statsort[3]; - stats.Wis = statsort[4]; - stats.Pow = statsort[5]; - stats.Cha = statsort[6]; + for (int i = 0; i < NUM_STATS; ++i) + stats.stat (i) = statsort [i]; stats.exp = 0; stats.ac = 0; @@ -816,17 +802,10 @@ void object::swap_stats (int a, int b) { - int tmp = get_attr_value (&contr->orig_stats, a); - set_attr_value (&contr->orig_stats, a, get_attr_value (&contr->orig_stats, b)); - set_attr_value (&contr->orig_stats, b, tmp); - - stats.Str = contr->orig_stats.Str; - stats.Dex = contr->orig_stats.Dex; - stats.Con = contr->orig_stats.Con; - stats.Int = contr->orig_stats.Int; - stats.Wis = contr->orig_stats.Wis; - stats.Pow = contr->orig_stats.Pow; - stats.Cha = contr->orig_stats.Cha; + swap (contr->orig_stats.stat (a), contr->orig_stats.stat (b)); + + for (int i = 0; i < NUM_STATS; ++i) + stats.stat (i) = contr->orig_stats.stat (i); //TODO: the following code looks so borked and should, at the very least, // be merged with the similar code in roll_stats @@ -867,105 +846,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; - - if (key == 'd' || key == 'D') - { - char buf[MAX_BUF]; - - /* this must before then initial items are given */ - esrv_new_player (op->contr, op->weight + op->carrying); - - treasurelist *tl = find_treasurelist ("starting_wealth"); - if (tl) - create_treasure (tl, op, 0, 0, 0); + /* this must before then initial items are given */ + esrv_new_player (ob->contr, ob->weight + ob->carrying); - INVOKE_PLAYER (BIRTH, op->contr); - INVOKE_PLAYER (LOGIN, op->contr); + treasurelist *tl = treasurelist::find ("starting_wealth"); + if (tl) + create_treasure (tl, ob, 0, 0, 0); - op->contr->ns->state = ST_PLAYING; + INVOKE_PLAYER (BIRTH, ob->contr); + INVOKE_PLAYER (LOGIN, ob->contr); - if (op->msg) - op->msg = NULL; + ob->contr->ns->state = ST_PLAYING; - /* 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); - - start_info (op); - CLEAR_FLAG (op, FLAG_WIZ); - give_initial_items (op, op->randomitems); - link_player_skills (op); - esrv_send_inventory (op, op); - op->update_stats (); + if (ob->msg) + ob->msg = 0; - /* 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]; + /* 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 (); - snprintf (mapname, MAX_BUF - 1, "%s/%s", &first_map_ext_path, &op->arch->name); - tmp = object::create (); - EXIT_PATH (tmp) = mapname; - EXIT_X (tmp) = op->x; - EXIT_Y (tmp) = op->y; - op->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"); + /* 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]; - 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"); +} +void +player::chargen_race_next () +{ /* Following actually changes the race - this is the default command * if we don't match with one of the options above. */ - tmp_loop = 0; - while (!tmp_loop) + do { - shstr name = op->name; - int x = op->x, y = op->y; + shstr name = ob->name; + int x = ob->x, y = ob->y; - op->remove_statbonus (); - op->remove (); - op->arch = get_player_archetype (op->arch); - op->arch->clone.copy_to (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); - op->add_statbonus (); - tmp_loop = allowed_class (op); - } - - update_object (op, UP_OBJ_FACE); - esrv_update_item (UPD_FACE, op, op); - op->update_stats (); - op->stats.hp = op->stats.maxhp; - op->stats.sp = op->stats.maxsp; - op->stats.grace = 0; - - if (op->msg) - new_draw_info (NDI_BLUE, 0, op, op->msg); + 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)); - send_query (op->contr->ns, CS_QUERY_SINGLECHAR, "Press any key for the next race.\nPress `d' to play this race.\n"); - return 0; + 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 @@ -1022,9 +992,8 @@ 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 @@ -1111,7 +1080,7 @@ &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)))); + &tmp->arch->archname, tmp->type, (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1)))); new_draw_info (NDI_UNIQUE, 0, op, putstring); } @@ -1196,7 +1165,7 @@ } 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); continue; @@ -1314,8 +1283,8 @@ { 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); continue; @@ -1324,7 +1293,7 @@ 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); continue; @@ -1359,7 +1328,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 @@ -1384,9 +1353,9 @@ 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); + tmp = find_arrow (splay (op), type); else if (op->type == ARROW && op->race == type) - return op; + return splay (op); return tmp; } @@ -1439,11 +1408,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) @@ -1472,7 +1441,6 @@ * type = bow->race * dir = fire direction */ - object * pick_arrow_target (object *op, const char *type, int dir) { @@ -1547,7 +1515,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) @@ -1556,8 +1524,8 @@ 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) @@ -1572,6 +1540,14 @@ 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) @@ -1580,15 +1556,6 @@ 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) @@ -1598,6 +1565,7 @@ /* FLAG_READY_BOW will get reset if the monsters picks up some arrows */ else CLEAR_FLAG (op, FLAG_READY_BOW); + return 0; } } @@ -1631,56 +1599,68 @@ arrow->skill = bow->skill; arrow->direction = dir; - if (op->type == PLAYER) - { - op->speed_left = 0.01 - (float) FABS (op->speed) * 100 / bowspeed; - op->update_stats (); - } - - SET_ANIMATION (arrow, arrow->direction); - arrow->stats.sp = arrow->stats.wc; /* save original wc and dam */ - arrow->stats.hp = arrow->stats.dam; + 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 != NULL) + + if (arrow->slaying) arrow->spellarg = strdup (arrow->slaying); - /* Note that this was different for monsters - they got their level - * added to the damage. I think the strength bonus is more proper. - */ +#if 0 + if (player *pl = op->contr) + { + float speed = pl->weapon_sp; + + /* penalize ROF for bestarrow */ + if (pl->bowtype == bow_bestarrow) + speed *= .9f; + else + speed *= 1.f + dex_bonus[op->stats.Dex] * .2f; - arrow->stats.dam += (QUERY_FLAG (bow, FLAG_NO_STRENGTH) ? 0 : dam_bonus[op->stats.Str]) + bow->stats.dam + bow->magic + arrow->magic; + op->speed_left += speed - op->speed; + } +#endif + + 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; - arrow->set_speed (max (arrow->speed, 1.0)); + 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->slaying) + arrow->slaying = bow->slaying; - if (arrow->attacktype == AT_PHYSICAL) - arrow->attacktype |= bow->attacktype; + arrow->attacktype |= bow->attacktype; + } - if (bow->slaying) - arrow->slaying = bow->slaying; + wc -= arrow->level; + arrow->stats.dam = clamp (arrow->stats.dam + op->stats.dam + arrow->magic, MIN_DAM, MAX_DAM); + 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); + op->play_sound (sound_find ("fire_arrow")); m->insert (arrow, sx, sy, op); if (!arrow->destroyed ()) @@ -1711,7 +1691,7 @@ 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) { @@ -1731,43 +1711,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; } } @@ -1775,11 +1758,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; } } @@ -1796,7 +1781,7 @@ if (item->arch) { CLEAR_FLAG (item, FLAG_ANIMATE); - item->face = item->arch->clone.face; + item->face = item->arch->face; item->set_speed (0); } @@ -1811,7 +1796,7 @@ /* Received a fire command for the player - go and do it. */ -void +bool fire (object *op, int dir) { int spellcost = 0; @@ -1820,50 +1805,51 @@ 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 (QUERY_FLAG (op->contr->ranges[range_golem], FLAG_REMOVED)) - { - op->contr->ranges[range_golem] = 0; - op->contr->shoottype = range_none; - } - else - control_golem (op->contr->ranges[range_golem], dir); - return; + if (op->speed_left > 0.f) + --op->speed_left; + else + return false; + + switch (ob->type) + { + case BOW: + player_fire_bow (op, dir); + break; - 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; - } + case SPELL: + spellcost = cast_spell (op, op, dir, ob, pl->spellparam[0] ? pl->spellparam : 0); + break; - do_skill (op, op, op->chosen_skill, dir, NULL); - return; - case range_builder: + 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 @@ -1960,20 +1946,20 @@ 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); @@ -1981,7 +1967,7 @@ 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 ;-) */ } @@ -1996,7 +1982,7 @@ 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; } @@ -2009,19 +1995,25 @@ * (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, 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 * want to be calling move_ob if braced, because move_ob will move the @@ -2031,174 +2023,145 @@ * 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)) - { - if (OUT_OF_REAL_MAP (op->map, nx, ny)) - { - m = op->map->xy_find (nx, ny); - if (!m) - return; /* Don't think this should happen */ - } - else - m = op->map; - - if (!(tmp = m->at (nx, ny).bot)) - return; + maptile *m = op->map->xy_find (nx, ny); - mon = 0; - /* 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) - { - if (tmp == op) - { - tmp = tmp->above; - continue; - } + /* 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 ((mon->flag [FLAG_ALIVE] + || mon->type == LOCKED_DOOR + || mon->flag [FLAG_CAN_ROLL]) + && mon != op) + break; + } - if (QUERY_FLAG (tmp, FLAG_ALIVE)) - { - mon = tmp; - break; - } + if (!mon) /* This happens anytime the player tries to move */ + return false; /* into a wall */ - if (tmp->type == LOCKED_DOOR || QUERY_FLAG (tmp, FLAG_CAN_ROLL)) - mon = tmp; + mon = mon->head_ (); - tmp = tmp->above; + 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)) + { + --op->contr->weapon_sp_left; + return true; } - if (!mon) /* This happens anytime the player tries to move */ - return; /* into a wall */ - - if (mon->head) - mon = mon->head; - - if ((mon->type == DOOR && mon->stats.hp >= 0) || (mon->type == LOCKED_DOOR)) - if (player_attack_door (op, mon)) - return; + /* 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. + */ - /* 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. - */ + /* 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 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 - && - ((mon->owner && mon->owner->contr - && same_party (mon->owner->contr->party, op->contr->party)) || mon->owner == op) -#else - && mon->owner == op -#endif - && (QUERY_FLAG (mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG (mon, FLAG_FRIENDLY))) + if (op->speed_left > 0.f) { - /* If we're braced, we don't want to switch places with it */ - if (op->contr->braced) - return; + --op->speed_left; + + op->play_sound (sound_find ("push_player")); + push_ob (mon, dir, op); - 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); + 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]) { - - /* 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 */ - } + --op->contr->weapon_sp_left; 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, 0, 0); - 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; @@ -2222,13 +2185,15 @@ 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); } @@ -2245,7 +2210,8 @@ * for players. */ animate_object (op, op->facing); - return 0; + + return retval; } /* This is similar to handle_player, below, but is only used by the @@ -2253,55 +2219,37 @@ * 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 (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); + + return true; } + else + return false; } - /* 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] && QUERY_FLAG (op->contr->ranges[range_golem], FLAG_REMOVED)) - op->contr->ranges[range_golem] = 0; - /* 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. */ if (op->contr->ns->handle_command ()) - return 1; + return true; - if (op->speed_left > 0) - { - if (op->direction && (op->contr->run_on || op->contr->fire_on)) - { - /* All move commands take 1 tick, at least for now */ - op->speed_left--; - - /* 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->direction && (op->contr->run_on || op->contr->fire_on)) + return move_player (op, op->direction); - return op->speed_left > 0; - } - } - - return 0; + return false; } int @@ -2313,7 +2261,7 @@ 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) @@ -2343,8 +2291,8 @@ * 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) { while (op) { @@ -2358,12 +2306,19 @@ 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 * Moved from apply.c to player.c - player.c is what @@ -2440,11 +2395,6 @@ } } - if (op->contr->outputs_sync) - for (i = 0; i < NUM_OUTPUT_BUFS; i++) - if (op->contr->outputs[i].buf && (op->contr->outputs[i].first_update + op->contr->outputs_sync) < (uint16) pticks) - flush_output_element (op, &op->contr->outputs[i]); - if (op->contr->ns->state == ST_PLAYING) { /* these next three if clauses make it possible to SLOW DOWN @@ -2473,48 +2423,6 @@ 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) @@ -2543,56 +2451,97 @@ /* 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); - 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); + op->last_eat = 25 * (1 + bonus) / (max (0, op->contr->gen_hp) + penalty + 1); /* dms do not consume food */ if (!QUERY_FLAG (op, FLAG_WIZ)) @@ -2629,8 +2578,11 @@ } } - 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->stats.hp < 0 && !QUERY_FLAG (op, FLAG_WIZ)) kill_player (op); @@ -2645,17 +2597,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; @@ -2663,7 +2607,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 @@ -2688,7 +2631,7 @@ 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; @@ -2696,12 +2639,12 @@ /* create a bodypart-trophy to make the winner happy */ 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->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); @@ -2718,14 +2661,9 @@ command_kill_pets (op, 0); if (op->stats.food < 0) - { - sprintf (buf, "%s starved to death.", &op->name); - strcpy (op->contr->killer, "starvation"); - } - else - sprintf (buf, "%s died.", &op->name); + strcpy (op->contr->killer, "starvation"); - play_sound_player_only (op->contr, SOUND_PLAYER_DIES, 0, 0); + op->contr->play_sound (sound_find ("player_dies")); /* save the map location for corpse, gravestone */ x = op->x; @@ -2898,7 +2836,7 @@ 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 */ /*add_exp(op, (op->stats.exp * -0.20)); */ apply_death_exp_penalty (op); @@ -2912,7 +2850,7 @@ * Check to see if the player has any unpaid items. If so, remove them * and put them back in the map. */ - remove_unpaid_objects (op->inv, op); + op->drop_unpaid_items (); /****************************************/ /* */ @@ -2942,8 +2880,8 @@ 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; + 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)) @@ -3062,6 +3000,7 @@ { op->hide = 0; op->invisible = 0; + if (op->type == PLAYER) { op->contr->tmp_invis = 0; @@ -3074,7 +3013,7 @@ int is_true_undead (object *op) { - if (QUERY_FLAG (&op->arch->clone, FLAG_UNDEAD)) + if (QUERY_FLAG (op->arch, FLAG_UNDEAD)) return 1; return 0; @@ -3084,7 +3023,6 @@ * the hideability of this object. Positive levels * indicate greater hideability. */ - int hideability (object *ob) { @@ -3128,7 +3066,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) { @@ -3220,14 +3157,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) { @@ -3249,25 +3185,27 @@ /* 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->ns->mapx / 2) && - FABS (dy) <= (pl->contr->ns->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; } @@ -3280,7 +3218,6 @@ int action_makes_visible (object *op) { - if (op->invisible && QUERY_FLAG (op, FLAG_ALIVE)) { if (QUERY_FLAG (op, FLAG_MAKE_INVIS)) @@ -3296,6 +3233,7 @@ return 1; } } + return 0; } @@ -3310,42 +3248,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; } @@ -3371,13 +3311,13 @@ /* 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; @@ -3391,7 +3331,7 @@ } /* everything seems okay - now bring on the gift: */ - item = &(tr->item->clone); + item = tr->item; if (item->type == SPELL) { @@ -3460,8 +3400,7 @@ object *skin; /* first get the dragon skin force */ - shstr_cmp dragon_skin_force ("dragon_skin_force"); - for (skin = who->inv; skin && !(skin->arch->name == dragon_skin_force); skin = skin->below) + for (skin = who->inv; skin && !(skin->arch->archname == shstr_dragon_skin_force); skin = skin->below) ; if (!skin) @@ -3519,13 +3458,14 @@ void player_unready_range_ob (player *pl, object *ob) { - for (rangetype i = (rangetype) 0; i < range_size; i = (rangetype) ((int) i + 1)) - if (pl->ranges[i] == ob) - { - pl->ranges[i] = 0; - if (pl->shoottype == i) - pl->shoottype = range_none; - } + if (pl->ob->current_weapon == ob) + pl->ob->current_weapon = 0; + + if (pl->combat_ob == ob) + pl->combat_ob = 0; + + if (pl->ranged_ob == ob) + pl->ranged_ob = 0; } sint8 @@ -3546,3 +3486,23 @@ 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); +} +