renamed alot of types; hope this will not break the game
[k8-i-v-a-n.git] / src / game / proto.cpp
blob65b554da42baa39b1178ca81aba9d4470d265275
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 /* Compiled through dataset.cpp */
14 itemdatabase **protosystem::ItemConfigData;
15 int protosystem::ItemConfigDataSize;
16 itemdatabase **protosystem::ItemCategoryData[ITEM_CATEGORIES];
17 int protosystem::ItemCategorySize[ITEM_CATEGORIES];
18 sLong protosystem::ItemCategoryPossibility[ITEM_CATEGORIES];
19 sLong protosystem::TotalItemPossibility;
22 character *protosystem::BalancedCreateMonster () {
23 for (int c = 0; ; ++c) {
24 double MinDifficulty = game::GetMinDifficulty(), MaxDifficulty = MinDifficulty*25;
25 std::vector<configid> Possible;
26 for (int Type = 1; Type < protocontainer<character>::GetSize(); ++Type) {
27 const character::prototype *Proto = protocontainer<character>::GetProto(Type);
28 const character::database *const *ConfigData = Proto->GetConfigData();
29 int ConfigSize = Proto->GetConfigSize();
30 for (int c = 0; c < ConfigSize; ++c) {
31 const character::database *DataBase = ConfigData[c];
32 if (!DataBase->IsAbstract && DataBase->CanBeGenerated) {
33 if (DataBase->IsUnique && DataBase->Flags & HAS_BEEN_GENERATED) continue;
34 truth IsCatacomb = *game::GetCurrentLevel()->GetLevelScript()->IsCatacomb();
35 if ((IsCatacomb && !DataBase->IsCatacombCreature) || (!IsCatacomb && DataBase->CanBeGeneratedOnlyInTheCatacombs)) continue;
36 configid ConfigID(Type, ConfigData[c]->Config);
37 if (c >= 100) {
38 Possible.push_back(ConfigID);
39 continue;
41 const dangerid &DangerID = game::GetDangerMap().find(ConfigID)->second;
42 if (!DataBase->IgnoreDanger) {
43 double Danger = DangerID.EquippedDanger;
44 if (Danger > 99.0 || Danger < 0.0011 || (DataBase->IsUnique && Danger < 3.5)) continue;
45 double DangerModifier = DataBase->DangerModifier==100 ? Danger : Danger*100/DataBase->DangerModifier;
46 if (DangerModifier < MinDifficulty || DangerModifier > MaxDifficulty) continue;
48 ivantime Time;
49 game::GetTime(Time);
51 if (!PLAYER) {
52 fprintf(stderr, "WARNING: WTF?! (protosystem::BalancedCreateMonster)\n");
53 //continue;
55 if (PLAYER->GetMaxHP() < DataBase->HPRequirementForGeneration && Time.Day < DataBase->DayRequirementForGeneration) continue;
56 Possible.push_back(ConfigID);
61 if (Possible.empty()) continue;
63 for (int i = 0; i < 25; ++i) {
64 configid Chosen = Possible[RAND_GOOD(Possible.size())];
65 const character::prototype* Proto = protocontainer<character>::GetProto(Chosen.Type);
66 character *Monster = Proto->Spawn(Chosen.Config);
67 if (c >= 100 ||
68 ((Monster->GetFrequency() == 10000 || Monster->GetFrequency() > RAND_GOOD(10000)) &&
69 (Monster->IsUnique() ||
70 (Monster->GetTimeToKill(PLAYER, true) > 5000 && PLAYER->GetTimeToKill(Monster, true) < 200000)))) {
71 Monster->SetTeam(game::GetTeam(MONSTER_TEAM));
72 return Monster;
74 delete Monster;
77 /* This line is never reached, but it prevents warnings given by some (stupid) compilers. */
78 return 0;
82 item *protosystem::BalancedCreateItem (sLong MinPrice, sLong MaxPrice, sLong RequiredCategory, int SpecialFlags, int ConfigFlags, int RequiredGod, truth Polymorph) {
83 typedef item::database database;
84 database **PossibleCategory[ITEM_CATEGORIES];
85 int PossibleCategorySize[ITEM_CATEGORIES];
86 sLong PartialCategoryPossibilitySum[ITEM_CATEGORIES];
87 int PossibleCategories = 0;
88 sLong TotalPossibility = 0;
89 sLong database::*PartialPossibilitySumPtr;
90 if (RequiredCategory == ANY_CATEGORY) {
91 PartialPossibilitySumPtr = &database::PartialPossibilitySum;
92 PossibleCategory[0] = ItemConfigData;
93 PossibleCategorySize[0] = ItemConfigDataSize;
94 TotalPossibility = TotalItemPossibility;
95 PartialCategoryPossibilitySum[0] = TotalPossibility;
96 PossibleCategories = 1;
97 } else {
98 PartialPossibilitySumPtr = &database::PartialCategoryPossibilitySum;
99 for (sLong CategoryIndex = 0, Category = 1; CategoryIndex < ITEM_CATEGORIES; ++CategoryIndex, Category <<= 1) {
100 if (Category & RequiredCategory) {
101 PossibleCategory[PossibleCategories] = ItemCategoryData[CategoryIndex];
102 PossibleCategorySize[PossibleCategories] = ItemCategorySize[CategoryIndex];
103 TotalPossibility += ItemCategoryPossibility[CategoryIndex];
104 PartialCategoryPossibilitySum[PossibleCategories] = TotalPossibility;
105 ++PossibleCategories;
110 for (int c0 = 0;; ++c0) {
111 for (int c1 = 0; c1 < BALANCED_CREATE_ITEM_ITERATIONS; ++c1) {
112 sLong Rand = RAND_GOOD(TotalPossibility);
113 int Category;
114 if (RequiredCategory == ANY_CATEGORY) Category = 0;
115 else {
116 for (int c2 = 0;; ++c2) if (PartialCategoryPossibilitySum[c2] > Rand) { Category = c2; break; }
117 if (Category) Rand -= PartialCategoryPossibilitySum[Category-1];
119 const database *const *ChosenCategory = PossibleCategory[Category];
120 const database *ChosenDataBase;
121 if (ChosenCategory[0]->PartialCategoryPossibilitySum > Rand) ChosenDataBase = ChosenCategory[0];
122 else {
123 sLong A = 0;
124 sLong B = PossibleCategorySize[Category] - 1;
125 for (;;) {
126 sLong C = (A + B) >> 1;
127 if (A != C) {
128 if(ChosenCategory[C]->*PartialPossibilitySumPtr > Rand) B = C; else A = C;
129 } else {
130 ChosenDataBase = ChosenCategory[B];
131 break;
135 int Config = ChosenDataBase->Config;
136 if ((!(ConfigFlags & NO_BROKEN) || !(Config & BROKEN)) && (!Polymorph || ChosenDataBase->IsPolymorphSpawnable)) {
137 item *Item = ChosenDataBase->ProtoType->Spawn(Config, SpecialFlags);
138 truth GodOK = !RequiredGod || Item->GetAttachedGod() == RequiredGod;
139 /* Optimization, GetTruePrice() may be rather slow */
140 if (((MinPrice == 0 && MaxPrice == MAX_PRICE) || (Config & BROKEN && ConfigFlags & IGNORE_BROKEN_PRICE)) && GodOK) return Item;
141 sLong Price = Item->GetTruePrice();
142 if (Item->HandleInPairs()) Price <<= 1;
143 if (Price >= MinPrice && Price <= MaxPrice && GodOK) return Item;
144 delete Item;
147 if (c0 == 25 && RequiredGod) return 0;
148 MinPrice = MinPrice * 7 >> 3;
149 MaxPrice = MaxPrice * 9 >> 3;
154 character *protosystem::CreateMonster (int MinDanger, int MaxDanger, int SpecialFlags) {
155 std::vector<configid> Possible;
156 character* Monster = 0;
158 for(int c = 0; !Monster; ++c)
160 for(int Type = 1; Type < protocontainer<character>::GetSize(); ++Type)
162 const character::prototype* Proto = protocontainer<character>::GetProto(Type);
163 const character::database*const* ConfigData = Proto->GetConfigData();
164 int ConfigSize = Proto->GetConfigSize();
166 for(int c = 0; c < ConfigSize; ++c)
168 const character::database* DataBase = ConfigData[c];
170 if(!DataBase->IsAbstract
171 && DataBase->CanBeGenerated
172 && DataBase->CanBeWished
173 && !DataBase->IsUnique
174 && (DataBase->Frequency == 10000
175 || DataBase->Frequency > RAND_GOOD(10000)))
177 configid ConfigID(Type, DataBase->Config);
179 if((MinDanger > 0 || MaxDanger < 1000000) && c < 25)
181 const dangerid& DangerID = game::GetDangerMap().find(ConfigID)->second;
182 double RawDanger = SpecialFlags & NO_EQUIPMENT ? DangerID.NakedDanger : DangerID.EquippedDanger;
183 int Danger = int(DataBase->DangerModifier == 100 ? RawDanger * 1000 : RawDanger * 100000 / DataBase->DangerModifier);
185 if(Danger < MinDanger || Danger > MaxDanger)
186 continue;
189 Possible.push_back(ConfigID);
194 if(Possible.empty())
196 MinDanger = MinDanger > 0 ? Max(MinDanger * 3 >> 2, 1) : 0;
197 MaxDanger = MaxDanger < 1000000 ? Min(MaxDanger * 5 >> 2, 999999) : 1000000;
198 continue;
201 configid Chosen = Possible[RAND_GOOD(Possible.size())];
202 Monster = protocontainer<character>::GetProto(Chosen.Type)->Spawn(Chosen.Config, SpecialFlags);
203 Monster->SignalGeneration();
204 Monster->SetTeam(game::GetTeam(MONSTER_TEAM));
207 return Monster;
210 template <class type> std::pair<int, int> CountCorrectNameLetters(const typename type::database* DataBase, cfestring& Identifier)
212 std::pair<int, int> Result(0, 0);
214 if(!DataBase->NameSingular.IsEmpty())
215 ++Result.second;
217 if(festring::IgnoreCaseFind(Identifier, " " + DataBase->NameSingular + ' ') != festring::NPos)
218 Result.first += DataBase->NameSingular.GetSize();
220 if(!DataBase->Adjective.IsEmpty())
221 ++Result.second;
223 if(DataBase->Adjective.GetSize() && festring::IgnoreCaseFind(Identifier, " " + DataBase->Adjective + ' ') != festring::NPos)
224 Result.first += DataBase->Adjective.GetSize();
226 if(!DataBase->PostFix.IsEmpty())
227 ++Result.second;
229 if(DataBase->PostFix.GetSize() && festring::IgnoreCaseFind(Identifier, " " + DataBase->PostFix + ' ') != festring::NPos)
230 Result.first += DataBase->PostFix.GetSize();
232 for(uInt c = 0; c < DataBase->Alias.Size; ++c)
233 if(festring::IgnoreCaseFind(Identifier, " " + DataBase->Alias[c] + ' ') != festring::NPos)
234 Result.first += DataBase->Alias[c].GetSize();
236 return Result;
239 template <class type> std::pair<const typename type::prototype*, int> SearchForProto(cfestring& What, truth Output)
241 typedef typename type::prototype prototype;
242 typedef typename type::database database;
244 festring Identifier;
245 Identifier << ' ' << What << ' ';
246 truth Illegal = false, Conflict = false;
247 std::pair<const prototype*, int> ID(0, 0);
248 std::pair<int, int> Best(0, 0);
250 for (int c = 1; c < protocontainer<type>::GetSize(); ++c) {
251 const prototype* Proto = protocontainer<type>::GetProto(c);
252 const database*const* ConfigData = Proto->GetConfigData();
253 int ConfigSize = Proto->GetConfigSize();
255 for (int c = 0; c < ConfigSize; ++c)
256 if (!ConfigData[c]->IsAbstract) {
257 truth BrokenRequested = festring::IgnoreCaseFind(Identifier, " broken ") != festring::NPos;
258 if (BrokenRequested == !(ConfigData[c]->Config & BROKEN)) continue;
259 std::pair<int, int> Correct = CountCorrectNameLetters<type>(ConfigData[c], Identifier);
260 if (Correct == Best) Conflict = true;
261 else if (Correct.first > Best.first || (Correct.first == Best.first && Correct.second < Best.second)) {
262 if (ConfigData[c]->CanBeWished || game::WizardModeIsActive()) {
263 ID.first = Proto;
264 ID.second = ConfigData[c]->Config;
265 Best = Correct;
266 Conflict = false;
267 } else Illegal = true;
272 if(Output)
274 if(!Best.first)
276 if(Illegal)
277 ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
278 else
279 ADD_MESSAGE("What a strange wish!");
281 else if(Conflict)
283 ADD_MESSAGE("Be more precise!");
284 return std::pair<const prototype*, int>(0, 0);
288 return ID;
291 character* protosystem::CreateMonster(cfestring& What, int SpecialFlags, truth Output)
293 std::pair<const character::prototype*, int> ID = SearchForProto<character>(What, Output);
295 if(ID.first)
297 character* Char = ID.first->Spawn(ID.second, SpecialFlags);
299 if(!Char->HasBeenSeen() && !game::WizardModeIsActive())
301 ADD_MESSAGE("You have no idea what this creature is like.");
302 delete Char;
303 return 0;
305 else
306 return Char;
308 else
309 return 0;
312 item* protosystem::CreateItem(cfestring& What, truth Output)
314 std::pair<const item::prototype*, int> ID = SearchForProto<item>(What, Output);
316 if(ID.first)
318 item* Item = ID.first->Spawn(ID.second);
319 festring Q = "Do you want to wish for ";
320 Item->AddName(Q, INDEFINITE|STRIPPED);
321 Q << "? [y/N]";
323 if(!game::TruthQuestion(Q))
325 delete Item;
326 return 0;
329 return Item;
331 else
332 return 0;
335 material* protosystem::CreateMaterial(cfestring& What, sLong Volume, truth Output)
337 for(int c1 = 1; c1 < protocontainer<material>::GetSize(); ++c1)
339 const material::prototype* Proto = protocontainer<material>::GetProto(c1);
340 const material::database*const* ConfigData = Proto->GetConfigData();
341 int ConfigSize = Proto->GetConfigSize();
343 for(int c2 = 1; c2 < ConfigSize; ++c2)
344 if (ConfigData[c2]->NameStem == What) {
345 if (ConfigData[c2]->CommonFlags & CAN_BE_WISHED || game::WizardModeIsActive())
346 return ConfigData[c2]->ProtoType->Spawn(ConfigData[c2]->Config, Volume);
347 else if (Output) {
348 ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
349 return 0;
354 if(Output)
355 ADD_MESSAGE("There is no such material.");
357 return 0;
360 #ifdef WIZARD
362 void protosystem::CreateEveryCharacter(charactervector& Character)
364 for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
366 const character::prototype* Proto = protocontainer<character>::GetProto(c1);
367 const character::database*const* ConfigData = Proto->GetConfigData();
368 int ConfigSize = Proto->GetConfigSize();
370 for(int c2 = 0; c2 < ConfigSize; ++c2)
371 if(!ConfigData[c2]->IsAbstract)
372 Character.push_back(Proto->Spawn(ConfigData[c2]->Config));
376 void protosystem::CreateEveryItem(itemvectorvector& Item)
378 for(int c = 1; c < protocontainer<item>::GetSize(); ++c)
380 const item::prototype* Proto = protocontainer<item>::GetProto(c);
381 const item::database*const* ConfigData = Proto->GetConfigData();
382 int ConfigSize = Proto->GetConfigSize();
384 for(int c2 = 0; c2 < ConfigSize; ++c2)
385 if(!ConfigData[c2]->IsAbstract && ConfigData[c2]->IsAutoInitializable)
386 Item.push_back(itemvector(1, Proto->Spawn(ConfigData[c2]->Config)));
390 void protosystem::CreateEveryMaterial(std::vector<material*>& Material)
392 for(int c1 = 1; c1 < protocontainer<material>::GetSize(); ++c1)
394 const material::prototype* Proto = protocontainer<material>::GetProto(c1);
395 const material::database*const* ConfigData = Proto->GetConfigData();
396 int ConfigSize = Proto->GetConfigSize();
398 for(int c2 = 1; c2 < ConfigSize; ++c2)
399 Material.push_back(Proto->Spawn(ConfigData[c2]->Config));
403 #endif
405 void protosystem::CreateEveryNormalEnemy(charactervector& EnemyVector)
407 for(int c = 1; c < protocontainer<character>::GetSize(); ++c)
409 const character::prototype* Proto = protocontainer<character>::GetProto(c);
410 const character::database*const* ConfigData = Proto->GetConfigData();
411 int ConfigSize = Proto->GetConfigSize();
413 for(int c2 = 0; c2 < ConfigSize; ++c2)
414 if(!ConfigData[c2]->IsAbstract
415 && !ConfigData[c2]->IsUnique
416 && ConfigData[c2]->CanBeGenerated)
417 EnemyVector.push_back(Proto->Spawn(ConfigData[c2]->Config, NO_PIC_UPDATE|NO_EQUIPMENT_PIC_UPDATE));
421 void protosystem::Initialize()
423 typedef item::prototype prototype;
424 typedef item::database database;
425 int c;
427 for(c = 1; c < protocontainer<item>::GetSize(); ++c)
429 const prototype *Proto = protocontainer<item>::GetProtoData()[c];
430 if (!Proto->GetConfigData()) {
431 ABORT("Seems that database is missing <%s>!", Proto->GetClassID());
433 ItemConfigDataSize += Proto->GetConfigSize();
434 if (Proto->GetConfigData()[0]->IsAbstract) --ItemConfigDataSize;
437 ItemConfigData = new database*[ItemConfigDataSize];
438 int Index = 0;
440 for(c = 1; c < protocontainer<item>::GetSize(); ++c)
442 const prototype* Proto = protocontainer<item>::GetProtoData()[c];
443 const database*const* ProtoConfigData = Proto->GetConfigData();
444 const database* MainDataBase = *ProtoConfigData;
446 if(!MainDataBase->IsAbstract)
447 ItemConfigData[Index++] = const_cast<database*>(MainDataBase);
449 int ConfigSize = Proto->GetConfigSize();
451 for(int c2 = 1; c2 < ConfigSize; ++c2)
452 ItemConfigData[Index++] = const_cast<database*>(ProtoConfigData[c2]);
455 database** DataBaseBuffer = new database*[ItemConfigDataSize];
457 for(int CategoryIndex = 0, Category = 1; CategoryIndex < ITEM_CATEGORIES; ++CategoryIndex, Category <<= 1)
459 sLong TotalPossibility = 0;
460 int CSize = 0;
462 for(int c = 0; c < ItemConfigDataSize; ++c)
464 database* DataBase = ItemConfigData[c];
466 if(DataBase->Category == Category)
468 DataBaseBuffer[CSize++] = DataBase;
469 TotalPossibility += DataBase->Possibility;
470 DataBase->PartialCategoryPossibilitySum = TotalPossibility;
474 ItemCategoryData[CategoryIndex] = new database*[CSize];
475 ItemCategorySize[CategoryIndex] = CSize;
476 ItemCategoryPossibility[CategoryIndex] = TotalPossibility;
477 memcpy(ItemCategoryData[CategoryIndex], DataBaseBuffer, CSize * sizeof(database*));
480 delete [] DataBaseBuffer;
482 for(c = 0; c < ItemConfigDataSize; ++c)
484 database* DataBase = ItemConfigData[c];
485 TotalItemPossibility += DataBase->Possibility;
486 DataBase->PartialPossibilitySum = TotalItemPossibility;
490 void protosystem::InitCharacterDataBaseFlags()
492 for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
494 const character::prototype* Proto = protocontainer<character>::GetProto(c1);
495 character::database** ConfigData = Proto->ConfigData;
496 int ConfigSize = Proto->GetConfigSize();
498 for(int c2 = 0; c2 < ConfigSize; ++c2)
499 if(!ConfigData[c2]->AutomaticallySeen)
500 ConfigData[c2]->Flags = 0;
501 else
502 ConfigData[c2]->Flags = HAS_BEEN_SEEN;
506 void protosystem::SaveCharacterDataBaseFlags(outputfile& SaveFile)
508 for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
510 const character::prototype* Proto = protocontainer<character>::GetProto(c1);
511 const character::database*const* ConfigData = Proto->ConfigData;
512 int ConfigSize = Proto->GetConfigSize();
514 for(int c2 = 0; c2 < ConfigSize; ++c2)
515 SaveFile << ConfigData[c2]->Flags;
519 void protosystem::LoadCharacterDataBaseFlags(inputfile& SaveFile)
521 for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
523 const character::prototype* Proto = protocontainer<character>::GetProto(c1);
524 character::database** ConfigData = Proto->ConfigData;
525 int ConfigSize = Proto->GetConfigSize();
527 for(int c2 = 0; c2 < ConfigSize; ++c2)
528 SaveFile >> ConfigData[c2]->Flags;
532 void protosystem::CreateEverySeenCharacter(charactervector& Character)
534 for(int c1 = 1; c1 < protocontainer<character>::GetSize(); ++c1)
536 const character::prototype* Proto = protocontainer<character>::GetProto(c1);
537 const character::database*const* ConfigData = Proto->GetConfigData();
538 int ConfigSize = Proto->GetConfigSize();
540 for(int c2 = 0; c2 < ConfigSize; ++c2)
541 if(!ConfigData[c2]->IsAbstract && ConfigData[c2]->Flags & HAS_BEEN_SEEN)
543 character* Char = Proto->Spawn(ConfigData[c2]->Config);
544 Char->SetAssignedName("");
545 Character.push_back(Char);
550 void protosystem::CreateEveryMaterial(std::vector<material*>& Material, const god* God, ccharacter* Char)
552 for(int c1 = 1; c1 < protocontainer<material>::GetSize(); ++c1)
554 const material::prototype* Proto = protocontainer<material>::GetProto(c1);
555 const material::database*const* ConfigData = Proto->GetConfigData();
556 int ConfigSize = Proto->GetConfigSize();
558 for(int c2 = 1; c2 < ConfigSize; ++c2)
559 if(God->LikesMaterial(ConfigData[c2], Char))
560 Material.push_back(Proto->Spawn(ConfigData[c2]->Config));