ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/player.C
Revision: 1.107
Committed: Wed Mar 14 04:12:29 2007 UTC (17 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.106: +21 -21 lines
Log Message:
- rewrote more face handling code
- automatically send smooth faces, as the client will need them anyways
  and it makes little sense to wait for the client to axk for it. of course,
  gcfclient suffers from weird ordering problems again.
- UP_OBJ_FACE was often abused in situations where other things changed,
  updated lots of spaces, probably more to be done.
- update_smooth became so small that inlining it actually clarified
  the code. similar for update_space, which is not inlined for other reasons.
- faces were not initialised properly
- add versioncheck for face data
- rewrite invisibility handling a bit: god finger etc. now makes you blink,
  blinking routine has changed to be less annoying and more useful while
  still indicating invisibleness.

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game
3 *
4 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5 * Copyright (C) 2002 Mark Wedel & Crossfire Development Team
6 * Copyright (C) 1992 Frank Tore Johansen
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * The author can be reached via e-mail to <crossfire@schmorp.de>
23 */
24
25 #include <global.h>
26 #include <pwd.h>
27 #include <sproto.h>
28 #include <sounds.h>
29 #include <living.h>
30 #include <object.h>
31 #include <spells.h>
32 #include <skills.h>
33
34 #include <algorithm>
35 #include <functional>
36
37 playervec players;
38
39 void
40 display_motd (const object *op)
41 {
42 char buf[MAX_BUF];
43 char motd[HUGE_BUF];
44 FILE *fp;
45 int comp;
46 int size;
47
48 sprintf (buf, "%s/%s", settings.confdir, settings.motd);
49 if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
50 return;
51
52 motd[0] = '\0';
53 size = 0;
54
55 while (fgets (buf, MAX_BUF, fp))
56 {
57 if (*buf == '#')
58 continue;
59
60 strncat (motd + size, buf, HUGE_BUF - size);
61 size += strlen (buf);
62 }
63
64 draw_ext_info (NDI_UNIQUE | NDI_GREEN, 0, op, MSG_TYPE_MOTD, MSG_SUBTYPE_NONE, motd, NULL);
65 close_and_delete (fp, comp);
66 }
67
68 void
69 send_rules (const object *op)
70 {
71 char buf[MAX_BUF];
72 char rules[HUGE_BUF];
73 FILE *fp;
74 int comp;
75 int size;
76
77 sprintf (buf, "%s/%s", settings.confdir, settings.rules);
78 if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
79 return;
80
81 rules[0] = '\0';
82 size = 0;
83
84 while (fgets (buf, MAX_BUF, fp))
85 {
86 if (*buf == '#')
87 continue;
88
89 if (size + strlen (buf) >= HUGE_BUF)
90 {
91 LOG (llevDebug, "Warning, rules size is > %d bytes.\n", HUGE_BUF);
92 break;
93 }
94
95 strncat (rules + size, buf, HUGE_BUF - size);
96 size += strlen (buf);
97 }
98
99 draw_ext_info (NDI_UNIQUE | NDI_GREEN, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_RULES, rules, NULL);
100 close_and_delete (fp, comp);
101 }
102
103 void
104 send_news (const object *op)
105 {
106 char buf[MAX_BUF];
107 char news[HUGE_BUF];
108 char subject[MAX_BUF];
109 FILE *fp;
110 int comp;
111 int size;
112
113 sprintf (buf, "%s/%s", settings.confdir, settings.news);
114 if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
115 return;
116
117 news[0] = '\0';
118 subject[0] = '\0';
119 size = 0;
120
121 while (fgets (buf, MAX_BUF, fp))
122 {
123 if (*buf == '#')
124 continue;
125
126 if (*buf == '%')
127 { /* send one news */
128 if (size > 0)
129 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 */
130
131 strcpy (subject, buf + 1);
132 strip_endline (subject);
133 size = 0;
134 news[0] = '\0';
135 }
136 else
137 {
138 if (size + strlen (buf) >= HUGE_BUF)
139 {
140 LOG (llevDebug, "Warning, one news item has size > %d bytes.\n", HUGE_BUF);
141 break;
142 }
143 strncat (news + size, buf, HUGE_BUF - size);
144 size += strlen (buf);
145 }
146 }
147
148 draw_ext_info_format (NDI_UNIQUE | NDI_GREEN, 0, op,
149 MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS, "INFORMATION: %s\n%s\n", "%s\n%s", subject, news);
150 close_and_delete (fp, comp);
151 }
152
153 /* This loads the first map an puts the player on it. */
154 static void
155 set_first_map (object *op)
156 {
157 op->contr->maplevel = first_map_path;
158 op->x = -1;
159 op->y = -1;
160 }
161
162 void
163 player::enter_map ()
164 {
165 object *tmp = object::create ();
166
167 EXIT_PATH (tmp) = maplevel;
168 EXIT_X (tmp) = ob->x;
169 EXIT_Y (tmp) = ob->y;
170 ob->enter_exit (tmp);
171
172 tmp->destroy ();
173 }
174
175 void
176 player::activate ()
177 {
178 if (active)
179 return;
180
181 players.insert (this);
182 ob->remove ();
183 ob->map = 0;
184 ob->activate_recursive ();
185 CLEAR_FLAG (ob, FLAG_FRIENDLY);
186 add_friendly_object (ob);
187 enter_map ();
188 }
189
190 void
191 player::deactivate ()
192 {
193 if (!active)
194 return;
195
196 terminate_all_pets (ob);
197 remove_friendly_object (ob);
198 ob->deactivate_recursive ();
199 maplevel = ob->map->path;
200 ob->remove ();
201 ob->map = 0;
202 party = 0;
203
204 // for weird reasons, this is often "ob", keeping a circular reference
205 ranges [range_skill] = 0;
206
207 players.erase (this);
208 }
209
210 // connect the player with a specific client
211 // also changed, rationalises, and fixes some incorrect settings
212 void
213 player::connect (client *ns)
214 {
215 this->ns = ns;
216 ns->pl = this;
217
218 run_on = 0;
219 fire_on = 0;
220 ob->close_container (); //TODO: client-specific
221
222 ns->update_look = 0;
223 ns->look_position = 0;
224
225 clear_los (ob);
226
227 ns->reset_stats ();
228
229 /* make sure he's a player -- needed because of class change. */
230 ob->type = PLAYER; // we are paranoid
231 ob->race = ob->arch->clone.race;
232
233 if (!legal_range (ob, shoottype))
234 shoottype = range_none;
235
236 ob->carrying = sum_weight (ob);
237 link_player_skills (ob);
238
239 CLEAR_FLAG (ob, FLAG_NO_FIX_PLAYER);
240
241 assign (title, ob->arch->clone.name);
242
243 /* if it's a dragon player, set the correct title here */
244 if (is_dragon_pl (ob))
245 {
246 object *tmp, *abil = 0, *skin = 0;
247
248 shstr_cmp dragon_ability_force ("dragon_ability_force");
249 shstr_cmp dragon_skin_force ("dragon_skin_force");
250
251 for (tmp = ob->inv; tmp; tmp = tmp->below)
252 if (tmp->type == FORCE)
253 if (tmp->arch->name == dragon_ability_force)
254 abil = tmp;
255 else if (tmp->arch->name == dragon_skin_force)
256 skin = tmp;
257
258 set_dragon_name (ob, abil, skin);
259 }
260
261 new_draw_info (NDI_UNIQUE, 0, ob, "Welcome Back!");
262
263 esrv_new_player (this, ob->weight + ob->carrying);
264
265 ob->update_stats ();
266 ns->floorbox_update ();
267
268 esrv_send_inventory (ob, ob);
269 esrv_add_spells (this, 0);
270
271 activate ();
272
273 send_rules (ob);
274 send_news (ob);
275 display_motd (ob);
276
277 INVOKE_PLAYER (CONNECT, this);
278 INVOKE_PLAYER (LOGIN, this);
279 }
280
281 void
282 player::disconnect ()
283 {
284 if (ns)
285 {
286 if (active)
287 INVOKE_PLAYER (LOGOUT, this, ARG_INT (0));
288
289 INVOKE_PLAYER (DISCONNECT, this);
290
291 ns->reset_stats ();
292 ns->pl = 0;
293 this->ns = 0;
294 }
295
296 if (ob)
297 ob->close_container (); //TODO: client-specific
298
299 deactivate ();
300 }
301
302 // the need for this function can be explained
303 // by load_object not returning the object
304 void
305 player::set_object (object *op)
306 {
307 ob = op;
308 ob->contr = this; /* this aren't yet in archetype */
309
310 ob->speed_left = 0.5;
311 ob->speed = 1.0;
312 ob->direction = 5; /* So player faces south */
313 }
314
315 player::player ()
316 {
317 /* There are some elements we want initialised to non zero value -
318 * we deal with that below this point.
319 */
320 outputs_sync = 16; /* Every 2 seconds */
321 outputs_count = 8; /* Keeps present behaviour */
322 unapply = unapply_nochoice;
323
324 savebed_map = first_map_path; /* Init. respawn position */
325
326 gen_sp_armour = 10;
327 shoottype = range_none;
328 bowtype = bow_normal;
329 petmode = pet_normal;
330 listening = 10;
331 usekeys = containers;
332 peaceful = 1; /* default peaceful */
333 do_los = 1;
334 }
335
336 void
337 player::do_destroy ()
338 {
339 disconnect ();
340
341 attachable::do_destroy ();
342
343 if (ob)
344 {
345 ob->destroy_inv (false);
346 ob->destroy ();
347 }
348 }
349
350 player::~player ()
351 {
352 /* Clear item stack */
353 free (stack_items);
354 }
355
356 /* Tries to add player on the connection passed in ns.
357 * All we can really get in this is some settings like host and display
358 * mode.
359 */
360 player *
361 player::create ()
362 {
363 player *pl = new player;
364
365 pl->set_object (arch_to_object (get_player_archetype (0)));
366
367 pl->ob->roll_stats ();
368 pl->ob->stats.wc = 2;
369 pl->ob->run_away = 25; /* Then we panick... */
370
371 set_first_map (pl->ob);
372
373 return pl;
374 }
375
376 /*
377 * get_player_archetype() return next player archetype from archetype
378 * list. Not very efficient routine, but used only creating new players.
379 * Note: there MUST be at least one player archetype!
380 */
381 archetype *
382 get_player_archetype (archetype *at)
383 {
384 archetype *start = at;
385
386 for (;;)
387 {
388 if (at == NULL || at->next == NULL)
389 at = first_archetype;
390 else
391 at = at->next;
392
393 if (at->clone.type == PLAYER)
394 return at;
395
396 if (at == start)
397 {
398 LOG (llevError, "No Player archetypes\n");
399 exit (-1);
400 }
401 }
402 }
403
404 object *
405 get_nearest_player (object *mon)
406 {
407 object *op = NULL;
408 objectlink *ol;
409 unsigned lastdist;
410 rv_vector rv;
411
412 for (ol = first_friendly_object, lastdist = 1000; ol; ol = ol->next)
413 {
414 /* We should not find free objects on this friendly list, but it
415 * does periodically happen. Given that, lets deal with it.
416 * While unlikely, it is possible the next object on the friendly
417 * list is also free, so encapsulate this in a while loop.
418 */
419 while (QUERY_FLAG (ol->ob, FLAG_FREED) || !QUERY_FLAG (ol->ob, FLAG_FRIENDLY))
420 {
421 object *tmp = ol->ob;
422
423 /* Can't do much more other than log the fact, because the object
424 * itself will have been cleared.
425 */
426 LOG (llevDebug, "get_nearest_player: Found free/non friendly object '%s' on friendly list\n",
427 tmp->debug_desc ());
428 ol = ol->next;
429 remove_friendly_object (tmp);
430 if (!ol)
431 return op;
432 }
433
434 /* Remove special check for player from this. First, it looks to cause
435 * some crashes (ol->ob->contr not set properly?), but secondly, a more
436 * complicated method of state checking would be needed in any case -
437 * as it was, a clever player could type quit, and the function would
438 * skip them over while waiting for confirmation. Remove
439 * on_same_map check, as can_detect_enemy also does this
440 */
441 if (!can_detect_enemy (mon, ol->ob, &rv))
442 continue;
443
444 if (lastdist > rv.distance)
445 {
446 op = ol->ob;
447 lastdist = rv.distance;
448 }
449 }
450
451 for_all_players (pl)
452 if (can_detect_enemy (mon, pl->ob, &rv))
453 if (lastdist > rv.distance)
454 {
455 op = pl->ob;
456 lastdist = rv.distance;
457 }
458
459 #if 0
460 LOG (llevDebug, "get_nearest_player() finds player: %s\n", op ? &op->name : "(null)");
461 #endif
462 return op;
463 }
464
465 /* I believe this can safely go to 2, 3 is questionable, 4 will likely
466 * result in a monster paths backtracking. It basically determines how large a
467 * detour a monster will take from the direction path when looking
468 * for a path to the player. The values are in the amount of direction
469 * the deviation is
470 */
471 #define DETOUR_AMOUNT 2
472
473 /* This is used to prevent infinite loops. Consider a case where the
474 * player is in a chamber (with gate closed), and monsters are outside.
475 * with DETOUR_AMOUNT==2, the function will turn each corner, trying to
476 * find a path into the chamber. This is a good thing, but since there
477 * is no real path, it will just keep circling the chamber for
478 * ever (this could be a nice effect for monsters, but not for the function
479 * to get stuck in. I think for the monsters, if max is reached and
480 * we return the first direction the creature could move would result in the
481 * circling behaviour. Unfortunately, this function is also used to determined
482 * if the creature should cast a spell, so returning a direction in that case
483 * is probably not a good thing.
484 */
485 #define MAX_SPACES 50
486
487 /*
488 * Returns the direction to the player, if valid. Returns 0 otherwise.
489 * modified to verify there is a path to the player. Does this by stepping towards
490 * player and if path is blocked then see if blockage is close enough to player that
491 * direction to player is changed (ie zig or zag). Continue zig zag until either
492 * reach player or path is blocked. Thus, will only return true if there is a free
493 * path to player. Though path may not be a straight line. Note that it will find
494 * player hiding along a corridor at right angles to the corridor with the monster.
495 *
496 * Modified by MSW 2001-08-06 to handle tiled maps. Various notes:
497 * 1) With DETOUR_AMOUNT being 2, it should still go and find players hiding
498 * down corriders.
499 * 2) I think the old code was broken if the first direction the monster
500 * should move was blocked - the code would store the first direction without
501 * verifying that the player can actually move in that direction. The new
502 * code does not store anything in firstdir until we have verified that the
503 * monster can in fact move one space in that direction.
504 * 3) I'm not sure how good this code will be for moving multipart monsters,
505 * since only simple checks to blocked are being called, which could mean the monster
506 * is blocking itself.
507 */
508 int
509 path_to_player (object *mon, object *pl, unsigned mindiff)
510 {
511 rv_vector rv;
512 sint16 x, y;
513 int lastx, lasty, dir, i, diff, firstdir = 0, lastdir, max = MAX_SPACES, mflags, blocked;
514 maptile *m, *lastmap;
515
516 get_rangevector (mon, pl, &rv, 0);
517
518 if (rv.distance < mindiff)
519 return 0;
520
521 x = mon->x;
522 y = mon->y;
523 m = mon->map;
524 dir = rv.direction;
525 lastdir = firstdir = rv.direction; /* perhaps we stand next to pl, init firstdir too */
526 diff = ::max (abs (rv.distance_x), abs (rv.distance_y));
527
528 /* If we can't solve it within the search distance, return now. */
529 if (diff > max)
530 return 0;
531
532 while (diff > 1 && max > 0)
533 {
534 lastx = x;
535 lasty = y;
536 lastmap = m;
537 x = lastx + freearr_x[dir];
538 y = lasty + freearr_y[dir];
539
540 mflags = get_map_flags (m, &m, x, y, &x, &y);
541 blocked = (mflags & P_OUT_OF_MAP) ? MOVE_ALL : GET_MAP_MOVE_BLOCK (m, x, y);
542
543 /* Space is blocked - try changing direction a little */
544 if ((mflags & P_OUT_OF_MAP) || ((OB_TYPE_MOVE_BLOCK (mon, blocked) || (mflags & P_BLOCKSVIEW))
545 && (m == mon->map && blocked_link (mon, m, x, y))))
546 {
547 /* recalculate direction from last good location. Possible
548 * we were not traversing ideal location before.
549 */
550 get_rangevector_from_mapcoord (lastmap, lastx, lasty, pl, &rv, 0);
551 if (rv.direction != dir)
552 {
553 /* OK - says direction should be different - lets reset the
554 * the values so it will try again.
555 */
556 x = lastx;
557 y = lasty;
558 m = lastmap;
559 dir = firstdir = rv.direction;
560 }
561 else
562 {
563 /* direct path is blocked - try taking a side step to
564 * either the left or right.
565 * Note increase the values in the loop below to be
566 * more than -1/1 respectively will mean the monster takes
567 * bigger detour. Have to be careful about these values getting
568 * too big (3 or maybe 4 or higher) as the monster may just try
569 * stepping back and forth
570 */
571 for (i = -DETOUR_AMOUNT; i <= DETOUR_AMOUNT; i++)
572 {
573 if (i == 0)
574 continue; /* already did this, so skip it */
575 /* Use lastdir here - otherwise,
576 * since the direction that the creature should move in
577 * may change, you could get infinite loops.
578 * ie, player is northwest, but monster can only
579 * move west, so it does that. It goes some distance,
580 * gets blocked, finds that it should move north,
581 * can't do that, but now finds it can move east, and
582 * gets back to its original point. lastdir contains
583 * the last direction the creature has successfully
584 * moved.
585 */
586
587 x = lastx + freearr_x[absdir (lastdir + i)];
588 y = lasty + freearr_y[absdir (lastdir + i)];
589 m = lastmap;
590 mflags = get_map_flags (m, &m, x, y, &x, &y);
591 if (mflags & P_OUT_OF_MAP)
592 continue;
593 blocked = GET_MAP_MOVE_BLOCK (m, x, y);
594 if (OB_TYPE_MOVE_BLOCK (mon, blocked))
595 continue;
596 if (mflags & P_BLOCKSVIEW)
597 continue;
598
599 if (m == mon->map && blocked_link (mon, m, x, y))
600 break;
601 }
602 /* go through entire loop without finding a valid
603 * sidestep to take - thus, no valid path.
604 */
605 if (i == (DETOUR_AMOUNT + 1))
606 return 0;
607 diff--;
608 lastdir = dir;
609 max--;
610 if (!firstdir)
611 firstdir = dir + i;
612 } /* else check alternate directions */
613 } /* if blocked */
614 else
615 {
616 /* we moved towards creature, so diff is less */
617 diff--;
618 max--;
619 lastdir = dir;
620 if (!firstdir)
621 firstdir = dir;
622 }
623
624 if (diff <= 1)
625 {
626 /* Recalculate diff (distance) because we may not have actually
627 * headed toward player for entire distance.
628 */
629 get_rangevector_from_mapcoord (m, x, y, pl, &rv, 0);
630 diff = ::max (abs (rv.distance_x), abs (rv.distance_y));
631 }
632
633 if (diff > max)
634 return 0;
635 }
636
637 /* If we reached the max, didn't find a direction in time */
638 if (!max)
639 return 0;
640
641 return firstdir;
642 }
643
644 void
645 give_initial_items (object *pl, treasurelist * items)
646 {
647 object *op, *next = NULL;
648
649 if (pl->randomitems != NULL)
650 create_treasure (items, pl, GT_STARTEQUIP | GT_ONLY_GOOD, 1, 0);
651
652 for (op = pl->inv; op; op = next)
653 {
654 next = op->below;
655
656 /* Forces get applied per default, unless they have the
657 * flag "neutral" set. Sorry but I can't think of a better way
658 */
659 if (op->type == FORCE && !QUERY_FLAG (op, FLAG_NEUTRAL))
660 SET_FLAG (op, FLAG_APPLIED);
661
662 /* we never give weapons/armour if these cannot be used
663 * by this player due to race restrictions
664 */
665 if (pl->type == PLAYER)
666 {
667 if ((!QUERY_FLAG (pl, FLAG_USE_ARMOUR) &&
668 (op->type == ARMOUR || op->type == BOOTS ||
669 op->type == CLOAK || op->type == HELMET ||
670 op->type == SHIELD || op->type == GLOVES ||
671 op->type == BRACERS || op->type == GIRDLE)) || (!QUERY_FLAG (pl, FLAG_USE_WEAPON) && op->type == WEAPON))
672 {
673 op->destroy ();
674 continue;
675 }
676 }
677
678 /* This really needs to be better - we should really give
679 * a substitute spellbook. The problem is that we don't really
680 * have a good idea what to replace it with (need something like
681 * a first level treasurelist for each skill.)
682 * remove duplicate skills also
683 */
684 if (op->type == SPELLBOOK || op->type == SKILL)
685 {
686 object *tmp;
687
688 for (tmp = op->below; tmp; tmp = tmp->below)
689 if (tmp->type == op->type && tmp->name == op->name)
690 break;
691
692 if (tmp)
693 {
694 op->destroy ();
695 LOG (llevError, "give_initial_items: Removing duplicate object %s\n", &tmp->name);
696 continue;
697 }
698
699 if (op->nrof > 1)
700 op->nrof = 1;
701 }
702
703 if (op->type == SPELLBOOK && op->inv)
704 {
705 CLEAR_FLAG (op->inv, FLAG_STARTEQUIP);
706 }
707
708 /* Give starting characters identified, uncursed, and undamned
709 * items. Just don't identify gold or silver, or it won't be
710 * merged properly.
711 */
712 if (need_identify (op))
713 {
714 SET_FLAG (op, FLAG_IDENTIFIED);
715 CLEAR_FLAG (op, FLAG_CURSED);
716 CLEAR_FLAG (op, FLAG_DAMNED);
717 }
718 if (op->type == SPELL)
719 {
720 op->destroy ();
721 continue;
722 }
723 else if (op->type == SKILL)
724 {
725 SET_FLAG (op, FLAG_CAN_USE_SKILL);
726 op->stats.exp = 0;
727 op->level = 1;
728 }
729 /* lock all 'normal items by default */
730 else
731 SET_FLAG (op, FLAG_INV_LOCKED);
732 } /* for loop of objects in player inv */
733
734 /* Need to set up the skill pointers */
735 link_player_skills (pl);
736 }
737
738 void
739 get_party_password (object *op, partylist *party)
740 {
741 if (party == NULL)
742 {
743 LOG (llevError, "get_party_password(): tried to make player %s join a NULL party", &op->name);
744 return;
745 }
746
747 op->contr->write_buf[0] = '\0';
748 op->contr->ns->state = ST_GET_PARTY_PASSWORD;
749 op->contr->party_to_join = party;
750 send_query (op->contr->ns, CS_QUERY_HIDEINPUT, "What is the password?\n:");
751 }
752
753 /* This rolls four 1-6 rolls and sums the best 3 of the 4. */
754 static int
755 roll_stat (void)
756 {
757 int a[4], i, j, k;
758
759 for (i = 0; i < 4; i++)
760 a[i] = (int) rndm (6) + 1;
761
762 for (i = 0, j = 0, k = 7; i < 4; i++)
763 if (a[i] < k)
764 k = a[i], j = i;
765
766 for (i = 0, k = 0; i < 4; i++)
767 if (i != j)
768 k += a[i];
769
770 return k;
771 }
772
773 void
774 object::roll_stats ()
775 {
776 int statsort [7];
777
778 for (;;)
779 {
780 int sum = 0;
781 for (int i = 7; i--; )
782 sum += statsort [i] = roll_stat ();
783
784 if (sum >= 82 && sum <= 116)
785 break;
786 }
787
788 // Sort the stats so that rerolling is easier...
789 std::sort (statsort, statsort + 7, std::greater<int>());
790
791 stats.Str = statsort[0];
792 stats.Dex = statsort[1];
793 stats.Con = statsort[2];
794 stats.Int = statsort[3];
795 stats.Wis = statsort[4];
796 stats.Pow = statsort[5];
797 stats.Cha = statsort[6];
798
799 stats.exp = 0;
800 stats.ac = 0;
801
802 stats.hp = stats.maxhp;
803 stats.sp = stats.maxsp;
804 stats.grace = stats.maxgrace;
805
806 if (contr)
807 {
808 contr->levhp[1] = 9;
809 contr->levsp[1] = 6;
810 contr->levgrace[1] = 3;
811
812 contr->orig_stats = stats;
813 }
814 }
815
816 void
817 object::swap_stats (int a, int b)
818 {
819 int tmp = get_attr_value (&contr->orig_stats, a);
820 set_attr_value (&contr->orig_stats, a, get_attr_value (&contr->orig_stats, b));
821 set_attr_value (&contr->orig_stats, b, tmp);
822
823 stats.Str = contr->orig_stats.Str;
824 stats.Dex = contr->orig_stats.Dex;
825 stats.Con = contr->orig_stats.Con;
826 stats.Int = contr->orig_stats.Int;
827 stats.Wis = contr->orig_stats.Wis;
828 stats.Pow = contr->orig_stats.Pow;
829 stats.Cha = contr->orig_stats.Cha;
830
831 //TODO: the following code looks so borked and should, at the very least,
832 // be merged with the similar code in roll_stats
833 stats.ac = 0;
834
835 level = 1;
836 stats.exp = 0;
837 stats.ac = 0;
838
839 stats.hp = stats.maxhp;
840 stats.sp = stats.maxsp;
841 stats.grace = stats.maxgrace;
842
843 if (contr)
844 {
845 contr->levhp[1] = 9;
846 contr->levsp[1] = 6;
847 contr->levgrace[1] = 3;
848
849 contr->orig_stats = stats;
850 }
851 }
852
853 static void
854 start_info (object *op)
855 {
856 char buf[MAX_BUF];
857
858 sprintf (buf, "Welcome to Crossfire v%s!", VERSION);
859 new_draw_info (NDI_UNIQUE, 0, op, buf);
860 //new_draw_info (NDI_UNIQUE, 0, op, "Press `?' for help");
861 //new_draw_info (NDI_UNIQUE, 0, op, " ");
862 }
863
864 /* This function takes the key that is passed, and does the
865 * appropriate action with it (change race, or other things).
866 * The function name is for historical reasons - now we have
867 * separate race and class; this actually changes the RACE,
868 * not the class.
869 */
870 int
871 key_change_class (object *op, char key)
872 {
873 int tmp_loop;
874
875 if (key == 'd' || key == 'D')
876 {
877 char buf[MAX_BUF];
878
879 /* this must before then initial items are given */
880 esrv_new_player (op->contr, op->weight + op->carrying);
881
882 treasurelist *tl = find_treasurelist ("starting_wealth");
883 if (tl)
884 create_treasure (tl, op, 0, 0, 0);
885
886 INVOKE_PLAYER (BIRTH, op->contr);
887 INVOKE_PLAYER (LOGIN, op->contr);
888
889 op->contr->ns->state = ST_PLAYING;
890
891 if (op->msg)
892 op->msg = NULL;
893
894 /* We create this now because some of the unique maps will need it
895 * to save here.
896 */
897 sprintf (buf, "%s/%s/%s", settings.localdir, settings.playerdir, &op->name);
898 make_path_to_file (buf);
899
900 start_info (op);
901 CLEAR_FLAG (op, FLAG_WIZ);
902 give_initial_items (op, op->randomitems);
903 link_player_skills (op);
904 esrv_send_inventory (op, op);
905 op->update_stats ();
906
907 /* This moves the player to a different start map, if there
908 * is one for this race
909 */
910 if (*first_map_ext_path)
911 {
912 object *tmp;
913 char mapname[MAX_BUF];
914
915 snprintf (mapname, MAX_BUF - 1, "%s/%s", &first_map_ext_path, &op->arch->name);
916 tmp = object::create ();
917 EXIT_PATH (tmp) = mapname;
918 EXIT_X (tmp) = op->x;
919 EXIT_Y (tmp) = op->y;
920 op->enter_exit (tmp); /* we don't really care if it succeeded;
921 * if the map isn't there, then stay on the
922 * default initial map */
923 tmp->destroy ();
924 }
925 else
926 LOG (llevDebug, "first_map_ext_path not set\n");
927
928 return 0;
929 }
930
931 /* Following actually changes the race - this is the default command
932 * if we don't match with one of the options above.
933 */
934
935 tmp_loop = 0;
936 while (!tmp_loop)
937 {
938 shstr name = op->name;
939 int x = op->x, y = op->y;
940
941 op->remove_statbonus ();
942 op->remove ();
943 op->arch = get_player_archetype (op->arch);
944 op->arch->clone.copy_to (op);
945 op->instantiate ();
946 op->stats = op->contr->orig_stats;
947 op->name = op->name_pl = name;
948 op->x = x;
949 op->y = y;
950 SET_ANIMATION (op, 2); /* So player faces south */
951 insert_ob_in_map (op, op->map, op, 0);
952 assign (op->contr->title, op->arch->clone.name);
953 op->add_statbonus ();
954 tmp_loop = allowed_class (op);
955 }
956
957 update_object (op, UP_OBJ_FACE);
958 esrv_update_item (UPD_FACE, op, op);
959 op->update_stats ();
960 op->stats.hp = op->stats.maxhp;
961 op->stats.sp = op->stats.maxsp;
962 op->stats.grace = 0;
963
964 if (op->msg)
965 new_draw_info (NDI_BLUE, 0, op, op->msg);
966
967 send_query (op->contr->ns, CS_QUERY_SINGLECHAR, "Press any key for the next race.\nPress `d' to play this race.\n");
968 return 0;
969 }
970
971 void
972 flee_player (object *op)
973 {
974 int dir, diff;
975 rv_vector rv;
976
977 if (op->stats.hp < 0)
978 {
979 LOG (llevDebug, "Fleeing player is dead.\n");
980 CLEAR_FLAG (op, FLAG_SCARED);
981 return;
982 }
983
984 if (op->enemy == NULL)
985 {
986 LOG (llevDebug, "Fleeing player had no enemy.\n");
987 CLEAR_FLAG (op, FLAG_SCARED);
988 return;
989 }
990
991 /* Seen some crashes here. Since we don't store an
992 * op->enemy_count, it is possible that something destroys the
993 * actual enemy, and the object is recycled.
994 */
995 if (op->enemy->map == NULL)
996 {
997 CLEAR_FLAG (op, FLAG_SCARED);
998 op->enemy = NULL;
999 return;
1000 }
1001
1002 if (!(random_roll (0, 4, op, PREFER_LOW)) && did_make_save (op, op->level, 0))
1003 {
1004 op->enemy = NULL;
1005 CLEAR_FLAG (op, FLAG_SCARED);
1006 return;
1007 }
1008
1009 get_rangevector (op, op->enemy, &rv, 0);
1010
1011 dir = absdir (4 + rv.direction);
1012 for (diff = 0; diff < 3; diff++)
1013 {
1014 int m = 1 - (RANDOM () & 2);
1015
1016 if (move_ob (op, absdir (dir + diff * m), op) || (diff == 0 && move_ob (op, absdir (dir - diff * m), op)))
1017 return;
1018 }
1019
1020 /* Cornered, get rid of scared */
1021 CLEAR_FLAG (op, FLAG_SCARED);
1022 op->enemy = NULL;
1023 }
1024
1025
1026 /* check_pick sees if there is stuff to be picked up/picks up stuff.
1027 * IT returns 1 if the player should keep on moving, 0 if he should
1028 * stop.
1029 */
1030 int
1031 check_pick (object *op)
1032 {
1033 object *tmp, *next;
1034 int stop = 0;
1035 int wvratio;
1036 char putstring[128];
1037
1038 /* if you're flying, you cna't pick up anything */
1039 if (op->move_type & MOVE_FLYING)
1040 return 1;
1041
1042 next = op->below;
1043
1044 /* loop while there are items on the floor that are not marked as
1045 * destroyed */
1046 while (next && !next->destroyed ())
1047 {
1048 tmp = next;
1049 next = tmp->below;
1050
1051 if (op->destroyed ())
1052 return 0;
1053
1054 if (!can_pick (op, tmp))
1055 continue;
1056
1057 if (op->contr->search_str[0] != '\0' && settings.search_items == TRUE)
1058 {
1059 if (item_matched_string (op, tmp, op->contr->search_str))
1060 pick_up (op, tmp);
1061 continue;
1062 }
1063
1064 /* high not bit set? We're using the old autopickup model */
1065 if (!(op->contr->mode & PU_NEWMODE))
1066 {
1067 switch (op->contr->mode)
1068 {
1069 case 0:
1070 return 1; /* don't pick up */
1071 case 1:
1072 pick_up (op, tmp);
1073 return 1;
1074 case 2:
1075 pick_up (op, tmp);
1076 return 0;
1077 case 3:
1078 return 0; /* stop before pickup */
1079 case 4:
1080 pick_up (op, tmp);
1081 break;
1082 case 5:
1083 pick_up (op, tmp);
1084 stop = 1;
1085 break;
1086 case 6:
1087 if (QUERY_FLAG (tmp, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED))
1088 pick_up (op, tmp);
1089 break;
1090
1091 case 7:
1092 if (tmp->type == MONEY || tmp->type == GEM)
1093 pick_up (op, tmp);
1094 break;
1095
1096 default:
1097 /* use value density */
1098 if (!QUERY_FLAG (tmp, FLAG_UNPAID)
1099 && (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1))) >= op->contr->mode)
1100 pick_up (op, tmp);
1101 }
1102 }
1103 else
1104 { /* old model */
1105 /* NEW pickup handling */
1106 if (op->contr->mode & PU_DEBUG)
1107 {
1108 /* some debugging code to figure out item information */
1109 if (tmp->name != NULL)
1110 sprintf (putstring, "item name: %s item type: %d weight/value: %d",
1111 &tmp->name, tmp->type, (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1))));
1112 else
1113 sprintf (putstring, "item name: %s item type: %d weight/value: %d",
1114 &tmp->arch->name, tmp->type, (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1))));
1115
1116 new_draw_info (NDI_UNIQUE, 0, op, putstring);
1117 }
1118
1119 /* philosophy:
1120 * It's easy to grab an item type from a pile, as long as it's
1121 * generic. This takes no game-time. For more detailed pickups
1122 * and selections, select-items should be used. This is a
1123 * grab-as-you-run type mode that's really useful for arrows for
1124 * example.
1125 * The drawback: right now it has no frontend, so you need to
1126 * stick the bits you want into a calculator in hex mode and then
1127 * convert to decimal and then 'pickup <#>
1128 */
1129
1130 /* the first two modes are exclusive: if NOTHING we return, if
1131 * STOP then we stop. All the rest are applied sequentially,
1132 * meaning if any test passes, the item gets picked up. */
1133
1134 /* if mode is set to pick nothing up, return */
1135
1136 if (op->contr->mode & PU_NOTHING)
1137 return 1;
1138
1139 /* if mode is set to stop when encountering objects, return */
1140 /* take STOP before INHIBIT since it doesn't actually pick
1141 * anything up */
1142
1143 if (op->contr->mode & PU_STOP)
1144 return 0;
1145
1146 /* useful for going into stores and not losing your settings... */
1147 /* and for battles wher you don't want to get loaded down while
1148 * fighting */
1149 if (op->contr->mode & PU_INHIBIT)
1150 return 1;
1151
1152 /* prevent us from turning into auto-thieves :) */
1153 if (QUERY_FLAG (tmp, FLAG_UNPAID))
1154 continue;
1155
1156 /* ignore known cursed objects */
1157 if (QUERY_FLAG (tmp, FLAG_KNOWN_CURSED) && op->contr->mode & PU_NOT_CURSED)
1158 continue;
1159
1160 /* all food and drink if desired */
1161 /* question: don't pick up known-poisonous stuff? */
1162 if (op->contr->mode & PU_FOOD)
1163 if (tmp->type == FOOD)
1164 {
1165 pick_up (op, tmp);
1166 continue;
1167 }
1168
1169 if (op->contr->mode & PU_DRINK)
1170 if (tmp->type == DRINK || (tmp->type == POISON && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED)))
1171 {
1172 pick_up (op, tmp);
1173 continue;
1174 }
1175
1176 if (op->contr->mode & PU_POTION)
1177 if (tmp->type == POTION)
1178 {
1179 pick_up (op, tmp);
1180 continue;
1181 }
1182
1183 /* spellbooks, skillscrolls and normal books/scrolls */
1184 if (op->contr->mode & PU_SPELLBOOK)
1185 if (tmp->type == SPELLBOOK)
1186 {
1187 pick_up (op, tmp);
1188 continue;
1189 }
1190
1191 if (op->contr->mode & PU_SKILLSCROLL)
1192 if (tmp->type == SKILLSCROLL)
1193 {
1194 pick_up (op, tmp);
1195 continue;
1196 }
1197
1198 if (op->contr->mode & PU_READABLES)
1199 if (tmp->type == BOOK || tmp->type == SCROLL)
1200 {
1201 pick_up (op, tmp);
1202 continue;
1203 }
1204
1205 /* wands/staves/rods/horns */
1206 if (op->contr->mode & PU_MAGIC_DEVICE)
1207 if (tmp->type == WAND || tmp->type == ROD || tmp->type == HORN)
1208 {
1209 pick_up (op, tmp);
1210 continue;
1211 }
1212
1213 /* pick up all magical items */
1214 if (op->contr->mode & PU_MAGICAL)
1215 if (QUERY_FLAG (tmp, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED))
1216 {
1217 pick_up (op, tmp);
1218 continue;
1219 }
1220
1221 if (op->contr->mode & PU_VALUABLES)
1222 {
1223 if (tmp->type == MONEY || tmp->type == GEM)
1224 {
1225 pick_up (op, tmp);
1226 continue;
1227 }
1228 }
1229
1230 /* rings & amulets - talismans seems to be typed AMULET */
1231 if (op->contr->mode & PU_JEWELS)
1232 if (tmp->type == RING || tmp->type == AMULET)
1233 {
1234 pick_up (op, tmp);
1235 continue;
1236 }
1237
1238 /* we don't forget dragon food */
1239 if (op->contr->mode & PU_FLESH)
1240 if (tmp->type == FLESH)
1241 {
1242 pick_up (op, tmp);
1243 continue;
1244 }
1245
1246 /* bows and arrows. Bows are good for selling! */
1247 if (op->contr->mode & PU_BOW)
1248 if (tmp->type == BOW)
1249 {
1250 pick_up (op, tmp);
1251 continue;
1252 }
1253
1254 if (op->contr->mode & PU_ARROW)
1255 if (tmp->type == ARROW)
1256 {
1257 pick_up (op, tmp);
1258 continue;
1259 }
1260
1261 /* all kinds of armor etc. */
1262 if (op->contr->mode & PU_ARMOUR)
1263 if (tmp->type == ARMOUR)
1264 {
1265 pick_up (op, tmp);
1266 continue;
1267 }
1268
1269 if (op->contr->mode & PU_HELMET)
1270 if (tmp->type == HELMET)
1271 {
1272 pick_up (op, tmp);
1273 continue;
1274 }
1275
1276 if (op->contr->mode & PU_SHIELD)
1277 if (tmp->type == SHIELD)
1278 {
1279 pick_up (op, tmp);
1280 continue;
1281 }
1282
1283 if (op->contr->mode & PU_BOOTS)
1284 if (tmp->type == BOOTS)
1285 {
1286 pick_up (op, tmp);
1287 continue;
1288 }
1289
1290 if (op->contr->mode & PU_GLOVES)
1291 if (tmp->type == GLOVES)
1292 {
1293 pick_up (op, tmp);
1294 continue;
1295 }
1296
1297 if (op->contr->mode & PU_CLOAK)
1298 if (tmp->type == CLOAK)
1299 {
1300 pick_up (op, tmp);
1301 continue;
1302 }
1303
1304 /* hoping to catch throwing daggers here */
1305 if (op->contr->mode & PU_MISSILEWEAPON)
1306 if (tmp->type == WEAPON && QUERY_FLAG (tmp, FLAG_IS_THROWN))
1307 {
1308 pick_up (op, tmp);
1309 continue;
1310 }
1311
1312 /* careful: chairs and tables are weapons! */
1313 if (op->contr->mode & PU_ALLWEAPON)
1314 {
1315 if (tmp->type == WEAPON && tmp->name != NULL)
1316 {
1317 if (strstr (tmp->name, "table") == NULL && strstr (tmp->arch->name, "table") == NULL &&
1318 strstr (tmp->name, "chair") && strstr (tmp->arch->name, "chair") == NULL)
1319 {
1320 pick_up (op, tmp);
1321 continue;
1322 }
1323 }
1324
1325 if (tmp->type == WEAPON && tmp->name == NULL)
1326 {
1327 if (strstr (tmp->arch->name, "table") == NULL && strstr (tmp->arch->name, "chair") == NULL)
1328 {
1329 pick_up (op, tmp);
1330 continue;
1331 }
1332 }
1333 }
1334
1335 /* misc stuff that's useful */
1336 if (op->contr->mode & PU_KEY)
1337 if (tmp->type == KEY || tmp->type == SPECIAL_KEY)
1338 {
1339 pick_up (op, tmp);
1340 continue;
1341 }
1342
1343 /* any of the last 4 bits set means we use the ratio for value
1344 * pickups */
1345 if (op->contr->mode & PU_RATIO)
1346 {
1347 /* use value density to decide what else to grab */
1348 /* >=7 was >= op->contr->mode */
1349 /* >=7 is the old standard setting. Now we take the last 4 bits
1350 * and multiply them by 5, giving 0..15*5== 5..75 */
1351 wvratio = (op->contr->mode & PU_RATIO) * 5;
1352 if ((query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1))) >= (unsigned int) wvratio)
1353 {
1354 pick_up (op, tmp);
1355 #if 0
1356 fprintf (stderr, "HIGH WEIGHT/VALUE [");
1357 if (tmp->name != NULL)
1358 {
1359 fprintf (stderr, "%s", tmp->name);
1360 }
1361 else
1362 fprintf (stderr, "%s", tmp->arch->name);
1363 fprintf (stderr, ",%d] = ", tmp->type);
1364 fprintf (stderr, "%d\n", (int) (query_cost (tmp, op, F_TRUE) * 100 / (tmp->weight * MAX (tmp->nrof, 1))));
1365 #endif
1366 continue;
1367 }
1368 }
1369 } /* the new pickup model */
1370 }
1371
1372 return !stop;
1373 }
1374
1375 /*
1376 * Find an arrow in the inventory and after that
1377 * in the right type container (quiver). Pointer to the
1378 * found object is returned.
1379 */
1380 object *
1381 find_arrow (object *op, const char *type)
1382 {
1383 object *tmp = 0;
1384
1385 for (op = op->inv; op; op = op->below)
1386 if (!tmp && op->type == CONTAINER && op->race == type && QUERY_FLAG (op, FLAG_APPLIED))
1387 tmp = find_arrow (op, type);
1388 else if (op->type == ARROW && op->race == type)
1389 return op;
1390
1391 return tmp;
1392 }
1393
1394 /*
1395 * Similar to find_arrow, but looks for (roughly) the best arrow to use
1396 * against the target. A full test is not performed, simply a basic test
1397 * of resistances. The archer is making a quick guess at what he sees down
1398 * the hall. Failing that it does it's best to pick the highest plus arrow.
1399 */
1400 object *
1401 find_better_arrow (object *op, object *target, const char *type, int *better)
1402 {
1403 object *tmp = NULL, *arrow, *ntmp;
1404 int attacknum, attacktype, betterby = 0, i;
1405
1406 if (!type)
1407 return NULL;
1408
1409 for (arrow = op->inv; arrow; arrow = arrow->below)
1410 {
1411 if (arrow->type == CONTAINER && arrow->race == type && QUERY_FLAG (arrow, FLAG_APPLIED))
1412 {
1413 i = 0;
1414 ntmp = find_better_arrow (arrow, target, type, &i);
1415 if (i > betterby)
1416 {
1417 tmp = ntmp;
1418 betterby = i;
1419 }
1420 }
1421 else if (arrow->type == ARROW && arrow->race == type)
1422 {
1423 /* allways prefer assasination/slaying */
1424 if (target->race != NULL && arrow->slaying != NULL && strstr (arrow->slaying, target->race))
1425 {
1426 if (arrow->attacktype & AT_DEATH)
1427 {
1428 *better = 100;
1429 return arrow;
1430 }
1431 else
1432 {
1433 tmp = arrow;
1434 betterby = (arrow->magic + arrow->stats.dam) * 2;
1435 }
1436 }
1437 else
1438 {
1439 for (attacknum = 0; attacknum < NROFATTACKS; attacknum++)
1440 {
1441 attacktype = 1 << attacknum;
1442 if ((arrow->attacktype & attacktype) && (target->arch->clone.resist[attacknum]) < 0)
1443 if (((arrow->magic + arrow->stats.dam) * (100 - target->arch->clone.resist[attacknum]) / 100) > betterby)
1444 {
1445 tmp = arrow;
1446 betterby = (arrow->magic + arrow->stats.dam) * (100 - target->arch->clone.resist[attacknum]) / 100;
1447 }
1448 }
1449 if ((2 + arrow->magic + arrow->stats.dam) > betterby)
1450 {
1451 tmp = arrow;
1452 betterby = 2 + arrow->magic + arrow->stats.dam;
1453 }
1454 if (arrow->title && (1 + arrow->magic + arrow->stats.dam) > betterby)
1455 {
1456 tmp = arrow;
1457 betterby = 1 + arrow->magic + arrow->stats.dam;
1458 }
1459 }
1460 }
1461 }
1462 if (tmp == NULL && arrow == NULL)
1463 return find_arrow (op, type);
1464
1465 *better = betterby;
1466 return tmp;
1467 }
1468
1469 /* looks in a given direction, finds the first valid target, and calls
1470 * find_better_arrow to find a decent arrow to use.
1471 * op = the shooter
1472 * type = bow->race
1473 * dir = fire direction
1474 */
1475
1476 object *
1477 pick_arrow_target (object *op, const char *type, int dir)
1478 {
1479 object *tmp = NULL;
1480 maptile *m;
1481 int i, mflags, found, number;
1482 sint16 x, y;
1483
1484 if (op->map == NULL)
1485 return find_arrow (op, type);
1486
1487 /* do a dex check */
1488 number = (die_roll (2, 40, op, PREFER_LOW) - 2) / 2;
1489 if (number > (op->stats.Dex + (op->chosen_skill ? op->chosen_skill->level : op->level)))
1490 return find_arrow (op, type);
1491
1492 m = op->map;
1493 x = op->x;
1494 y = op->y;
1495
1496 /* find the first target */
1497 for (i = 0, found = 0; i < 20; i++)
1498 {
1499 x += freearr_x[dir];
1500 y += freearr_y[dir];
1501 mflags = get_map_flags (m, &m, x, y, &x, &y);
1502 if (mflags & P_OUT_OF_MAP || mflags & P_BLOCKSVIEW)
1503 {
1504 tmp = NULL;
1505 break;
1506 }
1507 else if (GET_MAP_MOVE_BLOCK (m, x, y) == MOVE_FLY_LOW)
1508 {
1509 /* This block presumes arrows and the like are MOVE_FLY_SLOW -
1510 * perhaps a bad assumption.
1511 */
1512 tmp = NULL;
1513 break;
1514 }
1515 if (mflags & P_IS_ALIVE)
1516 {
1517 for (tmp = GET_MAP_OB (m, x, y); tmp; tmp = tmp->above)
1518 if (QUERY_FLAG (tmp, FLAG_ALIVE))
1519 {
1520 found++;
1521 break;
1522 }
1523 if (found)
1524 break;
1525 }
1526 }
1527 if (tmp == NULL)
1528 return find_arrow (op, type);
1529
1530 if (tmp->head)
1531 tmp = tmp->head;
1532
1533 return find_better_arrow (op, tmp, type, &i);
1534 }
1535
1536 /*
1537 * Creature fires a bow - op can be monster or player. Returns
1538 * 1 if bow was actually fired, 0 otherwise.
1539 * op is the object firing the bow.
1540 * part is for multipart creatures - the part firing the bow.
1541 * dir is the direction of fire.
1542 * wc_mod is any special modifier to give (used in special player fire modes)
1543 * sx, sy are coordinates to fire arrow from - also used in some of the special
1544 * player fire modes.
1545 */
1546 int
1547 fire_bow (object *op, object *part, object *arrow, int dir, int wc_mod, sint16 sx, sint16 sy)
1548 {
1549 object *left, *bow;
1550 int bowspeed, mflags;
1551 maptile *m;
1552
1553 if (!dir)
1554 {
1555 new_draw_info (NDI_UNIQUE, 0, op, "You can't shoot yourself!");
1556 return 0;
1557 }
1558
1559 if (op->type == PLAYER)
1560 bow = op->contr->ranges[range_bow];
1561 else
1562 {
1563 for (bow = op->inv; bow; bow = bow->below)
1564 /* Don't check for applied - monsters don't apply bows - in that way, they
1565 * don't need to switch back and forth between bows and weapons.
1566 */
1567 if (bow->type == BOW)
1568 break;
1569
1570 if (!bow)
1571 {
1572 LOG (llevError, "Range: bow without activated bow (%s).\n", &op->name);
1573 return 0;
1574 }
1575 }
1576
1577 if (!bow->race || !bow->skill)
1578 {
1579 new_draw_info_format (NDI_UNIQUE, 0, op, "Your %s is broken.", &bow->name);
1580 return 0;
1581 }
1582
1583 bowspeed = bow->stats.sp + dex_bonus[op->stats.Dex];
1584
1585 /* penalize ROF for bestarrow */
1586 if (op->type == PLAYER && op->contr->bowtype == bow_bestarrow)
1587 bowspeed -= dex_bonus[op->stats.Dex] + 5;
1588
1589 if (bowspeed < 1)
1590 bowspeed = 1;
1591
1592 if (arrow == NULL)
1593 {
1594 if ((arrow = find_arrow (op, bow->race)) == NULL)
1595 {
1596 if (op->type == PLAYER)
1597 new_draw_info_format (NDI_UNIQUE, 0, op, "You have no %s left.", &bow->race);
1598 /* FLAG_READY_BOW will get reset if the monsters picks up some arrows */
1599 else
1600 CLEAR_FLAG (op, FLAG_READY_BOW);
1601 return 0;
1602 }
1603 }
1604
1605 mflags = get_map_flags (op->map, &m, sx, sy, &sx, &sy);
1606 if (mflags & P_OUT_OF_MAP)
1607 return 0;
1608
1609 if (GET_MAP_MOVE_BLOCK (m, sx, sy) == MOVE_FLY_LOW)
1610 {
1611 new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way.");
1612 return 0;
1613 }
1614
1615 /* this should not happen, but sometimes does */
1616 if (arrow->nrof == 0)
1617 {
1618 arrow->destroy ();
1619 return 0;
1620 }
1621
1622 left = arrow; /* these are arrows left to the player */
1623 arrow = get_split_ob (arrow, 1);
1624 if (!arrow)
1625 {
1626 new_draw_info_format (NDI_UNIQUE, 0, op, "You have no %s left.", &bow->race);
1627 return 0;
1628 }
1629
1630 arrow->set_owner (op);
1631 arrow->skill = bow->skill;
1632 arrow->direction = dir;
1633
1634 if (op->type == PLAYER)
1635 {
1636 op->speed_left = 0.01 - (float) FABS (op->speed) * 100 / bowspeed;
1637 op->update_stats ();
1638 }
1639
1640 SET_ANIMATION (arrow, arrow->direction);
1641 arrow->stats.sp = arrow->stats.wc; /* save original wc and dam */
1642 arrow->stats.hp = arrow->stats.dam;
1643 arrow->stats.grace = arrow->attacktype;
1644 if (arrow->slaying != NULL)
1645 arrow->spellarg = strdup (arrow->slaying);
1646
1647 /* Note that this was different for monsters - they got their level
1648 * added to the damage. I think the strength bonus is more proper.
1649 */
1650
1651 arrow->stats.dam += (QUERY_FLAG (bow, FLAG_NO_STRENGTH) ? 0 : dam_bonus[op->stats.Str]) + bow->stats.dam + bow->magic + arrow->magic;
1652
1653 /* update the speed */
1654 arrow->speed = (float) ((QUERY_FLAG (bow, FLAG_NO_STRENGTH) ?
1655 0 : dam_bonus[op->stats.Str]) + bow->magic + arrow->magic) / 5.0 + (float) bow->stats.dam / 7.0;
1656
1657 arrow->set_speed (max (arrow->speed, 1.0));
1658 arrow->speed_left = 0;
1659
1660 if (op->type == PLAYER)
1661 {
1662 arrow->stats.wc = 20 - bow->magic - arrow->magic -
1663 (op->chosen_skill ? op->chosen_skill->level : op->level) -
1664 dex_bonus[op->stats.Dex] - thaco_bonus[op->stats.Str] - arrow->stats.wc - bow->stats.wc + wc_mod;
1665
1666 arrow->level = op->chosen_skill ? op->chosen_skill->level : op->level;
1667 }
1668 else
1669 {
1670 arrow->stats.wc = op->stats.wc - bow->magic - arrow->magic - arrow->stats.wc + wc_mod;
1671 arrow->level = op->level;
1672 }
1673
1674 if (arrow->attacktype == AT_PHYSICAL)
1675 arrow->attacktype |= bow->attacktype;
1676
1677 if (bow->slaying)
1678 arrow->slaying = bow->slaying;
1679
1680 arrow->move_type = MOVE_FLY_LOW;
1681 arrow->move_on = MOVE_FLY_LOW | MOVE_WALK;
1682
1683 play_sound_map (op->map, op->x, op->y, SOUND_FIRE_ARROW);
1684 m->insert (arrow, sx, sy, op);
1685
1686 if (!arrow->destroyed ())
1687 move_arrow (arrow);
1688
1689 if (op->type == PLAYER)
1690 {
1691 if (left->destroyed ())
1692 esrv_del_item (op->contr, left->count);
1693 else
1694 esrv_send_item (op, left);
1695 }
1696
1697 return 1;
1698 }
1699
1700 /* Special fire code for players - this takes into
1701 * account the special fire modes players can have
1702 * but monsters can't. Putting that code here
1703 * makes the fire_bow code much cleaner.
1704 * this function should only be called if 'op' is a player,
1705 * hence the function name.
1706 */
1707 int
1708 player_fire_bow (object *op, int dir)
1709 {
1710 int ret = 0, wcmod = 0;
1711
1712 if (op->contr->bowtype == bow_bestarrow)
1713 {
1714 ret = fire_bow (op, op, pick_arrow_target (op, op->contr->ranges[range_bow]->race, dir), dir, 0, op->x, op->y);
1715 }
1716 else if (op->contr->bowtype >= bow_n && op->contr->bowtype <= bow_nw)
1717 {
1718 if (!similar_direction (dir, op->contr->bowtype - bow_n + 1))
1719 wcmod = -1;
1720
1721 ret = fire_bow (op, op, NULL, op->contr->bowtype - bow_n + 1, wcmod, op->x, op->y);
1722 }
1723 else if (op->contr->bowtype == bow_threewide)
1724 {
1725 ret = fire_bow (op, op, NULL, dir, 0, op->x, op->y);
1726 ret |= fire_bow (op, op, NULL, dir, -5, op->x + freearr_x[absdir (dir + 2)], op->y + freearr_y[absdir (dir + 2)]);
1727 ret |= fire_bow (op, op, NULL, dir, -5, op->x + freearr_x[absdir (dir - 2)], op->y + freearr_y[absdir (dir - 2)]);
1728 }
1729 else if (op->contr->bowtype == bow_spreadshot)
1730 {
1731 ret |= fire_bow (op, op, NULL, dir, 0, op->x, op->y);
1732 ret |= fire_bow (op, op, NULL, absdir (dir - 1), -5, op->x, op->y);
1733 ret |= fire_bow (op, op, NULL, absdir (dir + 1), -5, op->x, op->y);
1734
1735 }
1736 else
1737 {
1738 /* Simple case */
1739 ret = fire_bow (op, op, NULL, dir, 0, op->x, op->y);
1740 }
1741 return ret;
1742 }
1743
1744
1745 /* Fires a misc (wand/rod/horn) object in 'dir'.
1746 * Broken apart from 'fire' to keep it more readable.
1747 */
1748 void
1749 fire_misc_object (object *op, int dir)
1750 {
1751 object *item;
1752
1753 if (!op->contr->ranges[range_misc])
1754 {
1755 new_draw_info (NDI_UNIQUE, 0, op, "You have range item readied.");
1756 return;
1757 }
1758
1759 item = op->contr->ranges[range_misc];
1760 if (!item->inv)
1761 {
1762 LOG (llevError, "Object %s lacks a spell\n", &item->name);
1763 return;
1764 }
1765 if (item->type == WAND)
1766 {
1767 if (item->stats.food <= 0)
1768 {
1769 play_sound_player_only (op->contr, SOUND_WAND_POOF, 0, 0);
1770 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s goes poof.", query_base_name (item, 0));
1771 return;
1772 }
1773 }
1774 else if (item->type == ROD || item->type == HORN)
1775 {
1776 if (item->stats.hp < MAX (item->inv->stats.sp, item->inv->stats.grace))
1777 {
1778 play_sound_player_only (op->contr, SOUND_WAND_POOF, 0, 0);
1779 if (item->type == ROD)
1780 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s whines for a while, but nothing happens.", query_base_name (item, 0));
1781 else
1782 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s needs more time to charge.", query_base_name (item, 0));
1783 return;
1784 }
1785 }
1786
1787 if (cast_spell (op, item, dir, item->inv, NULL))
1788 {
1789 SET_FLAG (op, FLAG_BEEN_APPLIED); /* You now know something about it */
1790 if (item->type == WAND)
1791 {
1792 if (!(--item->stats.food))
1793 {
1794 object *tmp;
1795
1796 if (item->arch)
1797 {
1798 CLEAR_FLAG (item, FLAG_ANIMATE);
1799 item->face = item->arch->clone.face;
1800 item->set_speed (0);
1801 }
1802
1803 if ((tmp = item->in_player ()))
1804 esrv_update_item (UPD_ANIM, tmp, item);
1805 }
1806 }
1807 else if (item->type == ROD || item->type == HORN)
1808 drain_rod_charge (item);
1809 }
1810 }
1811
1812 /* Received a fire command for the player - go and do it.
1813 */
1814 void
1815 fire (object *op, int dir)
1816 {
1817 int spellcost = 0;
1818
1819 /* check for loss of invisiblity/hide */
1820 if (action_makes_visible (op))
1821 make_visible (op);
1822
1823 switch (op->contr->shoottype)
1824 {
1825 case range_none:
1826 return;
1827
1828 case range_bow:
1829 player_fire_bow (op, dir);
1830 return;
1831
1832 case range_magic: /* Casting spells */
1833 spellcost = (cast_spell (op, op, dir, op->contr->ranges[range_magic], op->contr->spellparam[0] ? op->contr->spellparam : 0));
1834 return;
1835
1836 case range_misc:
1837 fire_misc_object (op, dir);
1838 return;
1839
1840 case range_golem: /* Control summoned monsters from scrolls */
1841 if (QUERY_FLAG (op->contr->ranges[range_golem], FLAG_REMOVED))
1842 {
1843 op->contr->ranges[range_golem] = 0;
1844 op->contr->shoottype = range_none;
1845 }
1846 else
1847 control_golem (op->contr->ranges[range_golem], dir);
1848 return;
1849
1850 case range_skill:
1851 if (!op->chosen_skill)
1852 {
1853 if (op->type == PLAYER)
1854 new_draw_info (NDI_UNIQUE, 0, op, "You have no applicable skill to use.");
1855 return;
1856 }
1857
1858 do_skill (op, op, op->chosen_skill, dir, NULL);
1859 return;
1860 case range_builder:
1861 apply_map_builder (op, dir);
1862 return;
1863 default:
1864 new_draw_info (NDI_UNIQUE, 0, op, "Illegal shoot type.");
1865 return;
1866 }
1867 }
1868
1869 /* find_key
1870 * We try to find a key for the door as passed. If we find a key
1871 * and successfully use it, we return the key, otherwise NULL
1872 * This function merges both normal and locked door, since the logic
1873 * for both is the same - just the specific key is different.
1874 * pl is the player,
1875 * inv is the objects inventory to searched
1876 * door is the door we are trying to match against.
1877 * This function can be called recursively to search containers.
1878 */
1879 object *
1880 find_key (object *pl, object *container, object *door)
1881 {
1882 object *tmp, *key;
1883
1884 /* Should not happen, but sanity checking is never bad */
1885 if (!container->inv)
1886 return 0;
1887
1888 /* First, lets try to find a key in the top level inventory */
1889 for (tmp = container->inv; tmp; tmp = tmp->below)
1890 {
1891 if (door->type == DOOR && tmp->type == KEY)
1892 break;
1893 /* For sanity, we should really check door type, but other stuff
1894 * (like containers) can be locked with special keys
1895 */
1896 if (tmp->slaying && tmp->type == SPECIAL_KEY && tmp->slaying == door->slaying)
1897 break;
1898 }
1899
1900 /* No key found - lets search inventories now */
1901 /* If we find and use a key in an inventory, return at that time.
1902 * otherwise, if we search all the inventories and still don't find
1903 * a key, return
1904 */
1905 if (!tmp)
1906 {
1907 for (tmp = container->inv; tmp; tmp = tmp->below)
1908 {
1909 /* No reason to search empty containers */
1910 if (tmp->type == CONTAINER && tmp->inv)
1911 {
1912 if ((key = find_key (pl, tmp, door)))
1913 return key;
1914 }
1915 }
1916
1917 if (!tmp)
1918 return NULL;
1919 }
1920
1921 /* We get down here if we have found a key. Now if its in a container,
1922 * see if we actually want to use it
1923 */
1924 if (pl != container)
1925 {
1926 /* Only let players use keys in containers */
1927 if (!pl->contr)
1928 return NULL;
1929 /* cases where this fails:
1930 * If we only search the player inventory, return now since we
1931 * are not in the players inventory.
1932 * If the container is not active, return now since only active
1933 * containers can be used.
1934 * If we only search keyrings and the container does not have
1935 * a race/isn't a keyring.
1936 * No checking for all containers - to fall through past here,
1937 * inv must have been an container and must have been active.
1938 *
1939 * Change the color so that the message doesn't disappear with
1940 * all the others.
1941 */
1942 if (pl->contr->usekeys == key_inventory ||
1943 !QUERY_FLAG (container, FLAG_APPLIED) ||
1944 (pl->contr->usekeys == keyrings && (!container->race || strcmp (container->race, "keys"))))
1945 {
1946 new_draw_info_format (NDI_UNIQUE | NDI_BROWN, 0, pl,
1947 "The %s in your %s vibrates as you approach the door", query_name (tmp), query_name (container));
1948 return NULL;
1949 }
1950 }
1951
1952 return tmp;
1953 }
1954
1955 /* moved door processing out of move_player_attack.
1956 * returns 1 if player has opened the door with a key
1957 * such that the caller should not do anything more,
1958 * 0 otherwise
1959 */
1960 static int
1961 player_attack_door (object *op, object *door)
1962 {
1963 /* If its a door, try to find a use a key. If we do destroy the door,
1964 * might as well return immediately as there is nothing more to do -
1965 * otherwise, we fall through to the rest of the code.
1966 */
1967 object *key = find_key (op, op, door);
1968
1969 /* IF we found a key, do some extra work */
1970 if (key)
1971 {
1972 object *container = key->env;
1973
1974 play_sound_map (op->map, op->x, op->y, SOUND_OPEN_DOOR);
1975 if (action_makes_visible (op))
1976 make_visible (op);
1977 if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP))
1978 spring_trap (door->inv, op);
1979
1980 if (door->type == DOOR)
1981 hit_player (door, 9998, op, AT_PHYSICAL, 1); /* Break through the door */
1982 else if (door->type == LOCKED_DOOR)
1983 {
1984 new_draw_info_format (NDI_UNIQUE, NDI_BROWN, op, "You open the door with the %s", query_short_name (key));
1985 remove_door2 (door); /* remove door without violence ;-) */
1986 }
1987
1988 /* Do this after we print the message */
1989 decrease_ob (key); /* Use up one of the keys */
1990 /* Need to update the weight the container the key was in */
1991 if (container != op)
1992 esrv_update_item (UPD_WEIGHT, op, container);
1993
1994 return 1; /* Nothing more to do below */
1995 }
1996 else if (door->type == LOCKED_DOOR)
1997 {
1998 /* Might as well return now - no other way to open this */
1999 new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, op, door->msg);
2000 return 1;
2001 }
2002
2003 return 0;
2004 }
2005
2006 /* This function is just part of a breakup from move_player.
2007 * It should keep the code cleaner.
2008 * When this is called, the players direction has been updated
2009 * (taking into account confusion.) The player is also actually
2010 * going to try and move (not fire weapons).
2011 */
2012 void
2013 move_player_attack (object *op, int dir)
2014 {
2015 object *tmp, *mon;
2016 sint16 nx, ny;
2017 int on_battleground;
2018 maptile *m;
2019
2020 nx = freearr_x[dir] + op->x;
2021 ny = freearr_y[dir] + op->y;
2022
2023 on_battleground = op_on_battleground (op, 0, 0);
2024
2025 /* If braced, or can't move to the square, and it is not out of the
2026 * map, attack it. Note order of if statement is important - don't
2027 * want to be calling move_ob if braced, because move_ob will move the
2028 * player. This is a pretty nasty hack, because if we could
2029 * move to some space, it then means that if we are braced, we should
2030 * do nothing at all. As it is, if we are braced, we go through
2031 * quite a bit of processing. However, it probably is less than what
2032 * move_ob uses.
2033 */
2034 if ((op->contr->braced || !move_ob (op, dir, op)) && !out_of_map (op->map, nx, ny))
2035 {
2036 if (OUT_OF_REAL_MAP (op->map, nx, ny))
2037 {
2038 m = op->map->xy_find (nx, ny);
2039 if (!m)
2040 return; /* Don't think this should happen */
2041 }
2042 else
2043 m = op->map;
2044
2045 if (!(tmp = m->at (nx, ny).bot))
2046 return;
2047
2048 mon = 0;
2049 /* Go through all the objects, and find ones of interest. Only stop if
2050 * we find a monster - that is something we know we want to attack.
2051 * if its a door or barrel (can roll) see if there may be monsters
2052 * on the space
2053 */
2054 while (tmp)
2055 {
2056 if (tmp == op)
2057 {
2058 tmp = tmp->above;
2059 continue;
2060 }
2061
2062 if (QUERY_FLAG (tmp, FLAG_ALIVE))
2063 {
2064 mon = tmp;
2065 break;
2066 }
2067
2068 if (tmp->type == LOCKED_DOOR || QUERY_FLAG (tmp, FLAG_CAN_ROLL))
2069 mon = tmp;
2070
2071 tmp = tmp->above;
2072 }
2073
2074 if (!mon) /* This happens anytime the player tries to move */
2075 return; /* into a wall */
2076
2077 if (mon->head)
2078 mon = mon->head;
2079
2080 if ((mon->type == DOOR && mon->stats.hp >= 0) || (mon->type == LOCKED_DOOR))
2081 if (player_attack_door (op, mon))
2082 return;
2083
2084 /* The following deals with possibly attacking peaceful
2085 * or frienddly creatures. Basically, all players are considered
2086 * unaggressive. If the moving player has peaceful set, then the
2087 * object should be pushed instead of attacked. It is assumed that
2088 * if you are braced, you will not attack friends accidently,
2089 * and thus will not push them.
2090 */
2091
2092 /* If the creature is a pet, push it even if the player is not
2093 * peaceful. Our assumption is the creature is a pet if the
2094 * player owns it and it is either friendly or unagressive.
2095 */
2096 if ((op->type == PLAYER)
2097 #if COZY_SERVER
2098 &&
2099 ((mon->owner && mon->owner->contr
2100 && same_party (mon->owner->contr->party, op->contr->party)) || mon->owner == op)
2101 #else
2102 && mon->owner == op
2103 #endif
2104 && (QUERY_FLAG (mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG (mon, FLAG_FRIENDLY)))
2105 {
2106 /* If we're braced, we don't want to switch places with it */
2107 if (op->contr->braced)
2108 return;
2109
2110 play_sound_map (op->map, op->x, op->y, SOUND_PUSH_PLAYER);
2111 (void) push_ob (mon, dir, op);
2112 if (op->contr->tmp_invis || op->hide)
2113 make_visible (op);
2114
2115 return;
2116 }
2117
2118 /* in certain circumstances, you shouldn't attack friendly
2119 * creatures. Note that if you are braced, you can't push
2120 * someone, but put it inside this loop so that you won't
2121 * attack them either.
2122 */
2123 if ((mon->type == PLAYER || mon->enemy != op) &&
2124 (mon->type == PLAYER || QUERY_FLAG (mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG (mon, FLAG_FRIENDLY)) && (
2125 #ifdef PROHIBIT_PLAYERKILL
2126 (op->contr->peaceful
2127 || (mon->type == PLAYER
2128 && mon->contr->
2129 peaceful)) &&
2130 #else
2131 op->contr->peaceful &&
2132 #endif
2133 !on_battleground))
2134 {
2135 if (!op->contr->braced)
2136 {
2137 play_sound_map (op->map, op->x, op->y, SOUND_PUSH_PLAYER);
2138 push_ob (mon, dir, op);
2139 }
2140 else
2141 new_draw_info (0, 0, op, "You withhold your attack");
2142
2143 if (op->contr->tmp_invis || op->hide)
2144 make_visible (op);
2145 }
2146
2147 /* If the object is a boulder or other rollable object, then
2148 * roll it if not braced. You can't roll it if you are braced.
2149 */
2150 else if (QUERY_FLAG (mon, FLAG_CAN_ROLL) && (!op->contr->braced))
2151 {
2152 recursive_roll (mon, dir, op);
2153 if (action_makes_visible (op))
2154 make_visible (op);
2155 }
2156
2157 /* Any generic living creature. Including things like doors.
2158 * Way it works is like this: First, it must have some hit points
2159 * and be living. Then, it must be one of the following:
2160 * 1) Not a player, 2) A player, but of a different party. Note
2161 * that party_number -1 is no party, so attacks can still happen.
2162 */
2163 else if ((mon->stats.hp >= 0) && QUERY_FLAG (mon, FLAG_ALIVE) &&
2164 ((mon->type != PLAYER || op->contr->party == NULL || op->contr->party != mon->contr->party)))
2165 {
2166
2167 /* If the player hasn't hit something this tick, and does
2168 * so, give them speed boost based on weapon speed. Doing
2169 * it here is better than process_players2, which basically
2170 * incurred a 1 tick offset.
2171 */
2172 if (!op->contr->has_hit)
2173 {
2174 op->speed_left += op->speed / op->contr->weapon_sp;
2175
2176 op->contr->has_hit = 1; /* The last action was to hit, so use weapon_sp */
2177 }
2178
2179 skill_attack (mon, op, 0, 0, 0);
2180
2181 /* If attacking another player, that player gets automatic
2182 * hitback, and doesn't loose luck either.
2183 * Disable hitback on the battleground or if the target is
2184 * the wiz.
2185 */
2186 if (mon->type == PLAYER && mon->stats.hp >= 0 && !mon->contr->has_hit && !on_battleground && !QUERY_FLAG (mon, FLAG_WIZ))
2187 {
2188 short luck = mon->stats.luck;
2189
2190 mon->contr->has_hit = 1;
2191 skill_attack (op, mon, 0, 0, 0);
2192 mon->stats.luck = luck;
2193 }
2194
2195 if (action_makes_visible (op))
2196 make_visible (op);
2197 }
2198 } /* if player should attack something */
2199 }
2200
2201 int
2202 move_player (object *op, int dir)
2203 {
2204 int pick;
2205
2206 if (!op->map || op->map->in_memory != MAP_IN_MEMORY)
2207 return 0;
2208
2209 /* Sanity check: make sure dir is valid */
2210 if ((dir < 0) || (dir >= 9))
2211 {
2212 LOG (llevError, "move_player: invalid direction %d\n", dir);
2213 return 0;
2214 }
2215
2216 /* peterm: added following line */
2217 if (QUERY_FLAG (op, FLAG_CONFUSED) && dir)
2218 dir = absdir (dir + rndm (3) + rndm (3) - 2);
2219
2220 op->facing = dir;
2221
2222 if (op->hide)
2223 do_hidden_move (op);
2224
2225 if (INVOKE_PLAYER (MOVE, op->contr, ARG_INT (dir)))
2226 /*nop */ ;
2227 else if (op->contr->fire_on)
2228 fire (op, dir);
2229 else
2230 {
2231 move_player_attack (op, dir);
2232 pick = check_pick (op);
2233 }
2234
2235 /* Add special check for newcs players and fire on - this way, the
2236 * server can handle repeat firing.
2237 */
2238 if (op->contr->fire_on || (op->contr->run_on && pick != 0))
2239 op->direction = dir;
2240 else
2241 op->direction = 0;
2242
2243 /* Update how the player looks. Use the facing, so direction may
2244 * get reset to zero. This allows for full animation capabilities
2245 * for players.
2246 */
2247 animate_object (op, op->facing);
2248 return 0;
2249 }
2250
2251 /* This is similar to handle_player, below, but is only used by the
2252 * new client/server stuff.
2253 * This is sort of special, in that the new client/server actually uses
2254 * the new speed values for commands.
2255 *
2256 * Returns true if there are more actions we can do.
2257 */
2258 int
2259 handle_newcs_player (object *op)
2260 {
2261 if (QUERY_FLAG (op, FLAG_SCARED))
2262 {
2263 flee_player (op);
2264 /* If player is still scared, that is his action for this tick */
2265 if (QUERY_FLAG (op, FLAG_SCARED))
2266 {
2267 op->speed_left--;
2268 return 0;
2269 }
2270 }
2271
2272 /* I've been seeing crashes where the golem has been destroyed, but
2273 * the player object still points to the defunct golem. The code that
2274 * destroys the golem looks correct, and it doesn't always happen, so
2275 * put this in a a workaround to clean up the golem pointer.
2276 */
2277 if (op->contr->ranges[range_golem] && QUERY_FLAG (op->contr->ranges[range_golem], FLAG_REMOVED))
2278 op->contr->ranges[range_golem] = 0;
2279
2280 /* call this here - we also will call this in do_ericserver, but
2281 * the players time has been increased when doericserver has been
2282 * called, so we recheck it here.
2283 */
2284 if (op->contr->ns->handle_command ())
2285 return 1;
2286
2287 if (op->speed_left > 0)
2288 {
2289 if (op->direction && (op->contr->run_on || op->contr->fire_on))
2290 {
2291 /* All move commands take 1 tick, at least for now */
2292 op->speed_left--;
2293
2294 /* Instead of all the stuff below, let move_player take care
2295 * of it. Also, some of the skill stuff is only put in
2296 * there, as well as the confusion stuff.
2297 */
2298 move_player (op, op->direction);
2299
2300 return op->speed_left > 0;
2301 }
2302 }
2303
2304 return 0;
2305 }
2306
2307 int
2308 save_life (object *op)
2309 {
2310 if (!QUERY_FLAG (op, FLAG_LIFESAVE))
2311 return 0;
2312
2313 for (object *tmp = op->inv; tmp; tmp = tmp->below)
2314 if (QUERY_FLAG (tmp, FLAG_APPLIED) && QUERY_FLAG (tmp, FLAG_LIFESAVE))
2315 {
2316 play_sound_map (op->map, op->x, op->y, SOUND_OB_EVAPORATE);
2317 new_draw_info_format (NDI_UNIQUE, 0, op, "Your %s vibrates violently, then evaporates.", query_name (tmp));
2318
2319 if (op->contr)
2320 esrv_del_item (op->contr, tmp->count);
2321
2322 tmp->destroy ();
2323 CLEAR_FLAG (op, FLAG_LIFESAVE);
2324
2325 if (op->stats.hp < 0)
2326 op->stats.hp = op->stats.maxhp;
2327
2328 if (op->stats.food < 0)
2329 op->stats.food = 999;
2330
2331 op->update_stats ();
2332 return 1;
2333 }
2334
2335 LOG (llevError, "Error: LIFESAVE set without applied object.\n");
2336 CLEAR_FLAG (op, FLAG_LIFESAVE);
2337 enter_player_savebed (op); /* bring him home. */
2338 return 0;
2339 }
2340
2341 /* This goes throws the inventory and removes unpaid objects, and puts them
2342 * back in the map (location and map determined by values of env). This
2343 * function will descend into containers. op is the object to start the search
2344 * from.
2345 */
2346 void
2347 remove_unpaid_objects (object *op, object *env)
2348 {
2349 while (op)
2350 {
2351 object *next = op->below; /* Make sure we have a good value, in case we remove object 'op' */
2352
2353 if (QUERY_FLAG (op, FLAG_UNPAID))
2354 {
2355 if (env->type == PLAYER)
2356 esrv_del_item (env->contr, op->count);
2357
2358 op->insert_at (env);
2359 }
2360 else if (op->inv)
2361 remove_unpaid_objects (op->inv, env);
2362
2363 op = next;
2364 }
2365 }
2366
2367 /*
2368 * Returns pointer a static string containing gravestone text
2369 * Moved from apply.c to player.c - player.c is what
2370 * actually uses this function. player.c may not be quite the
2371 * best, a misc file for object actions is probably better,
2372 * but there isn't one in the server directory.
2373 */
2374 char *
2375 gravestone_text (object *op)
2376 {
2377 static char buf2[MAX_BUF];
2378 char buf[MAX_BUF];
2379 time_t now = time (NULL);
2380
2381 strcpy (buf2, " R.I.P.\n\n");
2382 if (op->type == PLAYER)
2383 sprintf (buf, "%s the %s\n", &op->name, op->contr->title);
2384 else
2385 sprintf (buf, "%s\n", &op->name);
2386
2387 strncat (buf2, " ", 20 - strlen (buf) / 2);
2388 strcat (buf2, buf);
2389 if (op->type == PLAYER)
2390 sprintf (buf, "who was in level %d when killed\n", op->level);
2391 else
2392 sprintf (buf, "who was in level %d when died.\n\n", op->level);
2393
2394 strncat (buf2, " ", 20 - strlen (buf) / 2);
2395 strcat (buf2, buf);
2396 if (op->type == PLAYER)
2397 {
2398 sprintf (buf, "by %s.\n\n", op->contr->killer);
2399 strncat (buf2, " ", 21 - strlen (buf) / 2);
2400 strcat (buf2, buf);
2401 }
2402
2403 strftime (buf, MAX_BUF, "%b %d %Y\n", localtime (&now));
2404 strncat (buf2, " ", 20 - strlen (buf) / 2);
2405 strcat (buf2, buf);
2406
2407 return buf2;
2408 }
2409
2410 void
2411 do_some_living (object *op)
2412 {
2413 int last_food = op->stats.food;
2414 int gen_hp, gen_sp, gen_grace;
2415 int over_hp, over_sp, over_grace;
2416 int i;
2417 int rate_hp = 1200;
2418 int rate_sp = 2500;
2419 int rate_grace = 2000;
2420 const int max_hp = 1;
2421 const int max_sp = 1;
2422 const int max_grace = 1;
2423
2424 if (op->contr->hidden)
2425 {
2426 op->invisible = 1000;
2427 /* the socket code flashes the player visible/invisible
2428 * depending on the value of invisible, so we need to
2429 * alternate it here for it to work correctly.
2430 */
2431 if (pticks & 2)
2432 op->invisible--;
2433 }
2434 else if (op->invisible && !(QUERY_FLAG (op, FLAG_MAKE_INVIS)))
2435 {
2436 if (!op->invisible--)
2437 {
2438 make_visible (op);
2439 new_draw_info (NDI_UNIQUE, 0, op, "Your invisibility spell runs out.");
2440 }
2441 }
2442
2443 if (op->contr->outputs_sync)
2444 {
2445 for (i = 0; i < NUM_OUTPUT_BUFS; i++)
2446 if (op->contr->outputs[i].buf && (op->contr->outputs[i].first_update + op->contr->outputs_sync) < (uint16) pticks)
2447 flush_output_element (op, &op->contr->outputs[i]);
2448 }
2449
2450 if (op->contr->ns->state == ST_PLAYING)
2451 {
2452 /* these next three if clauses make it possible to SLOW DOWN
2453 hp/grace/spellpoint regeneration. */
2454 if (op->contr->gen_hp >= 0)
2455 gen_hp = (op->contr->gen_hp + 1) * op->stats.maxhp;
2456 else
2457 {
2458 gen_hp = op->stats.maxhp;
2459 rate_hp -= rate_hp / 2 * op->contr->gen_hp;
2460 }
2461
2462 if (op->contr->gen_sp >= 0)
2463 gen_sp = (op->contr->gen_sp + 1) * op->stats.maxsp;
2464 else
2465 {
2466 gen_sp = op->stats.maxsp;
2467 rate_sp -= rate_sp / 2 * op->contr->gen_sp;
2468 }
2469
2470 if (op->contr->gen_grace >= 0)
2471 gen_grace = (op->contr->gen_grace + 1) * op->stats.maxgrace;
2472 else
2473 {
2474 gen_grace = op->stats.maxgrace;
2475 rate_grace -= rate_grace / 2 * op->contr->gen_grace;
2476 }
2477
2478 /* Regenerate Spell Points */
2479 if (op->contr->ranges[range_golem] == NULL && --op->last_sp < 0)
2480 {
2481 gen_sp = gen_sp * 10 / (op->contr->gen_sp_armour < 10 ? 10 : op->contr->gen_sp_armour);
2482 if (op->stats.sp < op->stats.maxsp)
2483 {
2484 op->stats.sp++;
2485 /* dms do not consume food */
2486 if (!QUERY_FLAG (op, FLAG_WIZ))
2487 {
2488 op->stats.food--;
2489 if (op->contr->digestion < 0)
2490 op->stats.food += op->contr->digestion;
2491 else if (op->contr->digestion > 0 && random_roll (0, op->contr->digestion, op, PREFER_HIGH))
2492 op->stats.food = last_food;
2493 }
2494 }
2495
2496 if (max_sp > 1)
2497 {
2498 over_sp = (gen_sp + 10) / rate_sp;
2499 if (over_sp > 0)
2500 {
2501 if (op->stats.sp < op->stats.maxsp)
2502 {
2503 op->stats.sp += over_sp > max_sp ? max_sp : over_sp;
2504
2505 if (random_roll (0, rate_sp - 1, op, PREFER_LOW) > ((gen_sp + 10) % rate_sp))
2506 op->stats.sp--;
2507
2508 if (op->stats.sp > op->stats.maxsp)
2509 op->stats.sp = op->stats.maxsp;
2510 }
2511 op->last_sp = 0;
2512 }
2513 else
2514 op->last_sp = rate_sp / (gen_sp < 20 ? 30 : gen_sp + 10);
2515 }
2516 else
2517 op->last_sp = rate_sp / (gen_sp < 20 ? 30 : gen_sp + 10);
2518 }
2519
2520 /* Regenerate Grace */
2521 /* I altered this a little - maximum grace is ony achieved through prayer -b.t. */
2522 if (--op->last_grace < 0)
2523 {
2524 if (op->stats.grace < op->stats.maxgrace / 2)
2525 op->stats.grace++; /* no penalty in food for regaining grace */
2526
2527 if (max_grace > 1)
2528 {
2529 over_grace = (gen_grace < 20 ? 30 : gen_grace + 10) / rate_grace;
2530 if (over_grace > 0)
2531 {
2532 op->stats.sp += over_grace
2533 + (random_roll (0, rate_grace - 1, op, PREFER_HIGH) > ((gen_grace < 20 ? 30 : gen_grace + 10) % rate_grace)) ? -1 : 0;
2534 op->last_grace = 0;
2535 }
2536 else
2537 {
2538 op->last_grace = rate_grace / (gen_grace < 20 ? 30 : gen_grace + 10);
2539 }
2540 }
2541 else
2542 {
2543 op->last_grace = rate_grace / (gen_grace < 20 ? 30 : gen_grace + 10);
2544 }
2545 /* wearing stuff doesn't detract from grace generation. */
2546 }
2547
2548 /* Regenerate Hit Points */
2549 if (--op->last_heal < 0)
2550 {
2551 if (op->stats.hp < op->stats.maxhp)
2552 {
2553 op->stats.hp++;
2554 /* dms do not consume food */
2555 if (!QUERY_FLAG (op, FLAG_WIZ))
2556 {
2557 op->stats.food--;
2558 if (op->contr->digestion < 0)
2559 op->stats.food += op->contr->digestion;
2560 else if (op->contr->digestion > 0 && random_roll (0, op->contr->digestion, op, PREFER_HIGH))
2561 op->stats.food = last_food;
2562 }
2563 }
2564
2565 if (max_hp > 1)
2566 {
2567 over_hp = (gen_hp < 20 ? 30 : gen_hp + 10) / rate_hp;
2568 if (over_hp > 0)
2569 {
2570 op->stats.sp += over_hp + (RANDOM () % rate_hp > ((gen_hp < 20 ? 30 : gen_hp + 10) % rate_hp)) ? -1 : 0;
2571 op->last_heal = 0;
2572 }
2573 else
2574 {
2575 op->last_heal = rate_hp / (gen_hp < 20 ? 30 : gen_hp + 10);
2576 }
2577 }
2578 else
2579 {
2580 op->last_heal = rate_hp / (gen_hp < 20 ? 30 : gen_hp + 10);
2581 }
2582 }
2583
2584 /* Digestion */
2585 if (--op->last_eat < 0)
2586 {
2587 #ifdef COZY_SERVER
2588 int dg = op->contr->digestion >= 0 && op->contr->digestion < 2 ? 2 : op->contr->digestion;
2589 int bonus = dg > 0 ? dg : 0, penalty = dg < 0 ? -dg : 0;
2590 #else
2591 int bonus = op->contr->digestion > 0 ? op->contr->digestion : 0, penalty = op->contr->digestion < 0 ? -op->contr->digestion : 0;
2592 #endif
2593
2594 if (op->contr->gen_hp > 0)
2595 op->last_eat = 25 * (1 + bonus) / (op->contr->gen_hp + penalty + 1);
2596 else
2597 op->last_eat = 25 * (1 + bonus) / (penalty + 1);
2598
2599 /* dms do not consume food */
2600 if (!QUERY_FLAG (op, FLAG_WIZ))
2601 op->stats.food--;
2602 }
2603
2604 if (op->stats.food < 0 && op->stats.hp >= 0)
2605 {
2606 object *tmp, *flesh = 0;
2607
2608 for (tmp = op->inv; tmp; tmp = tmp->below)
2609 {
2610 if (!QUERY_FLAG (tmp, FLAG_UNPAID))
2611 {
2612 if (tmp->type == FOOD || tmp->type == DRINK || tmp->type == POISON)
2613 {
2614 new_draw_info (NDI_UNIQUE, 0, op, "You blindly grab for a bite of food.");
2615 manual_apply (op, tmp, 0);
2616 if (op->stats.food >= 0 || op->stats.hp < 0)
2617 break;
2618 }
2619 else if (tmp->type == FLESH)
2620 flesh = tmp;
2621 } /* End if paid for object */
2622 } /* end of for loop */
2623
2624 /* If player is still starving, it means they don't have any food, so
2625 * eat flesh instead.
2626 */
2627 if (op->stats.food < 0 && op->stats.hp >= 0 && flesh)
2628 {
2629 new_draw_info (NDI_UNIQUE, 0, op, "You blindly grab for a bite of food.");
2630 manual_apply (op, flesh, 0);
2631 }
2632 }
2633
2634 while (op->stats.food < 0 && op->stats.hp >= 0)
2635 op->stats.food++, op->stats.hp--;
2636
2637 if (op->stats.hp < 0 && !QUERY_FLAG (op, FLAG_WIZ))
2638 kill_player (op);
2639 }
2640 }
2641
2642 /* If the player should die (lack of hp, food, etc), we call this.
2643 * op is the player in jeopardy. If the player can not be saved (not
2644 * permadeath, no lifesave), this will take care of removing the player
2645 * file.
2646 */
2647 void
2648 kill_player (object *op)
2649 {
2650 char buf[MAX_BUF];
2651 int x, y;
2652
2653 //int i;
2654 maptile *map; /* this is for resurrection */
2655
2656 /* int z;
2657 int num_stats_lose;
2658 int lost_a_stat;
2659 int lose_this_stat;
2660 int this_stat; */
2661 int will_kill_again;
2662 archetype *at;
2663 object *tmp;
2664
2665 if (save_life (op))
2666 return;
2667
2668
2669 /* If player dies on BATTLEGROUND, no stat/exp loss! For Combat-Arenas
2670 * in cities ONLY!!! It is very important that this doesn't get abused.
2671 * Look at op_on_battleground() for more info --AndreasV
2672 */
2673 if (op_on_battleground (op, &x, &y))
2674 {
2675 new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, op, "You have been defeated in combat!");
2676 new_draw_info (NDI_UNIQUE | NDI_NAVY, 0, op, "Local medics have saved your life...");
2677
2678 /* restore player */
2679 at = archetype::find ("poisoning");
2680 if (object *tmp = present_arch_in_ob (at, op))
2681 {
2682 tmp->destroy ();
2683 new_draw_info (NDI_UNIQUE, 0, op, "Your body feels cleansed");
2684 }
2685
2686 at = archetype::find ("confusion");
2687 if (object *tmp = present_arch_in_ob (at, op))
2688 {
2689 tmp->destroy ();
2690 new_draw_info (NDI_UNIQUE, 0, tmp, "Your mind feels clearer");
2691 }
2692
2693 cure_disease (op, 0); /* remove any disease */
2694 op->stats.hp = op->stats.maxhp;
2695 if (op->stats.food <= 0)
2696 op->stats.food = 999;
2697
2698 /* create a bodypart-trophy to make the winner happy */
2699 if (object *tmp = arch_to_object (archetype::find ("finger")))
2700 {
2701 sprintf (buf, "%s's finger", &op->name);
2702 tmp->name = buf;
2703 sprintf (buf, " This finger has been cut off %s\n"
2704 " the %s, when he was defeated at\n level %d by %s.\n",
2705 &op->name, op->contr->title, (int) (op->level), op->contr->killer);
2706 tmp->msg = buf;
2707 tmp->value = 0, tmp->type = 0;
2708 tmp->materialname = "organics";
2709 tmp->insert_at (op, tmp);
2710 }
2711
2712 /* teleport defeated player to new destination */
2713 transfer_ob (op, x, y, 0, NULL);
2714 op->contr->braced = 0;
2715 return;
2716 }
2717
2718 INVOKE_PLAYER (DEATH, op->contr);
2719
2720 command_kill_pets (op, 0);
2721
2722 if (op->stats.food < 0)
2723 {
2724 sprintf (buf, "%s starved to death.", &op->name);
2725 strcpy (op->contr->killer, "starvation");
2726 }
2727 else
2728 sprintf (buf, "%s died.", &op->name);
2729
2730 play_sound_player_only (op->contr, SOUND_PLAYER_DIES, 0, 0);
2731
2732 /* save the map location for corpse, gravestone */
2733 x = op->x;
2734 y = op->y;
2735 map = op->map;
2736
2737 /* NOT_PERMADEATH code. This basically brings the character back to
2738 * life if they are dead - it takes some exp and a random stat.
2739 * See the config.h file for a little more in depth detail about this.
2740 */
2741
2742 /* Basically two ways to go - remove a stat permanently, or just
2743 * make it depletion. This bunch of code deals with that aspect
2744 * of death.
2745 */
2746 #ifndef COZY_SERVER
2747 if (settings.balanced_stat_loss)
2748 {
2749 /* If stat loss is permanent, lose one stat only. */
2750 /* Lower level chars don't lose as many stats because they suffer
2751 more if they do. */
2752 /* Higher level characters can afford things such as potions of
2753 restoration, or better, stat potions. So we slug them that
2754 little bit harder. */
2755 /* GD */
2756 if (settings.stat_loss_on_death)
2757 num_stats_lose = 1;
2758 else
2759 num_stats_lose = 1 + op->level / BALSL_NUMBER_LOSSES_RATIO;
2760 }
2761 else
2762 num_stats_lose = 1;
2763
2764 lost_a_stat = 0;
2765
2766 for (z = 0; z < num_stats_lose; z++)
2767 {
2768 i = RANDOM () % NUM_STATS;
2769
2770 if (settings.stat_loss_on_death)
2771 {
2772 /* Pick a random stat and take a point off it. Tell the player
2773 * what he lost.
2774 */
2775 change_attr_value (&(op->stats), i, -1);
2776 check_stat_bounds (&(op->stats));
2777 change_attr_value (&(op->contr->orig_stats), i, -1);
2778 check_stat_bounds (&(op->contr->orig_stats));
2779 new_draw_info (NDI_UNIQUE, 0, op, lose_msg[i]);
2780 lost_a_stat = 1;
2781 }
2782 else
2783 {
2784 /* deplete a stat */
2785 archetype *deparch = archetype::find ("depletion");
2786 object *dep;
2787
2788 dep = present_arch_in_ob (deparch, op);
2789 if (!dep)
2790 {
2791 dep = arch_to_object (deparch);
2792 insert_ob_in_ob (dep, op);
2793 }
2794 lose_this_stat = 1;
2795 if (settings.balanced_stat_loss)
2796 {
2797 /* GD */
2798 /* Get the stat that we're about to deplete. */
2799 this_stat = get_attr_value (&(dep->stats), i);
2800 if (this_stat < 0)
2801 {
2802 int loss_chance = 1 + op->level / BALSL_LOSS_CHANCE_RATIO;
2803 int keep_chance = this_stat * this_stat;
2804
2805 /* Yes, I am paranoid. Sue me. */
2806 if (keep_chance < 1)
2807 keep_chance = 1;
2808
2809 /* There is a maximum depletion total per level. */
2810 if (this_stat < -1 - op->level / BALSL_MAX_LOSS_RATIO)
2811 {
2812 lose_this_stat = 0;
2813 /* Take loss chance vs keep chance to see if we
2814 retain the stat. */
2815 }
2816 else
2817 {
2818 if (random_roll (0, loss_chance + keep_chance - 1, op, PREFER_LOW) < keep_chance)
2819 lose_this_stat = 0;
2820 /* LOG(llevDebug, "Determining stat loss. Stat: %d Keep: %d Lose: %d Result: %s.\n",
2821 this_stat, keep_chance, loss_chance,
2822 lose_this_stat?"LOSE":"KEEP"); */
2823 }
2824 }
2825 }
2826
2827 if (lose_this_stat)
2828 {
2829 this_stat = get_attr_value (&(dep->stats), i);
2830 /* We could try to do something clever like find another
2831 * stat to reduce if this fails. But chances are, if
2832 * stats have been depleted to -50, all are pretty low
2833 * and should be roughly the same, so it shouldn't make a
2834 * difference.
2835 */
2836 if (this_stat >= -50)
2837 {
2838 change_attr_value (&(dep->stats), i, -1);
2839 SET_FLAG (dep, FLAG_APPLIED);
2840 new_draw_info (NDI_UNIQUE, 0, op, lose_msg[i]);
2841 op->update_stats ();
2842 lost_a_stat = 1;
2843 }
2844 }
2845 }
2846 }
2847 /* If no stat lost, tell the player. */
2848 if (!lost_a_stat)
2849 {
2850 /* determine_god() seems to not work sometimes... why is this?
2851 Should I be using something else? GD */
2852 const char *god = determine_god (op);
2853
2854 if (god && (strcmp (god, "none")))
2855 new_draw_info_format (NDI_UNIQUE, 0, op, "For a brief moment you feel the holy presence of %s protecting" " you.", god);
2856 else
2857 new_draw_info (NDI_UNIQUE, 0, op, "For a brief moment you feel a holy presence protecting you.");
2858 }
2859 #else
2860 new_draw_info (NDI_UNIQUE, 0, op, "For a brief moment you" " feel a holy presence protecting you from losing yourself completely.");
2861 #endif
2862
2863 /* Put a gravestone up where the character 'almost' died. List the
2864 * exp loss on the stone.
2865 */
2866 tmp = arch_to_object (archetype::find ("gravestone"));
2867 sprintf (buf, "%s's gravestone", &op->name);
2868 tmp->name = buf;
2869 sprintf (buf, "%s's gravestones", &op->name);
2870 tmp->name_pl = buf;
2871 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);
2872 tmp->msg = buf;
2873 tmp->x = op->x, tmp->y = op->y;
2874 insert_ob_in_map (tmp, op->map, NULL, 0);
2875
2876 /**************************************/
2877 /* */
2878 /* Subtract the experience points, */
2879 /* if we died cause of food, give us */
2880 /* food, and reset HP's... */
2881 /* */
2882 /**************************************/
2883
2884 /* remove any poisoning and confusion the character may be suffering. */
2885 /* restore player */
2886 at = archetype::find ("poisoning");
2887 tmp = present_arch_in_ob (at, op);
2888
2889 if (tmp)
2890 {
2891 tmp->destroy ();
2892 new_draw_info (NDI_UNIQUE, 0, op, "Your body feels cleansed");
2893 }
2894
2895 at = archetype::find ("confusion");
2896 tmp = present_arch_in_ob (at, op);
2897 if (tmp)
2898 {
2899 tmp->destroy ();
2900 new_draw_info (NDI_UNIQUE, 0, tmp, "Your mind feels clearer");
2901 }
2902
2903 cure_disease (op, 0); /* remove any disease */
2904
2905 /*add_exp(op, (op->stats.exp * -0.20)); */
2906 apply_death_exp_penalty (op);
2907 if (op->stats.food < 100)
2908 op->stats.food = 900;
2909 op->stats.hp = op->stats.maxhp;
2910 op->stats.sp = MAX (op->stats.sp, op->stats.maxsp);
2911 op->stats.grace = MAX (op->stats.grace, op->stats.maxgrace);
2912
2913 /*
2914 * Check to see if the player is in a shop. IF so, then check to see if
2915 * the player has any unpaid items. If so, remove them and put them back
2916 * in the map.
2917 */
2918
2919 if (is_in_shop (op))
2920 remove_unpaid_objects (op->inv, op);
2921
2922 /****************************************/
2923 /* */
2924 /* Move player to his current respawn- */
2925 /* position (usually last savebed) */
2926 /* */
2927 /****************************************/
2928
2929 enter_player_savebed (op);
2930
2931 op->contr->braced = 0;
2932
2933 /* it is possible that the player has blown something up
2934 * at his savebed location, and that can have long lasting
2935 * spell effects. So first see if there is a spell effect
2936 * on the space that might harm the player.
2937 */
2938 will_kill_again = 0;
2939 for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp; tmp = tmp->above)
2940 if (tmp->type == SPELL_EFFECT)
2941 will_kill_again |= tmp->attacktype;
2942
2943 if (will_kill_again)
2944 {
2945 object *force;
2946 int at;
2947
2948 force = get_archetype (FORCE_NAME);
2949 /* 50 ticks should be enough time for the spell to abate */
2950 force->speed = 0.1;
2951 force->speed_left = -5.0;
2952 SET_FLAG (force, FLAG_APPLIED);
2953 for (at = 0; at < NROFATTACKS; at++)
2954 if (will_kill_again & (1 << at))
2955 force->resist[at] = 100;
2956
2957 insert_ob_in_ob (force, op);
2958 op->update_stats ();
2959
2960 }
2961
2962 new_draw_info (NDI_UNIQUE, 0, op, "YOU HAVE DIED.");
2963 }
2964
2965 void
2966 loot_object (object *op)
2967 { /* Grab and destroy some treasure */
2968 object *tmp, *tmp2, *next;
2969
2970 op->close_container (); /* close open sack first */
2971
2972 for (tmp = op->inv; tmp; tmp = next)
2973 {
2974 next = tmp->below;
2975
2976 if (tmp->invisible)
2977 continue;
2978
2979 tmp->remove ();
2980 tmp->x = op->x, tmp->y = op->y;
2981
2982 if (tmp->type == CONTAINER)
2983 loot_object (tmp); /* empty container to ground */
2984
2985 if (!QUERY_FLAG (tmp, FLAG_UNIQUE) && (QUERY_FLAG (tmp, FLAG_STARTEQUIP) || QUERY_FLAG (tmp, FLAG_NO_DROP) || !(rndm (3))))
2986 {
2987 if (tmp->nrof > 1)
2988 {
2989 tmp2 = get_split_ob (tmp, 1 + RANDOM () % (tmp->nrof - 1));
2990 tmp2->destroy ();
2991 insert_ob_in_map (tmp, op->map, NULL, 0);
2992 }
2993 else
2994 tmp->destroy ();
2995 }
2996 else
2997 insert_ob_in_map (tmp, op->map, NULL, 0);
2998 }
2999 }
3000
3001 /*
3002 * fix_weight(): Check recursively the weight of all players, and fix
3003 * what needs to be fixed. Refresh windows and fix speed if anything
3004 * was changed.
3005 */
3006 void
3007 fix_weight (void)
3008 {
3009 for_all_players (pl)
3010 {
3011 int old = pl->ob->carrying, sum = sum_weight (pl->ob);
3012
3013 if (old == sum)
3014 continue;
3015 pl->ob->update_stats ();
3016 LOG (llevDebug, "Fixed inventory in %s (%d -> %d)\n", &pl->ob->name, old, sum);
3017 }
3018 }
3019
3020 void
3021 fix_luck (void)
3022 {
3023 for_all_players (pl)
3024 if (!pl->ob->contr->ns->state)
3025 pl->ob->change_luck (0);
3026 }
3027
3028 /* cast_dust() - handles op throwing objects of type 'DUST'.
3029 * This is much simpler in the new spell code - we basically
3030 * just treat this as any other spell casting object.
3031 */
3032 void
3033 cast_dust (object *op, object *throw_ob, int dir)
3034 {
3035 object *skop, *spob;
3036
3037 skop = find_skill_by_name (op, throw_ob->skill);
3038
3039 /* casting POTION 'dusts' is really a use_magic_item skill */
3040 if (op->type == PLAYER && throw_ob->type == POTION && !skop)
3041 {
3042 LOG (llevError, "Player %s lacks critical skill use_magic_item!\n", &op->name);
3043 return;
3044 }
3045
3046 spob = throw_ob->inv;
3047
3048 // elmex Tue Aug 15 17:19:46 CEST 2006: Added this check to
3049 // not pass NULL to cast_spell (which did indeed check itself, but
3050 // errors should be reported as early as possible IMHO)
3051 if (!spob)
3052 {
3053 LOG (llevError, "cast_dust: thrown object %s (by %s) had no spell in it!", &throw_ob->name, &op->name);
3054 return;
3055 }
3056
3057 if (op->type == PLAYER)
3058 new_draw_info_format (NDI_UNIQUE, 0, op, "You cast %s.", &spob->name);
3059
3060 cast_spell (op, throw_ob, dir, spob, NULL);
3061
3062 throw_ob->destroy ();
3063 }
3064
3065 void
3066 make_visible (object *op)
3067 {
3068 op->hide = 0;
3069 op->invisible = 0;
3070 if (op->type == PLAYER)
3071 {
3072 op->contr->tmp_invis = 0;
3073 op->contr->invis_race = 0;
3074 }
3075
3076 update_object (op, UP_OBJ_CHANGE);
3077 }
3078
3079 int
3080 is_true_undead (object *op)
3081 {
3082 if (QUERY_FLAG (&op->arch->clone, FLAG_UNDEAD))
3083 return 1;
3084
3085 return 0;
3086 }
3087
3088 /* look at the surrounding terrain to determine
3089 * the hideability of this object. Positive levels
3090 * indicate greater hideability.
3091 */
3092
3093 int
3094 hideability (object *ob)
3095 {
3096 int i, level = 0, mflag;
3097 sint16 x, y;
3098
3099 if (!ob || !ob->map)
3100 return 0;
3101
3102 /* so, on normal lighted maps, its hard to hide */
3103 level = ob->map->darkness - 2;
3104
3105 /* this also picks up whether the object is glowing.
3106 * If you carry a light on a non-dark map, its not
3107 * as bad as carrying a light on a pitch dark map */
3108 if (has_carried_lights (ob))
3109 level = -(10 + (2 * ob->map->darkness));
3110
3111 /* scan through all nearby squares for terrain to hide in */
3112 for (i = 0, x = ob->x, y = ob->y; i < 9; i++, x = ob->x + freearr_x[i], y = ob->y + freearr_y[i])
3113 {
3114 mflag = get_map_flags (ob->map, NULL, x, y, NULL, NULL);
3115 if (mflag & P_OUT_OF_MAP)
3116 {
3117 continue;
3118 }
3119 if (mflag & P_BLOCKSVIEW) /* something to hide near! */
3120 level += 2;
3121 else /* open terrain! */
3122 level -= 1;
3123 }
3124
3125 #if 0
3126 LOG (llevDebug, "hideability of %s is %d\n", ob->name, level);
3127 #endif
3128 return level;
3129 }
3130
3131 /* For Hidden creatures - a chance of becoming 'unhidden'
3132 * every time they move - as we subtract off 'invisibility'
3133 * AND, for players, if they move into a ridiculously unhideable
3134 * spot (surrounded by clear terrain in broad daylight). -b.t.
3135 */
3136
3137 void
3138 do_hidden_move (object *op)
3139 {
3140 int hide = 0, num = random_roll (0, 19, op, PREFER_LOW);
3141 object *skop;
3142
3143 if (!op || !op->map)
3144 return;
3145
3146 skop = find_obj_by_type_subtype (op, SKILL, SK_HIDING);
3147
3148 /* its *extremely* hard to run and sneak/hide at the same time! */
3149 if (op->type == PLAYER && op->contr->run_on)
3150 if (!skop || num >= skop->level)
3151 {
3152 new_draw_info (NDI_UNIQUE, 0, op, "You ran too much! You are no longer hidden!");
3153 make_visible (op);
3154 return;
3155 }
3156 else
3157 num += 20;
3158
3159 num += op->map->difficulty;
3160 hide = hideability (op); /* modify by terrain hidden level */
3161 num -= hide;
3162
3163 if ((op->type == PLAYER && hide < -10) || ((op->invisible -= num) <= 0))
3164 {
3165 make_visible (op);
3166 if (op->type == PLAYER)
3167 new_draw_info (NDI_UNIQUE, 0, op, "You moved out of hiding! You are visible!");
3168 }
3169 else if (op->type == PLAYER && skop)
3170 change_exp (op, calc_skill_exp (op, NULL, skop), skop->skill, 0);
3171 }
3172
3173 /* determine if who is standing near a hostile creature. */
3174
3175 int
3176 stand_near_hostile (object *who)
3177 {
3178 object *tmp = NULL;
3179 int i, friendly = 0, player = 0, mflags;
3180 maptile *m;
3181 sint16 x, y;
3182
3183 if (!who)
3184 return 0;
3185
3186 if (who->type == PLAYER)
3187 player = 1;
3188
3189 else
3190 friendly = QUERY_FLAG (who, FLAG_FRIENDLY);
3191
3192 /* search adjacent squares */
3193 for (i = 1; i < 9; i++)
3194 {
3195 x = who->x + freearr_x[i];
3196 y = who->y + freearr_y[i];
3197 m = who->map;
3198 mflags = get_map_flags (m, &m, x, y, &x, &y);
3199 /* space must be blocked if there is a monster. If not
3200 * blocked, don't need to check this space.
3201 */
3202 if (mflags & P_OUT_OF_MAP)
3203 continue;
3204 if (OB_TYPE_MOVE_BLOCK (who, GET_MAP_MOVE_BLOCK (m, x, y)))
3205 continue;
3206
3207 for (tmp = GET_MAP_OB (m, x, y); tmp; tmp = tmp->above)
3208 {
3209 if ((player ||friendly) &&QUERY_FLAG (tmp, FLAG_MONSTER) && !QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE))
3210 return 1;
3211 else if (tmp->type == PLAYER)
3212 {
3213 /*don't let a hidden DM prevent you from hiding */
3214 if (!QUERY_FLAG (tmp, FLAG_WIZ) || tmp->contr->hidden == 0)
3215 return 1;
3216 }
3217 }
3218 }
3219 return 0;
3220 }
3221
3222 /* check the player los field for viewability of the
3223 * object op. This function works fine for monsters,
3224 * but we dont worry if the object isnt the top one in
3225 * a pile (say a coin under a table would return "viewable"
3226 * by this routine). Another question, should we be
3227 * concerned with the direction the player is looking
3228 * in? Realistically, most of use cant see stuff behind
3229 * our backs...on the other hand, does the "facing" direction
3230 * imply the way your head, or body is facing? Its possible
3231 * for them to differ. Sigh, this fctn could get a bit more complex.
3232 * -b.t.
3233 * This function is now map tiling safe.
3234 */
3235
3236 int
3237 player_can_view (object *pl, object *op)
3238 {
3239 rv_vector rv;
3240 int dx, dy;
3241
3242 if (pl->type != PLAYER)
3243 {
3244 LOG (llevError, "player_can_view() called for non-player object\n");
3245 return -1;
3246 }
3247
3248 if (!pl || !op)
3249 return 0;
3250
3251 op = op->head_ ();
3252
3253 get_rangevector (pl, op, &rv, 0x1);
3254
3255 /* starting with the 'head' part, lets loop
3256 * through the object and find if it has any
3257 * part that is in the los array but isnt on
3258 * a blocked los square.
3259 * we use the archetype to figure out offsets.
3260 */
3261 while (op)
3262 {
3263 dx = rv.distance_x + op->arch->clone.x;
3264 dy = rv.distance_y + op->arch->clone.y;
3265
3266 /* only the viewable area the player sees is updated by LOS
3267 * code, so we need to restrict ourselves to that range of values
3268 * for any meaningful values.
3269 */
3270 if (FABS (dx) <= (pl->contr->ns->mapx / 2) &&
3271 FABS (dy) <= (pl->contr->ns->mapy / 2) &&
3272 !pl->contr->blocked_los[dx + (pl->contr->ns->mapx / 2)][dy + (pl->contr->ns->mapy / 2)])
3273 return 1;
3274 op = op->more;
3275 }
3276 return 0;
3277 }
3278
3279 /* routine for both players and monsters. We call this when
3280 * there is a possibility for our action distrubing our hiding
3281 * place or invisiblity spell. Artefact invisiblity is not
3282 * effected by this. If we arent invisible to begin with, we
3283 * return 0.
3284 */
3285 int
3286 action_makes_visible (object *op)
3287 {
3288
3289 if (op->invisible && QUERY_FLAG (op, FLAG_ALIVE))
3290 {
3291 if (QUERY_FLAG (op, FLAG_MAKE_INVIS))
3292 return 0;
3293
3294 if (op->contr && op->contr->tmp_invis == 0)
3295 return 0;
3296
3297 /* If monsters, they should become visible */
3298 if (op->hide || !op->contr || (op->contr && op->contr->tmp_invis))
3299 {
3300 new_draw_info_format (NDI_UNIQUE, 0, op, "You become %s!", op->hide ? "unhidden" : "visible");
3301 return 1;
3302 }
3303 }
3304 return 0;
3305 }
3306
3307 /* op_on_battleground - checks if the given object op (usually
3308 * a player) is standing on a valid battleground-tile,
3309 * function returns TRUE/FALSE. If true x, y returns the battleground
3310 * -exit-coord. (and if x, y not NULL)
3311 * 19 March 2005 - josh@woosworld.net modifed to check if the battleground also has slaying, maxhp, and maxsp set
3312 * and if those are all set and the player has a marker that matches the slaying send them to a different x, y
3313 * Default is to do the same as before, so only people wanting to have different points need worry about this
3314 */
3315 int
3316 op_on_battleground (object *op, int *x, int *y)
3317 {
3318 object *tmp;
3319
3320 /* A battleground-tile needs the following attributes to be valid:
3321 * is_floor 1 (has to be the FIRST floor beneath the player's feet),
3322 * name="battleground", no_pick 1, type=58 (type BATTLEGROUND)
3323 * and the exit-coordinates sp/hp must both be > 0.
3324 * => The intention here is to prevent abuse of the battleground-
3325 * feature (like pickable or hidden battleground tiles). */
3326 for (tmp = op->below; tmp != NULL; tmp = tmp->below)
3327 {
3328 if (QUERY_FLAG (tmp, FLAG_IS_FLOOR))
3329 {
3330 if (QUERY_FLAG (tmp, FLAG_NO_PICK) &&
3331 strcmp (tmp->name, "battleground") == 0 && tmp->type == BATTLEGROUND && EXIT_X (tmp) && EXIT_Y (tmp))
3332 {
3333 /*before we assign the exit, check if this is a teambattle */
3334 if (EXIT_ALT_X (tmp) && EXIT_ALT_Y (tmp) && EXIT_PATH (tmp))
3335 {
3336 object *invtmp;
3337
3338 for (invtmp = op->inv; invtmp != NULL; invtmp = invtmp->below)
3339 {
3340 if (invtmp->type == FORCE && invtmp->slaying && !strcmp (EXIT_PATH (tmp), invtmp->slaying))
3341 {
3342 if (x != NULL && y != NULL)
3343 *x = EXIT_ALT_X (tmp), *y = EXIT_ALT_Y (tmp);
3344 return 1;
3345 }
3346 }
3347 }
3348 if (x != NULL && y != NULL)
3349 *x = EXIT_X (tmp), *y = EXIT_Y (tmp);
3350 return 1;
3351 }
3352 }
3353 }
3354 /* If we got here, did not find a battleground */
3355 return 0;
3356 }
3357
3358 /*
3359 * When a dragon-player gains a new stage of evolution,
3360 * he gets some treasure
3361 *
3362 * attributes:
3363 * object *who the dragon player
3364 * int atnr the attack-number of the ability focus
3365 * int level ability level
3366 */
3367 void
3368 dragon_ability_gain (object *who, int atnr, int level)
3369 {
3370 treasurelist *trlist = NULL; /* treasurelist */
3371 treasure *tr; /* treasure */
3372 object *tmp, *skop; /* tmp. object */
3373 object *item; /* treasure object */
3374 char buf[MAX_BUF]; /* tmp. string buffer */
3375 int i = 0, j = 0;
3376
3377 /* get the appropriate treasurelist */
3378 if (atnr == ATNR_FIRE)
3379 trlist = find_treasurelist ("dragon_ability_fire");
3380 else if (atnr == ATNR_COLD)
3381 trlist = find_treasurelist ("dragon_ability_cold");
3382 else if (atnr == ATNR_ELECTRICITY)
3383 trlist = find_treasurelist ("dragon_ability_elec");
3384 else if (atnr == ATNR_POISON)
3385 trlist = find_treasurelist ("dragon_ability_poison");
3386
3387 if (trlist == NULL || who->type != PLAYER)
3388 return;
3389
3390 for (i = 0, tr = trlist->items; tr != NULL && i < level - 1; tr = tr->next, i++);
3391
3392 if (!tr || !tr->item)
3393 {
3394 /* LOG(llevDebug, "-> no more treasure for %s\n", change_resist_msg[atnr]); */
3395 return;
3396 }
3397
3398 /* everything seems okay - now bring on the gift: */
3399 item = &(tr->item->clone);
3400
3401 if (item->type == SPELL)
3402 {
3403 if (check_spell_known (who, item->name))
3404 return;
3405
3406 new_draw_info_format (NDI_UNIQUE | NDI_BLUE, 0, who, "You gained the ability of %s", &item->name);
3407 do_learn_spell (who, item, 0);
3408 return;
3409 }
3410
3411 /* grant direct spell */
3412 if (item->type == SPELLBOOK)
3413 {
3414 if (!item->inv)
3415 {
3416 LOG (llevDebug, "dragon_ability_gain: Broken spellbook %s\n", &item->name);
3417 return;
3418 }
3419 if (check_spell_known (who, item->inv->name))
3420 return;
3421 if (item->invisible)
3422 {
3423 new_draw_info_format (NDI_UNIQUE | NDI_BLUE, 0, who, "You gained the ability of %s", &item->inv->name);
3424 do_learn_spell (who, item->inv, 0);
3425 return;
3426 }
3427 }
3428 else if (item->type == SKILL_TOOL && item->invisible)
3429 {
3430 if (item->subtype == SK_CLAWING && (skop = find_skill_by_name (who, item->skill)) != NULL)
3431 {
3432
3433 /* should this perhaps be (skop->attackyp & item->attacktype)!=item->attacktype ...
3434 * in this way, if the player is missing any of the attacktypes, he gets
3435 * them. As it is now, if the player has any that match the granted skill,
3436 * but not all of them, he gets nothing.
3437 */
3438 if (!(skop->attacktype & item->attacktype))
3439 {
3440 /* Give new attacktype */
3441 skop->attacktype |= item->attacktype;
3442
3443 /* always add physical if there's none */
3444 skop->attacktype |= AT_PHYSICAL;
3445
3446 if (item->msg != NULL)
3447 new_draw_info (NDI_UNIQUE | NDI_BLUE, 0, who, item->msg);
3448
3449 /* Give player new face */
3450 if (item->animation_id)
3451 {
3452 who->face = skop->face;
3453 who->animation_id = item->animation_id;
3454 who->anim_speed = item->anim_speed;
3455 who->last_anim = 0;
3456 who->state = 0;
3457 animate_object (who, who->direction);
3458 }
3459 }
3460 }
3461 }
3462 else if (item->type == FORCE)
3463 {
3464 /* forces in the treasurelist can alter the player's stats */
3465 object *skin;
3466
3467 /* first get the dragon skin force */
3468 shstr_cmp dragon_skin_force ("dragon_skin_force");
3469 for (skin = who->inv; skin && !(skin->arch->name == dragon_skin_force); skin = skin->below)
3470 ;
3471
3472 if (!skin)
3473 return;
3474
3475 /* adding new spellpath attunements */
3476 if (item->path_attuned > 0 && !(skin->path_attuned & item->path_attuned))
3477 {
3478 skin->path_attuned |= item->path_attuned; /* add attunement to skin */
3479
3480 /* print message */
3481 sprintf (buf, "You feel attuned to ");
3482 for (i = 0, j = 0; i < NRSPELLPATHS; i++)
3483 {
3484 if (item->path_attuned & (1 << i))
3485 {
3486 if (j)
3487 strcat (buf, " and ");
3488 else
3489 j = 1;
3490 strcat (buf, spellpathnames[i]);
3491 }
3492 }
3493 strcat (buf, ".");
3494 new_draw_info (NDI_UNIQUE | NDI_BLUE, 0, who, buf);
3495 }
3496
3497 /* evtl. adding flags: */
3498 if (QUERY_FLAG (item, FLAG_XRAYS))
3499 SET_FLAG (skin, FLAG_XRAYS);
3500 if (QUERY_FLAG (item, FLAG_STEALTH))
3501 SET_FLAG (skin, FLAG_STEALTH);
3502 if (QUERY_FLAG (item, FLAG_SEE_IN_DARK))
3503 SET_FLAG (skin, FLAG_SEE_IN_DARK);
3504
3505 /* print message if there is one */
3506 if (item->msg != NULL)
3507 new_draw_info (NDI_UNIQUE | NDI_BLUE, 0, who, item->msg);
3508 }
3509 else
3510 {
3511 /* generate misc. treasure */
3512 tmp = arch_to_object (tr->item);
3513 new_draw_info_format (NDI_UNIQUE | NDI_BLUE, 0, who, "You gained %s", query_short_name (tmp));
3514 tmp = insert_ob_in_ob (tmp, who);
3515 if (who->type == PLAYER)
3516 esrv_send_item (who, tmp);
3517 }
3518 }
3519
3520 /**
3521 * Unready an object for a player. This function does nothing if the object was
3522 * not readied.
3523 */
3524 void
3525 player_unready_range_ob (player *pl, object *ob)
3526 {
3527 for (rangetype i = (rangetype) 0; i < range_size; i = (rangetype) ((int) i + 1))
3528 if (pl->ranges[i] == ob)
3529 {
3530 pl->ranges[i] = 0;
3531 if (pl->shoottype == i)
3532 pl->shoottype = range_none;
3533 }
3534 }
3535
3536 sint8
3537 player::visibility_at (maptile *map, int x, int y) const
3538 {
3539 if (!ns)
3540 return 0;
3541
3542 int dx, dy;
3543 if (!adjacent_map (map, ns->current_map, &dx, &dy))
3544 return 0;
3545
3546 x += dx - ns->current_x + ns->mapx / 2;
3547 y += dy - ns->current_y + ns->mapy / 2;
3548
3549 if (!IN_RANGE_EXC (x, 0, ns->mapx) || !IN_RANGE_EXC (y, 0, ns->mapy))
3550 return 0;
3551
3552 return 100 - blocked_los [x][y];
3553 }