Atrinik Server  4.0
container.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 
32 #include <global.h>
33 #include <plugin.h>
34 #include <arch.h>
35 #include <player.h>
36 #include <object.h>
37 #include <object_methods.h>
38 #include <container.h>
39 #include <rune.h>
40 #include <key.h>
41 
52 bool
53 container_check_magical (object *op, object *container)
54 {
55  if (op->type == CONTAINER && container->type == CONTAINER &&
56  !DBL_EQUAL(op->weapon_speed, 1.0) &&
57  !DBL_EQUAL(container->weapon_speed, 1.0)) {
58  return true;
59  }
60 
61  return false;
62 }
63 
73 static void
74 container_open (object *applier, object *op)
75 {
76  HARD_ASSERT(applier != NULL);
77  HARD_ASSERT(op != NULL);
78 
79  /* Safety. */
80  if (applier->type != PLAYER) {
81  return;
82  }
83 
84  /* Safety. */
85  if (op->attacked_by && op->attacked_by->type != PLAYER) {
86  op->attacked_by = NULL;
87  }
88 
89  /* Check for quest containers. */
90  if (HAS_EVENT(op, EVENT_QUEST)) {
91  FOR_INV_PREPARE(op, inv) {
92  if (inv->type == QUEST_CONTAINER) {
93  quest_handle(applier, inv);
94  }
95  } FOR_INV_FINISH();
96  }
97 
98  player *pl = CONTR(applier);
99  pl->container = op;
100  pl->container_count = op->count;
101  pl->container_above = op->attacked_by;
102 
103  /* Already opened. */
104  if (op->attacked_by != NULL) {
105  CONTR(op->attacked_by)->container_below = applier;
106  } else {
107  /* Not open yet. */
108  SET_FLAG(op, FLAG_APPLIED);
109 
110  if (op->other_arch != NULL) {
111  op->face = op->other_arch->clone.face;
114  esrv_update_item(UPD_FACE | UPD_ANIM | UPD_FLAGS, op);
115  } else {
116  esrv_update_item(UPD_FLAGS, op);
117  }
118 
120 
121  FOR_INV_PREPARE(op, tmp) {
122  if (tmp->type == RUNE) {
123  rune_spring(tmp, applier);
124  } else if (tmp->type == MONSTER) {
125  object_remove(tmp, 0);
126  tmp->x = op->x;
127  tmp->y = op->y;
128 
129  int i = map_free_spot(op->map,
130  tmp->x,
131  tmp->y,
132  0,
133  SIZEOFFREE1, tmp->arch,
134  tmp);
135  if (i != -1) {
136  tmp->x += freearr_x[i];
137  tmp->y += freearr_y[i];
138  }
139 
140  tmp = object_insert_map(tmp, op->map, tmp, 0);
141  if (tmp != NULL) {
143  char *name = object_get_base_name_s(op, applier);
144  char *monster_name = object_get_base_name_s(tmp, applier);
145  draw_info_format(COLOR_WHITE, applier,
146  "A %s jumps out of the %s!",
147  monster_name,
148  name);
149  efree(name);
150  efree(monster_name);
151  }
152  }
153  } FOR_INV_FINISH();
154  }
155 
156  esrv_send_inventory(applier, op);
157  pl->container_below = NULL;
158  op->attacked_by = applier;
159  op->attacked_by_count = applier->count;
160 }
161 
175 bool
176 container_close (object *applier, object *op)
177 {
178  HARD_ASSERT(applier != NULL || op != NULL);
179 
180  if (applier != NULL && applier->type == PLAYER) {
181  player *pl = CONTR(applier);
182 
183  /* No container, nothing to do. */
184  if (pl->container == 0) {
185  return false;
186  }
187 
188  /* Make sure the object is valid. */
189  if (!OBJECT_VALID(pl->container, pl->container_count)) {
190  pl->container = NULL;
191  pl->container_count = 0;
192  return false;
193  }
194 
195  /* Only applier left, go ahead and close the container. */
196  if (pl->container_below == NULL && !pl->container_above) {
197  return container_close(NULL, pl->container);
198  }
199 
200  /* The applier is at the beginning of the linked list. */
201  if (pl->container_below == NULL) {
204  CONTR(pl->container_above)->container_below = NULL;
205  } else {
206  /* Elsewhere in the list. */
207  CONTR(pl->container_below)->container_above = pl->container_above;
208 
209  if (pl->container_above != NULL) {
210  CONTR(pl->container_above)->container_below =
211  pl->container_below;
212  }
213  }
214 
215  esrv_close_container(applier, pl->container);
216  pl->container_above = NULL;
217  pl->container_below = NULL;
218  pl->container = NULL;
219  pl->container_count = 0;
220  } else if (op != NULL) {
221  if (op->attacked_by == NULL) {
222  return false;
223  }
224 
226 
227  if (op->other_arch != NULL) {
228  op->face = op->arch->clone.face;
229  op->animation_id = op->arch->clone.animation_id;
231  esrv_update_item(UPD_FACE | UPD_ANIM | UPD_FLAGS, op);
232  } else {
233  esrv_update_item(UPD_FLAGS, op);
234  }
235 
237 
238  for (object *tmp = op->attacked_by, *next; tmp != NULL; tmp = next) {
239  next = CONTR(tmp)->container_above;
240 
241  CONTR(tmp)->container = NULL;
242  CONTR(tmp)->container_count = 0;
243  CONTR(tmp)->container_below = NULL;
244  CONTR(tmp)->container_above = NULL;
245  esrv_close_container(tmp, op);
246  }
247 
248  op->attacked_by = NULL;
249  op->attacked_by_count = 0;
250 
251  if (op->env != NULL && op->env->type == PLAYER && OBJECT_IS_AMMO(op)) {
252  living_update(op->env);
253  }
254 
255  return true;
256  }
257 
258  return false;
259 }
260 
262 static int
263 apply_func (object *op, object *applier, int aflags)
264 {
265  HARD_ASSERT(op != NULL);
266  HARD_ASSERT(applier != NULL);
267 
268  if (applier->type != PLAYER) {
270  }
271 
272  object *container = CONTR(applier)->container;
273  if (op == NULL || op->type != CONTAINER ||
274  (container != NULL && container->type != CONTAINER)) {
276  }
277 
278  /* Already opened container, so close it, even if the player wants to
279  * open another container. */
280  if (container != NULL) {
281  /* Trigger the CLOSE event. */
283  applier,
284  container,
285  NULL,
286  NULL,
287  0,
288  0,
289  0,
290  0) != 0) {
291  return OBJECT_METHOD_OK;
292  }
293 
294  char *name = object_get_base_name_s(container, applier);
295  if (container_close(applier, container)) {
296  draw_info_format(COLOR_WHITE, applier, "You close %s.", name);
297  } else {
298  draw_info_format(COLOR_WHITE, applier, "You leave %s.", name);
299  }
300  efree(name);
301 
302  /* Applied the one we just closed, no need to go on. */
303  if (container == op) {
304  return OBJECT_METHOD_OK;
305  }
306  }
307 
308  int basic_aflag = aflags & APPLY_BASIC_FLAGS;
309 
310  if (basic_aflag == APPLY_ALWAYS_UNAPPLY) {
311  if (QUERY_FLAG(op, FLAG_APPLIED)) {
312  if (OBJECT_IS_AMMO(op)) {
313  object_apply_item(op, applier, aflags);
314  } else {
315  char *name = object_get_base_name_s(op, applier);
316  draw_info_format(COLOR_WHITE, applier, "You unready %s.", name);
317  efree(name);
319  }
320  }
321 
322  return OBJECT_METHOD_OK;
323  }
324 
325  /* If the player is trying to open it (which he must be doing if we
326  * got here), and it is locked, check to see if player has the means
327  * to open it. */
328  if (op->slaying != NULL || op->stats.maxhp != 0) {
329  if (op->sub_type == ST1_CONTAINER_NORMAL) {
330  /* Locked container, try to open it with a key. */
331  char *name = object_get_base_name_s(op, applier);
332  object *tmp = key_match(applier, op);
333  if (tmp != NULL) {
334  if (tmp->type == KEY) {
335  char *key_name = object_get_base_name_s(tmp, applier);
336  draw_info_format(COLOR_WHITE, applier,
337  "You unlock %s with %s.", name, key_name);
338  efree(key_name);
339  } else if (tmp->type == FORCE) {
340  draw_info_format(COLOR_WHITE, applier,
341  "The %s is unlocked for you.", name);
342  }
343 
344  efree(name);
345  } else {
346  draw_info_format(COLOR_WHITE, applier,
347  "You don't have the key to unlock %s.", name);
348  efree(name);
349  return OBJECT_METHOD_OK;
350  }
351  } else {
353  !party_can_open_corpse(applier, op)) {
354  /* Party corpse. */
355  return OBJECT_METHOD_OK;
356  } else if (op->sub_type == ST1_CONTAINER_CORPSE_player &&
357  op->slaying != applier->name) {
358  /* Normal player-only corpse. */
359  draw_info(COLOR_WHITE, applier, "It's not your bounty.");
360  return OBJECT_METHOD_OK;
361  }
362  }
363  }
364 
365  char *name = object_get_base_name_s(op, applier);
366 
367  if (op->env != applier) {
368  /* If in inventory of some other object other than the applier,
369  * can't open it. */
370  if (op->env != NULL) {
371  draw_info_format(COLOR_WHITE, applier, "You can't open %s.", name);
372  efree(name);
373  return OBJECT_METHOD_OK;
374  }
375 
376  draw_info_format(COLOR_WHITE, applier, "You open %s.", name);
377  container_open(applier, op);
378 
379  /* Handle party corpses. */
380  if (op->slaying != NULL && op->sub_type == ST1_CONTAINER_CORPSE_party) {
381  party_handle_corpse(applier, op);
382  }
383  } else {
384  /* If it's readied, open it, otherwise ready it. */
385  if (QUERY_FLAG(op, FLAG_APPLIED)) {
386  draw_info_format(COLOR_WHITE, applier, "You open %s.", name);
387  container_open(applier, op);
388  } else {
389  if (OBJECT_IS_AMMO(op)) {
390  object_apply_item(op, applier, aflags);
391  } else {
392  draw_info_format(COLOR_WHITE, applier, "You ready %s.", name);
393  SET_FLAG(op, FLAG_APPLIED);
394 
396  esrv_update_item(UPD_FLAGS, op);
397  }
398  }
399  }
400 
401  efree(name);
402 
403  /* If it's a corpse and it has not been searched before, add to
404  * player's statistics. */
405  if ((op->sub_type == ST1_CONTAINER_CORPSE_party ||
408  CONTR(applier)->stat_corpses_searched++;
409  }
410 
411  /* Only after actually readying/opening the container we know more
412  * about it. */
414 
415  return OBJECT_METHOD_OK;
416 }
417 
419 static void
420 remove_inv_func (object *op)
421 {
422  container_close(NULL, op);
423 }
424 
426 static void
427 remove_map_func (object *op)
428 {
429  remove_inv_func(op);
430 }
431 
436 {
437  OBJECT_METHODS(CONTAINER)->apply_func = apply_func;
438  OBJECT_METHODS(CONTAINER)->remove_inv_func = remove_inv_func;
439  OBJECT_METHODS(CONTAINER)->remove_map_func = remove_map_func;
440 }
#define MONSTER
Definition: define.h:353
#define UP_OBJ_FACE
Definition: object.h:537
static void remove_inv_func(object *op)
Definition: container.c:420
void esrv_close_container(object *pl, object *op)
Definition: item.c:524
bool container_check_magical(object *op, object *container)
Definition: container.c:53
#define EVENT_CLOSE
Definition: plugin.h:95
static void remove_map_func(object *op)
Definition: container.c:427
double weapon_speed
Definition: object.h:475
tag_t attacked_by_count
Definition: object.h:222
#define QUEST_CONTAINER
Definition: define.h:489
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
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:1691
#define FOR_INV_FINISH()
Definition: define.h:1698
#define SIZEOFFREE1
Definition: define.h:654
static int apply_func(object *op, object *applier, int aflags)
Definition: container.c:263
bool container_close(object *applier, object *op)
Definition: container.c:176
void quest_handle(object *op, object *quest)
Definition: quest.c:377
const char * slaying
Definition: object.h:180
#define HAS_EVENT(ob, event)
Definition: plugin.h:179
Always unapply, never apply.
Definition: define.h:1491
OBJECT_TYPE_INIT_DEFINE(container)
Definition: container.c:435
#define PLAYER
Definition: define.h:122
int map_free_spot(mapstruct *m, int x, int y, int start, int stop, archetype_t *at, object *op)
Definition: map.c:2942
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
struct archetype * arch
Definition: object.h:225
struct archetype * other_arch
Definition: object.h:228
object * container_below
Definition: player.h:212
uint32_t container_count
Definition: player.h:311
void object_remove(object *op, int flags)
Definition: object.c:1623
#define FLAG_BEEN_APPLIED
Definition: define.h:934
int16_t y
Definition: object.h:276
int32_t maxhp
Definition: living.h:75
#define ST1_CONTAINER_NORMAL
Definition: define.h:566
void object_update(object *op, int action)
Definition: object.c:1117
char * object_get_base_name_s(const object *op, const object *caller)
Definition: item.c:534
object * container
Definition: player.h:206
struct sound_ambient_match * next
Next match rule in a linked list.
Definition: sound_ambient.c:39
#define ST1_CONTAINER_CORPSE_party
Definition: define.h:582
uint16_t animation_id
Definition: object.h:322
struct mapdef * map
Definition: object.h:139
#define EVENT_QUEST
Definition: plugin.h:97
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
struct obj * env
Definition: object.h:130
int party_can_open_corpse(object *pl, object *corpse)
Definition: party.c:230
#define CONTAINER
Definition: define.h:493
#define ST1_CONTAINER_CORPSE_player
Definition: define.h:577
void living_update_monster(object *op)
Definition: living.c:1174
#define OBJECT_METHOD_UNHANDLED
int16_t x
Definition: object.h:273
object * object_insert_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1741
#define FLAG_APPLIED
Definition: define.h:1182
int object_apply_item(object *op, object *applier, int aflags)
Definition: apply.c:110
Used internally.
Definition: define.h:1492
tag_t count
Definition: object.h:142
living stats
Definition: object.h:481
void esrv_update_item(int flags, object *op)
Definition: item.c:639
int freearr_x[SIZEOFFREE]
Definition: object.c:84
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
#define OBJECT_METHODS(type)
int living_update(object *op)
Definition: living.c:1661
#define RUNE
Definition: define.h:522
#define OBJECT_METHOD_OK
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
#define KEY
Definition: define.h:198
void rune_spring(object *op, object *victim)
Definition: rune.c:48
#define SET_ANIMATION_STATE(ob)
Definition: global.h:287
void party_handle_corpse(object *pl, object *corpse)
Definition: party.c:424
static void container_open(object *applier, object *op)
Definition: container.c:74
New_Face * face
Definition: object.h:234
object * container_above
Definition: player.h:209
#define FORCE
Definition: define.h:465
int freearr_y[SIZEOFFREE]
Definition: object.c:99
#define OBJECT_IS_AMMO(_ob)
Definition: object.h:833
void esrv_send_inventory(object *pl, object *op)
Definition: item.c:557
object clone
An object from which to do object_copy().
Definition: arch.h:47
struct obj * attacked_by
Definition: object.h:199
object * key_match(object *op, const object *locked)
Definition: key.c:48
uint8_t sub_type
Definition: object.h:363