• 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

10.42
/pol-core/pol/spells.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2009/09/20 MuadDib:   docast no longer unhides. Let scripts handle this.
5
 * - 2010/01/15 Turley:    (Tomi) SpeakPowerWords font and color params
6
 */
7

8

9
#include "spells.h"
10

11
#include <iterator>
12
#include <stdlib.h>
13
#include <time.h>
14

15
#include "../bscript/eprog.h"
16
#include "../clib/cfgelem.h"
17
#include "../clib/cfgfile.h"
18
#include "../clib/fileutil.h"
19
#include "../clib/logfacility.h"
20
#include "../clib/refptr.h"
21
#include "../clib/stlutil.h"
22
#include "../plib/pkg.h"
23
#include "../plib/systemstate.h"
24
#include "containr.h"
25
#include "globals/state.h"
26
#include "globals/uvars.h"
27
#include "item/item.h"
28
#include "item/itemdesc.h"
29
#include "layers.h"
30
#include "mkscrobj.h"
31
#include "mobile/charactr.h"
32
#include "network/client.h"
33
#include "network/pktin.h"
34
#include "polclass.h"
35
#include "polsig.h"
36
#include "regions/miscrgn.h"
37
#include "scrstore.h"
38
#include "skillid.h"
39
#include "spelbook.h"
40
#include "syshook.h"
41
#include "ufunc.h"
42
#include "umanip.h"
43
#include "vital.h"
44

45

46
namespace Pol::Core
47
{
48
static bool nocast_here( Mobile::Character* chr )
×
49
{
50
  NoCastRegion* rgn = gamestate.nocastdef->getregion( chr->pos() );
×
51
  if ( rgn == nullptr )
×
52
  {
53
    return false;
×
54
  }
55

NEW
56
  return rgn->nocast();
×
57
}
58

59
bool knows_spell( Mobile::Character* chr, u16 spellid )
×
60
{
61
  // copied code from Character::spellbook to support multiple spellbooks in the pack
62
  Items::Item* item = chr->wornitem( LAYER_HAND1 );
×
63
  if ( item != nullptr && item->script_isa( POLCLASS_SPELLBOOK ) )
×
64
  {
65
    Spellbook* book = static_cast<Spellbook*>( item );
×
66
    if ( book->has_spellid( spellid ) )
×
67
      return true;
×
68
  }
69

70
  UContainer* cont = chr->backpack();
×
71
  if ( cont != nullptr )
×
72
  {
73
    for ( UContainer::const_iterator itr = cont->begin(), end = cont->end(); itr != end; ++itr )
×
74
    {
75
      const Items::Item* _item = *itr;
×
76

77
      if ( _item != nullptr && _item->script_isa( POLCLASS_SPELLBOOK ) )
×
78
      {
79
        const Spellbook* book = static_cast<const Spellbook*>( _item );
×
80
        if ( book->has_spellid( spellid ) )
×
81
          return true;
×
82
      }
83
    }
84
  }
85

86
  return false;
×
87
}
88

89
bool hands_are_free( Mobile::Character* chr )
×
90
{
91
  Items::Item* item;
92

93
  item = chr->wornitem( LAYER_HAND1 );
×
94
  if ( item != nullptr )
×
95
  {
96
    const Items::ItemDesc& id = item->itemdesc();
×
97
    if ( id.blocks_casting_if_in_hand )
×
98
      return false;
×
99
  }
100

101
  item = chr->wornitem( LAYER_HAND2 );
×
102
  if ( item != nullptr )
×
103
  {
104
    const Items::ItemDesc& id = item->itemdesc();
×
105
    if ( id.blocks_casting_if_in_hand )
×
106
      return false;
×
107
  }
108

109
  return true;
×
110
}
111

112
USpellParams::USpellParams() : manacost( 0 ), difficulty( 0 ), pointvalue( 0 ), delay( 0 ) {}
×
113

114
USpellParams::USpellParams( Clib::ConfigElem& elem )
×
115
    : manacost( elem.remove_ushort( "MANA" ) ),
×
116
      difficulty( elem.remove_ushort( "DIFFICULTY" ) ),
×
117
      pointvalue( elem.remove_ushort( "POINTVALUE" ) ),
×
118
      delay( elem.remove_ushort( "DELAY" ) )
×
119
{
120
}
×
121

122

123
SpellCircle::SpellCircle( Clib::ConfigElem& elem ) : params( elem ) {}
×
124

125
USpell::USpell( Clib::ConfigElem& elem, Plib::Package* pkg )
×
126
    : pkg_( pkg ),
×
127
      spellid_( elem.remove_ushort( "SPELLID" ) ),
×
128
      name_( elem.remove_string( "NAME" ) ),
×
129
      power_words_( elem.remove_string( "POWERWORDS" ) ),
×
130
      scriptdef_( elem.remove_string( "SCRIPT", "" ), pkg, "scripts/" )
×
131
{
132
  unsigned short action;
133
  if ( elem.remove_prop( "ANIMATION", &action ) )
×
134
  {
135
    if ( UACTION_IS_VALID( action ) )
×
136
    {
137
      action_ = static_cast<UACTION>( action );
×
138
    }
139
    else
140
    {
141
      elem.throw_error( "Animation is out of range" );
×
142
    }
143
  }
144
  else
145
  {
146
    action_ = ACTION_CAST_SPELL1;
×
147
  }
148

149
  unsigned short circle;
150
  if ( elem.remove_prop( "CIRCLE", &circle ) )
×
151
  {
152
    if ( circle < 1 || circle > gamestate.spellcircles.size() ||
×
153
         gamestate.spellcircles[circle - 1] == nullptr )
×
154
    {
155
      ERROR_PRINTLN( "Error reading spell {}: Circle {} is not defined.", name_, circle );
×
156
      throw std::runtime_error( "Config file error" );
×
157
    }
158

159
    params_ = gamestate.spellcircles[circle - 1]->params;
×
160
  }
161
  else
162
  {
163
    params_ = USpellParams( elem );
×
164
  }
165

166
  std::string reagent_name;
×
167
  while ( elem.remove_prop( "Reagent", &reagent_name ) )
×
168
  {
169
    unsigned int reagent = Items::get_objtype_from_string( reagent_name );
×
170

171
    reglist_.push_back( reagent );
×
172
  }
173
}
×
174

175
size_t USpell::estimateSize() const
×
176
{
177
  size_t size = sizeof( Plib::Package* )                                         /*pkg_*/
178
                + sizeof( unsigned short )                                       /*spellid_*/
179
                + name_.capacity() + power_words_.capacity() + sizeof( UACTION ) /*action_*/
×
180
                + Clib::memsize( reglist_ ) + sizeof( USpellParams )             /*params_*/
×
181
                + scriptdef_.estimatedSize();
×
182
  return size;
×
183
}
184

185
void USpell::cast( Mobile::Character* chr )
×
186
{
187
  if ( nocast_here( chr ) )
×
188
  {
189
    if ( chr->client != nullptr )
×
190
      send_sysmessage( chr->client, "Spells cannot be cast here." );
×
191
    return;
×
192
  }
193

194
  if ( !scriptdef_.empty() )
×
195
  {
196
    ref_ptr<Bscript::EScriptProgram> prog =
197
        find_script2( scriptdef_, true, Plib::systemstate.config.cache_interactive_scripts );
×
198

199
    if ( prog.get() != nullptr )
×
200
    {
201
      if ( chr->start_spell_script( prog.get(), this ) )
×
202
        return;
×
203
    }
204
  }
×
205

206
  if ( chr->client != nullptr )
×
207
    send_sysmessage( chr->client, "That spell doesn't seem to work." );
×
208
}
209

210
bool USpell::consume_reagents( Mobile::Character* chr )
×
211
{
212
  UContainer* bp = chr->backpack();
×
213
  if ( bp == nullptr )
×
214
    return false;
×
215

216
  for ( RegList::iterator itr = reglist_.begin(), end = reglist_.end(); itr != end; ++itr )
×
217
  {
218
    Items::Item* item = bp->find_objtype_noninuse( *itr );
×
219
    if ( item == nullptr )
×
220
      return false;
×
221
    subtract_amount_from_item( item, 1 );
×
222
  }
223

224
  return true;
×
225
}
226

227
bool USpell::check_mana( Mobile::Character* chr )
×
228
{
229
  return ( chr->vital( gamestate.pVitalMana->vitalid ).current_ones() >= manacost() );
×
230
}
231

232
void USpell::consume_mana( Mobile::Character* chr )
×
233
{
234
  chr->consume( gamestate.pVitalMana, chr->vital( gamestate.pVitalMana->vitalid ), manacost() * 100,
×
235
                Mobile::Character::VitalDepletedReason::SCRIPT );
236
}
×
237

238
void USpell::speak_power_words( Mobile::Character* chr, unsigned short font, unsigned short color )
×
239
{
240
  if ( chr->client != nullptr && chr->hidden() )
×
241
  {
242
    private_say_above( chr, chr, power_words_.c_str(), font, color );
×
243
  }
244
  else if ( !chr->hidden() )
×
245
  {
246
    say_above( chr, power_words_.c_str(), font, color );
×
247
  }
248
}
×
249

250
SpellTask::SpellTask( OneShotTask** handle, polclock_t run_when_clock, Mobile::Character* caster,
×
251
                      USpell* spell, bool /*dummy*/ )
×
252
    : OneShotTask( handle, run_when_clock ), caster_( caster ), spell_( spell )
×
253
{
254
}
×
255

256
void SpellTask::on_run()
×
257
{
258
  THREAD_CHECKPOINT( tasks, 900 );
×
259
  Mobile::Character* caster = caster_.get();
×
260
  if ( !caster->orphan() )
×
261
  {
262
    THREAD_CHECKPOINT( tasks, 911 );
×
263
    spell_->cast( caster );
×
264
    THREAD_CHECKPOINT( tasks, 912 );
×
265
  }
266
  THREAD_CHECKPOINT( tasks, 999 );
×
267
}
×
268

269
void do_cast( Network::Client* client, u16 spellid )
×
270
{
271
  if ( gamestate.system_hooks.on_cast_hook )
×
272
  {
273
    if ( gamestate.system_hooks.on_cast_hook->call( make_mobileref( client->chr ),
×
274
                                                    new Bscript::BLong( spellid ) ) )
×
275
      return;
×
276
  }
277
  // CHECKME should this look at spellnum, instead? static_cast behavior undefined if out of range.
278
  if ( spellid >= gamestate.spells.size() )
×
279
    return;
×
280

281
  USpell* spell = gamestate.spells[spellid];
×
282
  if ( spell == nullptr )
×
283
  {
284
    ERROR_PRINTLN( "Spell {} is not implemented.", spellid );
×
285
    send_sysmessage( client, "That spell does not function." );
×
286
    return;
×
287
  }
288

289
  // Let scripts handle this.
290
  // if (client->chr->hidden())
291
  //   client->chr->unhide();
292

293
  if ( client->chr->frozen() )
×
294
  {
295
    private_say_above( client->chr, client->chr, "I am frozen and cannot cast spells" );
×
296
    return;
×
297
  }
298

299
  if ( client->chr->paralyzed() )
×
300
  {
301
    private_say_above( client->chr, client->chr, "I am paralyzed and cannot cast spells" );
×
302
    return;
×
303
  }
304

305
  if ( client->chr->skill_ex_active() )
×
306
  {
307
    send_sysmessage( client, "You are already doing something else." );
×
308
    return;
×
309
  }
310

311
  if ( client->chr->casting_spell() )
×
312
  {
313
    send_sysmessage( client, "You are already casting a spell." );
×
314
    return;
×
315
  }
316

317
  if ( nocast_here( client->chr ) )
×
318
  {
319
    send_sysmessage( client, "Spells cannot be cast here." );
×
320
    return;
×
321
  }
322

323
  if ( Plib::systemstate.config.require_spellbooks )
×
324
  {
325
    if ( !knows_spell( client->chr, spellid ) )
×
326
    {
327
      send_sysmessage( client, "You don't know that spell." );
×
328
      return;
×
329
    }
330
  }
331

332
#if 1
333
  client->chr->schedule_spell( spell );
×
334
#else
335
  spell->cast( client );
336
  client->restart();
337
#endif
338
}
339

340
void handle_cast_spell( Network::Client* client, PKTIN_12* msg )
×
341
{
342
  u16 spellnum = static_cast<u16>( strtoul( (char*)msg->data, nullptr, 10 ) );
×
343

344
  do_cast( client, spellnum );
×
345
}
×
346

347

348
void handle_open_spellbook( Network::Client* client, PKTIN_12* /*msg*/ )
×
349
{
350
  if ( gamestate.system_hooks.open_spellbook_hook != nullptr )
×
351
  {
352
    if ( gamestate.system_hooks.open_spellbook_hook->call( make_mobileref( client->chr ) ) )
×
353
      return;
×
354
  }
355

356
  if ( client->chr->dead() )
×
357
  {
358
    send_sysmessage( client, "I am dead and cannot do that." );
×
359
    return;
×
360
  }
361

362

363
  Items::Item* spellbook = client->chr->wornitem( LAYER_HAND1 );
×
364
  if ( spellbook == nullptr )
×
365
  {
366
    UContainer* backpack = client->chr->backpack();
×
367
    if ( backpack != nullptr )
×
368
    {
369
      spellbook = backpack->find_toplevel_polclass( POLCLASS_SPELLBOOK );
×
370

371
      //
372
      // Client crashes if the pack isn't open and you don't tell him
373
      // about the spellbook
374
      //
375
      if ( spellbook != nullptr )
×
376
        send_put_in_container( client, spellbook );
×
377
    }
378
  }
379

380
  if ( spellbook != nullptr )
×
381
  {
382
    spellbook->double_click( client );
×
383
  }
384
}
385

386
void register_spell( USpell* spell, unsigned short spellid )
×
387
{
388
  if ( spellid >= gamestate.spells.size() )
×
389
  {
390
    gamestate.spells.resize( spellid + 1, nullptr );
×
391
  }
392

393
  if ( gamestate.spells[spellid] )
×
394
  {
395
    USpell* origspell = gamestate.spells[spellid];
×
396
    std::string tmp =
397
        fmt::format( "Spell ID {} ({}) multiply defined", spellid, origspell->name() );
×
398
    if ( origspell->pkg_ != nullptr )
×
399
    {
400
      fmt::format_to( std::back_inserter( tmp ),
×
401
                      "\n  Spell originally defined in package '{}' ({})", origspell->pkg_->name(),
×
402
                      origspell->pkg_->dir() );
×
403
    }
404
    else
405
    {
406
      tmp += "\n  Spell originally defined in main";
×
407
    }
408
    if ( spell->pkg_ != nullptr )
×
409
    {
410
      fmt::format_to( std::back_inserter( tmp ), "\n  Spell redefined in package '{}' ({})",
×
411
                      spell->pkg_->name(), spell->pkg_->dir() );
×
412
    }
413
    else
414
    {
415
      tmp += "\n  Spell redefined in main";
×
416
    }
417
    ERROR_PRINTLN( tmp );
×
418
    throw std::runtime_error( "Spell ID multiply defined" );
×
419
  }
×
420

421
  gamestate.spells[spellid] = spell;
×
422
}
×
423

424

425
void load_circle_data()
3✔
426
{
427
  if ( !Clib::FileExists( "config/circles.cfg" ) )
3✔
428
  {
429
    if ( Plib::systemstate.config.loglevel > 1 )
3✔
430
      INFO_PRINTLN( "File config/circles not found, skipping." );
3✔
431
    return;
3✔
432
  }
433

434
  Clib::ConfigFile cf( "config/circles.cfg", "Circle" );
×
435
  Clib::ConfigElem elem;
×
436

437
  while ( cf.read( elem ) )
×
438
  {
439
    int index = strtoul( elem.rest(), nullptr, 0 ) - 1;
×
440
    if ( index < 0 || index >= 100 )
×
441
    {
442
      ERROR_PRINTLN( "Error in CIRCLES.CFG: Circle must fall between 1 and 100" );
×
443
      throw std::runtime_error( "Config file error" );
×
444
    }
445

446
    gamestate.spellcircles.resize( index + 1, nullptr );
×
447

448
    if ( gamestate.spellcircles[index] != nullptr )
×
449
    {
450
      ERROR_PRINTLN( "Error in CIRCLES.CFG: Circle {} is multiply defined.", index + 1 );
×
451
      throw std::runtime_error( "Config file error" );
×
452
    }
453

454
    gamestate.spellcircles[index] = new SpellCircle( elem );
×
455
  }
456
}
×
457

458
void load_spells_cfg( const char* path, Plib::Package* pkg )
×
459
{
460
  Clib::ConfigFile cf( path, "Spell" );
×
461
  Clib::ConfigElem elem;
×
462

463
  while ( cf.read( elem ) )
×
464
  {
465
    std::unique_ptr<USpell> spell( new USpell( elem, pkg ) );
×
466

467
    unsigned short spellid = spell->spell_id();
×
468

469
    register_spell( spell.release(), spellid );
×
470
  }
×
471
}
×
472

473
void load_spell_data()
3✔
474
{
475
  load_circle_data();
3✔
476

477
  if ( Clib::FileExists( "config/spells.cfg" ) )
3✔
478
    load_spells_cfg( "config/spells.cfg", nullptr );
×
479
  else if ( Plib::systemstate.config.loglevel > 1 )
3✔
480
    INFO_PRINTLN( "File config/spells.cfg not found, skipping" );
3✔
481

482
  for ( Plib::Packages::iterator itr = Plib::systemstate.packages.begin();
3✔
483
        itr != Plib::systemstate.packages.end(); ++itr )
60✔
484
  {
485
    Plib::Package* pkg = ( *itr );
57✔
486
    std::string filename = Plib::GetPackageCfgPath( pkg, "spells.cfg" );
57✔
487
    if ( Clib::FileExists( filename.c_str() ) )
57✔
488
    {
489
      load_spells_cfg( filename.c_str(), pkg );
×
490
    }
491
  }
57✔
492
}
3✔
493

494
void clean_spells()
3✔
495
{
496
  std::vector<SpellCircle*>::iterator c_iter = gamestate.spellcircles.begin();
3✔
497
  for ( ; c_iter != gamestate.spellcircles.end(); ++c_iter )
3✔
498
  {
499
    delete *c_iter;
×
500
    *c_iter = nullptr;
×
501
  }
502
  gamestate.spellcircles.clear();
3✔
503
  std::vector<USpell*>::iterator s_iter = gamestate.spells.begin();
3✔
504
  for ( ; s_iter != gamestate.spells.end(); ++s_iter )
3✔
505
  {
506
    delete *s_iter;
×
507
    *s_iter = nullptr;
×
508
  }
509
  gamestate.spells.clear();
3✔
510
}
3✔
511
}  // 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