Pol  Revision:cb584c9
spells.cpp
Go to the documentation of this file.
1 
9 #include "spells.h"
10 
11 #include <stdlib.h>
12 #include <time.h>
13 
14 #include <format/format.h>
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 "../plib/pkg.h"
22 #include "../plib/systemstate.h"
23 #include "containr.h"
24 #include "globals/state.h"
25 #include "globals/uvars.h"
26 #include "item/item.h"
27 #include "item/itemdesc.h"
28 #include "layers.h"
29 #include "miscrgn.h"
30 #include "mkscrobj.h"
31 #include "mobile/charactr.h"
32 #include "network/client.h"
33 #include "pktin.h"
34 #include "polclass.h"
35 #include "polsig.h"
36 #include "scrstore.h"
37 #include "skillid.h"
38 #include "spelbook.h"
39 #include "syshook.h"
40 #include "ufunc.h"
41 #include "umanip.h"
42 #include "vital.h"
43 
44 namespace Pol
45 {
46 namespace Core
47 {
48 static bool nocast_here( Mobile::Character* chr )
49 {
50  NoCastRegion* rgn = gamestate.nocastdef->getregion( chr->x, chr->y, chr->realm );
51  if ( rgn == nullptr )
52  {
53  return false;
54  }
55  else
56  {
57  return rgn->nocast();
58  }
59 }
60 
61 bool knows_spell( Mobile::Character* chr, u16 spellid )
62 {
63  // copied code from Character::spellbook to support multiple spellbooks in the pack
64  Items::Item* item = chr->wornitem( LAYER_HAND1 );
65  if ( item != nullptr && item->script_isa( POLCLASS_SPELLBOOK ) )
66  {
67  Spellbook* book = static_cast<Spellbook*>( item );
68  if ( book->has_spellid( spellid ) )
69  return true;
70  }
71 
72  UContainer* cont = chr->backpack();
73  if ( cont != nullptr )
74  {
75  for ( UContainer::const_iterator itr = cont->begin(), end = cont->end(); itr != end; ++itr )
76  {
77  const Items::Item* _item = GET_ITEM_PTR( itr );
78 
79  if ( _item != nullptr && _item->script_isa( POLCLASS_SPELLBOOK ) )
80  {
81  const Spellbook* book = static_cast<const Spellbook*>( _item );
82  if ( book->has_spellid( spellid ) )
83  return true;
84  }
85  }
86  }
87 
88  return false;
89 }
90 
92 {
93  Items::Item* item;
94 
95  item = chr->wornitem( LAYER_HAND1 );
96  if ( item != nullptr )
97  {
98  const Items::ItemDesc& id = item->itemdesc();
99  if ( id.blocks_casting_if_in_hand )
100  return false;
101  }
102 
103  item = chr->wornitem( LAYER_HAND2 );
104  if ( item != nullptr )
105  {
106  const Items::ItemDesc& id = item->itemdesc();
107  if ( id.blocks_casting_if_in_hand )
108  return false;
109  }
110 
111  return true;
112 }
113 
114 USpellParams::USpellParams() : manacost( 0 ), difficulty( 0 ), pointvalue( 0 ), delay( 0 ) {}
115 
117  : manacost( elem.remove_ushort( "MANA" ) ),
118  difficulty( elem.remove_ushort( "DIFFICULTY" ) ),
119  pointvalue( elem.remove_ushort( "POINTVALUE" ) ),
120  delay( elem.remove_ushort( "DELAY" ) )
121 {
122 }
123 
124 
125 SpellCircle::SpellCircle( Clib::ConfigElem& elem ) : params( elem ) {}
126 
128  : pkg_( pkg ),
129  spellid_( elem.remove_ushort( "SPELLID" ) ),
130  name_( elem.remove_string( "NAME" ) ),
131  power_words_( elem.remove_string( "POWERWORDS" ) ),
132  scriptdef_( elem.remove_string( "SCRIPT", "" ), pkg, "scripts/" )
133 {
134  unsigned short action;
135  if ( elem.remove_prop( "ANIMATION", &action ) )
136  {
137  if ( UACTION_IS_VALID( action ) )
138  {
139  action_ = static_cast<UACTION>( action );
140  }
141  else
142  {
143  elem.throw_error( "Animation is out of range" );
144  }
145  }
146  else
147  {
149  }
150 
151  unsigned short circle;
152  if ( elem.remove_prop( "CIRCLE", &circle ) )
153  {
154  if ( circle < 1 || circle > gamestate.spellcircles.size() ||
155  gamestate.spellcircles[circle - 1] == nullptr )
156  {
157  ERROR_PRINT << "Error reading spell " << name_ << ": Circle " << circle
158  << " is not defined.\n";
159  throw std::runtime_error( "Config file error" );
160  }
161 
162  params_ = gamestate.spellcircles[circle - 1]->params;
163  }
164  else
165  {
166  params_ = USpellParams( elem );
167  }
168 
169  std::string reagent_name;
170  while ( elem.remove_prop( "Reagent", &reagent_name ) )
171  {
172  unsigned int reagent = Items::get_objtype_from_string( reagent_name );
173 
174  reglist_.push_back( reagent );
175  }
176 }
177 
178 size_t USpell::estimateSize() const
179 {
180  size_t size = sizeof( Plib::Package* ) /*pkg_*/
181  + sizeof( unsigned short ) /*spellid_*/
182  + name_.capacity() + power_words_.capacity() + sizeof( UACTION ) /*action_*/
183  + 3 * sizeof( unsigned int* ) + reglist_.capacity() * sizeof( unsigned int ) +
184  sizeof( USpellParams ) /*params_*/
186  return size;
187 }
188 
190 {
191  if ( nocast_here( chr ) )
192  {
193  if ( chr->client != nullptr )
194  send_sysmessage( chr->client, "Spells cannot be cast here." );
195  return;
196  }
197 
198  if ( !scriptdef_.empty() )
199  {
201  find_script2( scriptdef_, true, Plib::systemstate.config.cache_interactive_scripts );
202 
203  if ( prog.get() != nullptr )
204  {
205  if ( chr->start_spell_script( prog.get(), this ) )
206  return;
207  }
208  }
209 
210  if ( chr->client != nullptr )
211  send_sysmessage( chr->client, "That spell doesn't seem to work." );
212 }
213 
215 {
216  UContainer* bp = chr->backpack();
217  if ( bp == nullptr )
218  return false;
219 
220  for ( RegList::iterator itr = reglist_.begin(), end = reglist_.end(); itr != end; ++itr )
221  {
222  Items::Item* item = bp->find_objtype_noninuse( *itr );
223  if ( item == nullptr )
224  return false;
225  subtract_amount_from_item( item, 1 );
226  }
227 
228  return true;
229 }
230 
232 {
233  return ( chr->vital( gamestate.pVitalMana->vitalid ).current_ones() >= manacost() );
234 }
235 
237 {
239 }
240 
242 {
244  manacost() * 100 );
245 }
246 
247 void USpell::speak_power_words( Mobile::Character* chr, unsigned short font, unsigned short color )
248 {
249  if ( chr->client != nullptr && chr->hidden() )
250  {
251  private_say_above( chr, chr, power_words_.c_str(), font, color );
252  }
253  else if ( !chr->hidden() )
254  {
255  say_above( chr, power_words_.c_str(), font, color );
256  }
257 }
258 
260  USpell* spell, bool /*dummy*/ )
261  : OneShotTask( handle, run_when_clock ), caster_( caster ), spell_( spell )
262 {
263 }
264 
266 {
267  THREAD_CHECKPOINT( tasks, 900 );
268  Mobile::Character* caster = caster_.get();
269  if ( !caster->orphan() )
270  {
271  THREAD_CHECKPOINT( tasks, 911 );
272  spell_->cast( caster );
273  THREAD_CHECKPOINT( tasks, 912 );
274  }
275  THREAD_CHECKPOINT( tasks, 999 );
276 }
277 
278 void do_cast( Network::Client* client, u16 spellid )
279 {
280  if ( gamestate.system_hooks.on_cast_hook != nullptr )
281  {
283  new Bscript::BLong( spellid ) ) )
284  return;
285  }
286  // CHECKME should this look at spellnum, instead? static_cast behavior undefined if out of range.
287  if ( spellid > gamestate.spells.size() )
288  return;
289 
290  USpell* spell = gamestate.spells[spellid];
291  if ( spell == nullptr )
292  {
293  ERROR_PRINT << "Spell " << spellid << " is not implemented.\n";
294  send_sysmessage( client, "That spell does not function." );
295  return;
296  }
297 
298  // Let scripts handle this.
299  // if (client->chr->hidden())
300  // client->chr->unhide();
301 
302  if ( client->chr->frozen() )
303  {
304  private_say_above( client->chr, client->chr, "I am frozen and cannot cast spells" );
305  return;
306  }
307 
308  if ( client->chr->paralyzed() )
309  {
310  private_say_above( client->chr, client->chr, "I am paralyzed and cannot cast spells" );
311  return;
312  }
313 
314  if ( client->chr->skill_ex_active() )
315  {
316  send_sysmessage( client, "You are already doing something else." );
317  return;
318  }
319 
320  if ( client->chr->casting_spell() )
321  {
322  send_sysmessage( client, "You are already casting a spell." );
323  return;
324  }
325 
326  if ( nocast_here( client->chr ) )
327  {
328  send_sysmessage( client, "Spells cannot be cast here." );
329  return;
330  }
331 
332  if ( Plib::systemstate.config.require_spellbooks )
333  {
334  if ( !knows_spell( client->chr, spellid ) )
335  {
336  send_sysmessage( client, "You don't know that spell." );
337  return;
338  }
339  }
340 
341 #if 1
342  client->chr->schedule_spell( spell );
343 #else
344  spell->cast( client );
345  client->restart();
346 #endif
347 }
348 
350 {
351  u16 spellnum = static_cast<u16>( strtoul( (char*)msg->data, nullptr, 10 ) );
352 
353  do_cast( client, spellnum );
354 }
355 
356 
358 {
359  if ( gamestate.system_hooks.open_spellbook_hook != nullptr )
360  {
362  return;
363  }
364 
365  if ( client->chr->dead() )
366  {
367  send_sysmessage( client, "I am dead and cannot do that." );
368  return;
369  }
370 
371 
372  Items::Item* spellbook = client->chr->wornitem( LAYER_HAND1 );
373  if ( spellbook == nullptr )
374  {
375  UContainer* backpack = client->chr->backpack();
376  if ( backpack != nullptr )
377  {
378  spellbook = backpack->find_toplevel_polclass( POLCLASS_SPELLBOOK );
379 
380  //
381  // Client crashes if the pack isn't open and you don't tell him
382  // about the spellbook
383  //
384  if ( spellbook != nullptr )
385  send_put_in_container( client, spellbook );
386  }
387  }
388 
389  if ( spellbook != nullptr )
390  {
391  spellbook->double_click( client );
392  }
393 }
394 
395 void register_spell( USpell* spell, unsigned short spellid )
396 {
397  if ( spellid >= gamestate.spells.size() )
398  {
399  gamestate.spells.resize( spellid + 1, 0 );
400  }
401 
402  if ( gamestate.spells[spellid] )
403  {
404  USpell* origspell = gamestate.spells[spellid];
405  fmt::Writer tmp;
406  tmp << "Spell ID " << spellid << " (" << origspell->name() << ") multiply defined\n";
407  if ( origspell->pkg_ != nullptr )
408  {
409  tmp << " Spell originally defined in package '" << origspell->pkg_->name() << "' ("
410  << origspell->pkg_->dir() << ")\n";
411  }
412  else
413  {
414  tmp << " Spell originally defined in main\n";
415  }
416  if ( spell->pkg_ != nullptr )
417  {
418  tmp << " Spell redefined in package '" << spell->pkg_->name() << "' (" << spell->pkg_->dir()
419  << ")\n";
420  }
421  else
422  {
423  tmp << " Spell redefined in main\n";
424  }
425  ERROR_PRINT << tmp.str();
426  throw std::runtime_error( "Spell ID multiply defined" );
427  }
428 
429  gamestate.spells[spellid] = spell;
430 }
431 
432 
434 {
435  if ( !Clib::FileExists( "config/circles.cfg" ) )
436  {
437  if ( Plib::systemstate.config.loglevel > 1 )
438  INFO_PRINT << "File config/circles not found, skipping.\n";
439  return;
440  }
441 
442  Clib::ConfigFile cf( "config/circles.cfg", "Circle" );
443  Clib::ConfigElem elem;
444 
445  while ( cf.read( elem ) )
446  {
447  int index = strtoul( elem.rest(), nullptr, 0 ) - 1;
448  if ( index < 0 || index >= 100 )
449  {
450  ERROR_PRINT << "Error in CIRCLES.CFG: Circle must fall between 1 and 100\n";
451  throw std::runtime_error( "Config file error" );
452  }
453 
454  gamestate.spellcircles.resize( index + 1, nullptr );
455 
456  if ( gamestate.spellcircles[index] != nullptr )
457  {
458  ERROR_PRINT << "Error in CIRCLES.CFG: Circle " << index + 1 << " is multiply defined.\n";
459  throw std::runtime_error( "Config file error" );
460  }
461 
462  gamestate.spellcircles[index] = new SpellCircle( elem );
463  }
464 }
465 
466 void load_spells_cfg( const char* path, Plib::Package* pkg )
467 {
468  Clib::ConfigFile cf( path, "Spell" );
469  Clib::ConfigElem elem;
470 
471  while ( cf.read( elem ) )
472  {
473  std::unique_ptr<USpell> spell( new USpell( elem, pkg ) );
474 
475  unsigned short spellid = spell->spell_id();
476 
477  register_spell( spell.release(), spellid );
478  }
479 }
480 
482 {
484 
485  if ( Clib::FileExists( "config/spells.cfg" ) )
486  load_spells_cfg( "config/spells.cfg", nullptr );
487  else if ( Plib::systemstate.config.loglevel > 1 )
488  INFO_PRINT << "File config/spells.cfg not found, skipping\n";
489 
490  for ( Plib::Packages::iterator itr = Plib::systemstate.packages.begin();
491  itr != Plib::systemstate.packages.end(); ++itr )
492  {
493  Plib::Package* pkg = ( *itr );
494  std::string filename = Plib::GetPackageCfgPath( pkg, "spells.cfg" );
495  if ( Clib::FileExists( filename.c_str() ) )
496  {
497  load_spells_cfg( filename.c_str(), pkg );
498  }
499  }
500 }
501 
503 {
504  std::vector<SpellCircle*>::iterator c_iter = gamestate.spellcircles.begin();
505  for ( ; c_iter != gamestate.spellcircles.end(); ++c_iter )
506  {
507  delete *c_iter;
508  *c_iter = nullptr;
509  }
510  gamestate.spellcircles.clear();
511  std::vector<USpell*>::iterator s_iter = gamestate.spells.begin();
512  for ( ; s_iter != gamestate.spells.end(); ++s_iter )
513  {
514  delete *s_iter;
515  *s_iter = nullptr;
516  }
517  gamestate.spells.clear();
518 }
519 }
520 }
bool start_spell_script(Bscript::EScriptProgram *prog, Core::USpell *spell)
Definition: chrcast.cpp:20
bool empty() const
Definition: scrdef.h:41
Contents::const_iterator const_iterator
Definition: containr.h:115
std::string name_
Definition: spells.h:103
void load_circle_data()
Definition: spells.cpp:433
unsigned short manacost
Definition: spells.h:53
ref_ptr< Bscript::EScriptProgram > find_script2(const ScriptDef &script, bool complain_if_not_found, bool cache_script)
Definition: scrstore.cpp:83
USpellParams params_
Definition: spells.h:109
Plib::Package * pkg_
Definition: spells.h:100
void load_spell_data()
Definition: spells.cpp:481
size_t estimateSize() const
Definition: spells.cpp:178
UACTION action_
Definition: spells.h:105
bool skill_ex_active() const
Definition: charactr.h:1013
bool hands_are_free(Mobile::Character *chr)
Definition: spells.cpp:91
SystemState systemstate
Definition: systemstate.cpp:12
bool call(Bscript::BObjectImp *p0)
Definition: syshook.cpp:44
Network::Client * client
Definition: charactr.h:871
ExportedFunction * open_spellbook_hook
Definition: syshook.h:68
void register_spell(USpell *spell, unsigned short spellid)
Definition: spells.cpp:395
void handle_open_spellbook(Network::Client *client, PKTIN_12 *msg)
Definition: spells.cpp:357
void speak_power_words(Mobile::Character *chr, unsigned short font, unsigned short color)
Definition: spells.cpp:247
T * get() const
Definition: refptr.h:176
SystemHooks system_hooks
Definition: uvars.h:190
void clean_spells()
Definition: spells.cpp:502
size_t estimatedSize() const
Definition: scrdef.cpp:140
virtual void on_run() POL_OVERRIDE
Definition: spells.cpp:265
bool frozen() const
Definition: charactr.h:969
#define THREAD_CHECKPOINT(thread, check)
Definition: polsig.h:48
static bool nocast_here(Mobile::Character *chr)
Definition: spells.cpp:48
bool casting_spell() const
Definition: charactr.h:1018
unsigned short manacost() const
Definition: spells.h:121
SpellTask(OneShotTask **handle, polclock_t run_when, Mobile::Character *caster, USpell *spell, bool dummy)
Definition: spells.cpp:259
const std::string & name() const
Definition: pkg.h:83
CharacterRef caster_
Definition: spells.h:162
virtual T * getregion(xcoord x, ycoord y, Realms::Realm *realm)
Definition: region.h:126
unsigned short delay
Definition: spells.h:56
ExportedFunction * on_cast_hook
Definition: syshook.h:76
ScriptDef scriptdef_
Definition: spells.h:112
int current_ones() const
Definition: charactr.h:193
const std::string & name() const
Definition: spells.h:133
Mobile::Character * chr
Definition: client.h:182
virtual bool script_isa(unsigned isatype) const POL_OVERRIDE
Definition: uoscrobj.cpp:4604
bool private_say_above(Character *chr, const UObject *obj, const char *text, unsigned short font, unsigned short color, unsigned int journal_print)
Definition: ufunc.cpp:1328
bool consume(const Core::Vital *pVital, VitalValue &vv, unsigned int amt)
Definition: charactr.cpp:1540
void do_cast(Network::Client *client, u16 spellid)
Definition: spells.cpp:278
unsigned short u16
Definition: rawtypes.h:26
const char * rest() const
Definition: cfgfile.cpp:71
bool has_spellid(unsigned int spellid) const
Definition: spelbook.cpp:149
unsigned short pointvalue
Definition: spells.h:55
POL_NORETURN void throw_error(const std::string &errmsg) const
Definition: cfgfile.cpp:285
unsigned int get_objtype_from_string(const std::string &str)
Definition: itemdesc.cpp:67
unsigned short difficulty
Definition: spells.h:54
void consume_mana(Mobile::Character *chr)
Definition: spells.cpp:241
void handle_cast_spell(Network::Client *client, PKTIN_12 *msg)
Definition: spells.cpp:349
USpell * spell_
Definition: spells.h:163
bool consume_reagents(Mobile::Character *chr)
Definition: spells.cpp:214
bool check_mana(Mobile::Character *chr)
Definition: spells.cpp:231
bool orphan() const
Definition: baseobject.h:119
virtual void double_click(Network::Client *client)
Definition: item.cpp:510
bool paralyzed() const
Definition: charactr.h:974
SpellCircle(Clib::ConfigElem &elem)
Definition: spells.cpp:125
std::string power_words_
Definition: spells.h:104
int polclock_t
Definition: polclock.h:26
NoCastDef * nocastdef
Definition: uvars.h:149
#define GET_ITEM_PTR(itr)
Definition: containr.h:38
GameState gamestate
Definition: uvars.cpp:74
void cast(Mobile::Character *caster)
Definition: spells.cpp:189
const Vital * pVitalMana
Definition: uvars.h:201
std::vector< USpell * > spells
Definition: uvars.h:186
const unsigned POLCLASS_SPELLBOOK
Definition: polclass.h:21
Items::Item * find_objtype_noninuse(u32 objtype) const
Definition: containr.cpp:449
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
void subtract_amount_from_item(Item *item, unsigned short amount)
Definition: ufunc.cpp:1584
Core::UContainer * backpack() const
Definition: charactr.cpp:1276
void schedule_spell(Core::USpell *)
Definition: chrcast.cpp:25
Realms::Realm * realm
Definition: baseobject.h:56
bool nocast() const
Definition: miscrgn.h:44
u8 data[300]
Definition: pktin.h:185
const VitalValue & vital(unsigned vitalid) const
Definition: charactr.h:1061
std::string GetPackageCfgPath(const Package *pkg, const std::string &filename)
Definition: pkg.cpp:491
Items::Item * wornitem(int layer) const
Definition: charactr.cpp:1332
unsigned vitalid
Definition: vital.h:38
bool knows_spell(Mobile::Character *chr, u16 spellid)
Definition: spells.cpp:61
Items::Item * find_toplevel_polclass(unsigned int polclass) const
Definition: containr.cpp:353
#define ERROR_PRINT
Definition: logfacility.h:230
void load_spells_cfg(const char *path, Plib::Package *pkg)
Definition: spells.cpp:466
bool FileExists(const char *filename)
Definition: fileutil.cpp:118
const ItemDesc & itemdesc() const
Definition: item.cpp:127
RegList reglist_
Definition: spells.h:108
bool read(ConfigElem &elem)
Definition: cfgfile.cpp:1015
#define INFO_PRINT
Definition: logfacility.h:223
Bscript::BObjectImp * make_mobileref(Mobile::Character *chr)
Definition: mkscrobj.cpp:14
USpell(Clib::ConfigElem &elem, Plib::Package *pkg)
Definition: spells.cpp:127
bool UACTION_IS_VALID(unsigned short action)
Definition: action.h:67
bool check_skill(Core::USKILLID skillid, int difficulty, unsigned short pointvalue)
Definition: charactr.cpp:2626
std::vector< SpellCircle * > spellcircles
Definition: uvars.h:187
bool say_above(const UObject *obj, const char *text, unsigned short font, unsigned short color, unsigned int journal_print)
Definition: ufunc.cpp:1258
Definition: berror.cpp:12
bool check_skill(Mobile::Character *chr)
Definition: spells.cpp:236
bool dead() const
Definition: charactr.h:931
void send_sysmessage(Network::Client *client, const char *text, unsigned short font, unsigned short color)
Definition: ufunc.cpp:1147
void send_put_in_container(Client *client, const Item *item)
Definition: ufunc.cpp:528
bool hidden() const
Definition: charactr.h:941
const std::string & dir() const
Definition: pkg.h:79