• 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

25.28
/pol-core/pol/dropitem.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/05/28 Shinigami: now u can call open_trade_window without item and splitted (for use with
5
 * uo::SecureTradeWin)
6
 *                         place_item_in_secure_trade_container splitted (for use with
7
 * uo::MoveItemToSecureTradeWin)
8
 * - 2005/06/01 Shinigami: return_traded_items - added realm support
9
 * - 2005/11/28 MuadDib:   Added 2 more send_trade_statuses() for freshing of chr and droped_on
10
 *                         in sending of secure trade windows. This was per packet checks on
11
 *                         OSI, and works in newer clients. Unable to test in 2.0.0
12
 * - 2009/07/20 MuadDib:   Slot checks added where can_add() is called.
13
 * - 2009/07/23 MuadDib:   updates for new Enum::Packet Out ID
14
 * - 2009/08/06 MuadDib:   Added gotten_by code for items.
15
 * - 2009/08/09 MuadDib:   Refactor of Packet 0x25 for naming convention
16
 * - 2009/08/16 MuadDib:   find_giveitem_container(), removed passert, made it return nullptr to
17
 * reject move instead of a crash. Added slot support to find_giveitem_container()
18
 * - 2009/09/03 MuadDib:   Changes for account related source file relocation
19
 *                         Changes for multi related source file relocation
20
 * - 2009/09/06 Turley:    Changed Version checks to bitfield client->ClientType
21
 * - 2009/11/19 Turley:    removed sysmsg after CanInsert call (let scripter handle it) - Tomi
22
 *
23
 * @note FIXME: Does STW use slots with KR or newest 2d? If so, we must do slot checks there too.
24
 */
25

26

27
#include <cstdio>
28
#include <string>
29

30
#include "../bscript/berror.h"
31
#include "../clib/clib_endian.h"
32
#include "../clib/logfacility.h"
33
#include "../clib/passert.h"
34
#include "../clib/random.h"
35
#include "../clib/rawtypes.h"
36
#include "../plib/objtype.h"
37
#include "../plib/systemstate.h"
38
#include "base/position.h"
39
#include "containr.h"
40
#include "eventid.h"
41
#include "fnsearch.h"
42
#include "globals/uvars.h"
43
#include "item/item.h"
44
#include "los.h"
45
#include "mobile/charactr.h"
46
#include "mobile/npc.h"
47
#include "multi/multi.h"
48
#include "network/client.h"
49
#include "network/clientio.h"
50
#include "network/packetdefs.h"
51
#include "network/packethelper.h"
52
#include "network/packets.h"
53
#include "network/pktboth.h"
54
#include "network/pktdef.h"
55
#include "network/pktin.h"
56
#include "realms/realm.h"
57
#include "reftypes.h"
58
#include "statmsg.h"
59
#include "storage.h"
60
#include "syshook.h"
61
#include "systems/suspiciousacts.h"
62
#include "ufunc.h"
63
#include "uobject.h"
64
#include "uoscrobj.h"
65
#include "uworld.h"
66

67
namespace Pol
68
{
69
namespace Core
70
{
71
void send_trade_statuses( Mobile::Character* chr );
72

73
bool place_item_in_container( Network::Client* client, Items::Item* item, UContainer* cont,
5✔
74
                              const Pos2d& pos, u8 slotIndex )
75
{
76
  ItemRef itemref( item );
5✔
77
  if ( ( cont->serial == item->serial ) || is_a_parent( cont, item->serial ) )
5✔
78
  {
79
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
80
    return false;
×
81
  }
82

83
  if ( !cont->can_add( *item ) )
5✔
84
  {
85
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
86
    send_sysmessage( client, "That item is too heavy for the container or the container is full." );
×
87
    return false;
×
88
  }
89
  if ( !cont->can_insert_add_item( client->chr, UContainer::MT_PLAYER, item ) )
5✔
90
  {
91
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
92
    return false;
×
93
  }
94

95
  if ( item->orphan() )
5✔
96
    return true;
×
97

98
  if ( !cont->can_add_to_slot( slotIndex ) )
5✔
99
  {
100
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
101
    send_sysmessage( client, "The container has no free slots available!" );
×
102
    return false;
×
103
  }
104
  if ( !item->slot_index( slotIndex ) )
5✔
105
  {
106
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
107
    send_sysmessage( client, "The container has no free slots available!" );
×
108
    return false;
×
109
  }
110

111
  item->set_dirty();
5✔
112

113
  client->pause();
5✔
114
  send_remove_object_to_inrange( item );
5✔
115

116
  cont->add( item, pos );
5✔
117
  cont->restart_decay_timer();
5✔
118
  if ( !item->orphan() )
5✔
119
  {
120
    send_put_in_container_to_inrange( item );
5✔
121

122
    cont->on_insert_add_item( client->chr, UContainer::MT_PLAYER, item );
5✔
123
  }
124

125
  item->increv_send_object_recursive();
5✔
126

127
  client->restart();
5✔
128
  return true;
5✔
129
}
5✔
130

131
bool do_place_item_in_secure_trade_container( Network::Client* client, Items::Item* item,
132
                                              UContainer* cont, Mobile::Character* dropon,
133
                                              const Pos2d& pos, u8 move_type );
UNCOV
134
bool place_item_in_secure_trade_container( Network::Client* client, Items::Item* item,
×
135
                                           const Pos2d& pos )
136
{
UNCOV
137
  UContainer* cont = client->chr->trade_container();
×
UNCOV
138
  Mobile::Character* dropon = client->chr->trading_with.get();
×
139
  if ( dropon == nullptr || dropon->client == nullptr )
×
140
  {
141
    send_sysmessage( client, "Unable to complete trade" );
×
UNCOV
142
    return false;
×
143
  }
144
  if ( gamestate.system_hooks.can_trade )
×
145
  {
146
    if ( !gamestate.system_hooks.can_trade->call( new Module::ECharacterRefObjImp( client->chr ),
×
UNCOV
147
                                                  new Module::ECharacterRefObjImp( dropon ),
×
148
                                                  new Module::EItemRefObjImp( item ) ) )
×
149
    {
150
      send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
UNCOV
151
      return false;
×
152
    }
153
  }
UNCOV
154
  if ( !cont->can_add( *item ) )
×
155
  {
156
    send_sysmessage( client, "That's too heavy to trade." );
×
UNCOV
157
    return false;
×
158
  }
159
  if ( !cont->can_insert_add_item( client->chr, UContainer::MT_PLAYER, item ) )
×
160
  {
161
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
UNCOV
162
    return false;
×
163
  }
164

165
  // FIXME : Add Grid Index Default Location Checks here.
166
  // Remember, if index fails, move to the ground. That is, IF secure trade uses
167
  // grid index.
168

UNCOV
169
  return do_place_item_in_secure_trade_container( client, item, cont, dropon, pos, 0 );
×
170
}
171

UNCOV
172
Bscript::BObjectImp* place_item_in_secure_trade_container( Network::Client* client,
×
173
                                                           Items::Item* item )
174
{
UNCOV
175
  UContainer* cont = client->chr->trade_container();
×
UNCOV
176
  Mobile::Character* dropon = client->chr->trading_with.get();
×
177
  if ( dropon == nullptr || dropon->client == nullptr )
×
178
  {
179
    return new Bscript::BError( "Unable to complete trade" );
×
180
  }
181
  if ( gamestate.system_hooks.can_trade )
×
182
  {
183
    if ( !gamestate.system_hooks.can_trade->call( new Module::ECharacterRefObjImp( client->chr ),
×
UNCOV
184
                                                  new Module::ECharacterRefObjImp( dropon ),
×
185
                                                  new Module::EItemRefObjImp( item ) ) )
×
186
    {
187
      send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
UNCOV
188
      return new Bscript::BError( "Could not insert item into container." );
×
189
    }
190
  }
UNCOV
191
  if ( !cont->can_add( *item ) )
×
192
  {
193
    return new Bscript::BError( "That's too heavy to trade." );
×
194
  }
195
  if ( !cont->can_insert_add_item( client->chr, UContainer::MT_CORE_MOVED, item ) )
×
196
  {
197
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
UNCOV
198
    return new Bscript::BError( "Could not insert item into container." );
×
199
  }
200

201
  // FIXME : Add Grid Index Default Location Checks here.
202
  // Remember, if index fails, move to the ground.
203

UNCOV
204
  if ( do_place_item_in_secure_trade_container( client, item, cont, dropon,
×
UNCOV
205
                                                cont->get_random_location(), 1 ) )
×
206
    return new Bscript::BLong( 1 );
×
207
  else
208
    return new Bscript::BError( "Something went wrong with trade window." );
×
209
}
210

UNCOV
211
bool do_place_item_in_secure_trade_container( Network::Client* client, Items::Item* item,
×
212
                                              UContainer* cont, Mobile::Character* dropon,
213
                                              const Pos2d& pos, u8 move_type )
214
{
UNCOV
215
  client->pause();
×
216

217
  client->chr->trade_accepted( false );
×
UNCOV
218
  dropon->trade_accepted( false );
×
219
  send_trade_statuses( client->chr );
×
220

221
  send_remove_object_to_inrange( item );
×
222

223
  cont->add( item, pos );
×
224

UNCOV
225
  send_put_in_container( client, item );
×
226
  send_put_in_container( dropon->client, item );
×
227

228
  if ( move_type == 1 )
×
229
    cont->on_insert_add_item( client->chr, UContainer::MT_CORE_MOVED, item );
×
230
  else
231
    cont->on_insert_add_item( client->chr, UContainer::MT_PLAYER, item );
×
232

UNCOV
233
  send_trade_statuses( client->chr );
×
234
  send_trade_statuses( dropon->client->chr );
×
235

236
  client->restart();
×
237
  return true;
×
238
}
239

240
bool add_item_to_stack( Network::Client* client, Items::Item* item, Items::Item* target_item )
1✔
241
{
242
  // TJ 3/18/3: Only called (so far) from place_item(), so it's only ever from
243
  // a player; this means that there's no need to worry about passing
244
  // UContainer::MT_CORE_* constants to the can_insert function (yet). :)
245

246
  ItemRef itemref( item );
1✔
247
  if ( ( !target_item->stackable() ) || ( !target_item->can_add_to_self( *item, false ) ) ||
2✔
248
       ( target_item->container &&
1✔
249
         !target_item->container->can_insert_increase_stack(
1✔
250
             client->chr, UContainer::MT_PLAYER, target_item, item->getamount(), item ) ) )
1✔
251
  {
UNCOV
252
    send_sysmessage( client, "Could not add item to stack." );
×
UNCOV
253
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
254

255
    return false;
×
256
  }
257

258
  if ( item->orphan() )
1✔
UNCOV
259
    return false;
×
260

261
  /* At this point, we know:
262
       the object types match
263
       the combined stack isn't 'too large'
264
       We don't know: (FIXME)
265
       if a container that the target_item is in will overfill from this
266
       */
267

268
  send_remove_object_to_inrange( item );
1✔
269

270
  u16 amtadded = item->getamount();
1✔
271

272
  target_item->add_to_self( item );
1✔
273
  update_item_to_inrange( target_item );
1✔
274

275
  if ( target_item->container )
1✔
276
  {
277
    target_item->container->on_insert_increase_stack( client->chr, UContainer::MT_PLAYER,
1✔
278
                                                      target_item, amtadded );
279
    target_item->container->restart_decay_timer();
1✔
280
  }
281
  return true;
1✔
282
}
1✔
283

284
bool place_item( Network::Client* client, Items::Item* item, u32 target_serial, const Pos2d& pos,
12✔
285
                 u8 slotIndex )
286
{
287
  Items::Item* target_item = find_legal_item( client->chr, target_serial );
12✔
288

289
  if ( target_item == nullptr && client->chr->is_trading() )
12✔
290
  {
UNCOV
291
    UContainer* cont = client->chr->trade_container();
×
UNCOV
292
    if ( target_serial == cont->serial )
×
293
    {
294
      if ( item->no_drop() )
×
295
      {
UNCOV
296
        send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
297
        return false;
×
298
      }
299
      return place_item_in_secure_trade_container( client, item, pos );
×
300
    }
301
  }
302

303
  if ( target_item == nullptr )
12✔
304
  {
UNCOV
305
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
UNCOV
306
    return false;
×
307
  }
308

309
  if ( !client->chr->in_range( target_item, 2 ) && !client->chr->can_moveanydist() )
12✔
310
  {
311
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
6✔
312
    return false;
6✔
313
  }
314
  if ( !client->chr->realm()->has_los( *client->chr, *target_item->toplevel_owner() ) )
6✔
315
  {
UNCOV
316
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
UNCOV
317
    return false;
×
318
  }
319

320

321
  if ( target_item->isa( UOBJ_CLASS::CLASS_ITEM ) )
6✔
322
  {
323
    if ( item->no_drop() )
1✔
324
    {
UNCOV
325
      if ( target_item->container == nullptr || !target_item->container->no_drop_exception() )
×
326
      {
UNCOV
327
        send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
328
        return false;
×
329
      }
330
    }
331
    return add_item_to_stack( client, item, target_item );
1✔
332
  }
333
  else if ( target_item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
5✔
334
  {
335
    if ( item->no_drop() && !( static_cast<UContainer*>( target_item )->no_drop_exception() ) )
5✔
336
    {
337
      send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
1✔
338
      return false;
1✔
339
    }
340
    return place_item_in_container( client, item, static_cast<UContainer*>( target_item ), pos,
4✔
341
                                    slotIndex );
4✔
342
  }
343
  else
344
  {
345
    // UNTESTED CLIENT_HOLE?
UNCOV
346
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
347

UNCOV
348
    return false;
×
349
  }
350
}
351

352
bool drop_item_on_ground( Network::Client* client, Items::Item* item, const Pos3d& pos )
7✔
353
{
354
  if ( item->no_drop() )
7✔
355
  {
UNCOV
356
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
UNCOV
357
    return false;
×
358
  }
359

360
  Mobile::Character* chr = client->chr;
7✔
361

362
  Multi::UMulti* multi;
363
  short newz;
364
  if ( !chr->pos().in_range( pos, 2 ) && !client->chr->can_moveanydist() )
7✔
365
  {
366
    SuspiciousActs::DropItemOutOfRange( client, item->serial );
3✔
367
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
3✔
368
    return false;
3✔
369
  }
370

371
  if ( !chr->realm()->dropheight( pos, client->chr->z(), &newz, &multi ) )
4✔
372
  {
UNCOV
373
    SuspiciousActs::DropItemOutAtBlockedLocation( client, item->serial, pos );
×
UNCOV
374
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
×
UNCOV
375
    return false;
×
376
  }
377

378
  LosObj tgt( Pos4d( pos.xy(), static_cast<s8>( newz ), chr->realm() ) );
4✔
379
  if ( !chr->realm()->has_los( *client->chr, tgt ) )
4✔
380
  {
UNCOV
381
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
UNCOV
382
    return false;
×
383
  }
384

385
  item->set_dirty();
4✔
386
  item->restart_decay_timer();
4✔
387
  auto oldrealm = item->realm();  // TODO POS get rid of all the realm for_each
4✔
388
  item->setposition( tgt.pos() );
4✔
389
  if ( oldrealm != chr->realm() )
4✔
390
  {
UNCOV
391
    if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
×
392
    {
UNCOV
393
      UContainer* cont = static_cast<UContainer*>( item );
×
394
      cont->for_each_item( setrealm, (void*)chr->realm() );
×
395
    }
396
  }
397
  item->container = nullptr;
4✔
398
  item->reset_slot();
4✔
399
  add_item_to_world( item );
4✔
400
  if ( multi != nullptr )
4✔
401
  {
UNCOV
402
    multi->register_object( item );
×
403
  }
404

405
  send_item_to_inrange( item );
4✔
406
  return true;
4✔
407
}
408

UNCOV
409
UContainer* find_giveitem_container( Items::Item* item_to_add, u8 slotIndex )
×
410
{
UNCOV
411
  StorageArea* area = gamestate.storage.create_area( "GivenItems" );
×
412
  passert( area != nullptr );
×
413

414
  for ( unsigned short i = 0; i < 500; ++i )
×
415
  {
UNCOV
416
    std::string name = "Cont" + Clib::tostring( i );
×
417
    Items::Item* item = nullptr;
×
UNCOV
418
    item = area->find_root_item( name );
×
419
    if ( item == nullptr )
×
420
    {
421
      item = Items::Item::create( UOBJ_BACKPACK );
×
422
      item->setname( name );
×
UNCOV
423
      item->setposition( Pos4d( item->pos().xyz(),
×
424
                                find_realm( std::string( "britannia" ) ) ) );  // TODO POS nullptr
×
425
      area->insert_root_item( item );
×
426
    }
427
    // Changed this from a passert to return null.
428
    if ( !( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) ) )
×
UNCOV
429
      return nullptr;
×
UNCOV
430
    UContainer* cont = static_cast<UContainer*>( item );
×
431
    if ( !cont->can_add_to_slot( slotIndex ) )
×
432
    {
433
      return nullptr;
×
434
    }
UNCOV
435
    if ( !item_to_add->slot_index( slotIndex ) )
×
436
    {
UNCOV
437
      return nullptr;
×
438
    }
UNCOV
439
    if ( cont->can_add( *item_to_add ) )
×
440
      return cont;
×
UNCOV
441
  }
×
442
  return nullptr;
×
443
}
444

445
void send_trade_container( Network::Client* client, Mobile::Character* whos, UContainer* cont )
×
446
{
447
  auto msg =
448
      Network::AddItemContainerMsg( cont->serial_ext, cont->graphic, 1 /*amount*/, Pos2d() /*pos*/,
×
UNCOV
449
                                    cont->slot_index(), whos->serial_ext, cont->color );
×
UNCOV
450
  msg.Send( client );
×
451
}
×
452

453
bool do_open_trade_window( Network::Client* client, Items::Item* item, Mobile::Character* dropon );
454
bool open_trade_window( Network::Client* client, Items::Item* item, Mobile::Character* dropon )
×
455
{
UNCOV
456
  if ( !Plib::systemstate.config.enable_secure_trading )
×
457
  {
UNCOV
458
    send_sysmessage( client, "Secure trading is unavailable." );
×
459
    return false;
×
460
  }
461

462
  if ( !settingsManager.ssopt.allow_secure_trading_in_warmode )
×
463
  {
UNCOV
464
    if ( dropon->warmode() )
×
465
    {
UNCOV
466
      send_sysmessage( client, "You cannot trade with someone in war mode." );
×
467
      return false;
×
468
    }
469
    if ( client->chr->warmode() )
×
470
    {
UNCOV
471
      send_sysmessage( client, "You cannot trade while in war mode." );
×
472
      return false;
×
473
    }
474
  }
475
  if ( dropon->is_trading() )
×
476
  {
UNCOV
477
    send_sysmessage( client, "That person is already involved in a trade." );
×
478
    return false;
×
479
  }
480
  if ( client->chr->is_trading() )
×
481
  {
UNCOV
482
    send_sysmessage( client, "You are already involved in a trade." );
×
483
    return false;
×
484
  }
485
  if ( !dropon->client )
×
486
  {
UNCOV
487
    send_sysmessage( client, "That person is already involved in a trade." );
×
488
    return false;
×
489
  }
490
  if ( client->chr->dead() || dropon->dead() )
×
491
  {
UNCOV
492
    send_sysmessage( client, "Ghosts cannot trade items." );
×
493
    return false;
×
494
  }
495

496
  return do_open_trade_window( client, item, dropon );
×
497
}
498

499
Bscript::BObjectImp* open_trade_window( Network::Client* client, Mobile::Character* dropon )
×
500
{
UNCOV
501
  if ( !Plib::systemstate.config.enable_secure_trading )
×
502
  {
UNCOV
503
    return new Bscript::BError( "Secure trading is unavailable." );
×
504
  }
505

506
  if ( !settingsManager.ssopt.allow_secure_trading_in_warmode )
×
507
  {
UNCOV
508
    if ( dropon->warmode() )
×
509
    {
UNCOV
510
      return new Bscript::BError( "You cannot trade with someone in war mode." );
×
511
    }
UNCOV
512
    if ( client->chr->warmode() )
×
513
    {
UNCOV
514
      return new Bscript::BError( "You cannot trade while in war mode." );
×
515
    }
516
  }
517
  if ( dropon->is_trading() )
×
518
  {
UNCOV
519
    return new Bscript::BError( "That person is already involved in a trade." );
×
520
  }
UNCOV
521
  if ( client->chr->is_trading() )
×
522
  {
UNCOV
523
    return new Bscript::BError( "You are already involved in a trade." );
×
524
  }
UNCOV
525
  if ( !dropon->client )
×
526
  {
UNCOV
527
    return new Bscript::BError( "That person is already involved in a trade." );
×
528
  }
UNCOV
529
  if ( client->chr->dead() || dropon->dead() )
×
530
  {
UNCOV
531
    return new Bscript::BError( "Ghosts cannot trade items." );
×
532
  }
533

534
  if ( do_open_trade_window( client, nullptr, dropon ) )
×
UNCOV
535
    return new Bscript::BLong( 1 );
×
536
  else
537
    return new Bscript::BError( "Something goes wrong." );
×
538
}
539

540
bool do_open_trade_window( Network::Client* client, Items::Item* item, Mobile::Character* dropon )
×
541
{
UNCOV
542
  dropon->create_trade_container();
×
543
  client->chr->create_trade_container();
×
544

545
  dropon->trading_with.set( client->chr );
×
546
  dropon->trade_accepted( false );
×
UNCOV
547
  client->chr->trading_with.set( dropon );
×
548
  client->chr->trade_accepted( false );
×
549

550
  send_trade_container( client, dropon, dropon->trade_container() );
×
551
  send_trade_container( dropon->client, dropon, dropon->trade_container() );
×
UNCOV
552
  send_trade_container( client, client->chr, client->chr->trade_container() );
×
553
  send_trade_container( dropon->client, client->chr, client->chr->trade_container() );
×
554

555
  Network::PktHelper::PacketOut<Network::PktOut_6F> msg;
×
556
  msg->WriteFlipped<u16>( sizeof msg->buffer );
×
UNCOV
557
  msg->Write<u8>( PKTBI_6F::ACTION_INIT );
×
558
  msg->Write<u32>( dropon->serial_ext );
×
559
  msg->Write<u32>( client->chr->trade_container()->serial_ext );
×
560
  msg->Write<u32>( dropon->trade_container()->serial_ext );
×
561
  msg->Write<u8>( 1u );
×
562
  msg->Write( Clib::strUtf8ToCp1252( dropon->name() ).c_str(), 30, false );
×
563

564
  msg.Send( client );
×
565

UNCOV
566
  msg->offset = 4;
×
567
  msg->Write<u32>( client->chr->serial_ext );
×
UNCOV
568
  msg->Write<u32>( dropon->trade_container()->serial_ext );
×
569
  msg->Write<u32>( client->chr->trade_container()->serial_ext );
×
570
  msg->offset++;  // u8 havename same as above
×
571
  msg->Write( Clib::strUtf8ToCp1252( client->chr->name() ).c_str(), 30, false );
×
572
  msg.Send( dropon->client );
×
573

574
  if ( item != nullptr )
×
575
    return place_item_in_secure_trade_container( client, item, Pos2d( 20, 20 ) );
×
576
  else
577
    return true;
×
578
}
×
579

580
bool drop_item_on_mobile( Network::Client* client, Items::Item* item, u32 target_serial,
×
581
                          u8 slotIndex )
582
{
583
  Mobile::Character* dropon = find_character( target_serial );
×
584

UNCOV
585
  if ( dropon == nullptr )
×
586
  {
UNCOV
587
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
588
    return false;
×
589
  }
590

591
  if ( !client->chr->in_range( dropon, 2 ) && !client->chr->can_moveanydist() )
×
592
  {
UNCOV
593
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
×
594
    return false;
×
595
  }
596
  if ( !client->chr->realm()->has_los( *client->chr, *dropon ) )
×
597
  {
UNCOV
598
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
599
    return false;
×
600
  }
601

602
  if ( !dropon->isa( UOBJ_CLASS::CLASS_NPC ) )
×
603
  {
UNCOV
604
    if ( item->no_drop() )
×
605
    {
UNCOV
606
      send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
607
      return false;
×
608
    }
609
    if ( gamestate.system_hooks.can_trade )
×
610
    {
UNCOV
611
      if ( !gamestate.system_hooks.can_trade->call( new Module::ECharacterRefObjImp( client->chr ),
×
612
                                                    new Module::ECharacterRefObjImp( dropon ),
×
UNCOV
613
                                                    new Module::EItemRefObjImp( item ) ) )
×
614
      {
615
        send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
616
        return false;
×
617
      }
618
    }
619
    bool res = open_trade_window( client, item, dropon );
×
UNCOV
620
    if ( !res )
×
UNCOV
621
      send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
622
    return res;
×
623
  }
624

625
  Mobile::NPC* npc = static_cast<Mobile::NPC*>( dropon );
×
UNCOV
626
  if ( !npc->can_accept_event( EVID_ITEM_GIVEN ) )
×
627
  {
628
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
629
    return false;
×
630
  }
631
  if ( item->no_drop() && !npc->no_drop_exception() )
×
632
  {
UNCOV
633
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
634
    return false;
×
635
  }
636

637
  UContainer* cont = find_giveitem_container( item, slotIndex );
×
UNCOV
638
  if ( cont == nullptr )
×
639
  {
640
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
641
    return false;
×
642
  }
643

644
  if ( !cont->can_add_to_slot( slotIndex ) || !item->slot_index( slotIndex ) )
×
645
  {
UNCOV
646
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
647
    return false;
×
648
  }
649

650
  client->pause();
×
651

UNCOV
652
  send_remove_object_to_inrange( item );
×
653

UNCOV
654
  cont->add_at_random_location( item );
×
655

UNCOV
656
  npc->send_event( new Module::ItemGivenEvent( client->chr, item, npc ) );
×
657

UNCOV
658
  client->restart();
×
659

UNCOV
660
  return true;
×
661
}
662

663
// target_serial should indicate a character, or a container, but not a pile.
664
bool drop_item_on_object( Network::Client* client, Items::Item* item, u32 target_serial,
3✔
665
                          u8 slotIndex )
666
{
667
  ItemRef itemref( item );
3✔
668
  UContainer* cont = nullptr;
3✔
669
  if ( IsCharacter( target_serial ) )
3✔
670
  {
UNCOV
671
    if ( target_serial == client->chr->serial )
×
672
    {
UNCOV
673
      cont = client->chr->backpack();
×
674
    }
675
    else
676
    {
UNCOV
677
      return drop_item_on_mobile( client, item, target_serial, slotIndex );
×
678
    }
679
  }
680

681
  if ( cont == nullptr )
3✔
682
  {
683
    cont = find_legal_container( client->chr, target_serial );
3✔
684
  }
685

686
  if ( cont == nullptr )
3✔
687
  {
688
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
1✔
689
    return false;
1✔
690
  }
691
  if ( item->no_drop() && !cont->no_drop_exception() )
2✔
692
  {
UNCOV
693
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
UNCOV
694
    return false;
×
695
  }
696
  if ( !client->chr->in_range( cont, 2 ) && !client->chr->can_moveanydist() )
2✔
697
  {
UNCOV
698
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
×
UNCOV
699
    return false;
×
700
  }
701
  if ( !client->chr->realm()->has_los( *client->chr, *cont->toplevel_owner() ) )
2✔
702
  {
UNCOV
703
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
UNCOV
704
    return false;
×
705
  }
706

707
  if ( item->stackable() )
2✔
708
  {
709
    for ( UContainer::const_iterator itr = cont->begin(); itr != cont->end(); ++itr )
2✔
710
    {
711
      Items::Item* exitem = *itr;
1✔
712
      if ( exitem->can_add_to_self( *item, false ) )
1✔
713
      {
714
        if ( cont->can_add( *item ) )
1✔
715
        {
716
          if ( cont->can_insert_increase_stack( client->chr, UContainer::MT_PLAYER, exitem,
1✔
717
                                                item->getamount(), item ) )
1✔
718
          {
719
            if ( item->orphan() )
1✔
720
              return true;
1✔
721
            u16 amtadded = item->getamount();
1✔
722
            exitem->add_to_self( item );
1✔
723
            update_item_to_inrange( exitem );
1✔
724
            exitem->increv_send_object_recursive();
1✔
725

726
            cont->on_insert_increase_stack( client->chr, UContainer::MT_PLAYER, exitem, amtadded );
1✔
727

728
            return true;
1✔
729
          }
730
        }
UNCOV
731
        return false;
×
732
      }
733
    }
734
  }
735

736
  auto contpos = cont->get_random_location();
1✔
737

738
  return place_item_in_container( client, item, cont, contpos, slotIndex );
1✔
739
}
3✔
740

741
/* DROP_ITEM messages come in a couple varieties:
742

743
    1)  Dropping an item on another object, or a person:
744
    item_serial: serial number of item to drop
745
    x: 0xFFFF
746
    y: 0xFFFF
747
    z: 0
748
    target_serial: serial number of item or character to drop on.
749

750
    2)  Dropping an item on the ground:
751
    item_serial: serial number of item to drop
752
    x,y,z: position
753
    target_serial: 0xFFFFFFFF
754

755
    3)  Placing an item in a container, or in an existing pile:
756
    item_serial: serial number of item to drop
757
    x: x-position
758
    y: y-position
759
    z: 0
760
    target_serial: serial number of item or character or pile to drop on.
761
    */
762

763
/*
764
    Name:       drop_item
765
    Details:    Original version of packet is supported by this function.
766
    Access:     public
767
    Qualifier:
768
    Parameter:  Client * client
769
    Parameter:  PKTIN_08_V1 * msg
770
    */
UNCOV
771
void drop_item( Network::Client* client, PKTIN_08_V1* msg )
×
772
{
UNCOV
773
  u32 item_serial = cfBEu32( msg->item_serial );
×
774
  auto pos = Pos3d( cfBEu16( msg->x ), cfBEu16( msg->y ), msg->z );
×
UNCOV
775
  u32 target_serial = cfBEu32( msg->target_serial );
×
776

777
  GottenItem info = client->chr->gotten_item();
×
778
  Items::Item* item = info.item();
×
UNCOV
779
  if ( item == nullptr )
×
780
  {
781
    SuspiciousActs::DropItemButNoneGotten( client, item_serial );
×
782
    return;
×
783
  }
784
  if ( item->serial != item_serial )
×
785
  {
UNCOV
786
    SuspiciousActs::DropItemOtherThanGotten( client, item_serial, item->serial );
×
787
    item->gotten_by( nullptr );  // TODO: shouldn't we clear_gotten_item() here?
×
UNCOV
788
    return;
×
789
  }
790
  item->inuse( false );
×
791
  item->gotten_by( nullptr );
×
UNCOV
792
  client->chr->gotten_item( {} );
×
793

794
  bool res;
795
  if ( target_serial == 0xFFffFFffLu )
×
796
  {
UNCOV
797
    res = drop_item_on_ground( client, item, pos );
×
798
  }
UNCOV
799
  else if ( pos.x() == 0xFFFF )
×
800
  {
UNCOV
801
    res = drop_item_on_object( client, item, target_serial, 0 );
×
802
  }
803
  else
804
  {
UNCOV
805
    Multi::UMulti* multi = system_find_multi( target_serial );
×
806

UNCOV
807
    if ( multi != nullptr )
×
808
      res = drop_item_on_ground( client, item, pos + Vec2d( multi->x(), multi->y() ) );
×
809
    else
810
      res = place_item( client, item, target_serial, pos.xy(), 0 );
×
811
  }
812

813
  if ( !item->orphan() )
×
814
  {
UNCOV
815
    if ( !res )
×
816
      info.undo( client->chr );
×
UNCOV
817
    item->inuse( false );
×
818
    item->gotten_by( nullptr );
×
819
  }
820
  send_full_statmsg( client, client->chr );
×
821
}
×
822

823

824
/*
825
    Name:       drop_item_v2
826
    Details:    This is used for the version of the packet introduced in clients 6.0.1.7 2D and
827
   UO:KR+ to support Slots
828
    Access:     public
829
    Qualifier:
830
    Parameter:  Client * client
831
    Parameter:  PKTIN_08_V2 * msg
832
    */
833
void drop_item_v2( Network::Client* client, PKTIN_08_V2* msg )
22✔
834
{
835
  u32 item_serial = cfBEu32( msg->item_serial );
22✔
836
  auto pos = Pos3d( cfBEu16( msg->x ), cfBEu16( msg->y ), msg->z );
22✔
837
  u8 slotIndex = msg->slotindex;
22✔
838
  u32 target_serial = cfBEu32( msg->target_serial );
22✔
839

840
  GottenItem info = client->chr->gotten_item();
22✔
841
  Items::Item* item = info.item();
22✔
842
  if ( item == nullptr )
22✔
843
  {
UNCOV
844
    SuspiciousActs::DropItemButNoneGotten( client, item_serial );
×
UNCOV
845
    return;
×
846
  }
847
  if ( item->serial != item_serial )
22✔
848
  {
UNCOV
849
    SuspiciousActs::DropItemOtherThanGotten( client, item_serial, item->serial );
×
UNCOV
850
    item->gotten_by( nullptr );
×
UNCOV
851
    return;
×
852
  }
853
  item->inuse( false );
22✔
854
  item->gotten_by( nullptr );
22✔
855
  client->chr->gotten_item( {} );
22✔
856

857
  bool res;
858
  if ( target_serial == 0xFFffFFffLu )
22✔
859
  {
860
    res = drop_item_on_ground( client, item, pos );
7✔
861
  }
862
  else if ( pos.x() == 0xFFFF )
15✔
863
  {
864
    res = drop_item_on_object( client, item, target_serial, slotIndex );
3✔
865
  }
866
  else
867
  {
868
    Multi::UMulti* multi = system_find_multi( target_serial );
12✔
869

870
    if ( multi != nullptr )
12✔
UNCOV
871
      res = drop_item_on_ground( client, item, pos + Vec2d( multi->x(), multi->y() ) );
×
872
    else
873
      res = place_item( client, item, target_serial, pos.xy(), 0 );
12✔
874
  }
875

876
  if ( !item->orphan() )
22✔
877
  {
878
    if ( !res )
20✔
879
      info.undo( client->chr );
11✔
880
    item->inuse( false );
20✔
881
    item->gotten_by( nullptr );
20✔
882
  }
883

884
  if ( res )
22✔
885
  {
886
    Network::PktHelper::PacketOut<Network::PktOut_29> drop_msg;
11✔
887
    drop_msg.Send( client );
11✔
888
  }
11✔
889

890
  send_full_statmsg( client, client->chr );
22✔
891
}
22✔
892

UNCOV
893
void return_traded_items( Mobile::Character* chr )
×
894
{
UNCOV
895
  UContainer* cont = chr->trade_container();
×
896
  UContainer* bp = chr->backpack();
×
UNCOV
897
  if ( cont == nullptr || bp == nullptr )
×
898
    return;
×
899

900
  UContainer::Contents tmp;
×
901
  cont->extract( tmp );
×
UNCOV
902
  while ( !tmp.empty() )
×
903
  {
904
    Items::Item* item = tmp.back();
×
905
    tmp.pop_back();
×
UNCOV
906
    item->container = nullptr;
×
907
    item->layer = 0;
×
908

909
    ItemRef itemref( item );
×
910
    if ( bp->can_add( *item ) && bp->can_insert_add_item( chr, UContainer::MT_CORE_MOVED, item ) )
×
911
    {
912
      if ( item->orphan() )
×
913
      {
UNCOV
914
        continue;
×
915
      }
UNCOV
916
      u8 newSlot = 1;
×
917
      if ( !bp->can_add_to_slot( newSlot ) || !item->slot_index( newSlot ) )
×
918
      {
919
        item->setposition( chr->pos() );
×
920
        add_item_to_world( item );
×
UNCOV
921
        register_with_supporting_multi( item );
×
922
        move_item( item, item->pos() );
×
923
        return;
×
924
      }
925
      bp->add_at_random_location( item );
×
926
      if ( chr->client )
×
UNCOV
927
        send_put_in_container( chr->client, item );
×
928
      bp->on_insert_add_item( chr, UContainer::MT_CORE_MOVED, item );
×
929
    }
930
    else
931
    {
UNCOV
932
      item->setposition( chr->pos() );
×
UNCOV
933
      add_item_to_world( item );
×
UNCOV
934
      register_with_supporting_multi( item );
×
935
      move_item( item, item->pos() );
×
936
    }
937
  }
×
938
}
×
939

940

941
void cancel_trade( Mobile::Character* chr1 )
×
942
{
UNCOV
943
  Mobile::Character* chr2 = chr1->trading_with.get();
×
944

UNCOV
945
  return_traded_items( chr1 );
×
946
  chr1->trading_with.clear();
×
947

948
  if ( chr1->client )
×
949
  {
UNCOV
950
    Network::PktHelper::PacketOut<Network::PktOut_6F> msg;
×
951
    msg->WriteFlipped<u16>( 17u );  // no name
×
UNCOV
952
    msg->Write<u8>( PKTBI_6F::ACTION_CANCEL );
×
953
    msg->Write<u32>( chr1->trade_container()->serial_ext );
×
954
    msg->offset += 9;  // u32 cont1_serial, cont2_serial, u8 havename
×
955
    msg.Send( chr1->client );
×
956
    send_full_statmsg( chr1->client, chr1 );
×
957
  }
×
958

959
  if ( chr2 )
×
960
  {
UNCOV
961
    return_traded_items( chr2 );
×
962
    chr2->trading_with.clear();
×
UNCOV
963
    if ( chr2->client )
×
964
    {
965
      Network::PktHelper::PacketOut<Network::PktOut_6F> msg;
×
966
      msg->WriteFlipped<u16>( 17u );  // no name
×
UNCOV
967
      msg->Write<u8>( PKTBI_6F::ACTION_CANCEL );
×
968
      msg->Write<u32>( chr2->trade_container()->serial_ext );
×
969
      msg->offset += 9;  // u32 cont1_serial, cont2_serial, u8 havename
×
970
      msg.Send( chr2->client );
×
971
      send_full_statmsg( chr2->client, chr2 );
×
972
    }
×
973
  }
974
}
×
975

UNCOV
976
void send_trade_statuses( Mobile::Character* chr )
×
977
{
UNCOV
978
  unsigned int stat1 = chr->trade_accepted() ? 1 : 0;
×
979
  unsigned int stat2 = chr->trading_with->trade_accepted() ? 1 : 0;
×
980

981
  Network::PktHelper::PacketOut<Network::PktOut_6F> msg;
×
982
  msg->WriteFlipped<u16>( 17u );  // no name
×
UNCOV
983
  msg->Write<u8>( PKTBI_6F::ACTION_STATUS );
×
984
  msg->Write<u32>( chr->trade_container()->serial_ext );
×
985
  msg->WriteFlipped<u32>( stat1 );
×
986
  msg->WriteFlipped<u32>( stat2 );
×
987
  msg->offset++;  // u8 havename
×
988
  Network::transmit( chr->client, &msg->buffer, msg->offset );
×
989
  msg->offset = 4;
×
990
  msg->Write<u32>( chr->trading_with->trade_container()->serial_ext );
×
991
  msg->WriteFlipped<u32>( stat2 );
×
992
  msg->WriteFlipped<u32>( stat1 );
×
993
  msg->offset++;
×
994
  msg.Send( chr->trading_with->client );
×
995
}
×
996

997
void change_trade_status( Mobile::Character* chr, bool set )
×
998
{
UNCOV
999
  chr->trade_accepted( set );
×
1000
  send_trade_statuses( chr );
×
UNCOV
1001
  if ( chr->trade_accepted() && chr->trading_with->trade_accepted() )
×
1002

1003
  {
1004
    UContainer* cont0 = chr->trade_container();
×
UNCOV
1005
    UContainer* cont1 = chr->trading_with->trade_container();
×
UNCOV
1006
    if ( cont0->can_swap( *cont1 ) )
×
1007
    {
1008
      cont0->swap( *cont1 );
×
1009
      cancel_trade( chr );
×
1010
    }
1011
    else
1012
    {
UNCOV
1013
      POLLOG_ERRORLN( "Can't swap trade containers: ic0={},w0={}, ic1={},w1={}",
×
UNCOV
1014
                      cont0->item_count(), cont0->weight(), cont1->item_count(), cont1->weight() );
×
1015
    }
1016
  }
1017
}
×
1018

UNCOV
1019
void handle_secure_trade_msg( Network::Client* client, PKTBI_6F* msg )
×
1020
{
UNCOV
1021
  if ( !client->chr->is_trading() )
×
1022
    return;
×
UNCOV
1023
  switch ( msg->action )
×
1024
  {
1025
  case PKTBI_6F::ACTION_CANCEL:
×
1026
    INFO_PRINTLN_TRACE( 5 )( "Cancel secure trade" );
×
UNCOV
1027
    cancel_trade( client->chr );
×
1028
    break;
×
1029

1030
  case PKTBI_6F::ACTION_STATUS:
×
1031
    bool set;
UNCOV
1032
    set = msg->cont1_serial ? true : false;
×
1033
    if ( set )
×
1034
    {
1035
      INFO_PRINTLN_TRACE( 5 )( "Set secure trade indicator" );
×
1036
    }
1037
    else
1038
    {
UNCOV
1039
      INFO_PRINTLN_TRACE( 5 )( "Clear secure trade indicator" );
×
1040
    }
UNCOV
1041
    change_trade_status( client->chr, set );
×
1042
    break;
×
1043

1044
  default:
×
1045
    INFO_PRINTLN_TRACE( 5 )( "Unknown secure trade action: {}", (int)msg->action );
×
UNCOV
1046
    break;
×
1047
  }
1048
}
1049

1050
void cancel_all_trades()
5✔
1051
{
1052
  for ( auto& client : networkManager.clients )
5✔
1053
  {
UNCOV
1054
    if ( client->ready && client->chr )
×
1055
    {
UNCOV
1056
      Mobile::Character* chr = client->chr;
×
1057
      if ( chr->is_trading() )
×
UNCOV
1058
        cancel_trade( chr );
×
1059
    }
1060
  }
1061
}
5✔
1062
}  // namespace Core
1063
}  // 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