Atrinik Server  4.0
player.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
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 at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <loader.h>
32 #include <toolkit/string.h>
33 #include <plugin.h>
34 #include <monster_data.h>
35 #include <arch.h>
36 #include <ban.h>
37 #include <player.h>
38 #include <object.h>
39 #include <exp.h>
40 #include <object_methods.h>
41 #include <disease.h>
42 #include <container.h>
43 #include <server.h>
44 #include <toolkit/path.h>
45 
46 static int save_life(object *op);
47 static void remove_unpaid_objects(object *op, object *env);
48 
52 mempool_struct *pool_player;
53 
57 void
59 {
60  pool_player = mempool_create("players", 25, sizeof(player),
61  MEMPOOL_ALLOW_FREEING, NULL, NULL, NULL, NULL);
62 }
63 
67 void
69 {
70  while (first_player) {
72  }
73 }
74 
78 void
80 {
81  while (first_player) {
82  first_player->cs->state = ST_DEAD;
84  }
85 }
86 
95 player *
96 find_player (const char *plname)
97 {
98  for (player *pl = first_player; pl != NULL; pl = pl->next) {
99  if (strcasecmp(pl->ob->name, plname) == 0) {
100  return pl;
101  }
102  }
103 
104  return NULL;
105 }
106 
115 player *
116 find_player_sh (shstr *plname)
117 {
118  for (player *pl = first_player; pl != NULL; pl = pl->next) {
119  if (pl->ob->name == plname) {
120  return pl;
121  }
122  }
123 
124  return NULL;
125 }
126 
135 void
136 display_motd (object *op)
137 {
138  char buf[MAX_BUF];
139  FILE *fp;
140 
141  snprintf(buf, sizeof(buf), "%s/motd_custom", settings.datapath);
142 
143  if ((fp = fopen(buf, "r")) == NULL) {
144  snprintf(buf, sizeof(buf), "%s/motd", settings.datapath);
145 
146  if ((fp = fopen(buf, "r")) == NULL) {
147  return;
148  }
149  }
150 
151  while (fgets(buf, MAX_BUF, fp) != NULL) {
152  char *cp;
153 
154  if (buf[0] == '#' || buf[0] == '\n') {
155  continue;
156  }
157 
158  cp = strchr(buf, '\n');
159 
160  if (cp != NULL) {
161  *cp = '\0';
162  }
163 
164  draw_info(COLOR_WHITE, op, buf);
165  }
166 
167  fclose(fp);
168  draw_info(COLOR_WHITE, op, " ");
169 }
170 
179 static player *
181 {
182  if (!p) {
183  p = mempool_get(pool_player);
184 
185  if (!last_player) {
187  } else {
188  last_player->next = p;
189  p->prev = last_player;
190  last_player = p;
191  }
192  } else {
193  /* Clears basically the entire player structure except
194  * for next and socket. */
195  memset((char *) p + offsetof(player, maplevel), 0, sizeof(player) - offsetof(player, maplevel));
196  }
197 
198 #ifdef AUTOSAVE
199  p->last_save_tick = 9999999;
200 #endif
201 
202  p->target_hp = -1;
203  p->gen_sp_armour = 0;
204  p->last_speed = -1;
205  p->update_los = 1;
206 
207  p->last_stats.exp = -1;
208 
209  return p;
210 }
211 
218 void
220 {
221  /* If this player is in a party, leave the party */
222  if (pl->party) {
223  command_party(pl->ob, "party", "leave");
224  }
225 
226  pl->cs->state = ST_DEAD;
227 
228  /* Free command permissions. */
229  if (pl->cmd_permissions != NULL) {
230  int i;
231 
232  for (i = 0; i < pl->num_cmd_permissions; i++) {
233  if (pl->cmd_permissions[i] != NULL) {
234  efree(pl->cmd_permissions[i]);
235  }
236  }
237 
238  efree(pl->cmd_permissions);
239  }
240 
241  if (pl->followed_player != NULL) {
243  }
244 
245  if (pl->killer != NULL) {
246  efree(pl->killer);
247  }
248 
249  player_faction_t *faction, *tmp;
250 
251  HASH_ITER(hh, pl->factions, faction, tmp) {
252  player_faction_free(pl, faction);
253  }
254 
255  player_path_clear(pl);
256 
257  /* Now remove from list of players. */
258  if (pl->prev) {
259  pl->prev->next = pl->next;
260  } else {
261  first_player = pl->next;
262  }
263 
264  if (pl->next) {
265  pl->next->prev = pl->prev;
266  } else {
267  last_player = pl->prev;
268  }
269 
270  if (pl->ob) {
272 
273  if (!QUERY_FLAG(pl->ob, FLAG_REMOVED)) {
274  object_remove(pl->ob, 0);
275  }
276 
277  object_destroy(pl->ob);
278  }
279 
280  free_newsocket(pl->cs);
281 }
282 
291 void
293 {
294  object *op, *next = NULL;
295 
296  if (pl->randomitems) {
297  treasure_generate(items, pl, 1, GT_ONLY_GOOD | GT_NO_VALUE);
298  }
299 
300  for (op = pl->inv; op; op = next) {
301  next = op->below;
302 
303  /* Forces get applied by default */
304  if (op->type == FORCE) {
305  SET_FLAG(op, FLAG_APPLIED);
306  }
307 
308  /* We never give weapons/armour if they cannot be used by this
309  * player due to race restrictions */
310  if (pl->type == PLAYER) {
311  if ((!QUERY_FLAG(pl, FLAG_USE_ARMOUR) && IS_ARMOR(op)) ||
312  (!QUERY_FLAG(pl, FLAG_USE_WEAPON) && op->type == WEAPON)) {
313  object_remove(op, 0);
314  object_destroy(op);
315  continue;
316  }
317  }
318 
319  /* Give starting characters identified, uncursed, and undamned
320  * items. */
321  if (need_identify(op)) {
323  CLEAR_FLAG(op, FLAG_CURSED);
324  CLEAR_FLAG(op, FLAG_DAMNED);
325  }
326 
327  /* Apply initial armor */
328  if (IS_ARMOR(op)) {
329  manual_apply(pl, op, 0);
330  }
331  }
332 }
333 
346 int
348 {
349  if (!pl->ob || !OBJECT_ACTIVE(pl->ob)) {
350  return -1;
351  }
352 
354 
355  if (!pl->ob || !OBJECT_ACTIVE(pl->ob) || pl->cs->state == ST_DEAD) {
356  return -1;
357  }
358 
359  /* Check speed. */
360  if (pl->ob->speed_left < 0.0f) {
361  return 0;
362  }
363 
364  /* If we are here, we're never paralyzed anymore. */
366 
367  if (CONTR(pl->ob)->run_on) {
368  /* All move commands take 1 tick, at least for now. */
369  pl->ob->speed_left--;
370  move_object(pl->ob, pl->run_on_dir + 1);
371 
372  if (pl->ob->speed_left > 0) {
373  return 1;
374  } else {
375  return 0;
376  }
377  }
378 
379  return 0;
380 }
381 
390 static int
391 save_life (object *op)
392 {
393  object *tmp;
394 
395  if (!QUERY_FLAG(op, FLAG_LIFESAVE)) {
396  return 0;
397  }
398 
399  for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
400  if (QUERY_FLAG(tmp, FLAG_APPLIED) && QUERY_FLAG(tmp, FLAG_LIFESAVE)) {
401  play_sound_map(op->map, CMD_SOUND_EFFECT, "explosion.ogg", op->x, op->y, 0, 0);
402  char *name = object_get_name_s(tmp, op);
403  draw_info_format(COLOR_WHITE, op, "Your %s vibrates violently, "
404  "then evaporates.", name);
405  efree(name);
406 
407  object_remove(tmp, 0);
408  object_destroy(tmp);
410 
411  if (op->stats.hp < 0) {
412  op->stats.hp = op->stats.maxhp;
413  }
414 
415  if (op->stats.food < 0) {
416  op->stats.food = 999;
417  }
418 
419  /* Bring him home. */
420  object_enter_map(op,
421  NULL,
422  ready_map_name(CONTR(op)->savebed_map, NULL, 0),
423  CONTR(op)->bed_x,
424  CONTR(op)->bed_y,
425  true);
426  return 1;
427  }
428  }
429 
430  LOG(BUG, "LIFESAVE set without applied object.");
432  /* Bring him home. */
433  object_enter_map(op,
434  NULL,
435  ready_map_name(CONTR(op)->savebed_map, NULL, 0),
436  CONTR(op)->bed_x,
437  CONTR(op)->bed_y,
438  true);
439  return 0;
440 }
441 
451 static void
452 remove_unpaid_objects (object *op, object *env)
453 {
454  object *next;
455 
456  while (op) {
457  /* Make sure we have a good value, in case
458  * we remove object 'op' */
459  next = op->below;
460 
461  if (QUERY_FLAG(op, FLAG_UNPAID)) {
462  object_remove(op, 0);
463  op->x = env->x;
464  op->y = env->y;
465  object_insert_map(op, env->map, NULL, 0);
466  } else if (op->inv) {
467  remove_unpaid_objects(op->inv, env);
468  }
469 
470  op = next;
471  }
472 }
473 
485 static inline int
486 get_regen_amount (uint16_t regen, uint16_t *regen_remainder)
487 {
488  int ret = 0;
489  double division;
490 
491  /* Check whether it's time to update the remainder variable (which
492  * will distribute the remainder evenly over time). */
493  if (pticks % 8 == 0) {
494  *regen_remainder += regen;
495  }
496 
497  /* First check if we can distribute it evenly, if not, try to remove
498  * leftovers, if any. */
499  for (division = MAX_TICKS; ; division = 1.0) {
500  if (*regen_remainder / 10.0 / division >= 1.0) {
501  int add = (int) *regen_remainder / 10.0 / division;
502 
503  ret += add;
504  *regen_remainder -= add * 10;
505  break;
506  }
507 
508  if (DBL_EQUAL(division, 1.0)) {
509  break;
510  }
511  }
512 
513  return ret;
514 }
515 
526 static inline uint16_t
527 get_regen_value (double speed, double rate)
528 {
529  double value = MAX_TICKS;
530  value /= rate / (MAX(speed, 20.0) + 10.0);
531  value *= 10.0;
532  return value;
533 }
534 
544 static void
546 {
547  HARD_ASSERT(op != NULL);
548  SOFT_ASSERT(op->type == PLAYER, "Not a player object: %s",
549  object_get_str(op));
550 
551  player *pl = CONTR(op);
552 
553  double gen_hp = (pl->gen_hp * (PLAYER_REGEN_HP_RATE / 20.0)) +
554  (op->stats.maxhp / 4.0);
555 
556  double gen_sp = (pl->gen_sp * (PLAYER_REGEN_SP_RATE / 20.0)) +
557  op->stats.maxsp;
558  gen_sp = gen_sp * 10 / MAX(pl->gen_sp_armour, 10);
559 
560  /* Update client's regen rates. */
561  pl->gen_client_hp = get_regen_value(gen_hp, PLAYER_REGEN_HP_RATE);
562  pl->gen_client_sp = get_regen_value(gen_sp, PLAYER_REGEN_SP_RATE);
563 
564  if (pl->skill_ptr[SK_MEDITATION] != NULL) {
565  double modifier = (pticks - pl->last_combat) / MAX_TICKS;
566  modifier /= PLAYER_REGEN_MODIFIER;
567  modifier += 1.0;
568  modifier = MIN(PLAYER_REGEN_MODIFIER_MAX, modifier);
569  gen_hp *= modifier;
570  gen_sp *= modifier;
571  }
572 
573  uint16_t gen_real_hp = get_regen_value(gen_hp, PLAYER_REGEN_HP_RATE);
574  uint16_t gen_real_sp = get_regen_value(gen_sp, PLAYER_REGEN_SP_RATE);
575 
576  int16_t last_food = op->stats.food;
577 
578  /* Regenerate hit points. */
579  if (op->stats.hp < op->stats.maxhp && op->stats.food) {
580  int add = get_regen_amount(gen_real_hp, &pl->gen_hp_remainder);
581  if (add != 0) {
582  op->stats.hp += add;
583  pl->stat_hp_regen += add;
584 
585  if (op->stats.hp > op->stats.maxhp) {
586  op->stats.hp = op->stats.maxhp;
587  }
588 
589  if (!pl->tgm) {
590  op->stats.food--;
591 
592  if (pl->digestion < 0) {
593  op->stats.food += pl->digestion;
594  } else if (pl->digestion > 0 && rndm(0, pl->digestion)) {
595  op->stats.food = last_food;
596  }
597  }
598  }
599  } else {
600  pl->gen_hp_remainder = 0;
601  }
602 
603  /* Regenerate mana. */
604  if (op->stats.sp < op->stats.maxsp && op->stats.food) {
605  int add = get_regen_amount(gen_real_sp, &pl->gen_sp_remainder);
606  if (add != 0) {
607  op->stats.sp += add;
608  pl->stat_sp_regen += add;
609 
610  if (op->stats.sp > op->stats.maxsp) {
611  op->stats.sp = op->stats.maxsp;
612  }
613 
614  if (!pl->tgm) {
615  op->stats.food--;
616 
617  if (pl->digestion < 0) {
618  op->stats.food += pl->digestion;
619  } else if (pl->digestion > 0 && rndm(0, pl->digestion)) {
620  op->stats.food = last_food;
621  }
622  }
623  }
624  } else {
625  pl->gen_sp_remainder = 0;
626  }
627 
628  /* Digestion */
629  if (--op->last_eat < 0) {
630  int bonus = MAX(pl->digestion, 0);
631  int penalty = MAX(-pl->digestion, 0);
632 
633  if (pl->gen_hp > 0) {
634  op->last_eat = 25 * (1 + bonus) / (pl->gen_hp + penalty + 1);
635  } else {
636  op->last_eat = 25 * (1 + bonus) / (penalty + 1);
637  }
638 
639  if (!pl->tgm) {
640  op->stats.food--;
641  }
642  }
643 
644  if (op->stats.food < 0 && op->stats.hp >= 0) {
645  object *flesh = NULL;
646 
647  FOR_INV_PREPARE(op, tmp) {
648  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
649  continue;
650  }
651 
652  if (tmp->type == FOOD || tmp->type == DRINK) {
653  draw_info(COLOR_WHITE, op,
654  "You blindly grab for a bite of food.");
655  manual_apply(op, tmp, 0);
656 
657  if (op->stats.food >= 0 || op->stats.hp < 0) {
658  break;
659  }
660  } else if (tmp->type == FLESH && flesh == NULL) {
661  flesh = tmp;
662  }
663  } FOR_INV_FINISH();
664 
665  /* If player is still starving, it means they don't have any food, so
666  * eat flesh instead. */
667  if (op->stats.food < 0 && op->stats.hp >= 0 && flesh != NULL) {
668  draw_info(COLOR_WHITE, op, "You blindly grab for a bite of food.");
669  manual_apply(op, flesh, 0);
670  }
671  }
672 
673  /* Lose hitpoints for lack of food. */
674  while (op->stats.food < 0 && op->stats.hp > 0) {
675  op->stats.food++;
676  op->stats.hp--;
677  }
678 
679  if ((op->stats.hp <= 0 || op->stats.food < 0) && !pl->tgm) {
680  draw_info_format(COLOR_WHITE, NULL, "%s starved to death.", op->name);
681  player_set_killer(pl, "starvation");
682  kill_player(op);
683  }
684 }
685 
692 static void
694 {
695  HARD_ASSERT(op != NULL);
696 
697  int num_lose = 1 + op->level / BALSL_NUMBER_LOSSES_RATIO;
698  bool lost_stat = false;
699 
700  /* Protect low level players for a little while. */
701  if (op->level <= 3) {
702  num_lose = 0;
703  }
704 
705  object *depletion = NULL;
706  for (int i = 0; i < num_lose; i++) {
707  static archetype_t *at = NULL;
708  if (at == NULL) {
709  at = arch_find("depletion");
710  SOFT_ASSERT(at != NULL, "Could not find depletion archetype");
711  }
712 
713  if (depletion == NULL) {
714  depletion = object_find_arch(op, at);
715  if (depletion == NULL) {
716  depletion = arch_to_object(at);
717  depletion = object_insert_into(depletion, op, 0);
718  SOFT_ASSERT(depletion != NULL, "Could not insert depletion");
719  }
720  }
721 
722  int stat = rndm(0, NUM_STATS - 1);
723  bool lose = true;
724 
725  int8_t value = get_attr_value(&depletion->stats, stat);
726  if (value < 0) {
727  int loss_chance = 1 + op->level / BALSL_LOSS_CHANCE_RATIO;
728  int keep_chance = value * value;
729 
730  /* There is a maximum depletion total per level. */
731  if (value < -1 - op->level / BALSL_MAX_LOSS_RATIO) {
732  lose = false;
733  } else if (rndm(0, loss_chance + keep_chance - 1) < keep_chance) {
734  lose = false;
735  }
736  }
737 
738  if (!lose || value < -50) {
739  continue;
740  }
741 
742  change_attr_value(&depletion->stats, stat, -1);
743  SET_FLAG(depletion, FLAG_APPLIED);
744  lost_stat = true;
745  draw_info(COLOR_GRAY, op, lose_msg[stat]);
746  }
747 
748  if (lost_stat) {
750  } else {
751  draw_info(COLOR_WHITE, op,
752  "For a brief moment you feel a holy presence "
753  "protecting you.");
754  }
755 }
756 
764 void
765 kill_player (object *op)
766 {
767  char buf[HUGE_BUF];
768  object *tmp;
769 
770  if (pvp_area(NULL, op)) {
771  draw_info(COLOR_NAVY, op, "You have been defeated in combat!");
772  draw_info(COLOR_NAVY, op, "Local medics have saved your life...");
773 
774  /* Restore player */
775  cast_heal(op, op, MAXLEVEL, op, SP_CURE_POISON);
776  /* Remove any disease */
777  disease_cure(op, NULL);
778  op->stats.hp = op->stats.maxhp;
779  op->stats.sp = op->stats.maxsp;
780 
781  if (op->stats.food <= 0) {
782  op->stats.food = 999;
783  }
784 
785  /* Create a bodypart-trophy to make the winner happy */
786  tmp = arch_to_object(arch_find("finger"));
787  if (tmp != NULL) {
788  snprintf(buf, sizeof(buf), "%s's finger", op->name);
789  FREE_AND_COPY_HASH(tmp->name, buf);
790 
791  const char *killer = player_get_killer(CONTR(op));
792  if (killer == NULL) {
793  killer = "something nasty";
794  }
795 
796  StringBuffer *sb = stringbuffer_new();
797  stringbuffer_append_printf(sb,
798  "This finger has been cut off %s the "
799  "%s, when %s was defeated at level %d "
800  "by %s.",
801  op->name,
802  op->race,
804  op->level,
805  killer);
806  FREE_AND_COPY_HASH(tmp->msg, stringbuffer_finish_shared(sb));
807  tmp->value = 0;
808  tmp->material = 0;
809  tmp->type = 0;
810  tmp->x = op->x, tmp->y = op->y;
811  object_insert_map(tmp, op->map, op, 0);
812  }
813 
814  player_clear_killer(CONTR(op));
815 
816  /* Teleport defeated player to new destination */
817  transfer_ob(op, MAP_ENTER_X(op->map), MAP_ENTER_Y(op->map), 0, NULL, NULL);
818  return;
819  }
820 
821  if (save_life(op)) {
822  return;
823  }
824 
825  /* Trigger the DEATH event */
826  if (trigger_event(EVENT_DEATH, NULL, op, NULL, NULL, 0, 0, 0, 0) != 0) {
827  return;
828  }
829 
830  CONTR(op)->stat_deaths++;
831 
832  /* Trigger the global GDEATH event */
834 
835  play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "playerdead.ogg", 0, 0, 0, 0);
836 
837  /* Put a gravestone up where the character 'almost' died. */
838  tmp = arch_to_object(arch_find("gravestone"));
839  snprintf(VS(buf), "%s's gravestone", op->name);
840  FREE_AND_COPY_HASH(tmp->name, buf);
841  StringBuffer *sb = stringbuffer_new();
842  const char *killer = player_get_killer(CONTR(op));
843  if (killer == NULL) {
844  killer = "something nasty";
845  }
846  stringbuffer_append_printf(sb,
847  "R.I.P.\n\nHerein rests the hero %s the %s "
848  "who was killed at level %d by %s.",
849  op->name,
850  op->race,
851  op->level,
852  killer);
853  time_t now = time(NULL);
854  strftime(VS(buf), "\n\n%b %d %Y", localtime(&now));
855  stringbuffer_append_string(sb, buf);
856  tmp->msg = stringbuffer_finish_shared(sb);
857  tmp->x = op->x;
858  tmp->y = op->y;
859  object_insert_map(tmp, op->map, NULL, 0);
860 
862 
863  /* Remove any poisoning the character may be suffering. */
864  cast_heal(op, op, MAXLEVEL, op, SP_CURE_POISON);
865  /* Remove any disease */
866  disease_cure(op, NULL);
867 
868  /* If the player starved to death, give them some food back... */
869  if (op->stats.food <= 0) {
870  op->stats.food = 500;
871  }
872 
873  /* Reset HP/SP */
874  op->stats.hp = op->stats.maxhp;
875  op->stats.sp = op->stats.maxsp;
876 
877  hiscore_check(op, 1);
878 
879  /* Clear the killer, otherwise the highscore can get entries like
880  * 'xxx was killed by pudding on map Wilderness' even if they were
881  * killed in a dungeon. */
882  player_clear_killer(CONTR(op));
883 
884  /* Check to see if the player is in a shop. If so, then check to see
885  * if the player has any unpaid items. If so, remove them and put
886  * them back in the map. */
887  tmp = GET_MAP_OB(op->map, op->x, op->y);
888 
889  if (tmp && tmp->type == SHOP_FLOOR) {
890  remove_unpaid_objects(op->inv, op);
891  }
892 
893  /* Move player to his current respawn position (last savebed). */
894  object_enter_map(op,
895  NULL,
896  ready_map_name(CONTR(op)->savebed_map, NULL, 0),
897  CONTR(op)->bed_x,
898  CONTR(op)->bed_y,
899  true);
900 
901  /* Show a nasty message */
902  draw_info(COLOR_WHITE, op, "YOU HAVE DIED.");
903  player_save(op);
904 }
905 
917 void
918 cast_dust (object *op, object *throw_ob, int dir)
919 {
920  archetype_t *arch = NULL;
921 
922  if (!(spells[throw_ob->stats.sp].flags & SPELL_DESC_DIRECTION)) {
923  LOG(ERROR, "Warning, dust is not AoE spell: %s",
924  object_get_str(throw_ob));
925  return;
926  }
927 
928  if (spells[throw_ob->stats.sp].archname) {
929  arch = arch_find(spells[throw_ob->stats.sp].archname);
930  }
931 
932  /* Casting POTION 'dusts' is really use_magic_item skill */
933  if (op->type == PLAYER && throw_ob->type == POTION && !change_skill(op, SK_MAGIC_DEVICES)) {
934  return;
935  }
936 
937  if (throw_ob->type == POTION && arch != NULL) {
938  cast_cone(op, throw_ob, dir, 10, throw_ob->stats.sp, arch);
939  } else if ((arch = arch_find("dust_effect")) != NULL) {
940  /* dust_effect */
941  cast_cone(op, throw_ob, dir, 1, 0, arch);
942  } else {
943  /* Problem occurred! */
944  LOG(BUG, "can't find an archetype to use!");
945  }
946 
947  if (op->type == PLAYER && arch) {
948  char *name = object_get_name_s(throw_ob, op);
949  draw_info_format(COLOR_WHITE, op, "You cast %s.", name);
950  efree(name);
951  }
952 
953  if (op->chosen_skill) {
954  op->chosen_skill->stats.maxsp = throw_ob->last_grace;
955  }
956 
957  if (!QUERY_FLAG(throw_ob, FLAG_REMOVED)) {
958  object_destruct(throw_ob);
959  }
960 }
961 
976 int
977 pvp_area (object *attacker, object *victim)
978 {
979  /* No attacking of party members. */
980  if (attacker && victim && attacker->type == PLAYER && victim->type == PLAYER && CONTR(attacker)->party != NULL && CONTR(victim)->party != NULL && CONTR(attacker)->party == CONTR(victim)->party) {
981  return 0;
982  }
983 
984  if (attacker && victim && attacker == victim) {
985  return 0;
986  }
987 
988  if (attacker && attacker->map) {
989  if (!(attacker->map->map_flags & MAP_FLAG_PVP) || GET_MAP_FLAGS(attacker->map, attacker->x, attacker->y) & P_NO_PVP || GET_MAP_SPACE_PTR(attacker->map, attacker->x, attacker->y)->extra_flags & MSP_EXTRA_NO_PVP) {
990  return 0;
991  }
992  }
993 
994  if (victim && victim->map) {
995  if (!(victim->map->map_flags & MAP_FLAG_PVP) || GET_MAP_FLAGS(victim->map, victim->x, victim->y) & P_NO_PVP || GET_MAP_SPACE_PTR(victim->map, victim->x, victim->y)->extra_flags & MSP_EXTRA_NO_PVP) {
996  return 0;
997  }
998  }
999 
1000  return 1;
1001 }
1002 
1012 object *
1013 find_skill (object *op, int skillnr)
1014 {
1015  object *tmp;
1016 
1017  for (tmp = op->inv; tmp; tmp = tmp->below) {
1018  if (tmp->type == SKILL && tmp->stats.sp == skillnr) {
1019  return tmp;
1020  }
1021  }
1022 
1023  return NULL;
1024 }
1025 
1035 int
1036 player_can_carry (object *pl, uint32_t weight)
1037 {
1038  uint32_t effective_weight_limit;
1039 
1040  if (pl->stats.Str <= MAX_STAT) {
1041  effective_weight_limit = weight_limit[pl->stats.Str];
1042  } else {
1043  effective_weight_limit = weight_limit[MAX_STAT];
1044  }
1045 
1046  return (pl->carrying + weight) < effective_weight_limit;
1047 }
1048 
1060 void
1061 player_path_add (player *pl, mapstruct *map, int16_t x, int16_t y)
1062 {
1063  player_path *path = emalloc(sizeof(player_path));
1064 
1065  /* Initialize the values. */
1066  path->map = map;
1067  path->x = x;
1068  path->y = y;
1069  path->next = NULL;
1070  path->fails = 0;
1071 
1072  if (!pl->move_path) {
1073  pl->move_path = pl->move_path_end = path;
1074  } else {
1075  pl->move_path_end->next = path;
1076  pl->move_path_end = path;
1077  }
1078 }
1079 
1085 void
1087 {
1088  player_path *path, *next;
1089 
1090  if (!pl->move_path) {
1091  return;
1092  }
1093 
1094  for (path = pl->move_path; path; path = next) {
1095  next = path->next;
1096  efree(path);
1097  }
1098 
1099  pl->move_path = NULL;
1100  pl->move_path_end = NULL;
1101 }
1102 
1108 void
1110 {
1111  while (pl->ob->speed_left >= 0.0f && pl->move_path) {
1112  player_path *tmp = pl->move_path;
1113  rv_vector rv;
1114 
1115  /* Make sure the map exists and is loaded, then get the range vector. */
1116  if (!tmp->map || tmp->map->in_memory != MAP_IN_MEMORY || !get_rangevector_from_mapcoords(pl->ob->map, pl->ob->x, pl->ob->y, tmp->map, tmp->x, tmp->y, &rv, 0)) {
1117  /* Something went wrong (map not loaded or we got teleported
1118  * somewhere), clear all queued paths. */
1119  player_path_clear(pl);
1120  return;
1121  } else {
1122  int dir = rv.direction;
1123  mapstruct *map;
1124  int x, y;
1125 
1126  map = pl->ob->map;
1127  x = pl->ob->x;
1128  y = pl->ob->y;
1129 
1130  if (map == tmp->map && x == tmp->x && y == tmp->y) {
1131  /* The player is already on the correct tile, so there's nothing
1132  * to do -- they might have been teleported, for example. */
1133  dir = 0;
1134  } else {
1135  /* Move to the direction directly. */
1136  dir = move_object(pl->ob, dir);
1137 
1138  if (dir == 0) {
1139  int diff;
1140 
1141  /* Try to move around corners otherwise. */
1142  for (diff = 1; diff <= 2; diff++) {
1143  /* Try left or right first? */
1144  int m = 1 - rndm_chance(2) ? 2 : 0;
1145 
1146  dir = move_object(pl->ob, absdir(dir + diff * m));
1147 
1148  if (dir == 0) {
1149  dir = move_object(pl->ob, absdir(dir - diff * m));
1150  }
1151 
1152  if (dir != 0) {
1153  break;
1154  }
1155  }
1156  }
1157  }
1158 
1159  if (dir == -1) {
1160  return;
1161  }
1162 
1163  x += freearr_x[dir];
1164  y += freearr_y[dir];
1165 
1166  map = get_map_from_coord(map, &x, &y);
1167 
1168  /* See if we succeeded in moving where we wanted. */
1169  if (map == tmp->map && x == tmp->x && y == tmp->y) {
1170  pl->move_path = tmp->next;
1171  efree(tmp);
1172  } else if ((rv.distance <= 1 && dir != 0) ||
1173  tmp->fails > PLAYER_PATH_MAX_FAILS) {
1174  /* Clear all paths if we above check failed: this can happen
1175  * if we got teleported somewhere else by a teleporter or a
1176  * shop mat, in which case the player most likely doesn't want
1177  * to move to the original destination. Also see if we failed
1178  * to move to destination too many times already. */
1179  player_path_clear(pl);
1180  return;
1181  } else {
1182  /* Not any of the above; we failed to move where we wanted. */
1183  tmp->fails++;
1184  }
1185 
1186  pl->ob->speed_left--;
1187  }
1188  }
1189 }
1190 
1202 player_faction_create (player *pl, shstr *name)
1203 {
1204  HARD_ASSERT(pl != NULL);
1205  HARD_ASSERT(name != NULL);
1206 
1207  player_faction_t *faction = ecalloc(1, sizeof(*faction));
1208  faction->name = add_string(name);
1209  HASH_ADD(hh, pl->factions, name, sizeof(shstr *), faction);
1210 
1211  return faction;
1212 }
1213 
1222 void
1224 {
1225  HARD_ASSERT(pl != NULL);
1226  HARD_ASSERT(faction != NULL);
1227 
1228  HASH_DEL(pl->factions, faction);
1229  free_string_shared(faction->name);
1230  efree(faction);
1231 }
1232 
1243 player_faction_find (player *pl, shstr *name)
1244 {
1245  HARD_ASSERT(pl != NULL);
1246  HARD_ASSERT(name != NULL);
1247 
1249  HASH_FIND(hh, pl->factions, &name, sizeof(shstr *), faction);
1250  return faction;
1251 }
1252 
1262 void
1263 player_faction_update (player *pl, shstr *name, double reputation)
1264 {
1265  HARD_ASSERT(pl != NULL);
1266  HARD_ASSERT(name != NULL);
1267 
1269 
1270  if (faction == NULL) {
1271  faction = player_faction_create(pl, name);
1272  }
1273 
1274  faction->reputation += reputation;
1275 }
1276 
1286 double
1288 {
1289  HARD_ASSERT(pl != NULL);
1290  HARD_ASSERT(name != NULL);
1291 
1293 
1294  if (faction == NULL) {
1295  return 0.0;
1296  }
1297 
1298  return faction->reputation;
1299 }
1300 
1310 char *
1312 {
1313  if (!str) {
1314  return NULL;
1315  }
1316 
1317  string_replace_unprintable_chars(str);
1318  string_whitespace_squeeze(str);
1319  string_whitespace_trim(str);
1320 
1321  return *str == '\0' ? NULL : str;
1322 }
1323 
1329 void
1331 {
1332  string_whitespace_trim(str);
1333  string_capitalize(str);
1334 }
1335 
1348 static object *
1349 find_marked_object_rec (object *op, object **marked, uint32_t *marked_count)
1350 {
1351  object *tmp, *tmp2;
1352 
1353  /* This may seem like overkill, but we need to make sure that they
1354  * player hasn't dropped the item. We use count on the off chance
1355  * that an item got reincarnated at some point. */
1356  for (tmp = op->inv; tmp; tmp = tmp->below) {
1357  if (IS_INVISIBLE(tmp, op)) {
1358  continue;
1359  }
1360 
1361  if (tmp == *marked) {
1362  if (tmp->count == *marked_count) {
1363  return tmp;
1364  } else {
1365  *marked = NULL;
1366  *marked_count = 0;
1367  return NULL;
1368  }
1369  } else if (tmp->inv) {
1370  tmp2 = find_marked_object_rec(tmp, marked, marked_count);
1371 
1372  if (tmp2) {
1373  return tmp2;
1374  }
1375 
1376  if (*marked == NULL) {
1377  return NULL;
1378  }
1379  }
1380  }
1381 
1382  return NULL;
1383 }
1384 
1392 object *
1394 {
1395  if (op->type != PLAYER || !op || !CONTR(op) || !CONTR(op)->mark) {
1396  return NULL;
1397  }
1398 
1399  return find_marked_object_rec(op, &CONTR(op)->mark, &CONTR(op)->mark_count);
1400 }
1401 
1409 static void
1410 examine_living (object *op, object *tmp, StringBuffer *sb_capture)
1411 {
1412  tmp = HEAD(tmp);
1413 
1414  int gender = object_get_gender(tmp);
1415 
1416  if (QUERY_FLAG(tmp, FLAG_IS_GOOD)) {
1417  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1418  "%s is a good aligned %s %s.", gender_subjective_upper[gender],
1419  gender_noun[gender], tmp->race);
1420  } else if (QUERY_FLAG(tmp, FLAG_IS_EVIL)) {
1421  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1422  "%s is an evil aligned %s %s.", gender_subjective_upper[gender],
1423  gender_noun[gender], tmp->race);
1424  } else if (QUERY_FLAG(tmp, FLAG_IS_NEUTRAL)) {
1425  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1426  "%s is a neutral aligned %s %s.",
1427  gender_subjective_upper[gender], gender_noun[gender],
1428  tmp->race);
1429  } else {
1430  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1431  "%s is a %s %s.", gender_subjective_upper[gender],
1432  gender_noun[gender], tmp->race);
1433  }
1434 
1435  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1436  "%s is level %d.", gender_subjective_upper[gender], tmp->level);
1437  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1438  "%s has a base damage of %d and hp of %d.",
1439  gender_subjective_upper[gender], tmp->stats.dam, tmp->stats.maxhp);
1440  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1441  "%s has a wc of %d and ac of %d.", gender_subjective_upper[gender],
1442  tmp->stats.wc, tmp->stats.ac);
1443 
1444  bool has_protection = true, has_weakness = false;
1445  for (int i = 0; i < NROFATTACKS; i++) {
1446  if (tmp->protection[i] > 0) {
1447  has_protection = true;
1448  } else if (tmp->protection[i] < 0) {
1449  has_weakness = true;
1450  }
1451  }
1452 
1453  if (has_protection) {
1454  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1455  "%s can naturally resist some attacks.",
1456  gender_subjective_upper[gender]);
1457  }
1458 
1459  if (has_weakness) {
1460  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1461  "%s is naturally vulnerable to some attacks.",
1462  gender_subjective_upper[gender]);
1463  }
1464 
1465  int8_t highest_protection = 0;
1466  int best_protection = -1;
1467  for (int i = 0; i < NROFATTACKS; i++) {
1468  if (tmp->protection[i] > highest_protection) {
1469  best_protection = i;
1470  highest_protection = tmp->protection[i];
1471  }
1472  }
1473 
1474  if (best_protection != -1) {
1475  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1476  "Best armour protection seems to be for %s.",
1477  attack_name[best_protection]);
1478  }
1479 
1480  if (QUERY_FLAG(tmp, FLAG_UNDEAD)) {
1481  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1482  "%s is an undead force.", gender_subjective_upper[gender]);
1483  }
1484 
1485  switch ((tmp->stats.hp + 1) * 4 / (tmp->stats.maxhp + 1)) {
1486  case 1:
1487  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1488  "%s is in a bad shape.", gender_subjective_upper[gender]);
1489  break;
1490 
1491  case 2:
1492  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1493  "%s is hurt.", gender_subjective_upper[gender]);
1494  break;
1495 
1496  case 3:
1497  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1498  "%s is somewhat hurt.", gender_subjective_upper[gender]);
1499  break;
1500 
1501  default:
1502  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1503  "%s is in excellent shape.", gender_subjective_upper[gender]);
1504  break;
1505  }
1506 
1507  if (object_find_type(tmp, POISONING) != NULL) {
1508  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1509  "%s looks very ill.", gender_subjective_upper[gender]);
1510  }
1511 }
1512 
1520 void
1521 examine (object *op, object *tmp, StringBuffer *sb_capture)
1522 {
1523  int i;
1524 
1525  if (tmp == NULL) {
1526  return;
1527  }
1528 
1529  tmp = HEAD(tmp);
1530  char *name = object_get_name_description_s(tmp, op);
1531  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1532  "That is %s%s", name, !QUERY_FLAG(tmp, FLAG_IDENTIFIED) &&
1533  need_identify(tmp) ? " (unidentified)" : "");
1534  efree(name);
1535 
1536  if (tmp->custom_name != NULL) {
1537  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1538  "You name it %s.", tmp->custom_name);
1539  }
1540 
1541  if (QUERY_FLAG(tmp, FLAG_MONSTER) || tmp->type == PLAYER) {
1542  char *desc = stringbuffer_finish(object_get_description(tmp, op, NULL));
1543  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op,
1544  "%s.", desc);
1545  efree(desc);
1546  examine_living(op, tmp, sb_capture);
1547  } else if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1548  /* We don't double use the item_xxx arch commands, so they are always valid */
1549 
1550  if (QUERY_FLAG(tmp, FLAG_IS_GOOD)) {
1551  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It is good aligned.");
1552  } else if (QUERY_FLAG(tmp, FLAG_IS_EVIL)) {
1553  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It is evil aligned.");
1554  } else if (QUERY_FLAG(tmp, FLAG_IS_NEUTRAL)) {
1555  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It is neutral aligned.");
1556  }
1557 
1558  if (tmp->item_level) {
1559  if (tmp->item_skill) {
1560  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It needs a level of %d in %s to use.", tmp->item_level, skills[tmp->item_skill - 1].name);
1561  } else {
1562  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It needs a level of %d to use.", tmp->item_level);
1563  }
1564  }
1565 
1566  if (tmp->item_quality) {
1567  if (QUERY_FLAG(tmp, FLAG_INDESTRUCTIBLE)) {
1568  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "Qua: %d Con: Indestructible.", tmp->item_quality);
1569  } else {
1570  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "Qua: %d Con: %d.", tmp->item_quality, tmp->item_condition);
1571  }
1572  }
1573  }
1574 
1575  switch (tmp->type) {
1576  case BOOK:
1577  {
1578  if (tmp->msg) {
1579  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "Something is written in it.");
1580 
1581  if (op->type == PLAYER && !QUERY_FLAG(tmp, FLAG_NO_SKILL_IDENT)) {
1582  int level = CONTR(op)->skill_ptr[SK_LITERACY]->level;
1583 
1584  /* Gray. */
1585  if (tmp->level < level_color[level].green) {
1586  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It seems to contain no knowledge you could learn from.");
1587  } else if (tmp->level < level_color[level].blue) {
1588  /* Green. */
1589  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It seems to contain tiny bits of knowledge you could learn from.");
1590  } else if (tmp->level < level_color[level].yellow) {
1591  /* Blue. */
1592  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It seems to contain a small amount of knowledge you could learn from.");
1593  } else if (tmp->level < level_color[level].orange) {
1594  /* Yellow. */
1595  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It seems to contain an average amount of knowledge you could learn from.");
1596  } else if (tmp->level < level_color[level].red) {
1597  /* Orange. */
1598  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It seems to contain a moderate amount of knowledge you could learn from.");
1599  } else if (tmp->level < level_color[level].purple) {
1600  /* Red. */
1601  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It seems to contain a fair amount of knowledge you could learn from.");
1602  } else {
1603  /* Purple. */
1604  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It seems to contain a great amount of knowledge you could learn from.");
1605  }
1606  }
1607  }
1608 
1609  break;
1610  }
1611 
1612  case BOOK_SPELL:
1613  if (tmp->stats.sp >= 0 && tmp->stats.sp < NROFREALSPELLS) {
1614  int level = spells[tmp->stats.sp].at->clone.level;
1615  draw_info_full_format(CHAT_TYPE_GAME,
1616  NULL,
1617  COLOR_WHITE,
1618  sb_capture,
1619  op,
1620  "It contains a level %d wizardry spell.",
1621  level);
1622  int learn_level = spells[tmp->stats.sp].at->clone.level - 15;
1623  learn_level = MAX(1, learn_level);
1624  draw_info_full_format(CHAT_TYPE_GAME,
1625  NULL,
1626  COLOR_WHITE,
1627  sb_capture,
1628  op,
1629  "It requires a minimum level of %d in "
1630  "literacy to learn.",
1631  learn_level);
1632  }
1633 
1634  break;
1635 
1636  case CONTAINER:
1637  {
1638  if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1639  if (tmp->race != NULL) {
1640  if (tmp->weight_limit != 0) {
1641  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1642  sb_capture, op, "It can hold only %s and its "
1643  "weight limit is %.1f kg.", tmp->race,
1644  tmp->weight_limit / 1000.0);
1645  } else {
1646  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1647  sb_capture, op, "It can hold only %s.", tmp->race);
1648  }
1649  } else {
1650  if (tmp->weight_limit != 0) {
1651  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1652  sb_capture, op, "Its weight limit is %.1f kg.",
1653  tmp->weight_limit / 1000.0);
1654  }
1655  }
1656 
1657  /* Has a magic modifier? */
1658  if (!DBL_EQUAL(tmp->weapon_speed, 1.0)) {
1659  if (tmp->weapon_speed > 1.0) {
1660  /* Increases weight of items (bad) */
1661  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1662  sb_capture, op, "It increases the weight of items "
1663  "inside by %.1f%%.", tmp->weapon_speed * 100.0);
1664  } else {
1665  /* Decreases weight of items (good) */
1666  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1667  sb_capture, op, "It decreases the weight of items "
1668  "inside by %.1f%%.", 100.0 -
1669  (tmp->weapon_speed * 100.0));
1670  }
1671  }
1672 
1673  if (DBL_EQUAL(tmp->weapon_speed, 1.0)) {
1674  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1675  sb_capture, op, "It contains %3.3f kg.",
1676  tmp->carrying / 1000.0);
1677  } else if (tmp->weapon_speed > 1.0) {
1678  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1679  sb_capture, op, "It contains %3.3f kg, increased to "
1680  "%3.3f kg.", tmp->damage_round_tag / 1000.0,
1681  tmp->carrying / 1000.0);
1682  } else {
1683  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1684  sb_capture, op, "It contains %3.3f kg, decreased to "
1685  "%3.3f kg.", tmp->damage_round_tag / 1000.0,
1686  tmp->carrying / 1000.0);
1687  }
1688  }
1689 
1690  break;
1691  }
1692 
1693  case WAND:
1694  {
1695  if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1696  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It has %d charges left.", tmp->stats.food);
1697  }
1698 
1699  break;
1700  }
1701 
1702  case POWER_CRYSTAL:
1703  {
1704  /* Avoid division by zero... */
1705  if (tmp->stats.maxsp == 0) {
1706  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It has capacity of %d.", tmp->stats.maxsp);
1707  } else {
1708  const char *charge;
1709 
1710  i = (tmp->stats.sp * 10) / tmp->stats.maxsp;
1711 
1712  if (tmp->stats.sp == 0) {
1713  charge = "empty";
1714  } else if (i == 0) {
1715  charge = "almost empty";
1716  } else if (i < 3) {
1717  charge = "partially filled";
1718  } else if (i < 6) {
1719  charge = "half full";
1720  } else if (i < 9) {
1721  charge = "well charged";
1722  } else if (tmp->stats.sp == tmp->stats.maxsp) {
1723  charge = "fully charged";
1724  } else {
1725  charge = "almost full";
1726  }
1727 
1728  /* Higher capacity crystals */
1729  if (tmp->stats.maxsp > 1000) {
1730  i = (tmp->stats.maxsp % 1000) / 100;
1731 
1732  if (i != 0) {
1733  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It has capacity of %d.%dk and is %s.", tmp->stats.maxsp / 1000, i, charge);
1734  } else {
1735  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It has capacity of %dk and is %s.", tmp->stats.maxsp / 1000, charge);
1736  }
1737  } else {
1738  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "It has capacity of %d and is %s.", tmp->stats.maxsp, charge);
1739  }
1740  }
1741 
1742  break;
1743  }
1744  }
1745 
1746  if (tmp->material && (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))) {
1747  char buf[MAX_BUF];
1748 
1749  snprintf(buf, sizeof(buf), "%s made of: ", tmp->nrof > 1 ? "They are" : "It is");
1750 
1751  for (i = 0; i < NROFMATERIALS; i++) {
1752  if (tmp->material & (1 << i)) {
1753  snprintfcat(buf, sizeof(buf), "%s ", materials[i].name);
1754  }
1755  }
1756 
1757  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, buf);
1758  }
1759 
1760  if (tmp->weight != 0) {
1761  double weight = MAX(1, tmp->nrof) * tmp->weight / 1000.0f;
1762 
1763  if (tmp->type == MONSTER) {
1764  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s weighs %3.3f kg.", gender_subjective_upper[object_get_gender(tmp)], weight);
1765  } else if (tmp->type == PLAYER) {
1766  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s weighs %3.3f kg and is carrying %3.3f kg.", gender_subjective_upper[object_get_gender(tmp)], weight, (float) tmp->carrying / 1000.0f);
1767  } else {
1768  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, tmp->nrof > 1 ? "They weigh %3.3f kg." : "It weighs %3.3f kg.", weight);
1769  }
1770  }
1771 
1772  if (QUERY_FLAG(tmp, FLAG_SOULBOUND)) {
1773  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1774  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture,
1775  op, "%s would become soulbound to you.",
1776  tmp->nrof > 1 ? "They" : "It");
1777  } else {
1778  shstr *soulbound_name = object_get_value(tmp, "soulbound_name");
1779  if (soulbound_name == NULL) {
1780  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1781  sb_capture, op,
1782  "%s soulbound without an owner.",
1783  tmp->nrof > 1 ? "They are" : "It is");
1784  } else {
1785  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE,
1786  sb_capture, op,
1787  "%s soulbound to %s.",
1788  tmp->nrof > 1 ? "They are" : "It is",
1789  soulbound_name);
1790  }
1791  }
1792  }
1793 
1794  if (QUERY_FLAG(tmp, FLAG_STARTEQUIP)) {
1795  /* Unpaid clone shop item */
1796  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1797  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", shop_get_cost_string_item(tmp, COST_BUY));
1798  } else {
1799  /* God-given item */
1800  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s god-given item%s.", tmp->nrof > 1 ? "They are" : "It is a", tmp->nrof > 1 ? "s" : "");
1801 
1802  if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1803  if (tmp->value) {
1804  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "But %s worth %s.", tmp->nrof > 1 ? "they are" : "it is", shop_get_cost_string_item(tmp, COST_TRUE));
1805  } else {
1806  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s worthless.", tmp->nrof > 1 ? "They are" : "It is");
1807  }
1808  }
1809  }
1810  } else if (tmp->value && !IS_LIVE(tmp)) {
1811  if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1812  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1813  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", shop_get_cost_string_item(tmp, COST_BUY));
1814  } else {
1815  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s worth %s.", tmp->nrof > 1 ? "They are" : "It is", shop_get_cost_string_item(tmp, COST_TRUE));
1816  }
1817  }
1818 
1819  if (!QUERY_FLAG(tmp, FLAG_UNPAID) && tmp->type != MONEY) {
1820  object *floor_ob;
1821 
1822  floor_ob = GET_MAP_OB_LAYER(op->map, op->x, op->y, LAYER_FLOOR, 0);
1823 
1824  if (floor_ob && floor_ob->type == SHOP_FLOOR) {
1825  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "This shop will pay you %s.", shop_get_cost_string_item(tmp, COST_SELL));
1826  }
1827  }
1828  } else if (!IS_LIVE(tmp)) {
1829  if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1830  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1831  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s would cost nothing.", tmp->nrof > 1 ? "They" : "It");
1832  } else {
1833  draw_info_full_format(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "%s worthless.", tmp->nrof > 1 ? "They are" : "It is");
1834  }
1835  }
1836  }
1837 
1838  /* Does the object have a message? Don't show message for all object
1839  * types - especially if the first entry is a match */
1840  if (tmp->msg && tmp->type != EXIT && tmp->type != BOOK && tmp->type != CORPSE && !QUERY_FLAG(tmp, FLAG_WALK_ON) && strncasecmp(tmp->msg, "@match", 7)) {
1841  /* This is just a hack so when identifying the items, we print
1842  * out the extra message */
1843  if ((need_identify(tmp) || QUERY_FLAG(tmp, FLAG_QUEST_ITEM)) && QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
1844  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, "The object has a story:");
1845  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, tmp->msg);
1846  }
1847  }
1848 
1849  trigger_map_event(MEVENT_EXAMINE, op->map, op, tmp, NULL, NULL, 0);
1850 
1851  /* Blank line */
1852  draw_info_full(CHAT_TYPE_GAME, NULL, COLOR_WHITE, sb_capture, op, " ");
1853 }
1854 
1869 int
1870 sack_can_hold (object *pl, object *sack, object *op, int nrof)
1871 {
1872  if (!QUERY_FLAG(sack, FLAG_APPLIED)) {
1873  if (pl != NULL) {
1874  char *name = object_get_name_s(sack, pl);
1875  draw_info_format(COLOR_WHITE, pl, "The %s is not active.", name);
1876  efree(name);
1877  }
1878 
1879  return 0;
1880  }
1881 
1882  if (sack == op) {
1883  if (pl != NULL) {
1884  char *name = object_get_name_s(sack, pl);
1885  draw_info_format(COLOR_WHITE, pl, "You can't put the %s into "
1886  "itself.", name);
1887  efree(name);
1888  }
1889 
1890  return 0;
1891  }
1892 
1893  if ((sack->race && sack->sub_type != ST1_CONTAINER_CORPSE) &&
1894  (sack->race != op->race || op->type == CONTAINER ||
1895  (sack->stats.food && sack->stats.food != op->type))) {
1896  if (pl != NULL) {
1897  char *name = object_get_name_s(sack, pl);
1898  draw_info_format(COLOR_WHITE, pl, "You can put only %s into the "
1899  "%s.", sack->race, name);
1900  efree(name);
1901  }
1902 
1903  return 0;
1904  }
1905 
1906  if (sack->weight_limit != 0 && sack->carrying + (((MAX(1, nrof) *
1907  op->weight) + op->carrying) * sack->weapon_speed) >
1908  sack->weight_limit) {
1909  if (pl != NULL) {
1910  char *name = object_get_name_s(sack, pl);
1911  draw_info_format(COLOR_WHITE, pl, "That won't fit in the %s!",
1912  name);
1913  efree(name);
1914  }
1915 
1916  return 0;
1917  }
1918 
1919  return 1;
1920 }
1921 
1922 static object *
1923 get_pickup_object (object *pl, object *op, int nrof)
1924 {
1925  char *name = object_get_name_s(op, pl);
1926 
1927  if (QUERY_FLAG(op, FLAG_UNPAID) && QUERY_FLAG(op, FLAG_NO_PICK)) {
1928  op = object_clone(op);
1929  CLEAR_FLAG(op, FLAG_NO_PICK);
1931  op->nrof = nrof;
1932 
1933  draw_info_format(COLOR_WHITE, pl, "You pick up %s for %s from the "
1934  "storage.", name, shop_get_cost_string_item(op, COST_BUY));
1935  } else {
1936  op = object_stack_get_removed(op, nrof);
1937 
1938  if (QUERY_FLAG(op, FLAG_UNPAID)) {
1939  draw_info_format(COLOR_WHITE, pl, "%s will cost you %s.", name,
1941  } else {
1942  draw_info_format(COLOR_WHITE, pl, "You pick up the %s.", name);
1943  }
1944  }
1945 
1946  efree(name);
1947  op->sub_layer = 0;
1948 
1949  return op;
1950 }
1951 
1965 static void
1966 pick_up_object (object *pl, object *op, object *tmp, int nrof, int no_mevent)
1967 {
1968  int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
1969 
1970  /* IF the player is flying and trying to take the item out of a container
1971  * that is in his inventory, let him. */
1972  if (QUERY_FLAG(pl, FLAG_FLYING) && !object_is_in_inventory(tmp, pl)) {
1973  draw_info(COLOR_WHITE, pl, "You are levitating, you can't reach the ground!");
1974  return;
1975  }
1976 
1977  if (nrof > tmp_nrof || nrof == 0) {
1978  nrof = tmp_nrof;
1979  }
1980 
1981  if (!player_can_carry(pl, WEIGHT_NROF(tmp, nrof))) {
1982  draw_info(COLOR_WHITE, pl, "That item is too heavy for you to pick up.");
1983  return;
1984  }
1985 
1986  if (tmp->type == ARROW) {
1988  }
1989 
1990  /* Trigger the PICKUP event */
1991  if (trigger_event(EVENT_PICKUP, pl, tmp, op, NULL, nrof, 0, 0, 0)) {
1992  return;
1993  }
1994 
1995  /* Trigger the map-wide pick up event. */
1996  if (!no_mevent && pl->map && pl->map->events && trigger_map_event(MEVENT_PICK, pl->map, pl, tmp, op, NULL, nrof)) {
1997  return;
1998  }
1999 
2000  if (pl->type == PLAYER) {
2001  CONTR(pl)->stat_items_picked++;
2002  }
2003 
2004  tmp = get_pickup_object(pl, tmp, nrof);
2005  object_insert_into(tmp, op, 0);
2006 }
2007 
2018 void
2019 pick_up (object *op, object *alt, int no_mevent)
2020 {
2021  int count;
2022  object *tmp = NULL;
2023 
2024  /* Decide which object to pick. */
2025  if (alt) {
2026  if (!object_can_pick(op, alt)) {
2027  draw_info_format(COLOR_WHITE, op, "You can't pick up %s.", alt->name);
2028  return;
2029  }
2030 
2031  tmp = alt;
2032  } else {
2033  if (op->below == NULL || !object_can_pick(op, op->below)) {
2034  draw_info(COLOR_WHITE, op, "There is nothing to pick up here.");
2035  return;
2036  }
2037 
2038  tmp = op->below;
2039  }
2040 
2041  if (!object_can_pick(op, tmp)) {
2042  return;
2043  }
2044 
2045  if (op->type == PLAYER) {
2046  count = CONTR(op)->count;
2047 
2048  if (count == 0) {
2049  count = tmp->nrof;
2050  }
2051  } else {
2052  count = tmp->nrof;
2053  }
2054 
2055  /* Container is open, so use it */
2056  if (op->type == PLAYER && CONTR(op)->container != NULL &&
2057  CONTR(op)->container != tmp && CONTR(op)->container != tmp->env) {
2058  alt = CONTR(op)->container;
2059 
2060  if (alt != tmp->env && !sack_can_hold(op, alt, tmp, count) && !container_check_magical(tmp, alt)) {
2061  return;
2062  }
2063  } else {
2064  /* Con container pickup */
2065 
2066  alt = NULL;
2067 
2068  if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) {
2069  for (alt = op->inv; alt; alt = alt->below) {
2070  if (alt->type == CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) && alt->race && alt->race == tmp->race && sack_can_hold(NULL, alt, tmp, count) && !container_check_magical(tmp, alt)) {
2071  /* Perfect match */
2072  break;
2073  }
2074  }
2075  }
2076 
2077  if (!alt) {
2078  for (alt = op->inv; alt; alt = alt->below) {
2079  if (alt->type == CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) && sack_can_hold(NULL, alt, tmp, count) && !container_check_magical(tmp, alt)) {
2080  /* General container comes next */
2081  break;
2082  }
2083  }
2084  }
2085 
2086  /* No free containers */
2087  if (!alt) {
2088  alt = op;
2089  }
2090  }
2091 
2092  if (tmp->env == alt) {
2093  alt = op;
2094  }
2095 
2096  /* Startequip items are not allowed to be put into containers. */
2097  if (op->type == PLAYER && alt->type == CONTAINER && QUERY_FLAG(tmp, FLAG_STARTEQUIP)) {
2098  draw_info(COLOR_WHITE, op, "This object cannot be put into containers!");
2099  return;
2100  }
2101 
2102  pick_up_object(op, alt, tmp, count, no_mevent);
2103 
2104  if (op->type == PLAYER) {
2105  CONTR(op)->count = 0;
2106  }
2107 }
2108 
2121 void
2122 put_object_in_sack (object *op, object *sack, object *tmp, long nrof)
2123 {
2124  int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
2125 
2126  if (op->type != PLAYER) {
2127  return;
2128  }
2129 
2130  /* Can't put an object in itself */
2131  if (sack == tmp) {
2132  return;
2133  }
2134 
2135  if (sack->type != CONTAINER) {
2136  char *name = object_get_name_s(sack, op);
2137  draw_info_format(COLOR_WHITE, op, "The %s is not a container.", name);
2138  efree(name);
2139  return;
2140  }
2141 
2142  if (container_check_magical(tmp, sack)) {
2143  draw_info(COLOR_WHITE, op, "You can't put a magical container into another magical container.");
2144  return;
2145  }
2146 
2147  if (tmp->map && sack->env) {
2148  if (trigger_event(EVENT_PICKUP, op, tmp, sack, NULL, nrof, 0, 0, 0)) {
2149  return;
2150  }
2151  }
2152 
2153  /* Trigger the map-wide put event. */
2154  if (op->map && op->map->events && trigger_map_event(MEVENT_PUT, op->map, op, tmp, sack, NULL, nrof)) {
2155  return;
2156  }
2157 
2158  if (nrof > tmp_nrof || nrof == 0) {
2159  nrof = tmp_nrof;
2160  }
2161 
2162  if (!sack_can_hold(op, sack, tmp, nrof)) {
2163  return;
2164  }
2165 
2166  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
2168  return;
2169  }
2170  }
2171 
2172  tmp = get_pickup_object(op, tmp, nrof);
2173 
2174  char *name = object_get_name_s(sack, op);
2175  char *tmp_name = object_get_name_s(tmp, op);
2176  draw_info_format(COLOR_WHITE, op, "You put the %s in %s.", tmp_name, name);
2177  efree(name);
2178  efree(tmp_name);
2179 
2180  object_insert_into(tmp, sack, 0);
2181 }
2182 
2194 void
2195 drop_object (object *op, object *tmp, long nrof, int no_mevent)
2196 {
2197  object *floor_ob;
2198 
2199  if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
2200  draw_info(COLOR_WHITE, op, "You can't drop that item.");
2201  return;
2202  }
2203 
2204  /* Trigger the map-wide drop event. */
2205  if (!no_mevent && op->map && op->map->events && trigger_map_event(MEVENT_DROP, op->map, op, tmp, NULL, NULL, nrof)) {
2206  return;
2207  }
2208 
2209  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
2210  /* Can't unapply it */
2212  return;
2213  }
2214  }
2215 
2216  /* Trigger the DROP event */
2217  if (trigger_event(EVENT_DROP, op, tmp, NULL, NULL, nrof, 0, 0, 0)) {
2218  return;
2219  }
2220 
2221  tmp = object_stack_get_removed(tmp, nrof);
2222 
2223  if (op->type == PLAYER) {
2224  CONTR(op)->stat_items_dropped++;
2225  }
2226 
2227  if (QUERY_FLAG(tmp, FLAG_STARTEQUIP) || QUERY_FLAG(tmp, FLAG_UNPAID)) {
2228  if (op->type == PLAYER) {
2229  char *name = object_get_name_s(tmp, op);
2230  draw_info_format(COLOR_WHITE, op, "You drop the %s.", name);
2231  efree(name);
2232 
2233  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
2234  draw_info(COLOR_WHITE, op, "The shop magic put it back to the storage.");
2235 
2236  floor_ob = GET_MAP_OB_LAYER(op->map, op->x, op->y, LAYER_FLOOR, 0);
2237 
2238  /* If the player is standing on a unique shop floor or unique
2239  * randomitems shop floor, drop the object back to the floor */
2240  if (floor_ob && floor_ob->type == SHOP_FLOOR && (QUERY_FLAG(floor_ob, FLAG_IS_MAGICAL) || (floor_ob->randomitems && QUERY_FLAG(floor_ob, FLAG_CURSED)))) {
2241  tmp->x = op->x;
2242  tmp->y = op->y;
2243  object_insert_map(tmp, op->map, op, 0);
2244  return;
2245  }
2246  } else {
2247  draw_info(COLOR_WHITE, op, "The god-given item vanishes to nowhere as you drop it!");
2248  }
2249  }
2250 
2251  object_destroy(tmp);
2252  return;
2253  }
2254 
2255  /* If SAVE_INTERVAL is commented out, we never want to save
2256  * the player here. */
2257 #ifdef SAVE_INTERVAL
2258 
2259  if (op->type == PLAYER && !QUERY_FLAG(tmp, FLAG_UNPAID) && (tmp->nrof ? tmp->value * tmp->nrof : tmp->value > 2000) && (CONTR(op)->last_save_time + SAVE_INTERVAL) <= time(NULL)) {
2260  player_save(op);
2261  CONTR(op)->last_save_time = time(NULL);
2262  }
2263 #endif
2264 
2265  floor_ob = GET_MAP_OB_LAYER(op->map, op->x, op->y, LAYER_FLOOR, 0);
2266 
2267  if (floor_ob && floor_ob->type == SHOP_FLOOR && !QUERY_FLAG(tmp, FLAG_UNPAID) && tmp->type != MONEY) {
2268  shop_sell_item(op, tmp);
2269 
2270  /* Ok, we have really sold it - not only dropped. Run this only
2271  * if the floor is not magical (i.e., unique shop) */
2272  if (QUERY_FLAG(tmp, FLAG_UNPAID) && !QUERY_FLAG(floor_ob, FLAG_IS_MAGICAL)) {
2273  if (op->type == PLAYER) {
2274  draw_info(COLOR_WHITE, op, "The shop magic put it to the storage.");
2275  }
2276 
2277  object_destroy(tmp);
2278  return;
2279  }
2280  }
2281 
2282  tmp->x = op->x;
2283  tmp->y = op->y;
2284  tmp->sub_layer = op->sub_layer;
2285 
2286  object_insert_map(tmp, op->map, op, 0);
2287 
2288  SET_FLAG(op, FLAG_NO_APPLY);
2289  object_remove(op, 0);
2292 }
2293 
2303 void
2304 drop (object *op, object *tmp, int no_mevent)
2305 {
2306  if (tmp == NULL) {
2307  draw_info(COLOR_WHITE, op, "You don't have anything to drop.");
2308  return;
2309  }
2310 
2311  if (QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
2312  draw_info(COLOR_WHITE, op, "This item is locked.");
2313  return;
2314  }
2315 
2316  if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
2317  draw_info(COLOR_WHITE, op, "You can't drop that item.");
2318  return;
2319  }
2320 
2321  if (op->type == PLAYER) {
2322  if (CONTR(op)->container) {
2323  put_object_in_sack(op, CONTR(op)->container, tmp, CONTR(op)->count);
2324  } else {
2325  drop_object(op, tmp, CONTR(op)->count, no_mevent);
2326  }
2327 
2328  CONTR(op)->count = 0;
2329  } else {
2330  drop_object(op, tmp, 0, no_mevent);
2331  }
2332 }
2333 
2334 char *
2335 player_make_path (const char *name, const char *ext)
2336 {
2337  StringBuffer *sb;
2338  char *name_lower, *cp;
2339  size_t i;
2340 
2341  sb = stringbuffer_new();
2342  stringbuffer_append_printf(sb, "%s/players/", settings.datapath);
2343  name_lower = estrdup(name);
2344  string_tolower(name_lower);
2345 
2346  for (i = 0; i < settings.limits[ALLOWED_CHARS_CHARNAME][0] - 1; i++) {
2347  stringbuffer_append_string_len(sb, name_lower, i + 1);
2348  stringbuffer_append_string(sb, "/");
2349  }
2350 
2351  stringbuffer_append_printf(sb, "%s/%s", name_lower, ext);
2352 
2353  efree(name_lower);
2354  cp = stringbuffer_finish(sb);
2355 
2356  return cp;
2357 }
2358 
2359 int
2360 player_exists (const char *name)
2361 {
2362  char *path;
2363  int ret;
2364 
2365  path = player_make_path(name, "player.dat");
2366  ret = path_exists(path);
2367  efree(path);
2368 
2369  return ret;
2370 }
2371 
2378 void
2379 player_save (object *op)
2380 {
2381  HARD_ASSERT(op != NULL);
2382 
2383  /* Is this a map players can't save on? */
2384  if (op->map != NULL && MAP_PLAYER_NO_SAVE(op->map)) {
2385  return;
2386  }
2387 
2388  char *path = player_make_path(op->name, "player.dat");
2389  char *path_tmp = player_make_path(op->name, "player.dat.tmp");
2390 
2391  player *pl = CONTR(op);
2392 
2393  path_ensure_directories(path_tmp);
2394  FILE *fp = fopen(path_tmp, "w");
2395  if (unlikely(fp == NULL)) {
2396  LOG(ERROR, "Failure opening %s for writing: %s",
2397  path_tmp, strerror(errno));
2398  goto error;
2399  }
2400 
2401  fprintf(fp, "no_chat %d\n", pl->no_chat);
2402  fprintf(fp, "tcl %d\n", pl->tcl);
2403  fprintf(fp, "tgm %d\n", pl->tgm);
2404  fprintf(fp, "tsi %d\n", pl->tsi);
2405  fprintf(fp, "tli %d\n", pl->tli);
2406  fprintf(fp, "tls %d\n", pl->tls);
2407  fprintf(fp, "map %s\n", op->map ? op->map->path : EMERGENCY_MAPPATH);
2408  fprintf(fp, "bed_map %s\n", pl->savebed_map);
2409  fprintf(fp, "bed_x %d\nbed_y %d\n", pl->bed_x, pl->bed_y);
2410 
2411  for (int i = 0; i < pl->num_cmd_permissions; i++) {
2412  if (unlikely(pl->cmd_permissions[i] == NULL)) {
2413  continue;
2414  }
2415 
2416  fprintf(fp, "cmd_permission %s\n", pl->cmd_permissions[i]);
2417  }
2418 
2419  player_faction_t *faction, *tmp;
2420  HASH_ITER(hh, pl->factions, faction, tmp) {
2421  fprintf(fp, "faction %s %e\n", faction->name, faction->reputation);
2422  }
2423 
2424  fprintf(fp, "fame %"PRId64 "\n", pl->fame);
2425  fprintf(fp, "endplst\n");
2426 
2428  object_save(op, fp);
2430 
2431  /* Make sure the write succeeded. */
2432  if (unlikely(fclose(fp) == EOF)) {
2433  LOG(ERROR, "Failure closing file %s: %s",
2434  path_tmp, strerror(errno));
2435  goto error;
2436  }
2437 
2438  /* Set the correct permissions. */
2439  if (unlikely(chmod(path_tmp, SAVE_MODE) != 0)) {
2440  LOG(ERROR, "Failure setting permissions of %s: %s",
2441  path_tmp, strerror(errno));
2442  goto error;
2443  }
2444 
2445  /* Rename the file, removing the .tmp extension. */
2446  if (unlikely(path_rename(path_tmp, path) != 0)) {
2447  LOG(ERROR, "Failure renaming %s to %s: %s",
2448  path_tmp, path, strerror(errno));
2449  goto error;
2450  }
2451 
2452  goto out;
2453 
2454 error:
2455  /* Handle errors. */
2456  draw_info(COLOR_RED, op, "Your character couldn't be saved.");
2457 
2458  /* Try to remove the temporary file if it was created. */
2459  if (fp != NULL && unlink(path_tmp) != 0) {
2460  LOG(ERROR, "Failure removing temporary file %s: %s",
2461  path_tmp, strerror(errno));
2462  }
2463 
2464 out:
2465  efree(path);
2466  efree(path_tmp);
2467 }
2468 
2477 static void
2478 player_load (player *pl, FILE *fp)
2479 {
2480  HARD_ASSERT(pl != NULL);
2481  HARD_ASSERT(fp != NULL);
2482 
2483  char buf[HUGE_BUF];
2484  while (fgets(VS(buf), fp)) {
2485  char *cp = buf;
2486  string_strip_newline(cp);
2487 
2488  if (strcmp(buf, "endplst") == 0) {
2489  break;
2490  } else if (strncmp(buf, "no_chat ", 8) == 0) {
2491  pl->no_chat = atoi(buf + 8);
2492  } else if (strncmp(buf, "tcl ", 4) == 0) {
2493  pl->tcl = atoi(buf + 4);
2494  } else if (strncmp(buf, "tgm ", 4) == 0) {
2495  pl->tgm = atoi(buf + 4);
2496  } else if (strncmp(buf, "tsi ", 4) == 0) {
2497  pl->tsi = atoi(buf + 4);
2498  } else if (strncmp(buf, "tli ", 4) == 0) {
2499  pl->tli = atoi(buf + 4);
2500  } else if (strncmp(buf, "tls ", 4) == 0) {
2501  pl->tls = atoi(buf + 4);
2502  } else if (strncmp(buf, "map ", 4) == 0) {
2503  snprintf(VS(pl->maplevel), "%s", buf + 4);
2504  } else if (strncmp(buf, "bed_map ", 8) == 0) {
2505  snprintf(VS(pl->savebed_map), "%s", buf + 8);
2506  } else if (strncmp(buf, "bed_x ", 5) == 0) {
2507  pl->bed_x = atoi(buf + 5);
2508  } else if (strncmp(buf, "bed_y ", 5) == 0) {
2509  pl->bed_y = atoi(buf + 5);
2510  } else if (strncmp(buf, "cmd_permission ", 15) == 0) {
2511  pl->cmd_permissions =
2512  erealloc(pl->cmd_permissions,
2513  sizeof(char *) * (pl->num_cmd_permissions + 1));
2514  pl->cmd_permissions[pl->num_cmd_permissions] = estrdup(buf + 15);
2515  pl->num_cmd_permissions++;
2516  } else if (strncmp(buf, "faction ", 8) == 0) {
2517  size_t pos = 8;
2518  char faction_name[MAX_BUF];
2519  if (string_get_word(buf, &pos, ' ', VS(faction_name), 0)) {
2521  player_faction_create(pl, faction_name);
2522  faction->reputation = atof(buf + pos);
2523  }
2524  } else if (strncmp(buf, "fame ", 5) == 0) {
2525  pl->fame = atoll(buf + 5);
2526  }
2527  }
2528 
2530  void *buffer = create_loader_buffer(fp);
2531  load_object_buffer(buffer, pl->ob, 0);
2532  delete_loader_buffer(buffer);
2534 
2535  /* The inventory of players is loaded in reverse order, so we need to
2536  * reorder it. */
2538 }
2539 
2550 static void
2551 player_create (player *pl, archetype_t *at, const char *name)
2552 {
2553  HARD_ASSERT(pl != NULL);
2554  HARD_ASSERT(at != NULL);
2555  HARD_ASSERT(name != NULL);
2556 
2557  object_copy(pl->ob, &at->clone, false);
2558  pl->ob->custom_attrset = pl;
2559  FREE_AND_COPY_HASH(pl->ob->name, name);
2560 
2562  give_initial_items(pl->ob, pl->ob->randomitems);
2563  trigger_global_event(GEVENT_BORN, pl->ob, NULL);
2565 
2566  snprintf(VS(pl->maplevel), "%s", first_map_path);
2567  pl->ob->x = first_map_x;
2568  pl->ob->y = first_map_y;
2569 }
2570 
2582 object *
2583 player_get_dummy (const char *name, const char *host)
2584 {
2585  player *pl;
2586 
2587  pl = get_player(NULL);
2588  pl->cs = ecalloc(1, sizeof(*pl->cs));
2589  pl->cs->sc = socket_create(host != NULL ? host : "127.0.0.1",
2590  13327,
2591  false,
2592  SOCKET_ROLE_SERVER,
2593  false);
2594  if (pl->cs->sc == NULL) {
2595  abort();
2596  }
2597 
2598  init_connection(pl->cs);
2599 
2600  pl->ob = arch_get("human_male");
2601  pl->ob->custom_attrset = pl;
2602 
2603  if (name != NULL) {
2604  FREE_AND_COPY_HASH(pl->ob->name, name);
2605  }
2606 
2607  snprintf(VS(pl->savebed_map), "%s", EMERGENCY_MAPPATH);
2608  pl->bed_x = EMERGENCY_X;
2609  pl->bed_y = EMERGENCY_Y;
2610 
2612  give_initial_items(pl->ob, pl->ob->randomitems);
2614  living_update_player(pl->ob);
2615  link_player_skills(pl->ob);
2616 
2617  pl->cs->state = ST_PLAYING;
2619  pl->cs->account = estrdup(ACCOUNT_TESTING_NAME);
2620  pl->cs->sound = 1;
2621 
2622  object_enter_map(pl->ob, NULL, NULL, 0, 0, false);
2623 
2624  return pl->ob;
2625 }
2626 
2627 object *
2628 player_find_spell (object *op, spell_struct *spell)
2629 {
2630  for (object *tmp = op->inv; tmp != NULL; tmp = tmp->below) {
2631  if (tmp->type == SPELL && tmp->name == spell->at->clone.name) {
2632  return tmp;
2633  }
2634  }
2635 
2636  return NULL;
2637 }
2638 
2646 void
2647 player_set_talking_to (player *pl, object *npc)
2648 {
2649  HARD_ASSERT(pl != NULL);
2650  HARD_ASSERT(npc != NULL);
2651 
2652  if (OBJECT_VALID(pl->talking_to, pl->talking_to_count) &&
2653  pl->talking_to != npc) {
2655  }
2656 
2657  pl->talking_to = npc;
2658  pl->talking_to_count = npc->count;
2659 
2660  if (pl->target_object != npc || pl->target_object_count != npc->count) {
2661  pl->target_object = npc;
2662  pl->target_object_count = npc->count;
2663  send_target_command(pl);
2664  }
2665 }
2666 
2675 const char *
2677 {
2678  SOFT_ASSERT_RC(pl != NULL, NULL, "pl is NULL");
2679 
2680  if (string_isempty(pl->killer)) {
2681  return NULL;
2682  }
2683 
2684  return pl->killer;
2685 }
2686 
2695 void
2696 player_set_killer (player *pl, const char *killer)
2697 {
2698  SOFT_ASSERT(pl != NULL, "pl is NULL");
2699  SOFT_ASSERT(killer != NULL, "killer is NULL");
2700 
2701  if (pl->killer != NULL) {
2702  efree(pl->killer);
2703  }
2704 
2705  pl->killer = estrdup(killer);
2706 }
2707 
2714 void
2716 {
2717  SOFT_ASSERT(pl != NULL, "pl is NULL");
2718 
2719  if (pl->killer != NULL) {
2720  efree(pl->killer);
2721  pl->killer = NULL;
2722  }
2723 }
2724 
2737 void
2738 player_login (socket_struct *ns, const char *name, struct archetype *at)
2739 {
2740  HARD_ASSERT(ns != NULL);
2741  HARD_ASSERT(name != NULL);
2742  HARD_ASSERT(at != NULL);
2743 
2744  /* Not in the login procedure, can't login. */
2745  if (ns->state != ST_LOGIN) {
2746  return;
2747  }
2748 
2749  player *pl = find_player(name);
2750  if (pl != NULL) {
2751  pl->cs->state = ST_DEAD;
2752  player_logout(pl);
2753  }
2754 
2755  if (ban_check(ns, name)) {
2756  LOG(SYSTEM, "Ban: Banned player tried to login. [%s, %s]",
2757  name, socket_get_addr(ns->sc));
2758  draw_info_send(CHAT_TYPE_GAME, NULL, COLOR_RED, ns,
2759  "Connection refused due to a ban.");
2760  ns->state = ST_ZOMBIE;
2761  return;
2762  }
2763 
2764  char *path = player_make_path(name, "player.dat");
2765  FILE *fp = fopen(path, "rb");
2766  /* This shouldn't happen, because creating a new character creates an
2767  * empty file (to reserve the character name until the player actually
2768  * logs in with the character). */
2769  if (unlikely(fp == NULL)) {
2770  LOG(ERROR, "Failed to open player data file %s: %s",
2771  path, strerror(errno));
2772  ns->state = ST_ZOMBIE;
2773  draw_info_send(CHAT_TYPE_GAME, NULL, COLOR_RED, ns,
2774  "Could not open your player file; contact an "
2775  "administrator.");
2776  goto out;
2777  }
2778 
2779  struct stat statbuf;
2780  /* Similar to above. */
2781  if (unlikely(fstat(fileno(fp), &statbuf) != 0)) {
2782  LOG(ERROR, "Failed to stat player data file %s: %s",
2783  path, strerror(errno));
2784  ns->state = ST_ZOMBIE;
2785  draw_info_send(CHAT_TYPE_GAME, NULL, COLOR_RED, ns,
2786  "Could not stat your player file; contact an "
2787  "administrator.");
2788  goto out;
2789  }
2790 
2791  if (!socket_server_remove(ns)) {
2792  LOG(ERROR, "Failed to remove socket from managed list: %s",
2793  socket_get_str(ns->sc));
2794  goto out;
2795  }
2796 
2797  LOG(INFO, "Login %s from IP %s", name, socket_get_str(ns->sc));
2798 
2799  pl = get_player(NULL);
2800  pl->cs = ns;
2801 
2802  /* Create a new object for the player object data. */
2803  pl->ob = object_get();
2804 
2805 #ifdef SAVE_INTERVAL
2806  pl->last_save_time = time(NULL);
2807 #endif
2808 
2809 #ifdef AUTOSAVE
2810  pl->last_save_tick = pticks;
2811 #endif
2812 
2813  /* If the file is empty, it's a new character. */
2814  if (statbuf.st_size == 0) {
2815  player_create(pl, at, name);
2816  } else {
2817  player_load(pl, fp);
2818  }
2819 
2820  pl->ob->custom_attrset = pl;
2821  pl->ob->speed_left = 0.5;
2822 
2823  object_weight_sum(pl->ob);
2824  living_update_player(pl->ob);
2825  link_player_skills(pl->ob);
2826 
2827  pl->cs->state = ST_PLAYING;
2828 
2829  display_motd(pl->ob);
2830  draw_info_format(COLOR_DK_ORANGE, NULL, "%s has entered the game.", pl->ob->name);
2831  trigger_global_event(GEVENT_LOGIN, pl, socket_get_addr(pl->cs->sc));
2832 
2833  mapstruct *m = ready_map_name(pl->maplevel, NULL, 0);
2834 
2835  if (!m && strncmp(pl->maplevel, "/random/", 8) == 0) {
2836  object_enter_map(pl->ob,
2837  NULL,
2838  ready_map_name(pl->savebed_map, NULL, 0),
2839  pl->bed_x,
2840  pl->bed_y,
2841  true);
2842  } else {
2843  object_enter_map(pl->ob,
2844  NULL,
2845  m,
2846  pl->ob->x,
2847  pl->ob->y,
2848  true);
2849  }
2850 
2851  /* No savebed map yet, initialize it. */
2852  if (*pl->savebed_map == '\0') {
2853  strncpy(pl->savebed_map, pl->maplevel, sizeof(pl->savebed_map) - 1);
2854  pl->savebed_map[sizeof(pl->savebed_map) - 1] = '\0';
2855 
2856  pl->bed_x = pl->ob->x;
2857  pl->bed_y = pl->ob->y;
2858  }
2859 
2860  pl->cs->update_tile = 0;
2861  pl->cs->look_position = 0;
2862  pl->cs->ext_title_flag = 1;
2863 
2864  /* No direction; default to southeast. */
2865  if (!pl->ob->direction) {
2866  pl->ob->direction = SOUTHEAST;
2867  }
2868 
2869  SET_ANIMATION(pl->ob, (NUM_ANIMATIONS(pl->ob) / NUM_FACINGS(pl->ob)) * pl->ob->direction);
2870 
2871  esrv_new_player(pl, pl->ob->weight + pl->ob->carrying);
2872  esrv_send_inventory(pl->ob, pl->ob);
2873  send_quickslots(pl);
2874 
2875  if (pl->ob->map && pl->ob->map->events) {
2876  trigger_map_event(MEVENT_LOGIN, pl->ob->map, pl->ob, NULL, NULL, NULL, 0);
2877  }
2878 
2879 out:
2880  if (fp != NULL) {
2881  fclose(fp);
2882  }
2883 
2884  efree(path);
2885 }
2886 
2894 void
2896 {
2897  HARD_ASSERT(pl != NULL);
2898  SOFT_ASSERT(pl->cs->state == ST_DEAD,
2899  "Player socket state is: %d",
2900  pl->cs->state);
2901 
2902  if (pl->ob->type == DEAD_OBJECT) {
2903  return;
2904  }
2905 
2906  /* Trigger the global LOGOUT event */
2907  trigger_global_event(GEVENT_LOGOUT, pl->ob, socket_get_addr(pl->cs->sc));
2909 
2910  draw_info_format(COLOR_DK_ORANGE, NULL, "%s left the game.", pl->ob->name);
2911 
2912  player_set_killer(pl, "left");
2913  hiscore_check(pl->ob, 1);
2914 
2915  /* Be sure we have closed container when we leave */
2916  container_close(pl->ob, NULL);
2917 
2918  player_save(pl->ob);
2919  account_logout_char(pl->cs, pl);
2920  leave_map(pl->ob);
2921 
2922  LOG(SYSTEM, "Connection: dropping connection: %s (%s)",
2923  socket_get_str(pl->cs->sc),
2924  pl->ob->name);
2925 
2926  /* To avoid problems with inventory window */
2927  pl->ob->type = DEAD_OBJECT;
2928  free_player(pl);
2929 }
2930 
2938 static void
2940 {
2941  HARD_ASSERT(op != NULL);
2942  SOFT_ASSERT(op->type == PLAYER, "Not a player: %s", object_get_str(op));
2943 
2944  player *pl = CONTR(op);
2945  if (pl->tgm) {
2946  /* God mode, no negative effects. */
2947  return;
2948  }
2949 
2950  int max = settings.item_power_factor * op->level;
2951  if (pl->item_power <= max) {
2952  /* If we're within the maximum item power, nothing to do. */
2953  return;
2954  }
2955 
2956  int diff = pl->item_power - max;
2957  int chance = MAX(1, 100 - diff);
2958  if (!rndm_chance(MAX_TICKS + 5 + chance)) {
2959  return;
2960  }
2961 
2962  if (pticks < pl->item_power_effects) {
2963  return;
2964  }
2965 
2966  pl->item_power_effects = pticks + rndm(50, 100) * MAX_TICKS;
2967 
2968  if (diff <= 15 || rndm_chance(diff / 3)) {
2969  static archetype_t *at = NULL;
2970  if (at == NULL) {
2971  at = arch_find("soul_depletion");
2972  SOFT_ASSERT(at != NULL, "Failed to find soul_depletion arch");
2973  }
2974 
2975  object *force = object_find_arch(op, at);
2976  if (force == NULL) {
2977  force = arch_to_object(at);
2978  force->speed_left = -1.0;
2979  force->stats.food = rndm(1, 5) + rndm(0, MAX(1, diff / 3));
2980  force->stats.food *= rndm(2, 6);
2981  force->stats.food *= MAX_TICKS;
2982  force->stats.food *= force->speed;
2983  if (force->stats.food < 0) {
2984  force->stats.food = 0;
2985  }
2986 
2987  force = object_insert_into(force, op, 0);
2988  SOFT_ASSERT(force != NULL, "Failed to insert force into player %s",
2989  object_get_str(op));
2990  }
2991 
2992  /* Try to pick a random protection/stat/etc to decrease. */
2993  int tries = 0;
2994  bool done = false;
2995  while (!done && tries < 5) {
2996  switch (rndm(0, 7)) {
2997  case 0:
2998  case 1:
2999  case 2: {
3000  int num = rndm(0, LAST_PROTECTION - 1);
3001  if (force->protection[num] > -100) {
3002  int prot = force->protection[num];
3003  prot -= rndm(1, 5 + diff / 2);
3004  if (prot < -100) {
3005  prot = -100;
3006  }
3007  force->protection[num] = prot;
3008  done = true;
3009  }
3010 
3011  break;
3012  }
3013 
3014  case 3:
3015  case 4:
3016  case 5: {
3017  int num = rndm(0, NUM_STATS - 1);
3018  int8_t val = get_attr_value(&force->stats, num);
3019  if (val > -MAX_STAT) {
3020  int stat = val - rndm(1, MAX(1, diff / 5));
3021  if (stat < -MAX_STAT) {
3022  stat = -MAX_STAT;
3023  }
3024  set_attr_value(&force->stats, num, stat);
3025  done = true;
3026  }
3027 
3028  break;
3029  }
3030 
3031  case 6:
3032  if (force->stats.ac > -10) {
3033  force->stats.ac--;
3034  done = true;
3035  }
3036 
3037  break;
3038 
3039  case 7:
3040  if (force->stats.wc > -10) {
3041  force->stats.wc--;
3042  done = true;
3043  }
3044 
3045  break;
3046  }
3047 
3048  tries++;
3049  }
3050 
3051  if (done) {
3052  draw_info(COLOR_RED, op, "The combined power of your equipped "
3053  "items begins to sicken your soul!");
3054  living_update(op);
3055  }
3056  } else if (diff > 50 && rndm_chance(MAX(25, 100 - diff))) {
3057  draw_info(COLOR_RED, op, "The combined power of your equipped items "
3058  "begins to consume your soul!");
3059  drain_stat(op);
3060  } else {
3061  draw_info(COLOR_RED, op, "The combined power of your equipped items "
3062  "releases wild magic!");
3063  spell_failure(op, diff);
3064  }
3065 }
3066 
3068 static void
3069 remove_map_func (object *op)
3070 {
3071  HARD_ASSERT(op != NULL);
3072  HARD_ASSERT(op->map != NULL);
3073 
3074  if (op->map->in_memory == MAP_SAVING) {
3075  return;
3076  }
3077 
3078  player *pl = CONTR(op);
3079 
3080  /* Remove player from the map's linked list of players. */
3081  if (pl->map_below != NULL) {
3082  CONTR(pl->map_below)->map_above = pl->map_above;
3083  } else {
3084  op->map->player_first = pl->map_above;
3085  }
3086 
3087  if (pl->map_above != NULL) {
3088  CONTR(pl->map_above)->map_below = pl->map_below;
3089  }
3090 
3091  pl->map_below = pl->map_above = NULL;
3092  pl->update_los = 1;
3093 
3094  /* If the player has a container open that is not in their inventory,
3095  * close it. */
3096  if (pl->container != NULL && pl->container->env != op) {
3097  container_close(op, NULL);
3098  }
3099 }
3100 
3102 static void
3103 process_func (object *op)
3104 {
3105  HARD_ASSERT(op != NULL);
3106 
3107  player *pl = CONTR(op);
3108  int retval;
3109 
3110  while ((retval = handle_newcs_player(pl)) == 1) {
3111  }
3112 
3113  if (retval == -1) {
3114  return;
3115  }
3116 
3117  if (pl->followed_player != NULL) {
3118  player *followed = find_player_sh(pl->followed_player);
3119  if (followed != NULL &&
3120  followed->ob != NULL &&
3121  followed->ob->map != NULL) {
3122  rv_vector rv;
3123 
3124  if (!on_same_map(pl->ob, followed->ob) || (get_rangevector(pl->ob, followed->ob, &rv, 0) && (rv.distance > 4 || rv.distance_z != 0))) {
3125  int space = map_free_spot(followed->ob->map, followed->ob->x, followed->ob->y, 1, SIZEOFFREE2, pl->ob->arch, pl->ob);
3126 
3127  if (space != -1 && followed->ob->x + freearr_x[space] >= 0 && followed->ob->y + freearr_y[space] >= 0 && followed->ob->x + freearr_x[space] < MAP_WIDTH(followed->ob->map) && followed->ob->y + freearr_y[space] < MAP_HEIGHT(followed->ob->map)) {
3128  object_remove(pl->ob, 0);
3129  pl->ob->x = followed->ob->x + freearr_x[space];
3130  pl->ob->y = followed->ob->y + freearr_y[space];
3131  object_insert_map(pl->ob, followed->ob->map, NULL, 0);
3132  }
3133  }
3134  } else {
3135  draw_info_format(COLOR_RED, pl->ob,
3136  "Player %s left.",
3137  pl->followed_player);
3139  }
3140  }
3141 
3142  /* Use the target system to hit our target - don't hit friendly
3143  * objects, ourselves or when we are not in combat mode. */
3144  if (pl->target_object && OBJECT_ACTIVE(pl->target_object) && pl->target_object_count != pl->ob->count && pl->combat && !is_friend_of(pl->ob, pl->target_object)) {
3145  if (global_round_tag >= pl->action_attack) {
3146  /* Now we force target as enemy */
3147  pl->ob->enemy = pl->target_object;
3148  pl->ob->enemy_count = pl->target_object_count;
3149 
3150  if (!OBJECT_VALID(pl->ob->enemy, pl->ob->enemy_count) || pl->ob->enemy->owner == pl->ob) {
3151  pl->ob->enemy = NULL;
3152  } else if (attack_is_melee_range(pl->ob, pl->ob->enemy)) {
3153  if (!OBJECT_VALID(pl->ob->enemy->enemy, pl->ob->enemy->enemy_count)) {
3154  set_npc_enemy(pl->ob->enemy, pl->ob, NULL);
3155  } else {
3156  /* Our target already has an enemy - then note we had
3157  * attacked */
3158  pl->ob->enemy->attacked_by = pl->ob;
3159  pl->ob->enemy->attacked_by_count = pl->ob->count;
3160  pl->ob->enemy->attacked_by_distance = 1;
3161  }
3162 
3163  skill_attack(pl->ob->enemy, pl->ob, 0, NULL);
3164 
3165  pl->action_attack = global_round_tag + pl->ob->weapon_speed;
3166 
3167  pl->action_timer = (float) (pl->action_attack - global_round_tag) / MAX_TICKS;
3168  pl->last_action_timer = 0;
3169  }
3170  }
3171  }
3172 
3173  if (pl->move_path) {
3174  player_path_handle(pl);
3175  }
3176 
3179 
3180 #ifdef AUTOSAVE
3181 
3182  /* Check for ST_PLAYING state so that we don't try to save off when
3183  * the player is logging in. */
3184  if ((pl->last_save_tick + AUTOSAVE) < pticks && pl->cs->state == ST_PLAYING) {
3185  player_save(pl->ob);
3186  pl->last_save_tick = pticks;
3187  hiscore_check(pl->ob, 1);
3188  }
3189 #endif
3190 
3191  /* Update total playing time. */
3192  if (pl->cs->state == ST_PLAYING && time(NULL) > pl->last_stat_time_played) {
3193  pl->last_stat_time_played = time(NULL);
3194 
3195  if (pl->afk) {
3196  pl->stat_time_afk++;
3197  } else {
3198  pl->stat_time_played++;
3199  }
3200  }
3201 
3202  /* Check if our target is still valid - if not, update client. */
3204  send_target_command(pl);
3205  }
3206 }
3207 
3212 {
3213  OBJECT_METHODS(PLAYER)->remove_map_func = remove_map_func;
3214  OBJECT_METHODS(PLAYER)->process_func = process_func;
3215 }
socket_t * sc
Definition: newserver.h:109
#define GT_ONLY_GOOD
Definition: treasure.h:63
void draw_info_full(uint8_t type, const char *name, const char *color, StringBuffer *sb_capture, object *pl, const char *buf)
Definition: info.c:85
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:239
const char * gender_subjective_upper[GENDER_MAX]
Definition: object.c:59
char maplevel[MAX_BUF]
Definition: player.h:153
uint8_t no_chat
Definition: player.h:516
#define FREE_AND_COPY_HASH(_sv_, _nv_)
Definition: global.h:100
#define MONSTER
Definition: define.h:353
void object_destroy(object *ob)
Definition: object.c:1441
bool socket_server_remove(socket_struct *cs)
Definition: server.c:556
static player * get_player(player *p)
Definition: player.c:180
const char *const attack_name[NROFATTACKS]
Definition: attack.c:57
int16_t ac
Definition: living.h:93
#define EXIT
Definition: define.h:312
#define MAP_IN_MEMORY
Definition: map.h:170
uint8_t tls
Definition: player.h:534
void drain_stat(object *op)
Definition: living.c:353
const char * shop_get_cost_string_item(object *op, int mode)
Definition: shop.c:238
player * find_player(const char *plname)
Definition: player.c:96
bool object_is_in_inventory(const object *op, const object *inv)
Definition: object.c:655
uint8_t item_level
Definition: object.h:375
bool container_check_magical(object *op, object *container)
Definition: container.c:53
#define FLAG_INDESTRUCTIBLE
Definition: define.h:1074
uint8_t tgm
Definition: player.h:528
uint8_t tcl
Definition: player.h:525
int get_rangevector(object *op1, object *op2, rv_vector *retval, int flags)
Definition: map.c:2238
uint32_t update_tile
Definition: newserver.h:134
#define FLAG_SYS_OBJECT
Definition: define.h:1243
int direction
Definition: map.h:787
const char * race
Definition: object.h:174
#define OBJECT_ACTIVE(_ob_)
Definition: object.h:551
char datapath[MAX_BUF]
Definition: global.h:343
char * object_get_name_description_s(const object *op, const object *caller)
Definition: item.c:1303
double weapon_speed
Definition: object.h:475
static object * find_marked_object_rec(object *op, object **marked, uint32_t *marked_count)
Definition: player.c:1349
#define FLAG_CURSED
Definition: define.h:1154
void spell_failure(object *caster, int level)
Definition: spell_util.c:1178
#define DEAD_OBJECT
Definition: define.h:275
int purple
Definition: exp.h:55
tag_t attacked_by_count
Definition: object.h:222
void player_path_add(player *pl, mapstruct *map, int16_t x, int16_t y)
Definition: player.c:1061
#define BOOK
Definition: define.h:150
unsigned int distance
Definition: map.h:775
tag_t enemy_count
Definition: object.h:216
#define NUM_FACINGS(ob)
Definition: global.h:300
#define PLAYER_PATH_MAX_FAILS
Definition: player.h:92
void * custom_attrset
Definition: object.h:160
#define GT_NO_VALUE
Definition: treasure.h:67
uint16_t material
Definition: object.h:304
#define MEVENT_DROP
Definition: plugin.h:129
int object_get_gender(const object *op)
Definition: object.c:2877
void display_motd(object *op)
Definition: player.c:136
int trigger_event(int event_type, object *const activator, object *const me, object *const other, const char *msg, int parm1, int parm2, int parm3, int flags)
Definition: plugins.c:510
int16_t x
Definition: player.h:114
object * object_get(void)
Definition: object.c:993
#define MAP_WIDTH(m)
Definition: map.h:120
void treasure_generate(treasure_list_t *treasure_list, object *op, int difficulty, int flags)
Definition: treasure.c:1641
const char * name
Definition: skills.h:120
void player_faction_free(player *pl, player_faction_t *faction)
Definition: player.c:1223
#define SOCKET_VERSION
Definition: config.h:181
material_t materials[NROFMATERIALS]
Definition: material.c:37
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:1691
static void player_create(player *pl, archetype_t *at, const char *name)
Definition: player.c:2551
#define FOR_INV_FINISH()
Definition: define.h:1698
#define MAP_ENTER_X(m)
Definition: map.h:129
#define GEVENT_LOGIN
Definition: plugin.h:153
shstr * path
Definition: map.h:568
#define NROFREALSPELLS
Definition: define.h:664
double speed_left
Definition: object.h:472
mapstruct * get_map_from_coord(mapstruct *m, int *x, int *y)
Definition: map.c:1869
#define FLAG_NO_APPLY
Definition: define.h:1114
int handle_newcs_player(player *pl)
Definition: player.c:347
#define WAND
Definition: define.h:445
uint32_t in_memory
Definition: map.h:627
#define EMERGENCY_MAPPATH
Definition: config.h:134
bool attack_is_melee_range(object *hitter, object *enemy)
Definition: attack.c:1476
player_faction_t * player_faction_find(player *pl, shstr *name)
Definition: player.c:1243
int blue
Definition: exp.h:43
void pick_up(object *op, object *alt, int no_mevent)
Definition: player.c:2019
#define SPELL_DESC_DIRECTION
Definition: spells.h:118
#define SET_ANIMATION(ob, newanim)
Definition: global.h:282
uint8_t item_skill
Definition: object.h:378
shstr * object_get_value(const object *op, const char *const key)
Definition: object.c:2515
struct treasure_list * randomitems
Definition: object.h:231
bool container_close(object *applier, object *op)
Definition: container.c:176
object * object_projectile_stop(object *op, int reason)
#define MAP_PLAYER_NO_SAVE(m)
Definition: map.h:114
void object_copy(object *op, const object *src, bool no_speed)
Definition: object.c:886
int yellow
Definition: exp.h:46
#define SHOP_FLOOR
Definition: define.h:316
object * ob
Definition: player.h:185
#define FLAG_SOULBOUND
Definition: define.h:1044
#define FLAG_SEE_INVISIBLE
Definition: define.h:946
void kill_player(object *op)
Definition: player.c:765
#define LAYER_FLOOR
Definition: map.h:49
uint32_t target_object_count
Definition: player.h:434
object * arch_get(const char *name)
Definition: arch.c:430
static void remove_map_func(object *op)
Definition: player.c:3069
void player_cleanup_name(char *str)
Definition: player.c:1330
int64_t exp
Definition: living.h:69
#define AUTOSAVE
Definition: config.h:178
#define MONEY
Definition: define.h:226
object * object_stack_get_removed(object *op, uint32_t nrof)
Definition: object.c:2078
#define NROFMATERIALS
Definition: material.h:34
Always unapply, never apply.
Definition: define.h:1491
uint32_t object_weight_sum(object *op)
Definition: object.c:523
#define MEVENT_LOGIN
Definition: plugin.h:137
player_faction_t * player_faction_create(player *pl, shstr *name)
Definition: player.c:1202
uint16_t gen_client_sp
Definition: player.h:484
uint8_t item_quality
Definition: object.h:366
#define ST1_CONTAINER_CORPSE
Definition: define.h:568
void object_destruct(object *op)
Definition: object.c:1521
static int absdir(int d)
Definition: define.h:1838
#define IS_LIVE(op)
Definition: define.h:841
void leave_map(object *op)
Definition: main.c:107
#define PLAYER
Definition: define.h:122
bool disease_cure(object *op, object *caster)
Definition: disease.c:507
#define FLAG_NO_DROP
Definition: define.h:1070
object * player_get_dummy(const char *name, const char *host)
Definition: player.c:2583
#define FLAG_PARALYZED
Definition: define.h:876
int8_t get_attr_value(const living *stats, int attr)
Definition: living.c:305
int16_t sp
Definition: living.h:78
void free_newsocket(socket_struct *ns)
Definition: init.c:113
bool object_enter_map(object *op, object *exit, mapstruct *m, int x, int y, bool fixed_pos)
Definition: object.c:2956
void drop(object *op, object *tmp, int no_mevent)
Definition: player.c:2304
char * killer
Definition: player.h:162
uint32_t socket_version
Definition: newserver.h:130
object * skill_ptr[NROFSKILLS]
Definition: player.h:194
double player_faction_reputation(player *pl, shstr *name)
Definition: player.c:1287
object * object_find_arch(object *op, archetype_t *at)
Definition: object.c:2258
int map_free_spot(mapstruct *m, int x, int y, int start, int stop, archetype_t *at, object *op)
Definition: map.c:2942
#define MEVENT_PICK
Definition: plugin.h:131
mempool_struct * pool_player
Definition: player.c:52
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
uint32_t afk
Definition: player.h:443
struct archetype * arch
Definition: object.h:225
struct obj * enemy
Definition: object.h:196
bool init_connection(socket_struct *ns)
Definition: init.c:51
#define POISONING
Definition: define.h:437
const char * gender_subjective[GENDER_MAX]
Definition: object.c:53
void object_remove(object *op, int flags)
Definition: object.c:1623
OBJECT_TYPE_INIT_DEFINE(player)
Definition: player.c:3211
player * find_player_sh(shstr *plname)
Definition: player.c:116
int16_t maxsp
Definition: living.h:81
int32_t hp
Definition: living.h:72
void socket_server_handle_client(player *pl)
Definition: server.c:503
double last_speed
Definition: player.h:268
static int save_life(object *op)
Definition: player.c:391
struct player_path * next
Definition: player.h:108
#define FLAG_STARTEQUIP
Definition: define.h:1004
int manual_apply(object *op, object *tmp, int aflag)
Definition: apply.c:52
char * object_get_name_s(const object *op, const object *caller)
Definition: item.c:398
#define FLESH
Definition: define.h:328
shstr * name
Name of the faction.
Definition: player.h:134
int player_can_carry(object *pl, uint32_t weight)
Definition: player.c:1036
#define EVENT_PICKUP
Definition: plugin.h:83
int first_map_y
Definition: init.c:73
size_t limits[ALLOWED_CHARS_NUM][2]
Definition: global.h:434
object * object_clone(const object *op)
Definition: object.c:2385
#define FLAG_NO_FIX_PLAYER
Definition: define.h:1036
struct obj * chosen_skill
Definition: object.h:210
#define FLAG_DAMNED
Definition: define.h:1158
int16_t y
Definition: object.h:276
int32_t maxhp
Definition: living.h:75
void player_set_talking_to(player *pl, object *npc)
Definition: player.c:2647
player_path * move_path
Definition: player.h:558
#define COST_BUY
Definition: define.h:1370
uint32_t sound
Definition: newserver.h:143
#define COST_TRUE
Definition: define.h:1374
#define FLAG_LIFESAVE
Definition: define.h:1125
void set_npc_enemy(object *npc, object *enemy, rv_vector *rv)
Definition: monster.c:85
int64_t fame
Definition: player.h:257
void player_login(socket_struct *ns, const char *name, struct archetype *at)
Definition: player.c:2738
const char * object_get_str(const object *op)
Definition: object.c:3151
#define FLAG_QUEST_ITEM
Definition: define.h:1233
uint8_t sub_layer
Definition: object.h:408
Do not merge unapplied items.
Definition: define.h:1494
int8_t direction
Definition: object.h:350
Definition: arch.h:40
mapstruct * map
Definition: player.h:111
static void player_load(player *pl, FILE *fp)
Definition: player.c:2478
object * container
Definition: player.h:206
int is_friend_of(object *op, object *obj)
Definition: monster.c:1685
#define EVENT_DEATH
Definition: plugin.h:79
#define SIZEOFFREE2
Definition: define.h:656
int transfer_ob(object *op, int x, int y, int randomly, object *originator, object *trap)
Definition: move.c:237
object * find_skill(object *op, int skillnr)
Definition: player.c:1013
uint32_t map_flags
Definition: map.h:605
#define FLAG_IS_GOOD
Definition: define.h:896
uint8_t item_condition
Definition: object.h:369
struct sound_ambient_match * next
Next match rule in a linked list.
Definition: sound_ambient.c:39
uint32_t weight
Definition: object.h:246
#define POWER_CRYSTAL
Definition: define.h:530
#define FLAG_USE_WEAPON
Definition: define.h:1094
static void pick_up_object(object *pl, object *op, object *tmp, int nrof, int no_mevent)
Definition: player.c:1966
#define FLAG_NO_PICK
Definition: define.h:900
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
struct mapdef * map
Definition: object.h:139
uint8_t ext_title_flag
Definition: newserver.h:161
#define FLAG_IS_INVISIBLE
Definition: define.h:888
void play_sound_player_only(player *pl, int type, const char *filename, int x, int y, int loop, int volume)
Definition: sounds.c:66
#define FLAG_IS_EVIL
Definition: define.h:1040
#define FLAG_UNPAID
Definition: define.h:1251
#define FLAG_IS_NEUTRAL
Definition: define.h:942
void drop_object(object *op, object *tmp, long nrof, int no_mevent)
Definition: player.c:2195
void draw_info_full_format(uint8_t type, const char *name, const char *color, StringBuffer *sb_capture, object *pl, const char *format,...)
Definition: info.c:125
#define MAP_FLAG_PVP
Definition: map.h:431
int16_t dam
Definition: living.h:87
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:277
object * map_above
Definition: player.h:203
uint32_t carrying
Definition: object.h:157
#define GEVENT_BORN
Definition: plugin.h:151
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
struct obj * env
Definition: object.h:130
uint32_t weight_limit[MAX_STAT+1]
Definition: living.c:129
struct archetype * at
Definition: spells.h:198
void player_deinit(void)
Definition: player.c:68
object * arch_to_object(archetype_t *at)
Definition: arch.c:446
uint32_t flags
Definition: spells.h:189
#define move_object(__op, __dir)
Definition: global.h:253
struct obj * below
Definition: object.h:114
long item_power_effects
Next time of item power effects.
Definition: player.h:576
#define CONTAINER
Definition: define.h:493
void shop_sell_item(object *op, object *item)
Definition: shop.c:554
#define INS_NO_WALK_ON
Definition: object.h:570
void object_save(const object *op, FILE *fp)
Definition: object.c:3305
int16_t last_grace
Definition: object.h:316
player_path * move_path_end
Definition: player.h:561
char first_map_path[MAX_BUF]
Definition: init.c:67
void give_initial_items(object *pl, treasure_list_t *items)
Definition: player.c:292
signed char gen_sp
Definition: player.h:286
uint8_t tli
Definition: player.h:531
uint32_t nrof
Definition: object.h:264
int sack_can_hold(object *pl, object *sack, object *op, int nrof)
Definition: player.c:1870
uint16_t gen_hp_remainder
Definition: player.h:475
#define MAP_SAVING
Definition: map.h:176
int orange
Definition: exp.h:49
object * target_object
Definition: player.h:188
uint32_t damage_round_tag
Definition: object.h:149
#define SOUTHEAST
Definition: map.h:843
int cast_heal(object *op, object *caster, int level, object *target, int spell_type)
Definition: spell_effect.c:404
static void player_death_deplete_stats(object *op)
Definition: player.c:693
static void player_item_power_effects(object *op)
Definition: player.c:2939
int red
Definition: exp.h:52
struct pl_player * prev
Definition: player.h:142
#define BOOK_SPELL
Definition: define.h:373
bool need_identify(const object *op)
Definition: item.c:1315
#define MAP_HEIGHT(m)
Definition: map.h:122
#define IS_INVISIBLE(__ob_, __player_)
Definition: define.h:1356
char savebed_map[MAX_BUF]
Definition: player.h:159
#define ARROW
Definition: define.h:170
#define FLAG_FLYING
Definition: define.h:918
#define COST_SELL
Definition: define.h:1372
int trigger_map_event(int event_id, mapstruct *m, object *activator, object *other, object *other2, const char *text, int parm)
Definition: plugins.c:416
object * find_marked_object(object *op)
Definition: player.c:1393
#define GEVENT_PLAYER_DEATH
Definition: plugin.h:157
void hiscore_check(object *op, int quiet)
Definition: hiscore.c:332
signed char digestion
Definition: player.h:277
void player_path_clear(player *pl)
Definition: player.c:1086
uint8_t fails
Definition: player.h:124
int change_skill(object *who, int sk_index)
Definition: skill_util.c:340
uint64_t num
Number of successful updates.
Definition: metaserver.c:43
double speed
Definition: object.h:469
#define HEAD(op)
Definition: object.h:657
uint8_t combat
Definition: player.h:543
void player_save(object *op)
Definition: player.c:2379
tag_t talking_to_count
ID of ::talking_to.
Definition: player.h:572
#define POTION
Definition: define.h:138
uint32_t update_los
Definition: player.h:440
#define MAP_ENTER_Y(m)
Definition: map.h:131
void link_player_skills(object *pl)
Definition: skill_util.c:287
void command_party(object *op, const char *command, char *params)
Definition: party.c:39
int num_cmd_permissions
Definition: player.h:254
#define FLAG_NO_SKILL_IDENT
Definition: define.h:1194
int16_t x
Definition: object.h:273
void put_object_in_sack(object *op, object *sack, object *tmp, long nrof)
Definition: player.c:2122
double reputation
Reputation.
Definition: player.h:135
static void player_do_some_living(object *op)
Definition: player.c:545
int16_t last_eat
Definition: object.h:319
#define FLAG_REMOVED
Definition: define.h:930
int cast_cone(object *op, object *caster, int dir, int strength, int spell_type, struct archetype *spell_arch)
Definition: spell_util.c:687
int16_t wc
Definition: living.h:90
object * object_insert_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1741
uint8_t tsi
Definition: player.h:537
void send_target_command(player *pl)
Definition: request.c:2144
#define OBJECT_PROJECTILE_PICKUP
spell_struct spells[NROFREALSPELLS]
Definition: spellist.h:34
void play_sound_map(mapstruct *map, int type, const char *filename, int x, int y, int loop, int volume)
Definition: sounds.c:159
struct settings_struct settings
Definition: init.c:55
uint32_t weight_limit
Definition: object.h:252
shstr * followed_player
Definition: player.h:165
#define MAXLEVEL
Definition: global.h:221
struct map_event * events
Definition: map.h:583
const char *const lose_msg[NUM_STATS]
Definition: living.c:203
int pvp_area(object *attacker, object *victim)
Definition: player.c:977
#define FLAG_APPLIED
Definition: define.h:1182
int8_t protection[NROFATTACKS]
Definition: object.h:439
living last_stats
Definition: player.h:552
int green
Definition: exp.h:40
int bed_x
Definition: player.h:221
int object_apply_item(object *op, object *applier, int aflags)
Definition: apply.c:110
const char * archname
Definition: spells.h:195
const char * player_get_killer(player *pl)
Definition: player.c:2676
#define MEVENT_EXAMINE
Definition: plugin.h:143
#define NUM_STATS
Definition: living.h:49
#define INS_NO_MERGE
Definition: object.h:564
bool ban_check(socket_struct *ns, const char *name)
Definition: ban.c:441
int on_same_map(object *op1, object *op2)
Definition: map.c:2414
int bed_y
Definition: player.h:224
struct obj * owner
Definition: object.h:207
float last_action_timer
Definition: player.h:265
int skill_attack(object *tmp, object *pl, int dir, char *string)
Definition: skill_util.c:377
static void process_func(object *op)
Definition: player.c:3103
#define NUM_ANIMATIONS(ob)
Definition: global.h:298
uint16_t gen_sp_remainder
Definition: player.h:478
#define GEVENT_LOGOUT
Definition: plugin.h:155
tag_t count
Definition: object.h:142
int get_rangevector_from_mapcoords(mapstruct *map1, int x, int y, mapstruct *map2, int x2, int y2, rv_vector *retval, int flags)
Definition: map.c:2297
living stats
Definition: object.h:481
int distance_z
Definition: map.h:784
void statistics_player_logout(player *pl)
Definition: statistics.c:111
signed char gen_sp_armour
Definition: player.h:280
uint32_t look_position
Definition: newserver.h:149
void player_init(void)
Definition: player.c:58
socket_struct * cs
Definition: player.h:148
int freearr_x[SIZEOFFREE]
Definition: object.c:84
#define SKILL
Definition: define.h:246
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
void esrv_new_player(player *pl, uint32_t weight)
Definition: request.c:337
#define WEAPON
Definition: define.h:178
#define OBJECT_METHODS(type)
int16_t attacked_by_distance
Definition: object.h:289
#define SAVE_MODE
Definition: config.h:155
void send_quickslots(player *pl)
Definition: item.c:870
void player_faction_update(player *pl, shstr *name, double reputation)
Definition: player.c:1263
#define FOOD
Definition: define.h:142
const char * msg
Definition: object.h:183
#define LAST_PROTECTION
Definition: attack.h:94
void trigger_global_event(int event_type, void *parm1, void *parm2)
Definition: plugins.c:455
int living_update(object *op)
Definition: living.c:1661
#define FREE_AND_CLEAR_HASH(_nv_)
Definition: global.h:130
void monster_data_dialogs_remove(object *op, object *activator)
Definition: monster_data.c:254
#define FLAG_MONSTER
Definition: define.h:922
object * player_first
Definition: map.h:594
#define OBJECT_METHOD_OK
party_struct * party
Definition: player.h:555
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
char target_hp
Definition: player.h:218
object * object_find_type(object *op, uint8_t type)
Definition: object.c:2284
#define FLAG_USE_ARMOUR
Definition: define.h:1090
#define FLAG_IS_MAGICAL
Definition: define.h:1129
int8_t Str
Definition: living.h:103
char * player_sanitize_input(char *str)
Definition: player.c:1311
char ** cmd_permissions
Definition: player.h:168
struct obj * inv
Definition: object.h:123
void object_reverse_inventory(object *op)
Definition: object.c:2901
double item_power_factor
Definition: global.h:413
object * map_below
Definition: player.h:200
shstr * custom_name
Definition: object.h:190
int16_t y
Definition: player.h:117
#define FLAG_UNDEAD
Definition: define.h:1012
#define FLAG_WALK_ON
Definition: define.h:904
#define DRINK
Definition: define.h:279
StringBuffer * object_get_description(const object *op, const object *caller, StringBuffer *sb)
Definition: item.c:839
mapstruct * ready_map_name(const char *name, mapstruct *originator, int flags)
Definition: map.c:1584
bool object_can_pick(const object *op, const object *item)
Definition: object.c:2335
player * first_player
Definition: main.c:57
uint16_t gen_client_hp
Definition: player.h:481
static uint16_t get_regen_value(double speed, double rate)
Definition: player.c:527
uint64_t stat_time_played
Definition: player.h:362
signed char gen_hp
Definition: player.h:283
#define FLAG_INV_LOCKED
Definition: define.h:1186
#define MEVENT_PUT
Definition: plugin.h:133
float action_timer
Definition: player.h:262
Definition: map.h:536
#define P_NO_PVP
Definition: map.h:280
object * talking_to
Object the player is talking to.
Definition: player.h:571
void examine(object *op, object *tmp, StringBuffer *sb_capture)
Definition: player.c:1521
long last_combat
Definition: player.h:308
skill_struct skills[NROFSKILLS]
Definition: skillist.h:62
int first_map_x
Definition: init.c:70
void cast_dust(object *op, object *throw_ob, int dir)
Definition: player.c:918
int8_t level
Definition: object.h:347
#define FORCE
Definition: define.h:465
int freearr_y[SIZEOFFREE]
Definition: object.c:99
time_t last_stat_time_played
Definition: player.h:368
struct pl_player * next
Definition: player.h:145
static void examine_living(object *op, object *tmp, StringBuffer *sb_capture)
Definition: player.c:1410
void free_player(player *pl)
Definition: player.c:219
void esrv_send_inventory(object *pl, object *op)
Definition: item.c:557
int64_t value
Definition: object.h:240
#define EVENT_DROP
Definition: plugin.h:81
int16_t item_power
Definition: player.h:499
player * last_player
Definition: main.c:67
unsigned int run_on_dir
Definition: player.h:292
object clone
An object from which to do object_copy().
Definition: arch.h:47
int load_object_buffer(void *buffer, object *op, int map_flags)
Definition: object.c:48633
#define CORPSE
Definition: define.h:534
void player_path_handle(player *pl)
Definition: player.c:1109
void player_clear_killer(player *pl)
Definition: player.c:2715
uint64_t stat_time_afk
Definition: player.h:365
void player_disconnect_all(void)
Definition: player.c:79
archetype_t * arch_find(const char *name)
Definition: arch.c:407
uint64_t stat_hp_regen
Definition: player.h:335
const char * gender_noun[GENDER_MAX]
Definition: object.c:47
static void remove_unpaid_objects(object *op, object *env)
Definition: player.c:452
void player_set_killer(player *pl, const char *killer)
Definition: player.c:2696
uint64_t stat_sp_regen
Definition: player.h:338
#define MAX_STAT
Definition: define.h:47
static int get_regen_amount(uint16_t regen, uint16_t *regen_remainder)
Definition: player.c:486
struct obj * attacked_by
Definition: object.h:199
#define FLAG_IDENTIFIED
Definition: define.h:980
#define MSP_EXTRA_NO_PVP
Definition: map.h:322
int16_t food
Definition: living.h:84
#define IS_ARMOR(op)
Definition: define.h:839
#define SPELL
Definition: define.h:210
uint8_t sub_type
Definition: object.h:363
void player_logout(player *pl)
Definition: player.c:2895
void living_update_player(object *op)
Definition: living.c:614