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>
49 #define SAVE_FILE_VERSION 134 // Increment this if changes make savefiles incompatible
50 #define BONE_FILE_VERSION 119 // Increment this if changes make bonefiles incompatible
57 std::stack
<TextInput
*> game::mFEStack
;
58 character
*game::mChar
= 0;
59 ccharacter
*game::mActor
= 0;
60 ccharacter
*game::mSecondActor
= 0;
61 item
*game::mItem
= 0;
62 int game::mResult
= 0;
65 int game::CurrentLevelIndex
;
66 truth
game::InWilderness
= false;
67 worldmap
* game::WorldMap
;
68 area
* game::AreaInLoad
;
69 square
* game::SquareInLoad
;
70 dungeon
** game::Dungeon
;
71 int game::CurrentDungeonIndex
;
72 feuLong
game::NextCharacterID
= 1;
73 feuLong
game::NextItemID
= 1;
74 feuLong
game::NextTrapID
= 1;
76 feuLong
game::LOSTick
;
77 v2
game::CursorPos(-1, -1);
79 truth
game::Generating
= false;
80 double game::AveragePlayerArmStrengthExperience
;
81 double game::AveragePlayerLegStrengthExperience
;
82 double game::AveragePlayerDexterityExperience
;
83 double game::AveragePlayerAgilityExperience
;
88 int game::XinrochTombStoryState
;
89 int game::MondedrPass
;
90 int game::RingOfThieves
;
93 int game::LoricatusHammer
;
95 int game::OmmelBloodMission
;
96 int game::RegiiTalkState
;
98 massacremap
game::PlayerMassacreMap
;
99 massacremap
game::PetMassacreMap
;
100 massacremap
game::MiscMassacreMap
;
101 sLong
game::PlayerMassacreAmount
= 0;
102 sLong
game::PetMassacreAmount
= 0;
103 sLong
game::MiscMassacreAmount
= 0;
104 boneidmap
game::BoneItemIDMap
;
105 boneidmap
game::BoneCharacterIDMap
;
106 truth
game::TooGreatDangerFoundTruth
;
107 itemvectorvector
game::ItemDrawVector
;
108 charactervector
game::CharacterDrawVector
;
109 truth
game::SumoWrestling
;
110 liquid
* game::GlobalRainLiquid
;
111 v2
game::GlobalRainSpeed
;
112 sLong
game::GlobalRainTimeModifier
;
113 truth
game::PlayerSumoChampion
;
114 truth
game::PlayerSolicitusChampion
;
115 feuLong
game::SquarePartEmitationTick
= 0;
117 truth
game::PlayerRunning
;
118 character
* game::LastPetUnderCursor
;
119 charactervector
game::PetVector
;
120 double game::DangerFound
;
121 int game::OldAttribute
[ATTRIBUTES
];
122 int game::NewAttribute
[ATTRIBUTES
];
123 int game::LastAttributeChangeTick
[ATTRIBUTES
];
124 int game::NecroCounter
;
125 int game::CursorData
;
126 truth
game::CausePanicFlag
;
128 truth
game::Loading
= false;
129 truth
game::JumpToPlayerBe
= false;
130 truth
game::InGetCommand
= false;
131 character
*game::Petrus
= 0;
132 time_t game::TimePlayedBeforeLastLoad
;
133 time_t game::LastLoad
;
134 time_t game::GameBegan
;
135 truth
game::PlayerHasReceivedAllGodsKnownBonus
;
137 festring
game::AutoSaveFileName
= game::GetSavePath()+"AutoSave";
138 cchar
*const game::Alignment
[] = { "L++", "L+", "L", "L-", "N+", "N=", "N-", "C+", "C", "C-", "C--" };
141 cint
game::MoveNormalCommandKey
[] = { KEY_HOME
, KEY_UP
, KEY_PAGE_UP
, KEY_LEFT
, KEY_RIGHT
, KEY_END
, KEY_DOWN
, KEY_PAGE_DOWN
, '.' };
142 int game::MoveAbnormalCommandKey
[] = { '7','8','9','u','o','j','k','l','.' };
144 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) };
145 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) };
146 cv2
game::BasicMoveVector
[] = { v2(-1, 0), v2(1, 0), v2(0, -1), v2(0, 1) };
147 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) };
148 cint
game::LargeMoveDirection
[] = { 0, 1, 1, 2, 3, 4, 3, 4, 5, 6, 6, 7, 8, 8, 8, 8 };
150 truth
game::LOSUpdateRequested
= false;
151 uChar
***game::LuxTable
= 0;
153 character
*game::Player
;
154 v2
game::Camera(0, 0);
156 gamescript
*game::GameScript
= 0;
157 valuemap
game::GlobalValueMap
;
158 dangermap
game::DangerMap
;
159 int game::NextDangerIDType
;
160 int game::NextDangerIDConfigIndex
;
161 characteridmap
game::CharacterIDMap
;
162 itemidmap
game::ItemIDMap
;
163 trapidmap
game::TrapIDMap
;
164 truth
game::PlayerHurtByExplosion
;
165 area
*game::CurrentArea
;
166 level
*game::CurrentLevel
;
167 wsquare
***game::CurrentWSquareMap
;
168 lsquare
***game::CurrentLSquareMap
;
169 festring
game::DefaultPolymorphTo
;
170 festring
game::DefaultSummonMonster
;
171 festring
game::DefaultWish
;
172 festring
game::DefaultChangeMaterial
;
173 festring
game::DefaultDetectMaterial
;
174 festring
game::DefaultTeam
;
175 truth
game::WizardMode
;
176 int game::SeeWholeMapCheatMode
;
177 truth
game::GoThroughWallsCheat
;
178 int game::QuestMonstersFound
;
179 bitmap
*game::BusyAnimationCache
[32];
180 festring
game::PlayerName
;
181 feuLong
game::EquipmentMemory
[MAX_EQUIPMENT_SLOTS
];
182 olterrain
*game::MonsterPortal
;
183 std::vector
<v2
> game::SpecialCursorPos
;
184 std::vector
<int> game::SpecialCursorData
;
185 cbitmap
*game::EnterImage
;
186 v2
game::EnterTextDisplacement
;
190 int game::MoveVectorToDirection (cv2
&mv
) {
191 for (int c
= 0; c
< 9; ++c
) if (MoveVector
[c
] == mv
) return c
;
196 char game::GetAbnormalMoveKey (int idx
) {
197 if (idx
< 0 || idx
> 8) return 0;
198 return MoveAbnormalCommandKey
[idx
];
202 void game::SetAbnormalMoveKey (int idx
, char ch
) {
203 if (idx
>= 0 && idx
<= 8) MoveAbnormalCommandKey
[idx
] = ch
;
207 void game::AddCharacterID (character
*Char
, feuLong ID
) {
208 /*k8:??? if (CharacterIDMap.find(ID) != CharacterIDMap.end())
209 int esko = esko = 2;*/
210 CharacterIDMap
.insert(std::make_pair(ID
, Char
));
214 void game::RemoveCharacterID (feuLong ID
) {
215 /*k8:??? if (CharacterIDMap.find(ID) == CharacterIDMap.end())
216 int esko = esko = 2;*/
217 CharacterIDMap
.erase(CharacterIDMap
.find(ID
));
221 void game::AddItemID (item
*Item
, feuLong ID
) {
222 /*k8:??? if (ItemIDMap.find(ID) != ItemIDMap.end())
223 int esko = esko = 2;*/
224 ItemIDMap
.insert(std::make_pair(ID
, Item
));
228 void game::RemoveItemID (feuLong ID
) {
229 /*k8:??? if(ID && ItemIDMap.find(ID) == ItemIDMap.end())
230 int esko = esko = 2;*/
231 if (ID
) ItemIDMap
.erase(ItemIDMap
.find(ID
));
235 void game::UpdateItemID (item
*Item
, feuLong ID
) {
236 /*k8:??? if(ItemIDMap.find(ID) == ItemIDMap.end())
237 int esko = esko = 2;*/
238 ItemIDMap
.find(ID
)->second
= Item
;
242 void game::AddTrapID (entity
*Trap
, feuLong ID
) {
243 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
244 int esko = esko = 2;*/
245 if (ID
) TrapIDMap
.insert(std::make_pair(ID
, Trap
));
249 void game::RemoveTrapID (feuLong ID
) {
250 /*k8:??? if(ID && TrapIDMap.find(ID) == TrapIDMap.end())
251 int esko = esko = 2;*/
252 if (ID
) TrapIDMap
.erase(TrapIDMap
.find(ID
));
256 void game::UpdateTrapID (entity
*Trap
, feuLong ID
) {
257 /*k8:??? if(TrapIDMap.find(ID) == TrapIDMap.end())
258 int esko = esko = 2;*/
259 TrapIDMap
.find(ID
)->second
= Trap
;
263 const dangermap
&game::GetDangerMap () { return DangerMap
; }
264 void game::ClearItemDrawVector () { ItemDrawVector
.clear(); }
265 void game::ClearCharacterDrawVector () { CharacterDrawVector
.clear(); }
268 void game::InitScript () {
269 TextInputFile
ScriptFile(GetGameDir()+"script/dungeon.dat", &GlobalValueMap
);
270 GameScript
= new gamescript
;
271 GameScript
->ReadFrom(ScriptFile
);
272 { /* additional dungeon files */
273 for (int f
= 0; f
<= 99; f
++) {
275 sprintf(bnum
, "script/dungeon_%02d.dat", f
);
276 TextInputFile
ifl(game::GetGameDir()+bnum
, &game::GetGlobalValueMap(), false);
278 //fprintf(stderr, "loading: %s\n", bnum+7);
279 GameScript
->ReadFrom(ifl
);
284 GameScript
->RandomizeLevels();
288 truth
game::Init (cfestring
&Name
) {
289 if (Name
.IsEmpty()) {
290 if (ivanconfig::GetDefaultName().IsEmpty()) {
292 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;
294 PlayerName
= ivanconfig::GetDefaultName();
300 mkdir(GetSavePath().CStr(), S_IRWXU
|S_IRWXG
);
301 mkdir(GetBonePath().CStr(), S_IRWXU
|S_IRWXG
);
306 CausePanicFlag
= false;
309 switch (Load(SaveName(PlayerName
))) {
311 globalwindowhandler::InstallControlLoop(AnimationController
);
313 SetForceJumpToPlayerBe(true);
314 GetCurrentArea()->SendNewDrawRequest();
315 SendLOSUpdateRequest();
316 ADD_MESSAGE("Game loaded successfully.");
319 iosystem::TextScreen(CONST_S(
320 "You couldn't possibly have guessed this day would differ from any other.\n"
321 "It began just as always. You woke up at dawn and drove off the giant spider\n"
322 "resting on your face. On your way to work you had serious trouble avoiding\n"
323 "the lions and pythons roaming wild around the village. After getting kicked\n"
324 "by colony masters for being late you performed your twelve-hour routine of\n"
325 "climbing trees, gathering bananas, climbing trees, gathering bananas, chasing\n"
326 "monkeys that stole the first gathered bananas, carrying bananas to the village\n"
327 "and trying to look happy when real food was distributed.\n\n"
328 "Finally you were about to enjoy your free time by taking a quick dip in the\n"
329 "nearby crocodile bay. However, at this point something unusual happened.\n"
330 "You were summoned to the mansion of Richel Decos, the viceroy of the\n"
331 "colony, and were led directly to him."));
332 iosystem::TextScreen(CONST_S(
333 "\"I have a task for you, citizen\", said the viceroy picking his golden\n"
334 "teeth, \"The market price of bananas has taken a deep dive and yet the\n"
335 "central government is about to raise taxes. I have sent appeals to high\n"
336 "priest Petrus but received no response. I fear my enemies in Attnam are\n"
337 "plotting against me and intercepting my messages before they reach him!\"\n\n"
338 "\"That is why you must travel to Attnam with a letter I'll give you and\n"
339 "deliver it to Petrus directly. Alas, you somehow have to cross the sea\n"
340 "between. Because it's winter, all Attnamese ships are trapped by ice and\n"
341 "I have none. Therefore you must venture through the small underwater tunnel\n"
342 "connecting our islands. It is infested with monsters, but since you have\n"
343 "stayed alive here so long, the trip will surely cause you no trouble.\"\n\n"
344 "You have never been so happy! According to the mansion's traveling\n"
345 "brochures, Attnam is a peaceful but bustling world city on a beautiful\n"
346 "snowy fell surrounded by frozen lakes glittering in the arctic sun just\n"
347 "like the diamonds of the imperial treasury. Not that you would believe a\n"
348 "word. The point is that tomorrow you can finally forget your home and\n"
349 "face the untold adventures ahead."));
350 pool::RemoveEverything(); // memory leak!
352 globalwindowhandler::InstallControlLoop(AnimationController
);
355 CausePanicFlag
= false;
358 iosystem::TextScreen(CONST_S("Generating game...\n\nThis may take some time, please wait."), ZERO_V2
, WHITE
, false, true, &BusyAnimation
);
359 igraph::CreateBackGround(GRAY_FRACTAL
);
366 SetPlayer(playerkind::Spawn());
367 Player
->SetAssignedName(PlayerName
);
368 Player
->SetTeam(GetTeam(PLAYER_TEAM
));
369 Player
->SetNP(SATIATED_LEVEL
);
370 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
371 if (c
!= ENDURANCE
) Player
->EditAttribute(c
, (RAND()&1)-(RAND()&1));
372 Player
->EditExperience(c
, 500, 1<<11);
374 Player
->SetMoney(Player
->GetMoney()+RAND()%11);
375 GetTeam(0)->SetLeader(Player
);
379 if (Player
->IsEnabled()) { Player
->Disable(); Player
->Enable(); }
383 SetCurrentArea(WorldMap
= new worldmap(128, 128));
384 CurrentWSquareMap
= WorldMap
->GetMap();
385 WorldMap
->Generate();
387 SendLOSUpdateRequest();
390 InitPlayerAttributeAverage();
393 XinrochTombStoryState
= 0;
400 OmmelBloodMission
= 0;
403 PlayerMassacreMap
.clear();
404 PetMassacreMap
.clear();
405 MiscMassacreMap
.clear();
406 PlayerMassacreAmount
= PetMassacreAmount
= MiscMassacreAmount
= 0;
407 DefaultPolymorphTo
.Empty();
408 DefaultSummonMonster
.Empty();
410 DefaultChangeMaterial
.Empty();
411 DefaultDetectMaterial
.Empty();
413 Player
->GetStack()->AddItem(encryptedscroll::Spawn());
414 if (ivanconfig::GetDefaultPetName() != "_none_") {
415 character
*Doggie
= dog::Spawn();
416 Doggie
->SetTeam(GetTeam(PLAYER_TEAM
));
417 GetWorldMap()->GetPlayerGroup().push_back(Doggie
);
418 Doggie
->SetAssignedName(ivanconfig::GetDefaultPetName());
421 SeeWholeMapCheatMode
= MAP_HIDDEN
;
422 GoThroughWallsCheat
= false;
423 SumoWrestling
= false;
424 GlobalRainTimeModifier
= 2048-(RAND()&4095);
425 PlayerSumoChampion
= false;
426 PlayerSolicitusChampion
= false;
427 protosystem::InitCharacterDataBaseFlags();
428 memset(EquipmentMemory
, 0, sizeof(EquipmentMemory
));
429 PlayerRunning
= false;
430 InitAttributeMemory();
434 TimePlayedBeforeLastLoad
= time::GetZeroTime();
435 /*k8: damn! seems that this is field, not local! bool PlayerHasReceivedAllGodsKnownBonus = false; */
436 PlayerHasReceivedAllGodsKnownBonus
= false;
437 ADD_MESSAGE("You commence your journey to Attnam. Use direction keys to move, '>' to enter an area and '?' to view other commands.");
438 game::ClearEventData();
439 RunOnEvent("game_start");
441 item
*Present
= banana::Spawn();
442 Player
->GetStack()->AddItem(Present
);
443 ADD_MESSAGE("Atavus is happy today! He gives you %s.", Present
->CHAR_NAME(INDEFINITE
));
446 default: return false;
451 void game::DeInit () {
456 for (int c
= 1; c
< Dungeons
; ++c
) delete Dungeon
[c
];
461 for (int c
= 1; c
<= GODS
; ++c
) delete God
[c
]; // sorry, Valpuri!
466 for (int c
= 0; c
< Teams
; ++c
) delete Team
[c
];
480 /* Temporary places */
481 static int Counter
= 0;
482 if (++Counter
== 10) {
483 CurrentLevel
->GenerateMonsters();
486 if (CurrentDungeonIndex
== ELPURI_CAVE
&& CurrentLevelIndex
== ZOMBIE_LEVEL
&& !RAND_N(1000+NecroCounter
)) {
487 character
*Char
= necromancer::Spawn(RAND_N(4) ? APPRENTICE_NECROMANCER
: MASTER_NECROMANCER
);
489 for (int c2
= 0; c2
< 30; ++c2
) {
490 Pos
= GetCurrentLevel()->GetRandomSquare(Char
);
491 if (abs(int(Pos
.X
)-Player
->GetPos().X
) > 20 || abs(int(Pos
.Y
)-Player
->GetPos().Y
) > 20) break;
493 if (Pos
!= ERROR_V2
) {
494 Char
->SetTeam(GetTeam(MONSTER_TEAM
));
496 Char
->SetGenerationDanger(GetCurrentLevel()->GetDifficulty());
497 Char
->SignalGeneration();
498 Char
->SignalNaturalGeneration();
501 int Modifier
= Time
.Day
- EDIT_ATTRIBUTE_DAY_MIN
;
502 if (Modifier
> 0) Char
->EditAllAttributes(Modifier
>> EDIT_ATTRIBUTE_DAY_SHIFT
);
506 //Char->SendToHell(); // k8:equipment
510 if (!(GetTick() % 1000)) CurrentLevel
->CheckSunLight();
512 if ((CurrentDungeonIndex
== NEW_ATTNAM
|| CurrentDungeonIndex
== ATTNAM
) && CurrentLevelIndex
== 0) {
513 sLong OldVolume
= GlobalRainLiquid
->GetVolume();
514 sLong NewVolume
= Max(sLong(sin((Tick
+GlobalRainTimeModifier
)*0.0003)*300-150), 0);
515 if (NewVolume
&& !OldVolume
) CurrentLevel
->EnableGlobalRain();
516 else if(!NewVolume
&& OldVolume
) CurrentLevel
->DisableGlobalRain();
517 GlobalRainLiquid
->SetVolumeNoSignals(NewVolume
);
521 if (!RAND_N(2)) Item = wand::Spawn(1 + RAND_N(12));
522 else if(!RAND_N(2)) {
523 Item = beartrap::Spawn();
524 Item->SetIsActive(true);
525 Item->SetTeam(MONSTER_TEAM);
526 } else if(!RAND_N(2)) {
527 Item = mine::Spawn();
528 Item->SetIsActive(true);
529 Item->SetTeam(MONSTER_TEAM);
530 } else Item = holybanana::Spawn();
531 CurrentLevel->GetLSquare(CurrentLevel->GetRandomSquare())->AddItem(Item);
535 character *Char = protosystem::CreateMonster(0, 1000000);
536 Char->ChangeTeam(GetTeam(RAND() % Teams));
537 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
542 if (!RAND_N(5)) Char = spider::Spawn(GIANT);
543 else if (!RAND_N(5)) Char = darkmage::Spawn(1 + RAND_N(4));
544 else if (!RAND_N(5)) Char = necromancer::Spawn(1 + RAND_N(2));
545 else if (!RAND_N(5)) Char = chameleon::Spawn();
546 else if (!RAND_N(5)) Char = kamikazedwarf::Spawn(1 + RAND_N(GODS));
547 else if (!RAND_N(5)) Char = mommo::Spawn(1 + RAND_N(2));
548 else if (!RAND_N(3)) Char = bunny::Spawn(RAND_2 ? ADULT_MALE : ADULT_FEMALE);
549 else if (!RAND_N(3)) Char = eddy::Spawn();
550 else if (!RAND_N(3)) Char = magicmushroom::Spawn();
551 else if (!RAND_N(5)) Char = mushroom::Spawn();
552 else if (!RAND_N(3)) Char = blinkdog::Spawn();
553 else if (!RAND_N(5)) Char = tourist::Spawn(1 + RAND_N(3));
554 else if (!RAND_N(5)) Char = hattifattener::Spawn();
555 else if (!RAND_N(5)) Char = genetrixvesana::Spawn();
556 else if (!RAND_N(5)) Char = skunk::Spawn();
557 else if (!RAND_N(5)) Char = ennerbeast::Spawn();
558 else if (!RAND_N(5)) Char = werewolfhuman::Spawn();
559 else if (!RAND_N(5)) Char = unicorn::Spawn(1 + RAND_N(3));
560 else if (!RAND_N(5)) Char = floatingeye::Spawn();
561 else if (!RAND_N(5)) Char = zombie::Spawn();
562 else if (!RAND_N(5)) Char = magpie::Spawn();
563 else if (!RAND_N(5)) Char = elpuri::Spawn();
564 else if (!RAND_N(5)) Char = vladimir::Spawn();
565 else if (!RAND_N(5)) Char = billswill::Spawn();
566 else if (!RAND_N(5)) Char = ghost::Spawn();
567 else if (!RAND_N(5)) Char = dolphin::Spawn();
568 else if (!RAND_N(5)) Char = cossack::Spawn();
569 else Char = invisiblestalker::Spawn();
570 Char->SetTeam(GetTeam(RAND() % Teams));
571 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
582 } catch (quitrequest
) {
584 } catch (areachangerequest
) {
590 void game::InitLuxTable () {
592 Alloc3D(LuxTable
, 256, 33, 33);
593 for (int c
= 0; c
< 0x100; ++c
)
594 for (int x
= 0; x
< 33; ++x
)
595 for (int y
= 0; y
< 33; ++y
) {
596 int X
= x
-16, Y
= y
-16;
597 LuxTable
[c
][x
][y
] = int(c
/(double(X
*X
+Y
*Y
)/128+1));
599 atexit(DeInitLuxTable
);
604 void game::DeInitLuxTable () {
610 void game::UpdateCameraX () {
611 UpdateCameraX(Player
->GetPos().X
);
615 void game::UpdateCameraY () {
616 UpdateCameraY(Player
->GetPos().Y
);
620 void game::UpdateCameraX (int X
) {
621 UpdateCameraCoordinate(Camera
.X
, X
, GetCurrentArea()->GetXSize(), GetScreenXSize());
625 void game::UpdateCameraY (int Y
) {
626 UpdateCameraCoordinate(Camera
.Y
, Y
, GetCurrentArea()->GetYSize(), GetScreenYSize());
630 void game::UpdateCameraCoordinate (int &Coordinate
, int Center
, int Size
, int ScreenSize
) {
631 int OldCoordinate
= Coordinate
;
632 if (Size
< ScreenSize
) Coordinate
= (Size
-ScreenSize
)>>1;
633 else if(Center
< ScreenSize
>>1) Coordinate
= 0;
634 else if(Center
> Size
-(ScreenSize
>>1)) Coordinate
= Size
-ScreenSize
;
635 else Coordinate
= Center
-(ScreenSize
>>1);
636 if (Coordinate
!= OldCoordinate
) GetCurrentArea()->SendNewDrawRequest();
640 cchar
*game::Insult () {
641 static const char *insults
[19] = {
654 "stupid-headed person",
658 "person-with-problems",
663 if (n
< 0 || n
> 18) n
= 18;
668 /* DefaultAnswer = REQUIRES_ANSWER the question requires an answer */
669 truth
game::TruthQuestion (cfestring
&String
, int DefaultAnswer
, int OtherKeyForTrue
) {
670 festring xstr
= String
;
671 if (DefaultAnswer
== NO
) { DefaultAnswer
= 'n'; xstr
<< " [\1Cy\2/\1RN\2]"; }
672 else if (DefaultAnswer
== YES
) { DefaultAnswer
= 'y'; xstr
<< " [\1RY\2/\1Cn\2]"; }
673 else if (DefaultAnswer
== REQUIRES_ANSWER
) { xstr
<< " [\1Cy\2/\1Cn\2]"; }
674 else ABORT("Illegal TruthQuestion DefaultAnswer send!");
675 int FromKeyQuestion
= KeyQuestion(/*String*/xstr
, DefaultAnswer
, 9, 'y', 'Y', 'n', 'N', 't', 'T', 'o', 'O', OtherKeyForTrue
);
677 FromKeyQuestion
== 'y' || FromKeyQuestion
== 'Y' ||
678 FromKeyQuestion
== 't' || FromKeyQuestion
== 'T' ||
679 FromKeyQuestion
== OtherKeyForTrue
;
683 void game::DrawEverything () {
684 DrawEverythingNoBlit();
685 graphics::BlitDBToScreen();
689 truth
game::OnScreen (v2 Pos
) {
690 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();
694 void game::DrawEverythingNoBlit (truth AnimationDraw
) {
695 if (LOSUpdateRequested
&& Player
->IsEnabled()) {
696 if (!IsInWilderness()) GetCurrentLevel()->UpdateLOS(); else GetWorldMap()->UpdateLOS();
699 if (OnScreen(CursorPos
)) {
700 if (!IsInWilderness() || CurrentWSquareMap
[CursorPos
.X
][CursorPos
.Y
]->GetLastSeen() || GetSeeWholeMapCheatMode())
701 CurrentArea
->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
703 DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, 0);
706 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
707 if (OnScreen(SpecialCursorPos
[c
])) CurrentArea
->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
710 globalwindowhandler::UpdateTick();
711 GetCurrentArea()->Draw(AnimationDraw
);
712 Player
->DrawPanel(AnimationDraw
);
714 if (!AnimationDraw
) msgsystem::Draw();
716 if (OnScreen(CursorPos
)) {
717 v2 ScreenCoord
= CalculateScreenCoordinates(CursorPos
);
721 { ScreenCoord
.X
, ScreenCoord
.Y
},
722 { TILE_SIZE
, TILE_SIZE
},
725 ALLOW_ANIMATE
|ALLOW_ALPHA
728 if (!IsInWilderness() && !GetSeeWholeMapCheatMode()) {
729 lsquare
*Square
= CurrentLSquareMap
[CursorPos
.X
][CursorPos
.Y
];
730 if (Square
->GetLastSeen() != GetLOSTick()) Square
->DrawMemorized(B
);
735 B
.Dest
.X
= RES
.X
- 96;
736 B
.Dest
.Y
= RES
.Y
- 96;
738 DOUBLE_BUFFER
->StretchBlit(B
);
741 igraph::DrawCursor(ScreenCoord
, CursorData
);
744 if (Player
->IsEnabled()) {
745 if (Player
->IsSmall()) {
746 v2 Pos
= Player
->GetPos();
748 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
749 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData());
752 for (int f
= 0; f
< Player
->GetSquaresUnder(); ++f
) {
753 v2 Pos
= Player
->GetPos(f
);
755 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
756 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData()|CURSOR_BIG
, f
);
762 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
763 if (OnScreen(SpecialCursorPos
[c
])) {
764 v2 ScreenCoord
= CalculateScreenCoordinates(SpecialCursorPos
[c
]);
765 igraph::DrawCursor(ScreenCoord
, SpecialCursorData
[c
]);
766 GetCurrentArea()->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
772 truth
game::Save (cfestring
&SaveName
) {
773 if (!GetCurrentArea()->GetSquare(Player
->GetPos())->GetCharacter()) {
774 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
);
777 DrawEverythingNoBlit();
778 #if defined(SGAME_SHOTS_IPU) || (!defined(HAVE_IMLIB2) && !defined(HAVE_LIBPNG))
779 DOUBLE_BUFFER
->SaveScaledIPU(SaveName
+".ipu", 0.8); //640; 320
781 DOUBLE_BUFFER
->SaveScaledPNG(SaveName
+".png", 0.8); //640; 320
783 outputfile
SaveFile(SaveName
+".sav", ivanconfig::GetUseMaximumCompression());
784 SaveFile
<< int(SAVE_FILE_VERSION
);
785 SaveFile
<< GameScript
<< CurrentDungeonIndex
<< CurrentLevelIndex
<< Camera
;
786 SaveFile
<< WizardMode
<< SeeWholeMapCheatMode
<< GoThroughWallsCheat
;
787 SaveFile
<< Tick
<< Turn
<< InWilderness
<< NextCharacterID
<< NextItemID
<< NextTrapID
<< NecroCounter
;
788 SaveFile
<< SumoWrestling
<< PlayerSumoChampion
<< GlobalRainTimeModifier
;
789 SaveFile
<< PlayerSolicitusChampion
;
790 //sLong Seed = RAND();
791 //femath::SetSeed(Seed);
793 femath::SavePRNG(SaveFile
);
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
<< XinrochTombStoryState
;
801 SaveFile
<< PlayerMassacreMap
<< PetMassacreMap
<< MiscMassacreMap
;
802 SaveFile
<< PlayerMassacreAmount
<< PetMassacreAmount
<< MiscMassacreAmount
;
803 SaveArray(SaveFile
, EquipmentMemory
, MAX_EQUIPMENT_SLOTS
);
804 for (int c
= 0; c
< ATTRIBUTES
; ++c
) SaveFile
<< OldAttribute
[c
] << NewAttribute
[c
] << LastAttributeChangeTick
[c
];
805 for (int c
= 1; c
< Dungeons
; ++c
) SaveFile
<< Dungeon
[c
];
806 for (int c
= 1; c
<= GODS
; ++c
) SaveFile
<< God
[c
];
807 for (int c
= 0; c
< Teams
; ++c
) SaveFile
<< Team
[c
];
809 SaveWorldMap(SaveName
, false);
811 GetCurrentDungeon()->SaveLevel(SaveName
, CurrentLevelIndex
, false);
813 SaveFile
<< Player
->GetPos() << PlayerName
;
814 msgsystem::Save(SaveFile
);
815 SaveFile
<< DangerMap
<< NextDangerIDType
<< NextDangerIDConfigIndex
;
816 SaveFile
<< DefaultPolymorphTo
<< DefaultSummonMonster
;
817 SaveFile
<< DefaultWish
<< DefaultChangeMaterial
<< DefaultDetectMaterial
<< DefaultTeam
;
818 SaveFile
<< GetTimeSpent();
819 /* or in more readable format: time() - LastLoad + TimeAtLastLoad */
820 SaveFile
<< PlayerHasReceivedAllGodsKnownBonus
;
821 protosystem::SaveCharacterDataBaseFlags(SaveFile
);
826 int game::Load (cfestring
&SaveName
) {
827 inputfile
SaveFile(SaveName
+".sav", false);
828 if (!SaveFile
.IsOpen()) return NEW_GAME
;
831 if (Version
!= SAVE_FILE_VERSION
) {
832 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
)) {
838 SaveFile
>> GameScript
>> CurrentDungeonIndex
>> CurrentLevelIndex
>> Camera
;
839 SaveFile
>> WizardMode
>> SeeWholeMapCheatMode
>> GoThroughWallsCheat
;
840 SaveFile
>> Tick
>> Turn
>> InWilderness
>> NextCharacterID
>> NextItemID
>> NextTrapID
>> NecroCounter
;
841 SaveFile
>> SumoWrestling
>> PlayerSumoChampion
>> GlobalRainTimeModifier
;
842 SaveFile
>> PlayerSolicitusChampion
;
843 //femath::SetSeed(ReadType(sLong, SaveFile));
844 femath::LoadPRNG(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
>> XinrochTombStoryState
;
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
>> DefaultTeam
;
888 SaveFile
>> TimePlayedBeforeLastLoad
;
889 SaveFile
>> PlayerHasReceivedAllGodsKnownBonus
;
891 protosystem::LoadCharacterDataBaseFlags(SaveFile
);
896 festring
game::SaveName (cfestring
&Base
) {
897 festring SaveName
= GetSavePath();
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
];
1067 for (int c
= 0; c
< Teams
; ++c
) {
1068 Team
[c
] = new team(c
);
1069 for (int i
= 0; i
< c
; ++i
) Team
[i
]->SetRelation(Team
[c
], UNCARING
);
1071 for (int c
= 0; c
< Teams
; ++c
) if (c
!= MONSTER_TEAM
) Team
[MONSTER_TEAM
]->SetRelation(Team
[c
], HOSTILE
);
1072 const std::list
<std::pair
<int, teamscript
> >& TeamScript
= GetGameScript()->GetTeam();
1073 for (std::list
<std::pair
<int, teamscript
> >::const_iterator i
= TeamScript
.begin(); i
!= TeamScript
.end(); ++i
) {
1074 for (uInt c
= 0; c
< i
->second
.GetRelation().size(); ++c
) {
1075 GetTeam(i
->second
.GetRelation()[c
].first
)->SetRelation(GetTeam(i
->first
), i
->second
.GetRelation()[c
].second
);
1077 cint
*KillEvilness
= i
->second
.GetKillEvilness();
1078 if (KillEvilness
) GetTeam(i
->first
)->SetKillEvilness(*KillEvilness
);
1079 if (i
->second
.GetName()) GetTeam(i
->first
)->SetName(*i
->second
.GetName());
1084 team
*game::FindTeam (cfestring
&name
) {
1085 for (int c
= 0; c
< Teams
; ++c
) {
1086 if (Team
[c
]->GetName().CompareIgnoreCase(name
) == 0) return Team
[c
];
1092 /* v2 Pos should be removed from xxxQuestion()s? */
1093 festring
game::StringQuestion (cfestring
&Topic
, col16 Color
, festring::sizetype MinLetters
, festring::sizetype MaxLetters
, truth AllowExit
, stringkeyhandler KeyHandler
) {
1094 DrawEverythingNoBlit();
1095 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); // pos may be incorrect!
1097 iosystem::StringQuestion(Return
, Topic
, v2(16, 6), Color
, MinLetters
, MaxLetters
, false, AllowExit
, KeyHandler
);
1098 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1103 sLong
game::NumberQuestion (cfestring
&Topic
, col16 Color
, truth ReturnZeroOnEsc
) {
1104 DrawEverythingNoBlit();
1105 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1106 sLong Return
= iosystem::NumberQuestion(Topic
, v2(16, 6), Color
, false, ReturnZeroOnEsc
);
1107 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1112 sLong
game::ScrollBarQuestion (cfestring
&Topic
, sLong BeginValue
, sLong Step
, sLong Min
, sLong Max
, sLong AbortValue
, col16 TopicColor
, col16 Color1
, col16 Color2
, void (*Handler
)(sLong
)) {
1113 DrawEverythingNoBlit();
1114 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1115 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
);
1116 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1121 feuLong
game::IncreaseLOSTick () {
1122 if (LOSTick
!= 0xFE) return LOSTick
+= 2;
1123 CurrentLevel
->InitLastSeen();
1128 void game::UpdateCamera () {
1134 truth
game::HandleQuitMessage () {
1136 if (IsInGetCommand()) {
1137 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
)) {
1143 GetCurrentArea()->SendNewDrawRequest();
1147 festring Msg
= CONST_S("cowardly quit the game");
1148 Player
->AddScoreEntry(Msg
, 0.75);
1149 End(Msg
, true, false);
1152 } 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
)) {
1155 GetCurrentArea()->SendNewDrawRequest();
1164 int game::GetDirectionForVector (v2 Vector
) {
1165 for (int c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) if (Vector
== GetMoveVector(c
)) return c
;
1170 cchar
*game::GetVerbalPlayerAlignment () {
1172 for (int c
= 1; c
<= GODS
; ++c
) {
1173 if (GetGod(c
)->GetRelation() > 0) Sum
+= GetGod(c
)->GetRelation() * (5 - GetGod(c
)->GetAlignment());
1175 if (Sum
> 15000) return "extremely lawful";
1176 if (Sum
> 10000) return "very lawful";
1177 if (Sum
> 5000) return "lawful";
1178 if (Sum
> 1000) return "mildly lawful";
1179 if (Sum
> -1000) return "neutral";
1180 if (Sum
> -5000) return "mildly chaotic";
1181 if (Sum
> -10000) return "chaotic";
1182 if (Sum
> -15000) return "very chaotic";
1183 return "extremely chaotic";
1187 void game::CreateGods () {
1188 God
= new god
*[GODS
+1];
1190 for (int c
= 1; c
< protocontainer
<god
>::GetSize(); ++c
) God
[c
] = protocontainer
<god
>::GetProto(c
)->Spawn();
1194 void game::BusyAnimation () {
1195 BusyAnimation(DOUBLE_BUFFER
, false);
1199 void game::BusyAnimation (bitmap
*Buffer
, truth ForceDraw
) {
1200 static clock_t LastTime
= 0;
1201 static int Frame
= 0;
1202 static blitdata B1
= {
1211 static blitdata B2
= {
1214 { (RES
.X
>> 1) - 100, (RES
.Y
<< 1) / 3 - 100 },
1220 if (ForceDraw
|| clock()-LastTime
> CLOCKS_PER_SEC
/25) {
1222 B2
.Dest
.X
= (RES
.X
>>1)-100+EnterTextDisplacement
.X
;
1223 B2
.Dest
.Y
= (RES
.Y
<<1)/3-100+EnterTextDisplacement
.Y
;
1226 EnterImage
->NormalMaskedBlit(B1
);
1228 BusyAnimationCache
[Frame
]->NormalBlit(B2
);
1229 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
1230 if (++Frame
== 32) Frame
= 0;
1236 void game::CreateBusyAnimationCache () {
1237 bitmap
Elpuri(TILE_V2
, TRANSPARENT_COLOR
);
1238 Elpuri
.ActivateFastFlag();
1239 packcol16 Color
= MakeRGB16(60, 60, 60);
1240 igraph::GetCharacterRawGraphic()->MaskedBlit(&Elpuri
, v2(64, 0), ZERO_V2
, TILE_V2
, &Color
);
1241 bitmap
Circle(v2(200, 200), TRANSPARENT_COLOR
);
1242 Circle
.ActivateFastFlag();
1243 for (int x
= 0; x
< 4; ++x
) Circle
.DrawPolygon(100, 100, 95+x
, 50, MakeRGB16(255-12*x
, 0, 0));
1248 { TILE_SIZE
, TILE_SIZE
},
1262 for (int c
= 0; c
< 32; ++c
) {
1263 B1
.Bitmap
= B2
.Bitmap
= BusyAnimationCache
[c
] = new bitmap(v2(200, 200), 0);
1264 B1
.Bitmap
->ActivateFastFlag();
1265 Elpuri
.NormalMaskedBlit(B1
);
1266 double Rotation
= 0.3+c
*FPI
/80;
1267 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);
1268 Circle
.NormalMaskedBlit(B2
);
1273 int game::AskForKeyPress (cfestring
&Topic
) {
1274 DrawEverythingNoBlit();
1275 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CapitalizeCopy().CStr());
1276 graphics::BlitDBToScreen();
1277 int Key
= GET_KEY();
1278 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1283 void game::AskForEscPress (cfestring
&Topic
) {
1284 DrawEverythingNoBlit();
1285 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), RED
/*WHITE*/, "%s [press ESC]", Topic
.CapitalizeCopy().CStr());
1286 graphics::BlitDBToScreen();
1290 } while (Key
!= KEY_ESC
);
1291 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1295 /* Handler is called when the key has been identified as a movement key
1296 * KeyHandler is called when the key has NOT been identified as a movement key
1297 * Both can be deactivated by passing 0 as parameter */
1298 v2
game::PositionQuestion (cfestring
&Topic
, v2 CursorPos
, void (*Handler
)(v2
), positionkeyhandler KeyHandler
, truth Zoom
) {
1302 CursorData
= RED_CURSOR
;
1303 if (Handler
) Handler(CursorPos
);
1305 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1306 if (!Square
->HasBeenSeen() &&
1307 (!Square
->GetCharacter() || !Square
->GetCharacter()->CanBeSeenByPlayer()) &&
1308 !GetSeeWholeMapCheatMode()) DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, BLACK
);
1309 else GetCurrentArea()->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
1311 if (Key
== ' ' || Key
== '.' || Key
== KEY_NUMPAD_5
) { Return
= CursorPos
; break; }
1312 if (Key
== KEY_ESC
) { Return
= ERROR_V2
; break; }
1314 v2 DirectionVector
= GetDirectionVectorForKey(Key
);
1315 if (DirectionVector
!= ERROR_V2
) {
1316 CursorPos
+= DirectionVector
;
1317 if (CursorPos
.X
> GetCurrentArea()->GetXSize()-1) CursorPos
.X
= 0;
1318 if (CursorPos
.X
< 0) CursorPos
.X
= GetCurrentArea()->GetXSize()-1;
1319 if (CursorPos
.Y
> GetCurrentArea()->GetYSize()-1) CursorPos
.Y
= 0;
1320 if (CursorPos
.Y
< 0) CursorPos
.Y
= GetCurrentArea()->GetYSize()-1;
1321 if (Handler
) Handler(CursorPos
);
1322 } else if (KeyHandler
) {
1323 CursorPos
= KeyHandler(CursorPos
, Key
);
1324 if (CursorPos
== ERROR_V2
|| CursorPos
== ABORT_V2
) {
1330 if (ivanconfig::GetAutoCenterMapOnLook()) {
1331 UpdateCameraX(CursorPos
.X
);
1332 UpdateCameraY(CursorPos
.Y
);
1334 if (CursorPos
.X
< GetCamera().X
+3 || CursorPos
.X
>= GetCamera().X
+GetScreenXSize()-3) UpdateCameraX(CursorPos
.X
);
1335 if (CursorPos
.Y
< GetCamera().Y
+3 || CursorPos
.Y
>= GetCamera().Y
+GetScreenYSize()-3) UpdateCameraY(CursorPos
.Y
);
1338 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CStr());
1339 SetCursorPos(CursorPos
);
1344 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1345 igraph::BlitBackGround(v2(RES
.X
-96, RES
.Y
-96), v2(80, 80));
1347 SetCursorPos(v2(-1, -1));
1352 void game::LookHandler (v2 CursorPos
) {
1353 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1356 if (GetSeeWholeMapCheatMode()) {
1357 OldMemory
= Square
->GetMemorizedDescription();
1358 if (IsInWilderness()) GetWorldMap()->GetWSquare(CursorPos
)->UpdateMemorizedDescription(true);
1359 else GetCurrentLevel()->GetLSquare(CursorPos
)->UpdateMemorizedDescription(true);
1363 if (Square
->HasBeenSeen() || GetSeeWholeMapCheatMode()) {
1364 if (!IsInWilderness() && !Square
->CanBeSeenByPlayer() && GetCurrentLevel()->GetLSquare(CursorPos
)->CanBeFeltByPlayer())
1365 Msg
= CONST_S("You feel here ");
1366 else if (Square
->CanBeSeenByPlayer(true) || GetSeeWholeMapCheatMode())
1367 Msg
= CONST_S("You see here ");
1369 Msg
= CONST_S("You remember here ");
1370 Msg
<< Square
->GetMemorizedDescription() << '.';
1371 if (!IsInWilderness() && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) {
1372 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1373 LSquare
->DisplaySmokeInfo(Msg
);
1374 if (LSquare
->HasEngravings() && LSquare
->IsTransparent()) {
1375 if (LSquare
->EngravingsCanBeReadByPlayer() || GetSeeWholeMapCheatMode()) LSquare
->DisplayEngravedInfo(Msg
);
1376 else Msg
<< " Something has been engraved here.";
1379 } else Msg
= CONST_S("You have never been here.");
1380 character
*Character
= Square
->GetCharacter();
1381 if (Character
&& (Character
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Character
->DisplayInfo(Msg
);
1382 if (!(RAND()%10000) && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Msg
<< " You see here a frog eating a magnolia.";
1383 ADD_MESSAGE("%s", Msg
.CStr());
1384 if (GetSeeWholeMapCheatMode()) Square
->SetMemorizedDescription(OldMemory
);
1388 truth
game::AnimationController () {
1389 DrawEverythingNoBlit(true);
1394 void game::LoadGlobalValueMap (TextInput
&fl
) {
1396 fl
.setGetVarCB(game::ldrGetVar
);
1397 for (fl
.ReadWord(word
, false); !fl
.Eof(); fl
.ReadWord(word
, false)) {
1398 if (word
== "Include") {
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, "loading: %s\n", word.CStr());
1402 TextInputFile
incf(game::GetGameDir()+"script/"+word
, &game::GetGlobalValueMap());
1403 LoadGlobalValueMap(incf
);
1406 if (word
== "Message") {
1407 word
= fl
.ReadWord();
1408 if (fl
.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1409 fprintf(stderr
, "MESSAGE: %s\n", word
.CStr());
1412 if (word
!= "#") ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1413 fl
.ReadWord(word
, true);
1414 if (word
== "enum" || word
== "bitenum") {
1415 truth isBit
= word
== "bitenum";
1417 if (fl
.ReadWord() != "{") ABORT("'{' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1421 fl
.ReadWord(word
, true);
1422 if (word
== "}") break;
1427 fl
.ReadWord(word
, true);
1430 // set current index
1431 idx
= fl
.ReadNumber();
1433 if (word
!= "," && word
!= ";" && word
!= "}") ABORT("',' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1434 if (word
== "}") done
= true;
1436 if (idName
.GetSize() > 0) {
1438 if (isBit
) i
= 1<<i
;
1439 GlobalValueMap
.insert(std::make_pair(idName
, i
));
1444 int ch
= fl
.GetChar();
1445 if (ch
!= EOF
&& ch
!= ';') fl
.UngetChar(ch
);
1446 //if (fl.ReadWord() != ";") ABORT("';' expected in file %s at line %d!", fl.GetFileName().CStr(), fl.TokenLine());
1449 if (word
== "define") {
1451 sLong v
= fl
.ReadNumber();
1452 GlobalValueMap
.insert(std::make_pair(word
, v
));
1455 ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1460 truth
game::HasGlobalValue (cfestring
&name
) {
1461 auto it
= GlobalValueMap
.find(name
);
1462 return (it
!= GlobalValueMap
.end());
1466 sLong
game::FindGlobalValue (cfestring
&name
, sLong defval
, truth
* found
) {
1467 auto it
= GlobalValueMap
.find(name
);
1468 if (it
!= GlobalValueMap
.end()) {
1469 if (found
) *found
= true;
1472 if (found
) *found
= false;
1478 sLong
game::FindGlobalValue (cfestring
&name
, truth
* found
) {
1479 return FindGlobalValue(name
, -1, found
);
1483 // this will fail if there is no such constant
1484 //TODO: cache values
1485 sLong
game::GetGlobalConst (cfestring
&name
) {
1486 auto it
= GlobalValueMap
.find(name
);
1487 if (it
== GlobalValueMap
.end()) ABORT("Global constant '%s' not found!", name
.CStr());
1492 void game::InitGlobalValueMap () {
1493 TextInputFile
SaveFile(GetGameDir()+"script/define.dat", &GlobalValueMap
);
1494 LoadGlobalValueMap(SaveFile
);
1495 { /* additional files */
1496 for (int f
= 0; f
<= 99; f
++) {
1498 sprintf(bnum
, "script/define_%02d.dat", f
);
1499 festring fn
= game::GetGameDir();
1501 if (inputfile::fileExists(fn
)) return;
1502 TextInputFile
ifl(fn
, &game::GetGlobalValueMap(), false);
1504 LoadGlobalValueMap(ifl
);
1512 void game::TextScreen (cfestring
&Text
, v2 Displacement
, col16 Color
, truth GKey
, truth Fade
, bitmapeditor BitmapEditor
) {
1513 globalwindowhandler::DisableControlLoops();
1514 iosystem::TextScreen(Text
, Displacement
, Color
, GKey
, Fade
, BitmapEditor
);
1515 globalwindowhandler::EnableControlLoops();
1519 /* ... all the keys that are acceptable
1520 DefaultAnswer = REQUIRES_ANSWER if this question requires an answer
1521 Not surprisingly KeyNumber is the number of keys at ...
1523 int game::KeyQuestion (cfestring
&Message
, int DefaultAnswer
, int KeyNumber
, ...) {
1527 va_start(Arguments
, KeyNumber
);
1528 for (int c
= 0; c
< KeyNumber
; ++c
) {
1529 if (c
> 255) ABORT("Too many keys in `game::KeyQuestion()`");
1530 Key
[c
] = va_arg(Arguments
, int);
1533 DrawEverythingNoBlit();
1534 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Message
.CStr());
1535 auto cursave
= graphics::getCursorState(16+Message
.rawLength()*8, 8+7, true);
1539 for (int c
= 0; c
< KeyNumber
; ++c
) {
1545 if (!Return
&& DefaultAnswer
!= REQUIRES_ANSWER
) Return
= DefaultAnswer
;
1547 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1552 v2
game::LookKeyHandler (v2 CursorPos
, int Key
) {
1553 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1556 if (!IsInWilderness()) {
1557 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1558 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1559 stack
*Stack
= LSquare
->GetStack();
1560 if (LSquare
->IsTransparent() && Stack
->GetVisibleItems(Player
))
1561 Stack
->DrawContents(Player
, "Items here", NO_SELECT
|(GetSeeWholeMapCheatMode() ? 0 : NO_SPECIAL_INFO
));
1563 ADD_MESSAGE("You see no items here.");
1564 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1568 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1569 character
*Char
= Square
->GetCharacter();
1570 if (Char
&& (Char
->CanBeSeenByPlayer() || Char
->IsPlayer() || GetSeeWholeMapCheatMode()))
1573 ADD_MESSAGE("You see no one here.");
1574 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1581 v2
game::NameKeyHandler (v2 CursorPos
, int Key
) {
1582 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
1583 if (Key
== 'n' || Key
== 'N') {
1584 character
*Char
= GetCurrentArea()->GetSquare(CursorPos
)->GetCharacter();
1585 if (Char
&& Char
->CanBeSeenByPlayer()) Char
->TryToName();
1586 else ADD_MESSAGE("You don't see anyone here to name.");
1592 void game::End (festring DeathMessage
, truth Permanently
, truth AndGoToMenu
) {
1593 if (!Permanently
|| WizardModeIsReallyActive()) game::Save();
1595 globalwindowhandler::DeInstallControlLoop(AnimationController
);
1596 SetIsRunning(false);
1597 if (Permanently
|| !WizardModeIsReallyActive()) RemoveSaves(Permanently
);
1598 if (Permanently
&& !WizardModeIsReallyActive()) {
1600 if (HScore
.LastAddFailed()) {
1601 iosystem::TextScreen(CONST_S("You didn't manage to get onto the high score list.\n\n\n\n")+GetPlayerName()+", "+DeathMessage
+"\nRIP");
1607 /* This prevents monster movement etc. after death. */
1608 throw quitrequest();
1613 int game::CalculateRoughDirection (v2 Vector
) {
1614 if (!Vector
.X
&& !Vector
.Y
) return YOURSELF
;
1615 double Angle
= femath::CalculateAngle(Vector
);
1616 if (Angle
< FPI
/ 8) return 4;
1617 else if (Angle
< 3*FPI
/8) return 7;
1618 else if (Angle
< 5*FPI
/8) return 6;
1619 else if (Angle
< 7*FPI
/8) return 5;
1620 else if (Angle
< 9*FPI
/8) return 3;
1621 else if (Angle
< 11*FPI
/8) return 0;
1622 else if (Angle
< 13*FPI
/8) return 1;
1623 else if (Angle
< 15*FPI
/8) return 2;
1628 int game::Menu (bitmap
*BackGround
, v2 Pos
, cfestring
&Topic
, cfestring
&sMS
, col16 Color
, cfestring
&SmallText1
, cfestring
&SmallText2
) {
1629 globalwindowhandler::DisableControlLoops();
1630 int Return
= iosystem::Menu(BackGround
, Pos
, Topic
, sMS
, Color
, SmallText1
, SmallText2
);
1631 globalwindowhandler::EnableControlLoops();
1636 void game::InitDangerMap () {
1638 pool::RegisterState(false);
1639 //fprintf(stderr, "game::InitDangerMap(): START\n");
1640 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
1642 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
1643 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1644 int ConfigSize
= Proto
->GetConfigSize();
1645 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
1646 if (!ConfigData
[c2
]->IsAbstract
) {
1647 int Config
= ConfigData
[c2
]->Config
;
1649 NextDangerIDType
= c1
;
1650 NextDangerIDConfigIndex
= c2
;
1653 character
*Char
= Proto
->Spawn(Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1654 double NakedDanger
= Char
->GetRelativeDanger(Player
, true);
1655 //fprintf(stderr, " game::InitDangerMap(): 00\n");
1657 //Char->SendToHell(); // k8: equipment
1658 Char
= Proto
->Spawn(Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1659 double EquippedDanger
= Char
->GetRelativeDanger(Player
, true);
1660 //fprintf(stderr, " game::InitDangerMap(): 01\n");
1662 //Char->SendToHell(); // k8: equipment
1663 DangerMap
[configid(c1
, Config
)] = dangerid(NakedDanger
, EquippedDanger
);
1667 pool::RegisterState(true);
1668 //fprintf(stderr, "game::InitDangerMap(): DONE\n");
1672 void game::CalculateNextDanger () {
1673 if (IsInWilderness() || !*CurrentLevel
->GetLevelScript()->GenerateMonsters()) return;
1674 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1675 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1676 const character::database
*DataBase
= ConfigData
[NextDangerIDConfigIndex
];
1677 dangermap::iterator DangerIterator
= DangerMap
.find(configid(NextDangerIDType
, DataBase
->Config
));
1678 team
*Team
= GetTeam(PLAYER_TEAM
);
1679 if (DataBase
&& DangerIterator
!= DangerMap
.end()) {
1680 //fprintf(stderr, "game::CalculateNextDanger(): START\n");
1681 pool::RegisterState(false);
1682 character
*Char
= Proto
->Spawn(DataBase
->Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1683 std::list
<character
*>::const_iterator i
;
1684 double DangerSum
= Player
->GetRelativeDanger(Char
, true);
1685 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
) {
1686 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10)) DangerSum
+= (*i
)->GetRelativeDanger(Char
, true)/4;
1688 double CurrentDanger
= 1/DangerSum
;
1689 double NakedDanger
= DangerIterator
->second
.NakedDanger
;
1690 //fprintf(stderr, " game::CalculateNextDanger(): 00\n");
1692 //Char->SendToHell(); // k8: equipment
1693 if (NakedDanger
> CurrentDanger
) DangerIterator
->second
.NakedDanger
= (NakedDanger
*9+CurrentDanger
)/10;
1694 Char
= Proto
->Spawn(DataBase
->Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1695 DangerSum
= Player
->GetRelativeDanger(Char
, true);
1696 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
) {
1697 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10)) DangerSum
+= (*i
)->GetRelativeDanger(Char
, true) / 4;
1699 CurrentDanger
= 1/DangerSum
;
1700 double EquippedDanger
= DangerIterator
->second
.EquippedDanger
;
1701 //fprintf(stderr, " game::CalculateNextDanger(): 01\n");
1703 //Char->SendToHell(); // k8: equipment
1704 pool::RegisterState(true);
1705 if (EquippedDanger
> CurrentDanger
) DangerIterator
->second
.EquippedDanger
= (EquippedDanger
*9+CurrentDanger
)/10;
1706 if (++NextDangerIDConfigIndex
< Proto
->GetConfigSize()) {
1707 //fprintf(stderr, "game::CalculateNextDanger(): EXIT0\n");
1711 if (++NextDangerIDType
>= protocontainer
<character
>::GetSize()) NextDangerIDType
= 1;
1712 Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1713 ConfigData
= Proto
->GetConfigData();
1714 int ConfigSize
= Proto
->GetConfigSize();
1715 for (int c
= 0; c
< ConfigSize
; ++c
) {
1716 if (!ConfigData
[c
]->IsAbstract
) {
1717 NextDangerIDConfigIndex
= c
;
1718 //fprintf(stderr, "game::CalculateNextDanger(): EXIT1\n");
1723 //fprintf(stderr, "game::CalculateNextDanger(): DONE\n");
1725 ABORT("It is dangerous to go ice fishing in the summer.");
1730 truth
game::TryTravel (int Dungeon
, int Area
, int EntryIndex
, truth AllowHostiles
, truth AlliesFollow
) {
1731 charactervector Group
;
1732 if (LeaveArea(Group
, AllowHostiles
, AlliesFollow
)) {
1733 CurrentDungeonIndex
= Dungeon
;
1734 EnterArea(Group
, Area
, EntryIndex
);
1741 truth
game::LeaveArea (charactervector
&Group
, truth AllowHostiles
, truth AlliesFollow
) {
1742 if (!IsInWilderness()) {
1743 if (AlliesFollow
&& !GetCurrentLevel()->CollectCreatures(Group
, Player
, AllowHostiles
)) return false;
1745 GetCurrentDungeon()->SaveLevel(SaveName(), CurrentLevelIndex
);
1748 GetWorldMap()->GetPlayerGroup().swap(Group
);
1755 /* Used always when the player enters an area. */
1756 void game::EnterArea (charactervector
&Group
, int Area
, int EntryIndex
) {
1757 if (Area
!= WORLD_MAP
) {
1759 SetIsInWilderness(false);
1760 CurrentLevelIndex
= Area
;
1761 truth New
= !PrepareRandomBone(Area
) && !GetCurrentDungeon()->PrepareLevel(Area
);
1762 igraph::CreateBackGround(*CurrentLevel
->GetLevelScript()->GetBackGroundType());
1763 GetCurrentArea()->SendNewDrawRequest();
1764 v2 Pos
= GetCurrentLevel()->GetEntryPos(Player
, EntryIndex
);
1766 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
1767 Player
->PutToOrNear(Pos
);
1768 } else SetPlayer(GetCurrentLevel()->GetLSquare(Pos
)->GetCharacter());
1770 for (c
= 0; c
< Group
.size(); ++c
) {
1771 v2 NPCPos
= GetCurrentLevel()->GetNearestFreeSquare(Group
[c
], Pos
);
1772 if (NPCPos
== ERROR_V2
) NPCPos
= GetCurrentLevel()->GetRandomSquare(Group
[c
]);
1773 Group
[c
]->PutTo(NPCPos
);
1775 GetCurrentLevel()->FiatLux();
1776 ctruth
*AutoReveal
= GetCurrentLevel()->GetLevelScript()->AutoReveal();
1777 if (New
&& AutoReveal
&& *AutoReveal
) GetCurrentLevel()->Reveal();
1779 SendLOSUpdateRequest();
1783 if (New
&& CurrentDungeonIndex
== ATTNAM
&& Area
== 0) {
1784 GlobalRainLiquid
= powder::Spawn(SNOW
);
1785 GlobalRainSpeed
= v2(-64, 128);
1786 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1789 if (New
&& CurrentDungeonIndex
== NEW_ATTNAM
&& Area
== 0) {
1790 GlobalRainLiquid
= liquid::Spawn(WATER
);
1791 GlobalRainSpeed
= v2(256, 512);
1792 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1795 if (New
&& CurrentDungeonIndex
== ELPURI_CAVE
&& Area
== OREE_LAIR
) {
1796 GlobalRainLiquid
= liquid::Spawn(BLOOD
);
1797 GlobalRainSpeed
= v2(256, 512);
1798 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1799 GlobalRainLiquid
->SetVolumeNoSignals(200);
1800 CurrentLevel
->EnableGlobalRain();
1803 if (New
&& CurrentDungeonIndex
== MUNTUO
&& Area
== 0) {
1804 GlobalRainLiquid
= liquid::Spawn(WATER
);
1805 GlobalRainSpeed
= v2(-64, 1024);
1806 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1810 GetCurrentLevel()->UpdateLOS();
1811 Player
->SignalStepFrom(0);
1813 for (c
= 0; c
< Group
.size(); ++c
) Group
[c
]->SignalStepFrom(0);
1815 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1817 igraph::CreateBackGround(GRAY_FRACTAL
);
1818 SetIsInWilderness(true);
1820 SetCurrentArea(WorldMap
);
1821 CurrentWSquareMap
= WorldMap
->GetMap();
1822 GetWorldMap()->GetPlayerGroup().swap(Group
);
1823 Player
->PutTo(GetWorldMap()->GetEntryPos(Player
, EntryIndex
));
1824 SendLOSUpdateRequest();
1826 GetWorldMap()->UpdateLOS();
1827 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1832 int game::CompareLightToInt (col24 L
, col24 Int
) {
1833 if ((L
& 0xFF0000) > Int
|| (L
& 0xFF00) > Int
|| (L
& 0xFF) > Int
) return 1;
1834 if ((L
& 0xFF0000) == Int
|| (L
& 0xFF00) == Int
|| (L
& 0xFF) == Int
) return 0;
1839 void game::SetStandardListAttributes (felist
&List
) {
1840 List
.SetPos(v2(26, 42));
1842 List
.SetFlags(DRAW_BACKGROUND_AFTERWARDS
);
1843 List
.SetUpKey(GetMoveCommandKey(KEY_UP_INDEX
));
1844 List
.SetDownKey(GetMoveCommandKey(KEY_DOWN_INDEX
));
1848 void game::InitPlayerAttributeAverage () {
1849 AveragePlayerArmStrengthExperience
1850 = AveragePlayerLegStrengthExperience
1851 = AveragePlayerDexterityExperience
1852 = AveragePlayerAgilityExperience
1855 if (!Player
->IsHumanoid()) return;
1857 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1860 arm
*RightArm
= Player
->GetRightArm();
1862 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1863 AveragePlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1864 AveragePlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1868 arm
*LeftArm
= Player
->GetLeftArm();
1870 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1871 AveragePlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1872 AveragePlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1876 leg
*RightLeg
= Player
->GetRightLeg();
1878 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1879 AveragePlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1880 AveragePlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1884 leg
*LeftLeg
= Player
->GetLeftLeg();
1886 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1887 AveragePlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1888 AveragePlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1893 AveragePlayerArmStrengthExperience
/= Arms
;
1894 AveragePlayerDexterityExperience
/= Arms
;
1898 AveragePlayerLegStrengthExperience
/= Legs
;
1899 AveragePlayerAgilityExperience
/= Legs
;
1904 void game::UpdatePlayerAttributeAverage () {
1905 if (!Player
->IsHumanoid()) return;
1907 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1908 double PlayerArmStrengthExperience
= 0;
1909 double PlayerLegStrengthExperience
= 0;
1910 double PlayerDexterityExperience
= 0;
1911 double PlayerAgilityExperience
= 0;
1914 arm
*RightArm
= Player
->GetRightArm();
1916 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1917 PlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1918 PlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1922 arm
*LeftArm
= Player
->GetLeftArm();
1924 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1925 PlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1926 PlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1930 leg
*RightLeg
= Player
->GetRightLeg();
1932 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1933 PlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1934 PlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1938 leg
*LeftLeg
= Player
->GetLeftLeg();
1940 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1941 PlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1942 PlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1947 AveragePlayerArmStrengthExperience
= (49 * AveragePlayerArmStrengthExperience
+ PlayerArmStrengthExperience
/ Arms
) / 50;
1948 AveragePlayerDexterityExperience
= (49 * AveragePlayerDexterityExperience
+ PlayerDexterityExperience
/ Arms
) / 50;
1952 AveragePlayerLegStrengthExperience
= (49 * AveragePlayerLegStrengthExperience
+ PlayerLegStrengthExperience
/ Legs
) / 50;
1953 AveragePlayerAgilityExperience
= (49 * AveragePlayerAgilityExperience
+ PlayerAgilityExperience
/ Legs
) / 50;
1958 void game::CallForAttention (v2 Pos
, int RangeSquare
) {
1959 for (int c
= 0; c
< GetTeams(); ++c
) {
1960 if (GetTeam(c
)->HasEnemy())
1961 for (std::list
<character
*>::const_iterator i
= GetTeam(c
)->GetMember().begin(); i
!= GetTeam(c
)->GetMember().end(); ++i
)
1962 if ((*i
)->IsEnabled()) {
1963 sLong ThisDistance
= HypotSquare(sLong((*i
)->GetPos().X
) - Pos
.X
, sLong((*i
)->GetPos().Y
) - Pos
.Y
);
1964 if (ThisDistance
<= RangeSquare
&& !(*i
)->IsGoingSomeWhere()) (*i
)->SetGoingTo(Pos
);
1970 outputfile
&operator << (outputfile
&SaveFile
, const homedata
*HomeData
) {
1973 SaveFile
<< HomeData
->Pos
<< HomeData
->Dungeon
<< HomeData
->Level
<< HomeData
->Room
;
1974 } else SaveFile
.Put(0);
1979 inputfile
&operator >> (inputfile
&SaveFile
, homedata
*&HomeData
) {
1980 if (SaveFile
.Get()) {
1981 HomeData
= new homedata
;
1982 SaveFile
>> HomeData
->Pos
>> HomeData
->Dungeon
>> HomeData
->Level
>> HomeData
->Room
;
1988 feuLong
game::CreateNewCharacterID (character
*NewChar
) {
1989 feuLong ID
= NextCharacterID
++;
1990 /*k8:??? if(CharacterIDMap.find(ID) != CharacterIDMap.end())
1991 int esko = esko = 2;*/
1992 CharacterIDMap
.insert(std::make_pair(ID
, NewChar
));
1997 feuLong
game::CreateNewItemID (item
*NewItem
) {
1998 feuLong ID
= NextItemID
++;
1999 /*k8:??? if(ItemIDMap.find(ID) != ItemIDMap.end())
2000 int esko = esko = 2;*/
2001 if (NewItem
) ItemIDMap
.insert(std::make_pair(ID
, NewItem
));
2006 feuLong
game::CreateNewTrapID (entity
*NewTrap
) {
2007 feuLong ID
= NextTrapID
++;
2008 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
2009 int esko = esko = 2;*/
2010 if (NewTrap
) TrapIDMap
.insert(std::make_pair(ID
, NewTrap
));
2015 character
*game::SearchCharacter (feuLong ID
) {
2016 characteridmap::iterator Iterator
= CharacterIDMap
.find(ID
);
2017 return (Iterator
!= CharacterIDMap
.end() ? Iterator
->second
: 0);
2021 item
*game::SearchItem (feuLong ID
) {
2022 itemidmap::iterator Iterator
= ItemIDMap
.find(ID
);
2023 return (Iterator
!= ItemIDMap
.end() ? Iterator
->second
: 0);
2027 entity
*game::SearchTrap (feuLong ID
) {
2028 trapidmap::iterator Iterator
= TrapIDMap
.find(ID
);
2029 return (Iterator
!= TrapIDMap
.end() ? Iterator
->second
: 0);
2033 outputfile
&operator << (outputfile
&SaveFile
, const configid
&Value
) {
2034 SaveFile
.Write(reinterpret_cast<cchar
*>(&Value
), sizeof(Value
));
2039 inputfile
&operator >> (inputfile
&SaveFile
, configid
&Value
) {
2040 SaveFile
.Read(reinterpret_cast<char*>(&Value
), sizeof(Value
));
2045 outputfile
&operator << (outputfile
&SaveFile
, const dangerid
&Value
) {
2046 SaveFile
<< Value
.NakedDanger
<< Value
.EquippedDanger
;
2051 inputfile
&operator >> (inputfile
&SaveFile
, dangerid
&Value
) {
2052 SaveFile
>> Value
.NakedDanger
>> Value
.EquippedDanger
;
2057 /* The program can only create directories to the deepness of one, no more... */
2058 festring
game::GetHomeDir () {
2060 Dir
<< getenv("HOME") << '/';
2065 festring
game::GetSavePath () {
2068 Dir
<< ivanconfig::GetMyDir() << "/save/";
2070 Dir
<< getenv("HOME") << "/.ivan-save/";
2076 festring
game::GetGameDir () {
2077 /*k8! return DATADIR "/ivan/"; */
2078 /*k8! return DATADIR "/"; */
2080 Dir
<< ivanconfig::GetMyDir() << "/";
2085 festring
game::GetBonePath () {
2086 /*k8! return LOCAL_STATE_DIR "/Bones/";*/
2089 Dir
<< ivanconfig::GetMyDir() << "/save/bones/";
2091 Dir
<< getenv("HOME") << "/.ivan-save/bones/";
2097 level
*game::GetLevel (int I
) {
2098 return GetCurrentDungeon()->GetLevel(I
);
2102 int game::GetLevels () {
2103 return GetCurrentDungeon()->GetLevels();
2107 void game::SignalDeath (ccharacter
*Ghost
, ccharacter
*Murderer
, festring DeathMsg
) {
2108 if (InWilderness
) DeathMsg
<< " in the world map";
2109 else DeathMsg
<< " in " << GetCurrentDungeon()->GetLevelDescription(CurrentLevelIndex
);
2110 massacremap
*MassacreMap
;
2112 ++MiscMassacreAmount
;
2113 MassacreMap
= &MiscMassacreMap
;
2114 } else if(Murderer
->IsPlayer()) {
2115 ++PlayerMassacreAmount
;
2116 MassacreMap
= &PlayerMassacreMap
;
2117 } else if(Murderer
->IsPet()) {
2118 ++PetMassacreAmount
;
2119 MassacreMap
= &PetMassacreMap
;
2121 ++MiscMassacreAmount
;
2122 MassacreMap
= &MiscMassacreMap
;
2125 massacreid
MI(Ghost
->GetType(), Ghost
->GetConfig(), Ghost
->GetAssignedName());
2126 massacremap::iterator i
= MassacreMap
->find(MI
);
2128 if (i
== MassacreMap
->end()) {
2129 i
= MassacreMap
->insert(std::make_pair(MI
, killdata(1, Ghost
->GetGenerationDanger()))).first
;
2130 i
->second
.Reason
.push_back(killreason(DeathMsg
, 1));
2133 i
->second
.DangerSum
+= Ghost
->GetGenerationDanger();
2134 std::vector
<killreason
>& Reason
= i
->second
.Reason
;
2136 for (c
= 0; c
< Reason
.size(); ++c
) {
2137 if (Reason
[c
].String
== DeathMsg
) {
2142 if (c
== Reason
.size()) Reason
.push_back(killreason(DeathMsg
, 1));
2147 void game::DisplayMassacreLists () {
2148 DisplayMassacreList(PlayerMassacreMap
, "directly by you.", PlayerMassacreAmount
);
2149 DisplayMassacreList(PetMassacreMap
, "by your allies.", PetMassacreAmount
);
2150 DisplayMassacreList(MiscMassacreMap
, "by some other reason.", MiscMassacreAmount
);
2154 struct massacresetentry
{
2155 bool operator < (const massacresetentry
&MSE
) const { return festring::IgnoreCaseCompare(Key
, MSE
.Key
); }
2158 std::vector
<festring
> Details
;
2163 void game::DisplayMassacreList (const massacremap
&MassacreMap
, cchar
*Reason
, sLong Amount
) {
2164 std::set
<massacresetentry
> MassacreSet
;
2165 festring FirstPronoun
;
2167 charactervector GraveYard
;
2169 for (massacremap::const_iterator i1
= MassacreMap
.begin(); i1
!= MassacreMap
.end(); ++i1
) {
2170 character
*Victim
= protocontainer
<character
>::GetProto(i1
->first
.Type
)->Spawn(i1
->first
.Config
);
2171 Victim
->SetAssignedName(i1
->first
.Name
);
2172 massacresetentry Entry
;
2173 GraveYard
.push_back(Victim
);
2174 Entry
.ImageKey
= AddToCharacterDrawVector(Victim
);
2175 if (i1
->second
.Amount
== 1) {
2176 Victim
->AddName(Entry
.Key
, UNARTICLED
);
2177 Victim
->AddName(Entry
.String
, INDEFINITE
);
2179 Victim
->AddName(Entry
.Key
, PLURAL
);
2180 Entry
.String
<< i1
->second
.Amount
<< ' ' << Entry
.Key
;
2183 FirstPronoun
= Victim
->GetSex() == UNDEFINED
? "it" : Victim
->GetSex() == MALE
? "he" : "she";
2186 const std::vector
<killreason
>& Reason
= i1
->second
.Reason
;
2187 std::vector
<festring
>& Details
= Entry
.Details
;
2188 if (Reason
.size() == 1) {
2190 if (Reason
[0].Amount
== 1) Begin
= "";
2191 else if(Reason
[0].Amount
== 2) Begin
= "both ";
2192 else Begin
= "all ";
2193 Details
.push_back(Begin
+ Reason
[0].String
);
2195 for (uInt c
= 0; c
< Reason
.size(); ++c
) Details
.push_back(CONST_S("")+Reason
[c
].Amount
+' '+Reason
[c
].String
);
2196 std::sort(Details
.begin(), Details
.end(), ignorecaseorderer());
2198 MassacreSet
.insert(Entry
);
2200 sLong Total
= PlayerMassacreAmount
+PetMassacreAmount
+MiscMassacreAmount
;
2202 if (Total
== 1) MainTopic
<< "One creature perished during your adventure.";
2203 else MainTopic
<< Total
<< " creatures perished during your adventure.";
2204 felist
List(MainTopic
);
2205 SetStandardListAttributes(List
);
2206 List
.SetPageLength(15);
2207 List
.AddFlags(SELECTABLE
);
2208 List
.SetEntryDrawer(CharacterEntryDrawer
);
2209 List
.AddDescription(CONST_S(""));
2211 if (Amount
!= Total
) {
2212 SideTopic
= CONST_S("The following ");
2213 if (Amount
== 1) SideTopic
<< "one was killed " << Reason
;
2214 else SideTopic
<< Amount
<< " were killed " << Reason
;
2217 FirstPronoun
.Capitalize();
2218 SideTopic
<< FirstPronoun
<< " was killed " << Reason
;
2219 } else SideTopic
<< "They were all killed " << Reason
;
2221 List
.AddDescription(SideTopic
);
2222 List
.AddDescription(CONST_S(""));
2223 List
.AddDescription("Choose a type of creatures to browse death details.");
2224 std::set
<massacresetentry
>::const_iterator i2
;
2225 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
) List
.AddEntry(i2
->String
, LIGHT_GRAY
, 0, i2
->ImageKey
);
2227 int Chosen
= List
.Draw();
2228 if (Chosen
& FELIST_ERROR_BIT
) break;
2229 felist
SubList(CONST_S("Massacre details"));
2230 SetStandardListAttributes(SubList
);
2231 SubList
.SetPageLength(20);
2233 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
, ++Counter
) {
2234 if (Counter
== Chosen
) {
2235 for (uInt c
= 0; c
< i2
->Details
.size(); ++c
) SubList
.AddEntry(i2
->Details
[c
], LIGHT_GRAY
);
2241 ClearCharacterDrawVector();
2242 for (uInt c
= 0; c
< GraveYard
.size(); ++c
) delete GraveYard
[c
];
2246 truth
game::MassacreListsEmpty () {
2247 return PlayerMassacreMap
.empty() && PetMassacreMap
.empty() && MiscMassacreMap
.empty();
2252 void game::SeeWholeMap () {
2253 if (SeeWholeMapCheatMode
< 2) ++SeeWholeMapCheatMode
; else SeeWholeMapCheatMode
= 0;
2254 GetCurrentArea()->SendNewDrawRequest();
2259 void game::CreateBone () {
2260 if (!WizardModeIsActive() && !IsInWilderness() && (RAND()&3) && GetCurrentLevel()->PreProcessForBone()) {
2263 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2264 BoneName
= GetBonePath()+"bon_"+CurrentDungeonIndex
+"_"+CurrentLevelIndex
+"_"+BoneIndex
;
2265 if (!inputfile::fileExists(BoneName
)) break;
2267 if (BoneIndex
!= 1000) {
2268 //festring BoneName = GetBonePath()+"bon"+CurrentDungeonIndex+CurrentLevelIndex+BoneIndex;
2269 fprintf(stderr
, "creating bone file: [%s]\n", BoneName
.CStr());
2270 outputfile
BoneFile(BoneName
, true);
2271 BoneFile
<< int(BONE_FILE_VERSION
) << PlayerName
<< CurrentLevel
;
2277 truth
game::PrepareRandomBone (int LevelIndex
) {
2278 if (/*k8:WizardModeIsActive() ||*/ GetCurrentDungeon()->IsGenerated(LevelIndex
) || !*GetCurrentDungeon()->GetLevelScript(LevelIndex
)->CanGenerateBone()) return false;
2281 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2282 BoneName
= GetBonePath()+"bon_"+CurrentDungeonIndex
+"_"+LevelIndex
+"_"+BoneIndex
;
2283 inputfile
BoneFile(BoneName
, false);
2284 if (BoneFile
.IsOpen() && !(RAND() & 7)) {
2285 if (ReadType(int, BoneFile
) != BONE_FILE_VERSION
) {
2287 remove(BoneName
.CStr());
2292 level
*NewLevel
= GetCurrentDungeon()->LoadLevel(BoneFile
, LevelIndex
);
2293 if (!NewLevel
->PostProcessForBone()) {
2295 GetBoneItemIDMap().clear();
2296 GetBoneCharacterIDMap().clear();
2299 NewLevel
->FinalProcessForBone();
2300 GetBoneItemIDMap().clear();
2301 GetBoneCharacterIDMap().clear();
2302 SetCurrentArea(NewLevel
);
2303 CurrentLevel
= NewLevel
;
2304 CurrentLSquareMap
= NewLevel
->GetMap();
2305 GetCurrentDungeon()->SetIsGenerated(LevelIndex
, true);
2306 if (Name
== PlayerName
) ADD_MESSAGE("This place is oddly familiar. Like you had been here in one of your past lives.");
2307 else ADD_MESSAGE("You smell the stench of death.");
2312 if (BoneIndex
!= 1000) {
2313 remove(BoneName
.CStr());
2320 double game::CalculateAverageDanger (const charactervector
&EnemyVector
, character
*Char
) {
2321 double DangerSum
= 0;
2323 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) {
2324 DangerSum
+= EnemyVector
[c
]->GetRelativeDanger(Char
, true);
2327 return DangerSum
/Enemies
;
2331 double game::CalculateAverageDangerOfAllNormalEnemies () {
2332 double DangerSum
= 0;
2334 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
2335 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
2336 const character::database
*const *ConfigData
= Proto
->GetConfigData();
2337 int ConfigSize
= Proto
->GetConfigSize();
2338 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
2339 if (!ConfigData
[c2
]->IsAbstract
&& !ConfigData
[c2
]->IsUnique
&& ConfigData
[c2
]->CanBeGenerated
) {
2340 DangerSum
+= DangerMap
.find(configid(c1
, ConfigData
[c2
]->Config
))->second
.EquippedDanger
;
2345 return DangerSum
/Enemies
;
2349 character
*game::CreateGhost () {
2350 double AverageDanger
= CalculateAverageDangerOfAllNormalEnemies();
2351 charactervector EnemyVector
;
2352 protosystem::CreateEveryNormalEnemy(EnemyVector
);
2353 ghost
*Ghost
= ghost::Spawn();
2354 Ghost
->SetTeam(GetTeam(MONSTER_TEAM
));
2355 Ghost
->SetGenerationDanger(CurrentLevel
->GetDifficulty());
2356 Ghost
->SetOwnerSoul(PlayerName
);
2357 Ghost
->SetIsActive(false);
2358 Ghost
->EditAllAttributes(-4);
2359 Player
->SetSoulID(Ghost
->GetID());
2360 while (CalculateAverageDanger(EnemyVector
, Ghost
) > AverageDanger
&& Ghost
->EditAllAttributes(1));
2361 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) delete EnemyVector
[c
];
2366 int game::GetMoveCommandKey (int I
) {
2367 if (!ivanconfig::GetUseAlternativeKeys()) return MoveNormalCommandKey
[I
];
2368 return MoveAbnormalCommandKey
[I
];
2372 sLong
game::GetScore () {
2374 massacremap::const_iterator i
;
2375 massacremap SumMap
= PlayerMassacreMap
;
2376 for (i
= PetMassacreMap
.begin(); i
!= PetMassacreMap
.end(); ++i
) {
2377 killdata
&KillData
= SumMap
[i
->first
];
2378 KillData
.Amount
+= i
->second
.Amount
;
2379 KillData
.DangerSum
+= i
->second
.DangerSum
;
2381 for (i
= SumMap
.begin(); i
!= SumMap
.end(); ++i
) {
2382 character
*Char
= protocontainer
<character
>::GetProto(i
->first
.Type
)->Spawn(i
->first
.Config
);
2383 int SumOfAttributes
= Char
->GetSumOfAttributes();
2384 Counter
+= sqrt(i
->second
.DangerSum
/ DEFAULT_GENERATION_DANGER
) * SumOfAttributes
* SumOfAttributes
;
2387 return sLong(0.01*Counter
);
2391 /* Only works if New Attnam is loaded */
2392 truth
game::TweraifIsFree () {
2393 for (std::list
<character
*>::const_iterator i
= GetTeam(COLONIST_TEAM
)->GetMember().begin(); i
!= GetTeam(COLONIST_TEAM
)->GetMember().end(); ++i
)
2394 if ((*i
)->IsEnabled()) return false;
2399 // returns true if date is christmaseve or day
2400 truth
game::IsXMas () {
2401 time_t Time
= time(0);
2402 struct tm
*TM
= localtime(&Time
);
2403 return (TM
->tm_mon
== 11 && (TM
->tm_mday
== 24 || TM
->tm_mday
== 25));
2407 int game::AddToItemDrawVector (const itemvector
&What
) {
2408 ItemDrawVector
.push_back(What
);
2409 return ItemDrawVector
.size()-1;
2413 v2 ItemDisplacement
[3][3] = {
2414 { v2(0, 0), ERROR_V2
, ERROR_V2
},
2415 { v2(-2, -2), v2(2, 2), ERROR_V2
},
2416 { v2(-4, -4), v2(0, 0), v2(4, 4) }
2420 void game::ItemEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2425 { TILE_SIZE
, TILE_SIZE
},
2426 { NORMAL_LUMINANCE
},
2430 itemvector ItemVector
= ItemDrawVector
[I
];
2431 int Amount
= Min
<int>(ItemVector
.size(), 3);
2432 for (int c
= 0; c
< Amount
; ++c
) {
2433 v2 Displacement
= ItemDisplacement
[Amount
-1][c
];
2434 if (!ItemVector
[0]->HasNormalPictureDirection()) Displacement
.X
= -Displacement
.X
;
2435 B
.Dest
= Pos
+Displacement
;
2436 if (ItemVector
[c
]->AllowAlphaEverywhere()) B
.CustomData
|= ALLOW_ALPHA
;
2437 ItemVector
[c
]->Draw(B
);
2438 B
.CustomData
&= ~ALLOW_ALPHA
;
2440 if (ItemVector
.size() > 3) {
2443 B
.Dest
= ItemVector
[0]->HasNormalPictureDirection() ? Pos
+v2(11, -2) : Pos
+v2(-2, -2);
2445 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2450 int game::AddToCharacterDrawVector (character
*What
) {
2451 CharacterDrawVector
.push_back(What
);
2452 return CharacterDrawVector
.size()-1;
2456 void game::CharacterEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2457 if (CharacterDrawVector
[I
]) {
2462 { TILE_SIZE
, TILE_SIZE
},
2463 { NORMAL_LUMINANCE
},
2465 ALLOW_ANIMATE
|ALLOW_ALPHA
2467 CharacterDrawVector
[I
]->DrawBodyParts(B
);
2472 void game::GodEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2477 { TILE_SIZE
, TILE_SIZE
},
2482 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2486 character
*game::GetSumo () {
2487 return GetCurrentLevel()->GetLSquare(SUMO_ROOM_POS
)->GetRoom()->GetMaster();
2491 truth
game::TryToEnterSumoArena () {
2492 character
*Sumo
= GetSumo();
2493 if (!Sumo
|| !Sumo
->IsEnabled() || Sumo
->GetRelation(Player
) == HOSTILE
|| !Player
->CanBeSeenBy(Sumo
)) return true;
2494 if (TweraifIsFree()) {
2495 ADD_MESSAGE("\"You started this stupid revolution, after which I've been constantly hungry. Get lost!\"");
2498 if (PlayerIsSumoChampion()) {
2499 ADD_MESSAGE("\"I don't really enjoy losing, especially many times to the same guy. Go away.\"");
2502 if (Player
->IsPolymorphed()) {
2503 ADD_MESSAGE("\"Don't try to cheat. Come back when you're normal again.\"");
2506 if (Player
->GetHungerState() < SATIATED
) {
2507 ADD_MESSAGE("\"Your figure is too slender for this sport. Eat a lot more and come back.\"");
2510 if (Player
->GetHungerState() < BLOATED
) {
2511 ADD_MESSAGE("\"You're still somewhat too thin. Eat some more and we'll compete.\"");
2514 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.\"");
2515 if (!TruthQuestion("Do you want to challenge him? [y/N]")) return false;
2517 SumoWrestling
= true;
2518 character
*MirrorPlayer
= Player
->Duplicate(IGNORE_PROHIBITIONS
);
2519 character
*MirrorSumo
= Sumo
->Duplicate(IGNORE_PROHIBITIONS
);
2520 SetPlayer(MirrorPlayer
);
2521 charactervector Spectators
;
2522 if (Player
->GetTeam()->GetRelation(GetTeam(TOURIST_GUIDE_TEAM
)) != HOSTILE
&&
2523 Player
->GetTeam()->GetRelation(GetTeam(TOURIST_TEAM
)) != HOSTILE
) {
2524 GetTeam(TOURIST_GUIDE_TEAM
)->MoveMembersTo(Spectators
);
2525 GetTeam(TOURIST_TEAM
)->MoveMembersTo(Spectators
);
2527 GetCurrentDungeon()->SaveLevel(SaveName(), 0);
2528 charactervector test
;
2529 EnterArea(test
, 1, STAIRS_UP
);
2530 MirrorSumo
->PutTo(SUMO_ARENA_POS
+v2(6, 5));
2531 MirrorSumo
->ChangeTeam(GetTeam(SUMO_TEAM
));
2532 GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->SetMasterID(MirrorSumo
->GetID());
2533 for (uInt c
= 0; c
< Spectators
.size(); ++c
) Spectators
[c
]->PutToOrNear(SUMO_ARENA_POS
+ v2(6, 10));
2534 throw areachangerequest();
2539 truth
game::TryToExitSumoArena () {
2540 if (GetTeam(PLAYER_TEAM
)->GetRelation(GetTeam(NEW_ATTNAM_TEAM
)) == HOSTILE
) return true;
2542 charactervector CVector
;
2543 if (IsSumoWrestling()) {
2544 if (TruthQuestion("Do you really wish to give up? [y/N]")) return EndSumoWrestling(LOST
);
2549 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2550 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2551 std::vector
<character
*> test
;
2552 EnterArea(test
, 0, STAIRS_DOWN
);
2553 Player
->GetStackUnder()->AddItems(IVector
);
2554 if (!IVector
.empty()) {
2555 character
*Sumo
= GetSumo();
2556 if (Sumo
&& Sumo
->GetRelation(Player
) != HOSTILE
&& Player
->CanBeSeenBy(Sumo
)) ADD_MESSAGE("\"Don't leave anything there, please.\"");
2558 v2 PlayerPos
= Player
->GetPos();
2559 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2560 throw areachangerequest();
2566 truth
game::EndSumoWrestling (int Result
) {
2568 msgsystem::LeaveBigMessageMode();
2569 if (Result
== LOST
) AskForKeyPress("You lose. [press any key to continue]");
2570 else if (Result
== WON
) AskForKeyPress("You win! [press any key to continue]");
2571 else if (Result
== DISQUALIFIED
) AskForKeyPress("You are disqualified! [press any key to continue]");
2572 character
*Sumo
= GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->GetMaster();
2573 /* We'll make a throw soon so deletes are allowed */
2582 charactervector CVector
;
2583 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2584 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2585 charactervector test
;
2586 EnterArea(test
, 0, STAIRS_DOWN
);
2587 SumoWrestling
= false;
2588 Player
->GetStackUnder()->AddItems(IVector
);
2589 v2 PlayerPos
= Player
->GetPos();
2590 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2591 if (Result
== LOST
) ADD_MESSAGE("\"I hope you've learned your lesson now!\"");
2592 else if (Result
== DISQUALIFIED
) ADD_MESSAGE("\"Don't do that again or I'll be really angry!\"");
2594 PlayerSumoChampion
= true;
2595 character
*Sumo
= GetSumo();
2596 festring Msg
= Sumo
->GetName(DEFINITE
)+" seems humbler than before. \"Darn. You bested me.\n";
2597 Msg
<< "Here's a little something as a reward\", " << Sumo
->GetPersonalPronoun() << " says and hands you a belt of levitation.\n\"";
2598 (belt::Spawn(BELT_OF_LEVITATION
))->MoveTo(Player
->GetStack());
2599 Msg
<< "Allow me to also teach you a few nasty martial art tricks the years have taught me.\"";
2600 Player
->GetCWeaponSkill(UNARMED
)->AddHit(100000);
2601 Player
->GetCWeaponSkill(KICK
)->AddHit(100000);
2602 character
*Imperialist
= GetCurrentLevel()->GetLSquare(5, 5)->GetRoom()->GetMaster();
2603 if (Imperialist
&& Imperialist
->GetRelation(Player
) != HOSTILE
) {
2604 v2 Pos
= Player
->GetPos()+v2(0, 1);
2605 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
2606 Imperialist
->Remove();
2607 Imperialist
->PutTo(Pos
);
2608 Msg
<< "\n\nSuddenly you notice " << Imperialist
->GetName(DEFINITE
) << " has also entered.\n"
2609 "\"I see we have a promising fighter among us. I had already heard of your\n"
2610 "adventures outside the village, but hardly could I believe that one day you\n"
2611 "would defeat even the mighty Huang Ming Pong! A hero such as you is bound\n"
2612 "to become world famous, and can earn a fortune if wealthy sponsors are behind\n"
2613 "him. May I therefore propose a mutually profitable contract: I'll give you this\n"
2614 "nice shirt with my company's ad, and you'll wear it as you journey bravely to\n"
2615 "the unknown and fight epic battles against the limitless minions of evil. I'll\n"
2616 "reward you well when you return, depending on how much you have used it.\"";
2617 Player
->GetStack()->AddItem(decosadshirt::Spawn());
2620 GetCurrentArea()->SendNewDrawRequest();
2623 Player
->EditNP(-25000);
2624 Player
->CheckStarvationDeath(CONST_S("exhausted after controlling a mirror image for too long"));
2625 throw areachangerequest();
2630 rain
*game::ConstructGlobalRain () {
2631 return new rain(GlobalRainLiquid
, static_cast<lsquare
*>(GetSquareInLoad()), GlobalRainSpeed
, MONSTER_TEAM
, false);
2635 v2
game::GetSunLightDirectionVector () {
2636 int Index
= Tick
% 48000 / 1000;
2637 /* Should have the same sign as sin(PI * Index / 24) and XTable[Index] /
2638 YTable[Index] should equal roughly -tan(PI * Index / 24). Also, vector
2639 (XTable[Index], YTable[Index]) + P should not be a valid position of
2640 any possible level L for any P belonging to L. */
2641 static int XTable
[48] = {
2642 0, 1000, 1000, 1000, 1000, 1000,
2643 1000, 1303, 1732, 2414, 3732, 7596,
2644 1000, 7596, 3732, 2414, 1732, 1303,
2645 1000, 1000, 1000, 1000, 1000, 1000,
2646 0, -1000, -1000, -1000, -1000, -1000,
2647 -1000, -1303, -1732, -2414, -3732, -7596,
2648 -1000, -7596, -3732, -2414, -1732, -1303,
2649 -1000, -1000, -1000, -1000, -1000, -1000 };
2650 /* Should have the same sign as -cos(PI * Index / 24) */
2651 static int YTable
[48] = { -1000, -7596, -3732, -2414, -1732, -1303,
2652 -1000, -1000, -1000, -1000, -1000, -1000,
2653 0, 1000, 1000, 1000, 1000, 1000,
2654 1000, 1303, 1732, 2414, 3732, 7596,
2655 1000, 7596, 3732, 2414, 1732, 1303,
2656 1000, 1000, 1000, 1000, 1000, 1000,
2657 0, -1000, -1000, -1000, -1000, -1000,
2658 -1000, -1303, -1732, -2414, -3732, -7596 };
2659 return v2(XTable
[Index
], YTable
[Index
]);
2663 int game::CalculateMinimumEmitationRadius (col24 E
) {
2664 int MaxElement
= Max(GetRed24(E
), GetGreen24(E
), GetBlue24(E
));
2665 return int(sqrt(double(MaxElement
<< 7) / LIGHT_BORDER
- 120.));
2669 feuLong
game::IncreaseSquarePartEmitationTicks () {
2670 if ((SquarePartEmitationTick
+= 2) == 0x100) {
2671 CurrentLevel
->InitSquarePartEmitationTicks();
2672 SquarePartEmitationTick
= 2;
2674 return SquarePartEmitationTick
;
2678 bool game::Wish (character
*Wisher
, cchar
*MsgSingle
, cchar
*MsgPair
, bool canAbort
) {
2680 festring oldDef
= DefaultWish
;
2681 festring Temp
= DefaultQuestion(CONST_S("What do you want to wish for?"), DefaultWish
);
2682 if (DefaultWish
== "nothing" && canAbort
) {
2683 DefaultWish
= oldDef
;
2686 if (Temp
== "socm") Temp
= "scroll of change material";
2687 else if (Temp
== "soc") Temp
= "scroll of charging";
2688 else if (Temp
== "sodm") Temp
= "scroll of detect material";
2689 else if (Temp
== "soea") Temp
= "scroll of enchant armor";
2690 else if (Temp
== "soew") Temp
= "scroll of enchant weapon";
2691 else if (Temp
== "sof") Temp
= "scroll of fireballs";
2692 else if (Temp
== "sogc") Temp
= "scroll of golem creation";
2693 else if (Temp
== "sohm") Temp
= "scroll of harden material";
2694 else if (Temp
== "sor") Temp
= "scroll of repair";
2695 else if (Temp
== "sot") Temp
= "scroll of taming";
2696 else if (Temp
== "sotp") Temp
= "scroll of teleportation";
2697 else if (Temp
== "sow") Temp
= "scroll of wishing";
2698 else if (Temp
== "vodka") Temp
= "bottle full of vodka";
2699 else if (Temp
== "troll blood") Temp
= "bottle full of troll blood";
2700 item
*TempItem
= protosystem::CreateItem(Temp
, Wisher
->IsPlayer());
2702 Wisher
->GetStack()->AddItem(TempItem
);
2703 TempItem
->SpecialGenerationHandler();
2704 if (TempItem
->HandleInPairs()) ADD_MESSAGE(MsgPair
, TempItem
->CHAR_NAME(PLURAL
));
2705 else ADD_MESSAGE(MsgSingle
, TempItem
->CHAR_NAME(INDEFINITE
));
2712 festring
game::DefaultQuestion (festring Topic
, festring
&Default
, stringkeyhandler KeyHandler
) {
2713 festring ShortDefault
= Default
;
2714 if (Default
.GetSize() > 29) {
2715 ShortDefault
.Resize(27);
2716 ShortDefault
= ShortDefault
<< CONST_S("...");
2718 if (!Default
.IsEmpty()) Topic
<< " [" << ShortDefault
<< ']';
2719 festring Answer
= StringQuestion(Topic
, WHITE
, 0, 80, false, KeyHandler
);
2720 if (Answer
.IsEmpty()) Answer
= Default
;
2721 return Default
= Answer
;
2725 void game::GetTime (ivantime
&Time
) {
2726 Time
.Hour
= 12 + Tick
/ 2000;
2727 Time
.Day
= Time
.Hour
/ 24 + 1;
2729 Time
.Min
= Tick
% 2000 * 60 / 2000;
2733 truth
NameOrderer (character
*C1
, character
*C2
) {
2734 return festring::IgnoreCaseCompare(C1
->GetName(UNARTICLED
), C2
->GetName(UNARTICLED
));
2738 truth
game::PolymorphControlKeyHandler (int Key
, festring
&String
) {
2740 felist
List(CONST_S("List of known creatures and their intelligence requirements"));
2741 SetStandardListAttributes(List
);
2742 List
.SetPageLength(15);
2743 List
.AddFlags(SELECTABLE
);
2744 protosystem::CreateEverySeenCharacter(CharacterDrawVector
);
2745 std::sort(CharacterDrawVector
.begin(), CharacterDrawVector
.end(), NameOrderer
);
2746 List
.SetEntryDrawer(CharacterEntryDrawer
);
2747 std::vector
<festring
> StringVector
;
2749 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) {
2750 character
*Char
= CharacterDrawVector
[c
];
2751 if (Char
->CanBeWished()) {
2753 Char
->AddName(Entry
, UNARTICLED
);
2754 StringVector
.push_back(Entry
);
2755 int Req
= Char
->GetPolymorphIntelligenceRequirement();
2756 if (Char
->IsSameAs(Player
) || (Player
->GetPolymorphBackup() && Player
->GetPolymorphBackup()->IsSameAs(Char
))) Req
= 0;
2757 Entry
<< " (" << Req
<< ')';
2758 int Int
= Player
->GetAttribute(INTELLIGENCE
);
2759 List
.AddEntry(Entry
, Req
> Int
? RED
: LIGHT_GRAY
, 0, c
);
2762 int Chosen
= List
.Draw();
2763 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) delete CharacterDrawVector
[c
];
2764 if (!(Chosen
& FELIST_ERROR_BIT
)) String
= StringVector
[Chosen
];
2765 CharacterDrawVector
.clear();
2772 outputfile
&operator << (outputfile
&SaveFile
, const killdata
&Value
) {
2773 SaveFile
<< Value
.Amount
<< Value
.DangerSum
<< Value
.Reason
;
2778 inputfile
&operator >> (inputfile
&SaveFile
, killdata
&Value
) {
2779 SaveFile
>> Value
.Amount
>> Value
.DangerSum
>> Value
.Reason
;
2784 outputfile
&operator << (outputfile
&SaveFile
, const killreason
&Value
) {
2785 SaveFile
<< Value
.Amount
<< Value
.String
;
2790 inputfile
&operator >> (inputfile
&SaveFile
, killreason
&Value
) {
2791 SaveFile
>> Value
.Amount
>> Value
.String
;
2796 truth
DistanceOrderer (character
*C1
, character
*C2
) {
2797 v2 PlayerPos
= PLAYER
->GetPos();
2798 v2 Pos1
= C1
->GetPos();
2799 v2 Pos2
= C2
->GetPos();
2800 int D1
= Max(abs(Pos1
.X
- PlayerPos
.X
), abs(Pos1
.Y
- PlayerPos
.Y
));
2801 int D2
= Max(abs(Pos2
.X
- PlayerPos
.X
), abs(Pos2
.Y
- PlayerPos
.Y
));
2802 if (D1
!= D2
) return D1
< D2
;
2803 if (Pos1
.Y
!= Pos2
.Y
) return Pos1
.Y
< Pos2
.Y
;
2804 return Pos1
.X
< Pos2
.X
;
2808 truth
game::FillPetVector (cchar
*Verb
) {
2810 team
*Team
= GetTeam(PLAYER_TEAM
);
2811 for (std::list
<character
*>::const_iterator i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
)
2812 if ((*i
)->IsEnabled() && !(*i
)->IsPlayer() && (*i
)->CanBeSeenByPlayer()) PetVector
.push_back(*i
);
2813 if (PetVector
.empty()) {
2814 ADD_MESSAGE("You don't detect any friends to %s.", Verb
);
2817 std::sort(PetVector
.begin(), PetVector
.end(), DistanceOrderer
);
2818 LastPetUnderCursor
= PetVector
[0];
2823 truth
game::CommandQuestion () {
2824 if (!FillPetVector("command")) return false;
2826 if (PetVector
.size() == 1) Char
= PetVector
[0];
2828 v2 Pos
= PetVector
[0]->GetPos();
2829 Pos
= PositionQuestion(CONST_S("Whom do you wish to command? [direction keys/'+'/'-'/'a'll/space/esc]"), Pos
, &PetHandler
, &CommandKeyHandler
);
2830 if (Pos
== ERROR_V2
) return false;
2831 if (Pos
== ABORT_V2
) return true;
2832 Char
= CurrentArea
->GetSquare(Pos
)->GetCharacter();
2833 if (!Char
|| !Char
->CanBeSeenByPlayer()) {
2834 ADD_MESSAGE("You don't see anyone here to command.");
2837 if (Char
->IsPlayer()) {
2838 ADD_MESSAGE("You do that all the time.");
2841 if (!Char
->IsPet()) {
2842 ADD_MESSAGE("%s refuses to be commanded by you.", Char
->CHAR_NAME(DEFINITE
));
2846 return Char
->IssuePetCommands();
2850 void game::NameQuestion () {
2851 if (!FillPetVector("name")) return;
2852 if (PetVector
.size() == 1) PetVector
[0]->TryToName();
2853 else PositionQuestion(CONST_S("Who do you want to name? [direction keys/'+'/'-'/'n'ame/esc]"), PetVector
[0]->GetPos(), &PetHandler
, &NameKeyHandler
);
2857 void game::PetHandler (v2 CursorPos
) {
2858 character
*Char
= CurrentArea
->GetSquare(CursorPos
)->GetCharacter();
2859 if (Char
&& Char
->CanBeSeenByPlayer() && Char
->IsPet() && !Char
->IsPlayer()) CursorData
= RED_CURSOR
|CURSOR_TARGET
;
2860 else CursorData
= RED_CURSOR
;
2861 if (Char
&& !Char
->IsPlayer() && Char
->IsPet()) LastPetUnderCursor
= Char
;
2865 v2
game::CommandKeyHandler (v2 CursorPos
, int Key
) {
2866 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
2867 if (Key
== 'a' || Key
== 'A') return CommandAll() ? ABORT_V2
: ERROR_V2
;
2872 truth
game::SelectPet (int Key
) {
2874 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2875 if (PetVector
[c
] == LastPetUnderCursor
) {
2876 if (++c
== PetVector
.size()) c
= 0;
2877 LastPetUnderCursor
= PetVector
[c
];
2881 } else if (Key
== '-') {
2882 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2883 if (PetVector
[c
] == LastPetUnderCursor
) {
2884 if (!c
) c
= PetVector
.size();
2885 LastPetUnderCursor
= PetVector
[--c
];
2894 void game::CommandScreen (cfestring
&Topic
, feuLong PossibleFlags
, feuLong ConstantFlags
, feuLong
&VaryFlags
, feuLong
&Flags
) {
2895 static cchar
*CommandDescription
[COMMAND_FLAGS
] = {
2897 "Flee from enemies",
2898 "Don't change your equipment",
2899 "Don't consume anything valuable"
2902 SetStandardListAttributes(List
);
2903 List
.AddFlags(SELECTABLE
);
2904 List
.AddDescription(CONST_S(""));
2905 List
.AddDescription(CONST_S("Command Active?"));
2908 for (c
= 0; c
< COMMAND_FLAGS
; ++c
) {
2909 if (1 << c
& PossibleFlags
) {
2910 truth Changeable
= !(1 << c
& ConstantFlags
);
2913 Entry
= CommandDescription
[c
];
2916 Entry
<< " " << CommandDescription
[c
];
2919 if (1 << c
& VaryFlags
) Entry
<< "varies"; else Entry
<< (1 << c
& Flags
? "yes" : "no");
2920 List
.AddEntry(Entry
, Changeable
? LIGHT_GRAY
: DARK_GRAY
, 0, NO_IMAGE
, Changeable
);
2923 int Chosen
= List
.Draw();
2924 if (Chosen
& FELIST_ERROR_BIT
) return;
2925 for (c
= 0, i
= 0; c
< COMMAND_FLAGS
; ++c
) {
2926 if (1 << c
& PossibleFlags
&& !(1 << c
& ConstantFlags
) && i
++ == Chosen
) {
2927 if (1 << c
& VaryFlags
) {
2928 VaryFlags
&= ~(1 << c
);
2930 } else Flags
^= 1 << c
;
2935 DrawEverythingNoBlit();
2940 truth
game::CommandAll () {
2941 feuLong PossibleFlags
= 0, ConstantFlags
= ALL_COMMAND_FLAGS
, VaryFlags
= 0, OldFlags
= 0;
2943 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2944 ConstantFlags
&= PetVector
[c1
]->GetConstantCommandFlags();
2945 feuLong C
= PetVector
[c1
]->GetCommandFlags();
2946 feuLong ThisPossible
= PetVector
[c1
]->GetPossibleCommandFlags();
2947 for (c2
= 0; c2
< COMMAND_FLAGS
; ++c2
)
2948 if (1 << c2
& PossibleFlags
& ThisPossible
&& (1 << c2
& C
) != (1 << c2
& OldFlags
)) VaryFlags
|= 1 << c2
;
2949 PossibleFlags
|= ThisPossible
;
2950 OldFlags
|= C
& ThisPossible
;
2952 if (!PossibleFlags
) {
2953 ADD_MESSAGE("Not a single creature in your visible team can be commanded.");
2956 feuLong NewFlags
= OldFlags
;
2957 CommandScreen(CONST_S("Issue commands to whole visible team"), PossibleFlags
, ConstantFlags
, VaryFlags
, NewFlags
);
2958 truth Change
= false;
2959 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2960 character
*Char
= PetVector
[c1
];
2961 if (!Char
->IsConscious()) continue;
2962 feuLong OldC
= Char
->GetCommandFlags();
2963 feuLong ConstC
= Char
->GetConstantCommandFlags();
2964 feuLong ThisC
= (NewFlags
& Char
->GetPossibleCommandFlags() & ~(ConstC
|VaryFlags
)) | (OldC
& (ConstC
|VaryFlags
));
2965 if (ThisC
!= OldC
) Change
= true;
2966 Char
->SetCommandFlags(ThisC
);
2968 if (!Change
) return false;
2969 Player
->EditAP(-500);
2970 Player
->EditExperience(CHARISMA
, 50, 1 << 7);
2975 col16
game::GetAttributeColor (int I
) {
2976 int Delta
= GetTick()-LastAttributeChangeTick
[I
];
2977 if (OldAttribute
[I
] == NewAttribute
[I
] || Delta
>= 510) return WHITE
;
2978 if (OldAttribute
[I
] < NewAttribute
[I
]) return MakeRGB16(255, 255, Delta
>> 1);
2979 return MakeRGB16(255, Delta
>> 1, Delta
>> 1);
2983 void game::UpdateAttributeMemory () {
2984 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
2985 int A
= Player
->GetAttribute(c
);
2986 if (A
!= NewAttribute
[c
]) {
2987 OldAttribute
[c
] = NewAttribute
[c
];
2988 NewAttribute
[c
] = A
;
2989 LastAttributeChangeTick
[c
] = GetTick();
2995 void game::InitAttributeMemory () {
2996 for (int c
= 0; c
< ATTRIBUTES
; ++c
) OldAttribute
[c
] = NewAttribute
[c
] = Player
->GetAttribute(c
);
3000 void game::TeleportHandler (v2 CursorPos
) {
3001 if ((CursorPos
-Player
->GetPos()).GetLengthSquare() > Player
->GetTeleportRangeSquare())
3002 CursorData
= BLUE_CURSOR
|CURSOR_TARGET
;
3004 CursorData
= RED_CURSOR
|CURSOR_TARGET
;
3008 double game::GetGameSituationDanger () {
3009 double SituationDanger
= 0;
3010 character
*Player
= GetPlayer();
3011 truth PlayerStuck
= Player
->IsStuck();
3012 v2 PlayerPos
= Player
->GetPos();
3013 character
*TruePlayer
= Player
;
3014 if (PlayerStuck
) (Player
= Player
->Duplicate(IGNORE_PROHIBITIONS
))->ChangeTeam(0);
3015 for (int c1
= 0; c1
< GetTeams(); ++c1
)
3016 if (GetTeam(c1
)->GetRelation(GetTeam(PLAYER_TEAM
)) == HOSTILE
)
3017 for (std::list
<character
*>::const_iterator i1
= GetTeam(c1
)->GetMember().begin(); i1
!= GetTeam(c1
)->GetMember().end(); ++i1
) {
3018 character
*Enemy
= *i1
;
3019 if (Enemy
->IsEnabled() && Enemy
->CanAttack() && (Enemy
->CanMove() || Enemy
->GetPos().IsAdjacent(PlayerPos
))) {
3020 truth EnemyStuck
= Enemy
->IsStuck();
3021 v2 EnemyPos
= Enemy
->GetPos();
3022 truth Sees
= TruePlayer
->CanBeSeenBy(Enemy
);
3023 character
*TrueEnemy
= Enemy
;
3024 if (EnemyStuck
) Enemy
= Enemy
->Duplicate(IGNORE_PROHIBITIONS
);
3025 double PlayerTeamDanger
= 1/Enemy
->GetSituationDanger(Player
, EnemyPos
, PlayerPos
, Sees
);
3026 for (int c2
= 0; c2
< GetTeams(); ++c2
)
3027 if (GetTeam(c2
)->GetRelation(GetTeam(c1
)) == HOSTILE
)
3028 for (std::list
<character
*>::const_iterator i2
= GetTeam(c2
)->GetMember().begin(); i2
!= GetTeam(c2
)->GetMember().end(); ++i2
) {
3029 character
*Friend
= *i2
;
3030 if (Friend
->IsEnabled() && !Friend
->IsPlayer() && Friend
->CanAttack() && (Friend
->CanMove() || Friend
->GetPos().IsAdjacent(EnemyPos
))) {
3031 v2 FriendPos
= Friend
->GetPos();
3032 truth Sees
= TrueEnemy
->CanBeSeenBy(Friend
);
3033 if (Friend
->IsStuck()) {
3034 Friend
= Friend
->Duplicate(IGNORE_PROHIBITIONS
);
3035 PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
) * .2;
3037 } else PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
);
3041 PlayerTeamDanger
*= 5;
3044 SituationDanger
+= 1 / PlayerTeamDanger
;
3047 Player
->ModifySituationDanger(SituationDanger
);
3049 SituationDanger
*= 2;
3052 return SituationDanger
;
3056 sLong
game::GetTimeSpent () {
3057 return time::TimeAdd(time::TimeDifference(time(0),LastLoad
), TimePlayedBeforeLastLoad
);
3061 outputfile
&operator << (outputfile
&SaveFile
, const massacreid
&MI
) {
3062 SaveFile
<< MI
.Type
<< MI
.Config
<< MI
.Name
;
3067 inputfile
&operator >> (inputfile
&SaveFile
, massacreid
&MI
) {
3068 SaveFile
>> MI
.Type
>> MI
.Config
>> MI
.Name
;
3073 truth
game::PlayerIsRunning () {
3074 return PlayerRunning
&& Player
->CanMove();
3078 void game::AddSpecialCursor (v2 Pos
, int Data
) {
3079 SpecialCursorPos
.push_back(Pos
);
3080 SpecialCursorData
.push_back(Data
);
3084 void game::RemoveSpecialCursors () {
3085 SpecialCursorPos
.clear();
3086 SpecialCursorData
.clear();
3090 void game::LearnAbout (god
*Who
) {
3091 Who
->SetIsKnown(true);
3092 /* slightly slow, but doesn't matter since this is run so rarely */
3093 if (PlayerKnowsAllGods() && !game::PlayerHasReceivedAllGodsKnownBonus
) {
3094 GetPlayer()->ApplyAllGodsKnownBonus();
3095 game::PlayerHasReceivedAllGodsKnownBonus
= true;
3100 truth
game::PlayerKnowsAllGods () {
3101 for (int c
= 1; c
<= GODS
; ++c
) if (!GetGod(c
)->IsKnown()) return false;
3106 void game::AdjustRelationsToAllGods (int Amount
) {
3107 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->AdjustRelation(Amount
);
3111 void game::SetRelationsToAllGods (int Amount
) {
3112 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->SetRelation(Amount
);
3116 void game::ShowDeathSmiley (bitmap
*Buffer
, truth
) {
3117 static blitdata B
= {
3120 { (RES
.X
>> 1) - 24, RES
.Y
* 4 / 7 - 24 },
3126 int Tick
= globalwindowhandler::UpdateTick();
3127 if (((Tick
>> 1) & 31) == 1) B
.Src
.X
= 48;
3128 else if (((Tick
>> 1) & 31) == 2) B
.Src
.X
= 96;
3131 igraph::GetSmileyGraphic()->NormalBlit(B
);
3132 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
3136 static int doListSelector (felist
&list
, int defsel
, int cnt
) {
3137 game::SetStandardListAttributes(list
);
3138 list
.AddFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3139 if (defsel
> 0) list
.SetSelected(defsel
);
3140 uInt sel
= list
.Draw();
3142 list
.RemoveFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3143 if (sel
& FELIST_ERROR_BIT
) return -1;
3144 if (sel
>= (uInt
)cnt
) return -1;
3149 int game::ListSelector (int defsel
, const cfestring title
, ...) {
3152 va_start(items
, title
);
3156 const char *s
= va_arg(items
, const char *);
3158 list
.AddEntry(s
, LIGHT_GRAY
);
3162 return doListSelector(list
, defsel
, cnt
);
3166 int game::ListSelectorArray (int defsel
, cfestring
&title
, const char *items
[]) {
3170 if (!items
[cnt
]) break;
3171 list
.AddEntry(items
[cnt
], LIGHT_GRAY
);
3174 return doListSelector(list
, defsel
, cnt
);
3178 void game::ClearEventData () {
3186 // '.': string or number
3189 // '*': collect all args
3190 int game::ParseFuncArgs (cfestring
&types
, std::vector
<FuncArg
> &args
, TextInput
*fl
, truth noterm
) {
3194 if (!fl
) fl
= mFEStack
.top();
3196 for (unsigned int f
= 0; f
< types
.GetSize(); f
++) {
3199 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3200 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3203 n
= fl
->ReadNumber(0xFF, true);
3204 args
.push_back(FuncArg(n
));
3208 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3209 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3210 fl
->ReadWord(s
, true);
3211 if (s
== ";") return args
.size();
3212 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3217 s
= fl
->ReadWord(true);
3218 args
.push_back(FuncArg(s
));
3221 if (f
== types
.GetSize()-1) {
3223 fl
->ReadWord(s
, true);
3224 if (s
!= ";") ABORT("';' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3227 fl
->ReadWord(s
, true);
3228 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3235 truth
game::GetWord (festring
&w
) {
3237 TextInput
*fl
= mFEStack
.top();
3238 fl
->ReadWord(w
, false);
3239 if (w
== "" && fl
->Eof()) {
3242 if (mFEStack
.empty()) return false;
3245 if (w
== "Include") {
3246 fl
->ReadWord(w
, true);
3247 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3248 w
= game::GetGameDir()+"script/"+w
;
3249 TextInput
*fl
= new TextInputFile(w
, &game::GetGlobalValueMap(), true);
3250 fl
->setGetVarCB(game::ldrGetVar
);
3254 if (w
== "Message") {
3255 fl
->ReadWord(w
, true);
3256 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TokenLine());
3257 fprintf(stderr
, "MESSAGE: %s\n", w
.CStr());
3265 void game::SkipBlock (truth brcEaten
) {
3268 mFEStack
.top()->ReadWord(w
, true);
3269 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3273 mFEStack
.top()->ReadWord(w
, true);
3274 if (w
== "{") cnt
++;
3275 else if (w
== "}") {
3276 if (--cnt
< 1) break;
3282 truth
game::DoOnEvent (truth brcEaten
, truth AllowScript
) {
3283 // do; only funcalls for now
3284 truth eaten
= AllowScript
? true : false;
3287 mFEStack
.top()->ReadWord(w
, true);
3288 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3292 if (AllowScript
) break;
3293 ABORT("Unexpected end of file %s!", mFEStack
.top()->GetFileName().CStr());
3295 //fprintf(stderr, " :[%s]\n", w.CStr());
3297 if (AllowScript
) ABORT("Unexpected '}' in AllowScript file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3300 if (w
== ";") continue;
3302 mFEStack
.top()->ReadWord(w
, true);
3303 if (mFEStack
.top()->ReadWord(true) != "=") ABORT("'=' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3304 //fprintf(stderr, "setvar: %s\n", w.CStr());
3306 sLong n
= mFEStack
.top()->ReadNumber(true);
3308 if (mChar
) mChar
->SetMoney(n
);
3311 if (w
== "result") {
3312 mResult
= mFEStack
.top()->ReadNumber(true);
3315 ABORT("Unknown var [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3317 //mFEStack.top()->ReadWord(w, true);
3318 std::vector
<FuncArg
> args
;
3319 //fprintf(stderr, "funcall: %s\n", w.CStr());
3321 if (w == "AddItem") {
3322 ParseFuncArgs("s", args);
3323 item *it = protosystem::CreateItem(args[0].sval, false); // no output
3325 mChar->GetStack()->AddItem(it);
3326 it->SpecialGenerationHandler();
3328 ADD_MESSAGE("ERROR: no item with id \"%s\"", args[0].sval.CStr());
3333 if (w
== "ActivateTombOfXinroch") {
3334 //GetArea()->SendNewDrawRequest();
3335 //game::ActivateWizardMode();
3336 //ADD_MESSAGE("Wizard mode activated.");
3338 //game::SetXinrochTombStoryState(1);
3339 //game::SaveWorldMap();
3341 maptotombofxinroch
*MapToTombOfXinroch
= maptotombofxinroch::Spawn();
3342 MapToTombOfXinroch
->MoveTo(PLAYER
->GetStack());
3343 //MapToTombOfXinroch->FinishReading(PLAYER);
3346 if (!game::IsInWilderness()) game::LoadWorldMap();
3347 v2 XinrochTombPos = game::GetWorldMap()->GetEntryPos(0, XINROCH_TOMB);
3348 game::GetWorldMap()->GetWSquare(XinrochTombPos)->ChangeOWTerrain(xinrochtomb::Spawn());
3349 game::GetWorldMap()->RevealEnvironment(XinrochTombPos, 1);
3351 //game::SaveWorldMap();
3353 (belt::Spawn(BELT_OF_LEVITATION
))->MoveTo(Player
->GetStack());
3356 if (!game::IsInWilderness()) game::LoadWorldMap();
3357 v2 ElpuriCavePos = game::GetWorldMap()->GetEntryPos(0, ELPURI_CAVE);
3358 game::GetWorldMap()->GetWSquare(ElpuriCavePos)->ChangeOWTerrain(elpuricave::Spawn());
3359 game::GetWorldMap()->RevealEnvironment(ElpuriCavePos, 1);
3360 if (game::IsInWilderness()) game::GetWorldMap()->SendNewDrawRequest(); else game::SaveWorldMap();
3364 //game::Save(game::GetAutoSaveFileName());
3367 if (w
== "SetMoney") {
3368 ParseFuncArgs("n", args
);
3369 sLong n
= args
[0].ival
;
3371 if (mChar
) mChar
->SetMoney(n
);
3374 if (w
== "EditMoney") {
3375 ParseFuncArgs("n", args
);
3376 sLong n
= args
[0].ival
;
3377 if (mChar
) mChar
->EditMoney(n
);
3380 if (w
== "AddMessage") {
3381 ParseFuncArgs("*", args
);
3383 for (uInt f
= 0; f
< args
.size(); f
++) {
3384 const FuncArg
&a
= args
[f
];
3385 if (a
.type
== FARG_STRING
) s
<< a
.sval
; else s
<< a
.ival
;
3387 ADD_MESSAGE("%s", s
.CStr());
3390 if (w
== "EatThisEvent") {
3391 if (AllowScript
) ABORT("'EatThisEvent' forbidden in AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3395 if (w
== "Disallow") {
3396 if (!AllowScript
) ABORT("'Disallow' forbidden in not-AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3400 ABORT("Unknown function [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3401 //if (mFEStack.top()->ReadWord() != ";") ABORT("';' expected in file %s line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TokenLine());
3403 //ABORT("Invalid term in file %s at line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TokenLine());
3405 //fprintf(stderr, "------------\n");
3410 //TODO: cache event scripts
3411 truth
game::RunOnEvent (cfestring
&ename
) {
3412 static std::vector
<festring
> scriptFiles
;
3413 static truth cached
= false;
3416 character
*old
= mChar
;
3420 for (int fno
= 99; fno
>= -1; fno
--) {
3422 cfname
<< game::GetGameDir() << "script/onevent";
3425 sprintf(bnum
, "_%02d", fno
);
3429 if (!inputfile::fileExists(cfname
)) continue;
3430 TextInput
*ifl
= new TextInputFile(cfname
, &game::GetGlobalValueMap(), false);
3431 if (!ifl
->IsOpen()) {
3435 scriptFiles
.push_back(cfname
);
3436 ifl
->setGetVarCB(game::ldrGetVar
);
3440 for (unsigned int f
= 0; f
< scriptFiles
.size(); ++f
) {
3441 festring cfname
= scriptFiles
[f
];
3442 TextInput
*ifl
= new TextInputFile(cfname
, &game::GetGlobalValueMap(), false);
3443 if (!ifl
->IsOpen()) {
3447 ifl
->setGetVarCB(game::ldrGetVar
);
3453 while (GetWord(w
)) {
3454 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3455 mFEStack
.top()->ReadWord(w
, true);
3456 truth doIt
= (w
==ename
);
3458 res
= DoOnEvent(false);
3469 truth
game::RunOnEventStr (cfestring
&ename
, cfestring
&str
) {
3471 if (str
.GetSize() < 1) return false;
3472 //fprintf(stderr, "=============\n%s=============\n", str.CStr());
3473 TextInput
*ifl
= new MemTextFile("<memory>", str
, &game::GetGlobalValueMap());
3474 ifl
->setGetVarCB(game::ldrGetVar
);
3477 //fprintf(stderr, "=============\n", str.CStr());
3478 //fprintf(stderr, "event: [%s]\n", ename.CStr());
3479 //fprintf(stderr, "---\n%s---\n", str.CStr());
3480 while (GetWord(w
)) {
3481 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TokenLine());
3482 mFEStack
.top()->ReadWord(w
, true);
3483 //fprintf(stderr, "on: [%s]\n", w.CStr());
3484 truth doIt
= (w
==ename
);
3486 //fprintf(stderr, " do it\n");
3487 res
= DoOnEvent(false);
3490 //fprintf(stderr, " skip it\n");
3498 truth
game::RunOnCharEvent (character
*who
, cfestring
&ename
) {
3500 if (!who
) return false;
3501 character
*old
= mChar
;
3503 res
= RunOnEventStr(ename
, who
->mOnEvents
);
3504 if (!res
) res
= RunOnEventStr(ename
, who
->GetProtoType()->mOnEvents
);
3510 truth
game::RunOnItemEvent (item
*what
, cfestring
&ename
) {
3512 if (!what
) return false;
3515 res
= RunOnEventStr(ename
, what
->mOnEvents
);
3516 if (!res
) res
= RunOnEventStr(ename
, what
->GetProtoType()->mOnEvents
);
3522 festring
game::ldrGetVar (TextInput
*fl
, cfestring
&name
) {
3523 //fprintf(stderr, "GETVAR: [%s]\n", name.CStr());
3524 if (name
== "player_name") {
3525 return game::GetPlayerName();
3527 if (name
== "money") {
3529 if (!mChar
) return "0";
3530 res
<< mChar
->GetMoney();
3533 if (name
== "name") {
3534 if (!mChar
) return "";
3535 return mChar
->GetAssignedName();
3537 if (name
== "team") {
3539 if (!mChar
) return "";
3540 res
<< mChar
->GetTeam()->GetID();
3543 if (name
== "friendly") {
3545 if (!mChar
|| !PLAYER
|| mChar
->GetRelation(PLAYER
) != HOSTILE
) return "tan";
3548 if (name
== "hostile") {
3550 if (!mChar
|| !PLAYER
) return "";
3551 if (mChar
->GetRelation(PLAYER
) == HOSTILE
) return "tan";
3554 if (name
== "has_item") {
3555 std::vector
<FuncArg
> args
;
3556 ParseFuncArgs("s", args
, fl
, true);
3560 festring s
= args
[0].sval
;
3562 //fprintf(stderr, "looking for [%s]\n", s.CStr());
3563 PLAYER
->GetStack()->FillItemVector(items
);
3564 for (unsigned int f
= 0; f
< items
.size(); ++f
) {
3565 for (uInt c
= 0; c
< items
[f
]->GetDataBase()->Alias
.Size
; ++c
) {
3566 //fprintf(stderr, "%u:%u: [%s]\n", f, c, items[f]->GetDataBase()->Alias[c].CStr());
3567 if (s
.CompareIgnoreCase(items
[f
]->GetDataBase()->Alias
[c
]) == 0) {
3568 //fprintf(stderr, " FOUND!\n");
3573 //fprintf(stderr, "checking equipment...\n");
3574 for (int f
= 0; f
< PLAYER
->GetEquipments(); ++f
) {
3575 item
*it
= PLAYER
->GetEquipment(f
);
3578 for (uInt c
= 0; c
< it
->GetDataBase()->Alias
.Size
; ++c
) {
3579 //fprintf(stderr, "%u:%u: [%s]\n", f, c, it->GetDataBase()->Alias[c].CStr());
3580 if (s
.CompareIgnoreCase(it
->GetDataBase()->Alias
[c
]) == 0) {
3581 //fprintf(stderr, " FOUND!\n");
3590 //if (name == "type") return mVarType;
3591 ABORT("unknown variable: %s", name
.CStr());
3596 truth
game::CheckDropLeftover (item
*i
) {
3597 if (i
->IsBottle() && !ivanconfig::GetAutoDropBottles()) return false;
3598 if (i
->IsCan() && !ivanconfig::GetAutoDropCans()) return false;
3599 if (!ivanconfig::GetAutoDropLeftOvers()) return false;
3604 truth
game::RunAllowScriptStr (cfestring
&str
) {
3606 if (str
.GetSize() < 1) return true;
3607 //fprintf(stderr, "====\n%s\n====\n", str.CStr());
3608 TextInput
*ifl
= new MemTextFile("<memory>", str
, &game::GetGlobalValueMap());
3609 ifl
->setGetVarCB(game::ldrGetVar
);
3611 res
= DoOnEvent(true, true);
3612 //fprintf(stderr, "mFEStack: %u\n", mFEStack.size());