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

polserver / polserver / 21108840797

18 Jan 2026 08:35AM UTC coverage: 60.508% (+0.02%) from 60.492%
21108840797

push

github

web-flow
ClangTidy readability-else-after-return (#857)

* trigger tidy

* Automated clang-tidy change: readability-else-after-return

* compile test

* rerun

* Automated clang-tidy change: readability-else-after-return

* trigger..

* Automated clang-tidy change: readability-else-after-return

* manually removed a few

* Automated clang-tidy change: readability-else-after-return

* removed duplicate code

* fix remaining warnings

* fixed scope

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

837 of 1874 new or added lines in 151 files covered. (44.66%)

46 existing lines in 25 files now uncovered.

44448 of 73458 relevant lines covered (60.51%)

525066.38 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

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

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

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

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

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

110
  item->set_dirty();
5✔
111

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

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

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

124
  item->increv_send_object_recursive();
5✔
125

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

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

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

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

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

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

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

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

215
  client->chr->trade_accepted( false );
×
216
  dropon->trade_accepted( false );
×
217
  send_trade_statuses( client->chr );
×
218

219
  send_remove_object_to_inrange( item );
×
220

221
  cont->add( item, pos );
×
222

223
  send_put_in_container( client, item );
×
224
  send_put_in_container( dropon->client, item );
×
225

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

231
  send_trade_statuses( client->chr );
×
232
  send_trade_statuses( dropon->client->chr );
×
233

234
  client->restart();
×
235
  return true;
×
236
}
237

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

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

253
    return false;
×
254
  }
255

256
  if ( item->orphan() )
1✔
257
    return false;
×
258

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

266
  send_remove_object_to_inrange( item );
1✔
267

268
  u16 amtadded = item->getamount();
1✔
269

270
  target_item->add_to_self( item );
1✔
271
  update_item_to_inrange( target_item );
1✔
272

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

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

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

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

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

318

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

342
  // UNTESTED CLIENT_HOLE?
NEW
343
  send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
344

NEW
345
  return false;
×
346
}
347

348
bool drop_item_on_ground( Network::Client* client, Items::Item* item, const Pos3d& pos )
7✔
349
{
350
  if ( item->no_drop() )
7✔
351
  {
352
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
353
    return false;
×
354
  }
355

356
  Mobile::Character* chr = client->chr;
7✔
357

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

367
  if ( !chr->realm()->dropheight( pos, client->chr->z(), &newz, &multi ) )
4✔
368
  {
369
    SuspiciousActs::DropItemOutAtBlockedLocation( client, item->serial, pos );
×
370
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
×
371
    return false;
×
372
  }
373

374
  LosObj tgt( Pos4d( pos.xy(), static_cast<s8>( newz ), chr->realm() ) );
4✔
375
  if ( !chr->realm()->has_los( *client->chr, tgt ) )
4✔
376
  {
377
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
378
    return false;
×
379
  }
380

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

401
  send_item_to_inrange( item );
4✔
402
  return true;
4✔
403
}
404

405
UContainer* find_giveitem_container( Items::Item* item_to_add, u8 slotIndex )
×
406
{
407
  StorageArea* area = gamestate.storage.create_area( "GivenItems" );
×
408
  passert( area != nullptr );
×
409

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

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

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

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

492
  return do_open_trade_window( client, item, dropon );
×
493
}
494

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

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

530
  if ( do_open_trade_window( client, nullptr, dropon ) )
×
531
    return new Bscript::BLong( 1 );
×
NEW
532
  return new Bscript::BError( "Something goes wrong." );
×
533
}
534

535
bool do_open_trade_window( Network::Client* client, Items::Item* item, Mobile::Character* dropon )
×
536
{
537
  dropon->create_trade_container();
×
538
  client->chr->create_trade_container();
×
539

540
  dropon->trading_with.set( client->chr );
×
541
  dropon->trade_accepted( false );
×
542
  client->chr->trading_with.set( dropon );
×
543
  client->chr->trade_accepted( false );
×
544

545
  send_trade_container( client, dropon, dropon->trade_container() );
×
546
  send_trade_container( dropon->client, dropon, dropon->trade_container() );
×
547
  send_trade_container( client, client->chr, client->chr->trade_container() );
×
548
  send_trade_container( dropon->client, client->chr, client->chr->trade_container() );
×
549

550
  Network::PktHelper::PacketOut<Network::PktOut_6F> msg;
×
551
  msg->WriteFlipped<u16>( sizeof msg->buffer );
×
552
  msg->Write<u8>( PKTBI_6F::ACTION_INIT );
×
553
  msg->Write<u32>( dropon->serial_ext );
×
554
  msg->Write<u32>( client->chr->trade_container()->serial_ext );
×
555
  msg->Write<u32>( dropon->trade_container()->serial_ext );
×
556
  msg->Write<u8>( 1u );
×
557
  msg->Write( Clib::strUtf8ToCp1252( dropon->name() ).c_str(), 30, false );
×
558

559
  msg.Send( client );
×
560

561
  msg->offset = 4;
×
562
  msg->Write<u32>( client->chr->serial_ext );
×
563
  msg->Write<u32>( dropon->trade_container()->serial_ext );
×
564
  msg->Write<u32>( client->chr->trade_container()->serial_ext );
×
565
  msg->offset++;  // u8 havename same as above
×
566
  msg->Write( Clib::strUtf8ToCp1252( client->chr->name() ).c_str(), 30, false );
×
567
  msg.Send( dropon->client );
×
568

569
  if ( item != nullptr )
×
570
    return place_item_in_secure_trade_container( client, item, Pos2d( 20, 20 ) );
×
NEW
571
  return true;
×
572
}
×
573

574
bool drop_item_on_mobile( Network::Client* client, Items::Item* item, u32 target_serial,
×
575
                          u8 slotIndex )
576
{
577
  Mobile::Character* dropon = find_character( target_serial );
×
578

579
  if ( dropon == nullptr )
×
580
  {
581
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
582
    return false;
×
583
  }
584

585
  if ( !client->chr->in_range( dropon, 2 ) && !client->chr->can_moveanydist() )
×
586
  {
587
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
×
588
    return false;
×
589
  }
590
  if ( !client->chr->realm()->has_los( *client->chr, *dropon ) )
×
591
  {
592
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
593
    return false;
×
594
  }
595

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

619
  Mobile::NPC* npc = static_cast<Mobile::NPC*>( dropon );
×
620
  if ( !npc->can_accept_event( EVID_ITEM_GIVEN ) )
×
621
  {
622
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
623
    return false;
×
624
  }
625
  if ( item->no_drop() && !npc->no_drop_exception() )
×
626
  {
627
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
628
    return false;
×
629
  }
630

631
  UContainer* cont = find_giveitem_container( item, slotIndex );
×
632
  if ( cont == nullptr )
×
633
  {
634
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
635
    return false;
×
636
  }
637

638
  if ( !cont->can_add_to_slot( slotIndex ) || !item->slot_index( slotIndex ) )
×
639
  {
640
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
641
    return false;
×
642
  }
643

644
  client->pause();
×
645

646
  send_remove_object_to_inrange( item );
×
647

648
  cont->add_at_random_location( item );
×
649

650
  npc->send_event( new Module::ItemGivenEvent( client->chr, item, npc ) );
×
651

652
  client->restart();
×
653

654
  return true;
×
655
}
656

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

675
  if ( cont == nullptr )
3✔
676
  {
677
    cont = find_legal_container( client->chr, target_serial );
3✔
678
  }
679

680
  if ( cont == nullptr )
3✔
681
  {
682
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
1✔
683
    return false;
1✔
684
  }
685
  if ( item->no_drop() && !cont->no_drop_exception() )
2✔
686
  {
687
    send_item_move_failure( client, MOVE_ITEM_FAILURE_UNKNOWN );
×
688
    return false;
×
689
  }
690
  if ( !client->chr->in_range( cont, 2 ) && !client->chr->can_moveanydist() )
2✔
691
  {
692
    send_item_move_failure( client, MOVE_ITEM_FAILURE_TOO_FAR_AWAY );
×
693
    return false;
×
694
  }
695
  if ( !client->chr->realm()->has_los( *client->chr, *cont->toplevel_owner() ) )
2✔
696
  {
697
    send_item_move_failure( client, MOVE_ITEM_FAILURE_OUT_OF_SIGHT );
×
698
    return false;
×
699
  }
700

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

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

722
            return true;
1✔
723
          }
724
        }
725
        return false;
×
726
      }
727
    }
728
  }
729

730
  auto contpos = cont->get_random_location();
1✔
731

732
  return place_item_in_container( client, item, cont, contpos, slotIndex );
1✔
733
}
3✔
734

735
/* DROP_ITEM messages come in a couple varieties:
736

737
    1)  Dropping an item on another object, or a person:
738
    item_serial: serial number of item to drop
739
    x: 0xFFFF
740
    y: 0xFFFF
741
    z: 0
742
    target_serial: serial number of item or character to drop on.
743

744
    2)  Dropping an item on the ground:
745
    item_serial: serial number of item to drop
746
    x,y,z: position
747
    target_serial: 0xFFFFFFFF
748

749
    3)  Placing an item in a container, or in an existing pile:
750
    item_serial: serial number of item to drop
751
    x: x-position
752
    y: y-position
753
    z: 0
754
    target_serial: serial number of item or character or pile to drop on.
755
    */
756

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

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

788
  bool res;
789
  if ( target_serial == 0xFFffFFffLu )
×
790
  {
791
    res = drop_item_on_ground( client, item, pos );
×
792
  }
793
  else if ( pos.x() == 0xFFFF )
×
794
  {
795
    res = drop_item_on_object( client, item, target_serial, 0 );
×
796
  }
797
  else
798
  {
799
    Multi::UMulti* multi = system_find_multi( target_serial );
×
800

801
    if ( multi != nullptr )
×
802
      res = drop_item_on_ground( client, item, pos + Vec2d( multi->x(), multi->y() ) );
×
803
    else
804
      res = place_item( client, item, target_serial, pos.xy(), 0 );
×
805
  }
806

807
  if ( !item->orphan() )
×
808
  {
809
    if ( !res )
×
810
      info.undo( client->chr );
×
811
    item->inuse( false );
×
812
    item->gotten_by( nullptr );
×
813
  }
814
  send_full_statmsg( client, client->chr );
×
815
}
×
816

817

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

834
  GottenItem info = client->chr->gotten_item();
22✔
835
  Items::Item* item = info.item();
22✔
836
  if ( item == nullptr )
22✔
837
  {
838
    SuspiciousActs::DropItemButNoneGotten( client, item_serial );
×
839
    return;
×
840
  }
841
  if ( item->serial != item_serial )
22✔
842
  {
843
    SuspiciousActs::DropItemOtherThanGotten( client, item_serial, item->serial );
×
844
    item->gotten_by( nullptr );
×
845
    return;
×
846
  }
847
  item->inuse( false );
22✔
848
  item->gotten_by( nullptr );
22✔
849
  client->chr->gotten_item( {} );
22✔
850

851
  bool res;
852
  if ( target_serial == 0xFFffFFffLu )
22✔
853
  {
854
    res = drop_item_on_ground( client, item, pos );
7✔
855
  }
856
  else if ( pos.x() == 0xFFFF )
15✔
857
  {
858
    res = drop_item_on_object( client, item, target_serial, slotIndex );
3✔
859
  }
860
  else
861
  {
862
    Multi::UMulti* multi = system_find_multi( target_serial );
12✔
863

864
    if ( multi != nullptr )
12✔
865
      res = drop_item_on_ground( client, item, pos + Vec2d( multi->x(), multi->y() ) );
×
866
    else
867
      res = place_item( client, item, target_serial, pos.xy(), 0 );
12✔
868
  }
869

870
  if ( !item->orphan() )
22✔
871
  {
872
    if ( !res )
20✔
873
      info.undo( client->chr );
11✔
874
    item->inuse( false );
20✔
875
    item->gotten_by( nullptr );
20✔
876
  }
877

878
  if ( res )
22✔
879
  {
880
    Network::PktHelper::PacketOut<Network::PktOut_29> drop_msg;
11✔
881
    drop_msg.Send( client );
11✔
882
  }
11✔
883

884
  send_full_statmsg( client, client->chr );
22✔
885
}
22✔
886

887
void return_traded_items( Mobile::Character* chr )
×
888
{
889
  UContainer* cont = chr->trade_container();
×
890
  UContainer* bp = chr->backpack();
×
891
  if ( cont == nullptr || bp == nullptr )
×
892
    return;
×
893

894
  UContainer::Contents tmp;
×
895
  cont->extract( tmp );
×
896
  while ( !tmp.empty() )
×
897
  {
898
    Items::Item* item = tmp.back();
×
899
    tmp.pop_back();
×
900
    item->container = nullptr;
×
901
    item->layer = 0;
×
902

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

934

935
void cancel_trade( Mobile::Character* chr1 )
×
936
{
937
  Mobile::Character* chr2 = chr1->trading_with.get();
×
938

939
  return_traded_items( chr1 );
×
940
  chr1->trading_with.clear();
×
941

942
  if ( chr1->client )
×
943
  {
944
    Network::PktHelper::PacketOut<Network::PktOut_6F> msg;
×
945
    msg->WriteFlipped<u16>( 17u );  // no name
×
946
    msg->Write<u8>( PKTBI_6F::ACTION_CANCEL );
×
947
    msg->Write<u32>( chr1->trade_container()->serial_ext );
×
948
    msg->offset += 9;  // u32 cont1_serial, cont2_serial, u8 havename
×
949
    msg.Send( chr1->client );
×
950
    send_full_statmsg( chr1->client, chr1 );
×
951
  }
×
952

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

970
void send_trade_statuses( Mobile::Character* chr )
×
971
{
972
  unsigned int stat1 = chr->trade_accepted() ? 1 : 0;
×
973
  unsigned int stat2 = chr->trading_with->trade_accepted() ? 1 : 0;
×
974

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

991
void change_trade_status( Mobile::Character* chr, bool set )
×
992
{
993
  chr->trade_accepted( set );
×
994
  send_trade_statuses( chr );
×
995
  if ( chr->trade_accepted() && chr->trading_with->trade_accepted() )
×
996

997
  {
998
    UContainer* cont0 = chr->trade_container();
×
999
    UContainer* cont1 = chr->trading_with->trade_container();
×
1000
    if ( cont0->can_swap( *cont1 ) )
×
1001
    {
1002
      cont0->swap( *cont1 );
×
1003
      cancel_trade( chr );
×
1004
    }
1005
    else
1006
    {
1007
      POLLOG_ERRORLN( "Can't swap trade containers: ic0={},w0={}, ic1={},w1={}",
×
1008
                      cont0->item_count(), cont0->weight(), cont1->item_count(), cont1->weight() );
×
1009
    }
1010
  }
1011
}
×
1012

1013
void handle_secure_trade_msg( Network::Client* client, PKTBI_6F* msg )
×
1014
{
1015
  if ( !client->chr->is_trading() )
×
1016
    return;
×
1017
  switch ( msg->action )
×
1018
  {
1019
  case PKTBI_6F::ACTION_CANCEL:
×
1020
    INFO_PRINTLN_TRACE( 5 )( "Cancel secure trade" );
×
1021
    cancel_trade( client->chr );
×
1022
    break;
×
1023

1024
  case PKTBI_6F::ACTION_STATUS:
×
1025
    bool set;
1026
    set = msg->cont1_serial ? true : false;
×
1027
    if ( set )
×
1028
    {
1029
      INFO_PRINTLN_TRACE( 5 )( "Set secure trade indicator" );
×
1030
    }
1031
    else
1032
    {
1033
      INFO_PRINTLN_TRACE( 5 )( "Clear secure trade indicator" );
×
1034
    }
1035
    change_trade_status( client->chr, set );
×
1036
    break;
×
1037

1038
  default:
×
1039
    INFO_PRINTLN_TRACE( 5 )( "Unknown secure trade action: {}", (int)msg->action );
×
1040
    break;
×
1041
  }
1042
}
1043

1044
void cancel_all_trades()
5✔
1045
{
1046
  for ( auto& client : networkManager.clients )
5✔
1047
  {
1048
    if ( client->ready && client->chr )
×
1049
    {
1050
      Mobile::Character* chr = client->chr;
×
1051
      if ( chr->is_trading() )
×
1052
        cancel_trade( chr );
×
1053
    }
1054
  }
1055
}
5✔
1056
}  // namespace Pol::Core
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