3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
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
);
38 Possible
.push_back(ConfigID
);
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;
52 fprintf(stderr
, "WARNING: WTF?! (protosystem::BalancedCreateMonster)\n");
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
);
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
));
77 /* This line is never reached, but it prevents warnings given by some (stupid) compilers. */
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;
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
);
114 if (RequiredCategory
== ANY_CATEGORY
) Category
= 0;
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];
124 sLong B
= PossibleCategorySize
[Category
] - 1;
126 sLong C
= (A
+ B
) >> 1;
128 if(ChosenCategory
[C
]->*PartialPossibilitySumPtr
> Rand
) B
= C
; else A
= C
;
130 ChosenDataBase
= ChosenCategory
[B
];
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
;
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
)
189 Possible
.push_back(ConfigID
);
196 MinDanger
= MinDanger
> 0 ? Max(MinDanger
* 3 >> 2, 1) : 0;
197 MaxDanger
= MaxDanger
< 1000000 ? Min(MaxDanger
* 5 >> 2, 999999) : 1000000;
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
));
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())
217 if(festring::IgnoreCaseFind(Identifier
, " " + DataBase
->NameSingular
+ ' ') != festring::NPos
)
218 Result
.first
+= DataBase
->NameSingular
.GetSize();
220 if(!DataBase
->Adjective
.IsEmpty())
223 if(DataBase
->Adjective
.GetSize() && festring::IgnoreCaseFind(Identifier
, " " + DataBase
->Adjective
+ ' ') != festring::NPos
)
224 Result
.first
+= DataBase
->Adjective
.GetSize();
226 if(!DataBase
->PostFix
.IsEmpty())
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();
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
;
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()) {
264 ID
.second
= ConfigData
[c
]->Config
;
267 } else Illegal
= true;
277 ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
279 ADD_MESSAGE("What a strange wish!");
283 ADD_MESSAGE("Be more precise!");
284 return std::pair
<const prototype
*, int>(0, 0);
291 character
* protosystem::CreateMonster(cfestring
& What
, int SpecialFlags
, truth Output
)
293 std::pair
<const character::prototype
*, int> ID
= SearchForProto
<character
>(What
, Output
);
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.");
312 item
* protosystem::CreateItem(cfestring
& What
, truth Output
)
314 std::pair
<const item::prototype
*, int> ID
= SearchForProto
<item
>(What
, Output
);
318 item
* Item
= ID
.first
->Spawn(ID
.second
);
319 festring Q
= "Do you want to wish for ";
320 Item
->AddName(Q
, INDEFINITE
|STRIPPED
);
323 if(!game::TruthQuestion(Q
))
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
);
348 ADD_MESSAGE("You hear a booming voice: \"No, mortal! This will not be done!\"");
355 ADD_MESSAGE("There is no such material.");
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
));
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
;
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
];
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;
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;
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
));