• 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

21.15
/pol-core/pol/spelbook.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/02/14 Shinigami: double_click - simple logical error in layer_is_equipped check
5
 * - 2009/07/26 MuadDib:   Packet struct refactoring.
6
 * - 2009/09/17 MuadDib:   Spellbook::can_add upgraded to check bitflags instead of contents.
7
 * - 2009/09/18 MuadDib:   Spellbook rewrite to deal with only bits, not scrolls inside them.
8
 * - 2009/10/10 Turley:    Added spellbook.addspell() & .removespell() methods
9
 */
10

11
#include "spelbook.h"
12

13
#include <sstream>
14
#include <stddef.h>
15

16
#include "../bscript/executor.h"
17
#include "../clib/cfgelem.h"
18
#include "../clib/logfacility.h"
19
#include "../clib/streamsaver.h"
20
#include "../plib/systemstate.h"
21
#include "../plib/uoexpansion.h"
22
#include "baseobject.h"
23
#include "globals/uvars.h"
24
#include "item/itemdesc.h"
25
#include "mobile/charactr.h"
26
#include "network/client.h"
27
#include "network/packethelper.h"
28
#include "network/packets.h"
29
#include "polclass.h"
30
#include "syshookscript.h"
31
#include "ufunc.h"
32
#include "uobject.h"
33

34

35
namespace Pol::Core
36
{
37
Spellbook::Spellbook( const Items::SpellbookDesc& descriptor )
2✔
38
    : UContainer( descriptor ), spell_school( 0 )
2✔
39
{
40
  if ( descriptor.spelltype == "Magic" )
2✔
41
    spell_school = 0;
2✔
42
  else if ( descriptor.spelltype == "Necro" )
×
43
    spell_school = 1;
×
44
  else if ( descriptor.spelltype == "Paladin" )
×
45
    spell_school = 2;
×
46
  // Use spell school 3 for Mysticism with spellid 678+ because on OSI nothing uses this so we can
47
  // use this here.
48
  else if ( descriptor.spelltype == "Mysticism" )
×
49
    spell_school = 3;
×
50
  else if ( descriptor.spelltype == "Bushido" )
×
51
    spell_school = 4;
×
52
  else if ( descriptor.spelltype == "Ninjitsu" )
×
53
    spell_school = 5;
×
54
  else if ( descriptor.spelltype == "SpellWeaving" )
×
55
    spell_school = 6;
×
56
  else if ( descriptor.spelltype == "BardMasteries" )
×
57
    spell_school = 7;
×
58
  for ( int i = 0; i < 8; ++i )
18✔
59
    bitwise_contents[i] = 0;
16✔
60
}
2✔
61

62
Spellbook::~Spellbook() = default;
4✔
63

64
size_t Spellbook::estimatedSize() const
×
65
{
66
  return sizeof( u8 ) * 8 /* bitwise_contents*/
67
         + sizeof( u8 )   /*spell_school*/
68
         + base::estimatedSize();
×
69
}
70

71
void Spellbook::double_click( Network::Client* client )
×
72
{
73
  if ( client->chr->is_equipped( this ) )
×
74
  {
75
    send_put_in_container( client, this );
×
76
    send_wornitem( client, client->chr, this );
×
77
  }
78
  else if ( container != nullptr )
×
79
    send_put_in_container( client, this );
×
80
  else
81
  {
82
    send_sysmessage( client, "Spellbooks must be equipped or in the top level backpack to use." );
×
83
    return;
×
84
  }
85

86
  if ( bitwise_contents[0] == 0 )  // assume never been clicked using the new bitwise spell scheme
×
87
    calc_current_bitwise_contents();
×
88

89
  if ( !client->acctSupports( Plib::ExpansionVersion::AOS ) && ( spell_school == 0 ) )
×
90
  {
91
    send_book_old( client );
×
92
  }
93
  else if ( !client->acctSupports( Plib::ExpansionVersion::AOS ) &&
×
94
            ( spell_school == 1 || spell_school == 2 ) )
×
95
  {
96
    send_sysmessage( client, "This item requires at least the Age of Shadows Expansion." );
×
97
    return;
×
98
  }
99
  else if ( !client->acctSupports( Plib::ExpansionVersion::SE ) &&
×
100
            ( spell_school == 4 || spell_school == 5 ) )
×
101
  {
102
    send_sysmessage( client, "This item requires at least the Samurai Empire Expansion." );
×
103
    return;
×
104
  }
105
  else if ( !client->acctSupports( Plib::ExpansionVersion::ML ) && spell_school == 6 )
×
106
  {
107
    send_sysmessage( client, "This item requires at least the Mondain's Legacy Expansion." );
×
108
    return;
×
109
  }
110
  else if ( !client->acctSupports( Plib::ExpansionVersion::SA ) &&
×
111
            ( spell_school == 3 || spell_school == 7 ) )
×
112
  {
113
    send_sysmessage( client, "This item requires at least the Stygian Abyss Expansion." );
×
114
    return;
×
115
  }
116
  else
117
  {
118
    // Ok, now we do a strange check. This is for those people who have no idea that you
119
    // must have AOS Features Enabled on an acct with AOS Expansion to view Magery book.
120
    // All newer spellbooks will bug out if you use this method though.
121
    if ( client->acctSupports( Plib::ExpansionVersion::AOS ) && ( spell_school == 0 ) &&
×
122
         !settingsManager.ssopt.features.supportsAOS() )
×
123
    {
124
      if ( Plib::systemstate.config.loglevel > 1 )
×
125
        INFO_PRINTLN(
×
126
            "Client with AOS Expansion Account using spellbook without UOFeatureEnable "
127
            "0x20 Bitflag." );
128
      send_book_old( client );
×
129
      return;
×
130
    }
131

132
    send_open_gump( client, *this );
×
133

134
    Network::PktHelper::PacketOut<Network::PktOut_BF_Sub1B> msg;
×
135
    msg->WriteFlipped<u16>( 23u );
×
136
    msg->offset += 2;  // sub
×
137
    msg->WriteFlipped<u16>( 1u );
×
138
    msg->Write<u32>( serial_ext );
×
139
    msg->WriteFlipped<u16>( graphic );
×
140

141
    // Check Mysticism spell here
142
    if ( spell_school == 3 )
×
143
      msg->WriteFlipped<u16>( 678u );
×
144
    else
145
      msg->WriteFlipped<u16>( ( spell_school * 100u ) + 1u );
×
146

147
    msg->Write( bitwise_contents, 8 );
×
148
    msg.Send( client );
×
149
  }
×
150
}
151

152
bool Spellbook::has_spellid( unsigned int spellid ) const
4✔
153
{
154
  u8 spellschool;
155

156
  // Check Mysticism here
157
  if ( spellid >= 678 && spellid <= 693 )
4✔
158
    spellschool = 3;
×
159
  else
160
    spellschool = static_cast<u8>( spellid / 100 );
4✔
161

162
  if ( spellschool == this->spell_school )
4✔
163
  {
164
    u16 spellnumber;
165

166
    // Check Mysticism here
167
    if ( spellid >= 678 && spellid <= 693 )
4✔
168
      spellnumber = static_cast<u16>( spellid - 677 );
×
169
    else
170
      spellnumber = static_cast<u16>( spellid % 100 );
4✔
171

172
    // Limits spellnumber to be between 1-64
173
    spellnumber = spellnumber & 63;
4✔
174
    if ( spellnumber == 0 )
4✔
175
      spellnumber = 64;
×
176

177
    u8 spellslot = spellnumber & 7;
4✔
178
    if ( spellslot == 0 )
4✔
179
      spellslot = 8;
×
180

181
    if ( ( ( bitwise_contents[( spellnumber - 1 ) >> 3] ) & ( 1 << ( spellslot - 1 ) ) ) != 0 )
4✔
182
      return true;
2✔
183
  }
184
  return false;
2✔
185
}
186

187
bool Spellbook::remove_spellid( unsigned int spellid )
×
188
{
189
  if ( has_spellid( spellid ) )
×
190
  {
191
    u16 spellnumber;
192

193
    // Check Mysticism here
194
    if ( spellid >= 678 && spellid <= 693 )
×
195
      spellnumber = static_cast<u16>( spellid - 677 );
×
196
    else
197
      spellnumber = static_cast<u16>( spellid % 100 );
×
198

199
    // Limits spellnumber to be between 1-64
200
    spellnumber = spellnumber & 63;
×
201
    if ( spellnumber == 0 )
×
202
      spellnumber = 64;
×
203

204
    u8 spellslot = spellnumber & 7;
×
205
    if ( spellslot == 0 )
×
206
      spellslot = 8;
×
207
    bitwise_contents[( spellnumber - 1 ) >> 3] &= ~( 1 << ( spellslot - 1 ) );
×
208
    return true;
×
209
  }
210
  return false;
×
211
}
212

213
bool Spellbook::add_spellid( unsigned int spellid )
2✔
214
{
215
  if ( !has_spellid( spellid ) )
2✔
216
  {
217
    u16 spellnumber;
218

219
    // Check Mysticism here
220
    if ( spellid >= 678 && spellid <= 693 )
2✔
221
      spellnumber = static_cast<u16>( spellid - 677 );
×
222
    else
223
      spellnumber = static_cast<u16>( spellid % 100 );
2✔
224

225
    // Limits spellnumber to be between 1-64
226
    spellnumber = spellnumber & 63;
2✔
227
    if ( spellnumber == 0 )
2✔
228
      spellnumber = 64;
×
229

230
    u8 spellslot = spellnumber & 7;
2✔
231
    if ( spellslot == 0 )
2✔
232
      spellslot = 8;
×
233
    bitwise_contents[( spellnumber - 1 ) >> 3] |= 1 << ( spellslot - 1 );
2✔
234
    return true;
2✔
235
  }
236
  return false;
×
237
}
238

239
bool Spellbook::can_add( const Item& item ) const
×
240
{
241
  // note: item count maximums are implicitly checked for.
242

243
  // you can only add scrolls to spellbooks
244
  if ( item.objtype_ < gamestate.spell_scroll_objtype_limits[spell_school][0] ||
×
245
       item.objtype_ > gamestate.spell_scroll_objtype_limits[spell_school][1] )
×
246
  {
247
    return false;
×
248
  }
249

250
  // you can only add one of each kind of scroll to a spellbook.
251
  u16 spellnum = USpellScroll::convert_objtype_to_spellnum( item.objtype_, spell_school );
×
252
  u8 spellslot = spellnum & 7;
×
253
  if ( spellslot == 0 )
×
254
    spellslot = 8;
×
255
  if ( bitwise_contents[( spellnum - 1 ) >> 3] & ( 1 << ( spellslot - 1 ) ) )
×
256
    return false;
×
257

258
  return true;
×
259
}
260

261
void Spellbook::add_bulk( int /* item_count_delta */, int /* weight_delta */ )
×
262
{
263
  // spellbooks don't modify their weight, either when adding
264
  // or when removing an item.
265
}
×
266

267
void Spellbook::add( Item* item, const Pos2d& )
×
268
{
269
  // UContainer::add(item);
270
  u16 spellnum = USpellScroll::convert_objtype_to_spellnum( item->objtype_, spell_school );
×
271
  u8 spellslot = spellnum & 7;
×
272
  if ( spellslot == 0 )
×
273
    spellslot = 8;
×
274
  bitwise_contents[( spellnum - 1 ) >> 3] |= 1 << ( spellslot - 1 );
×
275
  item->destroy();
×
276
  // item->saveonexit(0);
277
}
×
278

279
void Spellbook::printProperties( Clib::StreamWriter& sw ) const
2✔
280
{
281
  base::printProperties( sw );
2✔
282

283
  for ( int i = 0; i < 8; ++i )
18✔
284
    sw.add( fmt::format( FMT_COMPILE( "Spellbits{}" ), i ), (int)bitwise_contents[i] );
64✔
285
}
2✔
286

287

288
void Spellbook::readProperties( Clib::ConfigElem& elem )
1✔
289
{
290
  base::readProperties( elem );
1✔
291
  std::ostringstream os;
1✔
292
  for ( int i = 0; i < 8; ++i )
9✔
293
  {
294
    os << "Spellbits" << i;
8✔
295
    bitwise_contents[i] = (u8)elem.remove_ushort( os.str().c_str(), 0 );
8✔
296
    os.str( "" );
8✔
297
  }
298
}
1✔
299

300

301
void Spellbook::printOn( Clib::StreamWriter& sw ) const
2✔
302
{
303
  base::printOn( sw );
2✔
304
  printContents( sw );
2✔
305
}
2✔
306

307
void Spellbook::printSelfOn( Clib::StreamWriter& sw ) const
×
308
{
309
  base::printOn( sw );
×
310
}
×
311

312

313
void Spellbook::calc_current_bitwise_contents()
×
314
{
315
  for ( UContainer::const_iterator itr = begin(), itrend = end(); itr != itrend; ++itr )
×
316
  {
317
    const Item* scroll = *itr;
×
318
    u16 spellnum = USpellScroll::convert_objtype_to_spellnum( scroll->objtype_, spell_school );
×
319
    u8 spellslot = spellnum & 7;
×
320
    if ( spellslot == 0 )
×
321
      spellslot = 8;
×
322
    bitwise_contents[( spellnum - 1 ) >> 3] |= 1 << ( spellslot - 1 );
×
323
  }
324

325
  // ok, it's been upgraded. Destroy everything inside it.
326
  for ( UContainer::iterator itr = begin(), itrend = end(); itr != itrend; ++itr )
×
327
  {
328
    Item* scroll = *itr;
×
329
    scroll->destroy();
×
330
  }
331
}
×
332

333
USpellScroll::USpellScroll( const Items::ItemDesc& itemdesc )
×
334
    : Item( itemdesc, UOBJ_CLASS::CLASS_ITEM )
×
335
{
336
}
×
337

338
u16 USpellScroll::convert_objtype_to_spellnum( u32 objtype, u8 school )
×
339
{
340
  u16 spellnum = static_cast<u16>( objtype - gamestate.spell_scroll_objtype_limits[school][0] + 1 );
×
341
  if ( school == 0 )  // weirdness in order of original spells
×
342
  {
343
    if ( spellnum == 1 )
×
344
      spellnum = 7;
×
345
    else if ( spellnum <= 7 )
×
346
      --spellnum;
×
347
  }
348
  return spellnum;
×
349
}
350

351
// Spell scrolls send their spell ID in AMOUNT while they're in a spellbook.
352
// Otherwise, they're stackable I believe.
353
u16 USpellScroll::get_senditem_amount() const
×
354
{
355
  if ( ( container != nullptr ) && ( container->script_isa( POLCLASS_SPELLBOOK ) ) )
×
356
  {
357
    Spellbook* book = static_cast<Spellbook*>( container );
×
358
    return convert_objtype_to_spellnum( objtype_, book->spell_school );
×
359
  }
360
  // not contained, or not in a spellbook
NEW
361
  return amount_;
×
362
}
363
size_t USpellScroll::estimatedSize() const
×
364
{
365
  return base::estimatedSize();
×
366
}
367

368
void Spellbook::send_book_old( Network::Client* client )
×
369
{
370
  client->pause();
×
371

372
  if ( !locked() )
×
373
  {
374
    send_open_gump( client, *this );
×
375
    send_spellbook_contents( client, *this );
×
376
  }
377
  else
378
  {
379
    send_sysmessage( client, "That is locked." );
×
380
  }
381

382
  client->restart();
×
383
}
×
384

385
bool Spellbook::get_method_hook( const char* methodname, Bscript::Executor* ex, ExportScript** hook,
×
386
                                 unsigned int* PC ) const
387
{
388
  if ( gamestate.system_hooks.get_method_hook( gamestate.system_hooks.spellbook_method_script.get(),
×
389
                                               methodname, ex, hook, PC ) )
390
    return true;
×
391
  return base::get_method_hook( methodname, ex, hook, PC );
×
392
}
393

394
void send_spellbook_contents( Network::Client* client, Spellbook& spellbook )
×
395
{
396
  Network::PktHelper::PacketOut<Network::PktOut_3C> msg;
×
397
  msg->offset += 4;  // msglen+count
×
398
  u16 count = 0;
×
399
  for ( u16 i = 0; i < 64; ++i )
×
400
  {
401
    u32 objtype = gamestate.spell_scroll_objtype_limits[0][0] + i;
×
402
    u16 spellnumber = USpellScroll::convert_objtype_to_spellnum( objtype, spellbook.spell_school );
×
403
    u8 spellpos = spellnumber & 7;  // spellpos is the spell's position it it's circle's array.
×
404
    if ( spellpos == 0 )
×
405
      spellpos = 8;
×
406
    if ( ( ( spellbook.bitwise_contents[( ( spellnumber - 1 ) >> 3 )] ) &
×
407
           ( 1 << ( spellpos - 1 ) ) ) != 0 )
×
408
    {
409
      msg->Write<u32>( 0x7FFFFFFFu - spellnumber );
×
410
      msg->WriteFlipped<u16>( objtype );
×
411
      msg->offset++;                          // unk6
×
412
      msg->WriteFlipped<u16>( spellnumber );  // amount
×
413
      msg->WriteFlipped<u16>( 1u );           // x
×
414
      msg->WriteFlipped<u16>( 1u );           // y
×
415
      if ( client->ClientType & Network::CLIENTTYPE_6017 )
×
416
        msg->Write<u8>( 0u );  // slotindex
×
417
      msg->Write<u32>( spellbook.serial_ext );
×
418
      msg->WriteFlipped<u16>( 0u );  // color
×
419
      ++count;
×
420
    }
421
  }
422
  u16 len = msg->offset;
×
423
  msg->offset = 1;
×
424
  msg->WriteFlipped<u16>( len );
×
425
  msg->WriteFlipped<u16>( count );
×
426
  msg.Send( client, len );
×
427
}
×
428
}  // 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