entities without definitions in script will be removed from game; it is now possible...
[k8-i-v-a-n.git] / src / game / script.cpp
blobe9e590b27873a9004800447ee4b2f26be743d648
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;
303 SrcFile = SaveFile.GetFileName();
304 SrcLine = SaveFile.CurrentLine();
305 SaveFile.ReadWord(Word);
306 if (Word == "[") {
307 mCode = SaveFile.ReadCode();
308 SaveFile.ReadWord(Word);
310 if (Word == "=" || Word == ",") SaveFile.ReadWord(Word);
311 valuemap::const_iterator i = game::GetGlobalValueMap().find(Word);
312 if (i != game::GetGlobalValueMap().end()) {
313 if (!GetMainMaterial()) MainMaterialHolder.Member = new materialscript;
314 MainMaterialHolder.Member->SetConfig(i->second);
315 SaveFile.ReadWord(Word);
316 i = game::GetGlobalValueMap().find(Word);
317 if (i != game::GetGlobalValueMap().end()) {
318 if (!GetSecondaryMaterial()) SecondaryMaterialHolder.Member = new materialscript;
319 SecondaryMaterialHolder.Member->SetConfig(i->second);
320 SaveFile.ReadWord(Word);
323 if (Word == "NaturalMaterialForm") {
324 Random = false;
325 ContentType = NATURAL_MATERIAL_FORM;
326 SaveFile.ReadWord(Word);
327 } else if (Word == "Random") {
328 Random = true;
329 SaveFile.ReadWord(Word);
330 } else {
331 Random = false;
332 ContentType = SearchCodeName(Word);
333 if (ContentType || Word == "0") SaveFile.ReadWord(Word);
334 else ABORT("Odd script term %s encountered in %s content script, file %s line %d!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
336 if (Word == "(") {
337 Config = SaveFile.ReadNumber();
338 SaveFile.ReadWord(Word);
339 } else {
340 Config = 0;
342 if (Word == "{") {
343 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
344 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());
346 } else {
347 if (Word == "[") {
348 mCode = SaveFile.ReadCode();
349 SaveFile.ReadWord(Word);
351 if (Word != ";" && Word != ",") ABORT("Odd terminator %s encountered in %s content script, file %s line %d!", Word.CStr(), GetClassID(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
356 scriptmemberbase *basecontentscript::GetData (cchar *String) {
357 scriptmemberbase *Return = GetDataFromMap(GetDataMap(), String);
358 return Return ? Return : GetDataFromMap(DataMap, String);
362 void basecontentscript::Save (outputfile &SaveFile) const {
363 SaveFile << SrcFile << SrcLine;
364 SaveDataMap(GetDataMap(), SaveFile);
365 SaveDataMap(DataMap, SaveFile);
366 SaveFile << ContentType;
367 SaveFile.Put(!!Random);
368 SaveFile << Config;
372 void basecontentscript::Load (inputfile &SaveFile) {
373 SaveFile >> SrcFile >> SrcLine;
374 LoadDataMap(GetDataMap(), SaveFile);
375 LoadDataMap(DataMap, SaveFile);
376 ContentType = ReadType(uShort, SaveFile);
377 Random = SaveFile.Get();
378 SaveFile >> Config;
382 template <class type> type *contentscripttemplate<type>::BasicInstantiate (int SpecialFlags) const {
383 type *Instance = 0;
384 const typename type::prototype *Proto = protocontainer<type>::GetProto(ContentType);
385 if (!Proto) ABORT("BasicInstantiate error for type '%s'!", getTypeName<type>().c_str());
386 const typename type::database *const *ConfigData = Proto->GetConfigData();
387 const materialscript *MainMaterial = GetMainMaterial();
388 const materialscript *SecondaryMaterial = GetSecondaryMaterial();
389 const typename type::database *DataBase = *ConfigData;
390 truth UseOverriddenMaterials = false;
392 if (!Config && DataBase->IsAbstract) {
393 while (!Instance) {
394 DataBase = ConfigData[1+RAND()%(Proto->GetConfigSize()-1)];
395 if (DataBase->AllowRandomInstantiation()) {
396 if (!(SpecialFlags & NO_MATERIALS) && MainMaterial && (!DataBase->HasSecondaryMaterial || SecondaryMaterial)) {
397 SpecialFlags |= NO_MATERIALS;
398 UseOverriddenMaterials = true;
400 Instance = Proto->Spawn(DataBase->Config, SpecialFlags|NO_PIC_UPDATE);
403 } else {
404 if (!(SpecialFlags & NO_MATERIALS) && MainMaterial && (!DataBase->HasSecondaryMaterial || SecondaryMaterial)) {
405 SpecialFlags |= NO_MATERIALS;
406 UseOverriddenMaterials = true;
408 Instance = Proto->Spawn(Config, SpecialFlags|NO_PIC_UPDATE);
410 if (GetParameters() != NO_PARAMETERS) Instance->SetParameters(GetParameters());
411 if (UseOverriddenMaterials) {
412 Instance->InitMaterials(MainMaterial, SecondaryMaterial, false);
413 } else {
414 if (MainMaterial) Instance->ChangeMainMaterial(MainMaterial->Instantiate(), SpecialFlags|NO_PIC_UPDATE);
415 if (SecondaryMaterial) Instance->ChangeSecondaryMaterial(SecondaryMaterial->Instantiate(), SpecialFlags|NO_PIC_UPDATE);
417 if (!(SpecialFlags & NO_PIC_UPDATE)) Instance->UpdatePictures();
418 return Instance;
422 /* Called by an inline function in script.h... */
424 template glterrain *contentscripttemplate<glterrain>::BasicInstantiate (int) const;
426 template <class type> int contentscripttemplate<type>::SearchCodeName (cfestring &String) const {
427 return protocontainer<type>::SearchCodeName(String);
430 /* GCC 2.952 SUCKS!!! IT MUST BURN!!! */
432 template int contentscripttemplate<character>::SearchCodeName(cfestring &) const;
433 template int contentscripttemplate<item>::SearchCodeName(cfestring &) const;
434 template int contentscripttemplate<glterrain>::SearchCodeName(cfestring &) const;
435 template int contentscripttemplate<olterrain>::SearchCodeName(cfestring &) const;
437 cchar *contentscript<character>::GetClassID () const { return "character"; }
438 cchar *contentscript<item>::GetClassID () const { return "item"; }
439 cchar *contentscript<glterrain>::GetClassID () const { return "glterrain"; }
440 cchar *contentscript<olterrain>::GetClassID () const { return "olterrain"; }
443 void contentscript<character>::InitDataMap () {
444 INIT_ENTRY(Inventory);
445 INIT_ENTRY(WayPoint);
446 INIT_ENTRY(Team);
447 INIT_ENTRY(Flags);
451 contentscript<character>::contentscript () : INIT(Team, DEFAULT_TEAM), INIT(Flags, 0) {
455 character *contentscript<character>::Instantiate (int SpecialFlags) const {
456 character *Instance = contentscripttemplate<character>::BasicInstantiate(SpecialFlags);
457 //fprintf(stderr, "instantiating character '%s'\n", Instance->GetNameSingular().CStr());
458 if (!mCode.IsEmpty()) {
459 game::ClearEventData();
460 if (!game::RunAllowScriptStr(mCode)) {
461 //fprintf(stderr, "dropping character '%s'\n", Instance->GetNameSingular().CStr());
462 delete Instance;
463 return 0;
466 if (GetTeam() != DEFAULT_TEAM) Instance->SetTeam(game::GetTeam(GetTeam()));
467 const fearray<contentscript<item> > *Inventory = GetInventory();
468 if (Inventory) Instance->AddToInventory(*Inventory, SpecialFlags);
469 const fearray<packv2> *WayPoint = GetWayPoint();
470 if (WayPoint) Instance->SetWayPoints(*WayPoint);
471 Instance->RestoreHP();
472 Instance->RestoreStamina();
473 return Instance;
477 contentscript<item>::contentscript () :
478 INIT(Category, ANY_CATEGORY),
479 INIT(MinPrice, 0),
480 INIT(MaxPrice, MAX_PRICE),
481 INIT(Team, DEFAULT_TEAM),
482 INIT(SquarePosition, CENTER),
483 INIT(Chance, 100),
484 INIT(ConfigFlags, 0),
485 INIT(SpoilPercentage, 0),
486 INIT(Enchantment, 0),
487 INIT(IsActive, false)
492 void contentscript<item>::InitDataMap () {
493 INIT_ENTRY(ItemsInside);
494 INIT_ENTRY(Times);
495 INIT_ENTRY(MinPrice);
496 INIT_ENTRY(MaxPrice);
497 INIT_ENTRY(LifeExpectancy);
498 INIT_ENTRY(Team);
499 INIT_ENTRY(Category);
500 INIT_ENTRY(SquarePosition);
501 INIT_ENTRY(Chance);
502 INIT_ENTRY(ConfigFlags);
503 INIT_ENTRY(SpoilPercentage);
504 INIT_ENTRY(Enchantment);
505 INIT_ENTRY(IsActive);
509 item *contentscript<item>::InstantiateBasedOnMaterial (int MaterialConfig, int SpecialFlags) const {
510 if (ContentType == NATURAL_MATERIAL_FORM) {
511 const materialscript *MainMaterial = GetMainMaterial();
512 sLong Volume = MainMaterial && MainMaterial->GetVolume() ? MainMaterial->GetVolume()->Randomize() : 0;
513 return material::CreateNaturalForm(MaterialConfig, Volume);
515 return Instantiate(SpecialFlags);
519 item *contentscript<item>::Instantiate (int SpecialFlags) const {
520 int Chance = GetChance();
521 item *Instance;
523 if (!mCode.IsEmpty()) {
524 game::ClearEventData();
525 if (!game::RunAllowScriptStr(mCode)) return 0;
528 if (Chance != 100 && Chance <= RAND_N(100)) return 0;
529 if (Random) {
530 Instance = protosystem::BalancedCreateItem(0, GetMinPrice(), GetMaxPrice(), GetCategory(), SpecialFlags, GetConfigFlags());
531 } else {
532 Instance = contentscripttemplate<item>::BasicInstantiate(SpecialFlags);
534 if (GetLifeExpectancy()) Instance->SetLifeExpectancy(GetLifeExpectancy()->Min, (GetLifeExpectancy()->Max - GetLifeExpectancy()->Min) + 1);
535 if (GetTeam() != DEFAULT_TEAM) Instance->SetTeam(GetTeam());
536 if (IsActive()) Instance->SetIsActive(true);
537 if (GetEnchantment() != 0) Instance->SetEnchantment(GetEnchantment());
538 const fearray<contentscript<item> > *ItemsInside = GetItemsInside();
539 if (ItemsInside) Instance->SetItemsInside(*ItemsInside, SpecialFlags);
540 if (GetSpoilPercentage() != 0) Instance->SetSpoilPercentage(GetSpoilPercentage());
541 return Instance;
545 truth IsValidScript (const fearray<contentscript<item> > *Array) {
546 for (uInt c = 0; c < Array->Size; ++c) if (IsValidScript(&Array->Data[c])) return true;
547 return false;
551 void contentscript<glterrain>::InitDataMap () {
552 INIT_ENTRY(IsInside);
556 contentscript<olterrain>::contentscript () :
557 INIT(VisualEffects, 0),
558 INIT(AttachedArea, DEFAULT_ATTACHED_AREA),
559 INIT(AttachedEntry, DEFAULT_ATTACHED_ENTRY)
564 void contentscript<olterrain>::InitDataMap () {
565 INIT_ENTRY(ItemsInside);
566 INIT_ENTRY(Text);
567 INIT_ENTRY(VisualEffects);
568 INIT_ENTRY(AttachedArea);
569 INIT_ENTRY(AttachedEntry);
573 olterrain *contentscript<olterrain>::Instantiate (int SpecialFlags) const {
574 if (!ContentType) return 0;
575 olterrain *Instance = contentscripttemplate<olterrain>::BasicInstantiate(SpecialFlags);
576 if (GetVisualEffects()) {
577 Instance->SetVisualEffects(GetVisualEffects());
578 Instance->UpdatePictures();
580 if (GetAttachedArea() != DEFAULT_ATTACHED_AREA) Instance->SetAttachedArea(GetAttachedArea());
581 if (GetAttachedEntry() != DEFAULT_ATTACHED_ENTRY) Instance->SetAttachedEntry(GetAttachedEntry());
582 cfestring *Text = GetText();
583 if (Text) Instance->SetText(*Text);
584 const fearray<contentscript<item> > *ItemsInside = GetItemsInside();
585 if (ItemsInside) Instance->SetItemsInside(*ItemsInside, SpecialFlags);
586 return Instance;
590 squarescript::squarescript () : INIT(EntryIndex, NO_ENTRY), INIT(AttachRequired, false) {
594 void squarescript::InitDataMap () {
595 INIT_ENTRY(Position);
596 INIT_ENTRY(Character);
597 INIT_ENTRY(Items);
598 INIT_ENTRY(GTerrain);
599 INIT_ENTRY(OTerrain);
600 INIT_ENTRY(Times);
601 INIT_ENTRY(EntryIndex);
602 INIT_ENTRY(AttachRequired);
606 void squarescript::ReadFrom (TextInput &SaveFile) {
607 static festring Word;
609 SrcFile = SaveFile.GetFileName();
610 SrcLine = SaveFile.CurrentLine();
611 SaveFile.ReadWord(Word);
612 if (Word != "=") {
613 PositionHolder.ReadFrom(SaveFile);
614 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in square script line %d!", SaveFile.TokenLine());
615 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
616 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in square script line %d!", Word.CStr(), SaveFile.TokenLine());
618 } else {
619 GTerrainHolder.ReadFrom(SaveFile);
620 OTerrainHolder.ReadFrom(SaveFile);
625 template <class type, class contenttype> contentmap<type, contenttype>::contentmap() : ContentMap(0) {
629 template <class type, class contenttype> contentmap<type, contenttype>::~contentmap<type, contenttype> () {
630 delete [] ContentMap;
634 template <class type, class contenttype> void contentmap<type, contenttype>::InitDataMap () {
635 INIT_ENTRY(Size);
636 INIT_ENTRY(Pos);
640 template <class type, class contenttype> void contentmap<type, contenttype>::ReadFrom (TextInput &SaveFile) {
641 typedef std::map<int, contenttype> maptype;
642 typedef typename maptype::iterator mapiterator;
643 festring Word1, Word2;
645 SrcFile = SaveFile.GetFileName();
646 SrcLine = SaveFile.CurrentLine();
647 if (ContentMap) ABORT("Illegal %s content map redefinition on line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
648 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in %s content map script line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
649 SymbolMap.insert(std::pair<int, contenttype>('.', contenttype()));
650 for (SaveFile.ReadWord(Word1); Word1 != "}"; Word1 = SaveFile.ReadWord()) {
651 if (Word1 == "Types") {
652 if (SaveFile.ReadWord() != "{") ABORT("Missing bracket in %s content map script line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
653 for (SaveFile.ReadWord(Word2); Word2 != "}"; Word2 = SaveFile.ReadWord()) {
654 std::pair<mapiterator, bool> Return = SymbolMap.insert(std::pair<int, contenttype>(Word2[0], contenttype()));
656 if (Return.second) {
657 ReadData(Return.first->second, SaveFile);
658 } else {
659 ABORT("Symbol %c defined again in %s content map script line %d!", Word2[0], protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
662 continue;
664 if (!ReadMember(SaveFile, Word1)) ABORT("Odd script term %s encountered in %s content script line %d!", Word1.CStr(), protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
667 v2 Size = *GetSize();
669 Alloc2D(ContentMap, Size.X, Size.Y);
670 if (SaveFile.ReadWord() != "{") ABORT("Missing bracket in %s content map script line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
671 for (int y = 0; y < Size.Y; ++y) {
672 for (int x = 0; x < Size.X; ++x) {
673 int Char = SaveFile.ReadLetter();
674 typename std::map<int, contenttype>::iterator i = SymbolMap.find(Char);
676 if (i != SymbolMap.end()) {
677 ContentMap[x][y] = std::make_pair(Char, &i->second);
678 } else {
679 ABORT("Illegal content %c in %s content map line %d!", Char, protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
683 if (SaveFile.ReadWord() != "}") ABORT("Missing bracket in %s content map script line %d!", protocontainer<type>::GetMainClassID(), SaveFile.TokenLine());
687 template <class type, class contenttype> void contentmap<type, contenttype>::Save (outputfile &SaveFile) const {
688 script::Save(SaveFile);
689 SaveFile << SymbolMap;
691 v2 Size = *GetSize();
692 for (int y = 0; y < Size.Y; ++y)
693 for (int x = 0; x < Size.X; ++x)
694 SaveFile << char(ContentMap[x][y].first);
698 template <class type, class contenttype> void contentmap<type, contenttype>::Load (inputfile &SaveFile) {
699 script::Load(SaveFile);
700 SaveFile >> SymbolMap;
702 v2 Size = *GetSize();
704 Alloc2D(ContentMap, Size.X, Size.Y);
705 for (int y = 0; y < Size.Y; ++y) {
706 for (int x = 0; x < Size.X; ++x) {
707 int Char = ReadType(char, SaveFile);
709 ContentMap[x][y] = std::make_pair(Char, &SymbolMap.find(Char)->second);
715 const std::list<squarescript> &roomscript::GetSquare () const { return Square; }
718 void roomscript::InitDataMap () {
719 INIT_ENTRY(CharacterMap);
720 INIT_ENTRY(ItemMap);
721 INIT_ENTRY(GTerrainMap);
722 INIT_ENTRY(OTerrainMap);
723 INIT_ENTRY(WallSquare);
724 INIT_ENTRY(FloorSquare);
725 INIT_ENTRY(DoorSquare);
726 INIT_ENTRY(Size);
727 INIT_ENTRY(Pos);
728 INIT_ENTRY(AltarPossible);
729 INIT_ENTRY(GenerateDoor);
730 INIT_ENTRY(GenerateTunnel);
731 INIT_ENTRY(DivineMaster);
732 INIT_ENTRY(GenerateLanterns);
733 INIT_ENTRY(Type);
734 INIT_ENTRY(GenerateFountains);
735 INIT_ENTRY(AllowLockedDoors);
736 INIT_ENTRY(AllowBoobyTrappedDoors);
737 INIT_ENTRY(Shape);
738 INIT_ENTRY(IsInside);
739 INIT_ENTRY(GenerateWindows);
740 INIT_ENTRY(UseFillSquareWalls);
741 INIT_ENTRY(Flags);
742 INIT_ENTRY(GenerateWards);
743 INIT_ENTRY(AllowedDivineMasters);
747 void roomscript::ReadFrom (TextInput &SaveFile) {
748 festring Word;
750 SrcFile = SaveFile.GetFileName();
751 SrcLine = SaveFile.CurrentLine();
752 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in room script line %d!", SaveFile.TokenLine());
753 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
754 if (Word == "Square") {
755 Square.push_back(squarescript());
756 Square.back().ReadFrom(SaveFile);
757 continue;
759 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in room script line %d!", Word.CStr(), SaveFile.TokenLine());
764 void roomscript::Save (outputfile &SaveFile) const {
765 script::Save(SaveFile);
766 SaveFile << Square;
770 void roomscript::Load (inputfile &SaveFile) {
771 script::Load(SaveFile);
772 SaveFile >> Square;
776 const std::list<squarescript> &levelscript::GetSquare () const { return Square; }
777 const std::list<roomscript> &levelscript::GetRoom () const { return Room; }
780 void levelscript::InitDataMap () {
781 INIT_ENTRY(RoomDefault);
782 INIT_ENTRY(FillSquare);
783 INIT_ENTRY(TunnelSquare);
784 INIT_ENTRY(LevelMessage);
785 INIT_ENTRY(Size);
786 INIT_ENTRY(Items);
787 INIT_ENTRY(Rooms);
788 INIT_ENTRY(GenerateMonsters);
789 INIT_ENTRY(IsOnGround);
790 INIT_ENTRY(EarthquakesAffectTunnels);
791 INIT_ENTRY(TeamDefault);
792 INIT_ENTRY(Description);
793 INIT_ENTRY(LOSModifier);
794 INIT_ENTRY(IgnoreDefaultSpecialSquares);
795 INIT_ENTRY(DifficultyBase);
796 INIT_ENTRY(DifficultyDelta);
797 INIT_ENTRY(MonsterAmountBase);
798 INIT_ENTRY(MonsterAmountDelta);
799 INIT_ENTRY(MonsterGenerationIntervalBase);
800 INIT_ENTRY(MonsterGenerationIntervalDelta);
801 INIT_ENTRY(AutoReveal);
802 INIT_ENTRY(ShortDescription);
803 INIT_ENTRY(CanGenerateBone);
804 INIT_ENTRY(ItemMinPriceBase);
805 INIT_ENTRY(ItemMinPriceDelta);
806 INIT_ENTRY(Type);
807 INIT_ENTRY(EnchantmentMinusChanceBase);
808 INIT_ENTRY(EnchantmentMinusChanceDelta);
809 INIT_ENTRY(EnchantmentPlusChanceBase);
810 INIT_ENTRY(EnchantmentPlusChanceDelta);
811 INIT_ENTRY(BackGroundType);
812 INIT_ENTRY(IsCatacomb);
813 INIT_ENTRY(EnterImage);
814 INIT_ENTRY(EnterTextDisplacement);
815 INIT_ENTRY(Tag);
819 void levelscript::ReadFrom (TextInput &SaveFile) {
820 festring Word;
822 SrcFile = SaveFile.GetFileName();
823 SrcLine = SaveFile.CurrentLine();
824 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in level script line %d!", SaveFile.TokenLine());
825 if (Base) {
826 cv2 *Size = static_cast<const levelscript *>(Base)->GetSize();
828 if (Size) {
829 game::GetGlobalValueMap()["XSize"] = Size->X;
830 game::GetGlobalValueMap()["YSize"] = Size->Y;
833 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
834 if (Word == "Square") {
835 Square.push_back(squarescript());
836 Square.back().ReadFrom(SaveFile);
837 continue;
839 if (Word == "Room") {
840 Room.push_back(roomscript());
841 const roomscript *RoomDefault = GetRoomDefault();
842 if (RoomDefault) Room.back().SetBase(RoomDefault);
843 Room.back().ReadFrom(SaveFile);
844 continue;
846 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in level script line %d!", Word.CStr(), SaveFile.TokenLine());
847 if (Word == "Size") {
848 game::GetGlobalValueMap()["XSize"] = GetSize()->X;
849 game::GetGlobalValueMap()["YSize"] = GetSize()->Y;
853 const levelscript *LevelBase = static_cast<const levelscript *>(Base);
855 if (LevelBase && RoomDefaultHolder.Member) RoomDefaultHolder.Member->SetBase(LevelBase->RoomDefaultHolder.Member);
856 valuemap::iterator i = game::GetGlobalValueMap().find("XSize");
857 if (i != game::GetGlobalValueMap().end()) game::GetGlobalValueMap().erase(i);
858 i = game::GetGlobalValueMap().find("YSize");
859 if (i != game::GetGlobalValueMap().end()) game::GetGlobalValueMap().erase(i);
863 void levelscript::Combine (levelscript &Script) {
864 if (!Base) Base = Script.Base;
865 Square.splice(Square.end(), Script.Square);
866 Room.splice(Room.end(), Script.Room);
867 for (std::list<roomscript>::iterator i1 = Room.begin(); i1 != Room.end(); ++i1) i1->SetBase(GetRoomDefault());
868 for (datamap::const_iterator i2 = DataMap.begin(); i2 != DataMap.end(); ++i2) (this->*i2->second).Replace(Script.*i2->second);
872 void levelscript::SetBase (const scriptwithbase *What) {
873 const levelscript *LevelBase = static_cast<const levelscript *>(Base = What);
874 roomscript *BaseRoomDefault = LevelBase->RoomDefaultHolder.Member;
876 if (BaseRoomDefault) {
877 roomscript *ThisRoomDefault = RoomDefaultHolder.Member;
878 if (!ThisRoomDefault) {
879 for (std::list<roomscript>::iterator i = Room.begin(); i != Room.end(); ++i) i->SetBase(BaseRoomDefault);
880 } else {
881 ThisRoomDefault->SetBase(BaseRoomDefault);
887 void levelscript::Save (outputfile &SaveFile) const {
888 script::Save(SaveFile);
889 //if (Tag) SaveFile << Tag; else SaveFile << "";
890 SaveFile << Square << Room;
894 void levelscript::Load (inputfile &SaveFile) {
895 script::Load(SaveFile);
896 SaveFile >> Square >> Room;
898 const roomscript *RoomDefault = GetRoomDefault();
899 if (RoomDefault) for (std::list<roomscript>::iterator i = Room.begin(); i != Room.end(); ++i) i->SetBase(RoomDefault);
903 dungeonscript::dungeonscript () {
907 dungeonscript::~dungeonscript () {
911 const std::map<int, levelscript> &dungeonscript::GetLevel () const { return Level; }
914 void dungeonscript::InitDataMap () {
915 INIT_ENTRY(LevelDefault);
916 INIT_ENTRY(Levels);
917 INIT_ENTRY(Description);
918 INIT_ENTRY(ShortDescription);
922 void dungeonscript::ReadFrom (TextInput &SaveFile) {
923 festring Word;
925 SrcFile = SaveFile.GetFileName();
926 SrcLine = SaveFile.CurrentLine();
927 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in dungeon script line %d!", SaveFile.TokenLine());
928 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
929 if (Word == "Level") {
930 int Index = SaveFile.ReadNumber();
931 std::pair<std::map<int, levelscript>::iterator, bool> Return = Level.insert(std::make_pair(Index, levelscript()));
933 if (Return.second) {
934 levelscript &LS = Return.first->second;
935 const levelscript *LevelDefault = GetLevelDefault();
937 if (LevelDefault) LS.SetBase(LevelDefault);
938 LS.ReadFrom(SaveFile);
939 } else {
940 ABORT("Level #%d defined again in dungeon script line %d!", Index, SaveFile.TokenLine());
942 continue;
944 if (Word == "RandomLevel") {
945 interval Interval;
946 ReadData(Interval, SaveFile);
947 RandomLevel.push_back(std::make_pair(Interval, levelscript()));
948 const levelscript *LevelDefault = GetLevelDefault();
950 if (LevelDefault) RandomLevel.back().second.SetBase(LevelDefault);
951 RandomLevel.back().second.ReadFrom(SaveFile);
952 continue;
954 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in dungeon script line %d!", Word.CStr(), SaveFile.TokenLine());
959 void dungeonscript::RandomizeLevels () {
960 for (std::list<std::pair<interval, levelscript> >::iterator i = RandomLevel.begin(); i != RandomLevel.end(); ++i) {
961 int Index = i->first.Randomize();
962 Level[Index].Combine(i->second);
964 RandomLevel.clear();
968 void dungeonscript::Save (outputfile &SaveFile) const {
969 script::Save(SaveFile);
970 SaveFile << Level << RandomLevel;
974 void dungeonscript::Load (inputfile &SaveFile) {
975 script::Load(SaveFile);
976 SaveFile >> Level >> RandomLevel;
977 const levelscript *LevelDefault = GetLevelDefault();
978 if (LevelDefault) {
979 for (std::map<int, levelscript>::iterator i1 = Level.begin(); i1 != Level.end(); ++i1) i1->second.SetBase(LevelDefault);
980 for (std::list<std::pair<interval, levelscript> >::iterator i2 = RandomLevel.begin(); i2 != RandomLevel.end(); ++i2) i2->second.SetBase(LevelDefault);
985 const std::vector<std::pair<int, int> > &teamscript::GetRelation () const { return Relation; }
988 void teamscript::InitDataMap () {
989 INIT_ENTRY(KillEvilness);
990 INIT_ENTRY(Name);
994 void teamscript::ReadFrom (TextInput &SaveFile) {
995 festring Word;
997 SrcFile = SaveFile.GetFileName();
998 SrcLine = SaveFile.CurrentLine();
999 if (SaveFile.ReadWord() != "{") ABORT("Bracket missing in team script line %d!", SaveFile.TokenLine());
1000 for (SaveFile.ReadWord(Word); Word != "}"; SaveFile.ReadWord(Word)) {
1001 if (Word == "Relation") {
1002 std::pair<int, int> Rel;
1004 Rel.first = SaveFile.ReadNumber();
1005 Rel.second = SaveFile.ReadNumber();
1006 Relation.push_back(Rel);
1007 continue;
1009 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in team script line %d!", Word.CStr(), SaveFile.TokenLine());
1014 void teamscript::Save (outputfile &SaveFile) const {
1015 script::Save(SaveFile);
1016 SaveFile << Relation;
1020 void teamscript::Load (inputfile &SaveFile) {
1021 script::Load(SaveFile);
1022 SaveFile >> Relation;
1026 const std::list<std::pair<int, teamscript> > &gamescript::GetTeam () const { return Team; }
1027 const std::map<int, dungeonscript> &gamescript::GetDungeon () const { return Dungeon; }
1030 void gamescript::InitDataMap () {
1031 //INIT_ENTRY(Dungeons);
1032 //INIT_ENTRY(Teams);
1036 void gamescript::ReadFrom (TextInput &SaveFile) {
1037 festring Word;
1039 SrcFile = SaveFile.GetFileName();
1040 SrcLine = SaveFile.CurrentLine();
1041 SaveFile.setGetVarCB(game::ldrGetVar);
1042 for (SaveFile.ReadWord(Word, false); !SaveFile.Eof(); SaveFile.ReadWord(Word, false)) {
1043 if (Word == "Dungeon") {
1044 int Index = SaveFile.ReadNumber();
1046 if (Index < 0 || Index > 16383) ABORT("Invalid dungeon number (%d) in game script file %s line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1047 if (mDungeons < Index) mDungeons = Index;
1048 //fprintf(stderr, "dungeon: %d; mDungeons: %d\n", Index, mDungeons);
1049 std::pair<dungeonlist::iterator, bool> Return = Dungeon.insert(std::make_pair(Index, dungeonscript()));
1051 if (Return.second) Return.first->second.ReadFrom(SaveFile);
1052 else ABORT("Dungeon #%d defined again in game script file %s line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1053 continue;
1055 if (Word == "Team") {
1056 int Index = SaveFile.ReadNumber();
1058 if (Index < 0 || Index > 16383) ABORT("Invalid team number (%d) in game script file %s line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1059 for (teamlist::const_iterator it = Team.begin(); it != Team.end(); ++it) {
1060 if (it->first == Index) ABORT("Team #%d redefinition in file %s at line %d!", Index, SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1062 if (mTeams < Index+1) mTeams = Index+1;
1065 fprintf(stderr, "new team #%d\n", Index);
1066 fprintf(stderr, "defined teams:\n");
1067 for (teamlist::const_iterator it = Team.begin(); it != Team.end(); ++it) fprintf(stderr, " %d\n", it->first);
1071 Team.push_back(teamlistitem(Index, teamscript()));
1072 Team.back().second.ReadFrom(SaveFile);
1073 continue;
1075 if (Word == "Include") {
1076 Word = SaveFile.ReadWord();
1077 if (SaveFile.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1078 //fprintf(stderr, "loading: %s\n", Word.CStr());
1079 TextInputFile incf(inputfile::buildIncludeName(SaveFile.GetFileName(), Word), &game::GetGlobalValueMap());
1080 ReadFrom(incf);
1081 continue;
1083 if (Word == "Message") {
1084 Word = SaveFile.ReadWord();
1085 if (SaveFile.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1086 fprintf(stderr, "MESSAGE: %s\n", Word.CStr());
1087 continue;
1089 if (!ReadMember(SaveFile, Word)) ABORT("Odd script term %s encountered in game script file %s line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1094 void gamescript::RandomizeLevels () {
1095 for (std::map<int, dungeonscript>::iterator i = Dungeon.begin(); i != Dungeon.end(); ++i) i->second.RandomizeLevels();
1099 void gamescript::Save (outputfile &SaveFile) const {
1100 script::Save(SaveFile);
1101 SaveFile << Team << Dungeon;
1105 void gamescript::Load (inputfile &SaveFile) {
1106 script::Load(SaveFile);
1107 SaveFile >> Team >> Dungeon;
1111 outputfile &operator << (outputfile &SaveFile, const gamescript *Script) {
1112 Script->Save(SaveFile);
1113 return SaveFile;
1117 inputfile &operator >> (inputfile &SaveFile, gamescript *&Script) {
1118 Script = new gamescript;
1119 Script->Load(SaveFile);
1120 return SaveFile;
1124 void scriptsystem::Initialize () {
1125 posscript::InitDataMap();
1126 materialscript::InitDataMap();
1127 basecontentscript::InitDataMap();
1128 contentscript<character>::InitDataMap();
1129 contentscript<item>::InitDataMap();
1130 contentscript<glterrain>::InitDataMap();
1131 contentscript<olterrain>::InitDataMap();
1132 squarescript::InitDataMap();
1133 itemcontentmap::InitDataMap();
1134 charactercontentmap::InitDataMap();
1135 glterraincontentmap::InitDataMap();
1136 olterraincontentmap::InitDataMap();
1137 roomscript::InitDataMap();
1138 levelscript::InitDataMap();
1139 dungeonscript::InitDataMap();
1140 teamscript::InitDataMap();
1141 gamescript::InitDataMap();