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 128 // Increment this if changes make savefiles incompatible
54 #define BONE_FILE_VERSION 113 // 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 truth
game::WizardMode
;
178 int game::SeeWholeMapCheatMode
;
179 truth
game::GoThroughWallsCheat
;
180 int game::QuestMonstersFound
;
181 bitmap
*game::BusyAnimationCache
[32];
182 festring
game::PlayerName
;
183 feuLong
game::EquipmentMemory
[MAX_EQUIPMENT_SLOTS
];
184 olterrain
*game::MonsterPortal
;
185 std::vector
<v2
> game::SpecialCursorPos
;
186 std::vector
<int> game::SpecialCursorData
;
187 cbitmap
*game::EnterImage
;
188 v2
game::EnterTextDisplacement
;
192 int game::MoveVectorToDirection (cv2
&mv
) {
193 for (int c
= 0; c
< 9; ++c
) if (MoveVector
[c
] == mv
) return c
;
198 char game::GetAbnormalMoveKey (int idx
) {
199 if (idx
< 0 || idx
> 8) return 0;
200 return MoveAbnormalCommandKey
[idx
];
204 void game::SetAbnormalMoveKey (int idx
, char ch
) {
205 if (idx
>= 0 && idx
<= 8) MoveAbnormalCommandKey
[idx
] = ch
;
209 void game::AddCharacterID (character
*Char
, feuLong ID
) {
210 /*k8:??? if (CharacterIDMap.find(ID) != CharacterIDMap.end())
211 int esko = esko = 2;*/
212 CharacterIDMap
.insert(std::make_pair(ID
, Char
));
216 void game::RemoveCharacterID (feuLong ID
) {
217 /*k8:??? if (CharacterIDMap.find(ID) == CharacterIDMap.end())
218 int esko = esko = 2;*/
219 CharacterIDMap
.erase(CharacterIDMap
.find(ID
));
223 void game::AddItemID (item
*Item
, feuLong ID
) {
224 /*k8:??? if (ItemIDMap.find(ID) != ItemIDMap.end())
225 int esko = esko = 2;*/
226 ItemIDMap
.insert(std::make_pair(ID
, Item
));
230 void game::RemoveItemID (feuLong ID
) {
231 /*k8:??? if(ID && ItemIDMap.find(ID) == ItemIDMap.end())
232 int esko = esko = 2;*/
233 if (ID
) ItemIDMap
.erase(ItemIDMap
.find(ID
));
237 void game::UpdateItemID (item
*Item
, feuLong ID
) {
238 /*k8:??? if(ItemIDMap.find(ID) == ItemIDMap.end())
239 int esko = esko = 2;*/
240 ItemIDMap
.find(ID
)->second
= Item
;
244 void game::AddTrapID (entity
*Trap
, feuLong ID
) {
245 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
246 int esko = esko = 2;*/
247 if (ID
) TrapIDMap
.insert(std::make_pair(ID
, Trap
));
251 void game::RemoveTrapID (feuLong ID
) {
252 /*k8:??? if(ID && TrapIDMap.find(ID) == TrapIDMap.end())
253 int esko = esko = 2;*/
254 if (ID
) TrapIDMap
.erase(TrapIDMap
.find(ID
));
258 void game::UpdateTrapID (entity
*Trap
, feuLong ID
) {
259 /*k8:??? if(TrapIDMap.find(ID) == TrapIDMap.end())
260 int esko = esko = 2;*/
261 TrapIDMap
.find(ID
)->second
= Trap
;
265 const dangermap
&game::GetDangerMap () { return DangerMap
; }
266 void game::ClearItemDrawVector () { ItemDrawVector
.clear(); }
267 void game::ClearCharacterDrawVector () { CharacterDrawVector
.clear(); }
270 void game::InitScript () {
271 inputfile
ScriptFile(GetGameDir()+"Script/dungeon.dat", &GlobalValueMap
);
272 GameScript
= new gamescript
;
273 GameScript
->ReadFrom(ScriptFile
);
274 { /* additional dungeon files */
275 for (int f
= 0; f
<= 99; f
++) {
277 sprintf(bnum
, "Script/dungeon_%02d.dat", f
);
278 inputfile
ifl(game::GetGameDir()+bnum
, &game::GetGlobalValueMap(), false);
280 //fprintf(stderr, "loading: %s\n", bnum+7);
281 GameScript
->ReadFrom(ifl
);
286 GameScript
->RandomizeLevels();
290 truth
game::Init (cfestring
&Name
) {
291 if (Name
.IsEmpty()) {
292 if (ivanconfig::GetDefaultName().IsEmpty()) {
294 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;
296 PlayerName
= ivanconfig::GetDefaultName();
303 mkdir(GetSaveDir().CStr(), S_IRWXU
|S_IRWXG
);
304 mkdir(GetBoneDir().CStr(), S_IRWXU
|S_IRWXG
);
306 mkdir(GetSaveDir().CStr());
307 mkdir(GetBoneDir().CStr());
313 CausePanicFlag
= false;
316 switch (Load(SaveName(PlayerName
))) {
318 globalwindowhandler::InstallControlLoop(AnimationController
);
320 SetForceJumpToPlayerBe(true);
321 GetCurrentArea()->SendNewDrawRequest();
322 SendLOSUpdateRequest();
323 ADD_MESSAGE("Game loaded successfully.");
326 iosystem::TextScreen(CONST_S(
327 "You couldn't possibly have guessed this day would differ from any other.\n"
328 "It began just as always. You woke up at dawn and drove off the giant spider\n"
329 "resting on your face. On your way to work you had serious trouble avoiding\n"
330 "the lions and pythons roaming wild around the village. After getting kicked\n"
331 "by colony masters for being late you performed your twelve-hour routine of\n"
332 "climbing trees, gathering bananas, climbing trees, gathering bananas, chasing\n"
333 "monkeys that stole the first gathered bananas, carrying bananas to the village\n"
334 "and trying to look happy when real food was distributed.\n\n"
335 "Finally you were about to enjoy your free time by taking a quick dip in the\n"
336 "nearby crocodile bay. However, at this point something unusual happened.\n"
337 "You were summoned to the mansion of Richel Decos, the viceroy of the\n"
338 "colony, and were led directly to him."));
339 iosystem::TextScreen(CONST_S(
340 "\"I have a task for you, citizen\", said the viceroy picking his golden\n"
341 "teeth, \"The market price of bananas has taken a deep dive and yet the\n"
342 "central government is about to raise taxes. I have sent appeals to high\n"
343 "priest Petrus but received no response. I fear my enemies in Attnam are\n"
344 "plotting against me and intercepting my messages before they reach him!\"\n\n"
345 "\"That is why you must travel to Attnam with a letter I'll give you and\n"
346 "deliver it to Petrus directly. Alas, you somehow have to cross the sea\n"
347 "between. Because it's winter, all Attnamese ships are trapped by ice and\n"
348 "I have none. Therefore you must venture through the small underwater tunnel\n"
349 "connecting our islands. It is infested with monsters, but since you have\n"
350 "stayed alive here so long, the trip will surely cause you no trouble.\"\n\n"
351 "You have never been so happy! According to the mansion's traveling\n"
352 "brochures, Attnam is a peaceful but bustling world city on a beautiful\n"
353 "snowy fell surrounded by frozen lakes glittering in the arctic sun just\n"
354 "like the diamonds of the imperial treasury. Not that you would believe a\n"
355 "word. The point is that tomorrow you can finally forget your home and\n"
356 "face the untold adventures ahead."));
357 globalwindowhandler::InstallControlLoop(AnimationController
);
360 CausePanicFlag
= false;
363 iosystem::TextScreen(CONST_S("Generating game...\n\nThis may take some time, please wait."), ZERO_V2
, WHITE
, false, true, &BusyAnimation
);
364 igraph::CreateBackGround(GRAY_FRACTAL
);
371 SetPlayer(playerkind::Spawn());
372 Player
->SetAssignedName(PlayerName
);
373 Player
->SetTeam(GetTeam(0));
374 Player
->SetNP(SATIATED_LEVEL
);
375 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
376 if (c
!= ENDURANCE
) Player
->EditAttribute(c
, (RAND()&1)-(RAND()&1));
377 Player
->EditExperience(c
, 500, 1<<11);
379 Player
->SetMoney(Player
->GetMoney()+RAND()%11);
380 GetTeam(0)->SetLeader(Player
);
384 if (Player
->IsEnabled()) { Player
->Disable(); Player
->Enable(); }
388 SetCurrentArea(WorldMap
= new worldmap(128, 128));
389 CurrentWSquareMap
= WorldMap
->GetMap();
390 WorldMap
->Generate();
392 SendLOSUpdateRequest();
395 InitPlayerAttributeAverage();
404 OmmelBloodMission
= 0;
407 PlayerMassacreMap
.clear();
408 PetMassacreMap
.clear();
409 MiscMassacreMap
.clear();
410 PlayerMassacreAmount
= PetMassacreAmount
= MiscMassacreAmount
= 0;
411 DefaultPolymorphTo
.Empty();
412 DefaultSummonMonster
.Empty();
414 DefaultChangeMaterial
.Empty();
415 DefaultDetectMaterial
.Empty();
416 Player
->GetStack()->AddItem(encryptedscroll::Spawn());
417 if (ivanconfig::GetDefaultPetName() != "_none_") {
418 character
*Doggie
= dog::Spawn();
419 Doggie
->SetTeam(GetTeam(0));
420 GetWorldMap()->GetPlayerGroup().push_back(Doggie
);
421 Doggie
->SetAssignedName(ivanconfig::GetDefaultPetName());
424 SeeWholeMapCheatMode
= MAP_HIDDEN
;
425 GoThroughWallsCheat
= false;
426 SumoWrestling
= false;
427 GlobalRainTimeModifier
= 2048-(RAND()&4095);
428 PlayerSumoChampion
= false;
429 PlayerSolicitusChampion
= false;
430 protosystem::InitCharacterDataBaseFlags();
431 memset(EquipmentMemory
, 0, sizeof(EquipmentMemory
));
432 PlayerRunning
= false;
433 InitAttributeMemory();
437 TimePlayedBeforeLastLoad
= time::GetZeroTime();
438 /*k8: damn! seems that this is field, not local! bool PlayerHasReceivedAllGodsKnownBonus = false; */
439 PlayerHasReceivedAllGodsKnownBonus
= false;
440 ADD_MESSAGE("You commence your journey to Attnam. Use direction keys to move, '>' to enter an area and '?' to view other commands.");
441 game::ClearEventData();
442 RunOnEvent("game_start");
444 item
*Present
= banana::Spawn();
445 Player
->GetStack()->AddItem(Present
);
446 ADD_MESSAGE("Atavus is happy today! He gives you %s.", Present
->CHAR_NAME(INDEFINITE
));
449 default: return false;
454 void game::DeInit () {
459 for (int c
= 1; c
< Dungeons
; ++c
) delete Dungeon
[c
];
464 for (int c
= 1; c
<= GODS
; ++c
) delete God
[c
]; // sorry, Valpuri!
469 for (int c
= 0; c
< Teams
; ++c
) delete Team
[c
];
483 /* Temporary places */
484 static int Counter
= 0;
485 if (++Counter
== 10) {
486 CurrentLevel
->GenerateMonsters();
489 if (CurrentDungeonIndex
== ELPURI_CAVE
&& CurrentLevelIndex
== ZOMBIE_LEVEL
&& !RAND_N(1000+NecroCounter
)) {
490 character
*Char
= necromancer::Spawn(RAND_N(4) ? APPRENTICE_NECROMANCER
: MASTER_NECROMANCER
);
492 for (int c2
= 0; c2
< 30; ++c2
) {
493 Pos
= GetCurrentLevel()->GetRandomSquare(Char
);
494 if (abs(int(Pos
.X
)-Player
->GetPos().X
) > 20 || abs(int(Pos
.Y
)-Player
->GetPos().Y
) > 20) break;
496 if (Pos
!= ERROR_V2
) {
497 Char
->SetTeam(GetTeam(MONSTER_TEAM
));
499 Char
->SetGenerationDanger(GetCurrentLevel()->GetDifficulty());
500 Char
->SignalGeneration();
501 Char
->SignalNaturalGeneration();
504 int Modifier
= Time
.Day
- EDIT_ATTRIBUTE_DAY_MIN
;
505 if (Modifier
> 0) Char
->EditAllAttributes(Modifier
>> EDIT_ATTRIBUTE_DAY_SHIFT
);
509 //Char->SendToHell(); // k8:equipment
513 if (!(GetTick() % 1000)) CurrentLevel
->CheckSunLight();
515 if ((CurrentDungeonIndex
== NEW_ATTNAM
|| CurrentDungeonIndex
== ATTNAM
) && CurrentLevelIndex
== 0) {
516 sLong OldVolume
= GlobalRainLiquid
->GetVolume();
517 sLong NewVolume
= Max(sLong(sin((Tick
+GlobalRainTimeModifier
)*0.0003)*300-150), 0);
518 if (NewVolume
&& !OldVolume
) CurrentLevel
->EnableGlobalRain();
519 else if(!NewVolume
&& OldVolume
) CurrentLevel
->DisableGlobalRain();
520 GlobalRainLiquid
->SetVolumeNoSignals(NewVolume
);
524 if (!RAND_N(2)) Item = wand::Spawn(1 + RAND_N(12));
525 else if(!RAND_N(2)) {
526 Item = beartrap::Spawn();
527 Item->SetIsActive(true);
528 Item->SetTeam(MONSTER_TEAM);
529 } else if(!RAND_N(2)) {
530 Item = mine::Spawn();
531 Item->SetIsActive(true);
532 Item->SetTeam(MONSTER_TEAM);
533 } else Item = holybanana::Spawn();
534 CurrentLevel->GetLSquare(CurrentLevel->GetRandomSquare())->AddItem(Item);
538 character *Char = protosystem::CreateMonster(0, 1000000);
539 Char->ChangeTeam(GetTeam(RAND() % Teams));
540 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
545 if (!RAND_N(5)) Char = spider::Spawn(GIANT);
546 else if (!RAND_N(5)) Char = darkmage::Spawn(1 + RAND_N(4));
547 else if (!RAND_N(5)) Char = necromancer::Spawn(1 + RAND_N(2));
548 else if (!RAND_N(5)) Char = chameleon::Spawn();
549 else if (!RAND_N(5)) Char = kamikazedwarf::Spawn(1 + RAND_N(GODS));
550 else if (!RAND_N(5)) Char = mommo::Spawn(1 + RAND_N(2));
551 else if (!RAND_N(3)) Char = bunny::Spawn(RAND_2 ? ADULT_MALE : ADULT_FEMALE);
552 else if (!RAND_N(3)) Char = eddy::Spawn();
553 else if (!RAND_N(3)) Char = magicmushroom::Spawn();
554 else if (!RAND_N(5)) Char = mushroom::Spawn();
555 else if (!RAND_N(3)) Char = blinkdog::Spawn();
556 else if (!RAND_N(5)) Char = tourist::Spawn(1 + RAND_N(3));
557 else if (!RAND_N(5)) Char = hattifattener::Spawn();
558 else if (!RAND_N(5)) Char = genetrixvesana::Spawn();
559 else if (!RAND_N(5)) Char = skunk::Spawn();
560 else if (!RAND_N(5)) Char = ennerbeast::Spawn();
561 else if (!RAND_N(5)) Char = werewolfhuman::Spawn();
562 else if (!RAND_N(5)) Char = unicorn::Spawn(1 + RAND_N(3));
563 else if (!RAND_N(5)) Char = floatingeye::Spawn();
564 else if (!RAND_N(5)) Char = zombie::Spawn();
565 else if (!RAND_N(5)) Char = magpie::Spawn();
566 else if (!RAND_N(5)) Char = elpuri::Spawn();
567 else if (!RAND_N(5)) Char = vladimir::Spawn();
568 else if (!RAND_N(5)) Char = billswill::Spawn();
569 else if (!RAND_N(5)) Char = ghost::Spawn();
570 else if (!RAND_N(5)) Char = dolphin::Spawn();
571 else if (!RAND_N(5)) Char = cossack::Spawn();
572 else Char = invisiblestalker::Spawn();
573 Char->SetTeam(GetTeam(RAND() % Teams));
574 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
585 } catch (quitrequest
) {
587 } catch (areachangerequest
) {
593 void game::InitLuxTable () {
595 Alloc3D(LuxTable
, 256, 33, 33);
596 for (int c
= 0; c
< 0x100; ++c
)
597 for (int x
= 0; x
< 33; ++x
)
598 for (int y
= 0; y
< 33; ++y
) {
599 int X
= x
-16, Y
= y
-16;
600 LuxTable
[c
][x
][y
] = int(c
/(double(X
*X
+Y
*Y
)/128+1));
602 atexit(DeInitLuxTable
);
607 void game::DeInitLuxTable () {
613 void game::UpdateCameraX () {
614 UpdateCameraX(Player
->GetPos().X
);
618 void game::UpdateCameraY () {
619 UpdateCameraY(Player
->GetPos().Y
);
623 void game::UpdateCameraX (int X
) {
624 UpdateCameraCoordinate(Camera
.X
, X
, GetCurrentArea()->GetXSize(), GetScreenXSize());
628 void game::UpdateCameraY (int Y
) {
629 UpdateCameraCoordinate(Camera
.Y
, Y
, GetCurrentArea()->GetYSize(), GetScreenYSize());
633 void game::UpdateCameraCoordinate (int &Coordinate
, int Center
, int Size
, int ScreenSize
) {
634 int OldCoordinate
= Coordinate
;
635 if (Size
< ScreenSize
) Coordinate
= (Size
-ScreenSize
)>>1;
636 else if(Center
< ScreenSize
>>1) Coordinate
= 0;
637 else if(Center
> Size
-(ScreenSize
>>1)) Coordinate
= Size
-ScreenSize
;
638 else Coordinate
= Center
-(ScreenSize
>>1);
639 if (Coordinate
!= OldCoordinate
) GetCurrentArea()->SendNewDrawRequest();
643 cchar
*game::Insult () {
644 static const char *insults
[19] = {
657 "stupid-headed person",
661 "person-with-problems",
666 if (n
< 0 || n
> 18) n
= 18;
671 /* DefaultAnswer = REQUIRES_ANSWER the question requires an answer */
672 truth
game::TruthQuestion (cfestring
&String
, int DefaultAnswer
, int OtherKeyForTrue
) {
673 if (DefaultAnswer
== NO
) DefaultAnswer
= 'n';
674 else if (DefaultAnswer
== YES
) DefaultAnswer
= 'y';
675 else if (DefaultAnswer
!= REQUIRES_ANSWER
) ABORT("Illegal TruthQuestion DefaultAnswer send!");
676 int FromKeyQuestion
= KeyQuestion(String
, DefaultAnswer
, 9, 'y', 'Y', 'n', 'N', 't', 'T', 'o', 'O', OtherKeyForTrue
);
678 FromKeyQuestion
== 'y' || FromKeyQuestion
== 'Y' ||
679 FromKeyQuestion
== 't' || FromKeyQuestion
== 'T' ||
680 FromKeyQuestion
== OtherKeyForTrue
;
684 void game::DrawEverything () {
685 DrawEverythingNoBlit();
686 graphics::BlitDBToScreen();
690 truth
game::OnScreen (v2 Pos
) {
691 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();
695 void game::DrawEverythingNoBlit (truth AnimationDraw
) {
696 if (LOSUpdateRequested
&& Player
->IsEnabled()) {
697 if (!IsInWilderness()) GetCurrentLevel()->UpdateLOS(); else GetWorldMap()->UpdateLOS();
700 if (OnScreen(CursorPos
)) {
701 if (!IsInWilderness() || CurrentWSquareMap
[CursorPos
.X
][CursorPos
.Y
]->GetLastSeen() || GetSeeWholeMapCheatMode())
702 CurrentArea
->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
704 DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, 0);
707 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
708 if (OnScreen(SpecialCursorPos
[c
])) CurrentArea
->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
711 globalwindowhandler::UpdateTick();
712 GetCurrentArea()->Draw(AnimationDraw
);
713 Player
->DrawPanel(AnimationDraw
);
715 if (!AnimationDraw
) msgsystem::Draw();
717 if (OnScreen(CursorPos
)) {
718 v2 ScreenCoord
= CalculateScreenCoordinates(CursorPos
);
722 { ScreenCoord
.X
, ScreenCoord
.Y
},
723 { TILE_SIZE
, TILE_SIZE
},
726 ALLOW_ANIMATE
|ALLOW_ALPHA
729 if (!IsInWilderness() && !GetSeeWholeMapCheatMode()) {
730 lsquare
*Square
= CurrentLSquareMap
[CursorPos
.X
][CursorPos
.Y
];
731 if (Square
->GetLastSeen() != GetLOSTick()) Square
->DrawMemorized(B
);
736 B
.Dest
.X
= RES
.X
- 96;
737 B
.Dest
.Y
= RES
.Y
- 96;
739 DOUBLE_BUFFER
->StretchBlit(B
);
742 igraph::DrawCursor(ScreenCoord
, CursorData
);
745 if (Player
->IsEnabled()) {
746 if (Player
->IsSmall()) {
747 v2 Pos
= Player
->GetPos();
749 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
750 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData());
753 for (int f
= 0; f
< Player
->GetSquaresUnder(); ++f
) {
754 v2 Pos
= Player
->GetPos(f
);
756 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
757 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData()|CURSOR_BIG
, f
);
763 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
764 if (OnScreen(SpecialCursorPos
[c
])) {
765 v2 ScreenCoord
= CalculateScreenCoordinates(SpecialCursorPos
[c
]);
766 igraph::DrawCursor(ScreenCoord
, SpecialCursorData
[c
]);
767 GetCurrentArea()->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
773 truth
game::Save (cfestring
&SaveName
) {
774 if (!GetCurrentArea()->GetSquare(Player
->GetPos())->GetCharacter()) {
775 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
);
778 DrawEverythingNoBlit();
779 #if defined(SGAME_SHOTS_IPU) || !defined(HAVE_IMLIB2)
780 DOUBLE_BUFFER
->SaveScaledIPU(SaveName
+".ipu", 0.8); //640; 320
782 DOUBLE_BUFFER
->SaveScaledPNG(SaveName
+".png", 0.8); //640; 320
784 outputfile
SaveFile(SaveName
+".sav", ivanconfig::GetUseMaximumCompression());
785 SaveFile
<< int(SAVE_FILE_VERSION
);
786 SaveFile
<< GameScript
<< CurrentDungeonIndex
<< CurrentLevelIndex
<< Camera
;
787 SaveFile
<< WizardMode
<< SeeWholeMapCheatMode
<< GoThroughWallsCheat
;
788 SaveFile
<< Tick
<< Turn
<< InWilderness
<< NextCharacterID
<< NextItemID
<< NextTrapID
<< NecroCounter
;
789 SaveFile
<< SumoWrestling
<< PlayerSumoChampion
<< GlobalRainTimeModifier
;
790 SaveFile
<< PlayerSolicitusChampion
;
792 femath::SetSeed(Seed
);
794 SaveFile
<< AveragePlayerArmStrengthExperience
;
795 SaveFile
<< AveragePlayerLegStrengthExperience
;
796 SaveFile
<< AveragePlayerDexterityExperience
;
797 SaveFile
<< AveragePlayerAgilityExperience
;
798 SaveFile
<< Teams
<< Dungeons
<< StoryState
<< PlayerRunning
;
799 SaveFile
<< MondedrPass
<< RingOfThieves
<< Masamune
<< Muramasa
<< LoricatusHammer
<< Liberator
;
800 SaveFile
<< OmmelBloodMission
<< RegiiTalkState
;
801 SaveFile
<< PlayerMassacreMap
<< PetMassacreMap
<< MiscMassacreMap
;
802 SaveFile
<< PlayerMassacreAmount
<< PetMassacreAmount
<< MiscMassacreAmount
;
803 SaveArray(SaveFile
, EquipmentMemory
, MAX_EQUIPMENT_SLOTS
);
805 for (c
= 0; c
< ATTRIBUTES
; ++c
) SaveFile
<< OldAttribute
[c
] << NewAttribute
[c
] << LastAttributeChangeTick
[c
];
806 for (c
= 1; c
< Dungeons
; ++c
) SaveFile
<< Dungeon
[c
];
807 for (c
= 1; c
<= GODS
; ++c
) SaveFile
<< God
[c
];
808 for (c
= 0; c
< Teams
; ++c
) SaveFile
<< Team
[c
];
810 SaveWorldMap(SaveName
, false);
812 GetCurrentDungeon()->SaveLevel(SaveName
, CurrentLevelIndex
, false);
814 SaveFile
<< Player
->GetPos() << PlayerName
;
815 msgsystem::Save(SaveFile
);
816 SaveFile
<< DangerMap
<< NextDangerIDType
<< NextDangerIDConfigIndex
;
817 SaveFile
<< DefaultPolymorphTo
<< DefaultSummonMonster
;
818 SaveFile
<< DefaultWish
<< DefaultChangeMaterial
<< DefaultDetectMaterial
;
819 SaveFile
<< GetTimeSpent();
820 /* or in more readable format: time() - LastLoad + TimeAtLastLoad */
821 SaveFile
<< PlayerHasReceivedAllGodsKnownBonus
;
822 protosystem::SaveCharacterDataBaseFlags(SaveFile
);
827 int game::Load (cfestring
&SaveName
) {
828 inputfile
SaveFile(SaveName
+".sav", 0, false);
829 if (!SaveFile
.IsOpen()) return NEW_GAME
;
832 if (Version
!= SAVE_FILE_VERSION
) {
833 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
)) {
839 SaveFile
>> GameScript
>> CurrentDungeonIndex
>> CurrentLevelIndex
>> Camera
;
840 SaveFile
>> WizardMode
>> SeeWholeMapCheatMode
>> GoThroughWallsCheat
;
841 SaveFile
>> Tick
>> Turn
>> InWilderness
>> NextCharacterID
>> NextItemID
>> NextTrapID
>> NecroCounter
;
842 SaveFile
>> SumoWrestling
>> PlayerSumoChampion
>> GlobalRainTimeModifier
;
843 SaveFile
>> PlayerSolicitusChampion
;
844 femath::SetSeed(ReadType
<sLong
>(SaveFile
));
845 SaveFile
>> AveragePlayerArmStrengthExperience
;
846 SaveFile
>> AveragePlayerLegStrengthExperience
;
847 SaveFile
>> AveragePlayerDexterityExperience
;
848 SaveFile
>> AveragePlayerAgilityExperience
;
849 SaveFile
>> Teams
>> Dungeons
>> StoryState
>> PlayerRunning
;
850 SaveFile
>> MondedrPass
>> RingOfThieves
>> Masamune
>> Muramasa
>> LoricatusHammer
>> Liberator
;
851 SaveFile
>> OmmelBloodMission
>> RegiiTalkState
;
852 SaveFile
>> PlayerMassacreMap
>> PetMassacreMap
>> MiscMassacreMap
;
853 SaveFile
>> PlayerMassacreAmount
>> PetMassacreAmount
>> MiscMassacreAmount
;
854 LoadArray(SaveFile
, EquipmentMemory
, MAX_EQUIPMENT_SLOTS
);
855 for (int c
= 0; c
< ATTRIBUTES
; ++c
) SaveFile
>> OldAttribute
[c
] >> NewAttribute
[c
] >> LastAttributeChangeTick
[c
];
856 Dungeon
= new dungeon
*[Dungeons
];
858 for (int c
= 1; c
< Dungeons
; ++c
) SaveFile
>> Dungeon
[c
];
859 God
= new god
*[GODS
+1];
861 for (int c
= 1; c
<= GODS
; ++c
) SaveFile
>> God
[c
];
862 Team
= new team
*[Teams
];
863 for (int c
= 0; c
< Teams
; ++c
) SaveFile
>> Team
[c
];
865 SetCurrentArea(LoadWorldMap(SaveName
));
866 CurrentWSquareMap
= WorldMap
->GetMap();
867 igraph::CreateBackGround(GRAY_FRACTAL
);
869 SetCurrentArea(CurrentLevel
= GetCurrentDungeon()->LoadLevel(SaveName
, CurrentLevelIndex
));
870 CurrentLSquareMap
= CurrentLevel
->GetMap();
871 igraph::CreateBackGround(*CurrentLevel
->GetLevelScript()->GetBackGroundType());
874 SaveFile
>> Pos
>> PlayerName
;
875 SetPlayer(GetCurrentArea()->GetSquare(Pos
)->GetCharacter());
878 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
)) {
884 msgsystem::Load(SaveFile
);
885 SaveFile
>> DangerMap
>> NextDangerIDType
>> NextDangerIDConfigIndex
;
886 SaveFile
>> DefaultPolymorphTo
>> DefaultSummonMonster
;
887 SaveFile
>> DefaultWish
>> DefaultChangeMaterial
>> DefaultDetectMaterial
;
888 SaveFile
>> TimePlayedBeforeLastLoad
;
889 SaveFile
>> PlayerHasReceivedAllGodsKnownBonus
;
891 protosystem::LoadCharacterDataBaseFlags(SaveFile
);
896 festring
game::SaveName (cfestring
&Base
) {
897 festring SaveName
= GetSaveDir();
898 if (!Base
.GetSize()) SaveName
<< PlayerName
; else SaveName
<< Base
;
899 for (festring::sizetype c
= 0; c
< SaveName
.GetSize(); ++c
) if (SaveName
[c
] == ' ') SaveName
[c
] = '_';
904 int game::GetMoveCommandKeyBetweenPoints (v2 A
, v2 B
) {
905 for (int c
= 0; c
< EXTENDED_DIRECTION_COMMAND_KEYS
; ++c
) {
906 if ((A
+ GetMoveVector(c
)) == B
) return GetMoveCommandKey(c
);
912 void game::ApplyDivineTick () {
913 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->ApplyDivineTick();
917 void game::ApplyDivineAlignmentBonuses (god
*CompareTarget
, int Multiplier
, truth Good
) {
918 for (int c
= 1; c
<= GODS
; ++c
) if (GetGod(c
) != CompareTarget
) GetGod(c
)->AdjustRelation(CompareTarget
, Multiplier
, Good
);
922 v2
game::GetDirectionVectorForKey (int Key
) {
923 if (Key
== KEY_NUMPAD_5
|| Key
== '.') return ZERO_V2
; /* k8: '.' */
924 for (int c
= 0; c
< EXTENDED_DIRECTION_COMMAND_KEYS
; ++c
) if (Key
== GetMoveCommandKey(c
)) return GetMoveVector(c
);
929 double game::GetMinDifficulty () {
930 double Base
= CurrentLevel
->GetDifficulty()*0.2;
931 sLong MultiplierExponent
= 0;
934 int Modifier
= Time
.Day
-DANGER_PLUS_DAY_MIN
;
935 if (Modifier
> 0) Base
+= DANGER_PLUS_MULTIPLIER
* Modifier
;
937 int Dice
= RAND()%25;
938 if (Dice
< 5 && MultiplierExponent
> -3) {
940 --MultiplierExponent
;
943 if (Dice
>= 20 && MultiplierExponent
< 3) {
945 ++MultiplierExponent
;
953 void game::ShowLevelMessage () {
954 if (CurrentLevel
->GetLevelMessage().GetSize()) ADD_MESSAGE("%s", CurrentLevel
->GetLevelMessage().CStr());
955 CurrentLevel
->SetLevelMessage("");
959 int game::DirectionQuestion (cfestring
&Topic
, truth RequireAnswer
, truth AcceptYourself
) {
961 int Key
= AskForKeyPress(Topic
);
962 if (AcceptYourself
&& (Key
== '.' || Key
== KEY_NUMPAD_5
)) return YOURSELF
; //k8
963 for (int c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) if (Key
== GetMoveCommandKey(c
)) return c
;
964 if (!RequireAnswer
) return DIR_ERROR
;
969 void game::RemoveSaves (truth RealSavesAlso
) {
971 remove(festring(SaveName()+".sav").CStr());
972 remove(festring(SaveName()+".wm").CStr());
973 remove(festring(SaveName()+".png").CStr());
974 remove(festring(SaveName()+".ipu").CStr());
976 remove(festring(AutoSaveFileName
+".sav").CStr());
977 remove(festring(AutoSaveFileName
+".wm").CStr());
978 remove(festring(AutoSaveFileName
+".png").CStr());
979 remove(festring(AutoSaveFileName
+".ipu").CStr());
981 for (int i
= 1; i
< Dungeons
; ++i
) {
982 for (int c
= 0; c
< GetDungeon(i
)->GetLevels(); ++c
) {
983 /* This looks very odd. And it is very odd.
984 * Indeed, gcc is very odd to not compile this correctly with -O3
985 * if it is written in a less odd way. */
986 File
= SaveName()+'.'+i
;
988 if (RealSavesAlso
) remove(File
.CStr());
989 File
= AutoSaveFileName
+'.'+i
;
997 void game::SetPlayer (character
*NP
) {
999 if (Player
) Player
->AddFlags(C_PLAYER
);
1003 void game::InitDungeons () {
1004 Dungeons
= *GetGameScript()->GetDungeons()+1;
1005 //fprintf(stderr, "dungeon count: %d\n", Dungeons);
1006 Dungeon
= new dungeon
*[Dungeons
];
1008 for (int c
= 1; c
< Dungeons
; ++c
) {
1009 Dungeon
[c
] = new dungeon(c
);
1010 Dungeon
[c
]->SetIndex(c
);
1015 void game::DoEvilDeed (int Amount
) {
1016 if (!Amount
) return;
1017 for (int c
= 1; c
<= GODS
; ++c
) {
1018 int Change
= Amount
-Amount
*GetGod(c
)->GetAlignment()/5;
1019 if (!IsInWilderness() && Player
->GetLSquareUnder()->GetDivineMaster() == c
) {
1020 if (GetGod(c
)->GetRelation()-(Change
<< 1) < -750) {
1021 if (GetGod(c
)->GetRelation() > -750) GetGod(c
)->SetRelation(-750);
1022 } else if (GetGod(c
)->GetRelation()-(Change
<< 1) > 750) {
1023 if (GetGod(c
)->GetRelation() < 750) GetGod(c
)->SetRelation(750);
1024 } else GetGod(c
)->SetRelation(GetGod(c
)->GetRelation()-(Change
<< 1));
1026 if(GetGod(c
)->GetRelation()-Change
< -500) {
1027 if (GetGod(c
)->GetRelation() > -500) GetGod(c
)->SetRelation(-500);
1028 } else if (GetGod(c
)->GetRelation()-Change
> 500) {
1029 if (GetGod(c
)->GetRelation() < 500) GetGod(c
)->SetRelation(500);
1030 } else GetGod(c
)->SetRelation(GetGod(c
)->GetRelation() - Change
);
1036 void game::SaveWorldMap (cfestring
&SaveName
, truth DeleteAfterwards
) {
1037 outputfile
SaveFile(SaveName
+".wm", ivanconfig::GetUseMaximumCompression());
1038 SaveFile
<< WorldMap
;
1039 if (DeleteAfterwards
) {
1046 worldmap
*game::LoadWorldMap (cfestring
&SaveName
) {
1047 inputfile
SaveFile(SaveName
+".wm");
1048 SaveFile
>> WorldMap
;
1053 void game::Hostility (team
*Attacker
, team
*Defender
) {
1054 for (int c
= 0; c
< Teams
; ++c
) {
1055 if (GetTeam(c
) != Attacker
&& GetTeam(c
) != Defender
&&
1056 GetTeam(c
)->GetRelation(Defender
) == FRIEND
&&
1057 c
!= NEW_ATTNAM_TEAM
&& c
!= TOURIST_GUIDE_TEAM
) // gum solution
1058 GetTeam(c
)->SetRelation(Attacker
, HOSTILE
);
1063 void game::CreateTeams () {
1064 Teams
= *GetGameScript()->GetTeams();
1065 //fprintf(stderr, "team count: %d\n", Teams);
1066 Team
= new team
*[Teams
];
1068 for (c
= 0; c
< Teams
; ++c
) {
1069 Team
[c
] = new team(c
);
1070 for (int i
= 0; i
< c
; ++i
) Team
[i
]->SetRelation(Team
[c
], UNCARING
);
1072 for (c
= 0; c
< Teams
; ++c
) if (c
!= 1) Team
[1]->SetRelation(Team
[c
], HOSTILE
);
1073 const std::list
<std::pair
<int, teamscript
> >& TeamScript
= GetGameScript()->GetTeam();
1074 for (std::list
<std::pair
<int, teamscript
> >::const_iterator i
= TeamScript
.begin(); i
!= TeamScript
.end(); ++i
) {
1075 for (uInt c
= 0; c
< i
->second
.GetRelation().size(); ++c
) {
1076 GetTeam(i
->second
.GetRelation()[c
].first
)->SetRelation(GetTeam(i
->first
), i
->second
.GetRelation()[c
].second
);
1078 cint
*KillEvilness
= i
->second
.GetKillEvilness();
1079 if (KillEvilness
) GetTeam(i
->first
)->SetKillEvilness(*KillEvilness
);
1084 /* v2 Pos should be removed from xxxQuestion()s? */
1085 festring
game::StringQuestion (cfestring
&Topic
, col16 Color
, festring::sizetype MinLetters
, festring::sizetype MaxLetters
, truth AllowExit
, stringkeyhandler KeyHandler
) {
1086 DrawEverythingNoBlit();
1087 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); // pos may be incorrect!
1089 iosystem::StringQuestion(Return
, Topic
, v2(16, 6), Color
, MinLetters
, MaxLetters
, false, AllowExit
, KeyHandler
);
1090 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1095 sLong
game::NumberQuestion (cfestring
&Topic
, col16 Color
, truth ReturnZeroOnEsc
) {
1096 DrawEverythingNoBlit();
1097 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1098 sLong Return
= iosystem::NumberQuestion(Topic
, v2(16, 6), Color
, false, ReturnZeroOnEsc
);
1099 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1104 sLong
game::ScrollBarQuestion (cfestring
&Topic
, sLong BeginValue
, sLong Step
, sLong Min
, sLong Max
, sLong AbortValue
, col16 TopicColor
, col16 Color1
, col16 Color2
, void (*Handler
)(sLong
)) {
1105 DrawEverythingNoBlit();
1106 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1107 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
);
1108 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1113 feuLong
game::IncreaseLOSTick () {
1114 if (LOSTick
!= 0xFE) return LOSTick
+= 2;
1115 CurrentLevel
->InitLastSeen();
1120 void game::UpdateCamera () {
1126 truth
game::HandleQuitMessage () {
1128 if (IsInGetCommand()) {
1129 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
)) {
1135 GetCurrentArea()->SendNewDrawRequest();
1139 festring Msg
= CONST_S("cowardly quit the game");
1140 Player
->AddScoreEntry(Msg
, 0.75);
1141 End(Msg
, true, false);
1144 } 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
)) {
1147 GetCurrentArea()->SendNewDrawRequest();
1156 int game::GetDirectionForVector (v2 Vector
) {
1157 for (int c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) if (Vector
== GetMoveVector(c
)) return c
;
1162 cchar
*game::GetVerbalPlayerAlignment () {
1164 for (int c
= 1; c
<= GODS
; ++c
) {
1165 if (GetGod(c
)->GetRelation() > 0) Sum
+= GetGod(c
)->GetRelation() * (5 - GetGod(c
)->GetAlignment());
1167 if (Sum
> 15000) return "extremely lawful";
1168 if (Sum
> 10000) return "very lawful";
1169 if (Sum
> 5000) return "lawful";
1170 if (Sum
> 1000) return "mildly lawful";
1171 if (Sum
> -1000) return "neutral";
1172 if (Sum
> -5000) return "mildly chaotic";
1173 if (Sum
> -10000) return "chaotic";
1174 if (Sum
> -15000) return "very chaotic";
1175 return "extremely chaotic";
1179 void game::CreateGods () {
1180 God
= new god
*[GODS
+1];
1182 for (int c
= 1; c
< protocontainer
<god
>::GetSize(); ++c
) God
[c
] = protocontainer
<god
>::GetProto(c
)->Spawn();
1186 void game::BusyAnimation () {
1187 BusyAnimation(DOUBLE_BUFFER
, false);
1191 void game::BusyAnimation (bitmap
*Buffer
, truth ForceDraw
) {
1192 static clock_t LastTime
= 0;
1193 static int Frame
= 0;
1194 static blitdata B1
= {
1203 static blitdata B2
= {
1206 { (RES
.X
>> 1) - 100, (RES
.Y
<< 1) / 3 - 100 },
1212 if (ForceDraw
|| clock()-LastTime
> CLOCKS_PER_SEC
/25) {
1214 B2
.Dest
.X
= (RES
.X
>>1)-100+EnterTextDisplacement
.X
;
1215 B2
.Dest
.Y
= (RES
.Y
<<1)/3-100+EnterTextDisplacement
.Y
;
1218 EnterImage
->NormalMaskedBlit(B1
);
1220 BusyAnimationCache
[Frame
]->NormalBlit(B2
);
1221 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
1222 if (++Frame
== 32) Frame
= 0;
1228 void game::CreateBusyAnimationCache () {
1229 bitmap
Elpuri(TILE_V2
, TRANSPARENT_COLOR
);
1230 Elpuri
.ActivateFastFlag();
1231 packcol16 Color
= MakeRGB16(60, 60, 60);
1232 igraph::GetCharacterRawGraphic()->MaskedBlit(&Elpuri
, v2(64, 0), ZERO_V2
, TILE_V2
, &Color
);
1233 bitmap
Circle(v2(200, 200), TRANSPARENT_COLOR
);
1234 Circle
.ActivateFastFlag();
1235 for (int x
= 0; x
< 4; ++x
) Circle
.DrawPolygon(100, 100, 95+x
, 50, MakeRGB16(255-12*x
, 0, 0));
1240 { TILE_SIZE
, TILE_SIZE
},
1254 for (int c
= 0; c
< 32; ++c
) {
1255 B1
.Bitmap
= B2
.Bitmap
= BusyAnimationCache
[c
] = new bitmap(v2(200, 200), 0);
1256 B1
.Bitmap
->ActivateFastFlag();
1257 Elpuri
.NormalMaskedBlit(B1
);
1258 double Rotation
= 0.3+c
*FPI
/80;
1259 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);
1260 Circle
.NormalMaskedBlit(B2
);
1265 int game::AskForKeyPress (cfestring
&Topic
) {
1266 DrawEverythingNoBlit();
1267 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CapitalizeCopy().CStr());
1268 graphics::BlitDBToScreen();
1269 int Key
= GET_KEY();
1270 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1275 void game::AskForEscPress (cfestring
&Topic
) {
1276 DrawEverythingNoBlit();
1277 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), RED
/*WHITE*/, "%s [press ESC]", Topic
.CapitalizeCopy().CStr());
1278 graphics::BlitDBToScreen();
1282 } while (Key
!= KEY_ESC
);
1283 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1287 /* Handler is called when the key has been identified as a movement key
1288 * KeyHandler is called when the key has NOT been identified as a movement key
1289 * Both can be deactivated by passing 0 as parameter */
1290 v2
game::PositionQuestion (cfestring
&Topic
, v2 CursorPos
, void (*Handler
)(v2
), positionkeyhandler KeyHandler
, truth Zoom
) {
1294 CursorData
= RED_CURSOR
;
1295 if (Handler
) Handler(CursorPos
);
1297 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1298 if (!Square
->HasBeenSeen() &&
1299 (!Square
->GetCharacter() || !Square
->GetCharacter()->CanBeSeenByPlayer()) &&
1300 !GetSeeWholeMapCheatMode()) DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, BLACK
);
1301 else GetCurrentArea()->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
1303 if (Key
== ' ' || Key
== '.' || Key
== KEY_NUMPAD_5
) { Return
= CursorPos
; break; }
1304 if (Key
== KEY_ESC
) { Return
= ERROR_V2
; break; }
1306 v2 DirectionVector
= GetDirectionVectorForKey(Key
);
1307 if (DirectionVector
!= ERROR_V2
) {
1308 CursorPos
+= DirectionVector
;
1309 if (CursorPos
.X
> GetCurrentArea()->GetXSize()-1) CursorPos
.X
= 0;
1310 if (CursorPos
.X
< 0) CursorPos
.X
= GetCurrentArea()->GetXSize()-1;
1311 if (CursorPos
.Y
> GetCurrentArea()->GetYSize()-1) CursorPos
.Y
= 0;
1312 if (CursorPos
.Y
< 0) CursorPos
.Y
= GetCurrentArea()->GetYSize()-1;
1313 if (Handler
) Handler(CursorPos
);
1314 } else if (KeyHandler
) {
1315 CursorPos
= KeyHandler(CursorPos
, Key
);
1316 if (CursorPos
== ERROR_V2
|| CursorPos
== ABORT_V2
) {
1322 if (ivanconfig::GetAutoCenterMapOnLook()) {
1323 UpdateCameraX(CursorPos
.X
);
1324 UpdateCameraY(CursorPos
.Y
);
1326 if (CursorPos
.X
< GetCamera().X
+3 || CursorPos
.X
>= GetCamera().X
+GetScreenXSize()-3) UpdateCameraX(CursorPos
.X
);
1327 if (CursorPos
.Y
< GetCamera().Y
+3 || CursorPos
.Y
>= GetCamera().Y
+GetScreenYSize()-3) UpdateCameraY(CursorPos
.Y
);
1330 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CStr());
1331 SetCursorPos(CursorPos
);
1336 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1337 igraph::BlitBackGround(v2(RES
.X
-96, RES
.Y
-96), v2(80, 80));
1339 SetCursorPos(v2(-1, -1));
1344 void game::LookHandler (v2 CursorPos
) {
1345 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1348 if (GetSeeWholeMapCheatMode()) {
1349 OldMemory
= Square
->GetMemorizedDescription();
1350 if (IsInWilderness()) GetWorldMap()->GetWSquare(CursorPos
)->UpdateMemorizedDescription(true);
1351 else GetCurrentLevel()->GetLSquare(CursorPos
)->UpdateMemorizedDescription(true);
1355 if (Square
->HasBeenSeen() || GetSeeWholeMapCheatMode()) {
1356 if (!IsInWilderness() && !Square
->CanBeSeenByPlayer() && GetCurrentLevel()->GetLSquare(CursorPos
)->CanBeFeltByPlayer())
1357 Msg
= CONST_S("You feel here ");
1358 else if (Square
->CanBeSeenByPlayer(true) || GetSeeWholeMapCheatMode())
1359 Msg
= CONST_S("You see here ");
1361 Msg
= CONST_S("You remember here ");
1362 Msg
<< Square
->GetMemorizedDescription() << '.';
1363 if (!IsInWilderness() && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) {
1364 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1365 LSquare
->DisplaySmokeInfo(Msg
);
1366 if (LSquare
->HasEngravings() && LSquare
->IsTransparent()) {
1367 if (LSquare
->EngravingsCanBeReadByPlayer() || GetSeeWholeMapCheatMode()) LSquare
->DisplayEngravedInfo(Msg
);
1368 else Msg
<< " Something has been engraved here.";
1371 } else Msg
= CONST_S("You have never been here.");
1372 character
*Character
= Square
->GetCharacter();
1373 if (Character
&& (Character
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Character
->DisplayInfo(Msg
);
1374 if (!(RAND()%10000) && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Msg
<< " You see here a frog eating a magnolia.";
1375 ADD_MESSAGE("%s", Msg
.CStr());
1376 if (GetSeeWholeMapCheatMode()) Square
->SetMemorizedDescription(OldMemory
);
1380 truth
game::AnimationController () {
1381 DrawEverythingNoBlit(true);
1386 void game::LoadGlobalValueMap (inputfile
&fl
) {
1388 fl
.setGetVarCB(game::ldrGetVar
);
1389 for (fl
.ReadWord(word
, false); !fl
.Eof(); fl
.ReadWord(word
, false)) {
1390 if (word
== "Include") {
1391 word
= fl
.ReadWord();
1392 if (fl
.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1393 //fprintf(stderr, "loading: %s\n", word.CStr());
1394 inputfile
incf(game::GetGameDir()+"Script/"+word
, &game::GetGlobalValueMap());
1395 LoadGlobalValueMap(incf
);
1398 if (word
== "Message") {
1399 word
= fl
.ReadWord();
1400 if (fl
.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1401 fprintf(stderr
, "MESSAGE: %s\n", word
.CStr());
1404 if (word
!= "#") ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1405 fl
.ReadWord(word
, true);
1406 if (word
== "enum" || word
== "bitenum") {
1407 truth isBit
= word
== "bitenum";
1409 if (fl
.ReadWord() != "{") ABORT("'{' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1413 fl
.ReadWord(word
, true);
1414 if (word
== "}") break;
1419 fl
.ReadWord(word
, true);
1422 // set current index
1423 idx
= fl
.ReadNumber();
1425 if (word
!= "," && word
!= ";" && word
!= "}") ABORT("',' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1426 if (word
== "}") done
= true;
1428 if (idName
.GetSize() > 0) {
1430 if (isBit
) i
= 1<<i
;
1431 GlobalValueMap
.insert(std::make_pair(idName
, i
));
1437 if (ch
!= EOF
&& ch
!= ';') fl
.Unget(ch
);
1438 //if (fl.ReadWord() != ";") ABORT("';' expected in file %s at line %d!", fl.GetFileName().CStr(), fl.TokenLine());
1441 if (word
== "define") {
1443 sLong v
= fl
.ReadNumber();
1444 GlobalValueMap
.insert(std::make_pair(word
, v
));
1447 ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1452 void game::InitGlobalValueMap () {
1453 inputfile
SaveFile(GetGameDir()+"Script/define.dat", &GlobalValueMap
);
1454 LoadGlobalValueMap(SaveFile
);
1455 { /* additional files */
1456 for (int f
= 0; f
<= 99; f
++) {
1458 sprintf(bnum
, "Script/define_%02d.dat", f
);
1459 festring fn
= game::GetGameDir();
1461 if (inputfile::fileExists(fn
)) return;
1462 inputfile
ifl(fn
, &game::GetGlobalValueMap(), false);
1464 LoadGlobalValueMap(ifl
);
1472 void game::TextScreen (cfestring
&Text
, v2 Displacement
, col16 Color
, truth GKey
, truth Fade
, bitmapeditor BitmapEditor
) {
1473 globalwindowhandler::DisableControlLoops();
1474 iosystem::TextScreen(Text
, Displacement
, Color
, GKey
, Fade
, BitmapEditor
);
1475 globalwindowhandler::EnableControlLoops();
1479 /* ... all the keys that are acceptable
1480 DefaultAnswer = REQUIRES_ANSWER if this question requires an answer
1481 Not surprisingly KeyNumber is the number of keys at ...
1483 int game::KeyQuestion (cfestring
&Message
, int DefaultAnswer
, int KeyNumber
, ...) {
1484 int *Key
= new int[KeyNumber
];
1486 va_start(Arguments
, KeyNumber
);
1487 for (int c
= 0; c
< KeyNumber
; ++c
) Key
[c
] = va_arg(Arguments
, int);
1489 DrawEverythingNoBlit();
1490 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Message
.CStr());
1491 graphics::BlitDBToScreen();
1495 for (int c
= 0; c
< KeyNumber
; ++c
) {
1501 if (!Return
&& DefaultAnswer
!= REQUIRES_ANSWER
) Return
= DefaultAnswer
;
1504 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1509 v2
game::LookKeyHandler (v2 CursorPos
, int Key
) {
1510 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1513 if (!IsInWilderness()) {
1514 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1515 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1516 stack
*Stack
= LSquare
->GetStack();
1517 if (LSquare
->IsTransparent() && Stack
->GetVisibleItems(Player
))
1518 Stack
->DrawContents(Player
, "Items here", NO_SELECT
|(GetSeeWholeMapCheatMode() ? 0 : NO_SPECIAL_INFO
));
1520 ADD_MESSAGE("You see no items here.");
1521 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1525 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1526 character
*Char
= Square
->GetCharacter();
1527 if (Char
&& (Char
->CanBeSeenByPlayer() || Char
->IsPlayer() || GetSeeWholeMapCheatMode()))
1530 ADD_MESSAGE("You see no one here.");
1531 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1538 v2
game::NameKeyHandler (v2 CursorPos
, int Key
) {
1539 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
1540 if (Key
== 'n' || Key
== 'N') {
1541 character
*Char
= GetCurrentArea()->GetSquare(CursorPos
)->GetCharacter();
1542 if (Char
&& Char
->CanBeSeenByPlayer()) Char
->TryToName();
1543 else ADD_MESSAGE("You don't see anyone here to name.");
1549 void game::End (festring DeathMessage
, truth Permanently
, truth AndGoToMenu
) {
1551 globalwindowhandler::DeInstallControlLoop(AnimationController
);
1552 SetIsRunning(false);
1553 if (Permanently
|| !WizardModeIsReallyActive()) RemoveSaves(Permanently
);
1554 if (Permanently
&& !WizardModeIsReallyActive()) {
1557 if (HScore
.LastAddFailed()) {
1558 iosystem::TextScreen(CONST_S("You didn't manage to get onto the high score list.\n\n\n\n")+GetPlayerName()+", "+DeathMessage
+"\nRIP");
1564 /* This prevents monster movement etc. after death. */
1565 throw quitrequest();
1570 int game::CalculateRoughDirection (v2 Vector
) {
1571 if (!Vector
.X
&& !Vector
.Y
) return YOURSELF
;
1572 double Angle
= femath::CalculateAngle(Vector
);
1573 if (Angle
< FPI
/ 8) return 4;
1574 else if (Angle
< 3*FPI
/8) return 7;
1575 else if (Angle
< 5*FPI
/8) return 6;
1576 else if (Angle
< 7*FPI
/8) return 5;
1577 else if (Angle
< 9*FPI
/8) return 3;
1578 else if (Angle
< 11*FPI
/8) return 0;
1579 else if (Angle
< 13*FPI
/8) return 1;
1580 else if (Angle
< 15*FPI
/8) return 2;
1585 int game::Menu (bitmap
*BackGround
, v2 Pos
, cfestring
&Topic
, cfestring
&sMS
, col16 Color
, cfestring
&SmallText1
, cfestring
&SmallText2
) {
1586 globalwindowhandler::DisableControlLoops();
1587 int Return
= iosystem::Menu(BackGround
, Pos
, Topic
, sMS
, Color
, SmallText1
, SmallText2
);
1588 globalwindowhandler::EnableControlLoops();
1593 void game::InitDangerMap () {
1595 pool::RegisterState(false);
1596 //fprintf(stderr, "game::InitDangerMap(): START\n");
1597 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
1599 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
1600 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1601 int ConfigSize
= Proto
->GetConfigSize();
1602 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
1603 if (!ConfigData
[c2
]->IsAbstract
) {
1604 int Config
= ConfigData
[c2
]->Config
;
1607 NextDangerIDType
= c1
;
1608 NextDangerIDConfigIndex
= c2
;
1611 character
*Char
= Proto
->Spawn(Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1612 double NakedDanger
= Char
->GetRelativeDanger(Player
, true);
1613 //fprintf(stderr, " game::InitDangerMap(): 00\n");
1615 //Char->SendToHell(); // k8: equipment
1616 Char
= Proto
->Spawn(Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1617 double EquippedDanger
= Char
->GetRelativeDanger(Player
, true);
1618 //fprintf(stderr, " game::InitDangerMap(): 01\n");
1620 //Char->SendToHell(); // k8: equipment
1621 DangerMap
[configid(c1
, Config
)] = dangerid(NakedDanger
, EquippedDanger
);
1625 pool::RegisterState(true);
1626 //fprintf(stderr, "game::InitDangerMap(): DONE\n");
1630 void game::CalculateNextDanger () {
1631 if (IsInWilderness() || !*CurrentLevel
->GetLevelScript()->GenerateMonsters()) return;
1632 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1633 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1634 const character::database
*DataBase
= ConfigData
[NextDangerIDConfigIndex
];
1635 dangermap::iterator DangerIterator
= DangerMap
.find(configid(NextDangerIDType
, DataBase
->Config
));
1636 team
*Team
= GetTeam(PLAYER_TEAM
);
1637 if (DataBase
&& DangerIterator
!= DangerMap
.end()) {
1638 //fprintf(stderr, "game::CalculateNextDanger(): START\n");
1639 pool::RegisterState(false);
1640 character
*Char
= Proto
->Spawn(DataBase
->Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1641 std::list
<character
*>::const_iterator i
;
1642 double DangerSum
= Player
->GetRelativeDanger(Char
, true);
1643 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
) {
1644 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10)) DangerSum
+= (*i
)->GetRelativeDanger(Char
, true)/4;
1646 double CurrentDanger
= 1/DangerSum
;
1647 double NakedDanger
= DangerIterator
->second
.NakedDanger
;
1648 //fprintf(stderr, " game::CalculateNextDanger(): 00\n");
1650 //Char->SendToHell(); // k8: equipment
1651 if (NakedDanger
> CurrentDanger
) DangerIterator
->second
.NakedDanger
= (NakedDanger
*9+CurrentDanger
)/10;
1652 Char
= Proto
->Spawn(DataBase
->Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1653 DangerSum
= Player
->GetRelativeDanger(Char
, true);
1654 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
) {
1655 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10)) DangerSum
+= (*i
)->GetRelativeDanger(Char
, true) / 4;
1657 CurrentDanger
= 1/DangerSum
;
1658 double EquippedDanger
= DangerIterator
->second
.EquippedDanger
;
1659 //fprintf(stderr, " game::CalculateNextDanger(): 01\n");
1661 //Char->SendToHell(); // k8: equipment
1662 pool::RegisterState(true);
1663 if (EquippedDanger
> CurrentDanger
) DangerIterator
->second
.EquippedDanger
= (EquippedDanger
*9+CurrentDanger
)/10;
1664 if (++NextDangerIDConfigIndex
< Proto
->GetConfigSize()) {
1665 //fprintf(stderr, "game::CalculateNextDanger(): EXIT0\n");
1669 if (++NextDangerIDType
>= protocontainer
<character
>::GetSize()) NextDangerIDType
= 1;
1670 Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1671 ConfigData
= Proto
->GetConfigData();
1672 int ConfigSize
= Proto
->GetConfigSize();
1674 for (int c
= 0; c
< ConfigSize
; ++c
) {
1675 if (!ConfigData
[c
]->IsAbstract
) {
1676 NextDangerIDConfigIndex
= c
;
1677 //fprintf(stderr, "game::CalculateNextDanger(): EXIT1\n");
1682 //fprintf(stderr, "game::CalculateNextDanger(): DONE\n");
1684 ABORT("It is dangerous to go ice fishing in the summer.");
1689 truth
game::TryTravel (int Dungeon
, int Area
, int EntryIndex
, truth AllowHostiles
, truth AlliesFollow
) {
1690 charactervector Group
;
1691 if (LeaveArea(Group
, AllowHostiles
, AlliesFollow
)) {
1692 CurrentDungeonIndex
= Dungeon
;
1693 EnterArea(Group
, Area
, EntryIndex
);
1700 truth
game::LeaveArea (charactervector
&Group
, truth AllowHostiles
, truth AlliesFollow
) {
1701 if (!IsInWilderness()) {
1702 if (AlliesFollow
&& !GetCurrentLevel()->CollectCreatures(Group
, Player
, AllowHostiles
)) return false;
1704 GetCurrentDungeon()->SaveLevel(SaveName(), CurrentLevelIndex
);
1707 GetWorldMap()->GetPlayerGroup().swap(Group
);
1714 /* Used always when the player enters an area. */
1715 void game::EnterArea (charactervector
&Group
, int Area
, int EntryIndex
) {
1716 if (Area
!= WORLD_MAP
) {
1718 SetIsInWilderness(false);
1719 CurrentLevelIndex
= Area
;
1720 truth New
= !PrepareRandomBone(Area
) && !GetCurrentDungeon()->PrepareLevel(Area
);
1721 igraph::CreateBackGround(*CurrentLevel
->GetLevelScript()->GetBackGroundType());
1722 GetCurrentArea()->SendNewDrawRequest();
1723 v2 Pos
= GetCurrentLevel()->GetEntryPos(Player
, EntryIndex
);
1725 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
1726 Player
->PutToOrNear(Pos
);
1727 } else SetPlayer(GetCurrentLevel()->GetLSquare(Pos
)->GetCharacter());
1729 for (c
= 0; c
< Group
.size(); ++c
) {
1730 v2 NPCPos
= GetCurrentLevel()->GetNearestFreeSquare(Group
[c
], Pos
);
1731 if (NPCPos
== ERROR_V2
) NPCPos
= GetCurrentLevel()->GetRandomSquare(Group
[c
]);
1732 Group
[c
]->PutTo(NPCPos
);
1734 GetCurrentLevel()->FiatLux();
1735 ctruth
*AutoReveal
= GetCurrentLevel()->GetLevelScript()->AutoReveal();
1736 if (New
&& AutoReveal
&& *AutoReveal
) GetCurrentLevel()->Reveal();
1738 SendLOSUpdateRequest();
1742 if (New
&& CurrentDungeonIndex
== ATTNAM
&& Area
== 0) {
1743 GlobalRainLiquid
= powder::Spawn(SNOW
);
1744 GlobalRainSpeed
= v2(-64, 128);
1745 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1748 if (New
&& CurrentDungeonIndex
== NEW_ATTNAM
&& Area
== 0) {
1749 GlobalRainLiquid
= liquid::Spawn(WATER
);
1750 GlobalRainSpeed
= v2(256, 512);
1751 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1754 if (New
&& CurrentDungeonIndex
== ELPURI_CAVE
&& Area
== OREE_LAIR
) {
1755 GlobalRainLiquid
= liquid::Spawn(BLOOD
);
1756 GlobalRainSpeed
= v2(256, 512);
1757 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1758 GlobalRainLiquid
->SetVolumeNoSignals(200);
1759 CurrentLevel
->EnableGlobalRain();
1762 if (New
&& CurrentDungeonIndex
== MUNTUO
&& Area
== 0) {
1763 GlobalRainLiquid
= liquid::Spawn(WATER
);
1764 GlobalRainSpeed
= v2(-64, 1024);
1765 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1769 GetCurrentLevel()->UpdateLOS();
1770 Player
->SignalStepFrom(0);
1772 for (c
= 0; c
< Group
.size(); ++c
) Group
[c
]->SignalStepFrom(0);
1774 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1776 igraph::CreateBackGround(GRAY_FRACTAL
);
1777 SetIsInWilderness(true);
1779 SetCurrentArea(WorldMap
);
1780 CurrentWSquareMap
= WorldMap
->GetMap();
1781 GetWorldMap()->GetPlayerGroup().swap(Group
);
1782 Player
->PutTo(GetWorldMap()->GetEntryPos(Player
, EntryIndex
));
1783 SendLOSUpdateRequest();
1785 GetWorldMap()->UpdateLOS();
1786 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1791 int game::CompareLightToInt (col24 L
, col24 Int
) {
1792 if ((L
& 0xFF0000) > Int
|| (L
& 0xFF00) > Int
|| (L
& 0xFF) > Int
) return 1;
1793 if ((L
& 0xFF0000) == Int
|| (L
& 0xFF00) == Int
|| (L
& 0xFF) == Int
) return 0;
1798 void game::SetStandardListAttributes (felist
&List
) {
1799 List
.SetPos(v2(26, 42));
1801 List
.SetFlags(DRAW_BACKGROUND_AFTERWARDS
);
1802 List
.SetUpKey(GetMoveCommandKey(KEY_UP_INDEX
));
1803 List
.SetDownKey(GetMoveCommandKey(KEY_DOWN_INDEX
));
1807 void game::InitPlayerAttributeAverage () {
1808 AveragePlayerArmStrengthExperience
1809 = AveragePlayerLegStrengthExperience
1810 = AveragePlayerDexterityExperience
1811 = AveragePlayerAgilityExperience
1814 if (!Player
->IsHumanoid()) return;
1816 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1819 arm
*RightArm
= Player
->GetRightArm();
1821 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1822 AveragePlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1823 AveragePlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1827 arm
*LeftArm
= Player
->GetLeftArm();
1829 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1830 AveragePlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1831 AveragePlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1835 leg
*RightLeg
= Player
->GetRightLeg();
1837 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1838 AveragePlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1839 AveragePlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1843 leg
*LeftLeg
= Player
->GetLeftLeg();
1845 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1846 AveragePlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1847 AveragePlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1852 AveragePlayerArmStrengthExperience
/= Arms
;
1853 AveragePlayerDexterityExperience
/= Arms
;
1857 AveragePlayerLegStrengthExperience
/= Legs
;
1858 AveragePlayerAgilityExperience
/= Legs
;
1863 void game::UpdatePlayerAttributeAverage () {
1864 if (!Player
->IsHumanoid()) return;
1866 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1867 double PlayerArmStrengthExperience
= 0;
1868 double PlayerLegStrengthExperience
= 0;
1869 double PlayerDexterityExperience
= 0;
1870 double PlayerAgilityExperience
= 0;
1873 arm
*RightArm
= Player
->GetRightArm();
1875 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1876 PlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1877 PlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1881 arm
*LeftArm
= Player
->GetLeftArm();
1883 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1884 PlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1885 PlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1889 leg
*RightLeg
= Player
->GetRightLeg();
1891 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1892 PlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1893 PlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1897 leg
*LeftLeg
= Player
->GetLeftLeg();
1899 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1900 PlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1901 PlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1906 AveragePlayerArmStrengthExperience
= (49 * AveragePlayerArmStrengthExperience
+ PlayerArmStrengthExperience
/ Arms
) / 50;
1907 AveragePlayerDexterityExperience
= (49 * AveragePlayerDexterityExperience
+ PlayerDexterityExperience
/ Arms
) / 50;
1911 AveragePlayerLegStrengthExperience
= (49 * AveragePlayerLegStrengthExperience
+ PlayerLegStrengthExperience
/ Legs
) / 50;
1912 AveragePlayerAgilityExperience
= (49 * AveragePlayerAgilityExperience
+ PlayerAgilityExperience
/ Legs
) / 50;
1917 void game::CallForAttention (v2 Pos
, int RangeSquare
) {
1918 for (int c
= 0; c
< GetTeams(); ++c
) {
1919 if (GetTeam(c
)->HasEnemy())
1920 for (std::list
<character
*>::const_iterator i
= GetTeam(c
)->GetMember().begin(); i
!= GetTeam(c
)->GetMember().end(); ++i
)
1921 if ((*i
)->IsEnabled()) {
1922 sLong ThisDistance
= HypotSquare(sLong((*i
)->GetPos().X
) - Pos
.X
, sLong((*i
)->GetPos().Y
) - Pos
.Y
);
1923 if (ThisDistance
<= RangeSquare
&& !(*i
)->IsGoingSomeWhere()) (*i
)->SetGoingTo(Pos
);
1929 outputfile
&operator << (outputfile
&SaveFile
, const homedata
*HomeData
) {
1932 SaveFile
<< HomeData
->Pos
<< HomeData
->Dungeon
<< HomeData
->Level
<< HomeData
->Room
;
1933 } else SaveFile
.Put(0);
1938 inputfile
&operator >> (inputfile
&SaveFile
, homedata
*&HomeData
) {
1939 if (SaveFile
.Get()) {
1940 HomeData
= new homedata
;
1941 SaveFile
>> HomeData
->Pos
>> HomeData
->Dungeon
>> HomeData
->Level
>> HomeData
->Room
;
1947 feuLong
game::CreateNewCharacterID (character
*NewChar
) {
1948 feuLong ID
= NextCharacterID
++;
1949 /*k8:??? if(CharacterIDMap.find(ID) != CharacterIDMap.end())
1950 int esko = esko = 2;*/
1951 CharacterIDMap
.insert(std::make_pair(ID
, NewChar
));
1956 feuLong
game::CreateNewItemID (item
*NewItem
) {
1957 feuLong ID
= NextItemID
++;
1958 /*k8:??? if(ItemIDMap.find(ID) != ItemIDMap.end())
1959 int esko = esko = 2;*/
1960 if (NewItem
) ItemIDMap
.insert(std::make_pair(ID
, NewItem
));
1965 feuLong
game::CreateNewTrapID (entity
*NewTrap
) {
1966 feuLong ID
= NextTrapID
++;
1967 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
1968 int esko = esko = 2;*/
1969 if (NewTrap
) TrapIDMap
.insert(std::make_pair(ID
, NewTrap
));
1974 character
*game::SearchCharacter (feuLong ID
) {
1975 characteridmap::iterator Iterator
= CharacterIDMap
.find(ID
);
1976 return Iterator
!= CharacterIDMap
.end() ? Iterator
->second
: 0;
1980 item
*game::SearchItem (feuLong ID
) {
1981 itemidmap::iterator Iterator
= ItemIDMap
.find(ID
);
1982 return Iterator
!= ItemIDMap
.end() ? Iterator
->second
: 0;
1986 entity
*game::SearchTrap (feuLong ID
) {
1987 trapidmap::iterator Iterator
= TrapIDMap
.find(ID
);
1988 return Iterator
!= TrapIDMap
.end() ? Iterator
->second
: 0;
1992 outputfile
&operator << (outputfile
&SaveFile
, const configid
&Value
) {
1993 SaveFile
.Write(reinterpret_cast<cchar
*>(&Value
), sizeof(Value
));
1998 inputfile
&operator >> (inputfile
&SaveFile
, configid
&Value
) {
1999 SaveFile
.Read(reinterpret_cast<char*>(&Value
), sizeof(Value
));
2004 outputfile
&operator << (outputfile
&SaveFile
, const dangerid
&Value
) {
2005 SaveFile
<< Value
.NakedDanger
<< Value
.EquippedDanger
;
2010 inputfile
&operator >> (inputfile
&SaveFile
, dangerid
&Value
) {
2011 SaveFile
>> Value
.NakedDanger
>> Value
.EquippedDanger
;
2016 /* The program can only create directories to the deepness of one, no more... */
2017 festring
game::GetHomeDir () {
2019 Dir
<< getenv("HOME") << '/';
2024 festring
game::GetSaveDir () {
2027 Dir
<< ivanconfig::GetMyDir() << "/Save/";
2029 Dir
<< getenv("HOME") << "/IvanSave/";
2035 festring
game::GetGameDir () {
2036 /*k8! return DATADIR "/ivan/"; */
2037 /*k8! return DATADIR "/"; */
2039 Dir
<< ivanconfig::GetMyDir() << "/";
2044 festring
game::GetBoneDir () {
2045 /*k8! return LOCAL_STATE_DIR "/Bones/";*/
2048 Dir
<< ivanconfig::GetMyDir() << "/Save/Bones/";
2050 Dir
<< getenv("HOME") << "/IvanSave/Bones/";
2056 level
*game::GetLevel (int I
) {
2057 return GetCurrentDungeon()->GetLevel(I
);
2061 int game::GetLevels () {
2062 return GetCurrentDungeon()->GetLevels();
2066 void game::SignalDeath (ccharacter
*Ghost
, ccharacter
*Murderer
, festring DeathMsg
) {
2067 if (InWilderness
) DeathMsg
<< " in the world map";
2068 else DeathMsg
<< " in " << GetCurrentDungeon()->GetLevelDescription(CurrentLevelIndex
);
2069 massacremap
*MassacreMap
;
2071 ++MiscMassacreAmount
;
2072 MassacreMap
= &MiscMassacreMap
;
2073 } else if(Murderer
->IsPlayer()) {
2074 ++PlayerMassacreAmount
;
2075 MassacreMap
= &PlayerMassacreMap
;
2076 } else if(Murderer
->IsPet()) {
2077 ++PetMassacreAmount
;
2078 MassacreMap
= &PetMassacreMap
;
2080 ++MiscMassacreAmount
;
2081 MassacreMap
= &MiscMassacreMap
;
2084 massacreid
MI(Ghost
->GetType(), Ghost
->GetConfig(), Ghost
->GetAssignedName());
2085 massacremap::iterator i
= MassacreMap
->find(MI
);
2087 if (i
== MassacreMap
->end()) {
2088 i
= MassacreMap
->insert(std::make_pair(MI
, killdata(1, Ghost
->GetGenerationDanger()))).first
;
2089 i
->second
.Reason
.push_back(killreason(DeathMsg
, 1));
2092 i
->second
.DangerSum
+= Ghost
->GetGenerationDanger();
2093 std::vector
<killreason
>& Reason
= i
->second
.Reason
;
2095 for (c
= 0; c
< Reason
.size(); ++c
) {
2096 if (Reason
[c
].String
== DeathMsg
) {
2101 if (c
== Reason
.size()) Reason
.push_back(killreason(DeathMsg
, 1));
2106 void game::DisplayMassacreLists () {
2107 DisplayMassacreList(PlayerMassacreMap
, "directly by you.", PlayerMassacreAmount
);
2108 DisplayMassacreList(PetMassacreMap
, "by your allies.", PetMassacreAmount
);
2109 DisplayMassacreList(MiscMassacreMap
, "by some other reason.", MiscMassacreAmount
);
2113 struct massacresetentry
{
2114 bool operator < (const massacresetentry
&MSE
) const { return festring::IgnoreCaseCompare(Key
, MSE
.Key
); }
2117 std::vector
<festring
> Details
;
2122 void game::DisplayMassacreList (const massacremap
&MassacreMap
, cchar
*Reason
, sLong Amount
) {
2123 std::set
<massacresetentry
> MassacreSet
;
2124 festring FirstPronoun
;
2126 charactervector GraveYard
;
2128 for (massacremap::const_iterator i1
= MassacreMap
.begin(); i1
!= MassacreMap
.end(); ++i1
) {
2129 character
*Victim
= protocontainer
<character
>::GetProto(i1
->first
.Type
)->Spawn(i1
->first
.Config
);
2130 Victim
->SetAssignedName(i1
->first
.Name
);
2131 massacresetentry Entry
;
2132 GraveYard
.push_back(Victim
);
2133 Entry
.ImageKey
= AddToCharacterDrawVector(Victim
);
2134 if (i1
->second
.Amount
== 1) {
2135 Victim
->AddName(Entry
.Key
, UNARTICLED
);
2136 Victim
->AddName(Entry
.String
, INDEFINITE
);
2138 Victim
->AddName(Entry
.Key
, PLURAL
);
2139 Entry
.String
<< i1
->second
.Amount
<< ' ' << Entry
.Key
;
2142 FirstPronoun
= Victim
->GetSex() == UNDEFINED
? "it" : Victim
->GetSex() == MALE
? "he" : "she";
2145 const std::vector
<killreason
>& Reason
= i1
->second
.Reason
;
2146 std::vector
<festring
>& Details
= Entry
.Details
;
2147 if (Reason
.size() == 1) {
2149 if (Reason
[0].Amount
== 1) Begin
= "";
2150 else if(Reason
[0].Amount
== 2) Begin
= "both ";
2151 else Begin
= "all ";
2152 Details
.push_back(Begin
+ Reason
[0].String
);
2154 for (uInt c
= 0; c
< Reason
.size(); ++c
) Details
.push_back(CONST_S("")+Reason
[c
].Amount
+' '+Reason
[c
].String
);
2155 std::sort(Details
.begin(), Details
.end(), ignorecaseorderer());
2157 MassacreSet
.insert(Entry
);
2159 sLong Total
= PlayerMassacreAmount
+PetMassacreAmount
+MiscMassacreAmount
;
2161 if (Total
== 1) MainTopic
<< "One creature perished during your adventure.";
2162 else MainTopic
<< Total
<< " creatures perished during your adventure.";
2163 felist
List(MainTopic
);
2164 SetStandardListAttributes(List
);
2165 List
.SetPageLength(15);
2166 List
.AddFlags(SELECTABLE
);
2167 List
.SetEntryDrawer(CharacterEntryDrawer
);
2168 List
.AddDescription(CONST_S(""));
2170 if (Amount
!= Total
) {
2171 SideTopic
= CONST_S("The following ");
2172 if (Amount
== 1) SideTopic
<< "one was killed " << Reason
;
2173 else SideTopic
<< Amount
<< " were killed " << Reason
;
2176 FirstPronoun
.Capitalize();
2177 SideTopic
<< FirstPronoun
<< " was killed " << Reason
;
2178 } else SideTopic
<< "They were all killed " << Reason
;
2180 List
.AddDescription(SideTopic
);
2181 List
.AddDescription(CONST_S(""));
2182 List
.AddDescription("Choose a type of creatures to browse death details.");
2183 std::set
<massacresetentry
>::const_iterator i2
;
2184 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
) List
.AddEntry(i2
->String
, LIGHT_GRAY
, 0, i2
->ImageKey
);
2186 int Chosen
= List
.Draw();
2187 if (Chosen
& FELIST_ERROR_BIT
) break;
2188 felist
SubList(CONST_S("Massacre details"));
2189 SetStandardListAttributes(SubList
);
2190 SubList
.SetPageLength(20);
2192 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
, ++Counter
) {
2193 if (Counter
== Chosen
) {
2194 for (uInt c
= 0; c
< i2
->Details
.size(); ++c
) SubList
.AddEntry(i2
->Details
[c
], LIGHT_GRAY
);
2200 ClearCharacterDrawVector();
2201 for (uInt c
= 0; c
< GraveYard
.size(); ++c
) delete GraveYard
[c
];
2205 truth
game::MassacreListsEmpty () {
2206 return PlayerMassacreMap
.empty() && PetMassacreMap
.empty() && MiscMassacreMap
.empty();
2211 void game::SeeWholeMap () {
2212 if (SeeWholeMapCheatMode
< 2) ++SeeWholeMapCheatMode
; else SeeWholeMapCheatMode
= 0;
2213 GetCurrentArea()->SendNewDrawRequest();
2218 void game::CreateBone () {
2219 if (!WizardModeIsActive() && !IsInWilderness() && RAND() & 3 && GetCurrentLevel()->PreProcessForBone()) {
2222 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2223 BoneName
= GetBoneDir()+"bon"+CurrentDungeonIndex
+CurrentLevelIndex
+BoneIndex
;
2224 if (!inputfile::fileExists(BoneName
)) break;
2226 if (BoneIndex
!= 1000) {
2227 //festring BoneName = GetBoneDir()+"bon"+CurrentDungeonIndex+CurrentLevelIndex+BoneIndex;
2228 fprintf(stderr
, "creating bone file: [%s]\n", BoneName
.CStr());
2229 outputfile
BoneFile(BoneName
, true);
2230 BoneFile
<< int(BONE_FILE_VERSION
) << PlayerName
<< CurrentLevel
;
2236 truth
game::PrepareRandomBone (int LevelIndex
) {
2237 if (/*k8:WizardModeIsActive() ||*/ GetCurrentDungeon()->IsGenerated(LevelIndex
) || !*GetCurrentDungeon()->GetLevelScript(LevelIndex
)->CanGenerateBone()) return false;
2240 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2241 BoneName
= GetBoneDir()+"bon"+CurrentDungeonIndex
+LevelIndex
+BoneIndex
;
2242 inputfile
BoneFile(BoneName
, 0, false);
2243 if (BoneFile
.IsOpen() && !(RAND() & 7)) {
2244 if (ReadType
<int>(BoneFile
) != BONE_FILE_VERSION
) {
2246 remove(BoneName
.CStr());
2251 level
*NewLevel
= GetCurrentDungeon()->LoadLevel(BoneFile
, LevelIndex
);
2252 if (!NewLevel
->PostProcessForBone()) {
2254 GetBoneItemIDMap().clear();
2255 GetBoneCharacterIDMap().clear();
2258 NewLevel
->FinalProcessForBone();
2259 GetBoneItemIDMap().clear();
2260 GetBoneCharacterIDMap().clear();
2261 SetCurrentArea(NewLevel
);
2262 CurrentLevel
= NewLevel
;
2263 CurrentLSquareMap
= NewLevel
->GetMap();
2264 GetCurrentDungeon()->SetIsGenerated(LevelIndex
, true);
2265 if (Name
== PlayerName
) ADD_MESSAGE("This place is oddly familiar. Like you had been here in one of your past lives.");
2266 else ADD_MESSAGE("You smell the stench of death.");
2271 if (BoneIndex
!= 1000) {
2272 remove(BoneName
.CStr());
2279 double game::CalculateAverageDanger (const charactervector
&EnemyVector
, character
*Char
) {
2280 double DangerSum
= 0;
2282 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) {
2283 DangerSum
+= EnemyVector
[c
]->GetRelativeDanger(Char
, true);
2286 return DangerSum
/Enemies
;
2290 double game::CalculateAverageDangerOfAllNormalEnemies () {
2291 double DangerSum
= 0;
2293 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
2294 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
2295 const character::database
*const *ConfigData
= Proto
->GetConfigData();
2296 int ConfigSize
= Proto
->GetConfigSize();
2297 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
2298 if (!ConfigData
[c2
]->IsAbstract
&& !ConfigData
[c2
]->IsUnique
&& ConfigData
[c2
]->CanBeGenerated
) {
2299 DangerSum
+= DangerMap
.find(configid(c1
, ConfigData
[c2
]->Config
))->second
.EquippedDanger
;
2304 return DangerSum
/Enemies
;
2308 character
*game::CreateGhost () {
2309 double AverageDanger
= CalculateAverageDangerOfAllNormalEnemies();
2310 charactervector EnemyVector
;
2311 protosystem::CreateEveryNormalEnemy(EnemyVector
);
2312 ghost
*Ghost
= ghost::Spawn();
2313 Ghost
->SetTeam(GetTeam(MONSTER_TEAM
));
2314 Ghost
->SetGenerationDanger(CurrentLevel
->GetDifficulty());
2315 Ghost
->SetOwnerSoul(PlayerName
);
2316 Ghost
->SetIsActive(false);
2317 Ghost
->EditAllAttributes(-4);
2318 Player
->SetSoulID(Ghost
->GetID());
2319 while (CalculateAverageDanger(EnemyVector
, Ghost
) > AverageDanger
&& Ghost
->EditAllAttributes(1));
2320 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) delete EnemyVector
[c
];
2325 int game::GetMoveCommandKey (int I
) {
2326 if (!ivanconfig::GetUseAlternativeKeys()) return MoveNormalCommandKey
[I
];
2327 return MoveAbnormalCommandKey
[I
];
2331 sLong
game::GetScore () {
2333 massacremap::const_iterator i
;
2334 massacremap SumMap
= PlayerMassacreMap
;
2335 for (i
= PetMassacreMap
.begin(); i
!= PetMassacreMap
.end(); ++i
) {
2336 killdata
&KillData
= SumMap
[i
->first
];
2337 KillData
.Amount
+= i
->second
.Amount
;
2338 KillData
.DangerSum
+= i
->second
.DangerSum
;
2340 for (i
= SumMap
.begin(); i
!= SumMap
.end(); ++i
) {
2341 character
*Char
= protocontainer
<character
>::GetProto(i
->first
.Type
)->Spawn(i
->first
.Config
);
2342 int SumOfAttributes
= Char
->GetSumOfAttributes();
2343 Counter
+= sqrt(i
->second
.DangerSum
/ DEFAULT_GENERATION_DANGER
) * SumOfAttributes
* SumOfAttributes
;
2346 return sLong(0.01*Counter
);
2350 /* Only works if New Attnam is loaded */
2351 truth
game::TweraifIsFree () {
2352 for (std::list
<character
*>::const_iterator i
= GetTeam(COLONIST_TEAM
)->GetMember().begin(); i
!= GetTeam(COLONIST_TEAM
)->GetMember().end(); ++i
)
2353 if ((*i
)->IsEnabled()) return false;
2358 // returns true if date is christmaseve or day
2359 truth
game::IsXMas () {
2360 time_t Time
= time(0);
2361 struct tm
*TM
= localtime(&Time
);
2362 return (TM
->tm_mon
== 11 && (TM
->tm_mday
== 24 || TM
->tm_mday
== 25));
2366 int game::AddToItemDrawVector (const itemvector
&What
) {
2367 ItemDrawVector
.push_back(What
);
2368 return ItemDrawVector
.size()-1;
2372 v2 ItemDisplacement
[3][3] = {
2373 { v2(0, 0), ERROR_V2
, ERROR_V2
},
2374 { v2(-2, -2), v2(2, 2), ERROR_V2
},
2375 { v2(-4, -4), v2(0, 0), v2(4, 4) }
2379 void game::ItemEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2384 { TILE_SIZE
, TILE_SIZE
},
2385 { NORMAL_LUMINANCE
},
2389 itemvector ItemVector
= ItemDrawVector
[I
];
2390 int Amount
= Min
<int>(ItemVector
.size(), 3);
2391 for (int c
= 0; c
< Amount
; ++c
) {
2392 v2 Displacement
= ItemDisplacement
[Amount
-1][c
];
2393 if (!ItemVector
[0]->HasNormalPictureDirection()) Displacement
.X
= -Displacement
.X
;
2394 B
.Dest
= Pos
+Displacement
;
2395 if (ItemVector
[c
]->AllowAlphaEverywhere()) B
.CustomData
|= ALLOW_ALPHA
;
2396 ItemVector
[c
]->Draw(B
);
2397 B
.CustomData
&= ~ALLOW_ALPHA
;
2399 if (ItemVector
.size() > 3) {
2402 B
.Dest
= ItemVector
[0]->HasNormalPictureDirection() ? Pos
+v2(11, -2) : Pos
+v2(-2, -2);
2404 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2409 int game::AddToCharacterDrawVector (character
*What
) {
2410 CharacterDrawVector
.push_back(What
);
2411 return CharacterDrawVector
.size()-1;
2415 void game::CharacterEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2416 if (CharacterDrawVector
[I
]) {
2421 { TILE_SIZE
, TILE_SIZE
},
2422 { NORMAL_LUMINANCE
},
2424 ALLOW_ANIMATE
|ALLOW_ALPHA
2426 CharacterDrawVector
[I
]->DrawBodyParts(B
);
2431 void game::GodEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2436 { TILE_SIZE
, TILE_SIZE
},
2441 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2445 character
*game::GetSumo () {
2446 return GetCurrentLevel()->GetLSquare(SUMO_ROOM_POS
)->GetRoom()->GetMaster();
2450 truth
game::TryToEnterSumoArena () {
2451 character
*Sumo
= GetSumo();
2452 if (!Sumo
|| !Sumo
->IsEnabled() || Sumo
->GetRelation(Player
) == HOSTILE
|| !Player
->CanBeSeenBy(Sumo
)) return true;
2453 if (TweraifIsFree()) {
2454 ADD_MESSAGE("\"You started this stupid revolution, after which I've been constantly hungry. Get lost!\"");
2457 if (PlayerIsSumoChampion()) {
2458 ADD_MESSAGE("\"I don't really enjoy losing, especially many times to the same guy. Go away.\"");
2461 if (Player
->IsPolymorphed()) {
2462 ADD_MESSAGE("\"Don't try to cheat. Come back when you're normal again.\"");
2465 if (Player
->GetHungerState() < SATIATED
) {
2466 ADD_MESSAGE("\"Your figure is too slender for this sport. Eat a lot more and come back.\"");
2469 if (Player
->GetHungerState() < BLOATED
) {
2470 ADD_MESSAGE("\"You're still somewhat too thin. Eat some more and we'll compete.\"");
2473 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.\"");
2474 if (!TruthQuestion("Do you want to challenge him? [y/N]")) return false;
2476 SumoWrestling
= true;
2477 character
*MirrorPlayer
= Player
->Duplicate(IGNORE_PROHIBITIONS
);
2478 character
*MirrorSumo
= Sumo
->Duplicate(IGNORE_PROHIBITIONS
);
2479 SetPlayer(MirrorPlayer
);
2480 charactervector Spectators
;
2481 if (Player
->GetTeam()->GetRelation(GetTeam(TOURIST_GUIDE_TEAM
)) != HOSTILE
&&
2482 Player
->GetTeam()->GetRelation(GetTeam(TOURIST_TEAM
)) != HOSTILE
) {
2483 GetTeam(TOURIST_GUIDE_TEAM
)->MoveMembersTo(Spectators
);
2484 GetTeam(TOURIST_TEAM
)->MoveMembersTo(Spectators
);
2486 GetCurrentDungeon()->SaveLevel(SaveName(), 0);
2487 charactervector test
;
2488 EnterArea(test
, 1, STAIRS_UP
);
2489 MirrorSumo
->PutTo(SUMO_ARENA_POS
+v2(6, 5));
2490 MirrorSumo
->ChangeTeam(GetTeam(SUMO_TEAM
));
2491 GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->SetMasterID(MirrorSumo
->GetID());
2492 for (uInt c
= 0; c
< Spectators
.size(); ++c
) Spectators
[c
]->PutToOrNear(SUMO_ARENA_POS
+ v2(6, 10));
2493 throw areachangerequest();
2498 truth
game::TryToExitSumoArena () {
2499 if (GetTeam(PLAYER_TEAM
)->GetRelation(GetTeam(NEW_ATTNAM_TEAM
)) == HOSTILE
) return true;
2501 charactervector CVector
;
2502 if (IsSumoWrestling()) {
2503 if (TruthQuestion("Do you really wish to give up? [y/N]")) return EndSumoWrestling(LOST
);
2508 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2509 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2510 std::vector
<character
*> test
;
2511 EnterArea(test
, 0, STAIRS_DOWN
);
2512 Player
->GetStackUnder()->AddItems(IVector
);
2513 if (!IVector
.empty()) {
2514 character
*Sumo
= GetSumo();
2515 if (Sumo
&& Sumo
->GetRelation(Player
) != HOSTILE
&& Player
->CanBeSeenBy(Sumo
)) ADD_MESSAGE("\"Don't leave anything there, please.\"");
2517 v2 PlayerPos
= Player
->GetPos();
2518 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2519 throw areachangerequest();
2525 truth
game::EndSumoWrestling (int Result
) {
2527 msgsystem::LeaveBigMessageMode();
2528 if (Result
== LOST
) AskForKeyPress("You lose. [press any key to continue]");
2529 else if (Result
== WON
) AskForKeyPress("You win! [press any key to continue]");
2530 else if (Result
== DISQUALIFIED
) AskForKeyPress("You are disqualified! [press any key to continue]");
2531 character
*Sumo
= GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->GetMaster();
2532 /* We'll make a throw soon so deletes are allowed */
2541 charactervector CVector
;
2542 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2543 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2544 charactervector test
;
2545 EnterArea(test
, 0, STAIRS_DOWN
);
2546 SumoWrestling
= false;
2547 Player
->GetStackUnder()->AddItems(IVector
);
2548 v2 PlayerPos
= Player
->GetPos();
2549 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2550 if (Result
== LOST
) ADD_MESSAGE("\"I hope you've learned your lesson now!\"");
2551 else if (Result
== DISQUALIFIED
) ADD_MESSAGE("\"Don't do that again or I'll be really angry!\"");
2553 PlayerSumoChampion
= true;
2554 character
*Sumo
= GetSumo();
2555 festring Msg
= Sumo
->GetName(DEFINITE
)+" seems humbler than before. \"Darn. You bested me.\n";
2556 Msg
<< "Here's a little something as a reward\", " << Sumo
->GetPersonalPronoun() << " says and hands you a belt of levitation.\n\"";
2557 (belt::Spawn(BELT_OF_LEVITATION
))->MoveTo(Player
->GetStack());
2558 Msg
<< "Allow me to also teach you a few nasty martial art tricks the years have taught me.\"";
2559 Player
->GetCWeaponSkill(UNARMED
)->AddHit(100000);
2560 Player
->GetCWeaponSkill(KICK
)->AddHit(100000);
2561 character
*Imperialist
= GetCurrentLevel()->GetLSquare(5, 5)->GetRoom()->GetMaster();
2562 if (Imperialist
&& Imperialist
->GetRelation(Player
) != HOSTILE
) {
2563 v2 Pos
= Player
->GetPos()+v2(0, 1);
2564 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
2565 Imperialist
->Remove();
2566 Imperialist
->PutTo(Pos
);
2567 Msg
<< "\n\nSuddenly you notice " << Imperialist
->GetName(DEFINITE
) << " has also entered.\n"
2568 "\"I see we have a promising fighter among us. I had already heard of your\n"
2569 "adventures outside the village, but hardly could I believe that one day you\n"
2570 "would defeat even the mighty Huang Ming Pong! A hero such as you is bound\n"
2571 "to become world famous, and can earn a fortune if wealthy sponsors are behind\n"
2572 "him. May I therefore propose a mutually profitable contract: I'll give you this\n"
2573 "nice shirt with my company's ad, and you'll wear it as you journey bravely to\n"
2574 "the unknown and fight epic battles against the limitless minions of evil. I'll\n"
2575 "reward you well when you return, depending on how much you have used it.\"";
2576 Player
->GetStack()->AddItem(decosadshirt::Spawn());
2579 GetCurrentArea()->SendNewDrawRequest();
2582 Player
->EditNP(-25000);
2583 Player
->CheckStarvationDeath(CONST_S("exhausted after controlling a mirror image for too long"));
2584 throw areachangerequest();
2589 rain
*game::ConstructGlobalRain () {
2590 return new rain(GlobalRainLiquid
, static_cast<lsquare
*>(GetSquareInLoad()), GlobalRainSpeed
, MONSTER_TEAM
, false);
2594 v2
game::GetSunLightDirectionVector () {
2595 int Index
= Tick
% 48000 / 1000;
2596 /* Should have the same sign as sin(PI * Index / 24) and XTable[Index] /
2597 YTable[Index] should equal roughly -tan(PI * Index / 24). Also, vector
2598 (XTable[Index], YTable[Index]) + P should not be a valid position of
2599 any possible level L for any P belonging to L. */
2600 static int XTable
[48] = {
2601 0, 1000, 1000, 1000, 1000, 1000,
2602 1000, 1303, 1732, 2414, 3732, 7596,
2603 1000, 7596, 3732, 2414, 1732, 1303,
2604 1000, 1000, 1000, 1000, 1000, 1000,
2605 0, -1000, -1000, -1000, -1000, -1000,
2606 -1000, -1303, -1732, -2414, -3732, -7596,
2607 -1000, -7596, -3732, -2414, -1732, -1303,
2608 -1000, -1000, -1000, -1000, -1000, -1000 };
2609 /* Should have the same sign as -cos(PI * Index / 24) */
2610 static int YTable
[48] = { -1000, -7596, -3732, -2414, -1732, -1303,
2611 -1000, -1000, -1000, -1000, -1000, -1000,
2612 0, 1000, 1000, 1000, 1000, 1000,
2613 1000, 1303, 1732, 2414, 3732, 7596,
2614 1000, 7596, 3732, 2414, 1732, 1303,
2615 1000, 1000, 1000, 1000, 1000, 1000,
2616 0, -1000, -1000, -1000, -1000, -1000,
2617 -1000, -1303, -1732, -2414, -3732, -7596 };
2618 return v2(XTable
[Index
], YTable
[Index
]);
2622 int game::CalculateMinimumEmitationRadius (col24 E
) {
2623 int MaxElement
= Max(GetRed24(E
), GetGreen24(E
), GetBlue24(E
));
2624 return int(sqrt(double(MaxElement
<< 7) / LIGHT_BORDER
- 120.));
2628 feuLong
game::IncreaseSquarePartEmitationTicks () {
2629 if ((SquarePartEmitationTick
+= 2) == 0x100) {
2630 CurrentLevel
->InitSquarePartEmitationTicks();
2631 SquarePartEmitationTick
= 2;
2633 return SquarePartEmitationTick
;
2637 bool game::Wish (character
*Wisher
, cchar
*MsgSingle
, cchar
*MsgPair
, bool canAbort
) {
2639 festring oldDef
= DefaultWish
;
2640 festring Temp
= DefaultQuestion(CONST_S("What do you want to wish for?"), DefaultWish
);
2641 if (DefaultWish
== "nothing" && canAbort
) {
2642 DefaultWish
= oldDef
;
2645 item
*TempItem
= protosystem::CreateItem(Temp
, Wisher
->IsPlayer());
2647 Wisher
->GetStack()->AddItem(TempItem
);
2648 TempItem
->SpecialGenerationHandler();
2649 if (TempItem
->HandleInPairs()) ADD_MESSAGE(MsgPair
, TempItem
->CHAR_NAME(PLURAL
));
2650 else ADD_MESSAGE(MsgSingle
, TempItem
->CHAR_NAME(INDEFINITE
));
2657 festring
game::DefaultQuestion (festring Topic
, festring
&Default
, stringkeyhandler KeyHandler
) {
2658 festring ShortDefault
= Default
;
2659 if (Default
.GetSize() > 29) {
2660 ShortDefault
.Resize(27);
2661 ShortDefault
= ShortDefault
<< CONST_S("...");
2663 if (!Default
.IsEmpty()) Topic
<< " [" << ShortDefault
<< ']';
2664 festring Answer
= StringQuestion(Topic
, WHITE
, 0, 80, false, KeyHandler
);
2665 if (Answer
.IsEmpty()) Answer
= Default
;
2666 return Default
= Answer
;
2670 void game::GetTime (ivantime
&Time
) {
2671 Time
.Hour
= 12 + Tick
/ 2000;
2672 Time
.Day
= Time
.Hour
/ 24 + 1;
2674 Time
.Min
= Tick
% 2000 * 60 / 2000;
2678 truth
NameOrderer (character
*C1
, character
*C2
) {
2679 return festring::IgnoreCaseCompare(C1
->GetName(UNARTICLED
), C2
->GetName(UNARTICLED
));
2683 truth
game::PolymorphControlKeyHandler (int Key
, festring
&String
) {
2685 felist
List(CONST_S("List of known creatures and their intelligence requirements"));
2686 SetStandardListAttributes(List
);
2687 List
.SetPageLength(15);
2688 List
.AddFlags(SELECTABLE
);
2689 protosystem::CreateEverySeenCharacter(CharacterDrawVector
);
2690 std::sort(CharacterDrawVector
.begin(), CharacterDrawVector
.end(), NameOrderer
);
2691 List
.SetEntryDrawer(CharacterEntryDrawer
);
2692 std::vector
<festring
> StringVector
;
2694 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) {
2695 character
*Char
= CharacterDrawVector
[c
];
2696 if (Char
->CanBeWished()) {
2698 Char
->AddName(Entry
, UNARTICLED
);
2699 StringVector
.push_back(Entry
);
2700 int Req
= Char
->GetPolymorphIntelligenceRequirement();
2701 if (Char
->IsSameAs(Player
) || (Player
->GetPolymorphBackup() && Player
->GetPolymorphBackup()->IsSameAs(Char
))) Req
= 0;
2702 Entry
<< " (" << Req
<< ')';
2703 int Int
= Player
->GetAttribute(INTELLIGENCE
);
2704 List
.AddEntry(Entry
, Req
> Int
? RED
: LIGHT_GRAY
, 0, c
);
2707 int Chosen
= List
.Draw();
2708 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) delete CharacterDrawVector
[c
];
2709 if (!(Chosen
& FELIST_ERROR_BIT
)) String
= StringVector
[Chosen
];
2710 CharacterDrawVector
.clear();
2717 outputfile
&operator << (outputfile
&SaveFile
, const killdata
&Value
) {
2718 SaveFile
<< Value
.Amount
<< Value
.DangerSum
<< Value
.Reason
;
2723 inputfile
&operator >> (inputfile
&SaveFile
, killdata
&Value
) {
2724 SaveFile
>> Value
.Amount
>> Value
.DangerSum
>> Value
.Reason
;
2729 outputfile
&operator << (outputfile
&SaveFile
, const killreason
&Value
) {
2730 SaveFile
<< Value
.Amount
<< Value
.String
;
2735 inputfile
&operator >> (inputfile
&SaveFile
, killreason
&Value
) {
2736 SaveFile
>> Value
.Amount
>> Value
.String
;
2741 truth
DistanceOrderer (character
*C1
, character
*C2
) {
2742 v2 PlayerPos
= PLAYER
->GetPos();
2743 v2 Pos1
= C1
->GetPos();
2744 v2 Pos2
= C2
->GetPos();
2745 int D1
= Max(abs(Pos1
.X
- PlayerPos
.X
), abs(Pos1
.Y
- PlayerPos
.Y
));
2746 int D2
= Max(abs(Pos2
.X
- PlayerPos
.X
), abs(Pos2
.Y
- PlayerPos
.Y
));
2747 if (D1
!= D2
) return D1
< D2
;
2748 if (Pos1
.Y
!= Pos2
.Y
) return Pos1
.Y
< Pos2
.Y
;
2749 return Pos1
.X
< Pos2
.X
;
2753 truth
game::FillPetVector (cchar
*Verb
) {
2755 team
*Team
= GetTeam(PLAYER_TEAM
);
2756 for (std::list
<character
*>::const_iterator i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
)
2757 if ((*i
)->IsEnabled() && !(*i
)->IsPlayer() && (*i
)->CanBeSeenByPlayer()) PetVector
.push_back(*i
);
2758 if (PetVector
.empty()) {
2759 ADD_MESSAGE("You don't detect any friends to %s.", Verb
);
2762 std::sort(PetVector
.begin(), PetVector
.end(), DistanceOrderer
);
2763 LastPetUnderCursor
= PetVector
[0];
2768 truth
game::CommandQuestion () {
2769 if (!FillPetVector("command")) return false;
2771 if (PetVector
.size() == 1) Char
= PetVector
[0];
2773 v2 Pos
= PetVector
[0]->GetPos();
2774 Pos
= PositionQuestion(CONST_S("Whom do you wish to command? [direction keys/'+'/'-'/'a'll/space/esc]"), Pos
, &PetHandler
, &CommandKeyHandler
);
2775 if (Pos
== ERROR_V2
) return false;
2776 if (Pos
== ABORT_V2
) return true;
2777 Char
= CurrentArea
->GetSquare(Pos
)->GetCharacter();
2778 if (!Char
|| !Char
->CanBeSeenByPlayer()) {
2779 ADD_MESSAGE("You don't see anyone here to command.");
2782 if (Char
->IsPlayer()) {
2783 ADD_MESSAGE("You do that all the time.");
2786 if (!Char
->IsPet()) {
2787 ADD_MESSAGE("%s refuses to be commanded by you.", Char
->CHAR_NAME(DEFINITE
));
2791 return Char
->IssuePetCommands();
2795 void game::NameQuestion () {
2796 if (!FillPetVector("name")) return;
2797 if (PetVector
.size() == 1) PetVector
[0]->TryToName();
2798 else PositionQuestion(CONST_S("Who do you want to name? [direction keys/'+'/'-'/'n'ame/esc]"), PetVector
[0]->GetPos(), &PetHandler
, &NameKeyHandler
);
2802 void game::PetHandler (v2 CursorPos
) {
2803 character
*Char
= CurrentArea
->GetSquare(CursorPos
)->GetCharacter();
2804 if (Char
&& Char
->CanBeSeenByPlayer() && Char
->IsPet() && !Char
->IsPlayer()) CursorData
= RED_CURSOR
|CURSOR_TARGET
;
2805 else CursorData
= RED_CURSOR
;
2806 if (Char
&& !Char
->IsPlayer() && Char
->IsPet()) LastPetUnderCursor
= Char
;
2810 v2
game::CommandKeyHandler (v2 CursorPos
, int Key
) {
2811 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
2812 if (Key
== 'a' || Key
== 'A') return CommandAll() ? ABORT_V2
: ERROR_V2
;
2817 truth
game::SelectPet (int Key
) {
2819 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2820 if (PetVector
[c
] == LastPetUnderCursor
) {
2821 if (++c
== PetVector
.size()) c
= 0;
2822 LastPetUnderCursor
= PetVector
[c
];
2826 } else if (Key
== '-') {
2827 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2828 if (PetVector
[c
] == LastPetUnderCursor
) {
2829 if (!c
) c
= PetVector
.size();
2830 LastPetUnderCursor
= PetVector
[--c
];
2839 void game::CommandScreen (cfestring
&Topic
, feuLong PossibleFlags
, feuLong ConstantFlags
, feuLong
&VaryFlags
, feuLong
&Flags
) {
2840 static cchar
*CommandDescription
[COMMAND_FLAGS
] = {
2842 "Flee from enemies",
2843 "Don't change your equipment",
2844 "Don't consume anything valuable"
2847 SetStandardListAttributes(List
);
2848 List
.AddFlags(SELECTABLE
);
2849 List
.AddDescription(CONST_S(""));
2850 List
.AddDescription(CONST_S("Command Active?"));
2853 for (c
= 0; c
< COMMAND_FLAGS
; ++c
) {
2854 if (1 << c
& PossibleFlags
) {
2855 truth Changeable
= !(1 << c
& ConstantFlags
);
2858 Entry
= CommandDescription
[c
];
2861 Entry
<< " " << CommandDescription
[c
];
2864 if (1 << c
& VaryFlags
) Entry
<< "varies"; else Entry
<< (1 << c
& Flags
? "yes" : "no");
2865 List
.AddEntry(Entry
, Changeable
? LIGHT_GRAY
: DARK_GRAY
, 0, NO_IMAGE
, Changeable
);
2868 int Chosen
= List
.Draw();
2869 if (Chosen
& FELIST_ERROR_BIT
) return;
2870 for (c
= 0, i
= 0; c
< COMMAND_FLAGS
; ++c
) {
2871 if (1 << c
& PossibleFlags
&& !(1 << c
& ConstantFlags
) && i
++ == Chosen
) {
2872 if (1 << c
& VaryFlags
) {
2873 VaryFlags
&= ~(1 << c
);
2875 } else Flags
^= 1 << c
;
2880 DrawEverythingNoBlit();
2885 truth
game::CommandAll () {
2886 feuLong PossibleFlags
= 0, ConstantFlags
= ALL_COMMAND_FLAGS
, VaryFlags
= 0, OldFlags
= 0;
2888 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2889 ConstantFlags
&= PetVector
[c1
]->GetConstantCommandFlags();
2890 feuLong C
= PetVector
[c1
]->GetCommandFlags();
2891 feuLong ThisPossible
= PetVector
[c1
]->GetPossibleCommandFlags();
2892 for (c2
= 0; c2
< COMMAND_FLAGS
; ++c2
)
2893 if (1 << c2
& PossibleFlags
& ThisPossible
&& (1 << c2
& C
) != (1 << c2
& OldFlags
)) VaryFlags
|= 1 << c2
;
2894 PossibleFlags
|= ThisPossible
;
2895 OldFlags
|= C
& ThisPossible
;
2897 if (!PossibleFlags
) {
2898 ADD_MESSAGE("Not a single creature in your visible team can be commanded.");
2901 feuLong NewFlags
= OldFlags
;
2902 CommandScreen(CONST_S("Issue commands to whole visible team"), PossibleFlags
, ConstantFlags
, VaryFlags
, NewFlags
);
2903 truth Change
= false;
2904 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2905 character
*Char
= PetVector
[c1
];
2906 if (!Char
->IsConscious()) continue;
2907 feuLong OldC
= Char
->GetCommandFlags();
2908 feuLong ConstC
= Char
->GetConstantCommandFlags();
2909 feuLong ThisC
= (NewFlags
& Char
->GetPossibleCommandFlags() & ~(ConstC
|VaryFlags
)) | (OldC
& (ConstC
|VaryFlags
));
2910 if (ThisC
!= OldC
) Change
= true;
2911 Char
->SetCommandFlags(ThisC
);
2913 if (!Change
) return false;
2914 Player
->EditAP(-500);
2915 Player
->EditExperience(CHARISMA
, 50, 1 << 7);
2920 col16
game::GetAttributeColor (int I
) {
2921 int Delta
= GetTick()-LastAttributeChangeTick
[I
];
2922 if (OldAttribute
[I
] == NewAttribute
[I
] || Delta
>= 510) return WHITE
;
2923 if (OldAttribute
[I
] < NewAttribute
[I
]) return MakeRGB16(255, 255, Delta
>> 1);
2924 return MakeRGB16(255, Delta
>> 1, Delta
>> 1);
2928 void game::UpdateAttributeMemory () {
2929 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
2930 int A
= Player
->GetAttribute(c
);
2931 if (A
!= NewAttribute
[c
]) {
2932 OldAttribute
[c
] = NewAttribute
[c
];
2933 NewAttribute
[c
] = A
;
2934 LastAttributeChangeTick
[c
] = GetTick();
2940 void game::InitAttributeMemory () {
2941 for (int c
= 0; c
< ATTRIBUTES
; ++c
) OldAttribute
[c
] = NewAttribute
[c
] = Player
->GetAttribute(c
);
2945 void game::TeleportHandler (v2 CursorPos
) {
2946 if ((CursorPos
-Player
->GetPos()).GetLengthSquare() > Player
->GetTeleportRangeSquare())
2947 CursorData
= BLUE_CURSOR
|CURSOR_TARGET
;
2949 CursorData
= RED_CURSOR
|CURSOR_TARGET
;
2953 double game::GetGameSituationDanger () {
2954 double SituationDanger
= 0;
2955 character
*Player
= GetPlayer();
2956 truth PlayerStuck
= Player
->IsStuck();
2957 v2 PlayerPos
= Player
->GetPos();
2958 character
*TruePlayer
= Player
;
2959 if (PlayerStuck
) (Player
= Player
->Duplicate(IGNORE_PROHIBITIONS
))->ChangeTeam(0);
2960 for (int c1
= 0; c1
< GetTeams(); ++c1
)
2961 if (GetTeam(c1
)->GetRelation(GetTeam(PLAYER_TEAM
)) == HOSTILE
)
2962 for (std::list
<character
*>::const_iterator i1
= GetTeam(c1
)->GetMember().begin(); i1
!= GetTeam(c1
)->GetMember().end(); ++i1
) {
2963 character
*Enemy
= *i1
;
2964 if (Enemy
->IsEnabled() && Enemy
->CanAttack() && (Enemy
->CanMove() || Enemy
->GetPos().IsAdjacent(PlayerPos
))) {
2965 truth EnemyStuck
= Enemy
->IsStuck();
2966 v2 EnemyPos
= Enemy
->GetPos();
2967 truth Sees
= TruePlayer
->CanBeSeenBy(Enemy
);
2968 character
*TrueEnemy
= Enemy
;
2969 if (EnemyStuck
) Enemy
= Enemy
->Duplicate(IGNORE_PROHIBITIONS
);
2970 double PlayerTeamDanger
= 1/Enemy
->GetSituationDanger(Player
, EnemyPos
, PlayerPos
, Sees
);
2971 for (int c2
= 0; c2
< GetTeams(); ++c2
)
2972 if (GetTeam(c2
)->GetRelation(GetTeam(c1
)) == HOSTILE
)
2973 for (std::list
<character
*>::const_iterator i2
= GetTeam(c2
)->GetMember().begin(); i2
!= GetTeam(c2
)->GetMember().end(); ++i2
) {
2974 character
*Friend
= *i2
;
2975 if (Friend
->IsEnabled() && !Friend
->IsPlayer() && Friend
->CanAttack() && (Friend
->CanMove() || Friend
->GetPos().IsAdjacent(EnemyPos
))) {
2976 v2 FriendPos
= Friend
->GetPos();
2977 truth Sees
= TrueEnemy
->CanBeSeenBy(Friend
);
2978 if (Friend
->IsStuck()) {
2979 Friend
= Friend
->Duplicate(IGNORE_PROHIBITIONS
);
2980 PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
) * .2;
2982 } else PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
);
2986 PlayerTeamDanger
*= 5;
2989 SituationDanger
+= 1 / PlayerTeamDanger
;
2992 Player
->ModifySituationDanger(SituationDanger
);
2994 SituationDanger
*= 2;
2997 return SituationDanger
;
3001 sLong
game::GetTimeSpent () {
3002 return time::TimeAdd(time::TimeDifference(time(0),LastLoad
), TimePlayedBeforeLastLoad
);
3006 outputfile
&operator << (outputfile
&SaveFile
, const massacreid
&MI
) {
3007 SaveFile
<< MI
.Type
<< MI
.Config
<< MI
.Name
;
3012 inputfile
&operator >> (inputfile
&SaveFile
, massacreid
&MI
) {
3013 SaveFile
>> MI
.Type
>> MI
.Config
>> MI
.Name
;
3018 truth
game::PlayerIsRunning () {
3019 return PlayerRunning
&& Player
->CanMove();
3023 void game::AddSpecialCursor (v2 Pos
, int Data
) {
3024 SpecialCursorPos
.push_back(Pos
);
3025 SpecialCursorData
.push_back(Data
);
3029 void game::RemoveSpecialCursors () {
3030 SpecialCursorPos
.clear();
3031 SpecialCursorData
.clear();
3035 void game::LearnAbout (god
*Who
) {
3036 Who
->SetIsKnown(true);
3037 /* slightly slow, but doesn't matter since this is run so rarely */
3038 if (PlayerKnowsAllGods() && !game::PlayerHasReceivedAllGodsKnownBonus
) {
3039 GetPlayer()->ApplyAllGodsKnownBonus();
3040 game::PlayerHasReceivedAllGodsKnownBonus
= true;
3045 truth
game::PlayerKnowsAllGods () {
3046 for (int c
= 1; c
<= GODS
; ++c
) if (!GetGod(c
)->IsKnown()) return false;
3051 void game::AdjustRelationsToAllGods (int Amount
) {
3052 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->AdjustRelation(Amount
);
3056 void game::SetRelationsToAllGods (int Amount
) {
3057 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->SetRelation(Amount
);
3061 void game::ShowDeathSmiley (bitmap
*Buffer
, truth
) {
3062 static blitdata B
= {
3065 { (RES
.X
>> 1) - 24, RES
.Y
* 4 / 7 - 24 },
3071 int Tick
= globalwindowhandler::UpdateTick();
3072 if (((Tick
>> 1) & 31) == 1) B
.Src
.X
= 48;
3073 else if (((Tick
>> 1) & 31) == 2) B
.Src
.X
= 96;
3076 igraph::GetSmileyGraphic()->NormalBlit(B
);
3077 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
3081 static int doListSelector (felist
&list
, int defsel
, int cnt
) {
3082 game::SetStandardListAttributes(list
);
3083 list
.AddFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3084 if (defsel
> 0) list
.SetSelected(defsel
);
3085 uInt sel
= list
.Draw();
3087 list
.RemoveFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3088 if (sel
& FELIST_ERROR_BIT
) return -1;
3089 if (sel
>= (uInt
)cnt
) return -1;
3094 int game::ListSelector (int defsel
, cfestring
&title
, ...) {
3097 va_start(items
, title
);
3101 const char *s
= va_arg(items
, const char *);
3103 list
.AddEntry(s
, LIGHT_GRAY
);
3107 return doListSelector(list
, defsel
, cnt
);
3111 int game::ListSelectorArray (int defsel
, cfestring
&title
, const char *items
[]) {
3115 if (!items
[cnt
]) break;
3116 list
.AddEntry(items
[cnt
], LIGHT_GRAY
);
3119 return doListSelector(list
, defsel
, cnt
);
3123 void game::ClearEventData () {
3131 // '.': string or number
3134 // '*': collect all args
3135 int game::ParseFuncArgs (cfestring
&types
, std::vector
<FuncArg
> &args
, inputfile
*fl
, truth noterm
) {
3139 if (!fl
) fl
= mFEStack
.top();
3141 for (unsigned int f
= 0; f
< types
.GetSize(); f
++) {
3144 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3145 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3148 n
= fl
->ReadNumber(0xFF, true);
3149 args
.push_back(FuncArg(n
));
3153 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3154 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3155 fl
->ReadWord(s
, true);
3156 if (s
== ";") return args
.size();
3157 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3162 s
= fl
->ReadWord(true);
3163 args
.push_back(FuncArg(s
));
3166 if (f
== types
.GetSize()-1) {
3168 fl
->ReadWord(s
, true);
3169 if (s
!= ";") ABORT("';' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3172 fl
->ReadWord(s
, true);
3173 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3180 truth
game::GetWord (festring
&w
) {
3182 inputfile
*fl
= mFEStack
.top();
3183 fl
->ReadWord(w
, false);
3184 if (w
== "" && fl
->Eof()) {
3187 if (mFEStack
.empty()) return false;
3190 if (w
== "Include") {
3191 fl
->ReadWord(w
, true);
3192 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3193 w
= game::GetGameDir()+"Script/"+w
;
3194 inputfile
*fl
= new inputfile(w
, &game::GetGlobalValueMap(), true);
3195 fl
->setGetVarCB(game::ldrGetVar
);
3199 if (w
== "Message") {
3200 fl
->ReadWord(w
, true);
3201 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3202 fprintf(stderr
, "MESSAGE: %s\n", w
.CStr());
3210 void game::SkipBlock (truth brcEaten
) {
3213 mFEStack
.top()->ReadWord(w
, true);
3214 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3218 mFEStack
.top()->ReadWord(w
, true);
3219 if (w
== "{") cnt
++;
3220 else if (w
== "}") {
3221 if (--cnt
< 1) break;
3227 truth
game::DoOnEvent (truth brcEaten
, truth AllowScript
) {
3228 // do; only funcalls for now
3229 truth eaten
= AllowScript
? true : false;
3232 mFEStack
.top()->ReadWord(w
, true);
3233 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3237 if (AllowScript
) break;
3238 ABORT("Unexpected end of file %s!", mFEStack
.top()->GetFileName().CStr());
3240 //fprintf(stderr, " :[%s]\n", w.CStr());
3242 if (AllowScript
) ABORT("Unexpected '}' in AllowScript file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3245 if (w
== ";") continue;
3247 mFEStack
.top()->ReadWord(w
, true);
3248 if (mFEStack
.top()->ReadWord(true) != "=") ABORT("'=' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3249 //fprintf(stderr, "setvar: %s\n", w.CStr());
3251 sLong n
= mFEStack
.top()->ReadNumber(true);
3253 if (mChar
) mChar
->SetMoney(n
);
3256 if (w
== "result") {
3257 mResult
= mFEStack
.top()->ReadNumber(true);
3260 ABORT("Unknown var [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3262 //mFEStack.top()->ReadWord(w, true);
3263 std::vector
<FuncArg
> args
;
3264 //fprintf(stderr, "funcall: %s\n", w.CStr());
3265 if (w
== "SetMoney") {
3266 ParseFuncArgs("n", args
);
3267 sLong n
= args
[0].ival
;
3269 if (mChar
) mChar
->SetMoney(n
);
3272 if (w
== "EditMoney") {
3273 ParseFuncArgs("n", args
);
3274 sLong n
= args
[0].ival
;
3275 if (mChar
) mChar
->EditMoney(n
);
3278 if (w
== "AddMessage") {
3279 ParseFuncArgs("*", args
);
3281 for (uInt f
= 0; f
< args
.size(); f
++) {
3282 const FuncArg
&a
= args
[f
];
3283 if (a
.type
== FARG_STRING
) s
<< a
.sval
; else s
<< a
.ival
;
3285 ADD_MESSAGE("%s", s
.CStr());
3288 if (w
== "EatThisEvent") {
3289 if (AllowScript
) ABORT("'EatThisEvent' forbidden in AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3293 if (w
== "Disallow") {
3294 if (!AllowScript
) ABORT("'Disallow' forbidden in not-AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3298 ABORT("Unknown function [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3299 //if (mFEStack.top()->ReadWord() != ";") ABORT("';' expected in file %s line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TokenLine());
3301 //ABORT("Invalid term in file %s at line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TokenLine());
3303 //fprintf(stderr, "------------\n");
3308 //TODO: cache event scripts
3309 truth
game::RunOnEvent (cfestring
&ename
) {
3310 static std::vector
<festring
> scriptFiles
;
3311 static truth cached
= false;
3314 character
*old
= mChar
;
3318 for (int fno
= 99; fno
>= -1; fno
--) {
3320 cfname
<< game::GetGameDir() << "Script/onevent";
3323 sprintf(bnum
, "_%02d", fno
);
3327 if (!inputfile::fileExists(cfname
)) continue;
3328 inputfile
*ifl
= new inputfile(cfname
, &game::GetGlobalValueMap(), false);
3329 if (!ifl
->IsOpen()) {
3333 scriptFiles
.push_back(cfname
);
3334 ifl
->setGetVarCB(game::ldrGetVar
);
3338 for (unsigned int f
= 0; f
< scriptFiles
.size(); ++f
) {
3339 festring cfname
= scriptFiles
[f
];
3340 inputfile
*ifl
= new inputfile(cfname
, &game::GetGlobalValueMap(), false);
3341 if (!ifl
->IsOpen()) {
3345 ifl
->setGetVarCB(game::ldrGetVar
);
3351 while (GetWord(w
)) {
3352 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3353 mFEStack
.top()->ReadWord(w
, true);
3354 truth doIt
= (w
==ename
);
3356 res
= DoOnEvent(false);
3367 truth
game::RunOnEventStr (cfestring
&ename
, cfestring
&str
) {
3369 if (str
.GetSize() < 1) return false;
3370 //fprintf(stderr, "=============\n%s=============\n", str.CStr());
3371 inputfile
*ifl
= new meminputfile(str
, &game::GetGlobalValueMap());
3372 ifl
->setGetVarCB(game::ldrGetVar
);
3375 //fprintf(stderr, "=============\n", str.CStr());
3376 //fprintf(stderr, "event: [%s]\n", ename.CStr());
3377 //fprintf(stderr, "---\n%s---\n", str.CStr());
3378 while (GetWord(w
)) {
3379 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3380 mFEStack
.top()->ReadWord(w
, true);
3381 //fprintf(stderr, "on: [%s]\n", w.CStr());
3382 truth doIt
= (w
==ename
);
3384 //fprintf(stderr, " do it\n");
3385 res
= DoOnEvent(false);
3388 //fprintf(stderr, " skip it\n");
3396 truth
game::RunOnCharEvent (character
*who
, cfestring
&ename
) {
3398 if (!who
) return false;
3399 character
*old
= mChar
;
3401 res
= RunOnEventStr(ename
, who
->mOnEvents
);
3402 if (!res
) res
= RunOnEventStr(ename
, who
->GetProtoType()->mOnEvents
);
3408 truth
game::RunOnItemEvent (item
*what
, cfestring
&ename
) {
3410 if (!what
) return false;
3413 res
= RunOnEventStr(ename
, what
->mOnEvents
);
3414 if (!res
) res
= RunOnEventStr(ename
, what
->GetProtoType()->mOnEvents
);
3420 festring
game::ldrGetVar (inputfile
*fl
, cfestring
&name
) {
3421 //fprintf(stderr, "GETVAR: [%s]\n", name.CStr());
3422 if (name
== "player_name") {
3423 return game::GetPlayerName();
3425 if (name
== "money") {
3427 if (!mChar
) return "0";
3428 res
<< mChar
->GetMoney();
3431 if (name
== "name") {
3432 if (!mChar
) return "";
3433 return mChar
->GetAssignedName();
3435 if (name
== "team") {
3437 if (!mChar
) return "";
3438 res
<< mChar
->GetTeam()->GetID();
3441 if (name
== "friendly") {
3443 if (!mChar
|| !PLAYER
|| mChar
->GetRelation(PLAYER
) != HOSTILE
) return "tan";
3446 if (name
== "hostile") {
3448 if (!mChar
|| !PLAYER
) return "";
3449 if (mChar
->GetRelation(PLAYER
) == HOSTILE
) return "tan";
3452 if (name
== "has_item") {
3453 std::vector
<FuncArg
> args
;
3454 ParseFuncArgs("s", args
, fl
, true);
3458 festring s
= args
[0].sval
;
3460 //fprintf(stderr, "looking for [%s]\n", s.CStr());
3461 PLAYER
->GetStack()->FillItemVector(items
);
3462 for (unsigned int f
= 0; f
< items
.size(); ++f
) {
3463 for (uInt c
= 0; c
< items
[f
]->GetDataBase()->Alias
.Size
; ++c
) {
3464 //fprintf(stderr, "%u:%u: [%s]\n", f, c, items[f]->GetDataBase()->Alias[c].CStr());
3465 if (s
.CompareIgnoreCase(items
[f
]->GetDataBase()->Alias
[c
]) == 0) {
3466 //fprintf(stderr, " FOUND!\n");
3471 //fprintf(stderr, "checking equipment...\n");
3472 for (int f
= 0; f
< PLAYER
->GetEquipments(); ++f
) {
3473 item
*it
= PLAYER
->GetEquipment(f
);
3476 for (uInt c
= 0; c
< it
->GetDataBase()->Alias
.Size
; ++c
) {
3477 //fprintf(stderr, "%u:%u: [%s]\n", f, c, it->GetDataBase()->Alias[c].CStr());
3478 if (s
.CompareIgnoreCase(it
->GetDataBase()->Alias
[c
]) == 0) {
3479 //fprintf(stderr, " FOUND!\n");
3488 //if (name == "type") return mVarType;
3489 ABORT("unknown variable: %s", name
.CStr());
3494 truth
game::CheckDropLeftover (item
*i
) {
3495 if (i
->IsBottle() && !ivanconfig::GetAutoDropBottles()) return false;
3496 if (i
->IsCan() && !ivanconfig::GetAutoDropCans()) return false;
3497 if (!ivanconfig::GetAutoDropLeftOvers()) return false;
3502 truth
game::RunAllowScriptStr (cfestring
&str
) {
3504 if (str
.GetSize() < 1) return true;
3505 //fprintf(stderr, "====\n%s\n====\n", str.CStr());
3506 inputfile
*ifl
= new meminputfile(str
, &game::GetGlobalValueMap());
3507 ifl
->setGetVarCB(game::ldrGetVar
);
3509 res
= DoOnEvent(true, true);
3510 //fprintf(stderr, "mFEStack: %u\n", mFEStack.size());