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

polserver / polserver / 21008991693

14 Jan 2026 08:35PM UTC coverage: 60.508% (+0.001%) from 60.507%
21008991693

push

github

web-flow
ClangTidy "modernize-use-override" (#851)

* let clang-tidy do its thing

* Automated clang-tidy change: modernize-use-override

* compile test

---------

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

82 of 153 new or added lines in 40 files covered. (53.59%)

3 existing lines in 3 files now uncovered.

44461 of 73479 relevant lines covered (60.51%)

512457.07 hits per line

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

15.47
/pol-core/pol/module/npcmod.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2009/09/14 MuadDib:   CreateItem() now has slot support.
5
 */
6

7
#include "npcmod.h"
8
#include <iostream>
9
#include <stddef.h>
10
#include <string>
11

12
#include "../../bscript/berror.h"
13
#include "../../bscript/bobject.h"
14
#include "../../bscript/impstr.h"
15
#include "../../clib/clib.h"
16
#include "../../clib/logfacility.h"
17
#include "../../clib/random.h"
18
#include "../../clib/rawtypes.h"
19
#include "../../clib/stlutil.h"
20
#include "../../clib/strutil.h"
21
#include "../../plib/objtype.h"
22
#include "../../plib/poltype.h"
23
#include "../containr.h"
24
#include "../item/item.h"
25
#include "../mobile/boundbox.h"
26
#include "../mobile/charactr.h"
27
#include "../mobile/npc.h"
28
#include "../mobile/ufacing.h"
29
#include "../network/packethelper.h"
30
#include "../network/packets.h"
31
#include "../network/pktdef.h"
32
#include "../polobject.h"
33
#include "../uoscrobj.h"
34
#include "../uworld.h"
35
#include "osmod.h"
36
#include "unimod.h"
37

38
#include <module_defs/npc.h>
39

40
namespace Pol
41
{
42
namespace Module
43
{
44
using namespace Bscript;
45
NPCExecutorModule::NPCExecutorModule( Executor& ex, Mobile::NPC& npc )
10✔
46
    : TmplExecutorModule<NPCExecutorModule, Core::PolModule>( ex ), npcref( &npc ), npc( npc )
10✔
47
{
48
  os_module = static_cast<OSExecutorModule*>( exec.findModule( "OS" ) );
10✔
49
  if ( os_module == nullptr )
10✔
50
    throw std::runtime_error( "NPCExecutorModule needs OS module!" );
×
51
}
10✔
52

53
NPCExecutorModule::~NPCExecutorModule()
20✔
54
{
55
  if ( npc.ex == &exec )
10✔
56
    npc.ex = nullptr;
10✔
57
}
20✔
58

59
BApplicObjType bounding_box_type;
60

61
class BoundingBoxObjImp final : public Core::PolApplicObj<Mobile::BoundingBox>
62
{
63
public:
64
  BoundingBoxObjImp() : PolApplicObj<Mobile::BoundingBox>( &bounding_box_type ) {}
×
65
  explicit BoundingBoxObjImp( const Mobile::BoundingBox& b )
×
66
      : PolApplicObj<Mobile::BoundingBox>( &bounding_box_type, b )
×
67
  {
68
  }
×
NEW
69
  const char* typeOf() const override { return "BoundingBox"; }
×
NEW
70
  u8 typeOfInt() const override { return OTBoundingBox; }
×
NEW
71
  BObjectImp* copy() const override { return new BoundingBoxObjImp( value() ); }
×
72
};
73

74
/* IsLegalMove: parameters (move, bounding box)*/
75
BObjectImp* NPCExecutorModule::mf_IsLegalMove()
×
76
{
77
  String* facing_str = static_cast<String*>( exec.getParamImp( 0, BObjectImp::OTString ) );
×
78
  BApplicObjBase* appobj =
79
      static_cast<BApplicObjBase*>( exec.getParamImp( 1, BObjectImp::OTApplicObj ) );
×
80
  if ( facing_str == nullptr || appobj == nullptr || appobj->object_type() != &bounding_box_type )
×
81
  {
82
    return new BLong( 0 );
×
83
  }
84

85
  BApplicObj<Mobile::BoundingBox>* ao_bbox =
×
86
      static_cast<BApplicObj<Mobile::BoundingBox>*>( appobj );
87
  const Mobile::BoundingBox& bbox = ao_bbox->value();
×
88

89
  Core::UFACING facing;
90
  if ( Mobile::DecodeFacing( facing_str->value().c_str(), facing ) == false )
×
91
    return new BLong( 0 );
×
92

93
  auto pos = npc.pos().move( facing );
×
94

95
  return new BLong( bbox.contains( pos.xy() ) );
×
96
}
97

98
/* CanMove: parameters (facing)*/
99
BObjectImp* NPCExecutorModule::mf_CanMove()
×
100
{
101
  if ( exec.fparams.size() == 1 )
×
102
  {
103
    BObjectImp* param0 = exec.getParamImp( 0 );
×
104

105
    if ( auto* s = impptrIf<String>( param0 ) )
×
106
    {
107
      const char* dir = s->data();
×
108
      Core::UFACING facing;
109

110
      if ( Mobile::DecodeFacing( dir, facing ) == false )
×
111
      {
112
        DEBUGLOGLN(
×
113
            "Script Error in '{}' PC={}: \n"
114
            "\tCall to function npc::canmove():\n"
115
            "\tParameter 0: Expected direction: N S E W NW NE SW SE, got {}",
116
            scriptname(), exec.PC, dir );
×
117
        return new BError( "Invalid facing value" );
×
118
      }
119

120
      return new BLong( npc.could_move( facing ) ? 1 : 0 );
×
121
    }
122
    else if ( auto* l = impptrIf<BLong>( param0 ) )
×
123
    {
124
      Core::UFACING facing = static_cast<Core::UFACING>( l->value() & PKTIN_02_FACING_MASK );
×
125
      return new BLong( npc.could_move( facing ) ? 1 : 0 );
×
126
    }
127
    else
128
    {
129
      DEBUGLOGLN(
×
130
          "Script Error in '{}' PC={}: \n"
131
          "\tCall to function npc::canmove():\n"
132
          "\tParameter 0: Expected direction, got datatype {}",
133
          scriptname(), exec.PC, BObjectImp::typestr( param0->type() ) );
×
134
      return new BError( "Invalid parameter type" );
×
135
    }
136
  }
137
  else
138
    return new BError( "Invalid parameter count" );
×
139
}
140

141
BObjectImp* NPCExecutorModule::mf_Self()
8✔
142
{
143
  return new ECharacterRefObjImp( &npc );
8✔
144
}
145

146
BObjectImp* NPCExecutorModule::mf_SetAnchor()
×
147
{
148
  Core::Pos2d pos;
×
149
  int dstart, psub;
150
  if ( getPos2dParam( 0, 1, &pos, npc.realm() ) && getParam( 2, dstart ) && getParam( 3, psub ) )
×
151
  {
152
    if ( dstart )
×
153
    {
154
      npc.anchor.enabled = true;
×
155
      npc.anchor.pos = pos;
×
156
      npc.anchor.dstart = static_cast<unsigned short>( dstart );
×
157
      npc.anchor.psub = static_cast<unsigned short>( psub );
×
158
      return new BLong( 1 );
×
159
    }
160
    else
161
    {
162
      npc.anchor.enabled = false;
×
163
      return new BLong( 1 );
×
164
    }
165
  }
166
  else
167
  {
168
    return new BError( "Invalid parameter type" );
×
169
  }
170
}
171

172

173
bool NPCExecutorModule::_internal_move( Core::UFACING facing, int run )
×
174
{
175
  bool success = false;
×
176
  int dir = facing;
×
177
  if ( run )
×
178
    dir |= 0x80;  // FIXME HARDCODE
×
179

180
  if ( npc.could_move( facing ) )
×
181
  {
182
    if ( npc.move(
×
183
             static_cast<unsigned char>( dir ) ) )  // this could still fail, if paralyzed or frozen
184
    {
185
      npc.tellmove();
×
186
      // move was successful
187
      success = true;
×
188
    }
189
    // else, npc could move, but move failed.
190
  }
191
  // else npc could not move
192

193
  return success;
×
194
}
195

196
BObjectImp* NPCExecutorModule::move_self( Core::UFACING facing, bool run, bool adjust_ok )
1✔
197
{
198
  bool success = false;
1✔
199
  int dir = facing;
1✔
200

201
  if ( run )
1✔
202
    dir |= 0x80;  // FIXME HARDCODE
×
203

204
  if ( adjust_ok )
1✔
205
  {
206
    if ( npc.use_adjustments() )
×
207
    {
208
      for ( int adjust : Core::adjustments )
×
209
      {
210
        facing = static_cast<Core::UFACING>( ( dir + adjust ) & 7 );
×
211

212
        success = _internal_move( facing, run );
×
213
        if ( success == true )
×
214
          break;
×
215
      }
216
    }
217
    else
218
    {
219
      success = _internal_move( facing, run );
×
220
    }
221
  }
222
  else
223
  {
224
    if ( npc.anchor_allows_move( facing ) && npc.move( static_cast<unsigned char>( dir ) ) )
1✔
225
    {
226
      npc.tellmove();
1✔
227
      success = true;
1✔
228
    }
229
  }
230

231
  // int delay = 1000 - npc.dexterity() * 3;
232
  int delay = std::max<int>( 1000 - npc.run_speed * 3,
2✔
233
                             Core::settingsManager.ssopt.npc_minimum_movement_delay );
1✔
234
  u32 sleep = static_cast<u32>( delay );
1✔
235
  os_module->SleepForMs( run ? ( sleep / 2 ) : sleep );
1✔
236

237
  // return new String( FacingStr(facing) );
238
  return new BLong( success ? 1 : 0 );
1✔
239
}
240

241
BObjectImp* NPCExecutorModule::mf_Wander()
×
242
{
243
  u8 newfacing = 0;
×
244
  bool adjust_ok = true;
×
245
  switch ( Clib::random_int( 7 ) )
×
246
  {
247
  case 0:
×
248
  case 1:
249
  case 2:
250
  case 3:
251
  case 4:
252
  case 5:
253
    newfacing = npc.facing;
×
254
    break;
×
255
  case 6:
×
256
    newfacing = ( static_cast<int>( npc.facing ) - 1 ) & PKTIN_02_FACING_MASK;
×
257
    adjust_ok = false;
×
258
    break;
×
259
  case 7:
×
260
    newfacing = ( static_cast<int>( npc.facing ) + 1 ) & PKTIN_02_FACING_MASK;
×
261
    adjust_ok = false;
×
262
    break;
×
263
  }
264
  return move_self( static_cast<Core::UFACING>( newfacing ), false, adjust_ok );
×
265
}
266

267
BObjectImp* NPCExecutorModule::mf_Face()
×
268
{
269
  BObjectImp* param0 = exec.getParamImp( 0 );
×
270
  int flags;
271

272
  if ( param0 == nullptr || !exec.getParam( 1, flags ) )
×
273
    return new BError( "Invalid parameter type." );
×
274

275
  Core::UFACING i_facing;
276

277
  if ( auto* s = impptrIf<String>( param0 ) )
×
278
  {
279
    const char* dir = s->data();
×
280

281
    if ( Mobile::DecodeFacing( dir, i_facing ) == false )
×
282
    {
283
      DEBUGLOGLN(
×
284
          "Script Error in '{}' PC={}: \n"
285
          "\tCall to function npc::face():\n"
286
          "\tParameter 0: Expected direction: N S E W NW NE SW SE, got {}",
287
          scriptname(), exec.PC, dir );
×
288
      return nullptr;
×
289
    }
290
  }
291
  else if ( auto* l = impptrIf<BLong>( param0 ) )
×
292
  {
293
    i_facing = static_cast<Core::UFACING>( l->value() & PKTIN_02_FACING_MASK );
×
294
  }
295
  else
296
  {
297
    DEBUGLOGLN(
×
298
        "Script Error in '{}' PC={}: \n"
299
        "\tCall to function npc::face():\n"
300
        "\tParameter 0: Expected direction, , got datatype {}",
301
        scriptname(), exec.PC, BObjectImp::typestr( param0->type() ) );
×
302

303
    return nullptr;
×
304
  }
305

306
  if ( !npc.face( i_facing, flags ) )
×
307
    return new BLong( 0 );
×
308

309
  npc.on_facing_changed();
×
310
  return new BLong( i_facing );
×
311
}
312

313
BObjectImp* NPCExecutorModule::mf_Move()
1✔
314
{
315
  BObjectImp* param0 = exec.getParamImp( 0 );
1✔
316

317
  if ( auto* s = impptrIf<String>( param0 ) )
1✔
318
  {
319
    const char* dir = s->data();
1✔
320
    Core::UFACING facing;
321

322
    if ( Mobile::DecodeFacing( dir, facing ) == false )
1✔
323
    {
324
      DEBUGLOGLN(
×
325
          "Script Error in '{}' PC={}: \n"
326
          "\tCall to function npc::move():\n"
327
          "\tParameter 0: Expected direction: N S E W NW NE SW SE, got {}",
328
          scriptname(), exec.PC, dir );
×
329
      return nullptr;
×
330
    }
331

332
    return move_self( facing, false );
1✔
333
  }
334
  else if ( auto* l = impptrIf<BLong>( param0 ) )
×
335
  {
336
    Core::UFACING facing = static_cast<Core::UFACING>( l->value() & PKTIN_02_FACING_MASK );
×
337
    return move_self( facing, false );
×
338
  }
339
  else if ( param0->isa( BObjectImp::OTApplicObj ) )
×
340
  {
341
    BApplicObjBase* appobj = static_cast<BApplicObjBase*>( param0 );
×
342
    if ( appobj->object_type() == &bounding_box_type )
×
343
    {
344
      BApplicObj<Mobile::BoundingBox>* ao_bbox =
×
345
          static_cast<BApplicObj<Mobile::BoundingBox>*>( appobj );
346
      const Mobile::BoundingBox& bbox = ao_bbox->value();
×
347
      Core::UFACING facing = Mobile::GetRandomFacing();
×
348

349
      auto pos = npc.pos().move( facing );
×
350
      if ( bbox.contains( pos.xy() ) || !bbox.contains( npc.pos2d() ) )
×
351
      {
352
        npc.move( static_cast<unsigned char>( facing ) );
×
353
        npc.tellmove();
×
354
        os_module->SleepFor( 1 );
×
355
        return new String( Mobile::FacingStr( facing ) );
×
356
      }
357
      else
358
      {
359
        return new String( "" );
×
360
      }
361
    }
362
    else
363
    {
364
      DEBUGLOGLN(
×
365
          "Script Error in '{}' PC={}: \n"
366
          "\tCall to function npc::move():\n"
367
          "\tParameter 0: Expected direction or bounding box, , got datatype {}",
368
          scriptname(), exec.PC, BObjectImp::typestr( param0->type() ) );
×
369
      return nullptr;
×
370
    }
371
  }
372
  else
373
  {
374
    DEBUGLOGLN(
×
375
        "Script Error in '{}' PC={}: \n"
376
        "\tCall to function npc::move():\n"
377
        "\tParameter 0: Expected direction or bounding box, , got datatype {}",
378
        scriptname(), exec.PC, BObjectImp::typestr( param0->type() ) );
×
379

380
    return nullptr;
×
381
  }
382
}
383

384
BObjectImp* NPCExecutorModule::mf_WalkToward()
×
385
{
386
  Core::UObject* obj;
387
  if ( getUObjectParam( 0, obj ) )
×
388
  {
389
    if ( obj->ismobile() )
×
390
    {
391
      Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
×
392
      if ( !npc.is_visible_to_me( chr, /*check_range*/ false ) )
×
393
        return new BError( "Mobile specified cannot be seen" );
×
394
    }
395
    Core::UFACING fac = npc.direction_toward( obj );
×
396
    return move_self( fac, false, true );
×
397
  }
398
  else
399
  {
400
    return new BError( "Invalid parameter type" );
×
401
  }
402
}
403

404

405
BObjectImp* NPCExecutorModule::mf_RunToward()
×
406
{
407
  Core::UObject* obj;
408
  if ( getUObjectParam( 0, obj ) )
×
409
  {
410
    if ( obj->ismobile() )
×
411
    {
412
      Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
×
413
      if ( !npc.is_visible_to_me( chr, /*check_range*/ false ) )
×
414
        return new BError( "Mobile specified cannot be seen" );
×
415
    }
416
    return move_self( npc.direction_toward( obj ), true, true );
×
417
  }
418
  else
419
  {
420
    return new BError( "Invalid parameter type" );
×
421
  }
422
}
423

424
BObjectImp* NPCExecutorModule::mf_WalkAwayFrom()
×
425
{
426
  Core::UObject* obj;
427
  if ( getUObjectParam( 0, obj ) )
×
428
  {
429
    if ( obj->ismobile() )
×
430
    {
431
      Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
×
432
      if ( !npc.is_visible_to_me( chr, /*check_range*/ false ) )
×
433
        return new BError( "Mobile specified cannot be seen" );
×
434
    }
435
    return move_self( npc.direction_away( obj ),
×
436

437
                      false, true );
×
438
  }
439
  else
440
  {
441
    return new BError( "Invalid parameter type" );
×
442
  }
443
}
444

445
BObjectImp* NPCExecutorModule::mf_RunAwayFrom()
×
446
{
447
  Core::UObject* obj;
448
  if ( getUObjectParam( 0, obj ) )
×
449
  {
450
    if ( obj->ismobile() )
×
451
    {
452
      Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
×
453
      if ( !npc.is_visible_to_me( chr, /*check_range*/ false ) )
×
454
        return new BError( "Mobile specified cannot be seen" );
×
455
    }
456
    return move_self( npc.direction_away( obj ),
×
457

458
                      true, true );
×
459
  }
460
  else
461
  {
462
    return new BError( "Invalid parameter type" );
×
463
  }
464
}
465

466
BObjectImp* NPCExecutorModule::mf_TurnToward()
×
467
{
468
  Core::UObject* obj;
469
  int flags;
470

471
  if ( !getUObjectParam( 0, obj ) || !exec.getParam( 1, flags ) )
×
472
  {
473
    return new BError( "Invalid parameter type" );
×
474
  }
475

476
  if ( obj->ismobile() )
×
477
  {
478
    Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
×
479
    if ( !npc.is_visible_to_me( chr, /*check_range*/ false ) )
×
480
      return new BError( "Mobile specified cannot be seen" );
×
481
  }
482

483
  Core::UFACING facing = npc.direction_toward( obj );
×
484
  if ( facing == npc.facing )
×
485
    return new BLong( 0 );  // nothing to do here, I'm already facing that direction
×
486

487
  if ( !npc.face( facing, flags ) )
×
488
    return new BLong( 0 );  // Uh-oh, seems that I can't move to face that
×
489

490
  npc.on_facing_changed();
×
491
  return new BLong( 1 );
×
492
}
493

494
BObjectImp* NPCExecutorModule::mf_TurnAwayFrom()
×
495
{
496
  Core::UObject* obj;
497
  int flags;
498

499
  if ( !getUObjectParam( 0, obj ) || !exec.getParam( 1, flags ) )
×
500
  {
501
    return new BError( "Invalid parameter type" );
×
502
  }
503

504

505
  if ( obj->ismobile() )
×
506
  {
507
    Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
×
508
    if ( !npc.is_visible_to_me( chr, /*check_range*/ false ) )
×
509
      return new BError( "Mobile specified cannot be seen" );
×
510
  }
511

512
  Core::UFACING facing = npc.direction_away( obj );
×
513
  if ( facing == npc.facing )
×
514
    return new BLong( 0 );  // nothing to do here
×
515

516
  if ( !npc.face( facing, flags ) )
×
517
    return new BLong( 0 );  // couldn't move for some reason
×
518

519
  npc.on_facing_changed();
×
520
  return new BLong( 1 );
×
521
}
522

523
BObjectImp* NPCExecutorModule::mf_WalkTowardLocation()
×
524
{
525
  Core::Pos2d pos;
×
526
  if ( getPos2dParam( 0, 1, &pos, npc.realm() ) )
×
527
  {
528
    Core::UFACING fac = npc.direction_toward( pos );
×
529
    return move_self( fac, false, true );
×
530
  }
531
  return new BError( "Invalid parameter type" );
×
532
}
533

534
BObjectImp* NPCExecutorModule::mf_RunTowardLocation()
×
535
{
536
  Core::Pos2d pos;
×
537
  if ( getPos2dParam( 0, 1, &pos, npc.realm() ) )
×
538
  {
539
    Core::UFACING fac = npc.direction_toward( pos );
×
540
    return move_self( fac, true, true );
×
541
  }
542
  return new BError( "Invalid parameter type" );
×
543
}
544

545
BObjectImp* NPCExecutorModule::mf_WalkAwayFromLocation()
×
546
{
547
  Core::Pos2d pos;
×
548
  if ( getPos2dParam( 0, 1, &pos, npc.realm() ) )
×
549
  {
550
    Core::UFACING fac = npc.direction_away( pos );
×
551
    return move_self( fac, false, true );
×
552
  }
553
  return new BError( "Invalid parameter type" );
×
554
}
555

556
BObjectImp* NPCExecutorModule::mf_RunAwayFromLocation()
×
557
{
558
  Core::Pos2d pos;
×
559
  if ( getPos2dParam( 0, 1, &pos, npc.realm() ) )
×
560
  {
561
    Core::UFACING fac = npc.direction_away( pos );
×
562
    return move_self( fac, true, true );
×
563
  }
564
  return new BError( "Invalid parameter type" );
×
565
}
566

567
BObjectImp* NPCExecutorModule::mf_TurnTowardLocation()
×
568
{
569
  Core::Pos2d pos;
×
570
  int flags;
571

572
  if ( !getPos2dParam( 0, 1, &pos, npc.realm() ) || !exec.getParam( 2, flags ) )
×
573
  {
574
    return new BError( "Invalid parameter type" );
×
575
  }
576

577
  Core::UFACING fac = npc.direction_toward( pos );
×
578
  if ( npc.facing == fac )
×
579
    return new BLong( 0 );  // nothing to do here
×
580

581
  if ( !npc.face( fac, flags ) )
×
582
    return new BLong( 0 );  // I couldn't move!
×
583

584
  npc.on_facing_changed();
×
585
  return new BLong( 1 );
×
586
}
587

588
BObjectImp* NPCExecutorModule::mf_TurnAwayFromLocation()
×
589
{
590
  Core::Pos2d pos;
×
591
  int flags;
592

593
  if ( !getPos2dParam( 0, 1, &pos, npc.realm() ) || !exec.getParam( 2, flags ) )
×
594
  {
595
    return new BError( "Invalid parameter type" );
×
596
  }
597

598
  Core::UFACING fac = npc.direction_away( pos );
×
599
  if ( npc.facing == fac )
×
600
    return new BLong( 0 );  // nothing to do here
×
601

602
  if ( !npc.face( fac, flags ) )
×
603
    return new BLong( 0 );  // I couldn't move!
×
604

605
  npc.on_facing_changed();
×
606
  return new BLong( 1 );
×
607
}
608

609

610
BObjectImp* NPCExecutorModule::mf_Say()
1✔
611
{
612
  if ( npc.squelched() )
1✔
613
    return new BError( "NPC is squelched" );
×
614
  else if ( npc.hidden() )
1✔
615
    npc.unhide();
×
616

617
  const char* text = exec.paramAsString( 0 );
1✔
618
  std::string texttype_str = Clib::strlowerASCII( exec.paramAsString( 1 ) );
1✔
619
  int doevent;
620
  exec.getParam( 2, doevent );
1✔
621
  u8 texttype;
622
  if ( texttype_str == "default" )
1✔
623
    texttype = Plib::TEXTTYPE_NORMAL;
1✔
624
  else if ( texttype_str == "whisper" )
×
625
    texttype = Plib::TEXTTYPE_WHISPER;
×
626
  else if ( texttype_str == "yell" )
×
627
    texttype = Plib::TEXTTYPE_YELL;
×
628
  else
629
    return new BError( "texttype string param must be either 'default', 'whisper', or 'yell'" );
×
630

631

632
  Network::PktHelper::PacketOut<Network::PktOut_1C> msg;
1✔
633
  Network::PktHelper::PacketOut<Network::PktOut_AE> ucmsg;
1✔
634
  u16 len = 0;
1✔
635
  u16 uclen = 0;
1✔
636
  // switch to other pkt if utf8 found
637
  if ( !Bscript::String::hasUTF8Characters( text ) )
1✔
638
  {
639
    msg->offset += 2;
1✔
640
    msg->Write<u32>( npc.serial_ext );
1✔
641
    msg->WriteFlipped<u16>( npc.graphic );
1✔
642
    msg->Write<u8>( texttype );
1✔
643
    msg->WriteFlipped<u16>( npc.speech_color() );
1✔
644
    msg->WriteFlipped<u16>( npc.speech_font() );
1✔
645
    msg->Write( Clib::strUtf8ToCp1252( npc.name() ).c_str(), 30 );
1✔
646
    msg->Write( text, ( strlen( text ) > SPEECH_MAX_LEN + 1 )
2✔
647
                          ? SPEECH_MAX_LEN + 1
648
                          : static_cast<u16>( strlen( text ) + 1 ) );
1✔
649
    len = msg->offset;
1✔
650
    msg->offset = 1;
1✔
651
    msg->WriteFlipped<u16>( len );
1✔
652
  }
653
  else
654
  {
655
    std::vector<u16> utf16 = Bscript::String::toUTF16( text );
×
656
    if ( utf16.size() > SPEECH_MAX_LEN )
×
657
      utf16.resize( SPEECH_MAX_LEN );
×
658
    ucmsg->offset += 2;
×
659
    ucmsg->Write<u32>( npc.serial_ext );
×
660
    ucmsg->WriteFlipped<u16>( npc.graphic );
×
661
    ucmsg->Write<u8>( texttype );
×
662
    ucmsg->WriteFlipped<u16>( npc.speech_color() );
×
663
    ucmsg->WriteFlipped<u16>( npc.speech_font() );
×
664
    ucmsg->Write( "ENU", 4 );
×
665
    ucmsg->Write( Clib::strUtf8ToCp1252( npc.description() ).c_str(), 30 );
×
666
    ucmsg->WriteFlipped( utf16, true );
×
667
    uclen = ucmsg->offset;
×
668
    ucmsg->offset = 1;
×
669
    ucmsg->WriteFlipped<u16>( uclen );
×
670
  }
×
671

672
  // send to those nearby
673
  u16 range;
674
  if ( texttype == Plib::TEXTTYPE_WHISPER )
1✔
675
    range = Core::settingsManager.ssopt.whisper_range;
×
676
  else if ( texttype == Plib::TEXTTYPE_YELL )
1✔
677
    range = Core::settingsManager.ssopt.yell_range;
×
678
  else
679
    range = Core::settingsManager.ssopt.speech_range;
1✔
680
  Core::WorldIterator<Core::OnlinePlayerFilter>::InRange(
1✔
681
      &npc, range,
1✔
682
      [&]( Mobile::Character* chr )
1✔
683
      {
684
        if ( !chr->is_visible_to_me( &npc, /*check_range*/ false ) )
2✔
685
          return;
×
686
        if ( !uclen )
2✔
687
          msg.Send( chr->client, len );
2✔
688
        else
689
          ucmsg.Send( chr->client, uclen );
×
690
      } );
691

692
  if ( doevent >= 1 )
1✔
693
  {
694
    Core::WorldIterator<Core::NPCFilter>::InRange(
×
695
        &npc, range,
×
696
        [&]( Mobile::Character* chr )
×
697
        {
698
          Mobile::NPC* othernpc = static_cast<Mobile::NPC*>( chr );
×
699
          if ( chr != &npc )
×
700
            othernpc->on_pc_spoke( &npc, text, texttype );
×
701
        } );
×
702
  }
703

704
  return nullptr;
1✔
705
}
1✔
706

707
BObjectImp* NPCExecutorModule::mf_SayUC()
×
708
{
709
  if ( npc.squelched() )
×
710
    return new BError( "NPC is squelched" );
×
711
  else if ( npc.hidden() )
×
712
    npc.unhide();
×
713

714
  const String* text;
715
  const String* lang;
716
  int doevent;
717

718
  if ( getUnicodeStringParam( 0, text ) && getStringParam( 2, lang ) && getParam( 3, doevent ) )
×
719
  {
720
    std::string texttype_str = Clib::strlowerASCII( exec.paramAsString( 1 ) );
×
721
    if ( texttype_str != "default" && texttype_str != "whisper" && texttype_str != "yell" )
×
722
    {
723
      return new BError( "texttype string param must be either 'default', 'whisper', or 'yell'" );
×
724
    }
725

726
    if ( text->length() > SPEECH_MAX_LEN )
×
727
      return new BError( "Text exceeds maximum size." );
×
728
    if ( lang->length() != 3 )
×
729
      return new BError( "langcode must be a 3-character code." );
×
730

731
    std::vector<u16> utf16 = text->toUTF16();
×
732
    if ( utf16.size() > SPEECH_MAX_LEN )
×
733
      utf16.resize( SPEECH_MAX_LEN );
×
734
    std::string languc = Clib::strupperASCII( lang->value() );
×
735

736
    u8 texttype;
737
    if ( texttype_str == "whisper" )
×
738
      texttype = Plib::TEXTTYPE_WHISPER;
×
739
    else if ( texttype_str == "yell" )
×
740
      texttype = Plib::TEXTTYPE_YELL;
×
741
    else
742
      texttype = Plib::TEXTTYPE_NORMAL;
×
743

744
    Network::PktHelper::PacketOut<Network::PktOut_AE> talkmsg;
×
745
    talkmsg->offset += 2;
×
746
    talkmsg->Write<u32>( npc.serial_ext );
×
747
    talkmsg->WriteFlipped<u16>( npc.graphic );
×
748
    talkmsg->Write<u8>( texttype );
×
749
    talkmsg->WriteFlipped<u16>( npc.speech_color() );
×
750
    talkmsg->WriteFlipped<u16>( npc.speech_font() );
×
751
    talkmsg->Write( languc.c_str(), 4 );
×
752
    talkmsg->Write( Clib::strUtf8ToCp1252( npc.description() ).c_str(), 30 );
×
753
    talkmsg->WriteFlipped( utf16, true );
×
754
    u16 len = talkmsg->offset;
×
755
    talkmsg->offset = 1;
×
756
    talkmsg->WriteFlipped<u16>( len );
×
757

758
    u16 range;
759
    if ( texttype == Plib::TEXTTYPE_WHISPER )
×
760
      range = Core::settingsManager.ssopt.whisper_range;
×
761
    else if ( texttype == Plib::TEXTTYPE_YELL )
×
762
      range = Core::settingsManager.ssopt.yell_range;
×
763
    else
764
      range = Core::settingsManager.ssopt.speech_range;
×
765
    Core::WorldIterator<Core::OnlinePlayerFilter>::InRange(
×
766
        &npc, range,
×
767
        [&]( Mobile::Character* chr )
×
768
        {
769
          if ( !chr->is_visible_to_me( &npc, /*check_range*/ false ) )
×
770
            return;
×
771
          talkmsg.Send( chr->client, len );
×
772
        } );
773

774
    if ( doevent >= 1 )
×
775
    {
776
      Core::WorldIterator<Core::NPCFilter>::InRange(
×
777
          &npc, range,
×
778
          [&]( Mobile::Character* chr )
×
779
          {
780
            Mobile::NPC* othernpc = static_cast<Mobile::NPC*>( chr );
×
781
            if ( othernpc != &npc )
×
782
              othernpc->on_pc_spoke( &npc, text->value(), texttype, languc );
×
783
          } );
×
784
    }
785
  }
×
786
  else
787
  {
788
    return new BError( "A parameter was invalid" );
×
789
  }
790
  return nullptr;
×
791
}
792

793
BObjectImp* NPCExecutorModule::mf_position()
×
794
{
795
  std::unique_ptr<BStruct> oa( new BStruct );
×
796

797
  oa->addMember( "x", new BLong( npc.x() ) );
×
798
  oa->addMember( "y", new BLong( npc.y() ) );
×
799
  oa->addMember( "z", new BLong( npc.z() ) );
×
800

801
  return oa.release();
×
802
}
×
803

804
BObjectImp* NPCExecutorModule::mf_Facing()
×
805
{
806
  return new String( Mobile::FacingStr( static_cast<Core::UFACING>( npc.facing ) ) );
×
807
}
808

809
BObjectImp* NPCExecutorModule::mf_GetProperty()
×
810
{
811
  const String* propname_str;
812
  if ( exec.getStringParam( 0, propname_str ) )
×
813
  {
814
    std::string val;
×
815
    if ( npc.getprop( propname_str->value(), val ) )
×
816
    {
817
      return BObjectImp::unpack( val.c_str() );
×
818
    }
819
    else
820
    {
821
      return new BError( "Property not found" );
×
822
    }
823
  }
×
824
  else
825
  {
826
    return new BError( "Invalid parameter type" );
×
827
  }
828
}
829

830
BObjectImp* NPCExecutorModule::mf_SetProperty()
×
831
{
832
  const String* propname_str;
833
  if ( exec.getStringParam( 0, propname_str ) )
×
834
  {
835
    BObjectImp* propval = getParamImp( 1 );
×
836
    npc.setprop( propname_str->value(), propval->pack() );
×
837
    return new BLong( 1 );
×
838
  }
839
  else
840
  {
841
    return new BError( "Invalid parameter type" );
×
842
  }
843
}
844

845
BObjectImp* NPCExecutorModule::mf_CreateBackpack()
×
846
{
847
  // UNTESTED
848
  if ( !npc.layer_is_equipped( Core::LAYER_BACKPACK ) )
×
849
  {
850
    Items::Item* i = Items::Item::create( UOBJ_BACKPACK );
×
851
    std::unique_ptr<Items::Item> item( i );
×
852
    item->layer = Core::LAYER_BACKPACK;
×
853
    if ( npc.equippable( item.get() ) )
×
854
    {
855
      npc.equip( item.release() );
×
856
    }
857
    else
858
      item->destroy();
×
859
  }
×
860
  return new BLong( 1 );
×
861
}
862

863
BObjectImp* NPCExecutorModule::mf_CreateItem()
×
864
{
865
  // UNTESTED
866
  const BLong* objtype = exec.getLongParam( 0 );
×
867
  if ( objtype == nullptr )
×
868
    return new BLong( 0 );
×
869

870
  Core::UContainer* backpack = npc.backpack();
×
871
  if ( backpack == nullptr )
×
872
    return new BLong( 0 );
×
873

874
  std::unique_ptr<Items::Item> item(
875
      Items::Item::create( static_cast<unsigned int>( objtype->value() ) ) );
×
876
  if ( item.get() == nullptr )
×
877
    return new BLong( 0 );
×
878

879
  if ( !backpack->can_add( *item ) )
×
880
    return new BLong( 0 );
×
881

882
  u8 slotIndex = item->slot_index();
×
883
  if ( !backpack->can_add_to_slot( slotIndex ) )
×
884
    return new BLong( 0 );
×
885

886
  if ( !item->slot_index( slotIndex ) )
×
887
    return new BLong( 0 );
×
888

889
  u32 serial = item->serial;
×
890

891
  backpack->add_at_random_location( item.release() );
×
892

893
  return new BLong( serial );
×
894
}
×
895

896
BObjectImp* NPCExecutorModule::mf_MakeBoundingBox( /* areastring */ )
×
897
{
898
  auto arealist = static_cast<String*>( getParamImp( 0, BObjectImp::OTString ) );
×
899
  if ( arealist == nullptr )
×
900
    return new String( "" );
×
901

902
  BoundingBoxObjImp* bbox = new BoundingBoxObjImp;
×
903
  std::unique_ptr<BoundingBoxObjImp> bbox_owner( bbox );
×
904

905
  ISTRINGSTREAM is( arealist->value() );
×
906

907
  u16 tlx, tly, brx, bry;
908
  // FIXME this is a terrible data format.
909
  while ( is >> tlx >> tly >> brx >> bry )
×
910
  {
911
    ( *bbox )->addarea(
×
912
        Core::Range2d( Core::Pos2d{ tlx, tly }, Core::Pos2d{ brx, bry }, npc.realm() ) );
×
913
  }
914

915
  return bbox_owner.release();
×
916
}
×
917

918
BObjectImp* NPCExecutorModule::mf_SetOpponent()
11✔
919
{
920
  Mobile::Character* chr;
921
  if ( getCharacterParam( 0, chr ) && chr != &npc )
11✔
922
  {
923
    npc.set_opponent( chr );
4✔
924
    return new BLong( 1 );
4✔
925
  }
926
  else
927
  {
928
    npc.set_opponent( nullptr );
7✔
929
    return new BLong( 0 );
7✔
930
  }
931
}
932

933
BObjectImp* NPCExecutorModule::mf_SetWarMode()
×
934
{
935
  int warmode;
936
  if ( exec.getParam( 0, warmode ) )
×
937
  {
938
    npc.set_warmode( warmode != 0 );
×
939
    return new BLong( 1 );
×
940
  }
941
  else
942
  {
943
    return new BLong( 0 );
×
944
  }
945
}
946

947
size_t NPCExecutorModule::sizeEstimate() const
×
948
{
949
  return sizeof( *this );
×
950
}
951
}  // namespace Module
952
}  // 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