• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 20683523304

03 Jan 2026 10:03PM UTC coverage: 60.252% (-0.05%) from 60.298%
20683523304

push

github

web-flow
fixed warning with gcc by splitting the if statement (#843)

1 of 1 new or added line in 1 file covered. (100.0%)

1763 existing lines in 14 files now uncovered.

44251 of 73443 relevant lines covered (60.25%)

507583.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.26
/pol-core/pol/getitem.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/12/05 MuadDib:   Added set_decay_after(60) when item is approved to be "gotten" by
5
 *                         line 19x for a dirty fix for instant decay on items dropped in current
6
 *                         decay zone being processed.
7
 * - 2006/03/01 MuadDib:   Added check for != 1 on item-> amount to set_decay_after to help with
8
 *                         bug of changing the initial stack's decay timer.
9
 * - 2008/12/17 MuadDub:   unequipping item now resets item->layer to 0
10
 * - 2009/08/06 MuadDib:   Added gotten_by code for items.
11
 * - 2009/08/16 MuadDib:   Added Slot support to undo_get_item()
12
 * - 2009/09/03 MuadDib:   Changes for account related source file relocation
13
 *                         Changes for multi related source file relocation
14
 */
15

16
#include "getitem.h"
17

18
#include <cstdio>
19

20
#include "../clib/clib_endian.h"
21
#include "../clib/rawtypes.h"
22
#include "containr.h"
23
#include "fnsearch.h"
24
#include "item/item.h"
25
#include "mobile/charactr.h"
26
#include "network/client.h"
27
#include "network/pktdef.h"
28
#include "network/pktin.h"
29
#include "realms/realms.h"
30
#include "reftypes.h"
31
#include "statmsg.h"
32
#include "ufunc.h"
33
#include "uobject.h"
34
#include "uworld.h"
35

36

37
/* How get_item works:
38
   when the client drags an item off the ground,
39
     a GET_ITEM message is generated.
40
   when this item is dropped on the paperdoll,
41
     an EQUIP_ITEM message is generated.
42
   when this item is dropped on the ground,
43
     a DROP_ITEM message is generated
44
   when this item is placed in a container,
45
     a DROP_ITEM message is generated.
46

47

48
   if the item requested cannot be 'gotten', a ITEM_MOVE_FAILURE message is sent (0x27),
49
       reason code 0 (you cannot pick that up).  The client automatically places the
50
       item back where it was. (what do the real servers do?)
51

52
   if on equip, the item cannot be placed, an ITEM_MOVE_FAILURE(0x27,0x05) is sent.
53
       the client just beeps, and does not release the object.
54

55
*/
56
namespace Pol
57
{
58
namespace Core
59
{
60
void GottenItem::handle( Network::Client* client, PKTIN_07* msg )
27✔
61
{
62
  u32 serial = cfBEu32( msg->serial );
27✔
63
  u16 amount = cfBEu16( msg->amount );
27✔
64

65
  Items::Item* item;
66

67
  if ( client->chr->has_gotten_item() )
27✔
68
  {
69
    send_item_move_failure( client, MOVE_ITEM_FAILURE_ALREADY_HOLDING_AN_ITEM );
×
70
    return;
2✔
71
  }
72
  if ( client->chr->dead() )
27✔
73
  {
74
    send_item_move_failure( client, MOVE_ITEM_FAILURE_CANNOT_PICK_THAT_UP );
×
75
    return;
×
76
  }
77
  // try to find the item the client referenced, in all the legal places it might be.
78

79
  bool inRemoteContainer = false, isRemoteContainer = false;
27✔
80
  item = find_legal_item( client->chr, serial, &inRemoteContainer, &isRemoteContainer );
27✔
81
  if ( item == nullptr || isRemoteContainer )
27✔
82
  {
83
    Mobile::Character* owner = nullptr;
3✔
84
    item = find_snoopable_item( serial, &owner );
3✔
85

86
    if ( !item || !owner || !owner->is_equipped( item ) )
3✔
87
    {
88
      send_item_move_failure( client, MOVE_ITEM_FAILURE_CANNOT_PICK_THAT_UP );
1✔
89
      return;
1✔
90
    }
91
    else if ( !client->chr->can_clothe( owner ) )
2✔
92
    {
93
      send_item_move_failure( client, MOVE_ITEM_FAILURE_BELONGS_TO_OTHER );
×
94
      return;
×
95
    }
96
  }
97
  ItemRef itemref( item );  // dave 1/28/3 prevent item from being destroyed before function ends
26✔
98

99
  u8 oldSlot = item->slot_index();
26✔
100

101
  if ( !client->chr->in_range( item, 2 ) && !client->chr->can_moveanydist() )
26✔
102
  {
103
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
×
104
    return;
×
105
  }
106
  if ( !client->chr->realm()->has_los( *client->chr, *( item->toplevel_owner() ) ) )
26✔
107
  {
108
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
109
    return;
×
110
  }
111
  if ( item->inuse() )
26✔
112
  {
113
    send_sysmessage( client, "That is already being used." );
×
114
    send_item_move_failure( client, MOVE_ITEM_FAILURE_CANNOT_PICK_THAT_UP );
×
115
    return;
×
116
  }
117

118
  if ( !client->chr->can_move( item ) )
26✔
119
  {
120
    send_sysmessage( client, "You cannot move that." );
×
121
    send_item_move_failure( client, MOVE_ITEM_FAILURE_CANNOT_PICK_THAT_UP );
×
122
    return;
×
123
  }
124

125
  if ( !item->check_unequiptest_scripts( client->chr ) ||
52✔
126
       !item->check_unequip_script( client->chr ) )
26✔
127
  {
128
    send_sysmessage( client, "You cannot unequip that." );
×
129
    send_item_move_failure( client, MOVE_ITEM_FAILURE_CANNOT_PICK_THAT_UP );
×
130
    return;
×
131
  }
132
  if ( item->orphan() )
26✔
133
    return;
×
134

135
  if ( item->container )
26✔
136
  {
137
    if ( !item->container->check_can_remove_script( client->chr, item,
11✔
138
                                                    UContainer::MoveType::MT_PLAYER, amount ) )
139
    {
140
      send_item_move_failure( client, MOVE_ITEM_FAILURE_CANNOT_PICK_THAT_UP );
1✔
141
      return;
1✔
142
    }
143
    if ( item->orphan() )
10✔
144
      return;
×
145
  }
146

147
  UObject* my_owner = item->toplevel_owner();
25✔
148

149
  send_remove_object_to_inrange( item );
25✔
150

151
  UContainer* orig_container = item->container;
25✔
152
  Pos4d orig_pos = item->pos();  // potential container pos
25✔
153
  Pos4d orig_toppos = item->toplevel_pos();
25✔
154

155
  GottenItem gotten_info{ item, orig_pos };
25✔
156
  if ( orig_container != nullptr )
25✔
157
  {
158
    if ( IsCharacter( orig_container->serial ) )
10✔
159
    {
160
      gotten_info._source = GOTTEN_ITEM_TYPE::GOTTEN_ITEM_EQUIPPED;
2✔
161
      gotten_info._owner_serial = orig_container->serial;
2✔
162
    }
163
    else
164
    {
165
      gotten_info._source = GOTTEN_ITEM_TYPE::GOTTEN_ITEM_IN_CONTAINER;
8✔
166
      gotten_info._owner_serial = orig_container->serial;
8✔
167
    }
168
    gotten_info._slot_index = item->slot_index();
10✔
169
    item->extricate();
10✔
170
  }
171
  else
172
  {
173
    gotten_info._source = GOTTEN_ITEM_TYPE::GOTTEN_ITEM_ON_GROUND;
15✔
174
    remove_item_from_world( item );
15✔
175
  }
176

177
  client->chr->gotten_item( gotten_info );
25✔
178
  item->inuse( true );
25✔
179
  item->gotten_by( client->chr );
25✔
180
  item->setposition( Pos4d( 0, 0, 0, item->realm() ) );  // don't let a boat carry it around
25✔
181

182
  /* Check for moving part of a stack.  Here are the possibilities:
183
      1) Client specified more amount than was in the stack.
184
      2) Client specified exactly what was in the stack.
185
      These are handled identically.  The amount specified is ignored, and
186
      the item is effectively treated as normal unstackable atomic object.
187
      (the stack is moved as a whole)
188
      3) Client specified less than is in the stack.
189
      In this case, a new object is created at the same location as the old object,
190
      with the balance of the amount not removed.
191
      */
192
  if ( item->amount_to_remove_is_partial( amount ) )
25✔
193
  {
194
    Items::Item* new_item = item->slice_stacked_item( amount );
2✔
195
    if ( new_item != nullptr )
2✔
196
    {
197
      new_item->restart_decay_timer();
2✔
198
      if ( orig_container != nullptr )
2✔
199
      {
200
        orig_container->on_remove( client->chr, item, UContainer::MoveType::MT_PLAYER, new_item );
1✔
201
        if ( new_item->orphan() )
1✔
UNCOV
202
          return;
×
203
        // NOTE: we just removed 'item' from its container,
204
        // so there's room for new_item.
205
        if ( !orig_container->can_add_to_slot( oldSlot ) || !item->slot_index( oldSlot ) )
1✔
206
        {
UNCOV
207
          new_item->setposition( client->chr->pos() );
×
208
          add_item_to_world( new_item );
×
209
          register_with_supporting_multi( new_item );
×
210
          move_item( new_item, orig_toppos );
×
211
        }
212
        else
213
        {
214
          orig_container->add( new_item, orig_pos.xy() );
1✔
215
          send_put_in_container_to_inrange( new_item );
1✔
216
        }
217
      }
218
      else
219
      {
220
        new_item->setposition( orig_pos );
1✔
221
        add_item_to_world( new_item );
1✔
222
        register_with_supporting_multi( new_item );
1✔
223
        send_item_to_inrange( new_item );
1✔
224
      }
225
    }
226
  }
227
  else
228
  {
229
    if ( orig_container )
23✔
230
    {
231
      orig_container->on_remove( client->chr, item );
9✔
232
      if ( item->orphan() )
9✔
233
        return;
×
234
    }
235
    item->set_decay_after( 60 );
23✔
236
  }
237

238
  if ( orig_container )
25✔
239
  {
240
    orig_container->increv_send_object_recursive();
10✔
241
  }
242

243
  // FIXME : Are these all the possibilities for sources and updating, correctly?
244
  if ( gotten_info._source == GOTTEN_ITEM_TYPE::GOTTEN_ITEM_ON_GROUND )
25✔
245
  {
246
    // Item was on the ground, so we ONLY need to update the character's weight
247
    // to the client.
248
    send_full_statmsg( client, client->chr );
15✔
249
  }
250
  else if ( gotten_info._source == GOTTEN_ITEM_TYPE::GOTTEN_ITEM_EQUIPPED )
10✔
251
  {
252
    // Item was equipped, let's send the full update for ar and statmsg.
253
    if ( auto chr = system_find_mobile( gotten_info._owner_serial ) )
2✔
254
      chr->refresh_ar();
2✔
255
  }
256
  else if ( my_owner->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
8✔
257
  {
258
    // Toplevel owner was a container (not a character). Only update weight.
259
    send_full_statmsg( client, client->chr );
8✔
260
  }
261
  else if ( ( my_owner->ismobile() ) && my_owner->serial != client->chr->serial )
×
262
  {
263
    // Toplevel was a mob. Make sure mob was not us. If it's not, send update to weight.
264
    send_full_statmsg( client, client->chr );
×
265
  }
266
}
26✔
267

268

269
GottenItem::GottenItem( Items::Item* item, const Core::Pos4d& pos )
25✔
270
    : _item( item ), _pos( pos.xyz() ), _realm( pos.realm()->name() ), _owner_serial( 0 )
25✔
271
{
272
}
25✔
273
/*
274
  undo:
275
  when a client issues a get_item command, the item is moved into gotten_items.
276
  all other clients are told to delete it, so they no longer have access to it.
277
  when the client finally tries to do something with it, if that fails, the
278
  object must be put back where it was.
279
  Sometimes, (ie trying to equip) the client just beeps, and keeps holding onto
280
  the item.  In those cases, this function is not called but rather the item
281
  is replaced in gotten_items, for a later EQUIP_ITEM message.
282
  */
283

284
void GottenItem::undo( Mobile::Character* chr )
13✔
285
{
286
  if ( !_item )
13✔
287
    return;
5✔
288
  // item needs to be returned to where it was..  either on
289
  // the ground, or equipped on the current character,
290
  // or in whatever it used to be in.
291
  ItemRef itemref( _item );  // dave 1/28/3 prevent item from being destroyed before function ends
13✔
292
  _item->restart_decay_timer();  // MuadDib: moved to top to help with instant decay.
13✔
293
  _item->gotten_by( nullptr );
13✔
294
  Realms::Realm* realm = nullptr;
13✔
295
  if ( _source == GOTTEN_ITEM_TYPE::GOTTEN_ITEM_EQUIPPED )
13✔
296
  {
297
    if ( auto equipped_chr = system_find_mobile( _owner_serial ) )
1✔
298
    {
299
      if ( equipped_chr->equippable( _item ) && _item->check_equiptest_scripts( equipped_chr ) &&
2✔
300
           _item->check_equip_script( equipped_chr, false ) )
1✔
301
      {
302
        if ( _item->orphan() )
1✔
303
          return;
×
304
        // is it possible the character doesn't exist? no, it's my character doing the undoing.
305
        equipped_chr->equip( _item );
1✔
306
        send_wornitem_to_inrange( equipped_chr, _item );
1✔
307
        return;
1✔
308
      }
309
    }
310

311
    if ( _item->orphan() )
×
312
      return;
×
313
    _source = GOTTEN_ITEM_TYPE::GOTTEN_ITEM_IN_CONTAINER;
×
314
    _owner_serial = 0;
×
315
    _pos = chr->pos().xyz();
×
316
    realm = chr->realm();
×
317
  }
318

319
  if ( _source == GOTTEN_ITEM_TYPE::GOTTEN_ITEM_IN_CONTAINER )
12✔
320
  {
321
    // First attempt to place the item in the player's backpack.
322
    UContainer* container = nullptr;
4✔
323
    if ( !_item->no_drop() )
4✔
324
    {
325
      container = chr->backpack();
2✔
326
      if ( !container || !container->can_add( *_item ) ||
3✔
327
           !container->can_insert_add_item( chr, UContainer::MT_PLAYER, _item ) )
1✔
328
        container = nullptr;
1✔
329
      if ( _item->orphan() )
2✔
330
        return;
×
331
    }
332
    // Attempt to put it back in the original container.
333
    if ( !container &&
7✔
334
         ( !Core::settingsManager.ssopt.undo_get_item_drop_here || _item->no_drop() ) )
3✔
335
    {
336
      auto* orig_obj = system_find_object( _owner_serial );
3✔
337
      if ( orig_obj && orig_obj->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
3✔
338
      {
339
        if ( _item->no_drop() || chr->can_moveanydist() ||
4✔
340
             !Core::settingsManager.ssopt.undo_get_item_enable_range_check ||
5✔
341
             chr->in_range( orig_obj, Core::settingsManager.ssopt.default_accessible_range ) )
1✔
342
        {
343
          container = static_cast<UContainer*>( orig_obj );
3✔
344
          if ( !container->can_add( *_item ) ||
6✔
345
               !container->can_insert_add_item( chr, UContainer::MT_PLAYER, _item ) )
3✔
346
            container = nullptr;
×
347
        }
348
      }
349
      if ( _item->orphan() )
3✔
350
        return;
×
351
    }
352

353
    // No drop item has not returned to original container, place the item in the player's backpack.
354
    if ( !container && _item->no_drop() )
4✔
355
    {
356
      container = chr->backpack();
×
357
      if ( !container || !container->can_add( *_item ) ||
×
358
           !container->can_insert_add_item( chr, UContainer::MT_PLAYER, _item ) )
×
359
        container = nullptr;
×
360
      if ( _item->orphan() )
×
361
        return;
×
362
    }
363

364
    if ( container )
4✔
365
    {
366
      u8 newSlot = _slot_index ? _slot_index : 1;
4✔
367
      if ( container->can_add_to_slot( newSlot ) && _item->slot_index( newSlot ) )
4✔
368
      {
369
        if ( container->is_legal_posn( _pos.xy() ) )
4✔
370
        {
371
          container->add( _item, _pos.xy() );
4✔
372
        }
373
        else
374
        {
UNCOV
375
          container->add_at_random_location( _item );
×
376
        }
377
        update_item_to_inrange( _item );
4✔
378
        container->on_insert_add_item( chr, UContainer::MT_PLAYER, _item );
4✔
379
        return;
4✔
380
      }
381
    }
UNCOV
382
    _pos = chr->pos3d();
×
383
    realm = chr->realm();
×
384
  }
385

386
  if ( Core::settingsManager.ssopt.undo_get_item_drop_here )
8✔
387
  {
388
    _pos = chr->pos3d();
1✔
389
    realm = chr->realm();
1✔
390
  }
391
  else if ( !chr->can_moveanydist() )
7✔
392
  {
393
    if ( Core::settingsManager.ssopt.undo_get_item_enable_range_check )
7✔
394
    {
395
      realm = Core::find_realm( _realm );
1✔
396
      if ( realm == nullptr ||
2✔
397
           !chr->in_range( Pos4d( _pos, realm ),
2✔
398
                           Core::settingsManager.ssopt.default_accessible_range ) )
1✔
399
      {
400
        _pos = chr->pos3d();
1✔
401
        realm = chr->realm();
1✔
402
      }
403
    }
404
  }
405

406
  // The (local variable) `realm` will be set in case of error from above.
407
  if ( realm == nullptr )
8✔
408
  {
409
    // Try finding realm from (instance member) `_realm` string.
410
    realm = Core::find_realm( _realm );
6✔
411

412
    // If the realm is not found, set it to the position of the character.
413
    if ( realm == nullptr )
6✔
414
    {
415
      realm = chr->realm();
1✔
416
      _pos = chr->pos3d();
1✔
417
    }
418
  }
419

420
  // Last resort - put it on the ground, to players feet in case of error from above.
421
  // Recursively update realm if it changed.
422
  if ( _item->pos().realm() != realm && _item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
8✔
423
  {
424
    Core::UContainer* cont = static_cast<Core::UContainer*>( _item );
1✔
425
    cont->for_each_item( Core::setrealm, realm );
1✔
426
  }
427

428
  _item->setposition( Pos4d( _pos, realm ) );
8✔
429
  _item->container = nullptr;
8✔
430
  // 12-17-2008 MuadDib added to clear item.layer properties.
431
  _item->layer = 0;
8✔
432

433
  add_item_to_world( _item );
8✔
434

435
  register_with_supporting_multi( _item );
8✔
436
  send_item_to_inrange( _item );
8✔
437

438
  // Need to explicitly send remove_object to chr if realms mismatch. Scenario:
439
  //
440
  // 1. pick up item on ground in `britannia`
441
  // 2. switch to `shadow-britannia`
442
  // 3. drop item in invalid location
443
  //
444
  // Because of the failure in step 3, core will send packet 0x27 Item Move
445
  // Failure via `Core::send_item_move_failure`, and the client will
446
  // automatically place it back at old `x,y` location. The item is in
447
  // `britannia`, but character is still in `shadow-britannia`, so the item will
448
  // appear on the ground in the client.
449
  if ( chr->realm() != realm )
8✔
450
  {
451
    send_remove_object( chr->client, _item );
1✔
452
  }
453
}
13✔
454
}  // namespace Core
455
}  // namespace Pol
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc