1 /*
2  *
3  *  Iter Vehemens ad Necem (IVAN)
4  *  Copyright (C) Timo Kiviluoto
5  *  Released under the GNU General
6  *  Public License
7  *
8  *  See LICENSING which should be included
9  *  along with this file for more details
10  *
11  */
12 
13 /* Compiled through dataset.cpp */
14 
15 #include "confdef.h"
16 #include "miscitem.h"
17 #include "stack.h"
18 
19 #include "dbgmsgproj.h"
20 
21 itemdatabase** protosystem::ItemConfigData;
22 int protosystem::ItemConfigDataSize;
23 itemdatabase** protosystem::ItemCategoryData[ITEM_CATEGORIES];
24 int protosystem::ItemCategorySize[ITEM_CATEGORIES];
25 long protosystem::ItemCategoryPossibility[ITEM_CATEGORIES];
26 long protosystem::TotalItemPossibility;
27 
BalancedCreateMonster()28 character* protosystem::BalancedCreateMonster()
29 {
30   for(int c = 0;; ++c)
31   {
32     double MinDifficulty = game::GetMinDifficulty(), MaxDifficulty = MinDifficulty * 25;
33     std::vector<configid> Possible;
34 
35     for(int Type = 1; Type < protocontainer<character>::GetSize(); ++Type)
36     {
37       const character::prototype* Proto = protocontainer<character>::GetProto(Type);
38       const character::database*const* ConfigData = Proto->GetConfigData();
39       int ConfigSize = Proto->GetConfigSize();
40 
41       for(int c = 0; c < ConfigSize; ++c)
42       {
43         const character::database* DataBase = ConfigData[c];
44 
45         if(!DataBase->IsAbstract && DataBase->CanBeGenerated)
46         {
47           truth Allowed = false;
48           const fearray<int> &DungeonList = DataBase->AllowedDungeons;
49           for (uint f = 0; f < DungeonList.Size; ++f)
50           {
51             if ((DungeonList[f] == ALL_DUNGEONS) || (DungeonList[f] == game::GetCurrentDungeonIndex()))
52             {
53               Allowed = true;
54               break;
55             }
56           }
57           if (!Allowed)
58             continue;
59 
60           if(DataBase->IsUnique
61              && DataBase->Flags & HAS_BEEN_GENERATED)
62             continue;
63 
64           truth IsCatacomb = *game::GetCurrentLevel()->GetLevelScript()->IsCatacomb();
65 
66           if((IsCatacomb && !DataBase->IsCatacombCreature)
67              || (!IsCatacomb && DataBase->CanBeGeneratedOnlyInTheCatacombs))
68             continue;
69 
70           configid ConfigID(Type, ConfigData[c]->Config);
71 
72           if(c >= 100)
73           {
74             Possible.push_back(ConfigID);
75             continue;
76           }
77 
78           const dangerid& DangerID = game::GetDangerMap().find(ConfigID)->second;
79 
80           if(!DataBase->IgnoreDanger)
81           {
82             double Danger = DangerID.EquippedDanger;
83 
84             if(Danger > 99.0
85                || Danger < 0.0011
86                || (DataBase->IsUnique && Danger < 3.5))
87               continue;
88 
89             double DangerModifier
90               = DataBase->DangerModifier == 100
91               ? Danger
92               : Danger * 100 / DataBase->DangerModifier;
93 
94             if(DangerModifier < MinDifficulty
95                || DangerModifier > MaxDifficulty)
96               continue;
97           }
98 
99           ivantime Time;
100           game::GetTime(Time);
101 
102           if(PLAYER->GetMaxHP() < DataBase->HPRequirementForGeneration
103              && Time.Day < DataBase->DayRequirementForGeneration)
104             continue;
105 
106           Possible.push_back(ConfigID);
107         }
108       }
109     }
110 
111     if(Possible.empty())
112       continue;
113 
114     for(int i = 0; i < 25; ++i)
115     {
116       configid Chosen = Possible[RAND_GOOD(Possible.size())];
117       const character::prototype* Proto = protocontainer<character>::GetProto(Chosen.Type);
118       character* Monster = Proto->Spawn(Chosen.Config);
119 
120       if(c >= 100
121          || ((Monster->GetFrequency() == 10000
122               || Monster->GetFrequency() > RAND_GOOD(10000))
123              && (Monster->IsUnique()
124                  || (Monster->GetTimeToKill(PLAYER, true) > 5000
125                      && PLAYER->GetTimeToKill(Monster, true) < 200000))))
126       {
127         Monster->SetTeam(game::GetTeam(MONSTER_TEAM));
128         return Monster;
129       }
130       else
131         delete Monster;
132     }
133   }
134 
135   ABORT("BalancedCreateMonster failed!");
136   /* This line is never reached, but it prevents warnings given by some (stupid) compilers. */
137   return NULL;
138 }
139 
BalancedCreateItem(long MinPrice,long MaxPrice,long RequiredCategory,int SpecialFlags,int ConfigFlags,int RequiredGod,truth Polymorph)140 item* protosystem::BalancedCreateItem(long MinPrice, long MaxPrice, long RequiredCategory,
141                                       int SpecialFlags, int ConfigFlags, int RequiredGod, truth Polymorph)
142 {
143   typedef item::database database;
144   database** PossibleCategory[ITEM_CATEGORIES];
145   int PossibleCategorySize[ITEM_CATEGORIES];
146   long PartialCategoryPossibilitySum[ITEM_CATEGORIES];
147   int PossibleCategories = 0;
148   long TotalPossibility = 0;
149   long database::*PartialPossibilitySumPtr;
150 
151   if(RequiredCategory == ANY_CATEGORY)
152   {
153     PartialPossibilitySumPtr = &database::PartialPossibilitySum;
154     PossibleCategory[0] = ItemConfigData;
155     PossibleCategorySize[0] = ItemConfigDataSize;
156     TotalPossibility = TotalItemPossibility;
157     PartialCategoryPossibilitySum[0] = TotalPossibility;
158     PossibleCategories = 1;
159   }
160   else
161   {
162     PartialPossibilitySumPtr = &database::PartialCategoryPossibilitySum;
163 
164     for(long CategoryIndex = 0, Category = 1; CategoryIndex < ITEM_CATEGORIES; ++CategoryIndex, Category <<= 1)
165       if(Category & RequiredCategory)
166       {
167         PossibleCategory[PossibleCategories] = ItemCategoryData[CategoryIndex];
168         PossibleCategorySize[PossibleCategories] = ItemCategorySize[CategoryIndex];
169         TotalPossibility += ItemCategoryPossibility[CategoryIndex];
170         PartialCategoryPossibilitySum[PossibleCategories] = TotalPossibility;
171         ++PossibleCategories;
172       }
173   }
174 
175   for(int c0 = 0;; ++c0)
176   {
177     for(int c1 = 0; c1 < BALANCED_CREATE_ITEM_ITERATIONS; ++c1)
178     {
179       long Rand = RAND_GOOD(TotalPossibility);
180       int Category;
181 
182       if(RequiredCategory == ANY_CATEGORY)
183         Category = 0;
184       else
185       {
186         for(int c2 = 0;; ++c2)
187           if(PartialCategoryPossibilitySum[c2] > Rand)
188           {
189             Category = c2;
190             break;
191           }
192 
193         if(Category)
194           Rand -= PartialCategoryPossibilitySum[Category - 1];
195       }
196 
197       const database*const* ChosenCategory = PossibleCategory[Category];
198       const database* ChosenDataBase;
199 
200       if(ChosenCategory[0]->PartialCategoryPossibilitySum > Rand)
201         ChosenDataBase = ChosenCategory[0];
202       else
203       {
204         long A = 0;
205         long B = PossibleCategorySize[Category] - 1;
206 
207         for(;;)
208         {
209           long C = (A + B) >> 1;
210 
211           if(A != C)
212           {
213             if(ChosenCategory[C]->*PartialPossibilitySumPtr > Rand)
214               B = C;
215             else
216               A = C;
217           }
218           else
219           {
220             ChosenDataBase = ChosenCategory[B];
221             break;
222           }
223         }
224       }
225 
226       int Config = ChosenDataBase->Config;
227 
228       if((!(ConfigFlags & NO_BROKEN)
229           || !(Config & BROKEN))
230          && (!Polymorph
231              || ChosenDataBase->IsPolymorphSpawnable))
232       {
233         // Check allowed dungeons
234           truth Allowed = false;
235           const fearray<int> &DungeonList = ChosenDataBase->AllowedDungeons;
236           for (uint f = 0; f < DungeonList.Size; ++f)
237           {
238             if (DungeonList[f] == ALL_DUNGEONS || DungeonList[f] == game::GetCurrentDungeonIndex())
239             {
240               Allowed = true;
241               break;
242             }
243           }
244           if (!Allowed)
245             continue;
246 
247         item* Item = ChosenDataBase->ProtoType->Spawn(Config, SpecialFlags);
248         truth GodOK = !RequiredGod || Item->GetAttachedGod() == RequiredGod;
249 
250         /* Optimization, GetTruePrice() may be rather slow */
251 
252         if(((MinPrice == 0 && MaxPrice == MAX_PRICE)
253             || (Config & BROKEN && ConfigFlags & IGNORE_BROKEN_PRICE)) && GodOK)
254           return Item;
255 
256         long Price = Item->GetTruePrice();
257 
258         if(Item->HandleInPairs())
259           Price <<= 1;
260 
261         if(Price >= MinPrice && Price <= MaxPrice && GodOK)
262           return Item;
263 
264         delete Item;
265       }
266     }
267 
268     if(c0 == 25 && RequiredGod)
269       return 0;
270 
271     MinPrice = MinPrice * 7 >> 3;
272     MaxPrice = MaxPrice * 9 >> 3;
273   }
274 }
275 
CreateMonster(int MinDanger,int MaxDanger,int SpecialFlags)276 character* protosystem::CreateMonster(int MinDanger, int MaxDanger, int SpecialFlags)
277 {
278   std::vector<configid> Possible;
279   character* Monster = NULL;
280 
281   for(int i = 0; !Monster; ++i)
282   {
283     if(i == -1)
284       break;  // this means the algorithm is bad
285 
286     for(int Type = 1; Type < protocontainer<character>::GetSize(); ++Type)
287     {
288       const character::prototype* Proto = protocontainer<character>::GetProto(Type);
289       const character::database*const* ConfigData = Proto->GetConfigData();
290       int ConfigSize = Proto->GetConfigSize();
291 
292       for(int c = 0; c < ConfigSize; ++c)
293       {
294         const character::database* DataBase = ConfigData[c];
295 
296         if(!DataBase->IsAbstract
297            && DataBase->CanBeGenerated
298            && DataBase->CanBeWished
299            && !DataBase->IsUnique
300            && (DataBase->Frequency == 10000
301                || DataBase->Frequency > RAND_GOOD(10000)))
302         {
303           configid ConfigID(Type, DataBase->Config);
304 
305           if((MinDanger > 0 || MaxDanger < 1000000) && c < 25)
306           {
307             const dangerid& DangerID = game::GetDangerMap().find(ConfigID)->second;
308             double RawDanger = SpecialFlags & NO_EQUIPMENT ? DangerID.NakedDanger : DangerID.EquippedDanger;
309             int Danger = int(DataBase->DangerModifier == 100 ?
310                              RawDanger * 1000 : RawDanger * 100000 / DataBase->DangerModifier);
311 
312             if(Danger < MinDanger || Danger > MaxDanger)
313               continue;
314           }
315 
316           Possible.push_back(ConfigID);
317         }
318       }
319     }
320 
321     if(Possible.empty())
322     {
323       MinDanger = MinDanger > 0 ? Max(MinDanger * 3 >> 2, 1) : 0;
324       MaxDanger = MaxDanger < 1000000 ? Min(MaxDanger * 5 >> 2, 999999) : 1000000;
325       continue;
326     }
327 
328     configid Chosen = Possible[RAND_GOOD(Possible.size())];
329     Monster = protocontainer<character>::GetProto(Chosen.Type)->Spawn(Chosen.Config, SpecialFlags);
330     Monster->SignalGeneration();
331     Monster->SetTeam(game::GetTeam(MONSTER_TEAM));
332   }
333 
334   if(Monster == NULL)
335     ABORT("Failed to create monster!");
336   return Monster;
337 }
338 
339 template <class type>
ScoreFlexibleName(const typename type::database * DataBase,cfestring & Identifier,festring & Name,festring::sizetype & NamePos,int & NameScore)340 static void ScoreFlexibleName(
341     const typename type::database* DataBase,
342     cfestring& Identifier,
343     festring& Name,
344     festring::sizetype& NamePos,
345     int& NameScore)
346 {
347   // noop
348 }
349 
350 template <>
ScoreFlexibleName(const item::database * DataBase,cfestring & Identifier,festring & Name,festring::sizetype & NamePos,int & NameScore)351 void ScoreFlexibleName<item>(
352     const item::database* DataBase,
353     cfestring& Identifier,
354     festring& Name,
355     festring::sizetype& NamePos,
356     int& NameScore)
357 {
358   if(!DataBase->FlexibleNameSingular.IsEmpty() && DataBase->NameSingular != DataBase->FlexibleNameSingular)
359   {
360     if((NamePos = festring::IgnoreCaseFind(Identifier, " " + DataBase->FlexibleNameSingular + ' ')) != festring::NPos)
361     {
362       Name = DataBase->FlexibleNameSingular;
363       NameScore = DataBase->FlexibleNameSingular.GetSize();
364     }
365   }
366 }
367 
368 template <class type>
ScoreName(const typename type::database * DataBase,cfestring & Identifier)369 static std::pair<int, int> ScoreName(const typename type::database* DataBase, cfestring& Identifier)
370 {
371   std::pair<int, int> Result(0, 0);  // (score, misses)
372   festring Name;
373   festring::sizetype NamePos, AdjectivePos, PostfixPos, AliasPos;
374   int NameScore, AdjectiveScore, PostfixScore, AliasScore;
375 
376   NameScore = AdjectiveScore = PostfixScore = AliasScore = 0;
377   NamePos = AdjectivePos = PostfixPos = AliasPos = festring::NPos;
378 
379   if(!DataBase->NameSingular.IsEmpty())
380   {
381     if((NamePos = festring::IgnoreCaseFind(Identifier, " " + DataBase->NameSingular + ' ')) != festring::NPos)
382     {
383       Name = DataBase->NameSingular;
384       NameScore = DataBase->NameSingular.GetSize();
385     }
386   }
387 
388   if(NamePos == festring::NPos)
389     ScoreFlexibleName<type>(DataBase, Identifier, Name, NamePos, NameScore);
390 
391   for(uint c = 0; c < DataBase->Alias.Size; ++c)
392   {
393     cfestring& Alias = DataBase->Alias[c];
394 
395     if((AliasPos = festring::IgnoreCaseFind(Identifier, " " + Alias + ' ')) != festring::NPos)
396     {
397       AliasScore = Alias.GetSize();
398 
399       // XXX: alias may contain adjective and postfix
400       if(AliasScore > NameScore)
401       {
402         Name = Alias;
403         NamePos = AliasPos;
404         NameScore = AliasScore;
405       }
406     }
407   }
408 
409   if(NamePos == festring::NPos)
410     ++Result.second;
411 
412   if(!DataBase->Adjective.IsEmpty())
413   {
414     if((AdjectivePos = festring::IgnoreCaseFind(Identifier, " " + DataBase->Adjective + ' ')) != festring::NPos)
415     {
416       if(NamePos != festring::NPos && AdjectivePos + DataBase->Adjective.GetSize() < NamePos)
417         AdjectiveScore = DataBase->Adjective.GetSize();
418       else
419         AdjectiveScore = DataBase->Adjective.GetSize() / 2;
420     }
421     else
422       ++Result.second;
423   }
424 
425   if(!DataBase->PostFix.IsEmpty())
426   {
427     if((PostfixPos = festring::IgnoreCaseFind(Identifier, " " + DataBase->PostFix + ' ')) != festring::NPos)
428     {
429       if(NamePos != festring::NPos && PostfixPos > NamePos + Name.GetSize())
430         PostfixScore = DataBase->PostFix.GetSize();
431       else
432         PostfixScore = DataBase->PostFix.GetSize() / 2;
433     }
434     else
435       ++Result.second;
436   }
437 
438   Result.first = NameScore + AdjectiveScore + PostfixScore;
439   return Result;
440 }
441 
SearchForProto(cfestring & What,truth Output)442 template <class type> std::pair<const typename type::prototype*, int> SearchForProto(cfestring& What, truth Output)
443 {
444   typedef typename type::prototype prototype;
445   typedef typename type::database database;
446 
447   festring Identifier = " " + What + ' ';
448   Identifier.ShrinkWhitespace();
449 
450   truth BrokenRequested = festring::IgnoreCaseFind(Identifier, " broken ") != festring::NPos;
451   truth Illegal = false, Conflict = false;
452   std::pair<const prototype*, int> ID(0, 0);
453   std::pair<int, int> Best(0, 0);
454 
455   for(int c = 1; c < protocontainer<type>::GetSize(); ++c)
456   {
457     const prototype* Proto = protocontainer<type>::GetProto(c);
458     const database*const* ConfigData = Proto->GetConfigData();
459     int ConfigSize = Proto->GetConfigSize();
460 
461     for(int c = 0; c < ConfigSize; ++c)
462       if(!ConfigData[c]->IsAbstract)
463       {
464         truth IsBroken = ConfigData[c]->Config & BROKEN
465           || festring::IgnoreCaseFind(" " + ConfigData[c]->Adjective + ' ', " broken ") != festring::NPos;
466 
467         if(BrokenRequested == !IsBroken)
468           continue;
469 
470         std::pair<int, int> Score = ScoreName<type>(ConfigData[c], Identifier);
471 
472         if(Score == Best)
473           Conflict = true;
474         else if(Score.first > Best.first || (Score.first == Best.first && Score.second < Best.second))
475         {
476           if(ConfigData[c]->CanBeWished || game::WizardModeIsActive())
477           {
478             ID.first = Proto;
479             ID.second = ConfigData[c]->Config;
480             Best = Score;
481             Conflict = false;
482           }
483           else
484             Illegal = true;
485         }
486       }
487   }
488 
489   if(Output)
490   {
491     if(!Best.first)
492     {
493       if(Illegal)
494         ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
495       else
496         ADD_MESSAGE("What a strange wish!");
497     }
498     else if(Conflict)
499     {
500       ADD_MESSAGE("Be more precise!");
501       return std::pair<const prototype*, int>(0, 0);
502     }
503   }
504 
505   return ID;
506 }
507 
CreateMonster(cfestring & What,int SpecialFlags,truth Output)508 character* protosystem::CreateMonster(cfestring& What, int SpecialFlags, truth Output)
509 {
510   std::pair<const character::prototype*, int> ID = SearchForProto<character>(What, Output);
511 
512   if(ID.first)
513   {
514     character* Char = ID.first->Spawn(ID.second, SpecialFlags);
515 
516     if(!Char->HasBeenSeen() && !game::WizardModeIsActive())
517     {
518       ADD_MESSAGE("You have no idea what this creature is like.");
519       delete Char;
520       return 0;
521     }
522     else
523       return Char;
524   }
525   else
526     return 0;
527 }
528 
EmptyContainer(item * Item)529 static void EmptyContainer(item* Item)
530 {
531   if (materialcontainer* Container = dynamic_cast<materialcontainer*>(Item))
532   {
533     delete Container->RemoveSecondaryMaterial();
534   }
535   else if (itemcontainer* Container = dynamic_cast<itemcontainer*>(Item))
536     Container->GetContained()->Clean();
537 }
538 
CreateItemToCraft(cfestring & What)539 item* protosystem::CreateItemToCraft(cfestring& What)
540 {
541   std::pair<const item::prototype*, int> ID = SearchForProto<item>(What, false);
542   if(ID.first)
543   {
544     item* Item = ID.first->Spawn(ID.second);
545     EmptyContainer(Item);
546     return Item;
547   }
548   return NULL;
549 }
550 
CreateItem(cfestring & What,truth Output)551 item* protosystem::CreateItem(cfestring& What, truth Output)
552 {
553   std::pair<const item::prototype*, int> ID = SearchForProto<item>(What, Output);
554 
555   if(ID.first)
556   {
557     item* Item = ID.first->Spawn(ID.second);
558 
559     if(festring::IgnoreCaseFind(" " + What + ' ', " empty ") != festring::NPos)
560       EmptyContainer(Item);
561 
562     if(game::WizardModeIsActive())
563         // If WizMode prompt player to confirm wish
564     {
565         festring Q = "Do you want to wish for ";
566             Item->AddName(Q, INDEFINITE|STRIPPED);
567             Q << "? [y/N]";
568 
569             if(!game::TruthQuestion(Q))
570             {
571               delete Item;
572               return 0;
573             }
574         }
575 
576     return Item;
577   }
578   else
579     return 0;
580 }
581 
CreateMaterialForDetection(cfestring & What)582 material* protosystem::CreateMaterialForDetection(cfestring& What)
583 {
584   return CreateMaterial(What,0,true,true);
585 }
586 
CreateMaterial(cfestring & What,long Volume,truth Output,truth DetectMode)587 material* protosystem::CreateMaterial(cfestring& What, long Volume, truth Output, truth DetectMode)
588 {
589   for(int c1 = 1; c1 < protocontainer<material>::GetSize(); ++c1)
590   {
591     const material::prototype* Proto = protocontainer<material>::GetProto(c1);
592     const material::database*const* ConfigData = Proto->GetConfigData();
593     int ConfigSize = Proto->GetConfigSize();
594 
595     for(int c2 = 1; c2 < ConfigSize; ++c2)
596       if(ConfigData[c2]->NameStem == What)
597       {
598         if(
599             (ConfigData[c2]->CommonFlags & CAN_BE_WISHED) ||
600             (DetectMode && (ConfigData[c2]->CommonFlags & CAN_BE_DETECTED)) ||
601             game::WizardModeIsActive()
602         ){
603           return ConfigData[c2]->ProtoType->Spawn(ConfigData[c2]->Config, Volume);
604         }else if(Output){
605           ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
606           return 0;
607         }
608       }
609   }
610 
611   if(Output)
612     ADD_MESSAGE("There is no such material.");
613 
614   return 0;
615 }
616 
617 #ifdef WIZARD
618 
CreateEveryCharacter(charactervector & Character)619 void protosystem::CreateEveryCharacter(charactervector& Character)
620 {
621   for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
622   {
623     const character::prototype* Proto = protocontainer<character>::GetProto(c1);
624     const character::database*const* ConfigData = Proto->GetConfigData();
625     int ConfigSize = Proto->GetConfigSize();
626 
627     for(int c2 = 0; c2 < ConfigSize; ++c2)
628       if(!ConfigData[c2]->IsAbstract)
629         Character.push_back(Proto->Spawn(ConfigData[c2]->Config));
630   }
631 }
632 
CreateEveryItem(itemvectorvector & Item)633 void protosystem::CreateEveryItem(itemvectorvector& Item)
634 {
635   for(int c = 1; c < protocontainer<item>::GetSize(); ++c)
636   {
637     const item::prototype* Proto = protocontainer<item>::GetProto(c);
638     const item::database*const* ConfigData = Proto->GetConfigData();
639     int ConfigSize = Proto->GetConfigSize();
640 
641     for(int c2 = 0; c2 < ConfigSize; ++c2)
642       if(!ConfigData[c2]->IsAbstract && ConfigData[c2]->IsAutoInitializable)
643         Item.push_back(itemvector(1, Proto->Spawn(ConfigData[c2]->Config)));
644   }
645 }
646 
647 #endif
648 
CreateEveryNormalEnemy(charactervector & EnemyVector)649 void protosystem::CreateEveryNormalEnemy(charactervector& EnemyVector)
650 {
651   for(int c = 1; c < protocontainer<character>::GetSize(); ++c)
652   {
653     const character::prototype* Proto = protocontainer<character>::GetProto(c);
654     const character::database*const* ConfigData = Proto->GetConfigData();
655     int ConfigSize = Proto->GetConfigSize();
656 
657     for(int c2 = 0; c2 < ConfigSize; ++c2)
658       if(!ConfigData[c2]->IsAbstract
659          && !ConfigData[c2]->IsUnique
660          && ConfigData[c2]->CanBeGenerated)
661         EnemyVector.push_back(Proto->Spawn(ConfigData[c2]->Config, NO_PIC_UPDATE|NO_EQUIPMENT_PIC_UPDATE));
662   }
663 }
664 
Initialize()665 void protosystem::Initialize()
666 {
667   typedef item::prototype prototype;
668   typedef item::database database;
669   int c;
670 
671   for(c = 1; c < protocontainer<item>::GetSize(); ++c)
672   {
673     const prototype* Proto = protocontainer<item>::GetProtoData()[c]; DBG1(Proto->GetClassID());
674     ItemConfigDataSize += Proto->GetConfigSize(); DBG1(Proto->GetConfigData());
675 
676     if(Proto->GetConfigData()==NULL)
677       ABORT("missing prototype for '%s' ex. at item.dat add: %s { }",Proto->GetClassID(),Proto->GetClassID());
678 
679     if(Proto->GetConfigData()[0]->IsAbstract)
680       --ItemConfigDataSize;
681   }
682 
683   ItemConfigData = new database*[ItemConfigDataSize];
684   int Index = 0;
685 
686   for(c = 1; c < protocontainer<item>::GetSize(); ++c)
687   {
688     const prototype* Proto = protocontainer<item>::GetProtoData()[c];
689     const database*const* ProtoConfigData = Proto->GetConfigData();
690     const database* MainDataBase = *ProtoConfigData;
691 
692     if(!MainDataBase->IsAbstract)
693       ItemConfigData[Index++] = const_cast<database*>(MainDataBase);
694 
695     int ConfigSize = Proto->GetConfigSize();
696 
697     for(int c2 = 1; c2 < ConfigSize; ++c2)
698       ItemConfigData[Index++] = const_cast<database*>(ProtoConfigData[c2]);
699   }
700 
701   database** DataBaseBuffer = new database*[ItemConfigDataSize];
702 
703   for(int CategoryIndex = 0, Category = 1; CategoryIndex < ITEM_CATEGORIES; ++CategoryIndex, Category <<= 1)
704   {
705     long TotalPossibility = 0;
706     int CSize = 0;
707 
708     for(int c = 0; c < ItemConfigDataSize; ++c)
709     {
710       database* DataBase = ItemConfigData[c];
711 
712       if(DataBase->Category == Category)
713       {
714         DataBaseBuffer[CSize++] = DataBase;
715         TotalPossibility += DataBase->Possibility;
716         DataBase->PartialCategoryPossibilitySum = TotalPossibility;
717       }
718     }
719 
720     ItemCategoryData[CategoryIndex] = new database*[CSize];
721     ItemCategorySize[CategoryIndex] = CSize;
722     ItemCategoryPossibility[CategoryIndex] = TotalPossibility;
723     memcpy(ItemCategoryData[CategoryIndex], DataBaseBuffer, CSize * sizeof(database*));
724   }
725 
726   delete [] DataBaseBuffer;
727 
728   for(c = 0; c < ItemConfigDataSize; ++c)
729   {
730     database* DataBase = ItemConfigData[c];
731     TotalItemPossibility += DataBase->Possibility;
732     DataBase->PartialPossibilitySum = TotalItemPossibility;
733   }
734 }
735 
InitCharacterDataBaseFlags()736 void protosystem::InitCharacterDataBaseFlags()
737 {
738   for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
739   {
740     const character::prototype* Proto = protocontainer<character>::GetProto(c1);
741     character::database** ConfigData = Proto->ConfigData;
742     int ConfigSize = Proto->GetConfigSize();
743 
744     for(int c2 = 0; c2 < ConfigSize; ++c2)
745       if(!ConfigData[c2]->AutomaticallySeen)
746         ConfigData[c2]->Flags = 0;
747       else
748         ConfigData[c2]->Flags = HAS_BEEN_SEEN;
749   }
750 }
751 
SaveCharacterDataBaseFlags(outputfile & SaveFile)752 void protosystem::SaveCharacterDataBaseFlags(outputfile& SaveFile)
753 {
754   for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
755   {
756     const character::prototype* Proto = protocontainer<character>::GetProto(c1);
757     const character::database*const* ConfigData = Proto->ConfigData;
758     int ConfigSize = Proto->GetConfigSize();
759 
760     for(int c2 = 0; c2 < ConfigSize; ++c2)
761       SaveFile << ConfigData[c2]->Flags;
762   }
763 }
764 
LoadCharacterDataBaseFlags(inputfile & SaveFile)765 void protosystem::LoadCharacterDataBaseFlags(inputfile& SaveFile)
766 {
767   for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
768   {
769     const character::prototype* Proto = protocontainer<character>::GetProto(c1);
770     character::database** ConfigData = Proto->ConfigData;
771     int ConfigSize = Proto->GetConfigSize();
772 
773     for(int c2 = 0; c2 < ConfigSize; ++c2)
774       SaveFile >> ConfigData[c2]->Flags;
775   }
776 }
777 
CreateEverySeenCharacter(charactervector & Character)778 void protosystem::CreateEverySeenCharacter(charactervector& Character)
779 {
780   for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
781   {
782     const character::prototype* Proto = protocontainer<character>::GetProto(c1);
783     const character::database*const* ConfigData = Proto->GetConfigData();
784     int ConfigSize = Proto->GetConfigSize();
785 
786     for(int c2 = 0; c2 < ConfigSize; ++c2)
787       if(!ConfigData[c2]->IsAbstract && ConfigData[c2]->Flags & HAS_BEEN_SEEN)
788       {
789         character* Char = Proto->Spawn(ConfigData[c2]->Config);
790         Char->SetAssignedName("");
791         Character.push_back(Char);
792       }
793   }
794 }
795 
796 // For gods
CreateEveryGodlyMaterial(std::vector<material * > & Material,const god * God,ccharacter * Char)797 void protosystem::CreateEveryGodlyMaterial(std::vector<material*>& Material, const god* God, ccharacter* Char)
798 {
799   for(int c1 = 1; c1 < protocontainer<material>::GetSize(); ++c1)
800   {
801     const material::prototype* Proto = protocontainer<material>::GetProto(c1);
802     const material::database*const* ConfigData = Proto->GetConfigData();
803     int ConfigSize = Proto->GetConfigSize();
804 
805     for(int c2 = 1; c2 < ConfigSize; ++c2)
806       if(God->LikesMaterial(ConfigData[c2], Char))
807         Material.push_back(Proto->Spawn(ConfigData[c2]->Config));
808   }
809 }
810 
811 // For wizards and alchemists
CreateEveryMaterial(std::vector<material * > & Material)812 void protosystem::CreateEveryMaterial(std::vector<material*>& Material)
813 {
814   for(int c1 = 1; c1 < protocontainer<material>::GetSize(); ++c1)
815   {
816     const material::prototype* Proto = protocontainer<material>::GetProto(c1);
817     const material::database*const* ConfigData = Proto->GetConfigData();
818     int ConfigSize = Proto->GetConfigSize();
819 
820     for(int c2 = 1; c2 < ConfigSize; ++c2)
821       Material.push_back(Proto->Spawn(ConfigData[c2]->Config));
822   }
823 }
824