Crossfire Server, Trunk  1.75.0
container.cpp
Go to the documentation of this file.
1 /*
2  CrossFire, A Multiplayer game for X-windows
3 
4  Copyright (C) 2007 Mark Wedel & Crossfire Development Team
5  Copyright (C) 1992 Frank Tore Johansen
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21  The authors can be reached via e-mail at crossfire-devel@real-time.com
22 */
23 
28 #include <global.h>
29 #include <ob_methods.h>
30 #include <ob_types.h>
31 #include <sounds.h>
32 #include <sproto.h>
33 
34 static method_ret container_type_move_on(object *trap, object *victim, object *originator);
35 
36 bool sack_race_can_contain(const object *sack, const object *ob) {
37  if (!sack->race)
38  // no restriction
39  return true;
40 
41  if (!ob->race)
42  // sack restricted, but object not defined
43  return false;
44 
45  return csv_contains(sack->race, ob->race);
46 }
47 
66 int sack_can_hold(const object *pl, const object *sack, const object *op, uint32_t nrof) {
67  char name[MAX_BUF];
68  query_name(sack, name, MAX_BUF);
69 
70  if (!QUERY_FLAG(sack, FLAG_APPLIED)) {
72  "The %s is not active.",
73  name);
74  return 0;
75  }
76  if (sack == op) {
78  "You can't put the %s into itself.",
79  name);
80  return 0;
81  }
82  if (sack->race
83  && (!sack_race_can_contain(sack, op) || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type))) {
85  "You can put only %s into the %s.",
86  sack->race, name);
87  return 0;
88  }
89  if (op->type == SPECIAL_KEY && sack->slaying && op->slaying) {
91  "You can't put the key into %s.",
92  name);
93  return 0;
94  }
95  if (sack->weight_limit) {
96  int32_t new_weight;
97 
98  new_weight = sack->carrying+(nrof ? nrof : 1)
99  /* Most non-containers should have op->carrying == 0. Icecubes, however, will not,
100  * and we need to handle those.
101  * Neila Hawkins 2021-01-21
102  */
103  *(op->weight+(op->type == CONTAINER ? op->carrying*op->stats.Str : op->carrying))
104  *(100-sack->stats.Str)/100;
105  if (new_weight > sack->weight_limit) {
107  "That won't fit in the %s!",
108  name);
109  return 0;
110  }
111  }
112  /* All other checks pass, must be OK */
113  return 1;
114 }
115 
127 int set_object_face_main(object *op) {
128  const Face *newface = op->arch->clone.face;
129  sstring saved = object_get_value(op, "face_closed");
130 
131  if (op->more)
133 
134  if (saved)
135  newface = try_find_face(saved, newface);
136  if (newface && op->face != newface) {
137  op->face = newface;
138  return TRUE;
139  }
140  return FALSE;
141 }
142 
154 static int set_object_face_other(object *op) {
155  sstring custom;
156  const Face *newface = NULL;
157  object *head = op->head ? op->head : op;
158 
159  if (op->more)
161 
162  if (head->face && head->other_arch && head->other_arch->clone.face)
163  newface = head->other_arch->clone.face;
164 
165  if (op->face != op->arch->clone.face) {
166  /* object has a custom face, save it so it gets correctly restored later. */
167  object_set_value(op, "face_closed", op->face->name, 1);
168  }
169 
170  custom = object_get_value(head, "face_opened");
171  if (custom)
172  newface = try_find_face(custom, newface);
173  if (newface && op->face != newface) {
174  op->face = newface;
175  return TRUE;
176  }
177  return FALSE;
178 }
179 
203 int apply_container(object *op, object *sack, int aflags) {
204  char name_sack[MAX_BUF], name_tmp[MAX_BUF];
205  object *tmp = op->container;
206 
207  if (op->type != PLAYER)
208  return 0; /* This might change */
209 
210  if (sack == NULL || sack->type != CONTAINER) {
211  LOG(llevError, "apply_container: '%s' tried to apply %s, which is not a container\n", op->name, sack ? sack->name : "(null)");
212  return 0;
213  }
214 
215  if (sack->head)
216  sack = sack->head;
217 
218  query_name(sack, name_sack, MAX_BUF);
219 
220  if ( aflags == AP_APPLY || aflags == AP_UNAPPLY || aflags == AP_OPEN ) {
221  // What if the container isn't in the player's inventory?
222  if ( sack->env != op ) {
224  "Not in your inventory: %s",
225  name_sack);
226  return 0;
227  }
228  }
229  if ( aflags == AP_APPLY ) {
230  // What if the container is open? Make it just ready!
231  if ( op->container == sack ) {
232  op->container = NULL;
233  if (op->contr != NULL)
234  op->contr->socket->container_position = 0;
235  CLEAR_FLAG(sack, FLAG_APPLIED);
237  "You readied %s.",
238  name_sack);
239  SET_FLAG(sack, FLAG_APPLIED);
240  // FIXME: This is not flipping the face!
241  if (set_object_face_main(sack)) // change image to closed
243  else
244  esrv_update_item(UPD_FLAGS, op, sack);
245  return 0;
246  }
247  // What if the container is already applied? Do nothing!
248  if (QUERY_FLAG(sack, FLAG_APPLIED)) {
250  "Already readied %s.",
251  name_sack);
252  return 0;
253  }
254  // What if the container is closed? Same as no aflags.
255  aflags = AP_NULL;
256  }
257 
258  if ( aflags == AP_OPEN ) {
259  // What if the container is already open?
260  if ( op->container == sack ) {
262  "Already opened %s.",
263  name_sack);
264  return 0;
265  }
266  // Set the container as applied and then proceed as if no special flags
267  SET_FLAG(sack, FLAG_APPLIED);
268  aflags = AP_NULL;
269  }
270 
271  if ( aflags == AP_UNAPPLY ) {
272  // If not open, two cases:
273  if ( op->container != sack ) {
274  if (QUERY_FLAG(sack, FLAG_APPLIED)) {
275  CLEAR_FLAG(sack, FLAG_APPLIED);
277  "You closed %s.",
278  name_sack);
279  esrv_update_item(UPD_FLAGS, op, sack);
280  return 0;
281  }
282  else {
284  "Already closed %s.",
285  name_sack);
286  return 0;
287  }
288  }
289  // open; same as no special flags
290  aflags = AP_NULL;
291  }
292 
293  /* If we have a currently open container, then it needs
294  * to be closed in all cases if we are opening this one up.
295  * We then fall through if appropriate for opening the new
296  * container.
297  */
298  if (op->container && QUERY_FLAG(op->container, FLAG_APPLIED) &&
299  (QUERY_FLAG(sack, FLAG_APPLIED) || sack->env != op) )
300  {
301  tag_t tmp_tag = op->container->count;
302 
303  if (op->container->env != op) { /* if container is on the ground */
304  object *part = op->container->head ? op->container->head : op->container;
305  while (part) {
306  part->move_off = 0;
307  part = part->more;
308  }
309  }
310 
311  /* Query name before the close event, as the container could be destroyed. */
312  query_name(op->container, name_tmp, MAX_BUF);
313 
314  if (events_execute_object_event(tmp, EVENT_CLOSE, op, NULL, NULL, SCRIPT_FIX_ALL) != 0)
315  return 1;
316 
319  "You close %s.",
320  name_tmp);
321 
322  op->container = NULL;
323  if (op->contr != NULL)
324  op->contr->socket->container_position = 0;
325 
326  /* The container may have been destroyed by the event handler. */
327  if (!object_was_destroyed(tmp, tmp_tag)) {
328  CLEAR_FLAG(tmp, FLAG_APPLIED);
329  if (set_object_face_main(tmp))
331  else
332  esrv_update_item(UPD_FLAGS, op, tmp);
333  }
334  if (tmp == sack)
335  return 1;
336  }
337 
338  /* If the player is trying to open it (which he must be doing
339  * if we got here), and it is locked, check to see if player
340  * has the equipment to open it.
341  */
342 
343  if (sack->slaying) { /* it's locked */
344  tmp = find_key(op, op, sack);
345  if (tmp) {
346  query_name(tmp, name_tmp, MAX_BUF);
349  "You unlock %s with %s.",
350  name_sack, name_tmp);
351  } else {
354  "You don't have the key to unlock %s.",
355  name_sack);
356  return 0;
357  }
358  }
359 
360  /* By the time we get here, we have made sure any other container
361  * has been closed and if this is a locked container, the player
362  * has the key to open it.
363  */
364 
365  /* There are really two cases - the sack is either on the ground,
366  * or the sack is part of the player's inventory. If on the ground,
367  * we assume that the player is opening it, since if it was being
368  * closed, that would have been taken care of above.
369  */
370 
371 
372  if (sack->env != op) {
373  /* Hypothetical case - the player is trying to open a sack
374  * that belongs to someone else. This normally should not
375  * happen, but a misbehaving client/player could
376  * try to do it, so let's handle it gracefully.
377  */
378  if (sack->env) {
380  "You can't open %s",
381  name_sack);
382  return 0;
383  }
384 
385  if (sack->nrof > 1) {
386  object *left = object_split(sack, sack->nrof-1, NULL, 0);
387 
388  object_insert_in_map_at(left, sack->map, NULL, INS_NO_MERGE, sack->x, sack->y);
389  /* recompute the name so it's nice */
390  query_name(sack, name_sack, MAX_BUF);
391  }
392 
393  /* set it so when the player walks off, we can unapply the sack */
394  {
395  object *part = sack->head ? sack->head : sack;
396  while (part) {
397  part->move_off = MOVE_ALL;
398  part = part->more;
399  }
400  }
401 
402  CLEAR_FLAG(sack, FLAG_APPLIED);
404  "You open %s.",
405  name_sack);
406  SET_FLAG(sack, FLAG_APPLIED);
407  op->container = sack;
408  if (op->contr != NULL)
409  op->contr->socket->container_position = 0;
410 
411  if (set_object_face_other(sack))
413  else
414  esrv_update_item(UPD_FLAGS, op, sack);
415  esrv_send_inventory(op, sack);
416  } else { /* sack is in players inventory */
417  if (QUERY_FLAG(sack, FLAG_APPLIED)) { /* readied sack becoming open */
418  CLEAR_FLAG(sack, FLAG_APPLIED);
420  "You open %s.",
421  name_sack);
422  SET_FLAG(sack, FLAG_APPLIED);
423  op->container = sack;
424  if (op->contr != NULL)
425  op->contr->socket->container_position = 0;
426 
427  if (set_object_face_other(sack))
429  else
430  esrv_update_item(UPD_FLAGS, op, sack);
431  esrv_send_inventory(op, sack);
432  } else {
433  object *left = NULL;
434 
435  if (sack->nrof > 1)
436  left = object_split(sack, sack->nrof-1, NULL, 1);
437 
438  CLEAR_FLAG(sack, FLAG_APPLIED);
440  "You readied %s.",
441  name_sack);
442  SET_FLAG(sack, FLAG_APPLIED);
443  esrv_update_item(UPD_FLAGS, op, sack);
444 
445  if (left) {
446  object_insert_in_ob(left, sack->env);
447  esrv_send_item(op, left);
448  }
449  }
450  }
451  return 1;
452 }
453 
454 method_ret container_apply(object *op, object *applier, int aflags) {
455  if (applier->type == PLAYER)
456  (void)apply_container(applier, op, aflags);
457  return METHOD_OK;
458 }
459 
460 method_ret close_container_apply(object *op, object *applier, int aflags) {
461  if (applier->type == PLAYER)
462  (void)apply_container(applier, op->env, aflags);
463  return METHOD_OK;
464 }
465 
466 void container_describe(const object *op, const object *observer, int use_media_tags, char *buf, size_t size) {
467  legacy_ob_describe(op, observer, use_media_tags, buf, size);
468  if (((op->env && op->env->container == op) || (!op->env && QUERY_FLAG(op, FLAG_APPLIED))))
469  strncat(buf, " (open)", size);
470 }
471 
472 method_ret container_examine(const object *op, const object *, int, char *buf, size_t size) {
473  if (op->race != NULL) {
474  if (op->weight_limit && op->stats.Str < 100)
475  snprintf(buf, size, "It can hold only %s and its weight limit is %.1f kg.", op->race, op->weight_limit/(10.0*(100-op->stats.Str)));
476  else
477  snprintf(buf, size, "It can hold only %s.", op->race);
478  } else
479  if (op->weight_limit && op->stats.Str < 100)
480  snprintf(buf, size, "Its weight limit is %.1f kg.", op->weight_limit/(10.0*(100-op->stats.Str)));
481  return METHOD_OK;
482 }
483 
493 }
501 static method_ret container_type_move_on(object *trap, object *victim, object *originator) {
502  if (common_pre_ob_move_on(trap, victim, originator) == METHOD_ERROR)
503  return METHOD_OK;
504  if (victim->type == PLAYER)
505  (void)apply_container(victim, trap, AP_NULL);
506  common_post_ob_move_on(trap, victim, originator);
507  return METHOD_OK;
508 }
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Checks if an object still exists.
Definition: object.h:70
Face::name
sstring name
Face name, as used by archetypes and such.
Definition: face.h:19
Face
New face structure - this enforces the notion that data is face by face only - you can not change the...
Definition: face.h:14
PLAYER
@ PLAYER
Definition: object.h:112
close_container_apply
method_ret close_container_apply(object *op, object *applier, int aflags)
Definition: container.cpp:460
global.h
UPD_FACE
#define UPD_FACE
Definition: newclient.h:321
sack_race_can_contain
bool sack_race_can_contain(const object *sack, const object *ob)
Definition: container.cpp:36
CLOSE_CON
@ CLOSE_CON
Eneq((at)csd.uu.se): Id for close_container archetype.
Definition: object.h:234
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:369
socket_struct::container_position
uint16_t container_position
Start of container contents to send to client.
Definition: newserver.h:119
register_examine
void register_examine(int ob_type, examine_func method)
Registers the examine method for the given type.
Definition: ob_types.cpp:89
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:371
AP_APPLY
#define AP_APPLY
Item is to be applied.
Definition: define.h:558
FALSE
#define FALSE
Definition: compat.h:14
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Sends inventory of a container.
Definition: item.cpp:316
object::arch
struct archetype * arch
Pointer to archetype.
Definition: object.h:424
container_apply
method_ret container_apply(object *op, object *applier, int aflags)
Definition: container.cpp:454
apply_container
int apply_container(object *op, object *sack, int aflags)
Handle apply on containers.
Definition: container.cpp:203
register_apply
void register_apply(int ob_type, apply_func method)
Registers the apply method for the given type.
Definition: ob_types.cpp:62
METHOD_OK
#define METHOD_OK
Definition: ob_methods.h:15
object::x
int16_t x
Definition: object.h:335
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
AP_UNAPPLY
#define AP_UNAPPLY
Item is to be remvoed.
Definition: define.h:559
register_move_on
void register_move_on(int ob_type, move_on_func method)
Registers the move_on method for the given type.
Definition: ob_types.cpp:98
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
object::count
tag_t count
Unique object number for this object.
Definition: object.h:307
csv_contains
bool csv_contains(std::string list, std::string item, std::string delim=",")
Split list by comma, then see if item matches anything in the list.
Definition: c_object.cpp:302
SCRIPT_FIX_ALL
#define SCRIPT_FIX_ALL
Definition: global.h:380
legacy_ob_describe
void legacy_ob_describe(const object *op, const object *observer, int use_media_tags, char *buf, size_t size)
Describes an object, seen by a given observer.
Definition: legacy_describe.cpp:39
MSG_TYPE_COMMAND_ERROR
#define MSG_TYPE_COMMAND_ERROR
Bad syntax/can't use command.
Definition: newclient.h:533
events_execute_object_event
int events_execute_object_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Execute an event on the specified object.
Definition: events.cpp:309
buf
StringBuffer * buf
Definition: readable.cpp:1565
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.cpp:2842
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:408
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
esrv_send_item
void esrv_send_item(object *pl, object *op)
Sends item's info to player.
Definition: main.cpp:354
container_describe
void container_describe(const object *op, const object *observer, int use_media_tags, char *buf, size_t size)
Definition: container.cpp:466
object::carrying
int32_t carrying
How much weight this object contains.
Definition: object.h:377
object::y
int16_t y
Position in the map for this object.
Definition: object.h:335
set_object_face_other
static int set_object_face_other(object *op)
Makes an object's face the other_arch face, supposed to be the "opened" one.
Definition: container.cpp:154
MOVE_ALL
#define MOVE_ALL
Mask of all movement types.
Definition: define.h:389
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
AP_NULL
#define AP_NULL
Nothing specific.
Definition: define.h:557
MSG_TYPE_APPLY_SUCCESS
#define MSG_TYPE_APPLY_SUCCESS
Was able to apply object.
Definition: newclient.h:607
query_name
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.cpp:594
archetype::clone
object clone
An object from which to do object_copy()
Definition: object.h:487
object::weight_limit
int32_t weight_limit
Weight-limit of object.
Definition: object.h:376
common_pre_ob_move_on
method_ret common_pre_ob_move_on(object *trap, object *victim, object *originator)
Definition: common_apply.cpp:35
CONTAINER
@ CONTAINER
Definition: object.h:236
container_examine
method_ret container_examine(const object *op, const object *, int, char *buf, size_t size)
Definition: container.cpp:472
object::face
const Face * face
Face with colors.
Definition: object.h:341
SPECIAL_KEY
@ SPECIAL_KEY
Definition: object.h:129
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:319
EVENT_CLOSE
#define EVENT_CLOSE
Container closed.
Definition: events.h:32
AP_OPEN
#define AP_OPEN
Item is a container to be fully opened.
Definition: define.h:560
living::food
int32_t food
How much food in stomach.
Definition: living.h:48
tag_t
uint32_t tag_t
Object tag, unique during the whole game.
Definition: object.h:14
sproto.h
find_key
object * find_key(object *pl, object *container, object *door)
We try to find a key for the door as passed.
Definition: player.cpp:2482
object::race
sstring race
Human, goblin, dragon, etc.
Definition: object.h:326
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Same as object_insert_in_map() except it handle separate coordinates and do a clean job preparing mul...
Definition: object.cpp:2085
object::other_arch
struct archetype * other_arch
Pointer used for various things - mostly used for what this objects turns into or what this object cr...
Definition: object.h:425
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
object::head
object * head
Points to the main object of a large body.
Definition: object.h:304
object::weight
int32_t weight
Attributes of the object.
Definition: object.h:375
set_object_face_main
int set_object_face_main(object *op)
Makes an object's face the main face, which is supposed to be the "closed" one.
Definition: container.cpp:127
method_ret
char method_ret
Define some standard return values for callbacks which don't need to return any other results.
Definition: ob_methods.h:14
ob_types.h
sounds.h
container_type_move_on
static method_ret container_type_move_on(object *trap, object *victim, object *originator)
Move on this Container object.
Definition: container.cpp:501
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
object::slaying
sstring slaying
Which race to do double damage to.
Definition: object.h:327
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
object::env
object * env
Pointer to the object which is the environment.
Definition: object.h:301
sstring
const typedef char * sstring
Definition: sstring.h:2
object_split
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
object_split(ob,nr) splits up ob into two parts.
Definition: object.cpp:2622
FLAG_APPLIED
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:222
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
Definition: main.cpp:359
object_get_value
sstring object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4331
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:370
INS_NO_MERGE
#define INS_NO_MERGE
Don't try to merge with other items.
Definition: object.h:580
object::container
object * container
Current container being used.
Definition: object.h:299
object::move_off
MoveType move_off
Move types affected moving off this space.
Definition: object.h:440
try_find_face
const Face * try_find_face(const char *name, const Face *error)
Definition: assets.cpp:290
METHOD_ERROR
#define METHOD_ERROR
Definition: ob_methods.h:17
object::nrof
uint32_t nrof
Number of objects.
Definition: object.h:342
player::socket
socket_struct * socket
Socket information for this player.
Definition: player.h:109
ob_methods.h
object::stats
living stats
Str, Con, Dex, etc.
Definition: object.h:378
object_set_value
int object_set_value(object *op, const char *key, const char *value, int add_key)
Updates the key in op to value.
Definition: object.cpp:4484
object::more
object * more
Pointer to the rest of a large body of objects.
Definition: object.h:303
TRUE
#define TRUE
Definition: compat.h:11
sack_can_hold
int sack_can_hold(const object *pl, const object *sack, const object *op, uint32_t nrof)
Check if an item op can be put into a sack.
Definition: container.cpp:66
init_type_container
void init_type_container(void)
Initializer for the CONTAINER object type.
Definition: container.cpp:487
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Applying objects.
Definition: newclient.h:412
common_post_ob_move_on
void common_post_ob_move_on(object *trap, object *victim, object *originator)
Definition: common_apply.cpp:67
MSG_TYPE_APPLY_ERROR
#define MSG_TYPE_APPLY_ERROR
Definition: newclient.h:605
register_describe
void register_describe(int ob_type, describe_func method)
Registers the describe method for the given type.
Definition: ob_types.cpp:80
living::Str
int8_t Str
Definition: living.h:36
MSG_TYPE_APPLY_UNAPPLY
#define MSG_TYPE_APPLY_UNAPPLY
Unapply an object.
Definition: newclient.h:606