"code script" system completely rewritten
[k8-i-v-a-n.git] / src / game / script.cpp
blob2810bb1c1b6a68f8046dd87324b4a8ef040429b5
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 #include "script.h"
13 #include "fesave.h"
14 #include "feparse.h"
15 #include "game.h"
16 #include "materia.h"
17 #include "char.h"
18 #include "proto.h"
19 #include "allocate.h"
21 script::datamap posscript::DataMap;
22 script::datamap materialscript::DataMap;
23 script::datamap basecontentscript::DataMap;
24 script::datamap contentscript<character>::DataMap;
25 script::datamap contentscript<item>::DataMap;
26 script::datamap contentscript<glterrain>::DataMap;
27 script::datamap contentscript<olterrain>::DataMap;
28 script::datamap squarescript::DataMap;
29 script::datamap roomscript::DataMap;
30 script::datamap levelscript::DataMap;
31 script::datamap dungeonscript::DataMap;
32 script::datamap teamscript::DataMap;
33 script::datamap gamescript::DataMap;
35 template <class type, class contenttype> script::datamap contentmap<type, contenttype>::DataMap;
38 template <class type> void scriptmember<type>::ReadFrom (TextInput &SaveFile) {
39 SrcFile = SaveFile.GetFileName();
40 SrcLine = SaveFile.CurrentLine();
41 if (!Member) Member = new type;
42 ReadData(*Member, SaveFile);
46 template <class type> void scriptmember<type>::Replace (scriptmemberbase &Base) {
47 scriptmember<type> &Data = static_cast<scriptmember<type> &>(Base);
49 if (Data.Member) {
50 delete Member;
51 Member = Data.Member;
52 Data.Member = 0;
54 SrcFile = Base.SrcFile;
55 SrcLine = Base.SrcLine;
59 template <class type> void scriptmember<type>::Save (outputfile &SaveFile) const {
60 if (Member) {
61 SaveFile.Put(1);
62 SaveFile << SrcFile << SrcLine;
63 SaveFile << *Member;
64 } else {
65 SaveFile.Put(0);
69 template <class type> void scriptmember<type>::Load (inputfile &SaveFile) {
70 if (SaveFile.Get()) {
71 Member = new type;
72 SaveFile >> SrcFile >> SrcLine;
73 SaveFile >> *Member;
78 #define INST_SCRIPT_MEMBER(type)\
79 template void scriptmember< type >::ReadFrom(TextInput &);\
80 template void scriptmember< type >::Replace(scriptmemberbase &);\
81 template void scriptmember< type >::Save(outputfile &) const;\
82 template void scriptmember< type >::Load(inputfile &)
84 INST_SCRIPT_MEMBER(uChar);
85 INST_SCRIPT_MEMBER(short);
86 INST_SCRIPT_MEMBER(int);
87 //INST_SCRIPT_MEMBER(sLong); //k8:64
88 INST_SCRIPT_MEMBER(v2);
89 INST_SCRIPT_MEMBER(festring);
90 INST_SCRIPT_MEMBER(fearray<v2>);
91 INST_SCRIPT_MEMBER(rect);
92 INST_SCRIPT_MEMBER(interval);
93 INST_SCRIPT_MEMBER(region);
94 INST_SCRIPT_MEMBER(posscript);
95 INST_SCRIPT_MEMBER(materialscript);
96 INST_SCRIPT_MEMBER(squarescript);
97 INST_SCRIPT_MEMBER(roomscript);
98 INST_SCRIPT_MEMBER(levelscript);
99 INST_SCRIPT_MEMBER(contentscript<character>);
100 INST_SCRIPT_MEMBER(fearray<contentscript<item> >);
101 INST_SCRIPT_MEMBER(contentscript<glterrain>);
102 INST_SCRIPT_MEMBER(contentscript<olterrain>);
103 INST_SCRIPT_MEMBER(charactercontentmap);
104 INST_SCRIPT_MEMBER(itemcontentmap);
105 INST_SCRIPT_MEMBER(glterraincontentmap);
106 INST_SCRIPT_MEMBER(olterraincontentmap);
107 INST_SCRIPT_MEMBER(fearray<packv2>);
110 template <class type> void fastscriptmember<type>::ReadFrom (TextInput &SaveFile) {
111 SrcFile = SaveFile.GetFileName();
112 SrcLine = SaveFile.CurrentLine();
113 ReadData(*&Member, SaveFile); // gcc 3.4.1 sucks
117 template <class type> void fastscriptmember<type>::Replace (scriptmemberbase &Base) {
118 fastscriptmember<type> &Data = static_cast<fastscriptmember<type> &>(Base);
120 Member = Data.Member;
121 SrcFile = Base.SrcFile;
122 SrcLine = Base.SrcLine;
126 template <class type> void fastscriptmember<type>::Save(outputfile &SaveFile) const {
127 SaveFile << SrcFile << SrcLine;
128 SaveFile << Member;
132 template <class type> void fastscriptmember<type>::Load (inputfile &SaveFile) {
133 SaveFile >> SrcFile >> SrcLine;
134 SaveFile >> *&Member; // gcc 3.4.1 sucks
138 #define INST_FAST_SCRIPT_MEMBER(type)\
139 template void fastscriptmember< type >::ReadFrom(TextInput&);\
140 template void fastscriptmember< type >::Replace(scriptmemberbase&);\
141 template void fastscriptmember< type >::Save(outputfile&) const;\
142 template void fastscriptmember< type >::Load(inputfile&)
144 INST_FAST_SCRIPT_MEMBER(char);
145 INST_FAST_SCRIPT_MEMBER(uChar);
146 INST_FAST_SCRIPT_MEMBER(int);
147 //INST_FAST_SCRIPT_MEMBER(sLong); //k8:64
148 INST_FAST_SCRIPT_MEMBER(feuLong);
149 INST_FAST_SCRIPT_MEMBER(packv2);
152 void script::Save (outputfile &SaveFile) const {
153 SaveFile << SrcFile << SrcLine;
154 SaveDataMap(GetDataMap(), SaveFile);
158 void script::Load (inputfile &SaveFile) {
159 SaveFile >> SrcFile >> SrcLine;
160 LoadDataMap(GetDataMap(), SaveFile);
164 truth script::ReadMember (TextInput &SaveFile, cfestring &Word) {
165 scriptmemberbase *Data = GetData(Word.CStr());
167 if (Data) {
168 Data->ReadFrom(SaveFile);
169 return true;
171 return false;
175 scriptmemberbase *script::GetDataFromMap (const datamap &DataMap, cchar *Identifier) {
176 datamap::const_iterator i = DataMap.find(Identifier);
178 return i != DataMap.end() ? &(this->*i->second) : 0;
182 void script::SaveDataMap (const datamap &DataMap, outputfile &SaveFile) const {
183 for (datamap::const_iterator i = DataMap.begin(); i != DataMap.end(); ++i) (this->*i->second).Save(SaveFile);
187 void script::LoadDataMap (const datamap &DataMap, inputfile &SaveFile) {
188 for (datamap::const_iterator i = DataMap.begin(); i != DataMap.end(); ++i) (this->*i->second).Load(SaveFile);
192 template <class scriptmemberptr> void InitMember (script::datamap &DataMap, cchar *Identifier, scriptmemberptr DataMember) {
193 DataMap[Identifier] = reinterpret_cast<scriptmemberbase script::*>(DataMember);
197 #define INIT_ENTRY(name) InitMember(DataMap, #name, &scripttype::name##Holder)
198 #define INIT(name, value) name##Holder(value)
201 void posscript::InitDataMap () {
202 INIT_ENTRY(Vector);
203 INIT_ENTRY(Flags);
204 INIT_ENTRY(Borders);
208 void posscript::ReadFrom (TextInput &SaveFile) {
209 static festring Word;
211 SrcFile = SaveFile.GetFileName();
212 SrcLine = SaveFile.CurrentLine();
213 SaveFile.ReadWord(Word);
214 if (Word == "Pos") {
215 Random = false;
216 VectorHolder.ReadFrom(SaveFile);
217 } else if (Word == "Random") {
218 Random = true;
219 FlagsHolder.ReadFrom(SaveFile);
220 } else if (Word == "BoundedRandom") {
221 Random = true;
222 BordersHolder.ReadFrom(SaveFile);
223 FlagsHolder.ReadFrom(SaveFile);
228 void posscript::Save (outputfile &SaveFile) const {
229 script::Save(SaveFile);
230 SaveFile << Random;
234 void posscript::Load (inputfile &SaveFile) {
235 script::Load(SaveFile);
236 SaveFile >> Random;
240 void materialscript::InitDataMap () {
241 INIT_ENTRY(Volume);
245 void materialscript::ReadFrom (TextInput &SaveFile) {
246 static festring Word;
248 SrcFile = SaveFile.GetFileName();
249 SrcLine = SaveFile.CurrentLine();
250 SaveFile.ReadWord(Word);
251 if (Word == "=") SaveFile.ReadWord(Word);
252 if (Word == "0") {
253 Config = 0;
254 } else {
255 valuemap::const_iterator i = game::GetGlobalValueMap().find(Word);
257 if (i != game::GetGlobalValueMap().end()) Config = i->second;
258 else ABORT("Unconfigured material script detected at line %d!", SaveFile.TokenLine());
260 if (SaveFile.ReadWord() != "{") return;
261 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
262 if (!ReadMember(SaveFile, Word)) {
263 ABORT("Odd script term %s encountered in material script line %d!", Word.CStr(), SaveFile.TokenLine());
269 material *materialscript::Instantiate () const {
270 return MAKE_MATERIAL(Config, GetVolume() ? GetVolume()->Randomize() : 0);
274 void materialscript::Save (outputfile &SaveFile) const {
275 script::Save(SaveFile);
276 SaveFile << (uShort)Config;
280 void materialscript::Load (inputfile &SaveFile) {
281 script::Load(SaveFile);
282 Config = 0;
283 uShort s2;
284 SaveFile >> s2;
285 Config = s2;
289 void basecontentscript::InitDataMap () {
290 INIT_ENTRY(MainMaterial);
291 INIT_ENTRY(SecondaryMaterial);
292 INIT_ENTRY(Parameters);
296 basecontentscript::basecontentscript () : script(), ContentType(0), Random(false), Config(0), INIT(Parameters, NO_PARAMETERS) {
300 void basecontentscript::ReadFrom (TextInput &SaveFile) {
301 static festring Word;
302 SrcFile = SaveFile.GetFileName();
303 SrcLine = SaveFile.CurrentLine();
304 for (;;) {
305 SaveFile.ReadWord(Word);
306 if (Word == "on") {
307 //mCode = SaveFile.ReadCode();
308 mCode.collectSource(SaveFile);
309 SaveFile.skipBlanks();
310 int ch = SaveFile.GetChar();
311 if (ch != ';') SaveFile.UngetChar(ch);
312 continue;
314 break;
316 if (Word == "=" || Word == ",") SaveFile.ReadWord(Word);
317 valuemap::const_iterator i = game::GetGlobalValueMap().find(Word);
318 if (i != game::GetGlobalValueMap().end()) {
319 if (!GetMainMaterial()) MainMaterialHolder.Member = new materialscript;
320 MainMaterialHolder.Member->SetConfig(i->second);
321 SaveFile.ReadWord(Word);
322 i = game::GetGlobalValueMap().find(Word);
323 if (i != game::GetGlobalValueMap().end()) {
324 if (!GetSecondaryMaterial()) SecondaryMaterialHolder.Member = new materialscript;
325 SecondaryMaterialHolder.Member->SetConfig(i->second);
326 SaveFile.ReadWord(Word);
329 if (Word == "NaturalMaterialForm") {
330 Random = false;
331 ContentType = NATURAL_MATERIAL_FORM;
332 SaveFile.ReadWord(Word);
333 } else if (Word == "Random") {
334 Random = true;
335 SaveFile.ReadWord(Word);
336 } else {
337 Random = false;
338 ContentType = SearchCodeName(Word);
339 if (ContentType || Word == "0") SaveFile.ReadWord(Word);
340 else ABORT("Odd script term %s encountered in %s content script, file %s line %d!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
342 if (Word == "(") {
343 Config = SaveFile.ReadNumber();
344 SaveFile.ReadWord(Word);
345 } else {
346 Config = 0;
348 if (Word == "{") {
349 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
350 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in %s content script, file %s line %d!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
352 } else {
354 if (Word == "[") {
355 mCode = SaveFile.ReadCode();
356 SaveFile.ReadWord(Word);
359 if (Word != ";" && Word != ",") ABORT("Odd terminator %s encountered in %s content script, file %s line %d!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
364 scriptmemberbase *basecontentscript::GetData (cchar *String) {
365 scriptmemberbase *Return = GetDataFromMap(GetDataMap(), String);
366 return Return ? Return : GetDataFromMap(DataMap, String);
370 void basecontentscript::Save (outputfile &SaveFile) const {
371 SaveFile << SrcFile << SrcLine;
372 SaveDataMap(GetDataMap(), SaveFile);
373 SaveDataMap(DataMap, SaveFile);
374 SaveFile << ContentType;
375 SaveFile.Put(!!Random);
376 SaveFile << Config;
380 void basecontentscript::Load (inputfile &SaveFile) {
381 SaveFile >> SrcFile >> SrcLine;
382 LoadDataMap(GetDataMap(), SaveFile);
383 LoadDataMap(DataMap, SaveFile);
384 ContentType = ReadType(uShort, SaveFile);
385 Random = SaveFile.Get();
386 SaveFile >> Config;
390 template <class type> type *contentscripttemplate<type>::BasicInstantiate (int SpecialFlags) const {
391 type *Instance = 0;
392 const typename type::prototype *Proto = protocontainer<type>::GetProto(ContentType);
393 if (!Proto) ABORT("BasicInstantiate error for type '%s'!", getTypeName<type>().c_str());
394 const typename type::database *const *ConfigData = Proto->GetConfigData();
395 const materialscript *MainMaterial = GetMainMaterial();
396 const materialscript *SecondaryMaterial = GetSecondaryMaterial();
397 const typename type::database *DataBase = *ConfigData;
398 truth UseOverriddenMaterials = false;
400 if (!Config && DataBase->IsAbstract) {
401 while (!Instance) {
402 DataBase = ConfigData[1+RAND()%(Proto->GetConfigSize()-1)];
403 if (DataBase->AllowRandomInstantiation()) {
404 if (!(SpecialFlags & NO_MATERIALS) && MainMaterial && (!DataBase->HasSecondaryMaterial || SecondaryMaterial)) {
405 SpecialFlags |= NO_MATERIALS;
406 UseOverriddenMaterials = true;
408 Instance = Proto->Spawn(DataBase->Config, SpecialFlags|NO_PIC_UPDATE);
411 } else {
412 if (!(SpecialFlags & NO_MATERIALS) && MainMaterial && (!DataBase->HasSecondaryMaterial || SecondaryMaterial)) {
413 SpecialFlags |= NO_MATERIALS;
414 UseOverriddenMaterials = true;
416 Instance = Proto->Spawn(Config, SpecialFlags|NO_PIC_UPDATE);
418 if (GetParameters() != NO_PARAMETERS) Instance->SetParameters(GetParameters());
419 if (UseOverriddenMaterials) {
420 Instance->InitMaterials(MainMaterial, SecondaryMaterial, false);
421 } else {
422 if (MainMaterial) Instance->ChangeMainMaterial(MainMaterial->Instantiate(), SpecialFlags|NO_PIC_UPDATE);
423 if (SecondaryMaterial) Instance->ChangeSecondaryMaterial(SecondaryMaterial->Instantiate(), SpecialFlags|NO_PIC_UPDATE);
425 if (!(SpecialFlags & NO_PIC_UPDATE)) Instance->UpdatePictures();
426 return Instance;
430 /* Called by an inline function in script.h... */
432 template glterrain *contentscripttemplate<glterrain>::BasicInstantiate (int) const;
434 template <class type> int contentscripttemplate<type>::SearchCodeName (cfestring &String) const {
435 return protocontainer<type>::SearchCodeName(String);
438 /* GCC 2.952 SUCKS!!! IT MUST BURN!!! */
440 template int contentscripttemplate<character>::SearchCodeName(cfestring &) const;
441 template int contentscripttemplate<item>::SearchCodeName(cfestring &) const;
442 template int contentscripttemplate<glterrain>::SearchCodeName(cfestring &) const;
443 template int contentscripttemplate<olterrain>::SearchCodeName(cfestring &) const;
445 cchar *contentscript<character>::GetClassID () const { return "character"; }
446 cchar *contentscript<item>::GetClassID () const { return "item"; }
447 cchar *contentscript<glterrain>::GetClassID () const { return "glterrain"; }
448 cchar *contentscript<olterrain>::GetClassID () const { return "olterrain"; }
451 void contentscript<character>::InitDataMap () {
452 INIT_ENTRY(Inventory);
453 INIT_ENTRY(WayPoint);
454 INIT_ENTRY(Team);
455 INIT_ENTRY(Flags);
459 contentscript<character>::contentscript () : INIT(Team, DEFAULT_TEAM), INIT(Flags, 0) {
463 character *contentscript<character>::Instantiate (int SpecialFlags) const {
464 character *Instance = contentscripttemplate<character>::BasicInstantiate(SpecialFlags);
465 //fprintf(stderr, "instantiating character '%s'\n", Instance->GetNameSingular().CStr());
467 game::ClearEventData();
468 if (!game::RunCharAllowScript(Instance, mCode, "spawn")) {
469 //fprintf(stderr, "dropping character '%s'\n", Instance->GetNameSingular().CStr());
470 delete Instance;
471 return 0;
474 if (GetTeam() != DEFAULT_TEAM) Instance->SetTeam(game::GetTeam(GetTeam()));
475 const fearray<contentscript<item> > *Inventory = GetInventory();
476 if (Inventory) Instance->AddToInventory(*Inventory, SpecialFlags);
477 const fearray<packv2> *WayPoint = GetWayPoint();
478 if (WayPoint) Instance->SetWayPoints(*WayPoint);
479 Instance->RestoreHP();
480 Instance->RestoreStamina();
481 return Instance;
485 contentscript<item>::contentscript () :
486 INIT(Category, ANY_CATEGORY),
487 INIT(MinPrice, 0),
488 INIT(MaxPrice, MAX_PRICE),
489 INIT(Team, DEFAULT_TEAM),
490 INIT(SquarePosition, CENTER),
491 INIT(Chance, 100),
492 INIT(ConfigFlags, 0),
493 INIT(SpoilPercentage, 0),
494 INIT(Enchantment, 0),
495 INIT(IsActive, false)
500 void contentscript<item>::InitDataMap () {
501 INIT_ENTRY(ItemsInside);
502 INIT_ENTRY(Times);
503 INIT_ENTRY(MinPrice);
504 INIT_ENTRY(MaxPrice);
505 INIT_ENTRY(LifeExpectancy);
506 INIT_ENTRY(Team);
507 INIT_ENTRY(Category);
508 INIT_ENTRY(SquarePosition);
509 INIT_ENTRY(Chance);
510 INIT_ENTRY(ConfigFlags);
511 INIT_ENTRY(SpoilPercentage);
512 INIT_ENTRY(Enchantment);
513 INIT_ENTRY(IsActive);
517 item *contentscript<item>::InstantiateBasedOnMaterial (int MaterialConfig, int SpecialFlags) const {
518 if (ContentType == NATURAL_MATERIAL_FORM) {
519 const materialscript *MainMaterial = GetMainMaterial();
520 sLong Volume = MainMaterial && MainMaterial->GetVolume() ? MainMaterial->GetVolume()->Randomize() : 0;
521 return material::CreateNaturalForm(MaterialConfig, Volume);
523 return Instantiate(SpecialFlags);
527 item *contentscript<item>::Instantiate (int SpecialFlags) const {
528 int Chance = GetChance();
529 item *Instance = 0;
531 if (Chance != 100 && Chance <= RAND_N(100)) return 0;
533 if (Random) {
534 Instance = protosystem::BalancedCreateItem(0, GetMinPrice(), GetMaxPrice(), GetCategory(), SpecialFlags, GetConfigFlags());
535 } else {
536 Instance = contentscripttemplate<item>::BasicInstantiate(SpecialFlags);
539 game::ClearEventData();
540 if (!game::RunItemAllowScript(Instance, mCode, "spawn")) {
541 //fprintf(stderr, "dropping character '%s'\n", Instance->GetNameSingular().CStr());
542 delete Instance;
543 return 0;
546 if (GetLifeExpectancy()) Instance->SetLifeExpectancy(GetLifeExpectancy()->Min, (GetLifeExpectancy()->Max - GetLifeExpectancy()->Min) + 1);
547 if (GetTeam() != DEFAULT_TEAM) Instance->SetTeam(GetTeam());
548 if (IsActive()) Instance->SetIsActive(true);
549 if (GetEnchantment() != 0) Instance->SetEnchantment(GetEnchantment());
550 const fearray<contentscript<item> > *ItemsInside = GetItemsInside();
551 if (ItemsInside) Instance->SetItemsInside(*ItemsInside, SpecialFlags);
552 if (GetSpoilPercentage() != 0) Instance->SetSpoilPercentage(GetSpoilPercentage());
553 return Instance;
557 truth IsValidScript (const fearray<contentscript<item> > *Array) {
558 for (uInt c = 0; c < Array->Size; ++c) if (IsValidScript(&Array->Data[c])) return true;
559 return false;
563 void contentscript<glterrain>::InitDataMap () {
564 INIT_ENTRY(IsInside);
568 contentscript<olterrain>::contentscript () :
569 INIT(VisualEffects, 0),
570 INIT(AttachedArea, DEFAULT_ATTACHED_AREA),
571 INIT(AttachedEntry, DEFAULT_ATTACHED_ENTRY)
576 void contentscript<olterrain>::InitDataMap () {
577 INIT_ENTRY(ItemsInside);
578 INIT_ENTRY(Text);
579 INIT_ENTRY(VisualEffects);
580 INIT_ENTRY(AttachedArea);
581 INIT_ENTRY(AttachedEntry);
585 olterrain *contentscript<olterrain>::Instantiate (int SpecialFlags) const {
586 if (!ContentType) return 0;
587 olterrain *Instance = contentscripttemplate<olterrain>::BasicInstantiate(SpecialFlags);
588 if (GetVisualEffects()) {
589 Instance->SetVisualEffects(GetVisualEffects());
590 Instance->UpdatePictures();
592 if (GetAttachedArea() != DEFAULT_ATTACHED_AREA) Instance->SetAttachedArea(GetAttachedArea());
593 if (GetAttachedEntry() != DEFAULT_ATTACHED_ENTRY) Instance->SetAttachedEntry(GetAttachedEntry());
594 cfestring *Text = GetText();
595 if (Text) Instance->SetText(*Text);
596 const fearray<contentscript<item> > *ItemsInside = GetItemsInside();
597 if (ItemsInside) Instance->SetItemsInside(*ItemsInside, SpecialFlags);
598 return Instance;
602 squarescript::squarescript () : INIT(EntryIndex, NO_ENTRY), INIT(AttachRequired, false) {
606 void squarescript::InitDataMap () {
607 INIT_ENTRY(Position);
608 INIT_ENTRY(Character);
609 INIT_ENTRY(Items);
610 INIT_ENTRY(GTerrain);
611 INIT_ENTRY(OTerrain);
612 INIT_ENTRY(Times);
613 INIT_ENTRY(EntryIndex);
614 INIT_ENTRY(AttachRequired);
618 void squarescript::ReadFrom (TextInput &SaveFile) {
619 static festring Word;
621 SrcFile = SaveFile.GetFileName();
622 SrcLine = SaveFile.CurrentLine();
623 SaveFile.ReadWord(Word);
624 if (Word != "=") {
625 PositionHolder.ReadFrom(SaveFile);
626 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in square script line %d!", SaveFile.TokenLine());
627 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
628 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in square script line %d!", Word.CStr(), SaveFile.TokenLine());
630 } else {
631 GTerrainHolder.ReadFrom(SaveFile);
632 OTerrainHolder.ReadFrom(SaveFile);
637 template <class type, class contenttype> contentmap<type, contenttype>::contentmap() : ContentMap(0) {
641 template <class type, class contenttype> contentmap<type, contenttype>::~contentmap<type, contenttype> () {
642 delete [] ContentMap;
646 template <class type, class contenttype> void contentmap<type, contenttype>::InitDataMap () {
647 INIT_ENTRY(Size);
648 INIT_ENTRY(Pos);
652 template <class type, class contenttype> void contentmap<type, contenttype>::ReadFrom (TextInput &SaveFile) {
653 typedef std::map<int, contenttype> maptype;
654 typedef typename maptype::iterator mapiterator;
655 festring Word1, Word2;
657 SrcFile = SaveFile.GetFileName();
658 SrcLine = SaveFile.CurrentLine();
659 if (ContentMap) ABORT("Illegal %s content map redefinition on line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
660 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in %s content map script line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
661 SymbolMap.insert(std::pair<int, contenttype>('.', contenttype()));
662 for (SaveFile.ReadWord(Word1); Word1 != "}"; Word1 = SaveFile.ReadWord()) {
663 if (Word1 == "Types") {
664 if (SaveFile.ReadWord() != "{") ABORT("Missing bracket in %s content map script line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
665 for (SaveFile.ReadWord(Word2); Word2 != "}"; Word2 = SaveFile.ReadWord()) {
666 std::pair<mapiterator, bool> Return = SymbolMap.insert(std::pair<int, contenttype>(Word2[0], contenttype()));
668 if (Return.second) {
669 ReadData(Return.first->second, SaveFile);
670 } else {
671 ABORT("Symbol %c defined again in %s content map script line %d!", Word2[0], protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
674 continue;
676 if (!ReadMember(SaveFile, Word1)) ABORT("Odd script term %s encountered in %s content script line %d!", Word1.CStr(), protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
679 v2 Size = *GetSize();
681 Alloc2D(ContentMap, Size.X, Size.Y);
682 if (SaveFile.ReadWord() != "{") ABORT("Missing bracket in %s content map script line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
683 for (int y = 0; y < Size.Y; ++y) {
684 for (int x = 0; x < Size.X; ++x) {
685 int Char = SaveFile.ReadLetter();
686 //typename std::map<int, contenttype>::iterator i = SymbolMap.find(Char);
687 auto i = SymbolMap.find(Char);
688 if (i != SymbolMap.end()) {
689 ContentMap[x][y] = std::make_pair(Char, &i->second);
690 } else {
691 if (Char <= ' ') {
692 ABORT("Illegal content '\\x%02x' in %s content map in file '%s' line %d!", (unsigned)Char, protocontainer<type>::GetMainClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
693 } else {
694 ABORT("Illegal content '%c' in %s content map in file '%s' line %d!", Char, protocontainer<type>::GetMainClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
699 if (SaveFile.ReadWord() != "}") ABORT("Missing bracket in %s content map script in file '%s' line %d!", protocontainer<type>::GetMainClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
703 template <class type, class contenttype> void contentmap<type, contenttype>::Save (outputfile &SaveFile) const {
704 script::Save(SaveFile);
705 SaveFile << SymbolMap;
707 v2 Size = *GetSize();
708 for (int y = 0; y < Size.Y; ++y)
709 for (int x = 0; x < Size.X; ++x)
710 SaveFile << char(ContentMap[x][y].first);
714 template <class type, class contenttype> void contentmap<type, contenttype>::Load (inputfile &SaveFile) {
715 script::Load(SaveFile);
716 SaveFile >> SymbolMap;
718 v2 Size = *GetSize();
720 Alloc2D(ContentMap, Size.X, Size.Y);
721 for (int y = 0; y < Size.Y; ++y) {
722 for (int x = 0; x < Size.X; ++x) {
723 int Char = ReadType(char, SaveFile);
725 ContentMap[x][y] = std::make_pair(Char, &SymbolMap.find(Char)->second);
731 const std::list<squarescript> &roomscript::GetSquare () const { return Square; }
734 void roomscript::InitDataMap () {
735 INIT_ENTRY(CharacterMap);
736 INIT_ENTRY(ItemMap);
737 INIT_ENTRY(GTerrainMap);
738 INIT_ENTRY(OTerrainMap);
739 INIT_ENTRY(WallSquare);
740 INIT_ENTRY(FloorSquare);
741 INIT_ENTRY(DoorSquare);
742 INIT_ENTRY(Size);
743 INIT_ENTRY(Pos);
744 INIT_ENTRY(AltarPossible);
745 INIT_ENTRY(GenerateDoor);
746 INIT_ENTRY(GenerateTunnel);
747 INIT_ENTRY(DivineMaster);
748 INIT_ENTRY(GenerateLanterns);
749 INIT_ENTRY(Type);
750 INIT_ENTRY(GenerateFountains);
751 INIT_ENTRY(AllowLockedDoors);
752 INIT_ENTRY(AllowBoobyTrappedDoors);
753 INIT_ENTRY(Shape);
754 INIT_ENTRY(IsInside);
755 INIT_ENTRY(GenerateWindows);
756 INIT_ENTRY(UseFillSquareWalls);
757 INIT_ENTRY(Flags);
758 INIT_ENTRY(GenerateWards);
759 INIT_ENTRY(AllowedDivineMasters);
763 void roomscript::ReadFrom (TextInput &SaveFile) {
764 festring Word;
766 SrcFile = SaveFile.GetFileName();
767 SrcLine = SaveFile.CurrentLine();
768 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in room script line %d!", SaveFile.TokenLine());
769 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
770 if (Word == "Square") {
771 Square.push_back(squarescript());
772 Square.back().ReadFrom(SaveFile);
773 continue;
775 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in room script line %d!", Word.CStr(), SaveFile.TokenLine());
780 void roomscript::Save (outputfile &SaveFile) const {
781 script::Save(SaveFile);
782 SaveFile << Square;
786 void roomscript::Load (inputfile &SaveFile) {
787 script::Load(SaveFile);
788 SaveFile >> Square;
792 const std::list<squarescript> &levelscript::GetSquare () const { return Square; }
793 const std::list<roomscript> &levelscript::GetRoom () const { return Room; }
796 void levelscript::InitDataMap () {
797 INIT_ENTRY(RoomDefault);
798 INIT_ENTRY(FillSquare);
799 INIT_ENTRY(TunnelSquare);
800 INIT_ENTRY(LevelMessage);
801 INIT_ENTRY(Size);
802 INIT_ENTRY(Items);
803 INIT_ENTRY(Rooms);
804 INIT_ENTRY(GenerateMonsters);
805 INIT_ENTRY(IsOnGround);
806 INIT_ENTRY(EarthquakesAffectTunnels);
807 INIT_ENTRY(TeamDefault);
808 INIT_ENTRY(Description);
809 INIT_ENTRY(LOSModifier);
810 INIT_ENTRY(IgnoreDefaultSpecialSquares);
811 INIT_ENTRY(DifficultyBase);
812 INIT_ENTRY(DifficultyDelta);
813 INIT_ENTRY(MonsterAmountBase);
814 INIT_ENTRY(MonsterAmountDelta);
815 INIT_ENTRY(MonsterGenerationIntervalBase);
816 INIT_ENTRY(MonsterGenerationIntervalDelta);
817 INIT_ENTRY(AutoReveal);
818 INIT_ENTRY(ShortDescription);
819 INIT_ENTRY(CanGenerateBone);
820 INIT_ENTRY(ItemMinPriceBase);
821 INIT_ENTRY(ItemMinPriceDelta);
822 INIT_ENTRY(Type);
823 INIT_ENTRY(EnchantmentMinusChanceBase);
824 INIT_ENTRY(EnchantmentMinusChanceDelta);
825 INIT_ENTRY(EnchantmentPlusChanceBase);
826 INIT_ENTRY(EnchantmentPlusChanceDelta);
827 INIT_ENTRY(BackGroundType);
828 INIT_ENTRY(IsCatacomb);
829 INIT_ENTRY(EnterImage);
830 INIT_ENTRY(EnterTextDisplacement);
831 INIT_ENTRY(Tag);
835 void levelscript::ReadFrom (TextInput &SaveFile) {
836 festring Word;
838 SrcFile = SaveFile.GetFileName();
839 SrcLine = SaveFile.CurrentLine();
840 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in level script line %d!", SaveFile.TokenLine());
841 if (Base) {
842 cv2 *Size = static_cast<const levelscript *>(Base)->GetSize();
844 if (Size) {
845 game::GetGlobalValueMap()["XSize"] = Size->X;
846 game::GetGlobalValueMap()["YSize"] = Size->Y;
849 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
850 if (Word == "Square") {
851 Square.push_back(squarescript());
852 Square.back().ReadFrom(SaveFile);
853 continue;
855 if (Word == "Room") {
856 Room.push_back(roomscript());
857 const roomscript *RoomDefault = GetRoomDefault();
858 if (RoomDefault) Room.back().SetBase(RoomDefault);
859 Room.back().ReadFrom(SaveFile);
860 continue;
862 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in level script line %d!", Word.CStr(), SaveFile.TokenLine());
863 if (Word == "Size") {
864 game::GetGlobalValueMap()["XSize"] = GetSize()->X;
865 game::GetGlobalValueMap()["YSize"] = GetSize()->Y;
869 const levelscript *LevelBase = static_cast<const levelscript *>(Base);
871 if (LevelBase && RoomDefaultHolder.Member) RoomDefaultHolder.Member->SetBase(LevelBase->RoomDefaultHolder.Member);
872 valuemap::iterator i = game::GetGlobalValueMap().find("XSize");
873 if (i != game::GetGlobalValueMap().end()) game::GetGlobalValueMap().erase(i);
874 i = game::GetGlobalValueMap().find("YSize");
875 if (i != game::GetGlobalValueMap().end()) game::GetGlobalValueMap().erase(i);
879 void levelscript::Combine (levelscript &Script) {
880 if (!Base) Base = Script.Base;
881 Square.splice(Square.end(), Script.Square);
882 Room.splice(Room.end(), Script.Room);
883 for (std::list<roomscript>::iterator i1 = Room.begin(); i1 != Room.end(); ++i1) i1->SetBase(GetRoomDefault());
884 for (datamap::const_iterator i2 = DataMap.begin(); i2 != DataMap.end(); ++i2) (this->*i2->second).Replace(Script.*i2->second);
888 void levelscript::SetBase (const scriptwithbase *What) {
889 const levelscript *LevelBase = static_cast<const levelscript *>(Base = What);
890 roomscript *BaseRoomDefault = LevelBase->RoomDefaultHolder.Member;
892 if (BaseRoomDefault) {
893 roomscript *ThisRoomDefault = RoomDefaultHolder.Member;
894 if (!ThisRoomDefault) {
895 for (std::list<roomscript>::iterator i = Room.begin(); i != Room.end(); ++i) i->SetBase(BaseRoomDefault);
896 } else {
897 ThisRoomDefault->SetBase(BaseRoomDefault);
903 void levelscript::Save (outputfile &SaveFile) const {
904 script::Save(SaveFile);
905 //if (Tag) SaveFile << Tag; else SaveFile << "";
906 SaveFile << Square << Room;
910 void levelscript::Load (inputfile &SaveFile) {
911 script::Load(SaveFile);
912 SaveFile >> Square >> Room;
914 const roomscript *RoomDefault = GetRoomDefault();
915 if (RoomDefault) for (std::list<roomscript>::iterator i = Room.begin(); i != Room.end(); ++i) i->SetBase(RoomDefault);
919 dungeonscript::dungeonscript () {
923 dungeonscript::~dungeonscript () {
927 const std::map<int, levelscript> &dungeonscript::GetLevel () const { return Level; }
930 void dungeonscript::InitDataMap () {
931 INIT_ENTRY(LevelDefault);
932 INIT_ENTRY(Levels);
933 INIT_ENTRY(Description);
934 INIT_ENTRY(ShortDescription);
938 void dungeonscript::ReadFrom (TextInput &SaveFile) {
939 festring Word;
941 SrcFile = SaveFile.GetFileName();
942 SrcLine = SaveFile.CurrentLine();
943 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in dungeon script line %d!", SaveFile.TokenLine());
944 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
945 if (Word == "Level") {
946 int Index = SaveFile.ReadNumber();
947 std::pair<std::map<int, levelscript>::iterator, bool> Return = Level.insert(std::make_pair(Index, levelscript()));
949 if (Return.second) {
950 levelscript &LS = Return.first->second;
951 const levelscript *LevelDefault = GetLevelDefault();
953 if (LevelDefault) LS.SetBase(LevelDefault);
954 LS.ReadFrom(SaveFile);
955 } else {
956 ABORT("Level #%d defined again in dungeon script line %d!", Index, SaveFile.TokenLine());
958 continue;
960 if (Word == "RandomLevel") {
961 interval Interval;
962 ReadData(Interval, SaveFile);
963 RandomLevel.push_back(std::make_pair(Interval, levelscript()));
964 const levelscript *LevelDefault = GetLevelDefault();
966 if (LevelDefault) RandomLevel.back().second.SetBase(LevelDefault);
967 RandomLevel.back().second.ReadFrom(SaveFile);
968 continue;
970 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in dungeon script line %d!", Word.CStr(), SaveFile.TokenLine());
975 void dungeonscript::RandomizeLevels () {
976 for (std::list<std::pair<interval, levelscript> >::iterator i = RandomLevel.begin(); i != RandomLevel.end(); ++i) {
977 int Index = i->first.Randomize();
978 Level[Index].Combine(i->second);
980 RandomLevel.clear();
984 void dungeonscript::Save (outputfile &SaveFile) const {
985 script::Save(SaveFile);
986 SaveFile << Level << RandomLevel;
990 void dungeonscript::Load (inputfile &SaveFile) {
991 script::Load(SaveFile);
992 SaveFile >> Level >> RandomLevel;
993 const levelscript *LevelDefault = GetLevelDefault();
994 if (LevelDefault) {
995 for (std::map<int, levelscript>::iterator i1 = Level.begin(); i1 != Level.end(); ++i1) i1->second.SetBase(LevelDefault);
996 for (std::list<std::pair<interval, levelscript> >::iterator i2 = RandomLevel.begin(); i2 != RandomLevel.end(); ++i2) i2->second.SetBase(LevelDefault);
1001 const std::vector<std::pair<int, int> > &teamscript::GetRelation () const { return Relation; }
1004 void teamscript::InitDataMap () {
1005 INIT_ENTRY(KillEvilness);
1006 INIT_ENTRY(Name);
1010 void teamscript::ReadFrom (TextInput &SaveFile) {
1011 festring Word;
1013 SrcFile = SaveFile.GetFileName();
1014 SrcLine = SaveFile.CurrentLine();
1015 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in team script line %d!", SaveFile.TokenLine());
1016 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
1017 if (Word == "Relation") {
1018 std::pair<int, int> Rel;
1020 Rel.first = SaveFile.ReadNumber();
1021 Rel.second = SaveFile.ReadNumber();
1022 Relation.push_back(Rel);
1023 continue;
1025 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in team script line %d!", Word.CStr(), SaveFile.TokenLine());
1030 void teamscript::Save (outputfile &SaveFile) const {
1031 script::Save(SaveFile);
1032 SaveFile << Relation;
1036 void teamscript::Load (inputfile &SaveFile) {
1037 script::Load(SaveFile);
1038 SaveFile >> Relation;
1042 const std::list<std::pair<int, teamscript> > &gamescript::GetTeam () const { return Team; }
1043 const std::map<int, dungeonscript> &gamescript::GetDungeon () const { return Dungeon; }
1046 void gamescript::InitDataMap () {
1047 //INIT_ENTRY(Dungeons);
1048 //INIT_ENTRY(Teams);
1052 void gamescript::ReadFrom (TextInput &SaveFile) {
1053 festring Word;
1055 SrcFile = SaveFile.GetFileName();
1056 SrcLine = SaveFile.CurrentLine();
1057 SaveFile.setGetVarCB(game::ldrGetVar);
1058 for (SaveFile.ReadWord(Word, false); !SaveFile.Eof(); SaveFile.ReadWord(Word, false)) {
1059 if (Word == "Dungeon") {
1060 int Index = SaveFile.ReadNumber();
1062 if (Index < 0 || Index > 16383) ABORT("Invalid dungeon number (%d) in game script file %s line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1063 if (mDungeons < Index) mDungeons = Index;
1064 //fprintf(stderr, "dungeon: %d; mDungeons: %d\n", Index, mDungeons);
1065 std::pair<dungeonlist::iterator, bool> Return = Dungeon.insert(std::make_pair(Index, dungeonscript()));
1067 if (Return.second) Return.first->second.ReadFrom(SaveFile);
1068 else ABORT("Dungeon #%d defined again in game script file %s line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1069 continue;
1071 if (Word == "Team") {
1072 int Index = SaveFile.ReadNumber();
1074 if (Index < 0 || Index > 16383) ABORT("Invalid team number (%d) in game script file %s line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1075 for (teamlist::const_iterator it = Team.begin(); it != Team.end(); ++it) {
1076 if (it->first == Index) ABORT("Team #%d redefinition in file %s at line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1078 if (mTeams < Index+1) mTeams = Index+1;
1081 fprintf(stderr, "new team #%d\n", Index);
1082 fprintf(stderr, "defined teams:\n");
1083 for (teamlist::const_iterator it = Team.begin(); it != Team.end(); ++it) fprintf(stderr, " %d\n", it->first);
1087 Team.push_back(teamlistitem(Index, teamscript()));
1088 Team.back().second.ReadFrom(SaveFile);
1089 continue;
1091 if (Word == "Include") {
1092 Word = SaveFile.ReadWord();
1093 if (SaveFile.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1094 //fprintf(stderr, "loading: %s\n", Word.CStr());
1095 TextInputFile incf(inputfile::buildIncludeName(SaveFile.GetFileName(), Word), &game::GetGlobalValueMap());
1096 ReadFrom(incf);
1097 continue;
1099 if (Word == "Message") {
1100 Word = SaveFile.ReadWord();
1101 if (SaveFile.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1102 fprintf(stderr, "MESSAGE: %s\n", Word.CStr());
1103 continue;
1105 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in game script file %s line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1110 void gamescript::RandomizeLevels () {
1111 for (std::map<int, dungeonscript>::iterator i = Dungeon.begin(); i != Dungeon.end(); ++i) i->second.RandomizeLevels();
1115 void gamescript::Save (outputfile &SaveFile) const {
1116 script::Save(SaveFile);
1117 SaveFile << Team << Dungeon;
1121 void gamescript::Load (inputfile &SaveFile) {
1122 script::Load(SaveFile);
1123 SaveFile >> Team >> Dungeon;
1127 outputfile &operator << (outputfile &SaveFile, const gamescript *Script) {
1128 Script->Save(SaveFile);
1129 return SaveFile;
1133 inputfile &operator >> (inputfile &SaveFile, gamescript *&Script) {
1134 Script = new gamescript;
1135 Script->Load(SaveFile);
1136 return SaveFile;
1140 void scriptsystem::Initialize () {
1141 posscript::InitDataMap();
1142 materialscript::InitDataMap();
1143 basecontentscript::InitDataMap();
1144 contentscript<character>::InitDataMap();
1145 contentscript<item>::InitDataMap();
1146 contentscript<glterrain>::InitDataMap();
1147 contentscript<olterrain>::InitDataMap();
1148 squarescript::InitDataMap();
1149 itemcontentmap::InitDataMap();
1150 charactercontentmap::InitDataMap();
1151 glterraincontentmap::InitDataMap();
1152 olterraincontentmap::InitDataMap();
1153 roomscript::InitDataMap();
1154 levelscript::InitDataMap();
1155 dungeonscript::InitDataMap();
1156 teamscript::InitDataMap();
1157 gamescript::InitDataMap();