1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 */
20
21 #include "Spellbook.h"
22
23 #include "ActorMgr.h"
24 #include "GameData.h"
25 #include "Interface.h"
26 #include "PluginMgr.h"
27 #include "Projectile.h"
28 #include "Spell.h"
29 #include "TableMgr.h"
30 #include "Scriptable/Actor.h"
31 #include "System/StringBuffer.h"
32
33 #include <cstdio>
34
35 namespace GemRB {
36
37 static bool SBInitialized = false;
38 static int NUM_BOOK_TYPES = 3;
39 static bool IWD2Style = false;
40
41 //spell header-->spell book type conversion (iwd2 is different)
42 static const int spelltypes[NUM_SPELL_TYPES]={
43 IE_SPELL_TYPE_INNATE, IE_SPELL_TYPE_WIZARD, IE_SPELL_TYPE_PRIEST,
44 IE_SPELL_TYPE_WIZARD, IE_SPELL_TYPE_INNATE, IE_SPELL_TYPE_SONG
45 };
46
Spellbook()47 Spellbook::Spellbook()
48 {
49 if (!SBInitialized) {
50 InitializeSpellbook();
51 }
52 spells = new std::vector<CRESpellMemorization*> [NUM_BOOK_TYPES];
53 sorcerer = 0;
54 if (IWD2Style) {
55 innate = 1<<IE_IWD2_SPELL_INNATE;
56 } else {
57 innate = 1<<IE_SPELL_TYPE_INNATE;
58 }
59 }
60
InitializeSpellbook()61 void Spellbook::InitializeSpellbook()
62 {
63 if (!SBInitialized) {
64 SBInitialized=true;
65 if (core->HasFeature(GF_HAS_SPELLLIST)) {
66 NUM_BOOK_TYPES=NUM_IWD2_SPELLTYPES; //iwd2 spell types
67 IWD2Style = true;
68 } else {
69 NUM_BOOK_TYPES=NUM_SPELLTYPES; //bg/pst/iwd1 spell types
70 IWD2Style = false;
71 }
72 }
73 return;
74 }
75
ReleaseMemory()76 void Spellbook::ReleaseMemory()
77 {
78 SBInitialized=false;
79 }
80
~Spellbook()81 Spellbook::~Spellbook()
82 {
83 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
84 for (unsigned int j = 0; j < spells[i].size(); j++) {
85 if (spells[i][j]) {
86 FreeSpellPage( spells[i][j] );
87 spells[i][j] = NULL;
88 }
89 }
90 }
91 ClearSpellInfo();
92 delete [] spells;
93 }
94
FreeSpellPage(CRESpellMemorization * sm)95 void Spellbook::FreeSpellPage(CRESpellMemorization *sm)
96 {
97 size_t i = sm->known_spells.size();
98 while(i--) {
99 delete sm->known_spells[i];
100 }
101 i = sm->memorized_spells.size();
102 while(i--) {
103 delete sm->memorized_spells[i];
104 }
105 delete sm;
106 }
107
108 // FIXME: exclude slayer, all bhaal innates?
CopyFrom(const Actor * source)109 void Spellbook::CopyFrom(const Actor *source)
110 {
111 if (!source) {
112 return;
113 }
114
115 // clear it first
116 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
117 for (unsigned int j = 0; j < spells[i].size(); j++) {
118 if (spells[i][j]) {
119 FreeSpellPage( spells[i][j] );
120 spells[i][j] = NULL;
121 }
122 }
123 spells[i].clear();
124 }
125 ClearSpellInfo();
126
127 const Spellbook &wikipedia = source->spellbook;
128
129 for (int t = 0; t < NUM_BOOK_TYPES; t++) {
130 for (size_t i = 0; i < wikipedia.spells[t].size(); i++) {
131 unsigned int k;
132 CRESpellMemorization *wm = wikipedia.spells[t][i];
133 CRESpellMemorization *sm = new CRESpellMemorization();
134 spells[t].push_back(sm);
135 sm->Level = wm->Level;
136 sm->SlotCount = wm->SlotCount;
137 sm->SlotCountWithBonus = wm->SlotCountWithBonus;
138 sm->Type = wm->Type;
139 for (k = 0; k < wm->known_spells.size(); k++) {
140 CREKnownSpell *tmp_known = new CREKnownSpell();
141 sm->known_spells.push_back(tmp_known);
142 memcpy(tmp_known, wm->known_spells[k], sizeof(CREKnownSpell));
143 }
144 for (k = 0; k < wm->memorized_spells.size(); k++) {
145 CREMemorizedSpell *tmp_mem = new CREMemorizedSpell();
146 sm->memorized_spells.push_back(tmp_mem);
147 memcpy(tmp_mem, wm->memorized_spells[k], sizeof(CREMemorizedSpell));
148 }
149 }
150 }
151
152 sorcerer = wikipedia.sorcerer;
153 }
154
155 //ITEM, SPPR, SPWI, SPIN, SPCL
156 int sections[]={3,0,1,2,2};
157 // domain spells are of all types, so look them up in all cases
158 // ignore songs and shapes altogether
159 int arcanetypes[] = {IE_IWD2_SPELL_BARD, IE_IWD2_SPELL_SORCERER, IE_IWD2_SPELL_WIZARD, IE_IWD2_SPELL_DOMAIN};
160 int divinetypes[] = {IE_IWD2_SPELL_CLERIC, IE_IWD2_SPELL_DRUID, IE_IWD2_SPELL_PALADIN, IE_IWD2_SPELL_RANGER, IE_IWD2_SPELL_DOMAIN};
161 int *alltypes[2] = {divinetypes, arcanetypes};
162
GetType(int spellid,unsigned int & bookcount,int & idx)163 int inline GetType(int spellid, unsigned int &bookcount, int &idx)
164 {
165 int type = spellid/1000;
166 if (type>4) {
167 return -1;
168 }
169 if (IWD2Style) {
170 if (type == 1) {
171 // check divine
172 idx = 0;
173 bookcount = sizeof(divinetypes)/sizeof(int);
174 } else if (type == 2) {
175 // check arcane
176 idx = 1;
177 bookcount = sizeof(arcanetypes)/sizeof(int);
178 } else if (type == 3) {
179 type = IE_IWD2_SPELL_INNATE;
180 }
181 } else {
182 type = sections[type];
183 if (type >= NUM_BOOK_TYPES) {
184 return -1;
185 }
186 }
187 return type;
188 }
189
190 //flags bits
191 // 1 - unmemorize it
HaveSpell(int spellid,ieDword flags)192 bool Spellbook::HaveSpell(int spellid, ieDword flags)
193 {
194 int idx = -1;
195 unsigned int bookcount;
196 int type = GetType(spellid, bookcount, idx);
197 if (type == -1) {
198 return false;
199 }
200
201 spellid = spellid % 1000;
202
203 if (idx == -1) {
204 return HaveSpell(spellid, type, flags);
205 } else {
206 for (unsigned int book = 0; book < bookcount; book++) {
207 if (HaveSpell(spellid, alltypes[idx][book], flags)) {
208 return true;
209 }
210 }
211 }
212 return false;
213 }
HaveSpell(int spellid,int type,ieDword flags)214 bool Spellbook::HaveSpell(int spellid, int type, ieDword flags)
215 {
216 for (unsigned int j = 0; j < GetSpellLevelCount(type); j++) {
217 CRESpellMemorization* sm = spells[type][j];
218 for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
219 CREMemorizedSpell* ms = sm->memorized_spells[k];
220 if (ms->Flags) {
221 if (atoi(ms->SpellResRef+4)==spellid) {
222 if (flags&HS_DEPLETE) {
223 if (DepleteSpell(ms) && (sorcerer & (1<<type) ) ) {
224 DepleteLevel (sm, ms->SpellResRef);
225 }
226 }
227 return true;
228 }
229 }
230 }
231 }
232 return false;
233 }
234
235 //returns count of memorized spells of a given name/type
CountSpells(const char * resref,unsigned int type,int flag) const236 int Spellbook::CountSpells(const char *resref, unsigned int type, int flag) const
237 {
238 int i, max;
239 int count = 0;
240
241 if (type==0xffffffff) {
242 i=0;
243 max = NUM_BOOK_TYPES;
244 } else {
245 i = type;
246 max = i+1;
247 }
248
249 while(i < max) {
250 for (unsigned int j = 0; j < spells[i].size(); j++) {
251 CRESpellMemorization* sm = spells[i][j];
252 for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
253 CREMemorizedSpell* ms = sm->memorized_spells[k];
254 if (resref[0] && !stricmp(ms->SpellResRef, resref) ) {
255 if (flag || ms->Flags) {
256 count++;
257 }
258 }
259 }
260 }
261 i++;
262 }
263 return count;
264 }
265
KnowSpell(int spellid) const266 bool Spellbook::KnowSpell(int spellid) const
267 {
268 int idx = -1;
269 unsigned int bookcount;
270 int type = GetType(spellid, bookcount, idx);
271 if (type == -1) {
272 return false;
273 }
274 spellid = spellid % 1000;
275
276 if (idx == -1) {
277 return KnowSpell(spellid, type);
278 } else {
279 for (unsigned int book = 0; book < bookcount; book++) {
280 if (KnowSpell(spellid, alltypes[idx][book])) {
281 return true;
282 }
283 }
284 }
285 return false;
286 }
287
KnowSpell(int spellid,int type) const288 bool Spellbook::KnowSpell(int spellid, int type) const
289 {
290 for (unsigned int j = 0; j < GetSpellLevelCount(type); j++) {
291 CRESpellMemorization* sm = spells[type][j];
292 for (unsigned int k = 0; k < sm->known_spells.size(); k++) {
293 CREKnownSpell* ks = sm->known_spells[k];
294 if (atoi(ks->SpellResRef+4)==spellid) {
295 return true;
296 }
297 }
298 }
299 return false;
300 }
301
302 //if resref=="" then it is a knownanyspell
KnowSpell(const char * resref) const303 bool Spellbook::KnowSpell(const char *resref) const
304 {
305 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
306 for (unsigned int j = 0; j < spells[i].size(); j++) {
307 CRESpellMemorization* sm = spells[i][j];
308 for (unsigned int k = 0; k < sm->known_spells.size(); k++) {
309 CREKnownSpell* ks = sm->known_spells[k];
310 if (resref[0] && stricmp(ks->SpellResRef, resref) ) {
311 continue;
312 }
313 return true;
314 }
315 }
316 }
317 return false;
318 }
319
320 //if resref=="" then it is a haveanyspell
HaveSpell(const char * resref,ieDword flags)321 bool Spellbook::HaveSpell(const char *resref, ieDword flags)
322 {
323 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
324 for (unsigned int j = 0; j < spells[i].size(); j++) {
325 CRESpellMemorization* sm = spells[i][j];
326 for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
327 CREMemorizedSpell* ms = sm->memorized_spells[k];
328
329 if (!ms->Flags) continue;
330 if (resref[0] && stricmp(ms->SpellResRef, resref)) {
331 continue;
332 }
333
334 if (flags&HS_DEPLETE) {
335 if (DepleteSpell(ms) && (sorcerer & (1<<i))) {
336 DepleteLevel(sm, ms->SpellResRef);
337 }
338 }
339 return true;
340 }
341 }
342 }
343 return false;
344 }
345
GetTypes() const346 int Spellbook::GetTypes() const
347 {
348 return NUM_BOOK_TYPES;
349 }
350
IsIWDSpellBook() const351 bool Spellbook::IsIWDSpellBook() const
352 {
353 return IWD2Style;
354 }
355
GetSpellLevelCount(int type) const356 unsigned int Spellbook::GetSpellLevelCount(int type) const
357 {
358 assert(type < NUM_BOOK_TYPES);
359 return (unsigned int) spells[type].size();
360 }
361
GetTotalPageCount() const362 unsigned int Spellbook::GetTotalPageCount() const
363 {
364 unsigned int total = 0;
365 for (int type = 0; type < NUM_BOOK_TYPES; type++) {
366 total += GetSpellLevelCount(type);
367 }
368 return total;
369 }
370
GetTotalKnownSpellsCount() const371 unsigned int Spellbook::GetTotalKnownSpellsCount() const
372 {
373 unsigned int total = 0;
374 for (int type = 0; type < NUM_BOOK_TYPES; type++) {
375 unsigned int level = GetSpellLevelCount(type);
376 while(level--) {
377 total += GetKnownSpellsCount(type, level);
378 }
379 }
380 return total;
381 }
382
GetTotalMemorizedSpellsCount() const383 unsigned int Spellbook::GetTotalMemorizedSpellsCount() const
384 {
385 unsigned int total = 0;
386 for (int type = 0; type < NUM_BOOK_TYPES; type++) {
387 unsigned int level = GetSpellLevelCount(type);
388 while(level--) {
389 total += GetMemorizedSpellsCount(type, level, false);
390 }
391 }
392 return total;
393 }
394
395 // returns the number of known spells of level (level+1)
GetKnownSpellsCount(int type,unsigned int level) const396 unsigned int Spellbook::GetKnownSpellsCount(int type, unsigned int level) const
397 {
398 if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type))
399 return 0;
400 return (unsigned int) spells[type][level]->known_spells.size();
401 }
402
403 //called when a spell was removed from spellbook
404 //this one purges all instances of known spells of the same name from memory
RemoveMemorization(CRESpellMemorization * sm,const ieResRef ResRef)405 void Spellbook::RemoveMemorization(CRESpellMemorization* sm, const ieResRef ResRef)
406 {
407 std::vector< CREMemorizedSpell* >::iterator ms;
408
409 for (ms = sm->memorized_spells.begin(); ms != sm->memorized_spells.end(); ++ms) {
410 if (strnicmp(ResRef, (*ms)->SpellResRef, sizeof(ieResRef) ) ) {
411 continue;
412 }
413 delete *ms;
414 ms = sm->memorized_spells.erase(ms);
415 --ms;
416 }
417 }
418
419 //removes one instance of spell (from creknownspell)
RemoveSpell(const CREKnownSpell * spell)420 bool Spellbook::RemoveSpell(const CREKnownSpell* spell)
421 {
422 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
423 std::vector< CRESpellMemorization* >::iterator sm;
424 for (sm = spells[i].begin(); sm != spells[i].end(); ++sm) {
425 std::vector< CREKnownSpell* >::iterator ks;
426 for (ks = (*sm)->known_spells.begin(); ks != (*sm)->known_spells.end(); ++ks) {
427 if (*ks == spell) {
428 ieResRef ResRef;
429
430 memcpy(ResRef, (*ks)->SpellResRef, sizeof(ieResRef) );
431 delete *ks;
432 (*sm)->known_spells.erase(ks);
433 RemoveMemorization(*sm, ResRef);
434 ClearSpellInfo();
435 return true;
436 }
437 }
438 }
439 }
440 return false;
441 }
442
443 //removes all instances of spellid (probably not needed)
444 //IWD2 clab files use it
RemoveSpell(int spellid)445 void Spellbook::RemoveSpell(int spellid)
446 {
447 int idx = -1;
448 unsigned int bookcount;
449 int type = GetType(spellid, bookcount, idx);
450 if (type == -1) {
451 return;
452 }
453 spellid = spellid % 1000;
454
455 if (idx == -1) {
456 RemoveSpell(spellid, type);
457 } else {
458 for (unsigned int book = 0; book < bookcount; book++) {
459 RemoveSpell(spellid, alltypes[idx][book]);
460 }
461 }
462
463 }
464
RemoveSpell(int spellid,int type)465 void Spellbook::RemoveSpell(int spellid, int type)
466 {
467 std::vector< CRESpellMemorization* >::iterator sm;
468 for (sm = spells[type].begin(); sm != spells[type].end(); ++sm) {
469 std::vector< CREKnownSpell* >::iterator ks;
470
471 for (ks = (*sm)->known_spells.begin(); ks != (*sm)->known_spells.end(); ++ks) {
472 if (atoi((*ks)->SpellResRef+4)==spellid) {
473 ieResRef ResRef;
474
475 memcpy(ResRef, (*ks)->SpellResRef, sizeof(ieResRef) );
476 delete *ks;
477 ks = (*sm)->known_spells.erase(ks);
478 RemoveMemorization(*sm, ResRef);
479 --ks;
480 ClearSpellInfo();
481 }
482 }
483 }
484 }
485
486 //removes spell from both memorized/book
RemoveSpell(const ieResRef ResRef,bool onlyknown)487 void Spellbook::RemoveSpell(const ieResRef ResRef, bool onlyknown)
488 {
489 for (int type = 0; type<NUM_BOOK_TYPES; type++) {
490 std::vector< CRESpellMemorization* >::iterator sm;
491 for (sm = spells[type].begin(); sm != spells[type].end(); ++sm) {
492 std::vector< CREKnownSpell* >::iterator ks;
493
494 for (ks = (*sm)->known_spells.begin(); ks != (*sm)->known_spells.end(); ++ks) {
495 if (strnicmp(ResRef, (*ks)->SpellResRef, sizeof(ieResRef) ) ) {
496 continue;
497 }
498 delete *ks;
499 ks = (*sm)->known_spells.erase(ks);
500 if (!onlyknown) RemoveMemorization(*sm, ResRef);
501 --ks;
502 ClearSpellInfo();
503 }
504 }
505 }
506 }
507
SetBookType(int bt)508 void Spellbook::SetBookType(int bt)
509 {
510 sorcerer |= bt;
511 }
512
513 //returns the page group of the spellbook this spelltype belongs to
514 //psionics are stored in the mage spell list
515 //wizard/priest are trivial
516 //songs are stored elsewhere
517 //wildshapes are marked as innate, they need some hack to get stored
518 //in the right group
519 //the rest are stored as innate
520
LearnSpell(Spell * spell,int memo,unsigned int clsmsk,unsigned int kit,int level)521 int Spellbook::LearnSpell(Spell *spell, int memo, unsigned int clsmsk, unsigned int kit, int level)
522 {
523 CREKnownSpell *spl = new CREKnownSpell();
524 CopyResRef(spl->SpellResRef, spell->Name);
525 spl->Level = 0;
526 if (IWD2Style) {
527 PluginHolder<ActorMgr> gm(IE_CRE_CLASS_ID);
528 // is there an override (domain spells)?
529 if (level == -1) {
530 level = spell->SpellLevel-1;
531 }
532 spl->Level = level;
533 spl->Type = gm->FindSpellType(spell->Name, spl->Level, clsmsk, kit);
534 } else {
535 //not IWD2
536 if (spell->SpellType<6) {
537 spl->Type = spelltypes[spell->SpellType];
538 if (spell->SpellLevel == 0) { // totemic druid has some broken innates (fixed by fixpack)
539 spell->SpellLevel = 1;
540 }
541 spl->Level = spell->SpellLevel-1;
542 } else {
543 spl->Type = IE_SPELL_TYPE_INNATE;
544 }
545 }
546
547 bool ret=AddKnownSpell(spl, memo);
548 if (!ret) {
549 delete spl;
550 return 0;
551 }
552 return spell->SpellLevel; // return only the spell level (xp is based on xpbonus)
553 }
554
555 //if flg is set, it will be also memorized
AddKnownSpell(CREKnownSpell * spl,int flg)556 bool Spellbook::AddKnownSpell(CREKnownSpell *spl, int flg)
557 {
558 int type = spl->Type;
559 if (type >= NUM_BOOK_TYPES) {
560 return false;
561 }
562 unsigned int level = spl->Level;
563 if ( level >= GetSpellLevelCount(type) ) {
564 CRESpellMemorization *sm = new CRESpellMemorization();
565 sm->Type = (ieWord) type;
566 sm->Level = (ieWord) level;
567 sm->SlotCount = sm->SlotCountWithBonus = 0;
568 if ( !AddSpellMemorization(sm) ) {
569 delete sm;
570 return false;
571 }
572 }
573
574 spells[type][level]->known_spells.push_back(spl);
575 if (1<<type == innate || 1<<type == 1<<IE_IWD2_SPELL_SONG) {
576 spells[type][level]->SlotCount++;
577 spells[type][level]->SlotCountWithBonus++;
578 }
579 if (flg) {
580 MemorizeSpell(spl, true);
581 }
582 return true;
583 }
584
GetKnownSpell(int type,unsigned int level,unsigned int index) const585 CREKnownSpell* Spellbook::GetKnownSpell(int type, unsigned int level, unsigned int index) const
586 {
587 if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type) || index >= spells[type][level]->known_spells.size())
588 return NULL;
589 return spells[type][level]->known_spells[index];
590 }
591
GetMemorizedSpellsCount(int type,bool real) const592 unsigned int Spellbook::GetMemorizedSpellsCount(int type, bool real) const
593 {
594 unsigned int count = 0;
595 size_t i=GetSpellLevelCount(type);
596 while(i--) {
597 if (real) {
598 int j = spells[type][i]->memorized_spells.size();
599 while(j--) {
600 if (spells[type][i]->memorized_spells[j]->Flags) count++;
601 }
602 } else {
603 count += (unsigned int) spells[type][i]->memorized_spells.size();
604 }
605 }
606 return count;
607 }
608
GetMemorizedSpellsCount(int type,unsigned int level,bool real) const609 unsigned int Spellbook::GetMemorizedSpellsCount(int type, unsigned int level, bool real) const
610 {
611 if (type >= NUM_BOOK_TYPES)
612 return 0;
613 if (level >= GetSpellLevelCount(type))
614 return 0;
615 if (real) {
616 unsigned int count = 0;
617 int j = spells[type][level]->memorized_spells.size();
618 while(j--) {
619 if (spells[type][level]->memorized_spells[j]->Flags) count++;
620 }
621 return count;
622 }
623 return (unsigned int) spells[type][level]->memorized_spells.size();
624 }
625
GetMemorizedSpellsCount(const ieResRef name,int type,bool real) const626 unsigned int Spellbook::GetMemorizedSpellsCount(const ieResRef name, int type, bool real) const
627 {
628 if (type >= NUM_BOOK_TYPES)
629 return 0;
630 int t;
631 if (type<0) {
632 t = NUM_BOOK_TYPES-1;
633 } else {
634 t = type;
635 }
636
637 int j = 0;
638 while(t>=0) {
639 unsigned int level = GetSpellLevelCount(t);
640 while(level--) {
641 int i = spells[t][level]->memorized_spells.size();
642 while(i--) {
643 CREMemorizedSpell *cms = spells[t][level]->memorized_spells[i];
644
645 if (strnicmp(cms->SpellResRef, name, sizeof(ieResRef) ) ) continue;
646 if (!real || cms->Flags) j++;
647 }
648 }
649 if (type>=0) break;
650 t--;
651 }
652 return j;
653 }
654
GetMemorizedSpell(int type,unsigned int level,unsigned int index) const655 CREMemorizedSpell* Spellbook::GetMemorizedSpell(int type, unsigned int level, unsigned int index) const
656 {
657 if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type) || index >= spells[type][level]->memorized_spells.size())
658 return NULL;
659 return spells[type][level]->memorized_spells[index];
660 }
661
662 //creates a spellbook level
AddSpellMemorization(CRESpellMemorization * sm)663 bool Spellbook::AddSpellMemorization(CRESpellMemorization* sm)
664 {
665 if (sm->Type>=NUM_BOOK_TYPES) {
666 return false;
667 }
668 std::vector<CRESpellMemorization*>* s = &spells[sm->Type];
669 //when loading, level starts on 0
670 unsigned int level = sm->Level;
671 if (level > MAX_SPELL_LEVEL ) {
672 return false;
673 }
674
675 while (s->size() < level ) {
676 // this code previously added NULLs, leading to crashes,
677 // so this is an attempt to make it not broken
678 CRESpellMemorization *newsm = new CRESpellMemorization();
679 newsm->Type = sm->Type;
680 newsm->Level = (ieWord) s->size();
681 newsm->SlotCount = newsm->SlotCountWithBonus = 0;
682 s->push_back( newsm );
683 }
684
685 // only add this one if necessary
686 assert (s->size() == level);
687 s->push_back(sm);
688 return true;
689 }
690
691 //apply the wisdom bonus on all spell levels for type
692 //count is optimally the count of spell levels
BonusSpells(int type,int count,const int * bonuses)693 void Spellbook::BonusSpells(int type, int count, const int *bonuses)
694 {
695 int level = GetSpellLevelCount(type);
696 if (level>count) level=count;
697 for (int i = 0; i < level; i++) {
698 CRESpellMemorization* sm = GetSpellMemorization(type, i);
699 // don't give access to new spell levels through these boni
700 if (sm->SlotCountWithBonus) {
701 sm->SlotCountWithBonus+=bonuses[i];
702 }
703 }
704 }
705
706 //call this in every ai cycle when recalculating spell bonus
707 //TODO:add in wisdom bonus here
ClearBonus()708 void Spellbook::ClearBonus()
709 {
710 int type;
711
712 for (type = 0; type < NUM_BOOK_TYPES; type++) {
713 int level = GetSpellLevelCount(type);
714 for (int i = 0; i < level; i++) {
715 CRESpellMemorization* sm = GetSpellMemorization(type, i);
716 sm->SlotCountWithBonus=sm->SlotCount;
717 }
718 }
719 }
720
GetSpellMemorization(unsigned int type,unsigned int level)721 CRESpellMemorization *Spellbook::GetSpellMemorization(unsigned int type, unsigned int level)
722 {
723 if (type >= (unsigned int)NUM_BOOK_TYPES)
724 return NULL;
725
726 CRESpellMemorization *sm;
727 if (level >= GetSpellLevelCount(type)) {
728 sm = new CRESpellMemorization();
729 sm->Type = (ieWord) type;
730 sm->Level = (ieWord) level;
731 sm->SlotCount = sm->SlotCountWithBonus = 0;
732 if ( !AddSpellMemorization(sm) ) {
733 delete sm;
734 return NULL;
735 }
736 assert(sm == spells[type][level]);
737 } else {
738 sm = spells[type][level];
739 }
740 return sm;
741 }
742 //if bonus is not set, then sets the base value (adjusts bonus too)
743 //if bonus is set, then sets only the bonus
744 //if the bonus value is 0, then the bonus is double base value
745 //bonus is cummulative, but not saved
SetMemorizableSpellsCount(int Value,int type,unsigned int level,bool bonus)746 void Spellbook::SetMemorizableSpellsCount(int Value, int type, unsigned int level, bool bonus)
747 {
748 int diff;
749
750 if (type >= NUM_BOOK_TYPES) {
751 return;
752 }
753
754 CRESpellMemorization* sm = GetSpellMemorization(type, level);
755 if (bonus) {
756 if (!Value) {
757 Value=sm->SlotCountWithBonus;
758 }
759 //if can't cast w/o bonus then can't cast at all!
760 if (sm->SlotCount)
761 sm->SlotCountWithBonus=(ieWord) (sm->SlotCountWithBonus+Value);
762 }
763 else {
764 diff=sm->SlotCountWithBonus-sm->SlotCount;
765 sm->SlotCount=(ieWord) Value;
766 sm->SlotCountWithBonus=(ieWord) (Value+diff);
767 }
768 }
769
GetMemorizableSpellsCount(int type,unsigned int level,bool bonus) const770 int Spellbook::GetMemorizableSpellsCount(int type, unsigned int level, bool bonus) const
771 {
772 if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type))
773 return 0;
774 CRESpellMemorization* sm = spells[type][level];
775 if (bonus)
776 return sm->SlotCountWithBonus;
777 return sm->SlotCount;
778 }
779
MemorizeSpell(const CREKnownSpell * spell,bool usable)780 bool Spellbook::MemorizeSpell(const CREKnownSpell* spell, bool usable)
781 {
782 ieWord spellType = spell->Type;
783 CRESpellMemorization* sm = spells[spellType][spell->Level];
784 if (sm->SlotCountWithBonus <= sm->memorized_spells.size() && !(innate & (1<<spellType))) {
785 //it is possible to have sorcerer type spellbooks for any spellbook type
786 if (! (sorcerer & (1<<spellType) ) )
787 return false;
788 }
789
790 CREMemorizedSpell* mem_spl = new CREMemorizedSpell();
791 CopyResRef( mem_spl->SpellResRef, spell->SpellResRef );
792 mem_spl->Flags = usable ? 1 : 0; // FIXME: is it all it's used for?
793
794 sm->memorized_spells.push_back( mem_spl );
795 ClearSpellInfo();
796 return true;
797 }
798
UnmemorizeSpell(const CREMemorizedSpell * spell)799 bool Spellbook::UnmemorizeSpell(const CREMemorizedSpell* spell)
800 {
801 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
802 std::vector< CRESpellMemorization* >::iterator sm;
803 for (sm = spells[i].begin(); sm != spells[i].end(); ++sm) {
804 std::vector< CREMemorizedSpell* >::iterator s;
805 for (s = (*sm)->memorized_spells.begin(); s != (*sm)->memorized_spells.end(); ++s) {
806 if (*s == spell) {
807 delete *s;
808 (*sm)->memorized_spells.erase( s );
809 ClearSpellInfo();
810 return true;
811 }
812 }
813 }
814 }
815
816 return false;
817 }
818
UnmemorizeSpell(const ieResRef ResRef,bool deplete,bool onlydepleted)819 bool Spellbook::UnmemorizeSpell(const ieResRef ResRef, bool deplete, bool onlydepleted)
820 {
821 for (int type = 0; type<NUM_BOOK_TYPES; type++) {
822 std::vector< CRESpellMemorization* >::iterator sm;
823 for (sm = spells[type].begin(); sm != spells[type].end(); ++sm) {
824 std::vector< CREMemorizedSpell* >::iterator s;
825 for (s = (*sm)->memorized_spells.begin(); s != (*sm)->memorized_spells.end(); ++s) {
826 if (strnicmp(ResRef, (*s)->SpellResRef, sizeof(ieResRef) ) ) {
827 continue;
828 }
829 if (onlydepleted && (*s)->Flags != 0) {
830 continue;
831 }
832 if (deplete) {
833 (*s)->Flags = 0;
834 } else {
835 delete *s;
836 (*sm)->memorized_spells.erase( s );
837 }
838 ClearSpellInfo();
839 return true;
840 }
841 }
842 }
843
844 return false;
845 }
846
847 //bitfield disabling type: 1 - mage, 2 - cleric etc
848 //level: if set, then finds that level only
FindUnchargedSpell(int type,int level) const849 CREMemorizedSpell* Spellbook::FindUnchargedSpell(int type, int level) const
850 {
851 int mask=1;
852
853 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
854 if (type&mask) {
855 mask<<=1;
856 continue;
857 }
858 mask<<=1;
859 for (unsigned int j = 0; j<spells[i].size(); j++) {
860 CRESpellMemorization* sm = spells[i][j];
861 if (level && (sm->Level!=level-1)) {
862 continue;
863 }
864
865 for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
866 CREMemorizedSpell *ret = sm->memorized_spells[k];
867 if (ret->Flags == 0) {
868 return ret;
869 }
870 }
871 }
872 }
873 return NULL;
874 }
875
876 //creates sorcerer style memory for the given spell type
CreateSorcererMemory(int type)877 void Spellbook::CreateSorcererMemory(int type)
878 {
879 for (size_t j = 0; j < spells[type].size(); j++) {
880 CRESpellMemorization* sm = spells[type][j];
881
882 size_t cnt = sm->memorized_spells.size();
883 while(cnt--) {
884 delete sm->memorized_spells[cnt];
885 }
886 sm->memorized_spells.clear();
887 for (unsigned int k = 0; k < sm->known_spells.size(); k++) {
888 CREKnownSpell *ck = sm->known_spells[k];
889 cnt = sm->SlotCountWithBonus;
890 while(cnt--) {
891 MemorizeSpell(ck, true);
892 }
893 }
894 }
895 }
896
ChargeAllSpells()897 void Spellbook::ChargeAllSpells()
898 {
899 int j = 1;
900 for (int i = 0; i < NUM_BOOK_TYPES; j+=j,i++) {
901 //this spellbook page type is sorcerer-like
902 if (sorcerer&j ) {
903 CreateSorcererMemory(i);
904 continue;
905 }
906
907 for (unsigned int j = 0; j < spells[i].size(); j++) {
908 CRESpellMemorization* sm = spells[i][j];
909
910 for (unsigned int k = 0; k < sm->memorized_spells.size(); k++)
911 ChargeSpell( sm->memorized_spells[k] );
912 }
913 }
914 }
915
916 //unmemorizes the highest level spell possible
917 //returns true if successful
DepleteSpell(int type)918 bool Spellbook::DepleteSpell(int type)
919 {
920 if (type>=NUM_BOOK_TYPES) {
921 return false;
922 }
923 size_t j = GetSpellLevelCount(type);
924 while(j--) {
925 CRESpellMemorization* sm = spells[type][j];
926
927 for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
928 if (DepleteSpell( sm->memorized_spells[k] )) {
929 if (sorcerer & (1<<type) ) {
930 DepleteLevel (sm, sm->memorized_spells[k]->SpellResRef);
931 }
932 return true;
933 }
934 }
935 }
936 return false;
937 }
938
DepleteLevel(CRESpellMemorization * sm,const ieResRef except)939 void Spellbook::DepleteLevel(CRESpellMemorization* sm, const ieResRef except)
940 {
941 size_t cnt = sm->memorized_spells.size();
942 ieResRef last={""};
943
944 for (size_t i = 0; i < cnt && cnt>0; i++) {
945 CREMemorizedSpell *cms = sm->memorized_spells[i];
946 //sorcerer spells are created in orderly manner
947 if (cms->Flags && strncmp(last,cms->SpellResRef,8) && strncmp(except,cms->SpellResRef,8)) {
948 memcpy(last, cms->SpellResRef, sizeof(ieResRef) );
949 cms->Flags=0;
950 /*
951 delete cms;
952 sm->memorized_spells.erase(sm->memorized_spells.begin()+i);
953 i--;
954 cnt--;
955 */
956 }
957 }
958 }
959
DepleteSpell(int type,unsigned int page,unsigned int slot)960 bool Spellbook::DepleteSpell(int type, unsigned int page, unsigned int slot)
961 {
962 bool ret;
963
964 if (NUM_BOOK_TYPES<=type) {
965 return false;
966 }
967 if (spells[type].size()<=page) {
968 return false;
969 }
970 CRESpellMemorization* sm = spells[page][type];
971 if (sm->memorized_spells.size()<=slot) {
972 return false;
973 }
974
975 CREMemorizedSpell* cms = sm->memorized_spells[slot];
976 ret = DepleteSpell(cms);
977 if (ret && (sorcerer & (1<<type) ) ) {
978 DepleteLevel (sm, cms->SpellResRef);
979 }
980
981 return ret;
982 }
983
ChargeSpell(CREMemorizedSpell * spl)984 bool Spellbook::ChargeSpell(CREMemorizedSpell* spl)
985 {
986 spl->Flags = 1;
987 ClearSpellInfo();
988 return true;
989 }
990
DepleteSpell(CREMemorizedSpell * spl)991 bool Spellbook::DepleteSpell(CREMemorizedSpell* spl)
992 {
993 if (spl->Flags) {
994 spl->Flags = 0;
995 ClearSpellInfo();
996 return true;
997 }
998 return false;
999 }
1000
ClearSpellInfo()1001 void Spellbook::ClearSpellInfo()
1002 {
1003 size_t i = spellinfo.size();
1004 while(i--) {
1005 delete spellinfo[i];
1006 }
1007 spellinfo.clear();
1008 }
1009
GetSpellInfo(SpellExtHeader * array,int type,int startindex,int count)1010 bool Spellbook::GetSpellInfo(SpellExtHeader *array, int type, int startindex, int count)
1011 {
1012 memset(array, 0, count * sizeof(SpellExtHeader) );
1013 if (spellinfo.empty()) {
1014 GenerateSpellInfo();
1015 }
1016 int actual = 0;
1017 bool ret = false;
1018 for (unsigned int i = 0; i<spellinfo.size(); i++) {
1019 if ( !(type & (1<<spellinfo[i]->type)) ) {
1020 continue;
1021 }
1022 if (startindex>0) {
1023 startindex--;
1024 continue;
1025 }
1026 if (actual>=count) {
1027 ret = true;
1028 break;
1029 }
1030 memcpy(array+actual, spellinfo[i], sizeof(SpellExtHeader));
1031 actual++;
1032 }
1033 return ret;
1034 }
1035
1036 // returns the size of spellinfo vector, if type is nonzero it is used as filter
1037 // for example type==1 lists the number of different mage spells
GetSpellInfoSize(int type)1038 unsigned int Spellbook::GetSpellInfoSize(int type)
1039 {
1040 size_t i = spellinfo.size();
1041 if (!i) {
1042 GenerateSpellInfo();
1043 i = spellinfo.size();
1044 }
1045 if (!type) {
1046 return (unsigned int) i;
1047 }
1048 unsigned int count = 0;
1049 while(i--) {
1050 if (1<<(spellinfo[i]->type)&type) {
1051 count++;
1052 }
1053 }
1054 return count;
1055 }
1056
1057 //type = 0 means any type
FindSpellInfo(SpellExtHeader * array,const ieResRef spellname,unsigned int type)1058 int Spellbook::FindSpellInfo(SpellExtHeader *array, const ieResRef spellname, unsigned int type)
1059 {
1060 memset(array, 0, sizeof(SpellExtHeader) );
1061 if (spellinfo.empty()) {
1062 GenerateSpellInfo();
1063 }
1064 int offset = 0;
1065 for (unsigned int i = 0; i<spellinfo.size(); i++) {
1066 // take the offset into account, since we need per-type indices
1067 if (type && !(1<<(spellinfo[i]->type) & type)) {
1068 offset++;
1069 continue;
1070 }
1071 if (strnicmp(spellinfo[i]->spellname, spellname, sizeof(ieResRef) ) ) continue;
1072 memcpy(array, spellinfo[i], sizeof(SpellExtHeader));
1073 return i-offset+1;
1074 }
1075 return 0;
1076 }
1077
FindSpellInfo(unsigned int level,unsigned int type,const ieResRef spellname) const1078 SpellExtHeader *Spellbook::FindSpellInfo(unsigned int level, unsigned int type, const ieResRef spellname) const
1079 {
1080 size_t i = spellinfo.size();
1081 while(i--) {
1082 if ( (spellinfo[i]->level==level) &&
1083 (spellinfo[i]->type==type) &&
1084 !strnicmp(spellinfo[i]->spellname, spellname, 8)) {
1085 return spellinfo[i];
1086 }
1087 }
1088 return NULL;
1089 }
1090
AddSpellInfo(unsigned int sm_level,unsigned int sm_type,const ieResRef spellname,unsigned int idx)1091 void Spellbook::AddSpellInfo(unsigned int sm_level, unsigned int sm_type, const ieResRef spellname, unsigned int idx)
1092 {
1093 Spell *spl = gamedata->GetSpell(spellname, true);
1094 if (!spl)
1095 return;
1096 if (spl->ExtHeaderCount<1)
1097 return;
1098
1099 ieDword level = 0;
1100 SpellExtHeader *seh = FindSpellInfo(sm_level, sm_type, spellname);
1101 if (seh) {
1102 seh->count++;
1103 return;
1104 }
1105
1106 seh = new SpellExtHeader;
1107 spellinfo.push_back( seh );
1108
1109 memcpy(seh->spellname, spellname, sizeof(ieResRef) );
1110 int ehc;
1111
1112 for (ehc = 0; ehc < spl->ExtHeaderCount-1; ehc++) {
1113 if (level<spl->ext_headers[ehc+1].RequiredLevel) {
1114 break;
1115 }
1116 }
1117
1118 SPLExtHeader *ext_header = spl->ext_headers+ehc;
1119 seh->headerindex = ehc;
1120 seh->level = sm_level;
1121 seh->type = sm_type;
1122 seh->slot = idx;
1123 seh->count = 1;
1124 seh->SpellForm = ext_header->SpellForm;
1125 memcpy(seh->MemorisedIcon, ext_header->MemorisedIcon,sizeof(ieResRef) );
1126 seh->Target = ext_header->Target;
1127 seh->TargetNumber = ext_header->TargetNumber;
1128 seh->Range = ext_header->Range;
1129 seh->Projectile = ext_header->ProjectileAnimation;
1130 seh->CastingTime = (ieWord) ext_header->CastingTime;
1131 seh->strref = spl->SpellName;
1132 gamedata->FreeSpell(spl, spellname, false);
1133 }
1134
SetCustomSpellInfo(const ieResRef * data,ieResRef spell,int type)1135 void Spellbook::SetCustomSpellInfo(const ieResRef *data, ieResRef spell, int type)
1136 {
1137 ClearSpellInfo();
1138 if (data) {
1139 for(int i = 0; i<type;i++) {
1140 AddSpellInfo(0,0,data[i],-1);
1141 }
1142 return;
1143 }
1144
1145 //if data is not set, use the known spells list to set up the spellinfo list
1146 for(int i = 0; i<NUM_BOOK_TYPES; i++) {
1147 if ((1<<i)&type) {
1148 for(unsigned int j = 0; j<spells[i].size(); j++) {
1149 CRESpellMemorization* sm = spells[i][j];
1150
1151 for(unsigned int k=0;k<sm->known_spells.size(); k++) {
1152 CREKnownSpell* slot = sm->known_spells[k];
1153 if (!slot)
1154 continue;
1155 //skip the spell itself
1156 if (spell && !strnicmp(slot->SpellResRef, spell, sizeof(ieResRef)))
1157 continue;
1158 AddSpellInfo(sm->Level, sm->Type, slot->SpellResRef, -1);
1159 }
1160 }
1161 }
1162 }
1163 }
1164
1165 // grouping the castable spells
GenerateSpellInfo()1166 void Spellbook::GenerateSpellInfo()
1167 {
1168 ClearSpellInfo(); //just in case
1169 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
1170 for (unsigned int j = 0; j < spells[i].size(); j++) {
1171 CRESpellMemorization* sm = spells[i][j];
1172
1173 for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
1174 CREMemorizedSpell* slot = sm->memorized_spells[k];
1175 if (!slot)
1176 continue;
1177 if (!slot->Flags)
1178 continue;
1179 AddSpellInfo(sm->Level, sm->Type, slot->SpellResRef, k);
1180 }
1181 }
1182 }
1183 }
1184
dump() const1185 void Spellbook::dump() const
1186 {
1187 StringBuffer buffer;
1188 dump(buffer);
1189 Log(DEBUG, "Spellbook", buffer);
1190 }
1191
dump(StringBuffer & buffer) const1192 void Spellbook::dump(StringBuffer& buffer) const
1193 {
1194 unsigned int k;
1195
1196 buffer.append( "SPELLBOOK:\n" );
1197 for (int i = 0; i < NUM_BOOK_TYPES; i++) {
1198 for (unsigned int j = 0; j < spells[i].size(); j++) {
1199 CRESpellMemorization* sm = spells[i][j];
1200
1201 if (sm->known_spells.size())
1202 buffer.append( " Known spells:\n" );
1203 for (k = 0; k < sm->known_spells.size(); k++) {
1204 CREKnownSpell* spl = sm->known_spells[k];
1205 if (!spl) continue;
1206
1207 buffer.appendFormatted ( " %2d: %8s L: %d T: %d\n", k, spl->SpellResRef, spl->Level, spl->Type );
1208 }
1209
1210 if (sm->memorized_spells.size())
1211 buffer.append( " Memorized spells:\n" );
1212 for (k = 0; k < sm->memorized_spells.size (); k++) {
1213 CREMemorizedSpell* spl = sm->memorized_spells[k];
1214 if (!spl) continue;
1215
1216 buffer.appendFormatted ( " %2u: %8s %x\n", k, spl->SpellResRef, spl->Flags );
1217 }
1218 }
1219 }
1220 }
1221
1222 }
1223