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
17 #include <sys/types.h>
53 #define SAVE_FILE_VERSION 130 // Increment this if changes make savefiles incompatible
54 #define BONE_FILE_VERSION 115 // Increment this if changes make bonefiles incompatible
61 std::stack
<inputfile
*> game::mFEStack
;
62 character
*game::mChar
= 0;
63 ccharacter
*game::mActor
= 0;
64 ccharacter
*game::mSecondActor
= 0;
65 item
*game::mItem
= 0;
66 int game::mResult
= 0;
69 int game::CurrentLevelIndex
;
70 truth
game::InWilderness
= false;
71 worldmap
* game::WorldMap
;
72 area
* game::AreaInLoad
;
73 square
* game::SquareInLoad
;
74 dungeon
** game::Dungeon
;
75 int game::CurrentDungeonIndex
;
76 feuLong
game::NextCharacterID
= 1;
77 feuLong
game::NextItemID
= 1;
78 feuLong
game::NextTrapID
= 1;
80 feuLong
game::LOSTick
;
81 v2
game::CursorPos(-1, -1);
83 truth
game::Generating
= false;
84 double game::AveragePlayerArmStrengthExperience
;
85 double game::AveragePlayerLegStrengthExperience
;
86 double game::AveragePlayerDexterityExperience
;
87 double game::AveragePlayerAgilityExperience
;
92 int game::MondedrPass
;
93 int game::RingOfThieves
;
96 int game::LoricatusHammer
;
98 int game::OmmelBloodMission
;
99 int game::RegiiTalkState
;
101 massacremap
game::PlayerMassacreMap
;
102 massacremap
game::PetMassacreMap
;
103 massacremap
game::MiscMassacreMap
;
104 sLong
game::PlayerMassacreAmount
= 0;
105 sLong
game::PetMassacreAmount
= 0;
106 sLong
game::MiscMassacreAmount
= 0;
107 boneidmap
game::BoneItemIDMap
;
108 boneidmap
game::BoneCharacterIDMap
;
109 truth
game::TooGreatDangerFoundTruth
;
110 itemvectorvector
game::ItemDrawVector
;
111 charactervector
game::CharacterDrawVector
;
112 truth
game::SumoWrestling
;
113 liquid
* game::GlobalRainLiquid
;
114 v2
game::GlobalRainSpeed
;
115 sLong
game::GlobalRainTimeModifier
;
116 truth
game::PlayerSumoChampion
;
117 truth
game::PlayerSolicitusChampion
;
118 feuLong
game::SquarePartEmitationTick
= 0;
120 truth
game::PlayerRunning
;
121 character
* game::LastPetUnderCursor
;
122 charactervector
game::PetVector
;
123 double game::DangerFound
;
124 int game::OldAttribute
[ATTRIBUTES
];
125 int game::NewAttribute
[ATTRIBUTES
];
126 int game::LastAttributeChangeTick
[ATTRIBUTES
];
127 int game::NecroCounter
;
128 int game::CursorData
;
129 truth
game::CausePanicFlag
;
131 truth
game::Loading
= false;
132 truth
game::JumpToPlayerBe
= false;
133 truth
game::InGetCommand
= false;
134 character
*game::Petrus
= 0;
135 time_t game::TimePlayedBeforeLastLoad
;
136 time_t game::LastLoad
;
137 time_t game::GameBegan
;
138 truth
game::PlayerHasReceivedAllGodsKnownBonus
;
140 festring
game::AutoSaveFileName
= game::GetSaveDir() + "AutoSave";
141 cchar
*const game::Alignment
[] = { "L++", "L+", "L", "L-", "N+", "N=", "N-", "C+", "C", "C-", "C--" };
144 cint
game::MoveNormalCommandKey
[] = { KEY_HOME
, KEY_UP
, KEY_PAGE_UP
, KEY_LEFT
, KEY_RIGHT
, KEY_END
, KEY_DOWN
, KEY_PAGE_DOWN
, '.' };
145 int game::MoveAbnormalCommandKey
[] = { '7','8','9','u','o','j','k','l','.' };
147 cv2
game::MoveVector
[] = { v2(-1, -1), v2(0, -1), v2(1, -1), v2(-1, 0), v2(1, 0), v2(-1, 1), v2(0, 1), v2(1, 1), v2(0, 0) };
148 cv2
game::RelativeMoveVector
[] = { v2(-1, -1), v2(1, 0), v2(1, 0), v2(-2, 1), v2(2, 0), v2(-2, 1), v2(1, 0), v2(1, 0), v2(-1, -1) };
149 cv2
game::BasicMoveVector
[] = { v2(-1, 0), v2(1, 0), v2(0, -1), v2(0, 1) };
150 cv2
game::LargeMoveVector
[] = { v2(-1, -1), v2(0, -1), v2(1, -1), v2(2, -1), v2(-1, 0), v2(2, 0), v2(-1, 1), v2(2, 1), v2(-1, 2), v2(0, 2), v2(1, 2), v2(2, 2), v2(0, 0), v2(1, 0), v2(0, 1), v2(1, 1) };
151 cint
game::LargeMoveDirection
[] = { 0, 1, 1, 2, 3, 4, 3, 4, 5, 6, 6, 7, 8, 8, 8, 8 };
153 truth
game::LOSUpdateRequested
= false;
154 uChar
***game::LuxTable
= 0;
156 character
*game::Player
;
157 v2
game::Camera(0, 0);
159 gamescript
*game::GameScript
= 0;
160 valuemap
game::GlobalValueMap
;
161 dangermap
game::DangerMap
;
162 int game::NextDangerIDType
;
163 int game::NextDangerIDConfigIndex
;
164 characteridmap
game::CharacterIDMap
;
165 itemidmap
game::ItemIDMap
;
166 trapidmap
game::TrapIDMap
;
167 truth
game::PlayerHurtByExplosion
;
168 area
*game::CurrentArea
;
169 level
*game::CurrentLevel
;
170 wsquare
***game::CurrentWSquareMap
;
171 lsquare
***game::CurrentLSquareMap
;
172 festring
game::DefaultPolymorphTo
;
173 festring
game::DefaultSummonMonster
;
174 festring
game::DefaultWish
;
175 festring
game::DefaultChangeMaterial
;
176 festring
game::DefaultDetectMaterial
;
177 festring
game::DefaultTeam
;
178 truth
game::WizardMode
;
179 int game::SeeWholeMapCheatMode
;
180 truth
game::GoThroughWallsCheat
;
181 int game::QuestMonstersFound
;
182 bitmap
*game::BusyAnimationCache
[32];
183 festring
game::PlayerName
;
184 feuLong
game::EquipmentMemory
[MAX_EQUIPMENT_SLOTS
];
185 olterrain
*game::MonsterPortal
;
186 std::vector
<v2
> game::SpecialCursorPos
;
187 std::vector
<int> game::SpecialCursorData
;
188 cbitmap
*game::EnterImage
;
189 v2
game::EnterTextDisplacement
;
193 int game::MoveVectorToDirection (cv2
&mv
) {
194 for (int c
= 0; c
< 9; ++c
) if (MoveVector
[c
] == mv
) return c
;
199 char game::GetAbnormalMoveKey (int idx
) {
200 if (idx
< 0 || idx
> 8) return 0;
201 return MoveAbnormalCommandKey
[idx
];
205 void game::SetAbnormalMoveKey (int idx
, char ch
) {
206 if (idx
>= 0 && idx
<= 8) MoveAbnormalCommandKey
[idx
] = ch
;
210 void game::AddCharacterID (character
*Char
, feuLong ID
) {
211 /*k8:??? if (CharacterIDMap.find(ID) != CharacterIDMap.end())
212 int esko = esko = 2;*/
213 CharacterIDMap
.insert(std::make_pair(ID
, Char
));
217 void game::RemoveCharacterID (feuLong ID
) {
218 /*k8:??? if (CharacterIDMap.find(ID) == CharacterIDMap.end())
219 int esko = esko = 2;*/
220 CharacterIDMap
.erase(CharacterIDMap
.find(ID
));
224 void game::AddItemID (item
*Item
, feuLong ID
) {
225 /*k8:??? if (ItemIDMap.find(ID) != ItemIDMap.end())
226 int esko = esko = 2;*/
227 ItemIDMap
.insert(std::make_pair(ID
, Item
));
231 void game::RemoveItemID (feuLong ID
) {
232 /*k8:??? if(ID && ItemIDMap.find(ID) == ItemIDMap.end())
233 int esko = esko = 2;*/
234 if (ID
) ItemIDMap
.erase(ItemIDMap
.find(ID
));
238 void game::UpdateItemID (item
*Item
, feuLong ID
) {
239 /*k8:??? if(ItemIDMap.find(ID) == ItemIDMap.end())
240 int esko = esko = 2;*/
241 ItemIDMap
.find(ID
)->second
= Item
;
245 void game::AddTrapID (entity
*Trap
, feuLong ID
) {
246 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
247 int esko = esko = 2;*/
248 if (ID
) TrapIDMap
.insert(std::make_pair(ID
, Trap
));
252 void game::RemoveTrapID (feuLong ID
) {
253 /*k8:??? if(ID && TrapIDMap.find(ID) == TrapIDMap.end())
254 int esko = esko = 2;*/
255 if (ID
) TrapIDMap
.erase(TrapIDMap
.find(ID
));
259 void game::UpdateTrapID (entity
*Trap
, feuLong ID
) {
260 /*k8:??? if(TrapIDMap.find(ID) == TrapIDMap.end())
261 int esko = esko = 2;*/
262 TrapIDMap
.find(ID
)->second
= Trap
;
266 const dangermap
&game::GetDangerMap () { return DangerMap
; }
267 void game::ClearItemDrawVector () { ItemDrawVector
.clear(); }
268 void game::ClearCharacterDrawVector () { CharacterDrawVector
.clear(); }
271 void game::InitScript () {
272 inputfile
ScriptFile(GetGameDir()+"Script/dungeon.dat", &GlobalValueMap
);
273 GameScript
= new gamescript
;
274 GameScript
->ReadFrom(ScriptFile
);
275 { /* additional dungeon files */
276 for (int f
= 0; f
<= 99; f
++) {
278 sprintf(bnum
, "Script/dungeon_%02d.dat", f
);
279 inputfile
ifl(game::GetGameDir()+bnum
, &game::GetGlobalValueMap(), false);
281 //fprintf(stderr, "loading: %s\n", bnum+7);
282 GameScript
->ReadFrom(ifl
);
287 GameScript
->RandomizeLevels();
291 truth
game::Init (cfestring
&Name
) {
292 if (Name
.IsEmpty()) {
293 if (ivanconfig::GetDefaultName().IsEmpty()) {
295 if (iosystem::StringQuestion(PlayerName
, CONST_S("What is your name? (1-20 letters)"), v2(30, 46), WHITE
, 1, 20, true, true) == ABORTED
|| PlayerName
.IsEmpty()) return false;
297 PlayerName
= ivanconfig::GetDefaultName();
304 mkdir(GetSaveDir().CStr(), S_IRWXU
|S_IRWXG
);
305 mkdir(GetBoneDir().CStr(), S_IRWXU
|S_IRWXG
);
307 mkdir(GetSaveDir().CStr());
308 mkdir(GetBoneDir().CStr());
314 CausePanicFlag
= false;
317 switch (Load(SaveName(PlayerName
))) {
319 globalwindowhandler::InstallControlLoop(AnimationController
);
321 SetForceJumpToPlayerBe(true);
322 GetCurrentArea()->SendNewDrawRequest();
323 SendLOSUpdateRequest();
324 ADD_MESSAGE("Game loaded successfully.");
327 iosystem::TextScreen(CONST_S(
328 "You couldn't possibly have guessed this day would differ from any other.\n"
329 "It began just as always. You woke up at dawn and drove off the giant spider\n"
330 "resting on your face. On your way to work you had serious trouble avoiding\n"
331 "the lions and pythons roaming wild around the village. After getting kicked\n"
332 "by colony masters for being late you performed your twelve-hour routine of\n"
333 "climbing trees, gathering bananas, climbing trees, gathering bananas, chasing\n"
334 "monkeys that stole the first gathered bananas, carrying bananas to the village\n"
335 "and trying to look happy when real food was distributed.\n\n"
336 "Finally you were about to enjoy your free time by taking a quick dip in the\n"
337 "nearby crocodile bay. However, at this point something unusual happened.\n"
338 "You were summoned to the mansion of Richel Decos, the viceroy of the\n"
339 "colony, and were led directly to him."));
340 iosystem::TextScreen(CONST_S(
341 "\"I have a task for you, citizen\", said the viceroy picking his golden\n"
342 "teeth, \"The market price of bananas has taken a deep dive and yet the\n"
343 "central government is about to raise taxes. I have sent appeals to high\n"
344 "priest Petrus but received no response. I fear my enemies in Attnam are\n"
345 "plotting against me and intercepting my messages before they reach him!\"\n\n"
346 "\"That is why you must travel to Attnam with a letter I'll give you and\n"
347 "deliver it to Petrus directly. Alas, you somehow have to cross the sea\n"
348 "between. Because it's winter, all Attnamese ships are trapped by ice and\n"
349 "I have none. Therefore you must venture through the small underwater tunnel\n"
350 "connecting our islands. It is infested with monsters, but since you have\n"
351 "stayed alive here so long, the trip will surely cause you no trouble.\"\n\n"
352 "You have never been so happy! According to the mansion's traveling\n"
353 "brochures, Attnam is a peaceful but bustling world city on a beautiful\n"
354 "snowy fell surrounded by frozen lakes glittering in the arctic sun just\n"
355 "like the diamonds of the imperial treasury. Not that you would believe a\n"
356 "word. The point is that tomorrow you can finally forget your home and\n"
357 "face the untold adventures ahead."));
358 pool::RemoveEverything(); // memory leak!
360 globalwindowhandler::InstallControlLoop(AnimationController
);
363 CausePanicFlag
= false;
366 iosystem::TextScreen(CONST_S("Generating game...\n\nThis may take some time, please wait."), ZERO_V2
, WHITE
, false, true, &BusyAnimation
);
367 igraph::CreateBackGround(GRAY_FRACTAL
);
374 SetPlayer(playerkind::Spawn());
375 Player
->SetAssignedName(PlayerName
);
376 Player
->SetTeam(GetTeam(PLAYER_TEAM
));
377 Player
->SetNP(SATIATED_LEVEL
);
378 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
379 if (c
!= ENDURANCE
) Player
->EditAttribute(c
, (RAND()&1)-(RAND()&1));
380 Player
->EditExperience(c
, 500, 1<<11);
382 Player
->SetMoney(Player
->GetMoney()+RAND()%11);
383 GetTeam(0)->SetLeader(Player
);
387 if (Player
->IsEnabled()) { Player
->Disable(); Player
->Enable(); }
391 SetCurrentArea(WorldMap
= new worldmap(128, 128));
392 CurrentWSquareMap
= WorldMap
->GetMap();
393 WorldMap
->Generate();
395 SendLOSUpdateRequest();
398 InitPlayerAttributeAverage();
407 OmmelBloodMission
= 0;
410 PlayerMassacreMap
.clear();
411 PetMassacreMap
.clear();
412 MiscMassacreMap
.clear();
413 PlayerMassacreAmount
= PetMassacreAmount
= MiscMassacreAmount
= 0;
414 DefaultPolymorphTo
.Empty();
415 DefaultSummonMonster
.Empty();
417 DefaultChangeMaterial
.Empty();
418 DefaultDetectMaterial
.Empty();
420 Player
->GetStack()->AddItem(encryptedscroll::Spawn());
421 if (ivanconfig::GetDefaultPetName() != "_none_") {
422 character
*Doggie
= dog::Spawn();
423 Doggie
->SetTeam(GetTeam(PLAYER_TEAM
));
424 GetWorldMap()->GetPlayerGroup().push_back(Doggie
);
425 Doggie
->SetAssignedName(ivanconfig::GetDefaultPetName());
428 SeeWholeMapCheatMode
= MAP_HIDDEN
;
429 GoThroughWallsCheat
= false;
430 SumoWrestling
= false;
431 GlobalRainTimeModifier
= 2048-(RAND()&4095);
432 PlayerSumoChampion
= false;
433 PlayerSolicitusChampion
= false;
434 protosystem::InitCharacterDataBaseFlags();
435 memset(EquipmentMemory
, 0, sizeof(EquipmentMemory
));
436 PlayerRunning
= false;
437 InitAttributeMemory();
441 TimePlayedBeforeLastLoad
= time::GetZeroTime();
442 /*k8: damn! seems that this is field, not local! bool PlayerHasReceivedAllGodsKnownBonus = false; */
443 PlayerHasReceivedAllGodsKnownBonus
= false;
444 ADD_MESSAGE("You commence your journey to Attnam. Use direction keys to move, '>' to enter an area and '?' to view other commands.");
445 game::ClearEventData();
446 RunOnEvent("game_start");
448 item
*Present
= banana::Spawn();
449 Player
->GetStack()->AddItem(Present
);
450 ADD_MESSAGE("Atavus is happy today! He gives you %s.", Present
->CHAR_NAME(INDEFINITE
));
453 default: return false;
458 void game::DeInit () {
463 for (int c
= 1; c
< Dungeons
; ++c
) delete Dungeon
[c
];
468 for (int c
= 1; c
<= GODS
; ++c
) delete God
[c
]; // sorry, Valpuri!
473 for (int c
= 0; c
< Teams
; ++c
) delete Team
[c
];
487 /* Temporary places */
488 static int Counter
= 0;
489 if (++Counter
== 10) {
490 CurrentLevel
->GenerateMonsters();
493 if (CurrentDungeonIndex
== ELPURI_CAVE
&& CurrentLevelIndex
== ZOMBIE_LEVEL
&& !RAND_N(1000+NecroCounter
)) {
494 character
*Char
= necromancer::Spawn(RAND_N(4) ? APPRENTICE_NECROMANCER
: MASTER_NECROMANCER
);
496 for (int c2
= 0; c2
< 30; ++c2
) {
497 Pos
= GetCurrentLevel()->GetRandomSquare(Char
);
498 if (abs(int(Pos
.X
)-Player
->GetPos().X
) > 20 || abs(int(Pos
.Y
)-Player
->GetPos().Y
) > 20) break;
500 if (Pos
!= ERROR_V2
) {
501 Char
->SetTeam(GetTeam(MONSTER_TEAM
));
503 Char
->SetGenerationDanger(GetCurrentLevel()->GetDifficulty());
504 Char
->SignalGeneration();
505 Char
->SignalNaturalGeneration();
508 int Modifier
= Time
.Day
- EDIT_ATTRIBUTE_DAY_MIN
;
509 if (Modifier
> 0) Char
->EditAllAttributes(Modifier
>> EDIT_ATTRIBUTE_DAY_SHIFT
);
513 //Char->SendToHell(); // k8:equipment
517 if (!(GetTick() % 1000)) CurrentLevel
->CheckSunLight();
519 if ((CurrentDungeonIndex
== NEW_ATTNAM
|| CurrentDungeonIndex
== ATTNAM
) && CurrentLevelIndex
== 0) {
520 sLong OldVolume
= GlobalRainLiquid
->GetVolume();
521 sLong NewVolume
= Max(sLong(sin((Tick
+GlobalRainTimeModifier
)*0.0003)*300-150), 0);
522 if (NewVolume
&& !OldVolume
) CurrentLevel
->EnableGlobalRain();
523 else if(!NewVolume
&& OldVolume
) CurrentLevel
->DisableGlobalRain();
524 GlobalRainLiquid
->SetVolumeNoSignals(NewVolume
);
528 if (!RAND_N(2)) Item = wand::Spawn(1 + RAND_N(12));
529 else if(!RAND_N(2)) {
530 Item = beartrap::Spawn();
531 Item->SetIsActive(true);
532 Item->SetTeam(MONSTER_TEAM);
533 } else if(!RAND_N(2)) {
534 Item = mine::Spawn();
535 Item->SetIsActive(true);
536 Item->SetTeam(MONSTER_TEAM);
537 } else Item = holybanana::Spawn();
538 CurrentLevel->GetLSquare(CurrentLevel->GetRandomSquare())->AddItem(Item);
542 character *Char = protosystem::CreateMonster(0, 1000000);
543 Char->ChangeTeam(GetTeam(RAND() % Teams));
544 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
549 if (!RAND_N(5)) Char = spider::Spawn(GIANT);
550 else if (!RAND_N(5)) Char = darkmage::Spawn(1 + RAND_N(4));
551 else if (!RAND_N(5)) Char = necromancer::Spawn(1 + RAND_N(2));
552 else if (!RAND_N(5)) Char = chameleon::Spawn();
553 else if (!RAND_N(5)) Char = kamikazedwarf::Spawn(1 + RAND_N(GODS));
554 else if (!RAND_N(5)) Char = mommo::Spawn(1 + RAND_N(2));
555 else if (!RAND_N(3)) Char = bunny::Spawn(RAND_2 ? ADULT_MALE : ADULT_FEMALE);
556 else if (!RAND_N(3)) Char = eddy::Spawn();
557 else if (!RAND_N(3)) Char = magicmushroom::Spawn();
558 else if (!RAND_N(5)) Char = mushroom::Spawn();
559 else if (!RAND_N(3)) Char = blinkdog::Spawn();
560 else if (!RAND_N(5)) Char = tourist::Spawn(1 + RAND_N(3));
561 else if (!RAND_N(5)) Char = hattifattener::Spawn();
562 else if (!RAND_N(5)) Char = genetrixvesana::Spawn();
563 else if (!RAND_N(5)) Char = skunk::Spawn();
564 else if (!RAND_N(5)) Char = ennerbeast::Spawn();
565 else if (!RAND_N(5)) Char = werewolfhuman::Spawn();
566 else if (!RAND_N(5)) Char = unicorn::Spawn(1 + RAND_N(3));
567 else if (!RAND_N(5)) Char = floatingeye::Spawn();
568 else if (!RAND_N(5)) Char = zombie::Spawn();
569 else if (!RAND_N(5)) Char = magpie::Spawn();
570 else if (!RAND_N(5)) Char = elpuri::Spawn();
571 else if (!RAND_N(5)) Char = vladimir::Spawn();
572 else if (!RAND_N(5)) Char = billswill::Spawn();
573 else if (!RAND_N(5)) Char = ghost::Spawn();
574 else if (!RAND_N(5)) Char = dolphin::Spawn();
575 else if (!RAND_N(5)) Char = cossack::Spawn();
576 else Char = invisiblestalker::Spawn();
577 Char->SetTeam(GetTeam(RAND() % Teams));
578 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
589 } catch (quitrequest
) {
591 } catch (areachangerequest
) {
597 void game::InitLuxTable () {
599 Alloc3D(LuxTable
, 256, 33, 33);
600 for (int c
= 0; c
< 0x100; ++c
)
601 for (int x
= 0; x
< 33; ++x
)
602 for (int y
= 0; y
< 33; ++y
) {
603 int X
= x
-16, Y
= y
-16;
604 LuxTable
[c
][x
][y
] = int(c
/(double(X
*X
+Y
*Y
)/128+1));
606 atexit(DeInitLuxTable
);
611 void game::DeInitLuxTable () {
617 void game::UpdateCameraX () {
618 UpdateCameraX(Player
->GetPos().X
);
622 void game::UpdateCameraY () {
623 UpdateCameraY(Player
->GetPos().Y
);
627 void game::UpdateCameraX (int X
) {
628 UpdateCameraCoordinate(Camera
.X
, X
, GetCurrentArea()->GetXSize(), GetScreenXSize());
632 void game::UpdateCameraY (int Y
) {
633 UpdateCameraCoordinate(Camera
.Y
, Y
, GetCurrentArea()->GetYSize(), GetScreenYSize());
637 void game::UpdateCameraCoordinate (int &Coordinate
, int Center
, int Size
, int ScreenSize
) {
638 int OldCoordinate
= Coordinate
;
639 if (Size
< ScreenSize
) Coordinate
= (Size
-ScreenSize
)>>1;
640 else if(Center
< ScreenSize
>>1) Coordinate
= 0;
641 else if(Center
> Size
-(ScreenSize
>>1)) Coordinate
= Size
-ScreenSize
;
642 else Coordinate
= Center
-(ScreenSize
>>1);
643 if (Coordinate
!= OldCoordinate
) GetCurrentArea()->SendNewDrawRequest();
647 cchar
*game::Insult () {
648 static const char *insults
[19] = {
661 "stupid-headed person",
665 "person-with-problems",
670 if (n
< 0 || n
> 18) n
= 18;
675 /* DefaultAnswer = REQUIRES_ANSWER the question requires an answer */
676 truth
game::TruthQuestion (cfestring
&String
, int DefaultAnswer
, int OtherKeyForTrue
) {
677 if (DefaultAnswer
== NO
) DefaultAnswer
= 'n';
678 else if (DefaultAnswer
== YES
) DefaultAnswer
= 'y';
679 else if (DefaultAnswer
!= REQUIRES_ANSWER
) ABORT("Illegal TruthQuestion DefaultAnswer send!");
680 int FromKeyQuestion
= KeyQuestion(String
, DefaultAnswer
, 9, 'y', 'Y', 'n', 'N', 't', 'T', 'o', 'O', OtherKeyForTrue
);
682 FromKeyQuestion
== 'y' || FromKeyQuestion
== 'Y' ||
683 FromKeyQuestion
== 't' || FromKeyQuestion
== 'T' ||
684 FromKeyQuestion
== OtherKeyForTrue
;
688 void game::DrawEverything () {
689 DrawEverythingNoBlit();
690 graphics::BlitDBToScreen();
694 truth
game::OnScreen (v2 Pos
) {
695 return Pos
.X
>= 0 && Pos
.Y
>= 0 && Pos
.X
>= Camera
.X
&& Pos
.Y
>= Camera
.Y
&& Pos
.X
< GetCamera().X
+ GetScreenXSize() && Pos
.Y
< GetCamera().Y
+ GetScreenYSize();
699 void game::DrawEverythingNoBlit (truth AnimationDraw
) {
700 if (LOSUpdateRequested
&& Player
->IsEnabled()) {
701 if (!IsInWilderness()) GetCurrentLevel()->UpdateLOS(); else GetWorldMap()->UpdateLOS();
704 if (OnScreen(CursorPos
)) {
705 if (!IsInWilderness() || CurrentWSquareMap
[CursorPos
.X
][CursorPos
.Y
]->GetLastSeen() || GetSeeWholeMapCheatMode())
706 CurrentArea
->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
708 DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, 0);
711 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
712 if (OnScreen(SpecialCursorPos
[c
])) CurrentArea
->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
715 globalwindowhandler::UpdateTick();
716 GetCurrentArea()->Draw(AnimationDraw
);
717 Player
->DrawPanel(AnimationDraw
);
719 if (!AnimationDraw
) msgsystem::Draw();
721 if (OnScreen(CursorPos
)) {
722 v2 ScreenCoord
= CalculateScreenCoordinates(CursorPos
);
726 { ScreenCoord
.X
, ScreenCoord
.Y
},
727 { TILE_SIZE
, TILE_SIZE
},
730 ALLOW_ANIMATE
|ALLOW_ALPHA
733 if (!IsInWilderness() && !GetSeeWholeMapCheatMode()) {
734 lsquare
*Square
= CurrentLSquareMap
[CursorPos
.X
][CursorPos
.Y
];
735 if (Square
->GetLastSeen() != GetLOSTick()) Square
->DrawMemorized(B
);
740 B
.Dest
.X
= RES
.X
- 96;
741 B
.Dest
.Y
= RES
.Y
- 96;
743 DOUBLE_BUFFER
->StretchBlit(B
);
746 igraph::DrawCursor(ScreenCoord
, CursorData
);
749 if (Player
->IsEnabled()) {
750 if (Player
->IsSmall()) {
751 v2 Pos
= Player
->GetPos();
753 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
754 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData());
757 for (int f
= 0; f
< Player
->GetSquaresUnder(); ++f
) {
758 v2 Pos
= Player
->GetPos(f
);
760 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
761 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData()|CURSOR_BIG
, f
);
767 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
768 if (OnScreen(SpecialCursorPos
[c
])) {
769 v2 ScreenCoord
= CalculateScreenCoordinates(SpecialCursorPos
[c
]);
770 igraph::DrawCursor(ScreenCoord
, SpecialCursorData
[c
]);
771 GetCurrentArea()->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
777 truth
game::Save (cfestring
&SaveName
) {
778 if (!GetCurrentArea()->GetSquare(Player
->GetPos())->GetCharacter()) {
779 Menu(0, v2(RES
.X
>> 1, RES
.Y
>> 1), CONST_S("Sorry, can't save due to I.V.A.N. bug.\r"), CONST_S("Continue\r"), LIGHT_GRAY
);
782 DrawEverythingNoBlit();
783 #if defined(SGAME_SHOTS_IPU) || !defined(HAVE_IMLIB2)
784 DOUBLE_BUFFER
->SaveScaledIPU(SaveName
+".ipu", 0.8); //640; 320
786 DOUBLE_BUFFER
->SaveScaledPNG(SaveName
+".png", 0.8); //640; 320
788 outputfile
SaveFile(SaveName
+".sav", ivanconfig::GetUseMaximumCompression());
789 SaveFile
<< int(SAVE_FILE_VERSION
);
790 SaveFile
<< GameScript
<< CurrentDungeonIndex
<< CurrentLevelIndex
<< Camera
;
791 SaveFile
<< WizardMode
<< SeeWholeMapCheatMode
<< GoThroughWallsCheat
;
792 SaveFile
<< Tick
<< Turn
<< InWilderness
<< NextCharacterID
<< NextItemID
<< NextTrapID
<< NecroCounter
;
793 SaveFile
<< SumoWrestling
<< PlayerSumoChampion
<< GlobalRainTimeModifier
;
794 SaveFile
<< PlayerSolicitusChampion
;
796 femath::SetSeed(Seed
);
798 SaveFile
<< AveragePlayerArmStrengthExperience
;
799 SaveFile
<< AveragePlayerLegStrengthExperience
;
800 SaveFile
<< AveragePlayerDexterityExperience
;
801 SaveFile
<< AveragePlayerAgilityExperience
;
802 SaveFile
<< Teams
<< Dungeons
<< StoryState
<< PlayerRunning
;
803 SaveFile
<< MondedrPass
<< RingOfThieves
<< Masamune
<< Muramasa
<< LoricatusHammer
<< Liberator
;
804 SaveFile
<< OmmelBloodMission
<< RegiiTalkState
;
805 SaveFile
<< PlayerMassacreMap
<< PetMassacreMap
<< MiscMassacreMap
;
806 SaveFile
<< PlayerMassacreAmount
<< PetMassacreAmount
<< MiscMassacreAmount
;
807 SaveArray(SaveFile
, EquipmentMemory
, MAX_EQUIPMENT_SLOTS
);
809 for (c
= 0; c
< ATTRIBUTES
; ++c
) SaveFile
<< OldAttribute
[c
] << NewAttribute
[c
] << LastAttributeChangeTick
[c
];
810 for (c
= 1; c
< Dungeons
; ++c
) SaveFile
<< Dungeon
[c
];
811 for (c
= 1; c
<= GODS
; ++c
) SaveFile
<< God
[c
];
812 for (c
= 0; c
< Teams
; ++c
) SaveFile
<< Team
[c
];
814 SaveWorldMap(SaveName
, false);
816 GetCurrentDungeon()->SaveLevel(SaveName
, CurrentLevelIndex
, false);
818 SaveFile
<< Player
->GetPos() << PlayerName
;
819 msgsystem::Save(SaveFile
);
820 SaveFile
<< DangerMap
<< NextDangerIDType
<< NextDangerIDConfigIndex
;
821 SaveFile
<< DefaultPolymorphTo
<< DefaultSummonMonster
;
822 SaveFile
<< DefaultWish
<< DefaultChangeMaterial
<< DefaultDetectMaterial
<< DefaultTeam
;
823 SaveFile
<< GetTimeSpent();
824 /* or in more readable format: time() - LastLoad + TimeAtLastLoad */
825 SaveFile
<< PlayerHasReceivedAllGodsKnownBonus
;
826 protosystem::SaveCharacterDataBaseFlags(SaveFile
);
831 int game::Load (cfestring
&SaveName
) {
832 inputfile
SaveFile(SaveName
+".sav", 0, false);
833 if (!SaveFile
.IsOpen()) return NEW_GAME
;
836 if (Version
!= SAVE_FILE_VERSION
) {
837 if (!iosystem::Menu(0, v2(RES
.X
>> 1, RES
.Y
>> 1), CONST_S("Sorry, this save is incompatible with the new version.\rStart new game?\r"), CONST_S("Yes\rNo\r"), LIGHT_GRAY
)) {
843 SaveFile
>> GameScript
>> CurrentDungeonIndex
>> CurrentLevelIndex
>> Camera
;
844 SaveFile
>> WizardMode
>> SeeWholeMapCheatMode
>> GoThroughWallsCheat
;
845 SaveFile
>> Tick
>> Turn
>> InWilderness
>> NextCharacterID
>> NextItemID
>> NextTrapID
>> NecroCounter
;
846 SaveFile
>> SumoWrestling
>> PlayerSumoChampion
>> GlobalRainTimeModifier
;
847 SaveFile
>> PlayerSolicitusChampion
;
848 femath::SetSeed(ReadType
<sLong
>(SaveFile
));
849 SaveFile
>> AveragePlayerArmStrengthExperience
;
850 SaveFile
>> AveragePlayerLegStrengthExperience
;
851 SaveFile
>> AveragePlayerDexterityExperience
;
852 SaveFile
>> AveragePlayerAgilityExperience
;
853 SaveFile
>> Teams
>> Dungeons
>> StoryState
>> PlayerRunning
;
854 SaveFile
>> MondedrPass
>> RingOfThieves
>> Masamune
>> Muramasa
>> LoricatusHammer
>> Liberator
;
855 SaveFile
>> OmmelBloodMission
>> RegiiTalkState
;
856 SaveFile
>> PlayerMassacreMap
>> PetMassacreMap
>> MiscMassacreMap
;
857 SaveFile
>> PlayerMassacreAmount
>> PetMassacreAmount
>> MiscMassacreAmount
;
858 LoadArray(SaveFile
, EquipmentMemory
, MAX_EQUIPMENT_SLOTS
);
859 for (int c
= 0; c
< ATTRIBUTES
; ++c
) SaveFile
>> OldAttribute
[c
] >> NewAttribute
[c
] >> LastAttributeChangeTick
[c
];
860 Dungeon
= new dungeon
*[Dungeons
];
862 for (int c
= 1; c
< Dungeons
; ++c
) SaveFile
>> Dungeon
[c
];
863 God
= new god
*[GODS
+1];
865 for (int c
= 1; c
<= GODS
; ++c
) SaveFile
>> God
[c
];
866 Team
= new team
*[Teams
];
867 for (int c
= 0; c
< Teams
; ++c
) SaveFile
>> Team
[c
];
869 SetCurrentArea(LoadWorldMap(SaveName
));
870 CurrentWSquareMap
= WorldMap
->GetMap();
871 igraph::CreateBackGround(GRAY_FRACTAL
);
873 SetCurrentArea(CurrentLevel
= GetCurrentDungeon()->LoadLevel(SaveName
, CurrentLevelIndex
));
874 CurrentLSquareMap
= CurrentLevel
->GetMap();
875 igraph::CreateBackGround(*CurrentLevel
->GetLevelScript()->GetBackGroundType());
878 SaveFile
>> Pos
>> PlayerName
;
879 SetPlayer(GetCurrentArea()->GetSquare(Pos
)->GetCharacter());
882 if (!iosystem::Menu(0, v2(RES
.X
>> 1, RES
.Y
>> 1), CONST_S("Sorry, this save is broken due to bug in I.V.A.N.\rStart new game?\r"), CONST_S("Yes\rNo\r"), LIGHT_GRAY
)) {
888 msgsystem::Load(SaveFile
);
889 SaveFile
>> DangerMap
>> NextDangerIDType
>> NextDangerIDConfigIndex
;
890 SaveFile
>> DefaultPolymorphTo
>> DefaultSummonMonster
;
891 SaveFile
>> DefaultWish
>> DefaultChangeMaterial
>> DefaultDetectMaterial
>> DefaultTeam
;
892 SaveFile
>> TimePlayedBeforeLastLoad
;
893 SaveFile
>> PlayerHasReceivedAllGodsKnownBonus
;
895 protosystem::LoadCharacterDataBaseFlags(SaveFile
);
900 festring
game::SaveName (cfestring
&Base
) {
901 festring SaveName
= GetSaveDir();
902 if (!Base
.GetSize()) SaveName
<< PlayerName
; else SaveName
<< Base
;
903 for (festring::sizetype c
= 0; c
< SaveName
.GetSize(); ++c
) if (SaveName
[c
] == ' ') SaveName
[c
] = '_';
908 int game::GetMoveCommandKeyBetweenPoints (v2 A
, v2 B
) {
909 for (int c
= 0; c
< EXTENDED_DIRECTION_COMMAND_KEYS
; ++c
) {
910 if ((A
+ GetMoveVector(c
)) == B
) return GetMoveCommandKey(c
);
916 void game::ApplyDivineTick () {
917 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->ApplyDivineTick();
921 void game::ApplyDivineAlignmentBonuses (god
*CompareTarget
, int Multiplier
, truth Good
) {
922 for (int c
= 1; c
<= GODS
; ++c
) if (GetGod(c
) != CompareTarget
) GetGod(c
)->AdjustRelation(CompareTarget
, Multiplier
, Good
);
926 v2
game::GetDirectionVectorForKey (int Key
) {
927 if (Key
== KEY_NUMPAD_5
|| Key
== '.') return ZERO_V2
; /* k8: '.' */
928 for (int c
= 0; c
< EXTENDED_DIRECTION_COMMAND_KEYS
; ++c
) if (Key
== GetMoveCommandKey(c
)) return GetMoveVector(c
);
933 double game::GetMinDifficulty () {
934 double Base
= CurrentLevel
->GetDifficulty()*0.2;
935 sLong MultiplierExponent
= 0;
938 int Modifier
= Time
.Day
-DANGER_PLUS_DAY_MIN
;
939 if (Modifier
> 0) Base
+= DANGER_PLUS_MULTIPLIER
* Modifier
;
941 int Dice
= RAND()%25;
942 if (Dice
< 5 && MultiplierExponent
> -3) {
944 --MultiplierExponent
;
947 if (Dice
>= 20 && MultiplierExponent
< 3) {
949 ++MultiplierExponent
;
957 void game::ShowLevelMessage () {
958 if (CurrentLevel
->GetLevelMessage().GetSize()) ADD_MESSAGE("%s", CurrentLevel
->GetLevelMessage().CStr());
959 CurrentLevel
->SetLevelMessage("");
963 int game::DirectionQuestion (cfestring
&Topic
, truth RequireAnswer
, truth AcceptYourself
) {
965 int Key
= AskForKeyPress(Topic
);
966 if (AcceptYourself
&& (Key
== '.' || Key
== KEY_NUMPAD_5
)) return YOURSELF
; //k8
967 for (int c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) if (Key
== GetMoveCommandKey(c
)) return c
;
968 if (!RequireAnswer
) return DIR_ERROR
;
973 void game::RemoveSaves (truth RealSavesAlso
) {
975 remove(festring(SaveName()+".sav").CStr());
976 remove(festring(SaveName()+".wm").CStr());
977 remove(festring(SaveName()+".png").CStr());
978 remove(festring(SaveName()+".ipu").CStr());
980 remove(festring(AutoSaveFileName
+".sav").CStr());
981 remove(festring(AutoSaveFileName
+".wm").CStr());
982 remove(festring(AutoSaveFileName
+".png").CStr());
983 remove(festring(AutoSaveFileName
+".ipu").CStr());
985 for (int i
= 1; i
< Dungeons
; ++i
) {
986 for (int c
= 0; c
< GetDungeon(i
)->GetLevels(); ++c
) {
987 /* This looks very odd. And it is very odd.
988 * Indeed, gcc is very odd to not compile this correctly with -O3
989 * if it is written in a less odd way. */
990 File
= SaveName()+'.'+i
;
992 if (RealSavesAlso
) remove(File
.CStr());
993 File
= AutoSaveFileName
+'.'+i
;
1001 void game::SetPlayer (character
*NP
) {
1003 if (Player
) Player
->AddFlags(C_PLAYER
);
1007 void game::InitDungeons () {
1008 Dungeons
= *GetGameScript()->GetDungeons()+1;
1009 //fprintf(stderr, "dungeon count: %d\n", Dungeons);
1010 Dungeon
= new dungeon
*[Dungeons
];
1012 for (int c
= 1; c
< Dungeons
; ++c
) {
1013 Dungeon
[c
] = new dungeon(c
);
1014 Dungeon
[c
]->SetIndex(c
);
1019 void game::DoEvilDeed (int Amount
) {
1020 if (!Amount
) return;
1021 for (int c
= 1; c
<= GODS
; ++c
) {
1022 int Change
= Amount
-Amount
*GetGod(c
)->GetAlignment()/5;
1023 if (!IsInWilderness() && Player
->GetLSquareUnder()->GetDivineMaster() == c
) {
1024 if (GetGod(c
)->GetRelation()-(Change
<< 1) < -750) {
1025 if (GetGod(c
)->GetRelation() > -750) GetGod(c
)->SetRelation(-750);
1026 } else if (GetGod(c
)->GetRelation()-(Change
<< 1) > 750) {
1027 if (GetGod(c
)->GetRelation() < 750) GetGod(c
)->SetRelation(750);
1028 } else GetGod(c
)->SetRelation(GetGod(c
)->GetRelation()-(Change
<< 1));
1030 if(GetGod(c
)->GetRelation()-Change
< -500) {
1031 if (GetGod(c
)->GetRelation() > -500) GetGod(c
)->SetRelation(-500);
1032 } else if (GetGod(c
)->GetRelation()-Change
> 500) {
1033 if (GetGod(c
)->GetRelation() < 500) GetGod(c
)->SetRelation(500);
1034 } else GetGod(c
)->SetRelation(GetGod(c
)->GetRelation() - Change
);
1040 void game::SaveWorldMap (cfestring
&SaveName
, truth DeleteAfterwards
) {
1041 outputfile
SaveFile(SaveName
+".wm", ivanconfig::GetUseMaximumCompression());
1042 SaveFile
<< WorldMap
;
1043 if (DeleteAfterwards
) {
1050 worldmap
*game::LoadWorldMap (cfestring
&SaveName
) {
1051 inputfile
SaveFile(SaveName
+".wm");
1052 SaveFile
>> WorldMap
;
1057 void game::Hostility (team
*Attacker
, team
*Defender
) {
1058 for (int c
= 0; c
< Teams
; ++c
) {
1059 if (GetTeam(c
) != Attacker
&& GetTeam(c
) != Defender
&&
1060 GetTeam(c
)->GetRelation(Defender
) == FRIEND
&&
1061 c
!= NEW_ATTNAM_TEAM
&& c
!= TOURIST_GUIDE_TEAM
) // gum solution
1062 GetTeam(c
)->SetRelation(Attacker
, HOSTILE
);
1067 void game::CreateTeams () {
1068 Teams
= *GetGameScript()->GetTeams();
1069 //fprintf(stderr, "team count: %d\n", Teams);
1070 Team
= new team
*[Teams
];
1071 for (int c
= 0; c
< Teams
; ++c
) {
1072 Team
[c
] = new team(c
);
1073 for (int i
= 0; i
< c
; ++i
) Team
[i
]->SetRelation(Team
[c
], UNCARING
);
1075 for (int c
= 0; c
< Teams
; ++c
) if (c
!= MONSTER_TEAM
) Team
[MONSTER_TEAM
]->SetRelation(Team
[c
], HOSTILE
);
1076 const std::list
<std::pair
<int, teamscript
> >& TeamScript
= GetGameScript()->GetTeam();
1077 for (std::list
<std::pair
<int, teamscript
> >::const_iterator i
= TeamScript
.begin(); i
!= TeamScript
.end(); ++i
) {
1078 for (uInt c
= 0; c
< i
->second
.GetRelation().size(); ++c
) {
1079 GetTeam(i
->second
.GetRelation()[c
].first
)->SetRelation(GetTeam(i
->first
), i
->second
.GetRelation()[c
].second
);
1081 cint
*KillEvilness
= i
->second
.GetKillEvilness();
1082 if (KillEvilness
) GetTeam(i
->first
)->SetKillEvilness(*KillEvilness
);
1083 if (i
->second
.GetName()) GetTeam(i
->first
)->SetName(*i
->second
.GetName());
1088 team
*game::FindTeam (cfestring
&name
) {
1089 for (int c
= 0; c
< Teams
; ++c
) {
1090 if (Team
[c
]->GetName().CompareIgnoreCase(name
) == 0) return Team
[c
];
1096 /* v2 Pos should be removed from xxxQuestion()s? */
1097 festring
game::StringQuestion (cfestring
&Topic
, col16 Color
, festring::sizetype MinLetters
, festring::sizetype MaxLetters
, truth AllowExit
, stringkeyhandler KeyHandler
) {
1098 DrawEverythingNoBlit();
1099 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); // pos may be incorrect!
1101 iosystem::StringQuestion(Return
, Topic
, v2(16, 6), Color
, MinLetters
, MaxLetters
, false, AllowExit
, KeyHandler
);
1102 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1107 sLong
game::NumberQuestion (cfestring
&Topic
, col16 Color
, truth ReturnZeroOnEsc
) {
1108 DrawEverythingNoBlit();
1109 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1110 sLong Return
= iosystem::NumberQuestion(Topic
, v2(16, 6), Color
, false, ReturnZeroOnEsc
);
1111 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1116 sLong
game::ScrollBarQuestion (cfestring
&Topic
, sLong BeginValue
, sLong Step
, sLong Min
, sLong Max
, sLong AbortValue
, col16 TopicColor
, col16 Color1
, col16 Color2
, void (*Handler
)(sLong
)) {
1117 DrawEverythingNoBlit();
1118 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1119 sLong Return
= iosystem::ScrollBarQuestion(Topic
, v2(16, 6), BeginValue
, Step
, Min
, Max
, AbortValue
, TopicColor
, Color1
, Color2
, GetMoveCommandKey(KEY_LEFT_INDEX
), GetMoveCommandKey(KEY_RIGHT_INDEX
), false, Handler
);
1120 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1125 feuLong
game::IncreaseLOSTick () {
1126 if (LOSTick
!= 0xFE) return LOSTick
+= 2;
1127 CurrentLevel
->InitLastSeen();
1132 void game::UpdateCamera () {
1138 truth
game::HandleQuitMessage () {
1140 if (IsInGetCommand()) {
1141 switch (Menu(0, v2(RES
.X
>> 1, RES
.Y
>> 1), CONST_S("Do you want to save your game before quitting?\r"), CONST_S("Yes\rNo\rCancel\r"), LIGHT_GRAY
)) {
1147 GetCurrentArea()->SendNewDrawRequest();
1151 festring Msg
= CONST_S("cowardly quit the game");
1152 Player
->AddScoreEntry(Msg
, 0.75);
1153 End(Msg
, true, false);
1156 } else if (!Menu(0, v2(RES
.X
>> 1, RES
.Y
>> 1), CONST_S("You can't save at this point. Are you sure you still want to do this?\r"), CONST_S("Yes\rNo\r"), LIGHT_GRAY
)) {
1159 GetCurrentArea()->SendNewDrawRequest();
1168 int game::GetDirectionForVector (v2 Vector
) {
1169 for (int c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) if (Vector
== GetMoveVector(c
)) return c
;
1174 cchar
*game::GetVerbalPlayerAlignment () {
1176 for (int c
= 1; c
<= GODS
; ++c
) {
1177 if (GetGod(c
)->GetRelation() > 0) Sum
+= GetGod(c
)->GetRelation() * (5 - GetGod(c
)->GetAlignment());
1179 if (Sum
> 15000) return "extremely lawful";
1180 if (Sum
> 10000) return "very lawful";
1181 if (Sum
> 5000) return "lawful";
1182 if (Sum
> 1000) return "mildly lawful";
1183 if (Sum
> -1000) return "neutral";
1184 if (Sum
> -5000) return "mildly chaotic";
1185 if (Sum
> -10000) return "chaotic";
1186 if (Sum
> -15000) return "very chaotic";
1187 return "extremely chaotic";
1191 void game::CreateGods () {
1192 God
= new god
*[GODS
+1];
1194 for (int c
= 1; c
< protocontainer
<god
>::GetSize(); ++c
) God
[c
] = protocontainer
<god
>::GetProto(c
)->Spawn();
1198 void game::BusyAnimation () {
1199 BusyAnimation(DOUBLE_BUFFER
, false);
1203 void game::BusyAnimation (bitmap
*Buffer
, truth ForceDraw
) {
1204 static clock_t LastTime
= 0;
1205 static int Frame
= 0;
1206 static blitdata B1
= {
1215 static blitdata B2
= {
1218 { (RES
.X
>> 1) - 100, (RES
.Y
<< 1) / 3 - 100 },
1224 if (ForceDraw
|| clock()-LastTime
> CLOCKS_PER_SEC
/25) {
1226 B2
.Dest
.X
= (RES
.X
>>1)-100+EnterTextDisplacement
.X
;
1227 B2
.Dest
.Y
= (RES
.Y
<<1)/3-100+EnterTextDisplacement
.Y
;
1230 EnterImage
->NormalMaskedBlit(B1
);
1232 BusyAnimationCache
[Frame
]->NormalBlit(B2
);
1233 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
1234 if (++Frame
== 32) Frame
= 0;
1240 void game::CreateBusyAnimationCache () {
1241 bitmap
Elpuri(TILE_V2
, TRANSPARENT_COLOR
);
1242 Elpuri
.ActivateFastFlag();
1243 packcol16 Color
= MakeRGB16(60, 60, 60);
1244 igraph::GetCharacterRawGraphic()->MaskedBlit(&Elpuri
, v2(64, 0), ZERO_V2
, TILE_V2
, &Color
);
1245 bitmap
Circle(v2(200, 200), TRANSPARENT_COLOR
);
1246 Circle
.ActivateFastFlag();
1247 for (int x
= 0; x
< 4; ++x
) Circle
.DrawPolygon(100, 100, 95+x
, 50, MakeRGB16(255-12*x
, 0, 0));
1252 { TILE_SIZE
, TILE_SIZE
},
1266 for (int c
= 0; c
< 32; ++c
) {
1267 B1
.Bitmap
= B2
.Bitmap
= BusyAnimationCache
[c
] = new bitmap(v2(200, 200), 0);
1268 B1
.Bitmap
->ActivateFastFlag();
1269 Elpuri
.NormalMaskedBlit(B1
);
1270 double Rotation
= 0.3+c
*FPI
/80;
1271 for (int x
= 0; x
< 10; ++x
) B1
.Bitmap
->DrawPolygon(100, 100, 95, 5, MakeRGB16(5+25*x
, 0, 0), false, true, Rotation
+double(x
)/50);
1272 Circle
.NormalMaskedBlit(B2
);
1277 int game::AskForKeyPress (cfestring
&Topic
) {
1278 DrawEverythingNoBlit();
1279 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CapitalizeCopy().CStr());
1280 graphics::BlitDBToScreen();
1281 int Key
= GET_KEY();
1282 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1287 void game::AskForEscPress (cfestring
&Topic
) {
1288 DrawEverythingNoBlit();
1289 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), RED
/*WHITE*/, "%s [press ESC]", Topic
.CapitalizeCopy().CStr());
1290 graphics::BlitDBToScreen();
1294 } while (Key
!= KEY_ESC
);
1295 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1299 /* Handler is called when the key has been identified as a movement key
1300 * KeyHandler is called when the key has NOT been identified as a movement key
1301 * Both can be deactivated by passing 0 as parameter */
1302 v2
game::PositionQuestion (cfestring
&Topic
, v2 CursorPos
, void (*Handler
)(v2
), positionkeyhandler KeyHandler
, truth Zoom
) {
1306 CursorData
= RED_CURSOR
;
1307 if (Handler
) Handler(CursorPos
);
1309 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1310 if (!Square
->HasBeenSeen() &&
1311 (!Square
->GetCharacter() || !Square
->GetCharacter()->CanBeSeenByPlayer()) &&
1312 !GetSeeWholeMapCheatMode()) DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, BLACK
);
1313 else GetCurrentArea()->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
1315 if (Key
== ' ' || Key
== '.' || Key
== KEY_NUMPAD_5
) { Return
= CursorPos
; break; }
1316 if (Key
== KEY_ESC
) { Return
= ERROR_V2
; break; }
1318 v2 DirectionVector
= GetDirectionVectorForKey(Key
);
1319 if (DirectionVector
!= ERROR_V2
) {
1320 CursorPos
+= DirectionVector
;
1321 if (CursorPos
.X
> GetCurrentArea()->GetXSize()-1) CursorPos
.X
= 0;
1322 if (CursorPos
.X
< 0) CursorPos
.X
= GetCurrentArea()->GetXSize()-1;
1323 if (CursorPos
.Y
> GetCurrentArea()->GetYSize()-1) CursorPos
.Y
= 0;
1324 if (CursorPos
.Y
< 0) CursorPos
.Y
= GetCurrentArea()->GetYSize()-1;
1325 if (Handler
) Handler(CursorPos
);
1326 } else if (KeyHandler
) {
1327 CursorPos
= KeyHandler(CursorPos
, Key
);
1328 if (CursorPos
== ERROR_V2
|| CursorPos
== ABORT_V2
) {
1334 if (ivanconfig::GetAutoCenterMapOnLook()) {
1335 UpdateCameraX(CursorPos
.X
);
1336 UpdateCameraY(CursorPos
.Y
);
1338 if (CursorPos
.X
< GetCamera().X
+3 || CursorPos
.X
>= GetCamera().X
+GetScreenXSize()-3) UpdateCameraX(CursorPos
.X
);
1339 if (CursorPos
.Y
< GetCamera().Y
+3 || CursorPos
.Y
>= GetCamera().Y
+GetScreenYSize()-3) UpdateCameraY(CursorPos
.Y
);
1342 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CStr());
1343 SetCursorPos(CursorPos
);
1348 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1349 igraph::BlitBackGround(v2(RES
.X
-96, RES
.Y
-96), v2(80, 80));
1351 SetCursorPos(v2(-1, -1));
1356 void game::LookHandler (v2 CursorPos
) {
1357 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1360 if (GetSeeWholeMapCheatMode()) {
1361 OldMemory
= Square
->GetMemorizedDescription();
1362 if (IsInWilderness()) GetWorldMap()->GetWSquare(CursorPos
)->UpdateMemorizedDescription(true);
1363 else GetCurrentLevel()->GetLSquare(CursorPos
)->UpdateMemorizedDescription(true);
1367 if (Square
->HasBeenSeen() || GetSeeWholeMapCheatMode()) {
1368 if (!IsInWilderness() && !Square
->CanBeSeenByPlayer() && GetCurrentLevel()->GetLSquare(CursorPos
)->CanBeFeltByPlayer())
1369 Msg
= CONST_S("You feel here ");
1370 else if (Square
->CanBeSeenByPlayer(true) || GetSeeWholeMapCheatMode())
1371 Msg
= CONST_S("You see here ");
1373 Msg
= CONST_S("You remember here ");
1374 Msg
<< Square
->GetMemorizedDescription() << '.';
1375 if (!IsInWilderness() && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) {
1376 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1377 LSquare
->DisplaySmokeInfo(Msg
);
1378 if (LSquare
->HasEngravings() && LSquare
->IsTransparent()) {
1379 if (LSquare
->EngravingsCanBeReadByPlayer() || GetSeeWholeMapCheatMode()) LSquare
->DisplayEngravedInfo(Msg
);
1380 else Msg
<< " Something has been engraved here.";
1383 } else Msg
= CONST_S("You have never been here.");
1384 character
*Character
= Square
->GetCharacter();
1385 if (Character
&& (Character
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Character
->DisplayInfo(Msg
);
1386 if (!(RAND()%10000) && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Msg
<< " You see here a frog eating a magnolia.";
1387 ADD_MESSAGE("%s", Msg
.CStr());
1388 if (GetSeeWholeMapCheatMode()) Square
->SetMemorizedDescription(OldMemory
);
1392 truth
game::AnimationController () {
1393 DrawEverythingNoBlit(true);
1398 void game::LoadGlobalValueMap (inputfile
&fl
) {
1400 fl
.setGetVarCB(game::ldrGetVar
);
1401 for (fl
.ReadWord(word
, false); !fl
.Eof(); fl
.ReadWord(word
, false)) {
1402 if (word
== "Include") {
1403 word
= fl
.ReadWord();
1404 if (fl
.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1405 //fprintf(stderr, "loading: %s\n", word.CStr());
1406 inputfile
incf(game::GetGameDir()+"Script/"+word
, &game::GetGlobalValueMap());
1407 LoadGlobalValueMap(incf
);
1410 if (word
== "Message") {
1411 word
= fl
.ReadWord();
1412 if (fl
.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1413 fprintf(stderr
, "MESSAGE: %s\n", word
.CStr());
1416 if (word
!= "#") ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1417 fl
.ReadWord(word
, true);
1418 if (word
== "enum" || word
== "bitenum") {
1419 truth isBit
= word
== "bitenum";
1421 if (fl
.ReadWord() != "{") ABORT("'{' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1425 fl
.ReadWord(word
, true);
1426 if (word
== "}") break;
1431 fl
.ReadWord(word
, true);
1434 // set current index
1435 idx
= fl
.ReadNumber();
1437 if (word
!= "," && word
!= ";" && word
!= "}") ABORT("',' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1438 if (word
== "}") done
= true;
1440 if (idName
.GetSize() > 0) {
1442 if (isBit
) i
= 1<<i
;
1443 GlobalValueMap
.insert(std::make_pair(idName
, i
));
1449 if (ch
!= EOF
&& ch
!= ';') fl
.Unget(ch
);
1450 //if (fl.ReadWord() != ";") ABORT("';' expected in file %s at line %d!", fl.GetFileName().CStr(), fl.TokenLine());
1453 if (word
== "define") {
1455 sLong v
= fl
.ReadNumber();
1456 GlobalValueMap
.insert(std::make_pair(word
, v
));
1459 ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1464 void game::InitGlobalValueMap () {
1465 inputfile
SaveFile(GetGameDir()+"Script/define.dat", &GlobalValueMap
);
1466 LoadGlobalValueMap(SaveFile
);
1467 { /* additional files */
1468 for (int f
= 0; f
<= 99; f
++) {
1470 sprintf(bnum
, "Script/define_%02d.dat", f
);
1471 festring fn
= game::GetGameDir();
1473 if (inputfile::fileExists(fn
)) return;
1474 inputfile
ifl(fn
, &game::GetGlobalValueMap(), false);
1476 LoadGlobalValueMap(ifl
);
1484 void game::TextScreen (cfestring
&Text
, v2 Displacement
, col16 Color
, truth GKey
, truth Fade
, bitmapeditor BitmapEditor
) {
1485 globalwindowhandler::DisableControlLoops();
1486 iosystem::TextScreen(Text
, Displacement
, Color
, GKey
, Fade
, BitmapEditor
);
1487 globalwindowhandler::EnableControlLoops();
1491 /* ... all the keys that are acceptable
1492 DefaultAnswer = REQUIRES_ANSWER if this question requires an answer
1493 Not surprisingly KeyNumber is the number of keys at ...
1495 int game::KeyQuestion (cfestring
&Message
, int DefaultAnswer
, int KeyNumber
, ...) {
1496 int *Key
= new int[KeyNumber
];
1498 va_start(Arguments
, KeyNumber
);
1499 for (int c
= 0; c
< KeyNumber
; ++c
) Key
[c
] = va_arg(Arguments
, int);
1501 DrawEverythingNoBlit();
1502 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Message
.CStr());
1503 graphics::BlitDBToScreen();
1507 for (int c
= 0; c
< KeyNumber
; ++c
) {
1513 if (!Return
&& DefaultAnswer
!= REQUIRES_ANSWER
) Return
= DefaultAnswer
;
1516 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1521 v2
game::LookKeyHandler (v2 CursorPos
, int Key
) {
1522 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1525 if (!IsInWilderness()) {
1526 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1527 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1528 stack
*Stack
= LSquare
->GetStack();
1529 if (LSquare
->IsTransparent() && Stack
->GetVisibleItems(Player
))
1530 Stack
->DrawContents(Player
, "Items here", NO_SELECT
|(GetSeeWholeMapCheatMode() ? 0 : NO_SPECIAL_INFO
));
1532 ADD_MESSAGE("You see no items here.");
1533 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1537 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1538 character
*Char
= Square
->GetCharacter();
1539 if (Char
&& (Char
->CanBeSeenByPlayer() || Char
->IsPlayer() || GetSeeWholeMapCheatMode()))
1542 ADD_MESSAGE("You see no one here.");
1543 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1550 v2
game::NameKeyHandler (v2 CursorPos
, int Key
) {
1551 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
1552 if (Key
== 'n' || Key
== 'N') {
1553 character
*Char
= GetCurrentArea()->GetSquare(CursorPos
)->GetCharacter();
1554 if (Char
&& Char
->CanBeSeenByPlayer()) Char
->TryToName();
1555 else ADD_MESSAGE("You don't see anyone here to name.");
1561 void game::End (festring DeathMessage
, truth Permanently
, truth AndGoToMenu
) {
1563 globalwindowhandler::DeInstallControlLoop(AnimationController
);
1564 SetIsRunning(false);
1565 if (Permanently
|| !WizardModeIsReallyActive()) RemoveSaves(Permanently
);
1566 if (Permanently
&& !WizardModeIsReallyActive()) {
1569 if (HScore
.LastAddFailed()) {
1570 iosystem::TextScreen(CONST_S("You didn't manage to get onto the high score list.\n\n\n\n")+GetPlayerName()+", "+DeathMessage
+"\nRIP");
1576 /* This prevents monster movement etc. after death. */
1577 throw quitrequest();
1582 int game::CalculateRoughDirection (v2 Vector
) {
1583 if (!Vector
.X
&& !Vector
.Y
) return YOURSELF
;
1584 double Angle
= femath::CalculateAngle(Vector
);
1585 if (Angle
< FPI
/ 8) return 4;
1586 else if (Angle
< 3*FPI
/8) return 7;
1587 else if (Angle
< 5*FPI
/8) return 6;
1588 else if (Angle
< 7*FPI
/8) return 5;
1589 else if (Angle
< 9*FPI
/8) return 3;
1590 else if (Angle
< 11*FPI
/8) return 0;
1591 else if (Angle
< 13*FPI
/8) return 1;
1592 else if (Angle
< 15*FPI
/8) return 2;
1597 int game::Menu (bitmap
*BackGround
, v2 Pos
, cfestring
&Topic
, cfestring
&sMS
, col16 Color
, cfestring
&SmallText1
, cfestring
&SmallText2
) {
1598 globalwindowhandler::DisableControlLoops();
1599 int Return
= iosystem::Menu(BackGround
, Pos
, Topic
, sMS
, Color
, SmallText1
, SmallText2
);
1600 globalwindowhandler::EnableControlLoops();
1605 void game::InitDangerMap () {
1607 pool::RegisterState(false);
1608 //fprintf(stderr, "game::InitDangerMap(): START\n");
1609 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
1611 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
1612 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1613 int ConfigSize
= Proto
->GetConfigSize();
1614 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
1615 if (!ConfigData
[c2
]->IsAbstract
) {
1616 int Config
= ConfigData
[c2
]->Config
;
1619 NextDangerIDType
= c1
;
1620 NextDangerIDConfigIndex
= c2
;
1623 character
*Char
= Proto
->Spawn(Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1624 double NakedDanger
= Char
->GetRelativeDanger(Player
, true);
1625 //fprintf(stderr, " game::InitDangerMap(): 00\n");
1627 //Char->SendToHell(); // k8: equipment
1628 Char
= Proto
->Spawn(Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1629 double EquippedDanger
= Char
->GetRelativeDanger(Player
, true);
1630 //fprintf(stderr, " game::InitDangerMap(): 01\n");
1632 //Char->SendToHell(); // k8: equipment
1633 DangerMap
[configid(c1
, Config
)] = dangerid(NakedDanger
, EquippedDanger
);
1637 pool::RegisterState(true);
1638 //fprintf(stderr, "game::InitDangerMap(): DONE\n");
1642 void game::CalculateNextDanger () {
1643 if (IsInWilderness() || !*CurrentLevel
->GetLevelScript()->GenerateMonsters()) return;
1644 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1645 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1646 const character::database
*DataBase
= ConfigData
[NextDangerIDConfigIndex
];
1647 dangermap::iterator DangerIterator
= DangerMap
.find(configid(NextDangerIDType
, DataBase
->Config
));
1648 team
*Team
= GetTeam(PLAYER_TEAM
);
1649 if (DataBase
&& DangerIterator
!= DangerMap
.end()) {
1650 //fprintf(stderr, "game::CalculateNextDanger(): START\n");
1651 pool::RegisterState(false);
1652 character
*Char
= Proto
->Spawn(DataBase
->Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1653 std::list
<character
*>::const_iterator i
;
1654 double DangerSum
= Player
->GetRelativeDanger(Char
, true);
1655 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
) {
1656 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10)) DangerSum
+= (*i
)->GetRelativeDanger(Char
, true)/4;
1658 double CurrentDanger
= 1/DangerSum
;
1659 double NakedDanger
= DangerIterator
->second
.NakedDanger
;
1660 //fprintf(stderr, " game::CalculateNextDanger(): 00\n");
1662 //Char->SendToHell(); // k8: equipment
1663 if (NakedDanger
> CurrentDanger
) DangerIterator
->second
.NakedDanger
= (NakedDanger
*9+CurrentDanger
)/10;
1664 Char
= Proto
->Spawn(DataBase
->Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1665 DangerSum
= Player
->GetRelativeDanger(Char
, true);
1666 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
) {
1667 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10)) DangerSum
+= (*i
)->GetRelativeDanger(Char
, true) / 4;
1669 CurrentDanger
= 1/DangerSum
;
1670 double EquippedDanger
= DangerIterator
->second
.EquippedDanger
;
1671 //fprintf(stderr, " game::CalculateNextDanger(): 01\n");
1673 //Char->SendToHell(); // k8: equipment
1674 pool::RegisterState(true);
1675 if (EquippedDanger
> CurrentDanger
) DangerIterator
->second
.EquippedDanger
= (EquippedDanger
*9+CurrentDanger
)/10;
1676 if (++NextDangerIDConfigIndex
< Proto
->GetConfigSize()) {
1677 //fprintf(stderr, "game::CalculateNextDanger(): EXIT0\n");
1681 if (++NextDangerIDType
>= protocontainer
<character
>::GetSize()) NextDangerIDType
= 1;
1682 Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1683 ConfigData
= Proto
->GetConfigData();
1684 int ConfigSize
= Proto
->GetConfigSize();
1686 for (int c
= 0; c
< ConfigSize
; ++c
) {
1687 if (!ConfigData
[c
]->IsAbstract
) {
1688 NextDangerIDConfigIndex
= c
;
1689 //fprintf(stderr, "game::CalculateNextDanger(): EXIT1\n");
1694 //fprintf(stderr, "game::CalculateNextDanger(): DONE\n");
1696 ABORT("It is dangerous to go ice fishing in the summer.");
1701 truth
game::TryTravel (int Dungeon
, int Area
, int EntryIndex
, truth AllowHostiles
, truth AlliesFollow
) {
1702 charactervector Group
;
1703 if (LeaveArea(Group
, AllowHostiles
, AlliesFollow
)) {
1704 CurrentDungeonIndex
= Dungeon
;
1705 EnterArea(Group
, Area
, EntryIndex
);
1712 truth
game::LeaveArea (charactervector
&Group
, truth AllowHostiles
, truth AlliesFollow
) {
1713 if (!IsInWilderness()) {
1714 if (AlliesFollow
&& !GetCurrentLevel()->CollectCreatures(Group
, Player
, AllowHostiles
)) return false;
1716 GetCurrentDungeon()->SaveLevel(SaveName(), CurrentLevelIndex
);
1719 GetWorldMap()->GetPlayerGroup().swap(Group
);
1726 /* Used always when the player enters an area. */
1727 void game::EnterArea (charactervector
&Group
, int Area
, int EntryIndex
) {
1728 if (Area
!= WORLD_MAP
) {
1730 SetIsInWilderness(false);
1731 CurrentLevelIndex
= Area
;
1732 truth New
= !PrepareRandomBone(Area
) && !GetCurrentDungeon()->PrepareLevel(Area
);
1733 igraph::CreateBackGround(*CurrentLevel
->GetLevelScript()->GetBackGroundType());
1734 GetCurrentArea()->SendNewDrawRequest();
1735 v2 Pos
= GetCurrentLevel()->GetEntryPos(Player
, EntryIndex
);
1737 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
1738 Player
->PutToOrNear(Pos
);
1739 } else SetPlayer(GetCurrentLevel()->GetLSquare(Pos
)->GetCharacter());
1741 for (c
= 0; c
< Group
.size(); ++c
) {
1742 v2 NPCPos
= GetCurrentLevel()->GetNearestFreeSquare(Group
[c
], Pos
);
1743 if (NPCPos
== ERROR_V2
) NPCPos
= GetCurrentLevel()->GetRandomSquare(Group
[c
]);
1744 Group
[c
]->PutTo(NPCPos
);
1746 GetCurrentLevel()->FiatLux();
1747 ctruth
*AutoReveal
= GetCurrentLevel()->GetLevelScript()->AutoReveal();
1748 if (New
&& AutoReveal
&& *AutoReveal
) GetCurrentLevel()->Reveal();
1750 SendLOSUpdateRequest();
1754 if (New
&& CurrentDungeonIndex
== ATTNAM
&& Area
== 0) {
1755 GlobalRainLiquid
= powder::Spawn(SNOW
);
1756 GlobalRainSpeed
= v2(-64, 128);
1757 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1760 if (New
&& CurrentDungeonIndex
== NEW_ATTNAM
&& Area
== 0) {
1761 GlobalRainLiquid
= liquid::Spawn(WATER
);
1762 GlobalRainSpeed
= v2(256, 512);
1763 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1766 if (New
&& CurrentDungeonIndex
== ELPURI_CAVE
&& Area
== OREE_LAIR
) {
1767 GlobalRainLiquid
= liquid::Spawn(BLOOD
);
1768 GlobalRainSpeed
= v2(256, 512);
1769 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1770 GlobalRainLiquid
->SetVolumeNoSignals(200);
1771 CurrentLevel
->EnableGlobalRain();
1774 if (New
&& CurrentDungeonIndex
== MUNTUO
&& Area
== 0) {
1775 GlobalRainLiquid
= liquid::Spawn(WATER
);
1776 GlobalRainSpeed
= v2(-64, 1024);
1777 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1781 GetCurrentLevel()->UpdateLOS();
1782 Player
->SignalStepFrom(0);
1784 for (c
= 0; c
< Group
.size(); ++c
) Group
[c
]->SignalStepFrom(0);
1786 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1788 igraph::CreateBackGround(GRAY_FRACTAL
);
1789 SetIsInWilderness(true);
1791 SetCurrentArea(WorldMap
);
1792 CurrentWSquareMap
= WorldMap
->GetMap();
1793 GetWorldMap()->GetPlayerGroup().swap(Group
);
1794 Player
->PutTo(GetWorldMap()->GetEntryPos(Player
, EntryIndex
));
1795 SendLOSUpdateRequest();
1797 GetWorldMap()->UpdateLOS();
1798 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1803 int game::CompareLightToInt (col24 L
, col24 Int
) {
1804 if ((L
& 0xFF0000) > Int
|| (L
& 0xFF00) > Int
|| (L
& 0xFF) > Int
) return 1;
1805 if ((L
& 0xFF0000) == Int
|| (L
& 0xFF00) == Int
|| (L
& 0xFF) == Int
) return 0;
1810 void game::SetStandardListAttributes (felist
&List
) {
1811 List
.SetPos(v2(26, 42));
1813 List
.SetFlags(DRAW_BACKGROUND_AFTERWARDS
);
1814 List
.SetUpKey(GetMoveCommandKey(KEY_UP_INDEX
));
1815 List
.SetDownKey(GetMoveCommandKey(KEY_DOWN_INDEX
));
1819 void game::InitPlayerAttributeAverage () {
1820 AveragePlayerArmStrengthExperience
1821 = AveragePlayerLegStrengthExperience
1822 = AveragePlayerDexterityExperience
1823 = AveragePlayerAgilityExperience
1826 if (!Player
->IsHumanoid()) return;
1828 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1831 arm
*RightArm
= Player
->GetRightArm();
1833 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1834 AveragePlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1835 AveragePlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1839 arm
*LeftArm
= Player
->GetLeftArm();
1841 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1842 AveragePlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1843 AveragePlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1847 leg
*RightLeg
= Player
->GetRightLeg();
1849 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1850 AveragePlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1851 AveragePlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1855 leg
*LeftLeg
= Player
->GetLeftLeg();
1857 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1858 AveragePlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1859 AveragePlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1864 AveragePlayerArmStrengthExperience
/= Arms
;
1865 AveragePlayerDexterityExperience
/= Arms
;
1869 AveragePlayerLegStrengthExperience
/= Legs
;
1870 AveragePlayerAgilityExperience
/= Legs
;
1875 void game::UpdatePlayerAttributeAverage () {
1876 if (!Player
->IsHumanoid()) return;
1878 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1879 double PlayerArmStrengthExperience
= 0;
1880 double PlayerLegStrengthExperience
= 0;
1881 double PlayerDexterityExperience
= 0;
1882 double PlayerAgilityExperience
= 0;
1885 arm
*RightArm
= Player
->GetRightArm();
1887 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1888 PlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1889 PlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1893 arm
*LeftArm
= Player
->GetLeftArm();
1895 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1896 PlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1897 PlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1901 leg
*RightLeg
= Player
->GetRightLeg();
1903 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1904 PlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1905 PlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1909 leg
*LeftLeg
= Player
->GetLeftLeg();
1911 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1912 PlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1913 PlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1918 AveragePlayerArmStrengthExperience
= (49 * AveragePlayerArmStrengthExperience
+ PlayerArmStrengthExperience
/ Arms
) / 50;
1919 AveragePlayerDexterityExperience
= (49 * AveragePlayerDexterityExperience
+ PlayerDexterityExperience
/ Arms
) / 50;
1923 AveragePlayerLegStrengthExperience
= (49 * AveragePlayerLegStrengthExperience
+ PlayerLegStrengthExperience
/ Legs
) / 50;
1924 AveragePlayerAgilityExperience
= (49 * AveragePlayerAgilityExperience
+ PlayerAgilityExperience
/ Legs
) / 50;
1929 void game::CallForAttention (v2 Pos
, int RangeSquare
) {
1930 for (int c
= 0; c
< GetTeams(); ++c
) {
1931 if (GetTeam(c
)->HasEnemy())
1932 for (std::list
<character
*>::const_iterator i
= GetTeam(c
)->GetMember().begin(); i
!= GetTeam(c
)->GetMember().end(); ++i
)
1933 if ((*i
)->IsEnabled()) {
1934 sLong ThisDistance
= HypotSquare(sLong((*i
)->GetPos().X
) - Pos
.X
, sLong((*i
)->GetPos().Y
) - Pos
.Y
);
1935 if (ThisDistance
<= RangeSquare
&& !(*i
)->IsGoingSomeWhere()) (*i
)->SetGoingTo(Pos
);
1941 outputfile
&operator << (outputfile
&SaveFile
, const homedata
*HomeData
) {
1944 SaveFile
<< HomeData
->Pos
<< HomeData
->Dungeon
<< HomeData
->Level
<< HomeData
->Room
;
1945 } else SaveFile
.Put(0);
1950 inputfile
&operator >> (inputfile
&SaveFile
, homedata
*&HomeData
) {
1951 if (SaveFile
.Get()) {
1952 HomeData
= new homedata
;
1953 SaveFile
>> HomeData
->Pos
>> HomeData
->Dungeon
>> HomeData
->Level
>> HomeData
->Room
;
1959 feuLong
game::CreateNewCharacterID (character
*NewChar
) {
1960 feuLong ID
= NextCharacterID
++;
1961 /*k8:??? if(CharacterIDMap.find(ID) != CharacterIDMap.end())
1962 int esko = esko = 2;*/
1963 CharacterIDMap
.insert(std::make_pair(ID
, NewChar
));
1968 feuLong
game::CreateNewItemID (item
*NewItem
) {
1969 feuLong ID
= NextItemID
++;
1970 /*k8:??? if(ItemIDMap.find(ID) != ItemIDMap.end())
1971 int esko = esko = 2;*/
1972 if (NewItem
) ItemIDMap
.insert(std::make_pair(ID
, NewItem
));
1977 feuLong
game::CreateNewTrapID (entity
*NewTrap
) {
1978 feuLong ID
= NextTrapID
++;
1979 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
1980 int esko = esko = 2;*/
1981 if (NewTrap
) TrapIDMap
.insert(std::make_pair(ID
, NewTrap
));
1986 character
*game::SearchCharacter (feuLong ID
) {
1987 characteridmap::iterator Iterator
= CharacterIDMap
.find(ID
);
1988 return Iterator
!= CharacterIDMap
.end() ? Iterator
->second
: 0;
1992 item
*game::SearchItem (feuLong ID
) {
1993 itemidmap::iterator Iterator
= ItemIDMap
.find(ID
);
1994 return Iterator
!= ItemIDMap
.end() ? Iterator
->second
: 0;
1998 entity
*game::SearchTrap (feuLong ID
) {
1999 trapidmap::iterator Iterator
= TrapIDMap
.find(ID
);
2000 return Iterator
!= TrapIDMap
.end() ? Iterator
->second
: 0;
2004 outputfile
&operator << (outputfile
&SaveFile
, const configid
&Value
) {
2005 SaveFile
.Write(reinterpret_cast<cchar
*>(&Value
), sizeof(Value
));
2010 inputfile
&operator >> (inputfile
&SaveFile
, configid
&Value
) {
2011 SaveFile
.Read(reinterpret_cast<char*>(&Value
), sizeof(Value
));
2016 outputfile
&operator << (outputfile
&SaveFile
, const dangerid
&Value
) {
2017 SaveFile
<< Value
.NakedDanger
<< Value
.EquippedDanger
;
2022 inputfile
&operator >> (inputfile
&SaveFile
, dangerid
&Value
) {
2023 SaveFile
>> Value
.NakedDanger
>> Value
.EquippedDanger
;
2028 /* The program can only create directories to the deepness of one, no more... */
2029 festring
game::GetHomeDir () {
2031 Dir
<< getenv("HOME") << '/';
2036 festring
game::GetSaveDir () {
2039 Dir
<< ivanconfig::GetMyDir() << "/Save/";
2041 Dir
<< getenv("HOME") << "/IvanSave/";
2047 festring
game::GetGameDir () {
2048 /*k8! return DATADIR "/ivan/"; */
2049 /*k8! return DATADIR "/"; */
2051 Dir
<< ivanconfig::GetMyDir() << "/";
2056 festring
game::GetBoneDir () {
2057 /*k8! return LOCAL_STATE_DIR "/Bones/";*/
2060 Dir
<< ivanconfig::GetMyDir() << "/Save/Bones/";
2062 Dir
<< getenv("HOME") << "/IvanSave/Bones/";
2068 level
*game::GetLevel (int I
) {
2069 return GetCurrentDungeon()->GetLevel(I
);
2073 int game::GetLevels () {
2074 return GetCurrentDungeon()->GetLevels();
2078 void game::SignalDeath (ccharacter
*Ghost
, ccharacter
*Murderer
, festring DeathMsg
) {
2079 if (InWilderness
) DeathMsg
<< " in the world map";
2080 else DeathMsg
<< " in " << GetCurrentDungeon()->GetLevelDescription(CurrentLevelIndex
);
2081 massacremap
*MassacreMap
;
2083 ++MiscMassacreAmount
;
2084 MassacreMap
= &MiscMassacreMap
;
2085 } else if(Murderer
->IsPlayer()) {
2086 ++PlayerMassacreAmount
;
2087 MassacreMap
= &PlayerMassacreMap
;
2088 } else if(Murderer
->IsPet()) {
2089 ++PetMassacreAmount
;
2090 MassacreMap
= &PetMassacreMap
;
2092 ++MiscMassacreAmount
;
2093 MassacreMap
= &MiscMassacreMap
;
2096 massacreid
MI(Ghost
->GetType(), Ghost
->GetConfig(), Ghost
->GetAssignedName());
2097 massacremap::iterator i
= MassacreMap
->find(MI
);
2099 if (i
== MassacreMap
->end()) {
2100 i
= MassacreMap
->insert(std::make_pair(MI
, killdata(1, Ghost
->GetGenerationDanger()))).first
;
2101 i
->second
.Reason
.push_back(killreason(DeathMsg
, 1));
2104 i
->second
.DangerSum
+= Ghost
->GetGenerationDanger();
2105 std::vector
<killreason
>& Reason
= i
->second
.Reason
;
2107 for (c
= 0; c
< Reason
.size(); ++c
) {
2108 if (Reason
[c
].String
== DeathMsg
) {
2113 if (c
== Reason
.size()) Reason
.push_back(killreason(DeathMsg
, 1));
2118 void game::DisplayMassacreLists () {
2119 DisplayMassacreList(PlayerMassacreMap
, "directly by you.", PlayerMassacreAmount
);
2120 DisplayMassacreList(PetMassacreMap
, "by your allies.", PetMassacreAmount
);
2121 DisplayMassacreList(MiscMassacreMap
, "by some other reason.", MiscMassacreAmount
);
2125 struct massacresetentry
{
2126 bool operator < (const massacresetentry
&MSE
) const { return festring::IgnoreCaseCompare(Key
, MSE
.Key
); }
2129 std::vector
<festring
> Details
;
2134 void game::DisplayMassacreList (const massacremap
&MassacreMap
, cchar
*Reason
, sLong Amount
) {
2135 std::set
<massacresetentry
> MassacreSet
;
2136 festring FirstPronoun
;
2138 charactervector GraveYard
;
2140 for (massacremap::const_iterator i1
= MassacreMap
.begin(); i1
!= MassacreMap
.end(); ++i1
) {
2141 character
*Victim
= protocontainer
<character
>::GetProto(i1
->first
.Type
)->Spawn(i1
->first
.Config
);
2142 Victim
->SetAssignedName(i1
->first
.Name
);
2143 massacresetentry Entry
;
2144 GraveYard
.push_back(Victim
);
2145 Entry
.ImageKey
= AddToCharacterDrawVector(Victim
);
2146 if (i1
->second
.Amount
== 1) {
2147 Victim
->AddName(Entry
.Key
, UNARTICLED
);
2148 Victim
->AddName(Entry
.String
, INDEFINITE
);
2150 Victim
->AddName(Entry
.Key
, PLURAL
);
2151 Entry
.String
<< i1
->second
.Amount
<< ' ' << Entry
.Key
;
2154 FirstPronoun
= Victim
->GetSex() == UNDEFINED
? "it" : Victim
->GetSex() == MALE
? "he" : "she";
2157 const std::vector
<killreason
>& Reason
= i1
->second
.Reason
;
2158 std::vector
<festring
>& Details
= Entry
.Details
;
2159 if (Reason
.size() == 1) {
2161 if (Reason
[0].Amount
== 1) Begin
= "";
2162 else if(Reason
[0].Amount
== 2) Begin
= "both ";
2163 else Begin
= "all ";
2164 Details
.push_back(Begin
+ Reason
[0].String
);
2166 for (uInt c
= 0; c
< Reason
.size(); ++c
) Details
.push_back(CONST_S("")+Reason
[c
].Amount
+' '+Reason
[c
].String
);
2167 std::sort(Details
.begin(), Details
.end(), ignorecaseorderer());
2169 MassacreSet
.insert(Entry
);
2171 sLong Total
= PlayerMassacreAmount
+PetMassacreAmount
+MiscMassacreAmount
;
2173 if (Total
== 1) MainTopic
<< "One creature perished during your adventure.";
2174 else MainTopic
<< Total
<< " creatures perished during your adventure.";
2175 felist
List(MainTopic
);
2176 SetStandardListAttributes(List
);
2177 List
.SetPageLength(15);
2178 List
.AddFlags(SELECTABLE
);
2179 List
.SetEntryDrawer(CharacterEntryDrawer
);
2180 List
.AddDescription(CONST_S(""));
2182 if (Amount
!= Total
) {
2183 SideTopic
= CONST_S("The following ");
2184 if (Amount
== 1) SideTopic
<< "one was killed " << Reason
;
2185 else SideTopic
<< Amount
<< " were killed " << Reason
;
2188 FirstPronoun
.Capitalize();
2189 SideTopic
<< FirstPronoun
<< " was killed " << Reason
;
2190 } else SideTopic
<< "They were all killed " << Reason
;
2192 List
.AddDescription(SideTopic
);
2193 List
.AddDescription(CONST_S(""));
2194 List
.AddDescription("Choose a type of creatures to browse death details.");
2195 std::set
<massacresetentry
>::const_iterator i2
;
2196 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
) List
.AddEntry(i2
->String
, LIGHT_GRAY
, 0, i2
->ImageKey
);
2198 int Chosen
= List
.Draw();
2199 if (Chosen
& FELIST_ERROR_BIT
) break;
2200 felist
SubList(CONST_S("Massacre details"));
2201 SetStandardListAttributes(SubList
);
2202 SubList
.SetPageLength(20);
2204 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
, ++Counter
) {
2205 if (Counter
== Chosen
) {
2206 for (uInt c
= 0; c
< i2
->Details
.size(); ++c
) SubList
.AddEntry(i2
->Details
[c
], LIGHT_GRAY
);
2212 ClearCharacterDrawVector();
2213 for (uInt c
= 0; c
< GraveYard
.size(); ++c
) delete GraveYard
[c
];
2217 truth
game::MassacreListsEmpty () {
2218 return PlayerMassacreMap
.empty() && PetMassacreMap
.empty() && MiscMassacreMap
.empty();
2223 void game::SeeWholeMap () {
2224 if (SeeWholeMapCheatMode
< 2) ++SeeWholeMapCheatMode
; else SeeWholeMapCheatMode
= 0;
2225 GetCurrentArea()->SendNewDrawRequest();
2230 void game::CreateBone () {
2231 if (!WizardModeIsActive() && !IsInWilderness() && RAND() & 3 && GetCurrentLevel()->PreProcessForBone()) {
2234 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2235 BoneName
= GetBoneDir()+"bon"+CurrentDungeonIndex
+CurrentLevelIndex
+BoneIndex
;
2236 if (!inputfile::fileExists(BoneName
)) break;
2238 if (BoneIndex
!= 1000) {
2239 //festring BoneName = GetBoneDir()+"bon"+CurrentDungeonIndex+CurrentLevelIndex+BoneIndex;
2240 fprintf(stderr
, "creating bone file: [%s]\n", BoneName
.CStr());
2241 outputfile
BoneFile(BoneName
, true);
2242 BoneFile
<< int(BONE_FILE_VERSION
) << PlayerName
<< CurrentLevel
;
2248 truth
game::PrepareRandomBone (int LevelIndex
) {
2249 if (/*k8:WizardModeIsActive() ||*/ GetCurrentDungeon()->IsGenerated(LevelIndex
) || !*GetCurrentDungeon()->GetLevelScript(LevelIndex
)->CanGenerateBone()) return false;
2252 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2253 BoneName
= GetBoneDir()+"bon"+CurrentDungeonIndex
+LevelIndex
+BoneIndex
;
2254 inputfile
BoneFile(BoneName
, 0, false);
2255 if (BoneFile
.IsOpen() && !(RAND() & 7)) {
2256 if (ReadType
<int>(BoneFile
) != BONE_FILE_VERSION
) {
2258 remove(BoneName
.CStr());
2263 level
*NewLevel
= GetCurrentDungeon()->LoadLevel(BoneFile
, LevelIndex
);
2264 if (!NewLevel
->PostProcessForBone()) {
2266 GetBoneItemIDMap().clear();
2267 GetBoneCharacterIDMap().clear();
2270 NewLevel
->FinalProcessForBone();
2271 GetBoneItemIDMap().clear();
2272 GetBoneCharacterIDMap().clear();
2273 SetCurrentArea(NewLevel
);
2274 CurrentLevel
= NewLevel
;
2275 CurrentLSquareMap
= NewLevel
->GetMap();
2276 GetCurrentDungeon()->SetIsGenerated(LevelIndex
, true);
2277 if (Name
== PlayerName
) ADD_MESSAGE("This place is oddly familiar. Like you had been here in one of your past lives.");
2278 else ADD_MESSAGE("You smell the stench of death.");
2283 if (BoneIndex
!= 1000) {
2284 remove(BoneName
.CStr());
2291 double game::CalculateAverageDanger (const charactervector
&EnemyVector
, character
*Char
) {
2292 double DangerSum
= 0;
2294 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) {
2295 DangerSum
+= EnemyVector
[c
]->GetRelativeDanger(Char
, true);
2298 return DangerSum
/Enemies
;
2302 double game::CalculateAverageDangerOfAllNormalEnemies () {
2303 double DangerSum
= 0;
2305 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
2306 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
2307 const character::database
*const *ConfigData
= Proto
->GetConfigData();
2308 int ConfigSize
= Proto
->GetConfigSize();
2309 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
2310 if (!ConfigData
[c2
]->IsAbstract
&& !ConfigData
[c2
]->IsUnique
&& ConfigData
[c2
]->CanBeGenerated
) {
2311 DangerSum
+= DangerMap
.find(configid(c1
, ConfigData
[c2
]->Config
))->second
.EquippedDanger
;
2316 return DangerSum
/Enemies
;
2320 character
*game::CreateGhost () {
2321 double AverageDanger
= CalculateAverageDangerOfAllNormalEnemies();
2322 charactervector EnemyVector
;
2323 protosystem::CreateEveryNormalEnemy(EnemyVector
);
2324 ghost
*Ghost
= ghost::Spawn();
2325 Ghost
->SetTeam(GetTeam(MONSTER_TEAM
));
2326 Ghost
->SetGenerationDanger(CurrentLevel
->GetDifficulty());
2327 Ghost
->SetOwnerSoul(PlayerName
);
2328 Ghost
->SetIsActive(false);
2329 Ghost
->EditAllAttributes(-4);
2330 Player
->SetSoulID(Ghost
->GetID());
2331 while (CalculateAverageDanger(EnemyVector
, Ghost
) > AverageDanger
&& Ghost
->EditAllAttributes(1));
2332 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) delete EnemyVector
[c
];
2337 int game::GetMoveCommandKey (int I
) {
2338 if (!ivanconfig::GetUseAlternativeKeys()) return MoveNormalCommandKey
[I
];
2339 return MoveAbnormalCommandKey
[I
];
2343 sLong
game::GetScore () {
2345 massacremap::const_iterator i
;
2346 massacremap SumMap
= PlayerMassacreMap
;
2347 for (i
= PetMassacreMap
.begin(); i
!= PetMassacreMap
.end(); ++i
) {
2348 killdata
&KillData
= SumMap
[i
->first
];
2349 KillData
.Amount
+= i
->second
.Amount
;
2350 KillData
.DangerSum
+= i
->second
.DangerSum
;
2352 for (i
= SumMap
.begin(); i
!= SumMap
.end(); ++i
) {
2353 character
*Char
= protocontainer
<character
>::GetProto(i
->first
.Type
)->Spawn(i
->first
.Config
);
2354 int SumOfAttributes
= Char
->GetSumOfAttributes();
2355 Counter
+= sqrt(i
->second
.DangerSum
/ DEFAULT_GENERATION_DANGER
) * SumOfAttributes
* SumOfAttributes
;
2358 return sLong(0.01*Counter
);
2362 /* Only works if New Attnam is loaded */
2363 truth
game::TweraifIsFree () {
2364 for (std::list
<character
*>::const_iterator i
= GetTeam(COLONIST_TEAM
)->GetMember().begin(); i
!= GetTeam(COLONIST_TEAM
)->GetMember().end(); ++i
)
2365 if ((*i
)->IsEnabled()) return false;
2370 // returns true if date is christmaseve or day
2371 truth
game::IsXMas () {
2372 time_t Time
= time(0);
2373 struct tm
*TM
= localtime(&Time
);
2374 return (TM
->tm_mon
== 11 && (TM
->tm_mday
== 24 || TM
->tm_mday
== 25));
2378 int game::AddToItemDrawVector (const itemvector
&What
) {
2379 ItemDrawVector
.push_back(What
);
2380 return ItemDrawVector
.size()-1;
2384 v2 ItemDisplacement
[3][3] = {
2385 { v2(0, 0), ERROR_V2
, ERROR_V2
},
2386 { v2(-2, -2), v2(2, 2), ERROR_V2
},
2387 { v2(-4, -4), v2(0, 0), v2(4, 4) }
2391 void game::ItemEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2396 { TILE_SIZE
, TILE_SIZE
},
2397 { NORMAL_LUMINANCE
},
2401 itemvector ItemVector
= ItemDrawVector
[I
];
2402 int Amount
= Min
<int>(ItemVector
.size(), 3);
2403 for (int c
= 0; c
< Amount
; ++c
) {
2404 v2 Displacement
= ItemDisplacement
[Amount
-1][c
];
2405 if (!ItemVector
[0]->HasNormalPictureDirection()) Displacement
.X
= -Displacement
.X
;
2406 B
.Dest
= Pos
+Displacement
;
2407 if (ItemVector
[c
]->AllowAlphaEverywhere()) B
.CustomData
|= ALLOW_ALPHA
;
2408 ItemVector
[c
]->Draw(B
);
2409 B
.CustomData
&= ~ALLOW_ALPHA
;
2411 if (ItemVector
.size() > 3) {
2414 B
.Dest
= ItemVector
[0]->HasNormalPictureDirection() ? Pos
+v2(11, -2) : Pos
+v2(-2, -2);
2416 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2421 int game::AddToCharacterDrawVector (character
*What
) {
2422 CharacterDrawVector
.push_back(What
);
2423 return CharacterDrawVector
.size()-1;
2427 void game::CharacterEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2428 if (CharacterDrawVector
[I
]) {
2433 { TILE_SIZE
, TILE_SIZE
},
2434 { NORMAL_LUMINANCE
},
2436 ALLOW_ANIMATE
|ALLOW_ALPHA
2438 CharacterDrawVector
[I
]->DrawBodyParts(B
);
2443 void game::GodEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2448 { TILE_SIZE
, TILE_SIZE
},
2453 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2457 character
*game::GetSumo () {
2458 return GetCurrentLevel()->GetLSquare(SUMO_ROOM_POS
)->GetRoom()->GetMaster();
2462 truth
game::TryToEnterSumoArena () {
2463 character
*Sumo
= GetSumo();
2464 if (!Sumo
|| !Sumo
->IsEnabled() || Sumo
->GetRelation(Player
) == HOSTILE
|| !Player
->CanBeSeenBy(Sumo
)) return true;
2465 if (TweraifIsFree()) {
2466 ADD_MESSAGE("\"You started this stupid revolution, after which I've been constantly hungry. Get lost!\"");
2469 if (PlayerIsSumoChampion()) {
2470 ADD_MESSAGE("\"I don't really enjoy losing, especially many times to the same guy. Go away.\"");
2473 if (Player
->IsPolymorphed()) {
2474 ADD_MESSAGE("\"Don't try to cheat. Come back when you're normal again.\"");
2477 if (Player
->GetHungerState() < SATIATED
) {
2478 ADD_MESSAGE("\"Your figure is too slender for this sport. Eat a lot more and come back.\"");
2481 if (Player
->GetHungerState() < BLOATED
) {
2482 ADD_MESSAGE("\"You're still somewhat too thin. Eat some more and we'll compete.\"");
2485 ADD_MESSAGE("\"So you want to compete? Okay, I'll explain the rules. First, I'll make a mirror image out of us both. We'll enter the arena and fight till one is knocked out. Use of any equipment is not allowed. Note that we will not gain experience from fighting as a mirror image, but won't get really hurt, either. However, controlling the image is exhausting and you can get hungry very quickly.\"");
2486 if (!TruthQuestion("Do you want to challenge him? [y/N]")) return false;
2488 SumoWrestling
= true;
2489 character
*MirrorPlayer
= Player
->Duplicate(IGNORE_PROHIBITIONS
);
2490 character
*MirrorSumo
= Sumo
->Duplicate(IGNORE_PROHIBITIONS
);
2491 SetPlayer(MirrorPlayer
);
2492 charactervector Spectators
;
2493 if (Player
->GetTeam()->GetRelation(GetTeam(TOURIST_GUIDE_TEAM
)) != HOSTILE
&&
2494 Player
->GetTeam()->GetRelation(GetTeam(TOURIST_TEAM
)) != HOSTILE
) {
2495 GetTeam(TOURIST_GUIDE_TEAM
)->MoveMembersTo(Spectators
);
2496 GetTeam(TOURIST_TEAM
)->MoveMembersTo(Spectators
);
2498 GetCurrentDungeon()->SaveLevel(SaveName(), 0);
2499 charactervector test
;
2500 EnterArea(test
, 1, STAIRS_UP
);
2501 MirrorSumo
->PutTo(SUMO_ARENA_POS
+v2(6, 5));
2502 MirrorSumo
->ChangeTeam(GetTeam(SUMO_TEAM
));
2503 GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->SetMasterID(MirrorSumo
->GetID());
2504 for (uInt c
= 0; c
< Spectators
.size(); ++c
) Spectators
[c
]->PutToOrNear(SUMO_ARENA_POS
+ v2(6, 10));
2505 throw areachangerequest();
2510 truth
game::TryToExitSumoArena () {
2511 if (GetTeam(PLAYER_TEAM
)->GetRelation(GetTeam(NEW_ATTNAM_TEAM
)) == HOSTILE
) return true;
2513 charactervector CVector
;
2514 if (IsSumoWrestling()) {
2515 if (TruthQuestion("Do you really wish to give up? [y/N]")) return EndSumoWrestling(LOST
);
2520 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2521 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2522 std::vector
<character
*> test
;
2523 EnterArea(test
, 0, STAIRS_DOWN
);
2524 Player
->GetStackUnder()->AddItems(IVector
);
2525 if (!IVector
.empty()) {
2526 character
*Sumo
= GetSumo();
2527 if (Sumo
&& Sumo
->GetRelation(Player
) != HOSTILE
&& Player
->CanBeSeenBy(Sumo
)) ADD_MESSAGE("\"Don't leave anything there, please.\"");
2529 v2 PlayerPos
= Player
->GetPos();
2530 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2531 throw areachangerequest();
2537 truth
game::EndSumoWrestling (int Result
) {
2539 msgsystem::LeaveBigMessageMode();
2540 if (Result
== LOST
) AskForKeyPress("You lose. [press any key to continue]");
2541 else if (Result
== WON
) AskForKeyPress("You win! [press any key to continue]");
2542 else if (Result
== DISQUALIFIED
) AskForKeyPress("You are disqualified! [press any key to continue]");
2543 character
*Sumo
= GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->GetMaster();
2544 /* We'll make a throw soon so deletes are allowed */
2553 charactervector CVector
;
2554 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2555 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2556 charactervector test
;
2557 EnterArea(test
, 0, STAIRS_DOWN
);
2558 SumoWrestling
= false;
2559 Player
->GetStackUnder()->AddItems(IVector
);
2560 v2 PlayerPos
= Player
->GetPos();
2561 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2562 if (Result
== LOST
) ADD_MESSAGE("\"I hope you've learned your lesson now!\"");
2563 else if (Result
== DISQUALIFIED
) ADD_MESSAGE("\"Don't do that again or I'll be really angry!\"");
2565 PlayerSumoChampion
= true;
2566 character
*Sumo
= GetSumo();
2567 festring Msg
= Sumo
->GetName(DEFINITE
)+" seems humbler than before. \"Darn. You bested me.\n";
2568 Msg
<< "Here's a little something as a reward\", " << Sumo
->GetPersonalPronoun() << " says and hands you a belt of levitation.\n\"";
2569 (belt::Spawn(BELT_OF_LEVITATION
))->MoveTo(Player
->GetStack());
2570 Msg
<< "Allow me to also teach you a few nasty martial art tricks the years have taught me.\"";
2571 Player
->GetCWeaponSkill(UNARMED
)->AddHit(100000);
2572 Player
->GetCWeaponSkill(KICK
)->AddHit(100000);
2573 character
*Imperialist
= GetCurrentLevel()->GetLSquare(5, 5)->GetRoom()->GetMaster();
2574 if (Imperialist
&& Imperialist
->GetRelation(Player
) != HOSTILE
) {
2575 v2 Pos
= Player
->GetPos()+v2(0, 1);
2576 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
2577 Imperialist
->Remove();
2578 Imperialist
->PutTo(Pos
);
2579 Msg
<< "\n\nSuddenly you notice " << Imperialist
->GetName(DEFINITE
) << " has also entered.\n"
2580 "\"I see we have a promising fighter among us. I had already heard of your\n"
2581 "adventures outside the village, but hardly could I believe that one day you\n"
2582 "would defeat even the mighty Huang Ming Pong! A hero such as you is bound\n"
2583 "to become world famous, and can earn a fortune if wealthy sponsors are behind\n"
2584 "him. May I therefore propose a mutually profitable contract: I'll give you this\n"
2585 "nice shirt with my company's ad, and you'll wear it as you journey bravely to\n"
2586 "the unknown and fight epic battles against the limitless minions of evil. I'll\n"
2587 "reward you well when you return, depending on how much you have used it.\"";
2588 Player
->GetStack()->AddItem(decosadshirt::Spawn());
2591 GetCurrentArea()->SendNewDrawRequest();
2594 Player
->EditNP(-25000);
2595 Player
->CheckStarvationDeath(CONST_S("exhausted after controlling a mirror image for too long"));
2596 throw areachangerequest();
2601 rain
*game::ConstructGlobalRain () {
2602 return new rain(GlobalRainLiquid
, static_cast<lsquare
*>(GetSquareInLoad()), GlobalRainSpeed
, MONSTER_TEAM
, false);
2606 v2
game::GetSunLightDirectionVector () {
2607 int Index
= Tick
% 48000 / 1000;
2608 /* Should have the same sign as sin(PI * Index / 24) and XTable[Index] /
2609 YTable[Index] should equal roughly -tan(PI * Index / 24). Also, vector
2610 (XTable[Index], YTable[Index]) + P should not be a valid position of
2611 any possible level L for any P belonging to L. */
2612 static int XTable
[48] = {
2613 0, 1000, 1000, 1000, 1000, 1000,
2614 1000, 1303, 1732, 2414, 3732, 7596,
2615 1000, 7596, 3732, 2414, 1732, 1303,
2616 1000, 1000, 1000, 1000, 1000, 1000,
2617 0, -1000, -1000, -1000, -1000, -1000,
2618 -1000, -1303, -1732, -2414, -3732, -7596,
2619 -1000, -7596, -3732, -2414, -1732, -1303,
2620 -1000, -1000, -1000, -1000, -1000, -1000 };
2621 /* Should have the same sign as -cos(PI * Index / 24) */
2622 static int YTable
[48] = { -1000, -7596, -3732, -2414, -1732, -1303,
2623 -1000, -1000, -1000, -1000, -1000, -1000,
2624 0, 1000, 1000, 1000, 1000, 1000,
2625 1000, 1303, 1732, 2414, 3732, 7596,
2626 1000, 7596, 3732, 2414, 1732, 1303,
2627 1000, 1000, 1000, 1000, 1000, 1000,
2628 0, -1000, -1000, -1000, -1000, -1000,
2629 -1000, -1303, -1732, -2414, -3732, -7596 };
2630 return v2(XTable
[Index
], YTable
[Index
]);
2634 int game::CalculateMinimumEmitationRadius (col24 E
) {
2635 int MaxElement
= Max(GetRed24(E
), GetGreen24(E
), GetBlue24(E
));
2636 return int(sqrt(double(MaxElement
<< 7) / LIGHT_BORDER
- 120.));
2640 feuLong
game::IncreaseSquarePartEmitationTicks () {
2641 if ((SquarePartEmitationTick
+= 2) == 0x100) {
2642 CurrentLevel
->InitSquarePartEmitationTicks();
2643 SquarePartEmitationTick
= 2;
2645 return SquarePartEmitationTick
;
2649 bool game::Wish (character
*Wisher
, cchar
*MsgSingle
, cchar
*MsgPair
, bool canAbort
) {
2651 festring oldDef
= DefaultWish
;
2652 festring Temp
= DefaultQuestion(CONST_S("What do you want to wish for?"), DefaultWish
);
2653 if (DefaultWish
== "nothing" && canAbort
) {
2654 DefaultWish
= oldDef
;
2657 item
*TempItem
= protosystem::CreateItem(Temp
, Wisher
->IsPlayer());
2659 Wisher
->GetStack()->AddItem(TempItem
);
2660 TempItem
->SpecialGenerationHandler();
2661 if (TempItem
->HandleInPairs()) ADD_MESSAGE(MsgPair
, TempItem
->CHAR_NAME(PLURAL
));
2662 else ADD_MESSAGE(MsgSingle
, TempItem
->CHAR_NAME(INDEFINITE
));
2669 festring
game::DefaultQuestion (festring Topic
, festring
&Default
, stringkeyhandler KeyHandler
) {
2670 festring ShortDefault
= Default
;
2671 if (Default
.GetSize() > 29) {
2672 ShortDefault
.Resize(27);
2673 ShortDefault
= ShortDefault
<< CONST_S("...");
2675 if (!Default
.IsEmpty()) Topic
<< " [" << ShortDefault
<< ']';
2676 festring Answer
= StringQuestion(Topic
, WHITE
, 0, 80, false, KeyHandler
);
2677 if (Answer
.IsEmpty()) Answer
= Default
;
2678 return Default
= Answer
;
2682 void game::GetTime (ivantime
&Time
) {
2683 Time
.Hour
= 12 + Tick
/ 2000;
2684 Time
.Day
= Time
.Hour
/ 24 + 1;
2686 Time
.Min
= Tick
% 2000 * 60 / 2000;
2690 truth
NameOrderer (character
*C1
, character
*C2
) {
2691 return festring::IgnoreCaseCompare(C1
->GetName(UNARTICLED
), C2
->GetName(UNARTICLED
));
2695 truth
game::PolymorphControlKeyHandler (int Key
, festring
&String
) {
2697 felist
List(CONST_S("List of known creatures and their intelligence requirements"));
2698 SetStandardListAttributes(List
);
2699 List
.SetPageLength(15);
2700 List
.AddFlags(SELECTABLE
);
2701 protosystem::CreateEverySeenCharacter(CharacterDrawVector
);
2702 std::sort(CharacterDrawVector
.begin(), CharacterDrawVector
.end(), NameOrderer
);
2703 List
.SetEntryDrawer(CharacterEntryDrawer
);
2704 std::vector
<festring
> StringVector
;
2706 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) {
2707 character
*Char
= CharacterDrawVector
[c
];
2708 if (Char
->CanBeWished()) {
2710 Char
->AddName(Entry
, UNARTICLED
);
2711 StringVector
.push_back(Entry
);
2712 int Req
= Char
->GetPolymorphIntelligenceRequirement();
2713 if (Char
->IsSameAs(Player
) || (Player
->GetPolymorphBackup() && Player
->GetPolymorphBackup()->IsSameAs(Char
))) Req
= 0;
2714 Entry
<< " (" << Req
<< ')';
2715 int Int
= Player
->GetAttribute(INTELLIGENCE
);
2716 List
.AddEntry(Entry
, Req
> Int
? RED
: LIGHT_GRAY
, 0, c
);
2719 int Chosen
= List
.Draw();
2720 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) delete CharacterDrawVector
[c
];
2721 if (!(Chosen
& FELIST_ERROR_BIT
)) String
= StringVector
[Chosen
];
2722 CharacterDrawVector
.clear();
2729 outputfile
&operator << (outputfile
&SaveFile
, const killdata
&Value
) {
2730 SaveFile
<< Value
.Amount
<< Value
.DangerSum
<< Value
.Reason
;
2735 inputfile
&operator >> (inputfile
&SaveFile
, killdata
&Value
) {
2736 SaveFile
>> Value
.Amount
>> Value
.DangerSum
>> Value
.Reason
;
2741 outputfile
&operator << (outputfile
&SaveFile
, const killreason
&Value
) {
2742 SaveFile
<< Value
.Amount
<< Value
.String
;
2747 inputfile
&operator >> (inputfile
&SaveFile
, killreason
&Value
) {
2748 SaveFile
>> Value
.Amount
>> Value
.String
;
2753 truth
DistanceOrderer (character
*C1
, character
*C2
) {
2754 v2 PlayerPos
= PLAYER
->GetPos();
2755 v2 Pos1
= C1
->GetPos();
2756 v2 Pos2
= C2
->GetPos();
2757 int D1
= Max(abs(Pos1
.X
- PlayerPos
.X
), abs(Pos1
.Y
- PlayerPos
.Y
));
2758 int D2
= Max(abs(Pos2
.X
- PlayerPos
.X
), abs(Pos2
.Y
- PlayerPos
.Y
));
2759 if (D1
!= D2
) return D1
< D2
;
2760 if (Pos1
.Y
!= Pos2
.Y
) return Pos1
.Y
< Pos2
.Y
;
2761 return Pos1
.X
< Pos2
.X
;
2765 truth
game::FillPetVector (cchar
*Verb
) {
2767 team
*Team
= GetTeam(PLAYER_TEAM
);
2768 for (std::list
<character
*>::const_iterator i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
)
2769 if ((*i
)->IsEnabled() && !(*i
)->IsPlayer() && (*i
)->CanBeSeenByPlayer()) PetVector
.push_back(*i
);
2770 if (PetVector
.empty()) {
2771 ADD_MESSAGE("You don't detect any friends to %s.", Verb
);
2774 std::sort(PetVector
.begin(), PetVector
.end(), DistanceOrderer
);
2775 LastPetUnderCursor
= PetVector
[0];
2780 truth
game::CommandQuestion () {
2781 if (!FillPetVector("command")) return false;
2783 if (PetVector
.size() == 1) Char
= PetVector
[0];
2785 v2 Pos
= PetVector
[0]->GetPos();
2786 Pos
= PositionQuestion(CONST_S("Whom do you wish to command? [direction keys/'+'/'-'/'a'll/space/esc]"), Pos
, &PetHandler
, &CommandKeyHandler
);
2787 if (Pos
== ERROR_V2
) return false;
2788 if (Pos
== ABORT_V2
) return true;
2789 Char
= CurrentArea
->GetSquare(Pos
)->GetCharacter();
2790 if (!Char
|| !Char
->CanBeSeenByPlayer()) {
2791 ADD_MESSAGE("You don't see anyone here to command.");
2794 if (Char
->IsPlayer()) {
2795 ADD_MESSAGE("You do that all the time.");
2798 if (!Char
->IsPet()) {
2799 ADD_MESSAGE("%s refuses to be commanded by you.", Char
->CHAR_NAME(DEFINITE
));
2803 return Char
->IssuePetCommands();
2807 void game::NameQuestion () {
2808 if (!FillPetVector("name")) return;
2809 if (PetVector
.size() == 1) PetVector
[0]->TryToName();
2810 else PositionQuestion(CONST_S("Who do you want to name? [direction keys/'+'/'-'/'n'ame/esc]"), PetVector
[0]->GetPos(), &PetHandler
, &NameKeyHandler
);
2814 void game::PetHandler (v2 CursorPos
) {
2815 character
*Char
= CurrentArea
->GetSquare(CursorPos
)->GetCharacter();
2816 if (Char
&& Char
->CanBeSeenByPlayer() && Char
->IsPet() && !Char
->IsPlayer()) CursorData
= RED_CURSOR
|CURSOR_TARGET
;
2817 else CursorData
= RED_CURSOR
;
2818 if (Char
&& !Char
->IsPlayer() && Char
->IsPet()) LastPetUnderCursor
= Char
;
2822 v2
game::CommandKeyHandler (v2 CursorPos
, int Key
) {
2823 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
2824 if (Key
== 'a' || Key
== 'A') return CommandAll() ? ABORT_V2
: ERROR_V2
;
2829 truth
game::SelectPet (int Key
) {
2831 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2832 if (PetVector
[c
] == LastPetUnderCursor
) {
2833 if (++c
== PetVector
.size()) c
= 0;
2834 LastPetUnderCursor
= PetVector
[c
];
2838 } else if (Key
== '-') {
2839 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2840 if (PetVector
[c
] == LastPetUnderCursor
) {
2841 if (!c
) c
= PetVector
.size();
2842 LastPetUnderCursor
= PetVector
[--c
];
2851 void game::CommandScreen (cfestring
&Topic
, feuLong PossibleFlags
, feuLong ConstantFlags
, feuLong
&VaryFlags
, feuLong
&Flags
) {
2852 static cchar
*CommandDescription
[COMMAND_FLAGS
] = {
2854 "Flee from enemies",
2855 "Don't change your equipment",
2856 "Don't consume anything valuable"
2859 SetStandardListAttributes(List
);
2860 List
.AddFlags(SELECTABLE
);
2861 List
.AddDescription(CONST_S(""));
2862 List
.AddDescription(CONST_S("Command Active?"));
2865 for (c
= 0; c
< COMMAND_FLAGS
; ++c
) {
2866 if (1 << c
& PossibleFlags
) {
2867 truth Changeable
= !(1 << c
& ConstantFlags
);
2870 Entry
= CommandDescription
[c
];
2873 Entry
<< " " << CommandDescription
[c
];
2876 if (1 << c
& VaryFlags
) Entry
<< "varies"; else Entry
<< (1 << c
& Flags
? "yes" : "no");
2877 List
.AddEntry(Entry
, Changeable
? LIGHT_GRAY
: DARK_GRAY
, 0, NO_IMAGE
, Changeable
);
2880 int Chosen
= List
.Draw();
2881 if (Chosen
& FELIST_ERROR_BIT
) return;
2882 for (c
= 0, i
= 0; c
< COMMAND_FLAGS
; ++c
) {
2883 if (1 << c
& PossibleFlags
&& !(1 << c
& ConstantFlags
) && i
++ == Chosen
) {
2884 if (1 << c
& VaryFlags
) {
2885 VaryFlags
&= ~(1 << c
);
2887 } else Flags
^= 1 << c
;
2892 DrawEverythingNoBlit();
2897 truth
game::CommandAll () {
2898 feuLong PossibleFlags
= 0, ConstantFlags
= ALL_COMMAND_FLAGS
, VaryFlags
= 0, OldFlags
= 0;
2900 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2901 ConstantFlags
&= PetVector
[c1
]->GetConstantCommandFlags();
2902 feuLong C
= PetVector
[c1
]->GetCommandFlags();
2903 feuLong ThisPossible
= PetVector
[c1
]->GetPossibleCommandFlags();
2904 for (c2
= 0; c2
< COMMAND_FLAGS
; ++c2
)
2905 if (1 << c2
& PossibleFlags
& ThisPossible
&& (1 << c2
& C
) != (1 << c2
& OldFlags
)) VaryFlags
|= 1 << c2
;
2906 PossibleFlags
|= ThisPossible
;
2907 OldFlags
|= C
& ThisPossible
;
2909 if (!PossibleFlags
) {
2910 ADD_MESSAGE("Not a single creature in your visible team can be commanded.");
2913 feuLong NewFlags
= OldFlags
;
2914 CommandScreen(CONST_S("Issue commands to whole visible team"), PossibleFlags
, ConstantFlags
, VaryFlags
, NewFlags
);
2915 truth Change
= false;
2916 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2917 character
*Char
= PetVector
[c1
];
2918 if (!Char
->IsConscious()) continue;
2919 feuLong OldC
= Char
->GetCommandFlags();
2920 feuLong ConstC
= Char
->GetConstantCommandFlags();
2921 feuLong ThisC
= (NewFlags
& Char
->GetPossibleCommandFlags() & ~(ConstC
|VaryFlags
)) | (OldC
& (ConstC
|VaryFlags
));
2922 if (ThisC
!= OldC
) Change
= true;
2923 Char
->SetCommandFlags(ThisC
);
2925 if (!Change
) return false;
2926 Player
->EditAP(-500);
2927 Player
->EditExperience(CHARISMA
, 50, 1 << 7);
2932 col16
game::GetAttributeColor (int I
) {
2933 int Delta
= GetTick()-LastAttributeChangeTick
[I
];
2934 if (OldAttribute
[I
] == NewAttribute
[I
] || Delta
>= 510) return WHITE
;
2935 if (OldAttribute
[I
] < NewAttribute
[I
]) return MakeRGB16(255, 255, Delta
>> 1);
2936 return MakeRGB16(255, Delta
>> 1, Delta
>> 1);
2940 void game::UpdateAttributeMemory () {
2941 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
2942 int A
= Player
->GetAttribute(c
);
2943 if (A
!= NewAttribute
[c
]) {
2944 OldAttribute
[c
] = NewAttribute
[c
];
2945 NewAttribute
[c
] = A
;
2946 LastAttributeChangeTick
[c
] = GetTick();
2952 void game::InitAttributeMemory () {
2953 for (int c
= 0; c
< ATTRIBUTES
; ++c
) OldAttribute
[c
] = NewAttribute
[c
] = Player
->GetAttribute(c
);
2957 void game::TeleportHandler (v2 CursorPos
) {
2958 if ((CursorPos
-Player
->GetPos()).GetLengthSquare() > Player
->GetTeleportRangeSquare())
2959 CursorData
= BLUE_CURSOR
|CURSOR_TARGET
;
2961 CursorData
= RED_CURSOR
|CURSOR_TARGET
;
2965 double game::GetGameSituationDanger () {
2966 double SituationDanger
= 0;
2967 character
*Player
= GetPlayer();
2968 truth PlayerStuck
= Player
->IsStuck();
2969 v2 PlayerPos
= Player
->GetPos();
2970 character
*TruePlayer
= Player
;
2971 if (PlayerStuck
) (Player
= Player
->Duplicate(IGNORE_PROHIBITIONS
))->ChangeTeam(0);
2972 for (int c1
= 0; c1
< GetTeams(); ++c1
)
2973 if (GetTeam(c1
)->GetRelation(GetTeam(PLAYER_TEAM
)) == HOSTILE
)
2974 for (std::list
<character
*>::const_iterator i1
= GetTeam(c1
)->GetMember().begin(); i1
!= GetTeam(c1
)->GetMember().end(); ++i1
) {
2975 character
*Enemy
= *i1
;
2976 if (Enemy
->IsEnabled() && Enemy
->CanAttack() && (Enemy
->CanMove() || Enemy
->GetPos().IsAdjacent(PlayerPos
))) {
2977 truth EnemyStuck
= Enemy
->IsStuck();
2978 v2 EnemyPos
= Enemy
->GetPos();
2979 truth Sees
= TruePlayer
->CanBeSeenBy(Enemy
);
2980 character
*TrueEnemy
= Enemy
;
2981 if (EnemyStuck
) Enemy
= Enemy
->Duplicate(IGNORE_PROHIBITIONS
);
2982 double PlayerTeamDanger
= 1/Enemy
->GetSituationDanger(Player
, EnemyPos
, PlayerPos
, Sees
);
2983 for (int c2
= 0; c2
< GetTeams(); ++c2
)
2984 if (GetTeam(c2
)->GetRelation(GetTeam(c1
)) == HOSTILE
)
2985 for (std::list
<character
*>::const_iterator i2
= GetTeam(c2
)->GetMember().begin(); i2
!= GetTeam(c2
)->GetMember().end(); ++i2
) {
2986 character
*Friend
= *i2
;
2987 if (Friend
->IsEnabled() && !Friend
->IsPlayer() && Friend
->CanAttack() && (Friend
->CanMove() || Friend
->GetPos().IsAdjacent(EnemyPos
))) {
2988 v2 FriendPos
= Friend
->GetPos();
2989 truth Sees
= TrueEnemy
->CanBeSeenBy(Friend
);
2990 if (Friend
->IsStuck()) {
2991 Friend
= Friend
->Duplicate(IGNORE_PROHIBITIONS
);
2992 PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
) * .2;
2994 } else PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
);
2998 PlayerTeamDanger
*= 5;
3001 SituationDanger
+= 1 / PlayerTeamDanger
;
3004 Player
->ModifySituationDanger(SituationDanger
);
3006 SituationDanger
*= 2;
3009 return SituationDanger
;
3013 sLong
game::GetTimeSpent () {
3014 return time::TimeAdd(time::TimeDifference(time(0),LastLoad
), TimePlayedBeforeLastLoad
);
3018 outputfile
&operator << (outputfile
&SaveFile
, const massacreid
&MI
) {
3019 SaveFile
<< MI
.Type
<< MI
.Config
<< MI
.Name
;
3024 inputfile
&operator >> (inputfile
&SaveFile
, massacreid
&MI
) {
3025 SaveFile
>> MI
.Type
>> MI
.Config
>> MI
.Name
;
3030 truth
game::PlayerIsRunning () {
3031 return PlayerRunning
&& Player
->CanMove();
3035 void game::AddSpecialCursor (v2 Pos
, int Data
) {
3036 SpecialCursorPos
.push_back(Pos
);
3037 SpecialCursorData
.push_back(Data
);
3041 void game::RemoveSpecialCursors () {
3042 SpecialCursorPos
.clear();
3043 SpecialCursorData
.clear();
3047 void game::LearnAbout (god
*Who
) {
3048 Who
->SetIsKnown(true);
3049 /* slightly slow, but doesn't matter since this is run so rarely */
3050 if (PlayerKnowsAllGods() && !game::PlayerHasReceivedAllGodsKnownBonus
) {
3051 GetPlayer()->ApplyAllGodsKnownBonus();
3052 game::PlayerHasReceivedAllGodsKnownBonus
= true;
3057 truth
game::PlayerKnowsAllGods () {
3058 for (int c
= 1; c
<= GODS
; ++c
) if (!GetGod(c
)->IsKnown()) return false;
3063 void game::AdjustRelationsToAllGods (int Amount
) {
3064 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->AdjustRelation(Amount
);
3068 void game::SetRelationsToAllGods (int Amount
) {
3069 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->SetRelation(Amount
);
3073 void game::ShowDeathSmiley (bitmap
*Buffer
, truth
) {
3074 static blitdata B
= {
3077 { (RES
.X
>> 1) - 24, RES
.Y
* 4 / 7 - 24 },
3083 int Tick
= globalwindowhandler::UpdateTick();
3084 if (((Tick
>> 1) & 31) == 1) B
.Src
.X
= 48;
3085 else if (((Tick
>> 1) & 31) == 2) B
.Src
.X
= 96;
3088 igraph::GetSmileyGraphic()->NormalBlit(B
);
3089 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
3093 static int doListSelector (felist
&list
, int defsel
, int cnt
) {
3094 game::SetStandardListAttributes(list
);
3095 list
.AddFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3096 if (defsel
> 0) list
.SetSelected(defsel
);
3097 uInt sel
= list
.Draw();
3099 list
.RemoveFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3100 if (sel
& FELIST_ERROR_BIT
) return -1;
3101 if (sel
>= (uInt
)cnt
) return -1;
3106 int game::ListSelector (int defsel
, cfestring
&title
, ...) {
3109 va_start(items
, title
);
3113 const char *s
= va_arg(items
, const char *);
3115 list
.AddEntry(s
, LIGHT_GRAY
);
3119 return doListSelector(list
, defsel
, cnt
);
3123 int game::ListSelectorArray (int defsel
, cfestring
&title
, const char *items
[]) {
3127 if (!items
[cnt
]) break;
3128 list
.AddEntry(items
[cnt
], LIGHT_GRAY
);
3131 return doListSelector(list
, defsel
, cnt
);
3135 void game::ClearEventData () {
3143 // '.': string or number
3146 // '*': collect all args
3147 int game::ParseFuncArgs (cfestring
&types
, std::vector
<FuncArg
> &args
, inputfile
*fl
, truth noterm
) {
3151 if (!fl
) fl
= mFEStack
.top();
3153 for (unsigned int f
= 0; f
< types
.GetSize(); f
++) {
3156 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3157 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3160 n
= fl
->ReadNumber(0xFF, true);
3161 args
.push_back(FuncArg(n
));
3165 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3166 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3167 fl
->ReadWord(s
, true);
3168 if (s
== ";") return args
.size();
3169 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3174 s
= fl
->ReadWord(true);
3175 args
.push_back(FuncArg(s
));
3178 if (f
== types
.GetSize()-1) {
3180 fl
->ReadWord(s
, true);
3181 if (s
!= ";") ABORT("';' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3184 fl
->ReadWord(s
, true);
3185 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3192 truth
game::GetWord (festring
&w
) {
3194 inputfile
*fl
= mFEStack
.top();
3195 fl
->ReadWord(w
, false);
3196 if (w
== "" && fl
->Eof()) {
3199 if (mFEStack
.empty()) return false;
3202 if (w
== "Include") {
3203 fl
->ReadWord(w
, true);
3204 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3205 w
= game::GetGameDir()+"Script/"+w
;
3206 inputfile
*fl
= new inputfile(w
, &game::GetGlobalValueMap(), true);
3207 fl
->setGetVarCB(game::ldrGetVar
);
3211 if (w
== "Message") {
3212 fl
->ReadWord(w
, true);
3213 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3214 fprintf(stderr
, "MESSAGE: %s\n", w
.CStr());
3222 void game::SkipBlock (truth brcEaten
) {
3225 mFEStack
.top()->ReadWord(w
, true);
3226 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3230 mFEStack
.top()->ReadWord(w
, true);
3231 if (w
== "{") cnt
++;
3232 else if (w
== "}") {
3233 if (--cnt
< 1) break;
3239 truth
game::DoOnEvent (truth brcEaten
, truth AllowScript
) {
3240 // do; only funcalls for now
3241 truth eaten
= AllowScript
? true : false;
3244 mFEStack
.top()->ReadWord(w
, true);
3245 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3249 if (AllowScript
) break;
3250 ABORT("Unexpected end of file %s!", mFEStack
.top()->GetFileName().CStr());
3252 //fprintf(stderr, " :[%s]\n", w.CStr());
3254 if (AllowScript
) ABORT("Unexpected '}' in AllowScript file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3257 if (w
== ";") continue;
3259 mFEStack
.top()->ReadWord(w
, true);
3260 if (mFEStack
.top()->ReadWord(true) != "=") ABORT("'=' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3261 //fprintf(stderr, "setvar: %s\n", w.CStr());
3263 sLong n
= mFEStack
.top()->ReadNumber(true);
3265 if (mChar
) mChar
->SetMoney(n
);
3268 if (w
== "result") {
3269 mResult
= mFEStack
.top()->ReadNumber(true);
3272 ABORT("Unknown var [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3274 //mFEStack.top()->ReadWord(w, true);
3275 std::vector
<FuncArg
> args
;
3276 //fprintf(stderr, "funcall: %s\n", w.CStr());
3277 if (w
== "SetMoney") {
3278 ParseFuncArgs("n", args
);
3279 sLong n
= args
[0].ival
;
3281 if (mChar
) mChar
->SetMoney(n
);
3284 if (w
== "EditMoney") {
3285 ParseFuncArgs("n", args
);
3286 sLong n
= args
[0].ival
;
3287 if (mChar
) mChar
->EditMoney(n
);
3290 if (w
== "AddMessage") {
3291 ParseFuncArgs("*", args
);
3293 for (uInt f
= 0; f
< args
.size(); f
++) {
3294 const FuncArg
&a
= args
[f
];
3295 if (a
.type
== FARG_STRING
) s
<< a
.sval
; else s
<< a
.ival
;
3297 ADD_MESSAGE("%s", s
.CStr());
3300 if (w
== "EatThisEvent") {
3301 if (AllowScript
) ABORT("'EatThisEvent' forbidden in AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3305 if (w
== "Disallow") {
3306 if (!AllowScript
) ABORT("'Disallow' forbidden in not-AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3310 ABORT("Unknown function [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3311 //if (mFEStack.top()->ReadWord() != ";") ABORT("';' expected in file %s line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TokenLine());
3313 //ABORT("Invalid term in file %s at line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TokenLine());
3315 //fprintf(stderr, "------------\n");
3320 //TODO: cache event scripts
3321 truth
game::RunOnEvent (cfestring
&ename
) {
3322 static std::vector
<festring
> scriptFiles
;
3323 static truth cached
= false;
3326 character
*old
= mChar
;
3330 for (int fno
= 99; fno
>= -1; fno
--) {
3332 cfname
<< game::GetGameDir() << "Script/onevent";
3335 sprintf(bnum
, "_%02d", fno
);
3339 if (!inputfile::fileExists(cfname
)) continue;
3340 inputfile
*ifl
= new inputfile(cfname
, &game::GetGlobalValueMap(), false);
3341 if (!ifl
->IsOpen()) {
3345 scriptFiles
.push_back(cfname
);
3346 ifl
->setGetVarCB(game::ldrGetVar
);
3350 for (unsigned int f
= 0; f
< scriptFiles
.size(); ++f
) {
3351 festring cfname
= scriptFiles
[f
];
3352 inputfile
*ifl
= new inputfile(cfname
, &game::GetGlobalValueMap(), false);
3353 if (!ifl
->IsOpen()) {
3357 ifl
->setGetVarCB(game::ldrGetVar
);
3363 while (GetWord(w
)) {
3364 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3365 mFEStack
.top()->ReadWord(w
, true);
3366 truth doIt
= (w
==ename
);
3368 res
= DoOnEvent(false);
3379 truth
game::RunOnEventStr (cfestring
&ename
, cfestring
&str
) {
3381 if (str
.GetSize() < 1) return false;
3382 //fprintf(stderr, "=============\n%s=============\n", str.CStr());
3383 inputfile
*ifl
= new meminputfile(str
, &game::GetGlobalValueMap());
3384 ifl
->setGetVarCB(game::ldrGetVar
);
3387 //fprintf(stderr, "=============\n", str.CStr());
3388 //fprintf(stderr, "event: [%s]\n", ename.CStr());
3389 //fprintf(stderr, "---\n%s---\n", str.CStr());
3390 while (GetWord(w
)) {
3391 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3392 mFEStack
.top()->ReadWord(w
, true);
3393 //fprintf(stderr, "on: [%s]\n", w.CStr());
3394 truth doIt
= (w
==ename
);
3396 //fprintf(stderr, " do it\n");
3397 res
= DoOnEvent(false);
3400 //fprintf(stderr, " skip it\n");
3408 truth
game::RunOnCharEvent (character
*who
, cfestring
&ename
) {
3410 if (!who
) return false;
3411 character
*old
= mChar
;
3413 res
= RunOnEventStr(ename
, who
->mOnEvents
);
3414 if (!res
) res
= RunOnEventStr(ename
, who
->GetProtoType()->mOnEvents
);
3420 truth
game::RunOnItemEvent (item
*what
, cfestring
&ename
) {
3422 if (!what
) return false;
3425 res
= RunOnEventStr(ename
, what
->mOnEvents
);
3426 if (!res
) res
= RunOnEventStr(ename
, what
->GetProtoType()->mOnEvents
);
3432 festring
game::ldrGetVar (inputfile
*fl
, cfestring
&name
) {
3433 //fprintf(stderr, "GETVAR: [%s]\n", name.CStr());
3434 if (name
== "player_name") {
3435 return game::GetPlayerName();
3437 if (name
== "money") {
3439 if (!mChar
) return "0";
3440 res
<< mChar
->GetMoney();
3443 if (name
== "name") {
3444 if (!mChar
) return "";
3445 return mChar
->GetAssignedName();
3447 if (name
== "team") {
3449 if (!mChar
) return "";
3450 res
<< mChar
->GetTeam()->GetID();
3453 if (name
== "friendly") {
3455 if (!mChar
|| !PLAYER
|| mChar
->GetRelation(PLAYER
) != HOSTILE
) return "tan";
3458 if (name
== "hostile") {
3460 if (!mChar
|| !PLAYER
) return "";
3461 if (mChar
->GetRelation(PLAYER
) == HOSTILE
) return "tan";
3464 if (name
== "has_item") {
3465 std::vector
<FuncArg
> args
;
3466 ParseFuncArgs("s", args
, fl
, true);
3470 festring s
= args
[0].sval
;
3472 //fprintf(stderr, "looking for [%s]\n", s.CStr());
3473 PLAYER
->GetStack()->FillItemVector(items
);
3474 for (unsigned int f
= 0; f
< items
.size(); ++f
) {
3475 for (uInt c
= 0; c
< items
[f
]->GetDataBase()->Alias
.Size
; ++c
) {
3476 //fprintf(stderr, "%u:%u: [%s]\n", f, c, items[f]->GetDataBase()->Alias[c].CStr());
3477 if (s
.CompareIgnoreCase(items
[f
]->GetDataBase()->Alias
[c
]) == 0) {
3478 //fprintf(stderr, " FOUND!\n");
3483 //fprintf(stderr, "checking equipment...\n");
3484 for (int f
= 0; f
< PLAYER
->GetEquipments(); ++f
) {
3485 item
*it
= PLAYER
->GetEquipment(f
);
3488 for (uInt c
= 0; c
< it
->GetDataBase()->Alias
.Size
; ++c
) {
3489 //fprintf(stderr, "%u:%u: [%s]\n", f, c, it->GetDataBase()->Alias[c].CStr());
3490 if (s
.CompareIgnoreCase(it
->GetDataBase()->Alias
[c
]) == 0) {
3491 //fprintf(stderr, " FOUND!\n");
3500 //if (name == "type") return mVarType;
3501 ABORT("unknown variable: %s", name
.CStr());
3506 truth
game::CheckDropLeftover (item
*i
) {
3507 if (i
->IsBottle() && !ivanconfig::GetAutoDropBottles()) return false;
3508 if (i
->IsCan() && !ivanconfig::GetAutoDropCans()) return false;
3509 if (!ivanconfig::GetAutoDropLeftOvers()) return false;
3514 truth
game::RunAllowScriptStr (cfestring
&str
) {
3516 if (str
.GetSize() < 1) return true;
3517 //fprintf(stderr, "====\n%s\n====\n", str.CStr());
3518 inputfile
*ifl
= new meminputfile(str
, &game::GetGlobalValueMap());
3519 ifl
->setGetVarCB(game::ldrGetVar
);
3521 res
= DoOnEvent(true, true);
3522 //fprintf(stderr, "mFEStack: %u\n", mFEStack.size());