3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
8 * See LICENSING which should be included
9 * along with this file for more details
17 #include <sys/types.h>
53 #define SAVE_FILE_VERSION 119 // Increment this if changes make savefiles incompatible
54 #define BONE_FILE_VERSION 106 // Increment this if changes make bonefiles incompatible
56 #define SAVE_FILE_VERSION 124 // Increment this if changes make savefiles incompatible
57 #define BONE_FILE_VERSION 109 // Increment this if changes make bonefiles incompatible
64 std::stack
<inputfile
*> game::mFEStack
;
65 character
*game::mChar
= 0;
66 ccharacter
*game::mActor
= 0;
67 ccharacter
*game::mSecondActor
= 0;
68 item
*game::mItem
= 0;
69 int game::mResult
= 0;
72 int game::CurrentLevelIndex
;
73 truth
game::InWilderness
= false;
74 worldmap
* game::WorldMap
;
75 area
* game::AreaInLoad
;
76 square
* game::SquareInLoad
;
77 dungeon
** game::Dungeon
;
78 int game::CurrentDungeonIndex
;
79 feuLong
game::NextCharacterID
= 1;
80 feuLong
game::NextItemID
= 1;
81 feuLong
game::NextTrapID
= 1;
83 feuLong
game::LOSTick
;
84 v2
game::CursorPos(-1, -1);
86 truth
game::Generating
= false;
87 double game::AveragePlayerArmStrengthExperience
;
88 double game::AveragePlayerLegStrengthExperience
;
89 double game::AveragePlayerDexterityExperience
;
90 double game::AveragePlayerAgilityExperience
;
95 int game::MondedrPass
;
96 int game::RingOfThieves
;
99 int game::LoricatusHammer
;
102 massacremap
game::PlayerMassacreMap
;
103 massacremap
game::PetMassacreMap
;
104 massacremap
game::MiscMassacreMap
;
105 sLong
game::PlayerMassacreAmount
= 0;
106 sLong
game::PetMassacreAmount
= 0;
107 sLong
game::MiscMassacreAmount
= 0;
108 boneidmap
game::BoneItemIDMap
;
109 boneidmap
game::BoneCharacterIDMap
;
110 truth
game::TooGreatDangerFoundTruth
;
111 itemvectorvector
game::ItemDrawVector
;
112 charactervector
game::CharacterDrawVector
;
113 truth
game::SumoWrestling
;
114 liquid
* game::GlobalRainLiquid
;
115 v2
game::GlobalRainSpeed
;
116 sLong
game::GlobalRainTimeModifier
;
117 truth
game::PlayerSumoChampion
;
118 truth
game::PlayerSolicitusChampion
;
119 feuLong
game::SquarePartEmitationTick
= 0;
121 truth
game::PlayerRunning
;
122 character
* game::LastPetUnderCursor
;
123 charactervector
game::PetVector
;
124 double game::DangerFound
;
125 int game::OldAttribute
[ATTRIBUTES
];
126 int game::NewAttribute
[ATTRIBUTES
];
127 int game::LastAttributeChangeTick
[ATTRIBUTES
];
128 int game::NecroCounter
;
129 int game::CursorData
;
130 truth
game::CausePanicFlag
;
132 truth
game::Loading
= false;
133 truth
game::JumpToPlayerBe
= false;
134 truth
game::InGetCommand
= false;
135 character
*game::Petrus
= 0;
136 time_t game::TimePlayedBeforeLastLoad
;
137 time_t game::LastLoad
;
138 time_t game::GameBegan
;
139 truth
game::PlayerHasReceivedAllGodsKnownBonus
;
141 festring
game::AutoSaveFileName
= game::GetSaveDir() + "AutoSave";
142 cchar
*const game::Alignment
[] = { "L++", "L+", "L", "L-", "N+", "N=", "N-", "C+", "C", "C-", "C--" };
145 cint
game::MoveNormalCommandKey
[] = { KEY_HOME
, KEY_UP
, KEY_PAGE_UP
, KEY_LEFT
, KEY_RIGHT
, KEY_END
, KEY_DOWN
, KEY_PAGE_DOWN
, '.' };
146 int game::MoveAbnormalCommandKey
[] = { '7','8','9','u','o','j','k','l','.' };
148 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) };
149 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) };
150 cv2
game::BasicMoveVector
[] = { v2(-1, 0), v2(1, 0), v2(0, -1), v2(0, 1) };
151 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) };
152 cint
game::LargeMoveDirection
[] = { 0, 1, 1, 2, 3, 4, 3, 4, 5, 6, 6, 7, 8, 8, 8, 8 };
154 truth
game::LOSUpdateRequested
= false;
155 uChar
***game::LuxTable
= 0;
157 character
*game::Player
;
158 v2
game::Camera(0, 0);
160 gamescript
*game::GameScript
= 0;
161 valuemap
game::GlobalValueMap
;
162 dangermap
game::DangerMap
;
163 int game::NextDangerIDType
;
164 int game::NextDangerIDConfigIndex
;
165 characteridmap
game::CharacterIDMap
;
166 itemidmap
game::ItemIDMap
;
167 trapidmap
game::TrapIDMap
;
168 truth
game::PlayerHurtByExplosion
;
169 area
*game::CurrentArea
;
170 level
*game::CurrentLevel
;
171 wsquare
***game::CurrentWSquareMap
;
172 lsquare
***game::CurrentLSquareMap
;
173 festring
game::DefaultPolymorphTo
;
174 festring
game::DefaultSummonMonster
;
175 festring
game::DefaultWish
;
176 festring
game::DefaultChangeMaterial
;
177 festring
game::DefaultDetectMaterial
;
178 truth
game::WizardMode
;
179 int game::SeeWholeMapCheatMode
;
180 truth
game::GoThroughWallsCheat
;
181 int game::QuestMonstersFound
;
182 bitmap
*game::BusyAnimationCache
[32];
183 festring
game::PlayerName
;
184 feuLong
game::EquipmentMemory
[MAX_EQUIPMENT_SLOTS
];
185 olterrain
*game::MonsterPortal
;
186 std::vector
<v2
> game::SpecialCursorPos
;
187 std::vector
<int> game::SpecialCursorData
;
188 cbitmap
*game::EnterImage
;
189 v2
game::EnterTextDisplacement
;
192 char game::GetAbnormalMoveKey (int idx
) {
193 if (idx
< 0 || idx
> 8) return 0;
194 return MoveAbnormalCommandKey
[idx
];
198 void game::SetAbnormalMoveKey (int idx
, char ch
) {
199 if (idx
>= 0 && idx
<= 8) MoveAbnormalCommandKey
[idx
] = ch
;
203 void game::AddCharacterID (character
*Char
, feuLong ID
) {
204 /*k8:??? if (CharacterIDMap.find(ID) != CharacterIDMap.end())
205 int esko = esko = 2;*/
206 CharacterIDMap
.insert(std::make_pair(ID
, Char
));
210 void game::RemoveCharacterID (feuLong ID
) {
211 /*k8:??? if (CharacterIDMap.find(ID) == CharacterIDMap.end())
212 int esko = esko = 2;*/
213 CharacterIDMap
.erase(CharacterIDMap
.find(ID
));
217 void game::AddItemID (item
*Item
, feuLong ID
) {
218 /*k8:??? if (ItemIDMap.find(ID) != ItemIDMap.end())
219 int esko = esko = 2;*/
220 ItemIDMap
.insert(std::make_pair(ID
, Item
));
224 void game::RemoveItemID (feuLong ID
) {
225 /*k8:??? if(ID && ItemIDMap.find(ID) == ItemIDMap.end())
226 int esko = esko = 2;*/
227 if (ID
) ItemIDMap
.erase(ItemIDMap
.find(ID
));
231 void game::UpdateItemID (item
*Item
, feuLong ID
) {
232 /*k8:??? if(ItemIDMap.find(ID) == ItemIDMap.end())
233 int esko = esko = 2;*/
234 ItemIDMap
.find(ID
)->second
= Item
;
238 void game::AddTrapID (entity
*Trap
, feuLong ID
) {
239 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
240 int esko = esko = 2;*/
241 if (ID
) TrapIDMap
.insert(std::make_pair(ID
, Trap
));
245 void game::RemoveTrapID (feuLong ID
) {
246 /*k8:??? if(ID && TrapIDMap.find(ID) == TrapIDMap.end())
247 int esko = esko = 2;*/
248 if (ID
) TrapIDMap
.erase(TrapIDMap
.find(ID
));
252 void game::UpdateTrapID (entity
*Trap
, feuLong ID
) {
253 /*k8:??? if(TrapIDMap.find(ID) == TrapIDMap.end())
254 int esko = esko = 2;*/
255 TrapIDMap
.find(ID
)->second
= Trap
;
259 const dangermap
&game::GetDangerMap () { return DangerMap
; }
260 void game::ClearItemDrawVector () { ItemDrawVector
.clear(); }
261 void game::ClearCharacterDrawVector () { CharacterDrawVector
.clear(); }
264 void game::InitScript () {
265 inputfile
ScriptFile(GetGameDir()+"Script/dungeon.dat", &GlobalValueMap
);
266 GameScript
= new gamescript
;
267 GameScript
->ReadFrom(ScriptFile
);
268 { /* additional dungeon files */
269 for (int f
= 0; f
<= 99; f
++) {
271 sprintf(bnum
, "Script/dungeon_%02d.dat", f
);
272 inputfile
ifl(game::GetGameDir()+bnum
, &game::GetGlobalValueMap(), false);
274 //fprintf(stderr, "loading: %s\n", bnum+7);
275 GameScript
->ReadFrom(ifl
);
280 GameScript
->RandomizeLevels();
284 truth
game::Init (cfestring
&Name
) {
285 if (Name
.IsEmpty()) {
286 if (ivanconfig::GetDefaultName().IsEmpty()) {
288 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;
290 PlayerName
= ivanconfig::GetDefaultName();
297 mkdir(GetSaveDir().CStr(), S_IRWXU
|S_IRWXG
);
298 mkdir(GetBoneDir().CStr(), S_IRWXU
|S_IRWXG
);
300 mkdir(GetSaveDir().CStr());
301 mkdir(GetBoneDir().CStr());
306 CausePanicFlag
= false;
308 switch (Load(SaveName(PlayerName
))) {
310 globalwindowhandler::InstallControlLoop(AnimationController
);
312 SetForceJumpToPlayerBe(true);
313 GetCurrentArea()->SendNewDrawRequest();
314 SendLOSUpdateRequest();
315 ADD_MESSAGE("Game loaded successfully.");
318 iosystem::TextScreen(CONST_S(
319 "You couldn't possibly have guessed this day would differ from any other.\n"
320 "It began just as always. You woke up at dawn and drove off the giant spider\n"
321 "resting on your face. On your way to work you had serious trouble avoiding\n"
322 "the lions and pythons roaming wild around the village. After getting kicked\n"
323 "by colony masters for being late you performed your twelve-hour routine of\n"
324 "climbing trees, gathering bananas, climbing trees, gathering bananas, chasing\n"
325 "monkeys that stole the first gathered bananas, carrying bananas to the village\n"
326 "and trying to look happy when real food was distributed.\n\n"
327 "Finally you were about to enjoy your free time by taking a quick dip in the\n"
328 "nearby crocodile bay. However, at this point something unusual happened.\n"
329 "You were summoned to the mansion of Richel Decos, the viceroy of the\n"
330 "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."));
351 globalwindowhandler::InstallControlLoop(AnimationController
);
354 iosystem::TextScreen(CONST_S("Generating game...\n\nThis may take some time, please wait."), ZERO_V2
, WHITE
, false, true, &BusyAnimation
);
355 igraph::CreateBackGround(GRAY_FRACTAL
);
362 SetPlayer(playerkind::Spawn());
363 Player
->SetAssignedName(PlayerName
);
364 Player
->SetTeam(GetTeam(0));
365 Player
->SetNP(SATIATED_LEVEL
);
367 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
368 if (c
!= ENDURANCE
) Player
->EditAttribute(c
, (RAND()&1)-(RAND()&1));
369 Player
->EditExperience(c
, 500, 1<<11);
371 Player
->SetMoney(Player
->GetMoney()+RAND()%11);
372 GetTeam(0)->SetLeader(Player
);
376 SetCurrentArea(WorldMap
= new worldmap(128, 128));
377 CurrentWSquareMap
= WorldMap
->GetMap();
378 WorldMap
->Generate();
380 SendLOSUpdateRequest();
383 InitPlayerAttributeAverage();
393 PlayerMassacreMap
.clear();
394 PetMassacreMap
.clear();
395 MiscMassacreMap
.clear();
396 PlayerMassacreAmount
= PetMassacreAmount
= MiscMassacreAmount
= 0;
397 DefaultPolymorphTo
.Empty();
398 DefaultSummonMonster
.Empty();
400 DefaultChangeMaterial
.Empty();
401 DefaultDetectMaterial
.Empty();
402 Player
->GetStack()->AddItem(encryptedscroll::Spawn());
403 if (ivanconfig::GetDefaultPetName() != "_none_") {
404 character
*Doggie
= dog::Spawn();
405 Doggie
->SetTeam(GetTeam(0));
406 GetWorldMap()->GetPlayerGroup().push_back(Doggie
);
407 Doggie
->SetAssignedName(ivanconfig::GetDefaultPetName());
410 SeeWholeMapCheatMode
= MAP_HIDDEN
;
411 GoThroughWallsCheat
= false;
412 SumoWrestling
= false;
413 GlobalRainTimeModifier
= 2048-(RAND()&4095);
414 PlayerSumoChampion
= false;
415 PlayerSolicitusChampion
= false;
416 protosystem::InitCharacterDataBaseFlags();
417 memset(EquipmentMemory
, 0, sizeof(EquipmentMemory
));
418 PlayerRunning
= false;
419 InitAttributeMemory();
423 TimePlayedBeforeLastLoad
= time::GetZeroTime();
424 /*k8: damn! seems that this is field, not local! bool PlayerHasReceivedAllGodsKnownBonus = false; */
425 PlayerHasReceivedAllGodsKnownBonus
= false;
426 ADD_MESSAGE("You commence your journey to Attnam. Use direction keys to move, '>' to enter an area and '?' to view other commands.");
427 game::ClearEventData();
428 RunOnEvent("game_start");
430 item
*Present
= banana::Spawn();
431 Player
->GetStack()->AddItem(Present
);
432 ADD_MESSAGE("Atavus is happy today! He gives you %s.", Present
->CHAR_NAME(INDEFINITE
));
435 default: return false;
440 void game::DeInit () {
444 for (c
= 1; c
< Dungeons
; ++c
) delete Dungeon
[c
];
446 for (c
= 1; c
<= GODS
; ++c
) delete God
[c
]; // sorry, Valpuri!
449 for (c
= 0; c
< Teams
; ++c
) delete Team
[c
];
460 /* Temporary places */
461 static int Counter
= 0;
462 if (++Counter
== 10) {
463 CurrentLevel
->GenerateMonsters();
466 if (CurrentDungeonIndex
== ELPURI_CAVE
&& CurrentLevelIndex
== ZOMBIE_LEVEL
&& !RAND_N(1000+NecroCounter
)) {
467 character
*Char
= necromancer::Spawn(RAND_N(4) ? APPRENTICE_NECROMANCER
: MASTER_NECROMANCER
);
469 for (int c2
= 0; c2
< 30; ++c2
) {
470 Pos
= GetCurrentLevel()->GetRandomSquare(Char
);
471 if (abs(int(Pos
.X
)-Player
->GetPos().X
) > 20 || abs(int(Pos
.Y
)-Player
->GetPos().Y
) > 20) break;
473 if (Pos
!= ERROR_V2
) {
474 Char
->SetTeam(GetTeam(MONSTER_TEAM
));
476 Char
->SetGenerationDanger(GetCurrentLevel()->GetDifficulty());
477 Char
->SignalGeneration();
478 Char
->SignalNaturalGeneration();
481 int Modifier
= Time
.Day
- EDIT_ATTRIBUTE_DAY_MIN
;
482 if (Modifier
> 0) Char
->EditAllAttributes(Modifier
>> EDIT_ATTRIBUTE_DAY_SHIFT
);
489 if (!(GetTick() % 1000)) CurrentLevel
->CheckSunLight();
491 if ((CurrentDungeonIndex
== NEW_ATTNAM
|| CurrentDungeonIndex
== ATTNAM
) && CurrentLevelIndex
== 0) {
492 sLong OldVolume
= GlobalRainLiquid
->GetVolume();
493 sLong NewVolume
= Max(sLong(sin((Tick
+GlobalRainTimeModifier
)*0.0003)*300-150), 0);
494 if (NewVolume
&& !OldVolume
) CurrentLevel
->EnableGlobalRain();
495 else if(!NewVolume
&& OldVolume
) CurrentLevel
->DisableGlobalRain();
496 GlobalRainLiquid
->SetVolumeNoSignals(NewVolume
);
500 if (!RAND_N(2)) Item = wand::Spawn(1 + RAND_N(12));
501 else if(!RAND_N(2)) {
502 Item = beartrap::Spawn();
503 Item->SetIsActive(true);
504 Item->SetTeam(MONSTER_TEAM);
505 } else if(!RAND_N(2)) {
506 Item = mine::Spawn();
507 Item->SetIsActive(true);
508 Item->SetTeam(MONSTER_TEAM);
509 } else Item = holybanana::Spawn();
510 CurrentLevel->GetLSquare(CurrentLevel->GetRandomSquare())->AddItem(Item);
514 character *Char = protosystem::CreateMonster(0, 1000000);
515 Char->ChangeTeam(GetTeam(RAND() % Teams));
516 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
521 if (!RAND_N(5)) Char = spider::Spawn(GIANT);
522 else if (!RAND_N(5)) Char = darkmage::Spawn(1 + RAND_N(4));
523 else if (!RAND_N(5)) Char = necromancer::Spawn(1 + RAND_N(2));
524 else if (!RAND_N(5)) Char = chameleon::Spawn();
525 else if (!RAND_N(5)) Char = kamikazedwarf::Spawn(1 + RAND_N(GODS));
526 else if (!RAND_N(5)) Char = mommo::Spawn(1 + RAND_N(2));
527 else if (!RAND_N(3)) Char = bunny::Spawn(RAND_2 ? ADULT_MALE : ADULT_FEMALE);
528 else if (!RAND_N(3)) Char = eddy::Spawn();
529 else if (!RAND_N(3)) Char = magicmushroom::Spawn();
530 else if (!RAND_N(5)) Char = mushroom::Spawn();
531 else if (!RAND_N(3)) Char = blinkdog::Spawn();
532 else if (!RAND_N(5)) Char = tourist::Spawn(1 + RAND_N(3));
533 else if (!RAND_N(5)) Char = hattifattener::Spawn();
534 else if (!RAND_N(5)) Char = genetrixvesana::Spawn();
535 else if (!RAND_N(5)) Char = skunk::Spawn();
536 else if (!RAND_N(5)) Char = ennerbeast::Spawn();
537 else if (!RAND_N(5)) Char = werewolfhuman::Spawn();
538 else if (!RAND_N(5)) Char = unicorn::Spawn(1 + RAND_N(3));
539 else if (!RAND_N(5)) Char = floatingeye::Spawn();
540 else if (!RAND_N(5)) Char = zombie::Spawn();
541 else if (!RAND_N(5)) Char = magpie::Spawn();
542 else if (!RAND_N(5)) Char = elpuri::Spawn();
543 else if (!RAND_N(5)) Char = vladimir::Spawn();
544 else if (!RAND_N(5)) Char = billswill::Spawn();
545 else if (!RAND_N(5)) Char = ghost::Spawn();
546 else if (!RAND_N(5)) Char = dolphin::Spawn();
547 else if (!RAND_N(5)) Char = cossack::Spawn();
548 else Char = invisiblestalker::Spawn();
549 Char->SetTeam(GetTeam(RAND() % Teams));
550 Char->PutTo(CurrentLevel->GetRandomSquare(Char));
561 } catch (quitrequest
) {
563 } catch (areachangerequest
) {
569 void game::InitLuxTable () {
571 Alloc3D(LuxTable
, 256, 33, 33);
572 for (int c
= 0; c
< 0x100; ++c
)
573 for (int x
= 0; x
< 33; ++x
)
574 for (int y
= 0; y
< 33; ++y
) {
575 int X
= x
-16, Y
= y
-16;
576 LuxTable
[c
][x
][y
] = int(c
/(double(X
*X
+Y
*Y
)/128+1));
578 atexit(DeInitLuxTable
);
583 void game::DeInitLuxTable () {
589 void game::UpdateCameraX () {
590 UpdateCameraX(Player
->GetPos().X
);
594 void game::UpdateCameraY () {
595 UpdateCameraY(Player
->GetPos().Y
);
599 void game::UpdateCameraX (int X
) {
600 UpdateCameraCoordinate(Camera
.X
, X
, GetCurrentArea()->GetXSize(), GetScreenXSize());
604 void game::UpdateCameraY (int Y
) {
605 UpdateCameraCoordinate(Camera
.Y
, Y
, GetCurrentArea()->GetYSize(), GetScreenYSize());
609 void game::UpdateCameraCoordinate (int &Coordinate
, int Center
, int Size
, int ScreenSize
) {
610 int OldCoordinate
= Coordinate
;
611 if (Size
< ScreenSize
) Coordinate
= (Size
-ScreenSize
)>>1;
612 else if(Center
< ScreenSize
>>1) Coordinate
= 0;
613 else if(Center
> Size
-(ScreenSize
>>1)) Coordinate
= Size
-ScreenSize
;
614 else Coordinate
= Center
-(ScreenSize
>>1);
615 if (Coordinate
!= OldCoordinate
) GetCurrentArea()->SendNewDrawRequest();
619 cchar
*game::Insult () {
620 static const char *insults
[19] = {
633 "stupid-headed person",
637 "person-with-problems",
642 if (n
< 0 || n
> 18) n
= 18;
647 /* DefaultAnswer = REQUIRES_ANSWER the question requires an answer */
648 truth
game::TruthQuestion (cfestring
&String
, int DefaultAnswer
, int OtherKeyForTrue
) {
649 if (DefaultAnswer
== NO
) DefaultAnswer
= 'n';
650 else if (DefaultAnswer
== YES
) DefaultAnswer
= 'y';
651 else if (DefaultAnswer
!= REQUIRES_ANSWER
) ABORT("Illegal TruthQuestion DefaultAnswer send!");
652 int FromKeyQuestion
= KeyQuestion(String
, DefaultAnswer
, 9, 'y', 'Y', 'n', 'N', 't', 'T', 'o', 'O', OtherKeyForTrue
);
654 FromKeyQuestion
== 'y' || FromKeyQuestion
== 'Y' ||
655 FromKeyQuestion
== 't' || FromKeyQuestion
== 'T' ||
656 FromKeyQuestion
== OtherKeyForTrue
;
660 void game::DrawEverything () {
661 DrawEverythingNoBlit();
662 graphics::BlitDBToScreen();
666 truth
game::OnScreen (v2 Pos
) {
667 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();
671 void game::DrawEverythingNoBlit (truth AnimationDraw
) {
672 if (LOSUpdateRequested
&& Player
->IsEnabled()) {
673 if (!IsInWilderness()) GetCurrentLevel()->UpdateLOS(); else GetWorldMap()->UpdateLOS();
676 if (OnScreen(CursorPos
)) {
677 if (!IsInWilderness() || CurrentWSquareMap
[CursorPos
.X
][CursorPos
.Y
]->GetLastSeen() || GetSeeWholeMapCheatMode())
678 CurrentArea
->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
680 DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, 0);
683 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
684 if (OnScreen(SpecialCursorPos
[c
])) CurrentArea
->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
687 globalwindowhandler::UpdateTick();
688 GetCurrentArea()->Draw(AnimationDraw
);
689 Player
->DrawPanel(AnimationDraw
);
691 if (!AnimationDraw
) msgsystem::Draw();
693 if (OnScreen(CursorPos
)) {
694 v2 ScreenCoord
= CalculateScreenCoordinates(CursorPos
);
698 { ScreenCoord
.X
, ScreenCoord
.Y
},
699 { TILE_SIZE
, TILE_SIZE
},
702 ALLOW_ANIMATE
|ALLOW_ALPHA
705 if (!IsInWilderness() && !GetSeeWholeMapCheatMode()) {
706 lsquare
*Square
= CurrentLSquareMap
[CursorPos
.X
][CursorPos
.Y
];
707 if (Square
->GetLastSeen() != GetLOSTick()) Square
->DrawMemorized(B
);
712 B
.Dest
.X
= RES
.X
- 96;
713 B
.Dest
.Y
= RES
.Y
- 96;
715 DOUBLE_BUFFER
->StretchBlit(B
);
718 igraph::DrawCursor(ScreenCoord
, CursorData
);
721 if (Player
->IsEnabled()) {
722 if (Player
->IsSmall()) {
723 v2 Pos
= Player
->GetPos();
725 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
726 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData());
729 for (int f
= 0; f
< Player
->GetSquaresUnder(); ++f
) {
730 v2 Pos
= Player
->GetPos(f
);
732 v2 ScreenCoord
= CalculateScreenCoordinates(Pos
);
733 igraph::DrawCursor(ScreenCoord
, Player
->GetCursorData()|CURSOR_BIG
, f
);
739 for (unsigned int c
= 0; c
< SpecialCursorPos
.size(); ++c
) {
740 if (OnScreen(SpecialCursorPos
[c
])) {
741 v2 ScreenCoord
= CalculateScreenCoordinates(SpecialCursorPos
[c
]);
742 igraph::DrawCursor(ScreenCoord
, SpecialCursorData
[c
]);
743 GetCurrentArea()->GetSquare(SpecialCursorPos
[c
])->SendStrongNewDrawRequest();
749 truth
game::Save (cfestring
&SaveName
) {
750 if (!GetCurrentArea()->GetSquare(Player
->GetPos())->GetCharacter()) {
751 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
);
754 DrawEverythingNoBlit();
755 #if defined(SGAME_SHOTS_IPU) || !defined(HAVE_IMLIB2)
756 DOUBLE_BUFFER
->SaveScaledIPU(SaveName
+".ipu", 0.8); //640; 320
758 DOUBLE_BUFFER
->SaveScaledPNG(SaveName
+".png", 0.8); //640; 320
760 outputfile
SaveFile(SaveName
+".sav");
761 SaveFile
<< int(SAVE_FILE_VERSION
);
762 SaveFile
<< GameScript
<< CurrentDungeonIndex
<< CurrentLevelIndex
<< Camera
;
763 SaveFile
<< WizardMode
<< SeeWholeMapCheatMode
<< GoThroughWallsCheat
;
764 SaveFile
<< Tick
<< Turn
<< InWilderness
<< NextCharacterID
<< NextItemID
<< NextTrapID
<< NecroCounter
;
765 SaveFile
<< SumoWrestling
<< PlayerSumoChampion
<< GlobalRainTimeModifier
;
766 SaveFile
<< PlayerSolicitusChampion
;
768 femath::SetSeed(Seed
);
770 SaveFile
<< AveragePlayerArmStrengthExperience
;
771 SaveFile
<< AveragePlayerLegStrengthExperience
;
772 SaveFile
<< AveragePlayerDexterityExperience
;
773 SaveFile
<< AveragePlayerAgilityExperience
;
774 SaveFile
<< Teams
<< Dungeons
<< StoryState
<< PlayerRunning
;
775 SaveFile
<< MondedrPass
<< RingOfThieves
<< Masamune
<< Muramasa
<< LoricatusHammer
<< Liberator
;
776 SaveFile
<< PlayerMassacreMap
<< PetMassacreMap
<< MiscMassacreMap
;
777 SaveFile
<< PlayerMassacreAmount
<< PetMassacreAmount
<< MiscMassacreAmount
;
778 SaveArray(SaveFile
, EquipmentMemory
, MAX_EQUIPMENT_SLOTS
);
780 for (c
= 0; c
< ATTRIBUTES
; ++c
) SaveFile
<< OldAttribute
[c
] << NewAttribute
[c
] << LastAttributeChangeTick
[c
];
781 for (c
= 1; c
< Dungeons
; ++c
) SaveFile
<< Dungeon
[c
];
782 for (c
= 1; c
<= GODS
; ++c
) SaveFile
<< God
[c
];
783 for (c
= 0; c
< Teams
; ++c
) SaveFile
<< Team
[c
];
785 SaveWorldMap(SaveName
, false);
787 GetCurrentDungeon()->SaveLevel(SaveName
, CurrentLevelIndex
, false);
789 SaveFile
<< Player
->GetPos() << PlayerName
;
790 msgsystem::Save(SaveFile
);
791 SaveFile
<< DangerMap
<< NextDangerIDType
<< NextDangerIDConfigIndex
;
792 SaveFile
<< DefaultPolymorphTo
<< DefaultSummonMonster
;
793 SaveFile
<< DefaultWish
<< DefaultChangeMaterial
<< DefaultDetectMaterial
;
794 SaveFile
<< GetTimeSpent();
795 /* or in more readable format: time() - LastLoad + TimeAtLastLoad */
796 SaveFile
<< PlayerHasReceivedAllGodsKnownBonus
;
797 protosystem::SaveCharacterDataBaseFlags(SaveFile
);
802 int game::Load (cfestring
&SaveName
) {
803 inputfile
SaveFile(SaveName
+".sav", 0, false);
804 if (!SaveFile
.IsOpen()) return NEW_GAME
;
807 if (Version
!= SAVE_FILE_VERSION
) {
808 if (true || Version
!= 120) {
809 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
))
815 SaveFile
>> GameScript
>> CurrentDungeonIndex
>> CurrentLevelIndex
>> Camera
;
816 SaveFile
>> WizardMode
>> SeeWholeMapCheatMode
>> GoThroughWallsCheat
;
817 SaveFile
>> Tick
>> Turn
>> InWilderness
>> NextCharacterID
>> NextItemID
>> NextTrapID
>> NecroCounter
;
818 SaveFile
>> SumoWrestling
>> PlayerSumoChampion
>> GlobalRainTimeModifier
;
819 SaveFile
>> PlayerSolicitusChampion
;
820 femath::SetSeed(ReadType
<sLong
>(SaveFile
));
821 SaveFile
>> AveragePlayerArmStrengthExperience
;
822 SaveFile
>> AveragePlayerLegStrengthExperience
;
823 SaveFile
>> AveragePlayerDexterityExperience
;
824 SaveFile
>> AveragePlayerAgilityExperience
;
825 SaveFile
>> Teams
>> Dungeons
>> StoryState
>> PlayerRunning
;
826 SaveFile
>> MondedrPass
>> RingOfThieves
>> Masamune
>> Muramasa
>> LoricatusHammer
;
827 if (Version
== SAVE_FILE_VERSION
) {
828 SaveFile
>> Liberator
;
832 SaveFile
>> PlayerMassacreMap
>> PetMassacreMap
>> MiscMassacreMap
;
833 SaveFile
>> PlayerMassacreAmount
>> PetMassacreAmount
>> MiscMassacreAmount
;
834 LoadArray(SaveFile
, EquipmentMemory
, MAX_EQUIPMENT_SLOTS
);
836 for (c
= 0; c
< ATTRIBUTES
; ++c
) SaveFile
>> OldAttribute
[c
] >> NewAttribute
[c
] >> LastAttributeChangeTick
[c
];
837 Dungeon
= new dungeon
*[Dungeons
];
839 for (c
= 1; c
< Dungeons
; ++c
) SaveFile
>> Dungeon
[c
];
840 God
= new god
*[GODS
+1];
842 for (c
= 1; c
<= GODS
; ++c
) SaveFile
>> God
[c
];
843 Team
= new team
*[Teams
];
844 for (c
= 0; c
< Teams
; ++c
) SaveFile
>> Team
[c
];
846 SetCurrentArea(LoadWorldMap(SaveName
));
847 CurrentWSquareMap
= WorldMap
->GetMap();
848 igraph::CreateBackGround(GRAY_FRACTAL
);
850 SetCurrentArea(CurrentLevel
= GetCurrentDungeon()->LoadLevel(SaveName
, CurrentLevelIndex
));
851 CurrentLSquareMap
= CurrentLevel
->GetMap();
852 igraph::CreateBackGround(*CurrentLevel
->GetLevelScript()->GetBackGroundType());
855 SaveFile
>> Pos
>> PlayerName
;
856 SetPlayer(GetCurrentArea()->GetSquare(Pos
)->GetCharacter());
858 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
))
863 msgsystem::Load(SaveFile
);
864 SaveFile
>> DangerMap
>> NextDangerIDType
>> NextDangerIDConfigIndex
;
865 SaveFile
>> DefaultPolymorphTo
>> DefaultSummonMonster
;
866 SaveFile
>> DefaultWish
>> DefaultChangeMaterial
>> DefaultDetectMaterial
;
867 SaveFile
>> TimePlayedBeforeLastLoad
;
868 SaveFile
>> PlayerHasReceivedAllGodsKnownBonus
;
870 protosystem::LoadCharacterDataBaseFlags(SaveFile
);
875 festring
game::SaveName (cfestring
&Base
) {
876 festring SaveName
= GetSaveDir();
877 if (!Base
.GetSize()) SaveName
<< PlayerName
; else SaveName
<< Base
;
878 for (festring::sizetype c
= 0; c
< SaveName
.GetSize(); ++c
) if (SaveName
[c
] == ' ') SaveName
[c
] = '_';
883 int game::GetMoveCommandKeyBetweenPoints (v2 A
, v2 B
) {
884 for (int c
= 0; c
< EXTENDED_DIRECTION_COMMAND_KEYS
; ++c
) {
885 if ((A
+ GetMoveVector(c
)) == B
) return GetMoveCommandKey(c
);
891 void game::ApplyDivineTick () {
892 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->ApplyDivineTick();
896 void game::ApplyDivineAlignmentBonuses (god
*CompareTarget
, int Multiplier
, truth Good
) {
897 for (int c
= 1; c
<= GODS
; ++c
) if (GetGod(c
) != CompareTarget
) GetGod(c
)->AdjustRelation(CompareTarget
, Multiplier
, Good
);
901 v2
game::GetDirectionVectorForKey (int Key
) {
902 if (Key
== KEY_NUMPAD_5
|| Key
== '.') return ZERO_V2
; /* k8: '.' */
903 for (int c
= 0; c
< EXTENDED_DIRECTION_COMMAND_KEYS
; ++c
) if (Key
== GetMoveCommandKey(c
)) return GetMoveVector(c
);
908 double game::GetMinDifficulty () {
909 double Base
= CurrentLevel
->GetDifficulty()*0.2;
910 sLong MultiplierExponent
= 0;
913 int Modifier
= Time
.Day
-DANGER_PLUS_DAY_MIN
;
914 if (Modifier
> 0) Base
+= DANGER_PLUS_MULTIPLIER
* Modifier
;
916 int Dice
= RAND()%25;
917 if (Dice
< 5 && MultiplierExponent
> -3) {
919 --MultiplierExponent
;
922 if (Dice
>= 20 && MultiplierExponent
< 3) {
924 ++MultiplierExponent
;
932 void game::ShowLevelMessage () {
933 if (CurrentLevel
->GetLevelMessage().GetSize()) ADD_MESSAGE("%s", CurrentLevel
->GetLevelMessage().CStr());
934 CurrentLevel
->SetLevelMessage("");
938 int game::DirectionQuestion (cfestring
&Topic
, truth RequireAnswer
, truth AcceptYourself
) {
940 int Key
= AskForKeyPress(Topic
);
941 if (AcceptYourself
&& (Key
== '.' || Key
== KEY_NUMPAD_5
)) return YOURSELF
; //k8
942 for (int c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) if (Key
== GetMoveCommandKey(c
)) return c
;
943 if (!RequireAnswer
) return DIR_ERROR
;
948 void game::RemoveSaves (truth RealSavesAlso
) {
950 remove(festring(SaveName()+".sav").CStr());
951 remove(festring(SaveName()+".wm").CStr());
952 remove(festring(SaveName()+".png").CStr());
953 remove(festring(SaveName()+".ipu").CStr());
955 remove(festring(AutoSaveFileName
+".sav").CStr());
956 remove(festring(AutoSaveFileName
+".wm").CStr());
957 remove(festring(AutoSaveFileName
+".png").CStr());
958 remove(festring(AutoSaveFileName
+".ipu").CStr());
960 for (int i
= 1; i
< Dungeons
; ++i
) {
961 for (int c
= 0; c
< GetDungeon(i
)->GetLevels(); ++c
) {
962 /* This looks very odd. And it is very odd.
963 * Indeed, gcc is very odd to not compile this correctly with -O3
964 * if it is written in a less odd way. */
965 File
= SaveName()+'.'+i
;
967 if (RealSavesAlso
) remove(File
.CStr());
968 File
= AutoSaveFileName
+'.'+i
;
976 void game::SetPlayer (character
*NP
) {
978 if (Player
) Player
->AddFlags(C_PLAYER
);
982 void game::InitDungeons () {
983 Dungeons
= *GetGameScript()->GetDungeons()+1;
984 Dungeon
= new dungeon
*[Dungeons
];
986 for (int c
= 1; c
< Dungeons
; ++c
) {
987 Dungeon
[c
] = new dungeon(c
);
988 Dungeon
[c
]->SetIndex(c
);
993 void game::DoEvilDeed (int Amount
) {
995 for (int c
= 1; c
<= GODS
; ++c
) {
996 int Change
= Amount
-Amount
*GetGod(c
)->GetAlignment()/5;
997 if (!IsInWilderness() && Player
->GetLSquareUnder()->GetDivineMaster() == c
) {
998 if (GetGod(c
)->GetRelation()-(Change
<< 1) < -750) {
999 if (GetGod(c
)->GetRelation() > -750) GetGod(c
)->SetRelation(-750);
1000 } else if (GetGod(c
)->GetRelation()-(Change
<< 1) > 750) {
1001 if (GetGod(c
)->GetRelation() < 750) GetGod(c
)->SetRelation(750);
1002 } else GetGod(c
)->SetRelation(GetGod(c
)->GetRelation()-(Change
<< 1));
1004 if(GetGod(c
)->GetRelation()-Change
< -500) {
1005 if (GetGod(c
)->GetRelation() > -500) GetGod(c
)->SetRelation(-500);
1006 } else if (GetGod(c
)->GetRelation()-Change
> 500) {
1007 if (GetGod(c
)->GetRelation() < 500) GetGod(c
)->SetRelation(500);
1008 } else GetGod(c
)->SetRelation(GetGod(c
)->GetRelation() - Change
);
1014 void game::SaveWorldMap (cfestring
&SaveName
, truth DeleteAfterwards
) {
1015 outputfile
SaveFile(SaveName
+".wm");
1016 SaveFile
<< WorldMap
;
1017 if (DeleteAfterwards
) {
1024 worldmap
*game::LoadWorldMap (cfestring
&SaveName
) {
1025 inputfile
SaveFile(SaveName
+".wm");
1026 SaveFile
>> WorldMap
;
1031 void game::Hostility (team
*Attacker
, team
*Defender
) {
1032 for (int c
= 0; c
< Teams
; ++c
) {
1033 if (GetTeam(c
) != Attacker
&& GetTeam(c
) != Defender
&&
1034 GetTeam(c
)->GetRelation(Defender
) == FRIEND
&&
1035 c
!= NEW_ATTNAM_TEAM
&& c
!= TOURIST_GUIDE_TEAM
) // gum solution
1036 GetTeam(c
)->SetRelation(Attacker
, HOSTILE
);
1041 void game::CreateTeams () {
1042 Teams
= *GetGameScript()->GetTeams();
1043 Team
= new team
*[Teams
];
1045 for (c
= 0; c
< Teams
; ++c
) {
1046 Team
[c
] = new team(c
);
1047 for (int i
= 0; i
< c
; ++i
) Team
[i
]->SetRelation(Team
[c
], UNCARING
);
1049 for (c
= 0; c
< Teams
; ++c
) if (c
!= 1) Team
[1]->SetRelation(Team
[c
], HOSTILE
);
1050 const std::list
<std::pair
<int, teamscript
> >& TeamScript
= GetGameScript()->GetTeam();
1051 for (std::list
<std::pair
<int, teamscript
> >::const_iterator i
= TeamScript
.begin(); i
!= TeamScript
.end(); ++i
) {
1052 for (uInt c
= 0; c
< i
->second
.GetRelation().size(); ++c
) {
1053 GetTeam(i
->second
.GetRelation()[c
].first
)->SetRelation(GetTeam(i
->first
), i
->second
.GetRelation()[c
].second
);
1055 cint
*KillEvilness
= i
->second
.GetKillEvilness();
1056 if (KillEvilness
) GetTeam(i
->first
)->SetKillEvilness(*KillEvilness
);
1061 /* v2 Pos should be removed from xxxQuestion()s? */
1062 festring
game::StringQuestion (cfestring
&Topic
, col16 Color
, festring::sizetype MinLetters
, festring::sizetype MaxLetters
, truth AllowExit
, stringkeyhandler KeyHandler
) {
1063 DrawEverythingNoBlit();
1064 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23)); // pos may be incorrect!
1066 iosystem::StringQuestion(Return
, Topic
, v2(16, 6), Color
, MinLetters
, MaxLetters
, false, AllowExit
, KeyHandler
);
1067 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1072 sLong
game::NumberQuestion (cfestring
&Topic
, col16 Color
, truth ReturnZeroOnEsc
) {
1073 DrawEverythingNoBlit();
1074 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1075 sLong Return
= iosystem::NumberQuestion(Topic
, v2(16, 6), Color
, false, ReturnZeroOnEsc
);
1076 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1081 sLong
game::ScrollBarQuestion (cfestring
&Topic
, sLong BeginValue
, sLong Step
, sLong Min
, sLong Max
, sLong AbortValue
, col16 TopicColor
, col16 Color1
, col16 Color2
, void (*Handler
)(sLong
)) {
1082 DrawEverythingNoBlit();
1083 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1084 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
);
1085 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize() << 4, 23));
1090 feuLong
game::IncreaseLOSTick () {
1091 if (LOSTick
!= 0xFE) return LOSTick
+= 2;
1092 CurrentLevel
->InitLastSeen();
1097 void game::UpdateCamera () {
1103 truth
game::HandleQuitMessage () {
1105 if (IsInGetCommand()) {
1106 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
)) {
1112 GetCurrentArea()->SendNewDrawRequest();
1116 festring Msg
= CONST_S("cowardly quit the game");
1117 Player
->AddScoreEntry(Msg
, 0.75);
1118 End(Msg
, true, false);
1121 } 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
)) {
1124 GetCurrentArea()->SendNewDrawRequest();
1133 int game::GetDirectionForVector (v2 Vector
) {
1134 for (int c
= 0; c
< DIRECTION_COMMAND_KEYS
; ++c
) if (Vector
== GetMoveVector(c
)) return c
;
1139 cchar
*game::GetVerbalPlayerAlignment () {
1141 for (int c
= 1; c
<= GODS
; ++c
) {
1142 if (GetGod(c
)->GetRelation() > 0) Sum
+= GetGod(c
)->GetRelation() * (5 - GetGod(c
)->GetAlignment());
1144 if (Sum
> 15000) return "extremely lawful";
1145 if (Sum
> 10000) return "very lawful";
1146 if (Sum
> 5000) return "lawful";
1147 if (Sum
> 1000) return "mildly lawful";
1148 if (Sum
> -1000) return "neutral";
1149 if (Sum
> -5000) return "mildly chaotic";
1150 if (Sum
> -10000) return "chaotic";
1151 if (Sum
> -15000) return "very chaotic";
1152 return "extremely chaotic";
1156 void game::CreateGods () {
1157 God
= new god
*[GODS
+1];
1159 for (int c
= 1; c
< protocontainer
<god
>::GetSize(); ++c
) God
[c
] = protocontainer
<god
>::GetProto(c
)->Spawn();
1163 void game::BusyAnimation () {
1164 BusyAnimation(DOUBLE_BUFFER
, false);
1168 void game::BusyAnimation (bitmap
*Buffer
, truth ForceDraw
) {
1169 static clock_t LastTime
= 0;
1170 static int Frame
= 0;
1171 static blitdata B1
= {
1180 static blitdata B2
= {
1183 { (RES
.X
>> 1) - 100, (RES
.Y
<< 1) / 3 - 100 },
1189 if (ForceDraw
|| clock()-LastTime
> CLOCKS_PER_SEC
/25) {
1191 B2
.Dest
.X
= (RES
.X
>>1)-100+EnterTextDisplacement
.X
;
1192 B2
.Dest
.Y
= (RES
.Y
<<1)/3-100+EnterTextDisplacement
.Y
;
1195 EnterImage
->NormalMaskedBlit(B1
);
1197 BusyAnimationCache
[Frame
]->NormalBlit(B2
);
1198 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
1199 if (++Frame
== 32) Frame
= 0;
1205 void game::CreateBusyAnimationCache () {
1206 bitmap
Elpuri(TILE_V2
, TRANSPARENT_COLOR
);
1207 Elpuri
.ActivateFastFlag();
1208 packcol16 Color
= MakeRGB16(60, 60, 60);
1209 igraph::GetCharacterRawGraphic()->MaskedBlit(&Elpuri
, v2(64, 0), ZERO_V2
, TILE_V2
, &Color
);
1210 bitmap
Circle(v2(200, 200), TRANSPARENT_COLOR
);
1211 Circle
.ActivateFastFlag();
1212 for (int x
= 0; x
< 4; ++x
) Circle
.DrawPolygon(100, 100, 95+x
, 50, MakeRGB16(255-12*x
, 0, 0));
1217 { TILE_SIZE
, TILE_SIZE
},
1231 for (int c
= 0; c
< 32; ++c
) {
1232 B1
.Bitmap
= B2
.Bitmap
= BusyAnimationCache
[c
] = new bitmap(v2(200, 200), 0);
1233 B1
.Bitmap
->ActivateFastFlag();
1234 Elpuri
.NormalMaskedBlit(B1
);
1235 double Rotation
= 0.3+c
*FPI
/80;
1236 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);
1237 Circle
.NormalMaskedBlit(B2
);
1242 int game::AskForKeyPress (cfestring
&Topic
) {
1243 DrawEverythingNoBlit();
1244 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CapitalizeCopy().CStr());
1245 graphics::BlitDBToScreen();
1246 int Key
= GET_KEY();
1247 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1252 void game::AskForEscPress (cfestring
&Topic
) {
1253 DrawEverythingNoBlit();
1254 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), RED
/*WHITE*/, "%s [press ESC]", Topic
.CapitalizeCopy().CStr());
1255 graphics::BlitDBToScreen();
1259 } while (Key
!= KEY_ESC
);
1260 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1264 /* Handler is called when the key has been identified as a movement key
1265 * KeyHandler is called when the key has NOT been identified as a movement key
1266 * Both can be deactivated by passing 0 as parameter */
1267 v2
game::PositionQuestion (cfestring
&Topic
, v2 CursorPos
, void (*Handler
)(v2
), positionkeyhandler KeyHandler
, truth Zoom
) {
1271 CursorData
= RED_CURSOR
;
1272 if (Handler
) Handler(CursorPos
);
1274 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1275 if (!Square
->HasBeenSeen() &&
1276 (!Square
->GetCharacter() || !Square
->GetCharacter()->CanBeSeenByPlayer()) &&
1277 !GetSeeWholeMapCheatMode()) DOUBLE_BUFFER
->Fill(CalculateScreenCoordinates(CursorPos
), TILE_V2
, BLACK
);
1278 else GetCurrentArea()->GetSquare(CursorPos
)->SendStrongNewDrawRequest();
1280 if (Key
== ' ' || Key
== '.' || Key
== KEY_NUMPAD_5
) { Return
= CursorPos
; break; }
1281 if (Key
== KEY_ESC
) { Return
= ERROR_V2
; break; }
1283 v2 DirectionVector
= GetDirectionVectorForKey(Key
);
1284 if (DirectionVector
!= ERROR_V2
) {
1285 CursorPos
+= DirectionVector
;
1286 if (CursorPos
.X
> GetCurrentArea()->GetXSize()-1) CursorPos
.X
= 0;
1287 if (CursorPos
.X
< 0) CursorPos
.X
= GetCurrentArea()->GetXSize()-1;
1288 if (CursorPos
.Y
> GetCurrentArea()->GetYSize()-1) CursorPos
.Y
= 0;
1289 if (CursorPos
.Y
< 0) CursorPos
.Y
= GetCurrentArea()->GetYSize()-1;
1290 if (Handler
) Handler(CursorPos
);
1291 } else if (KeyHandler
) {
1292 CursorPos
= KeyHandler(CursorPos
, Key
);
1293 if (CursorPos
== ERROR_V2
|| CursorPos
== ABORT_V2
) {
1299 if (ivanconfig::GetAutoCenterMapOnLook()) {
1300 UpdateCameraX(CursorPos
.X
);
1301 UpdateCameraY(CursorPos
.Y
);
1303 if (CursorPos
.X
< GetCamera().X
+3 || CursorPos
.X
>= GetCamera().X
+GetScreenXSize()-3) UpdateCameraX(CursorPos
.X
);
1304 if (CursorPos
.Y
< GetCamera().Y
+3 || CursorPos
.Y
>= GetCamera().Y
+GetScreenYSize()-3) UpdateCameraY(CursorPos
.Y
);
1307 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Topic
.CStr());
1308 SetCursorPos(CursorPos
);
1313 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1314 igraph::BlitBackGround(v2(RES
.X
-96, RES
.Y
-96), v2(80, 80));
1316 SetCursorPos(v2(-1, -1));
1321 void game::LookHandler (v2 CursorPos
) {
1322 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1325 if (GetSeeWholeMapCheatMode()) {
1326 OldMemory
= Square
->GetMemorizedDescription();
1327 if (IsInWilderness()) GetWorldMap()->GetWSquare(CursorPos
)->UpdateMemorizedDescription(true);
1328 else GetCurrentLevel()->GetLSquare(CursorPos
)->UpdateMemorizedDescription(true);
1332 if (Square
->HasBeenSeen() || GetSeeWholeMapCheatMode()) {
1333 if (!IsInWilderness() && !Square
->CanBeSeenByPlayer() && GetCurrentLevel()->GetLSquare(CursorPos
)->CanBeFeltByPlayer())
1334 Msg
= CONST_S("You feel here ");
1335 else if (Square
->CanBeSeenByPlayer(true) || GetSeeWholeMapCheatMode())
1336 Msg
= CONST_S("You see here ");
1338 Msg
= CONST_S("You remember here ");
1339 Msg
<< Square
->GetMemorizedDescription() << '.';
1340 if (!IsInWilderness() && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) {
1341 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1342 LSquare
->DisplaySmokeInfo(Msg
);
1343 if (LSquare
->HasEngravings() && LSquare
->IsTransparent()) {
1344 if (LSquare
->EngravingsCanBeReadByPlayer() || GetSeeWholeMapCheatMode()) LSquare
->DisplayEngravedInfo(Msg
);
1345 else Msg
<< " Something has been engraved here.";
1348 } else Msg
= CONST_S("You have never been here.");
1349 character
*Character
= Square
->GetCharacter();
1350 if (Character
&& (Character
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Character
->DisplayInfo(Msg
);
1351 if (!(RAND()%10000) && (Square
->CanBeSeenByPlayer() || GetSeeWholeMapCheatMode())) Msg
<< " You see here a frog eating a magnolia.";
1352 ADD_MESSAGE("%s", Msg
.CStr());
1353 if (GetSeeWholeMapCheatMode()) Square
->SetMemorizedDescription(OldMemory
);
1357 truth
game::AnimationController () {
1358 DrawEverythingNoBlit(true);
1363 void game::LoadGlobalValueMap (inputfile
&fl
) {
1365 fl
.setGetVarCB(game::ldrGetVar
);
1366 for (fl
.ReadWord(word
, false); !fl
.Eof(); fl
.ReadWord(word
, false)) {
1367 if (word
== "Include") {
1368 word
= fl
.ReadWord();
1369 if (fl
.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TellLine());
1370 //fprintf(stderr, "loading: %s\n", word.CStr());
1371 inputfile
incf(game::GetGameDir()+"Script/"+word
, &game::GetGlobalValueMap());
1372 LoadGlobalValueMap(incf
);
1375 if (word
== "Message") {
1376 word
= fl
.ReadWord();
1377 if (fl
.ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TellLine());
1378 fprintf(stderr
, "MESSAGE: %s\n", word
.CStr());
1381 if (word
!= "#") ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TellLine());
1382 fl
.ReadWord(word
, true);
1383 if (word
== "enum" || word
== "bitenum") {
1384 truth isBit
= word
== "bitenum";
1386 if (fl
.ReadWord() != "{") ABORT("'{' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TellLine());
1390 fl
.ReadWord(word
, true);
1391 if (word
== "}") break;
1396 fl
.ReadWord(word
, true);
1399 // set current index
1400 idx
= fl
.ReadNumber();
1402 if (word
!= "," && word
!= ";" && word
!= "}") ABORT("',' expected in file %s at line %d!", fl
.GetFileName().CStr(), fl
.TellLine());
1403 if (word
== "}") done
= true;
1405 if (idName
.GetSize() > 0) {
1407 if (isBit
) i
= 1<<i
;
1408 GlobalValueMap
.insert(std::make_pair(idName
, i
));
1414 if (ch
!= EOF
&& ch
!= ';') fl
.Unget(ch
);
1415 //if (fl.ReadWord() != ";") ABORT("';' expected in file %s at line %d!", fl.GetFileName().CStr(), fl.TellLine());
1418 if (word
== "define") {
1420 sLong v
= fl
.ReadNumber();
1421 GlobalValueMap
.insert(std::make_pair(word
, v
));
1424 ABORT("Illegal datafile define in file %s on line %d!", fl
.GetFileName().CStr(), fl
.TellLine());
1429 void game::InitGlobalValueMap () {
1430 inputfile
SaveFile(GetGameDir()+"Script/define.dat", &GlobalValueMap
);
1431 LoadGlobalValueMap(SaveFile
);
1432 { /* additional files */
1433 for (int f
= 0; f
<= 99; f
++) {
1435 sprintf(bnum
, "Script/define_%02d.dat", f
);
1436 festring fn
= game::GetGameDir();
1438 if (inputfile::fileExists(fn
)) return;
1439 inputfile
ifl(fn
, &game::GetGlobalValueMap(), false);
1441 LoadGlobalValueMap(ifl
);
1449 void game::TextScreen (cfestring
&Text
, v2 Displacement
, col16 Color
, truth GKey
, truth Fade
, bitmapeditor BitmapEditor
) {
1450 globalwindowhandler::DisableControlLoops();
1451 iosystem::TextScreen(Text
, Displacement
, Color
, GKey
, Fade
, BitmapEditor
);
1452 globalwindowhandler::EnableControlLoops();
1456 /* ... all the keys that are acceptable
1457 DefaultAnswer = REQUIRES_ANSWER if this question requires an answer
1458 Not surprisingly KeyNumber is the number of keys at ...
1460 int game::KeyQuestion (cfestring
&Message
, int DefaultAnswer
, int KeyNumber
, ...) {
1461 int *Key
= new int[KeyNumber
];
1463 va_start(Arguments
, KeyNumber
);
1464 for (int c
= 0; c
< KeyNumber
; ++c
) Key
[c
] = va_arg(Arguments
, int);
1466 DrawEverythingNoBlit();
1467 FONT
->Printf(DOUBLE_BUFFER
, v2(16, 8), WHITE
, "%s", Message
.CStr());
1468 graphics::BlitDBToScreen();
1472 for (int c
= 0; c
< KeyNumber
; ++c
) {
1478 if (!Return
&& DefaultAnswer
!= REQUIRES_ANSWER
) Return
= DefaultAnswer
;
1481 igraph::BlitBackGround(v2(16, 6), v2(GetScreenXSize()<<4, 23));
1486 v2
game::LookKeyHandler (v2 CursorPos
, int Key
) {
1487 square
*Square
= GetCurrentArea()->GetSquare(CursorPos
);
1490 if (!IsInWilderness()) {
1491 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1492 lsquare
*LSquare
= GetCurrentLevel()->GetLSquare(CursorPos
);
1493 stack
*Stack
= LSquare
->GetStack();
1494 if (LSquare
->IsTransparent() && Stack
->GetVisibleItems(Player
))
1495 Stack
->DrawContents(Player
, "Items here", NO_SELECT
|(GetSeeWholeMapCheatMode() ? 0 : NO_SPECIAL_INFO
));
1497 ADD_MESSAGE("You see no items here.");
1498 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1502 if (Square
->CanBeSeenByPlayer() || CursorPos
== Player
->GetPos() || GetSeeWholeMapCheatMode()) {
1503 character
*Char
= Square
->GetCharacter();
1504 if (Char
&& (Char
->CanBeSeenByPlayer() || Char
->IsPlayer() || GetSeeWholeMapCheatMode()))
1507 ADD_MESSAGE("You see no one here.");
1508 } else ADD_MESSAGE("You should perhaps move a bit closer.");
1515 v2
game::NameKeyHandler (v2 CursorPos
, int Key
) {
1516 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
1517 if (Key
== 'n' || Key
== 'N') {
1518 character
*Char
= GetCurrentArea()->GetSquare(CursorPos
)->GetCharacter();
1519 if (Char
&& Char
->CanBeSeenByPlayer()) Char
->TryToName();
1520 else ADD_MESSAGE("You don't see anyone here to name.");
1526 void game::End (festring DeathMessage
, truth Permanently
, truth AndGoToMenu
) {
1527 globalwindowhandler::DeInstallControlLoop(AnimationController
);
1528 SetIsRunning(false);
1529 if (Permanently
|| !WizardModeIsReallyActive()) RemoveSaves(Permanently
);
1530 if (Permanently
&& !WizardModeIsReallyActive()) {
1532 if (HScore
.LastAddFailed()) {
1533 iosystem::TextScreen(CONST_S("You didn't manage to get onto the high score list.\n\n\n\n")+GetPlayerName()+", "+DeathMessage
+"\nRIP");
1534 } else HScore
.Draw();
1537 /* This prevents monster movement etc. after death. */
1538 throw quitrequest();
1543 int game::CalculateRoughDirection (v2 Vector
) {
1544 if (!Vector
.X
&& !Vector
.Y
) return YOURSELF
;
1545 double Angle
= femath::CalculateAngle(Vector
);
1546 if (Angle
< FPI
/ 8) return 4;
1547 else if (Angle
< 3*FPI
/8) return 7;
1548 else if (Angle
< 5*FPI
/8) return 6;
1549 else if (Angle
< 7*FPI
/8) return 5;
1550 else if (Angle
< 9*FPI
/8) return 3;
1551 else if (Angle
< 11*FPI
/8) return 0;
1552 else if (Angle
< 13*FPI
/8) return 1;
1553 else if (Angle
< 15*FPI
/8) return 2;
1558 int game::Menu (bitmap
*BackGround
, v2 Pos
, cfestring
&Topic
, cfestring
&sMS
, col16 Color
, cfestring
&SmallText1
, cfestring
&SmallText2
) {
1559 globalwindowhandler::DisableControlLoops();
1560 int Return
= iosystem::Menu(BackGround
, Pos
, Topic
, sMS
, Color
, SmallText1
, SmallText2
);
1561 globalwindowhandler::EnableControlLoops();
1566 void game::InitDangerMap () {
1568 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
1570 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
1571 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1572 int ConfigSize
= Proto
->GetConfigSize();
1573 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
1574 if (!ConfigData
[c2
]->IsAbstract
) {
1575 int Config
= ConfigData
[c2
]->Config
;
1577 NextDangerIDType
= c1
;
1578 NextDangerIDConfigIndex
= c2
;
1581 character
*Char
= Proto
->Spawn(Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1582 double NakedDanger
= Char
->GetRelativeDanger(Player
, true);
1584 Char
= Proto
->Spawn(Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1585 double EquippedDanger
= Char
->GetRelativeDanger(Player
, true);
1587 DangerMap
[configid(c1
, Config
)] = dangerid(NakedDanger
, EquippedDanger
);
1594 void game::CalculateNextDanger () {
1595 if (IsInWilderness() || !*CurrentLevel
->GetLevelScript()->GenerateMonsters()) return;
1596 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1597 const character::database
*const *ConfigData
= Proto
->GetConfigData();
1598 const character::database
*DataBase
= ConfigData
[NextDangerIDConfigIndex
];
1599 dangermap::iterator DangerIterator
= DangerMap
.find(configid(NextDangerIDType
, DataBase
->Config
));
1600 team
*Team
= GetTeam(PLAYER_TEAM
);
1601 if (DataBase
&& DangerIterator
!= DangerMap
.end()) {
1602 character
*Char
= Proto
->Spawn(DataBase
->Config
, NO_EQUIPMENT
|NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1603 std::list
<character
*>::const_iterator i
;
1604 double DangerSum
= Player
->GetRelativeDanger(Char
, true);
1605 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
)
1606 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10))
1607 DangerSum
+= (*i
)->GetRelativeDanger(Char
, true)/4;
1608 double CurrentDanger
= 1/DangerSum
;
1609 double NakedDanger
= DangerIterator
->second
.NakedDanger
;
1611 if (NakedDanger
> CurrentDanger
) DangerIterator
->second
.NakedDanger
= (NakedDanger
*9+CurrentDanger
)/10;
1612 Char
= Proto
->Spawn(DataBase
->Config
, NO_PIC_UPDATE
|NO_EQUIPMENT_PIC_UPDATE
);
1613 DangerSum
= Player
->GetRelativeDanger(Char
, true);
1614 for (i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
)
1615 if ((*i
)->IsEnabled() && !(*i
)->IsTemporary() && !RAND_N(10))
1616 DangerSum
+= (*i
)->GetRelativeDanger(Char
, true) / 4;
1617 CurrentDanger
= 1/DangerSum
;
1618 double EquippedDanger
= DangerIterator
->second
.EquippedDanger
;
1620 if (EquippedDanger
> CurrentDanger
) DangerIterator
->second
.EquippedDanger
= (EquippedDanger
*9+CurrentDanger
)/10;
1621 if (++NextDangerIDConfigIndex
< Proto
->GetConfigSize()) return;
1623 if (++NextDangerIDType
>= protocontainer
<character
>::GetSize()) NextDangerIDType
= 1;
1624 Proto
= protocontainer
<character
>::GetProto(NextDangerIDType
);
1625 ConfigData
= Proto
->GetConfigData();
1626 int ConfigSize
= Proto
->GetConfigSize();
1627 for (int c
= 0; c
< ConfigSize
; ++c
) {
1628 if (!ConfigData
[c
]->IsAbstract
) {
1629 NextDangerIDConfigIndex
= c
;
1634 } else ABORT("It is dangerous to go ice fishing in the summer.");
1638 truth
game::TryTravel (int Dungeon
, int Area
, int EntryIndex
, truth AllowHostiles
, truth AlliesFollow
) {
1639 charactervector Group
;
1640 if (LeaveArea(Group
, AllowHostiles
, AlliesFollow
)) {
1641 CurrentDungeonIndex
= Dungeon
;
1642 EnterArea(Group
, Area
, EntryIndex
);
1649 truth
game::LeaveArea (charactervector
&Group
, truth AllowHostiles
, truth AlliesFollow
) {
1650 if (!IsInWilderness()) {
1651 if (AlliesFollow
&& !GetCurrentLevel()->CollectCreatures(Group
, Player
, AllowHostiles
)) return false;
1653 GetCurrentDungeon()->SaveLevel(SaveName(), CurrentLevelIndex
);
1656 GetWorldMap()->GetPlayerGroup().swap(Group
);
1663 /* Used always when the player enters an area. */
1664 void game::EnterArea (charactervector
&Group
, int Area
, int EntryIndex
) {
1665 if (Area
!= WORLD_MAP
) {
1667 SetIsInWilderness(false);
1668 CurrentLevelIndex
= Area
;
1669 truth New
= !PrepareRandomBone(Area
) && !GetCurrentDungeon()->PrepareLevel(Area
);
1670 igraph::CreateBackGround(*CurrentLevel
->GetLevelScript()->GetBackGroundType());
1671 GetCurrentArea()->SendNewDrawRequest();
1672 v2 Pos
= GetCurrentLevel()->GetEntryPos(Player
, EntryIndex
);
1674 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
1675 Player
->PutToOrNear(Pos
);
1676 } else SetPlayer(GetCurrentLevel()->GetLSquare(Pos
)->GetCharacter());
1678 for (c
= 0; c
< Group
.size(); ++c
) {
1679 v2 NPCPos
= GetCurrentLevel()->GetNearestFreeSquare(Group
[c
], Pos
);
1680 if (NPCPos
== ERROR_V2
) NPCPos
= GetCurrentLevel()->GetRandomSquare(Group
[c
]);
1681 Group
[c
]->PutTo(NPCPos
);
1683 GetCurrentLevel()->FiatLux();
1684 ctruth
*AutoReveal
= GetCurrentLevel()->GetLevelScript()->AutoReveal();
1685 if (New
&& AutoReveal
&& *AutoReveal
) GetCurrentLevel()->Reveal();
1687 SendLOSUpdateRequest();
1691 if (New
&& CurrentDungeonIndex
== ATTNAM
&& Area
== 0) {
1692 GlobalRainLiquid
= powder::Spawn(SNOW
);
1693 GlobalRainSpeed
= v2(-64, 128);
1694 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1697 if (New
&& CurrentDungeonIndex
== NEW_ATTNAM
&& Area
== 0) {
1698 GlobalRainLiquid
= liquid::Spawn(WATER
);
1699 GlobalRainSpeed
= v2(256, 512);
1700 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1703 if (New
&& CurrentDungeonIndex
== ELPURI_CAVE
&& Area
== OREE_LAIR
) {
1704 GlobalRainLiquid
= liquid::Spawn(BLOOD
);
1705 GlobalRainSpeed
= v2(256, 512);
1706 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1707 GlobalRainLiquid
->SetVolumeNoSignals(200);
1708 CurrentLevel
->EnableGlobalRain();
1711 if (New
&& CurrentDungeonIndex
== MUNTUO
&& Area
== 0) {
1712 GlobalRainLiquid
= liquid::Spawn(WATER
);
1713 GlobalRainSpeed
= v2(-64, 1024);
1714 CurrentLevel
->CreateGlobalRain(GlobalRainLiquid
, GlobalRainSpeed
);
1718 GetCurrentLevel()->UpdateLOS();
1719 Player
->SignalStepFrom(0);
1721 for (c
= 0; c
< Group
.size(); ++c
) Group
[c
]->SignalStepFrom(0);
1723 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1725 igraph::CreateBackGround(GRAY_FRACTAL
);
1726 SetIsInWilderness(true);
1728 SetCurrentArea(WorldMap
);
1729 CurrentWSquareMap
= WorldMap
->GetMap();
1730 GetWorldMap()->GetPlayerGroup().swap(Group
);
1731 Player
->PutTo(GetWorldMap()->GetEntryPos(Player
, EntryIndex
));
1732 SendLOSUpdateRequest();
1734 GetWorldMap()->UpdateLOS();
1735 if (ivanconfig::GetAutoSaveInterval()) Save(GetAutoSaveFileName().CStr());
1740 int game::CompareLightToInt (col24 L
, col24 Int
) {
1741 if ((L
& 0xFF0000) > Int
|| (L
& 0xFF00) > Int
|| (L
& 0xFF) > Int
) return 1;
1742 if ((L
& 0xFF0000) == Int
|| (L
& 0xFF00) == Int
|| (L
& 0xFF) == Int
) return 0;
1747 void game::SetStandardListAttributes (felist
&List
) {
1748 List
.SetPos(v2(26, 42));
1750 List
.SetFlags(DRAW_BACKGROUND_AFTERWARDS
);
1751 List
.SetUpKey(GetMoveCommandKey(KEY_UP_INDEX
));
1752 List
.SetDownKey(GetMoveCommandKey(KEY_DOWN_INDEX
));
1756 void game::InitPlayerAttributeAverage () {
1757 AveragePlayerArmStrengthExperience
1758 = AveragePlayerLegStrengthExperience
1759 = AveragePlayerDexterityExperience
1760 = AveragePlayerAgilityExperience
1763 if (!Player
->IsHumanoid()) return;
1765 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1768 arm
*RightArm
= Player
->GetRightArm();
1770 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1771 AveragePlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1772 AveragePlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1776 arm
*LeftArm
= Player
->GetLeftArm();
1778 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1779 AveragePlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1780 AveragePlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1784 leg
*RightLeg
= Player
->GetRightLeg();
1786 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1787 AveragePlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1788 AveragePlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1792 leg
*LeftLeg
= Player
->GetLeftLeg();
1794 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1795 AveragePlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1796 AveragePlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1801 AveragePlayerArmStrengthExperience
/= Arms
;
1802 AveragePlayerDexterityExperience
/= Arms
;
1806 AveragePlayerLegStrengthExperience
/= Legs
;
1807 AveragePlayerAgilityExperience
/= Legs
;
1812 void game::UpdatePlayerAttributeAverage () {
1813 if (!Player
->IsHumanoid()) return;
1815 humanoid
*Player
= static_cast<humanoid
*>(GetPlayer());
1816 double PlayerArmStrengthExperience
= 0;
1817 double PlayerLegStrengthExperience
= 0;
1818 double PlayerDexterityExperience
= 0;
1819 double PlayerAgilityExperience
= 0;
1822 arm
*RightArm
= Player
->GetRightArm();
1824 if (RightArm
&& !RightArm
->UseMaterialAttributes()) {
1825 PlayerArmStrengthExperience
+= RightArm
->GetStrengthExperience();
1826 PlayerDexterityExperience
+= RightArm
->GetDexterityExperience();
1830 arm
*LeftArm
= Player
->GetLeftArm();
1832 if (LeftArm
&& !LeftArm
->UseMaterialAttributes()) {
1833 PlayerArmStrengthExperience
+= LeftArm
->GetStrengthExperience();
1834 PlayerDexterityExperience
+= LeftArm
->GetDexterityExperience();
1838 leg
*RightLeg
= Player
->GetRightLeg();
1840 if (RightLeg
&& !RightLeg
->UseMaterialAttributes()) {
1841 PlayerLegStrengthExperience
+= RightLeg
->GetStrengthExperience();
1842 PlayerAgilityExperience
+= RightLeg
->GetAgilityExperience();
1846 leg
*LeftLeg
= Player
->GetLeftLeg();
1848 if (LeftLeg
&& !LeftLeg
->UseMaterialAttributes()) {
1849 PlayerLegStrengthExperience
+= LeftLeg
->GetStrengthExperience();
1850 PlayerAgilityExperience
+= LeftLeg
->GetAgilityExperience();
1855 AveragePlayerArmStrengthExperience
= (49 * AveragePlayerArmStrengthExperience
+ PlayerArmStrengthExperience
/ Arms
) / 50;
1856 AveragePlayerDexterityExperience
= (49 * AveragePlayerDexterityExperience
+ PlayerDexterityExperience
/ Arms
) / 50;
1860 AveragePlayerLegStrengthExperience
= (49 * AveragePlayerLegStrengthExperience
+ PlayerLegStrengthExperience
/ Legs
) / 50;
1861 AveragePlayerAgilityExperience
= (49 * AveragePlayerAgilityExperience
+ PlayerAgilityExperience
/ Legs
) / 50;
1866 void game::CallForAttention (v2 Pos
, int RangeSquare
) {
1867 for (int c
= 0; c
< GetTeams(); ++c
) {
1868 if (GetTeam(c
)->HasEnemy())
1869 for (std::list
<character
*>::const_iterator i
= GetTeam(c
)->GetMember().begin(); i
!= GetTeam(c
)->GetMember().end(); ++i
)
1870 if ((*i
)->IsEnabled()) {
1871 sLong ThisDistance
= HypotSquare(sLong((*i
)->GetPos().X
) - Pos
.X
, sLong((*i
)->GetPos().Y
) - Pos
.Y
);
1872 if (ThisDistance
<= RangeSquare
&& !(*i
)->IsGoingSomeWhere()) (*i
)->SetGoingTo(Pos
);
1878 outputfile
&operator << (outputfile
&SaveFile
, const homedata
*HomeData
) {
1881 SaveFile
<< HomeData
->Pos
<< HomeData
->Dungeon
<< HomeData
->Level
<< HomeData
->Room
;
1882 } else SaveFile
.Put(0);
1887 inputfile
&operator >> (inputfile
&SaveFile
, homedata
*&HomeData
) {
1888 if (SaveFile
.Get()) {
1889 HomeData
= new homedata
;
1890 SaveFile
>> HomeData
->Pos
>> HomeData
->Dungeon
>> HomeData
->Level
>> HomeData
->Room
;
1896 feuLong
game::CreateNewCharacterID (character
*NewChar
) {
1897 feuLong ID
= NextCharacterID
++;
1898 /*k8:??? if(CharacterIDMap.find(ID) != CharacterIDMap.end())
1899 int esko = esko = 2;*/
1900 CharacterIDMap
.insert(std::make_pair(ID
, NewChar
));
1905 feuLong
game::CreateNewItemID (item
*NewItem
) {
1906 feuLong ID
= NextItemID
++;
1907 /*k8:??? if(ItemIDMap.find(ID) != ItemIDMap.end())
1908 int esko = esko = 2;*/
1909 if (NewItem
) ItemIDMap
.insert(std::make_pair(ID
, NewItem
));
1914 feuLong
game::CreateNewTrapID (entity
*NewTrap
) {
1915 feuLong ID
= NextTrapID
++;
1916 /*k8:??? if(TrapIDMap.find(ID) != TrapIDMap.end())
1917 int esko = esko = 2;*/
1918 if (NewTrap
) TrapIDMap
.insert(std::make_pair(ID
, NewTrap
));
1923 character
*game::SearchCharacter (feuLong ID
) {
1924 characteridmap::iterator Iterator
= CharacterIDMap
.find(ID
);
1925 return Iterator
!= CharacterIDMap
.end() ? Iterator
->second
: 0;
1929 item
*game::SearchItem (feuLong ID
) {
1930 itemidmap::iterator Iterator
= ItemIDMap
.find(ID
);
1931 return Iterator
!= ItemIDMap
.end() ? Iterator
->second
: 0;
1935 entity
*game::SearchTrap (feuLong ID
) {
1936 trapidmap::iterator Iterator
= TrapIDMap
.find(ID
);
1937 return Iterator
!= TrapIDMap
.end() ? Iterator
->second
: 0;
1941 outputfile
&operator << (outputfile
&SaveFile
, const configid
&Value
) {
1942 SaveFile
.Write(reinterpret_cast<cchar
*>(&Value
), sizeof(Value
));
1947 inputfile
&operator >> (inputfile
&SaveFile
, configid
&Value
) {
1948 SaveFile
.Read(reinterpret_cast<char*>(&Value
), sizeof(Value
));
1953 outputfile
&operator << (outputfile
&SaveFile
, const dangerid
&Value
) {
1954 SaveFile
<< Value
.NakedDanger
<< Value
.EquippedDanger
;
1959 inputfile
&operator >> (inputfile
&SaveFile
, dangerid
&Value
) {
1960 SaveFile
>> Value
.NakedDanger
>> Value
.EquippedDanger
;
1965 /* The program can only create directories to the deepness of one, no more... */
1966 festring
game::GetHomeDir () {
1968 Dir
<< getenv("HOME") << '/';
1973 festring
game::GetSaveDir () {
1976 Dir
<< ivanconfig::GetMyDir() << "/Save/";
1978 Dir
<< getenv("HOME") << "/IvanSave/";
1984 festring
game::GetGameDir () {
1985 /*k8! return DATADIR "/ivan/"; */
1986 /*k8! return DATADIR "/"; */
1988 Dir
<< ivanconfig::GetMyDir() << "/";
1993 festring
game::GetBoneDir () {
1994 /*k8! return LOCAL_STATE_DIR "/Bones/";*/
1997 Dir
<< ivanconfig::GetMyDir() << "/Save/Bones/";
1999 Dir
<< getenv("HOME") << "/IvanSave/Bones/";
2005 level
*game::GetLevel (int I
) {
2006 return GetCurrentDungeon()->GetLevel(I
);
2010 int game::GetLevels () {
2011 return GetCurrentDungeon()->GetLevels();
2015 void game::SignalDeath (ccharacter
*Ghost
, ccharacter
*Murderer
, festring DeathMsg
) {
2016 if (InWilderness
) DeathMsg
<< " in the world map";
2017 else DeathMsg
<< " in " << GetCurrentDungeon()->GetLevelDescription(CurrentLevelIndex
);
2018 massacremap
*MassacreMap
;
2020 ++MiscMassacreAmount
;
2021 MassacreMap
= &MiscMassacreMap
;
2022 } else if(Murderer
->IsPlayer()) {
2023 ++PlayerMassacreAmount
;
2024 MassacreMap
= &PlayerMassacreMap
;
2025 } else if(Murderer
->IsPet()) {
2026 ++PetMassacreAmount
;
2027 MassacreMap
= &PetMassacreMap
;
2029 ++MiscMassacreAmount
;
2030 MassacreMap
= &MiscMassacreMap
;
2033 massacreid
MI(Ghost
->GetType(), Ghost
->GetConfig(), Ghost
->GetAssignedName());
2034 massacremap::iterator i
= MassacreMap
->find(MI
);
2036 if (i
== MassacreMap
->end()) {
2037 i
= MassacreMap
->insert(std::make_pair(MI
, killdata(1, Ghost
->GetGenerationDanger()))).first
;
2038 i
->second
.Reason
.push_back(killreason(DeathMsg
, 1));
2041 i
->second
.DangerSum
+= Ghost
->GetGenerationDanger();
2042 std::vector
<killreason
>& Reason
= i
->second
.Reason
;
2044 for (c
= 0; c
< Reason
.size(); ++c
) {
2045 if (Reason
[c
].String
== DeathMsg
) {
2050 if (c
== Reason
.size()) Reason
.push_back(killreason(DeathMsg
, 1));
2055 void game::DisplayMassacreLists () {
2056 DisplayMassacreList(PlayerMassacreMap
, "directly by you.", PlayerMassacreAmount
);
2057 DisplayMassacreList(PetMassacreMap
, "by your allies.", PetMassacreAmount
);
2058 DisplayMassacreList(MiscMassacreMap
, "by some other reason.", MiscMassacreAmount
);
2062 struct massacresetentry
{
2063 bool operator < (const massacresetentry
&MSE
) const { return festring::IgnoreCaseCompare(Key
, MSE
.Key
); }
2066 std::vector
<festring
> Details
;
2071 void game::DisplayMassacreList (const massacremap
&MassacreMap
, cchar
*Reason
, sLong Amount
) {
2072 std::set
<massacresetentry
> MassacreSet
;
2073 festring FirstPronoun
;
2075 charactervector GraveYard
;
2077 for (massacremap::const_iterator i1
= MassacreMap
.begin(); i1
!= MassacreMap
.end(); ++i1
) {
2078 character
*Victim
= protocontainer
<character
>::GetProto(i1
->first
.Type
)->Spawn(i1
->first
.Config
);
2079 Victim
->SetAssignedName(i1
->first
.Name
);
2080 massacresetentry Entry
;
2081 GraveYard
.push_back(Victim
);
2082 Entry
.ImageKey
= AddToCharacterDrawVector(Victim
);
2083 if (i1
->second
.Amount
== 1) {
2084 Victim
->AddName(Entry
.Key
, UNARTICLED
);
2085 Victim
->AddName(Entry
.String
, INDEFINITE
);
2087 Victim
->AddName(Entry
.Key
, PLURAL
);
2088 Entry
.String
<< i1
->second
.Amount
<< ' ' << Entry
.Key
;
2091 FirstPronoun
= Victim
->GetSex() == UNDEFINED
? "it" : Victim
->GetSex() == MALE
? "he" : "she";
2094 const std::vector
<killreason
>& Reason
= i1
->second
.Reason
;
2095 std::vector
<festring
>& Details
= Entry
.Details
;
2096 if (Reason
.size() == 1) {
2098 if (Reason
[0].Amount
== 1) Begin
= "";
2099 else if(Reason
[0].Amount
== 2) Begin
= "both ";
2100 else Begin
= "all ";
2101 Details
.push_back(Begin
+ Reason
[0].String
);
2103 for (uInt c
= 0; c
< Reason
.size(); ++c
) Details
.push_back(CONST_S("")+Reason
[c
].Amount
+' '+Reason
[c
].String
);
2104 std::sort(Details
.begin(), Details
.end(), ignorecaseorderer());
2106 MassacreSet
.insert(Entry
);
2108 sLong Total
= PlayerMassacreAmount
+PetMassacreAmount
+MiscMassacreAmount
;
2110 if (Total
== 1) MainTopic
<< "One creature perished during your adventure.";
2111 else MainTopic
<< Total
<< " creatures perished during your adventure.";
2112 felist
List(MainTopic
);
2113 SetStandardListAttributes(List
);
2114 List
.SetPageLength(15);
2115 List
.AddFlags(SELECTABLE
);
2116 List
.SetEntryDrawer(CharacterEntryDrawer
);
2117 List
.AddDescription(CONST_S(""));
2119 if (Amount
!= Total
) {
2120 SideTopic
= CONST_S("The following ");
2121 if (Amount
== 1) SideTopic
<< "one was killed " << Reason
;
2122 else SideTopic
<< Amount
<< " were killed " << Reason
;
2125 FirstPronoun
.Capitalize();
2126 SideTopic
<< FirstPronoun
<< " was killed " << Reason
;
2127 } else SideTopic
<< "They were all killed " << Reason
;
2129 List
.AddDescription(SideTopic
);
2130 List
.AddDescription(CONST_S(""));
2131 List
.AddDescription("Choose a type of creatures to browse death details.");
2132 std::set
<massacresetentry
>::const_iterator i2
;
2133 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
) List
.AddEntry(i2
->String
, LIGHT_GRAY
, 0, i2
->ImageKey
);
2135 int Chosen
= List
.Draw();
2136 if (Chosen
& FELIST_ERROR_BIT
) break;
2137 felist
SubList(CONST_S("Massacre details"));
2138 SetStandardListAttributes(SubList
);
2139 SubList
.SetPageLength(20);
2141 for (i2
= MassacreSet
.begin(); i2
!= MassacreSet
.end(); ++i2
, ++Counter
) {
2142 if (Counter
== Chosen
) {
2143 for (uInt c
= 0; c
< i2
->Details
.size(); ++c
) SubList
.AddEntry(i2
->Details
[c
], LIGHT_GRAY
);
2149 ClearCharacterDrawVector();
2150 for (uInt c
= 0; c
< GraveYard
.size(); ++c
) delete GraveYard
[c
];
2154 truth
game::MassacreListsEmpty () {
2155 return PlayerMassacreMap
.empty() && PetMassacreMap
.empty() && MiscMassacreMap
.empty();
2160 void game::SeeWholeMap () {
2161 if (SeeWholeMapCheatMode
< 2) ++SeeWholeMapCheatMode
; else SeeWholeMapCheatMode
= 0;
2162 GetCurrentArea()->SendNewDrawRequest();
2167 void game::CreateBone () {
2168 if (!WizardModeIsActive() && !IsInWilderness() && RAND() & 3 && GetCurrentLevel()->PreProcessForBone()) {
2171 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2172 BoneName
= GetBoneDir()+"bon"+CurrentDungeonIndex
+CurrentLevelIndex
+BoneIndex
;
2173 if (!inputfile::fileExists(BoneName
)) break;
2175 if (BoneIndex
!= 1000) {
2176 //festring BoneName = GetBoneDir()+"bon"+CurrentDungeonIndex+CurrentLevelIndex+BoneIndex;
2177 fprintf(stderr
, "creating bone file: [%s]\n", BoneName
.CStr());
2178 outputfile
BoneFile(BoneName
);
2179 BoneFile
<< int(BONE_FILE_VERSION
) << PlayerName
<< CurrentLevel
;
2185 truth
game::PrepareRandomBone (int LevelIndex
) {
2186 if (/*k8:WizardModeIsActive() ||*/ GetCurrentDungeon()->IsGenerated(LevelIndex
) || !*GetCurrentDungeon()->GetLevelScript(LevelIndex
)->CanGenerateBone()) return false;
2189 for (BoneIndex
= 0; BoneIndex
< 1000; ++BoneIndex
) {
2190 BoneName
= GetBoneDir()+"bon"+CurrentDungeonIndex
+LevelIndex
+BoneIndex
;
2191 inputfile
BoneFile(BoneName
, 0, false);
2192 if (BoneFile
.IsOpen() && !(RAND() & 7)) {
2193 if (ReadType
<int>(BoneFile
) != BONE_FILE_VERSION
) {
2195 remove(BoneName
.CStr());
2200 level
*NewLevel
= GetCurrentDungeon()->LoadLevel(BoneFile
, LevelIndex
);
2201 if (!NewLevel
->PostProcessForBone()) {
2203 GetBoneItemIDMap().clear();
2204 GetBoneCharacterIDMap().clear();
2207 NewLevel
->FinalProcessForBone();
2208 GetBoneItemIDMap().clear();
2209 GetBoneCharacterIDMap().clear();
2210 SetCurrentArea(NewLevel
);
2211 CurrentLevel
= NewLevel
;
2212 CurrentLSquareMap
= NewLevel
->GetMap();
2213 GetCurrentDungeon()->SetIsGenerated(LevelIndex
, true);
2214 if (Name
== PlayerName
) ADD_MESSAGE("This place is oddly familiar. Like you had been here in one of your past lives.");
2215 else ADD_MESSAGE("You smell the stench of death.");
2220 if (BoneIndex
!= 1000) {
2221 remove(BoneName
.CStr());
2228 double game::CalculateAverageDanger (const charactervector
&EnemyVector
, character
*Char
) {
2229 double DangerSum
= 0;
2231 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) {
2232 DangerSum
+= EnemyVector
[c
]->GetRelativeDanger(Char
, true);
2235 return DangerSum
/Enemies
;
2239 double game::CalculateAverageDangerOfAllNormalEnemies () {
2240 double DangerSum
= 0;
2242 for (int c1
= 1; c1
< protocontainer
<character
>::GetSize(); ++c1
) {
2243 const character::prototype
*Proto
= protocontainer
<character
>::GetProto(c1
);
2244 const character::database
*const *ConfigData
= Proto
->GetConfigData();
2245 int ConfigSize
= Proto
->GetConfigSize();
2246 for (int c2
= 0; c2
< ConfigSize
; ++c2
) {
2247 if (!ConfigData
[c2
]->IsAbstract
&& !ConfigData
[c2
]->IsUnique
&& ConfigData
[c2
]->CanBeGenerated
) {
2248 DangerSum
+= DangerMap
.find(configid(c1
, ConfigData
[c2
]->Config
))->second
.EquippedDanger
;
2253 return DangerSum
/Enemies
;
2257 character
*game::CreateGhost () {
2258 double AverageDanger
= CalculateAverageDangerOfAllNormalEnemies();
2259 charactervector EnemyVector
;
2260 protosystem::CreateEveryNormalEnemy(EnemyVector
);
2261 ghost
*Ghost
= ghost::Spawn();
2262 Ghost
->SetTeam(GetTeam(MONSTER_TEAM
));
2263 Ghost
->SetGenerationDanger(CurrentLevel
->GetDifficulty());
2264 Ghost
->SetOwnerSoul(PlayerName
);
2265 Ghost
->SetIsActive(false);
2266 Ghost
->EditAllAttributes(-4);
2267 Player
->SetSoulID(Ghost
->GetID());
2268 while (CalculateAverageDanger(EnemyVector
, Ghost
) > AverageDanger
&& Ghost
->EditAllAttributes(1));
2269 for (uInt c
= 0; c
< EnemyVector
.size(); ++c
) delete EnemyVector
[c
];
2274 int game::GetMoveCommandKey (int I
) {
2275 if (!ivanconfig::GetUseAlternativeKeys()) return MoveNormalCommandKey
[I
];
2276 return MoveAbnormalCommandKey
[I
];
2280 sLong
game::GetScore () {
2282 massacremap::const_iterator i
;
2283 massacremap SumMap
= PlayerMassacreMap
;
2284 for (i
= PetMassacreMap
.begin(); i
!= PetMassacreMap
.end(); ++i
) {
2285 killdata
&KillData
= SumMap
[i
->first
];
2286 KillData
.Amount
+= i
->second
.Amount
;
2287 KillData
.DangerSum
+= i
->second
.DangerSum
;
2289 for (i
= SumMap
.begin(); i
!= SumMap
.end(); ++i
) {
2290 character
*Char
= protocontainer
<character
>::GetProto(i
->first
.Type
)->Spawn(i
->first
.Config
);
2291 int SumOfAttributes
= Char
->GetSumOfAttributes();
2292 Counter
+= sqrt(i
->second
.DangerSum
/ DEFAULT_GENERATION_DANGER
) * SumOfAttributes
* SumOfAttributes
;
2295 return sLong(0.01*Counter
);
2299 /* Only works if New Attnam is loaded */
2300 truth
game::TweraifIsFree () {
2301 for (std::list
<character
*>::const_iterator i
= GetTeam(COLONIST_TEAM
)->GetMember().begin(); i
!= GetTeam(COLONIST_TEAM
)->GetMember().end(); ++i
)
2302 if ((*i
)->IsEnabled()) return false;
2307 // returns true if date is christmaseve or day
2308 truth
game::IsXMas () {
2309 time_t Time
= time(0);
2310 struct tm
*TM
= localtime(&Time
);
2311 return (TM
->tm_mon
== 11 && (TM
->tm_mday
== 24 || TM
->tm_mday
== 25));
2315 int game::AddToItemDrawVector (const itemvector
&What
) {
2316 ItemDrawVector
.push_back(What
);
2317 return ItemDrawVector
.size()-1;
2321 v2 ItemDisplacement
[3][3] = {
2322 { v2(0, 0), ERROR_V2
, ERROR_V2
},
2323 { v2(-2, -2), v2(2, 2), ERROR_V2
},
2324 { v2(-4, -4), v2(0, 0), v2(4, 4) }
2328 void game::ItemEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2333 { TILE_SIZE
, TILE_SIZE
},
2334 { NORMAL_LUMINANCE
},
2338 itemvector ItemVector
= ItemDrawVector
[I
];
2339 int Amount
= Min
<int>(ItemVector
.size(), 3);
2340 for (int c
= 0; c
< Amount
; ++c
) {
2341 v2 Displacement
= ItemDisplacement
[Amount
-1][c
];
2342 if (!ItemVector
[0]->HasNormalPictureDirection()) Displacement
.X
= -Displacement
.X
;
2343 B
.Dest
= Pos
+Displacement
;
2344 if (ItemVector
[c
]->AllowAlphaEverywhere()) B
.CustomData
|= ALLOW_ALPHA
;
2345 ItemVector
[c
]->Draw(B
);
2346 B
.CustomData
&= ~ALLOW_ALPHA
;
2348 if (ItemVector
.size() > 3) {
2351 B
.Dest
= ItemVector
[0]->HasNormalPictureDirection() ? Pos
+v2(11, -2) : Pos
+v2(-2, -2);
2353 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2358 int game::AddToCharacterDrawVector (character
*What
) {
2359 CharacterDrawVector
.push_back(What
);
2360 return CharacterDrawVector
.size()-1;
2364 void game::CharacterEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2365 if (CharacterDrawVector
[I
]) {
2370 { TILE_SIZE
, TILE_SIZE
},
2371 { NORMAL_LUMINANCE
},
2373 ALLOW_ANIMATE
|ALLOW_ALPHA
2375 CharacterDrawVector
[I
]->DrawBodyParts(B
);
2380 void game::GodEntryDrawer (bitmap
*Bitmap
, v2 Pos
, uInt I
) {
2385 { TILE_SIZE
, TILE_SIZE
},
2390 igraph::GetSymbolGraphic()->NormalMaskedBlit(B
);
2394 character
*game::GetSumo () {
2395 return GetCurrentLevel()->GetLSquare(SUMO_ROOM_POS
)->GetRoom()->GetMaster();
2399 truth
game::TryToEnterSumoArena () {
2400 character
*Sumo
= GetSumo();
2401 if (!Sumo
|| !Sumo
->IsEnabled() || Sumo
->GetRelation(Player
) == HOSTILE
|| !Player
->CanBeSeenBy(Sumo
)) return true;
2402 if (TweraifIsFree()) {
2403 ADD_MESSAGE("\"You started this stupid revolution, after which I've been constantly hungry. Get lost!\"");
2406 if (PlayerIsSumoChampion()) {
2407 ADD_MESSAGE("\"I don't really enjoy losing, especially many times to the same guy. Go away.\"");
2410 if (Player
->IsPolymorphed()) {
2411 ADD_MESSAGE("\"Don't try to cheat. Come back when you're normal again.\"");
2414 if (Player
->GetHungerState() < SATIATED
) {
2415 ADD_MESSAGE("\"Your figure is too slender for this sport. Eat a lot more and come back.\"");
2418 if (Player
->GetHungerState() < BLOATED
) {
2419 ADD_MESSAGE("\"You're still somewhat too thin. Eat some more and we'll compete.\"");
2422 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.\"");
2423 if (!TruthQuestion("Do you want to challenge him? [y/N]")) return false;
2424 SumoWrestling
= true;
2425 character
*MirrorPlayer
= Player
->Duplicate(IGNORE_PROHIBITIONS
);
2426 character
*MirrorSumo
= Sumo
->Duplicate(IGNORE_PROHIBITIONS
);
2427 SetPlayer(MirrorPlayer
);
2428 charactervector Spectators
;
2429 if (Player
->GetTeam()->GetRelation(GetTeam(TOURIST_GUIDE_TEAM
)) != HOSTILE
&&
2430 Player
->GetTeam()->GetRelation(GetTeam(TOURIST_TEAM
)) != HOSTILE
) {
2431 GetTeam(TOURIST_GUIDE_TEAM
)->MoveMembersTo(Spectators
);
2432 GetTeam(TOURIST_TEAM
)->MoveMembersTo(Spectators
);
2434 GetCurrentDungeon()->SaveLevel(SaveName(), 0);
2435 charactervector test
;
2436 EnterArea(test
, 1, STAIRS_UP
);
2437 MirrorSumo
->PutTo(SUMO_ARENA_POS
+v2(6, 5));
2438 MirrorSumo
->ChangeTeam(GetTeam(SUMO_TEAM
));
2439 GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->SetMasterID(MirrorSumo
->GetID());
2440 for (uInt c
= 0; c
< Spectators
.size(); ++c
) Spectators
[c
]->PutToOrNear(SUMO_ARENA_POS
+ v2(6, 10));
2441 throw areachangerequest();
2446 truth
game::TryToExitSumoArena () {
2447 if (GetTeam(PLAYER_TEAM
)->GetRelation(GetTeam(NEW_ATTNAM_TEAM
)) == HOSTILE
) return true;
2449 charactervector CVector
;
2450 if (IsSumoWrestling()) {
2451 if (TruthQuestion("Do you really wish to give up? [y/N]")) return EndSumoWrestling(LOST
);
2455 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2456 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2457 std::vector
<character
*> test
;
2458 EnterArea(test
, 0, STAIRS_DOWN
);
2459 Player
->GetStackUnder()->AddItems(IVector
);
2460 if (!IVector
.empty()) {
2461 character
*Sumo
= GetSumo();
2462 if (Sumo
&& Sumo
->GetRelation(Player
) != HOSTILE
&& Player
->CanBeSeenBy(Sumo
)) ADD_MESSAGE("\"Don't leave anything there, please.\"");
2464 v2 PlayerPos
= Player
->GetPos();
2465 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2466 throw areachangerequest();
2472 truth
game::EndSumoWrestling (int Result
) {
2473 msgsystem::LeaveBigMessageMode();
2474 if (Result
== LOST
) AskForKeyPress("You lose. [press any key to continue]");
2475 else if (Result
== WON
) AskForKeyPress("You win! [press any key to continue]");
2476 else if (Result
== DISQUALIFIED
) AskForKeyPress("You are disqualified! [press any key to continue]");
2477 character
*Sumo
= GetCurrentLevel()->GetLSquare(SUMO_ARENA_POS
)->GetRoom()->GetMaster();
2478 /* We'll make a throw soon so deletes are allowed */
2487 charactervector CVector
;
2488 GetCurrentLevel()->CollectEverything(IVector
, CVector
);
2489 GetCurrentDungeon()->SaveLevel(SaveName(), 1);
2490 charactervector test
;
2491 EnterArea(test
, 0, STAIRS_DOWN
);
2492 SumoWrestling
= false;
2493 Player
->GetStackUnder()->AddItems(IVector
);
2494 v2 PlayerPos
= Player
->GetPos();
2495 for (uInt c
= 0; c
< CVector
.size(); ++c
) CVector
[c
]->PutNear(PlayerPos
);
2496 if (Result
== LOST
) ADD_MESSAGE("\"I hope you've learned your lesson now!\"");
2497 else if (Result
== DISQUALIFIED
) ADD_MESSAGE("\"Don't do that again or I'll be really angry!\"");
2499 PlayerSumoChampion
= true;
2500 character
*Sumo
= GetSumo();
2501 festring Msg
= Sumo
->GetName(DEFINITE
)+" seems humbler than before. \"Darn. You bested me.\n";
2502 Msg
<< "Here's a little something as a reward\", " << Sumo
->GetPersonalPronoun() << " says and hands you a belt of levitation.\n\"";
2503 (belt::Spawn(BELT_OF_LEVITATION
))->MoveTo(Player
->GetStack());
2504 Msg
<< "Allow me to also teach you a few nasty martial art tricks the years have taught me.\"";
2505 Player
->GetCWeaponSkill(UNARMED
)->AddHit(100000);
2506 Player
->GetCWeaponSkill(KICK
)->AddHit(100000);
2507 character
*Imperialist
= GetCurrentLevel()->GetLSquare(5, 5)->GetRoom()->GetMaster();
2508 if (Imperialist
&& Imperialist
->GetRelation(Player
) != HOSTILE
) {
2509 v2 Pos
= Player
->GetPos()+v2(0, 1);
2510 GetCurrentLevel()->GetLSquare(Pos
)->KickAnyoneStandingHereAway();
2511 Imperialist
->Remove();
2512 Imperialist
->PutTo(Pos
);
2513 Msg
<< "\n\nSuddenly you notice " << Imperialist
->GetName(DEFINITE
) << " has also entered.\n"
2514 "\"I see we have a promising fighter among us. I had already heard of your\n"
2515 "adventures outside the village, but hardly could I believe that one day you\n"
2516 "would defeat even the mighty Huang Ming Pong! A hero such as you is bound\n"
2517 "to become world famous, and can earn a fortune if wealthy sponsors are behind\n"
2518 "him. May I therefore propose a mutually profitable contract: I'll give you this\n"
2519 "nice shirt with my company's ad, and you'll wear it as you journey bravely to\n"
2520 "the unknown and fight epic battles against the limitless minions of evil. I'll\n"
2521 "reward you well when you return, depending on how much you have used it.\"";
2522 Player
->GetStack()->AddItem(decosadshirt::Spawn());
2525 GetCurrentArea()->SendNewDrawRequest();
2528 Player
->EditNP(-25000);
2529 Player
->CheckStarvationDeath(CONST_S("exhausted after controlling a mirror image for too sLong"));
2530 throw areachangerequest();
2535 rain
*game::ConstructGlobalRain () {
2536 return new rain(GlobalRainLiquid
, static_cast<lsquare
*>(GetSquareInLoad()), GlobalRainSpeed
, MONSTER_TEAM
, false);
2540 v2
game::GetSunLightDirectionVector () {
2541 int Index
= Tick
% 48000 / 1000;
2542 /* Should have the same sign as sin(PI * Index / 24) and XTable[Index] /
2543 YTable[Index] should equal roughly -tan(PI * Index / 24). Also, vector
2544 (XTable[Index], YTable[Index]) + P should not be a valid position of
2545 any possible level L for any P belonging to L. */
2546 static int XTable
[48] = {
2547 0, 1000, 1000, 1000, 1000, 1000,
2548 1000, 1303, 1732, 2414, 3732, 7596,
2549 1000, 7596, 3732, 2414, 1732, 1303,
2550 1000, 1000, 1000, 1000, 1000, 1000,
2551 0, -1000, -1000, -1000, -1000, -1000,
2552 -1000, -1303, -1732, -2414, -3732, -7596,
2553 -1000, -7596, -3732, -2414, -1732, -1303,
2554 -1000, -1000, -1000, -1000, -1000, -1000 };
2555 /* Should have the same sign as -cos(PI * Index / 24) */
2556 static int YTable
[48] = { -1000, -7596, -3732, -2414, -1732, -1303,
2557 -1000, -1000, -1000, -1000, -1000, -1000,
2558 0, 1000, 1000, 1000, 1000, 1000,
2559 1000, 1303, 1732, 2414, 3732, 7596,
2560 1000, 7596, 3732, 2414, 1732, 1303,
2561 1000, 1000, 1000, 1000, 1000, 1000,
2562 0, -1000, -1000, -1000, -1000, -1000,
2563 -1000, -1303, -1732, -2414, -3732, -7596 };
2564 return v2(XTable
[Index
], YTable
[Index
]);
2568 int game::CalculateMinimumEmitationRadius (col24 E
) {
2569 int MaxElement
= Max(GetRed24(E
), GetGreen24(E
), GetBlue24(E
));
2570 return int(sqrt(double(MaxElement
<< 7) / LIGHT_BORDER
- 120.));
2574 feuLong
game::IncreaseSquarePartEmitationTicks () {
2575 if ((SquarePartEmitationTick
+= 2) == 0x100) {
2576 CurrentLevel
->InitSquarePartEmitationTicks();
2577 SquarePartEmitationTick
= 2;
2579 return SquarePartEmitationTick
;
2583 bool game::Wish (character
*Wisher
, cchar
*MsgSingle
, cchar
*MsgPair
, bool canAbort
) {
2585 festring oldDef
= DefaultWish
;
2586 festring Temp
= DefaultQuestion(CONST_S("What do you want to wish for?"), DefaultWish
);
2587 if (DefaultWish
== "nothing" && canAbort
) {
2588 DefaultWish
= oldDef
;
2591 item
*TempItem
= protosystem::CreateItem(Temp
, Wisher
->IsPlayer());
2593 Wisher
->GetStack()->AddItem(TempItem
);
2594 TempItem
->SpecialGenerationHandler();
2595 if (TempItem
->HandleInPairs()) ADD_MESSAGE(MsgPair
, TempItem
->CHAR_NAME(PLURAL
));
2596 else ADD_MESSAGE(MsgSingle
, TempItem
->CHAR_NAME(INDEFINITE
));
2603 festring
game::DefaultQuestion (festring Topic
, festring
&Default
, stringkeyhandler KeyHandler
) {
2604 festring ShortDefault
= Default
;
2605 if (Default
.GetSize() > 29) {
2606 ShortDefault
.Resize(27);
2607 ShortDefault
= ShortDefault
<< CONST_S("...");
2609 if (!Default
.IsEmpty()) Topic
<< " [" << ShortDefault
<< ']';
2610 festring Answer
= StringQuestion(Topic
, WHITE
, 0, 80, false, KeyHandler
);
2611 if (Answer
.IsEmpty()) Answer
= Default
;
2612 return Default
= Answer
;
2616 void game::GetTime (ivantime
&Time
) {
2617 Time
.Hour
= 12 + Tick
/ 2000;
2618 Time
.Day
= Time
.Hour
/ 24 + 1;
2620 Time
.Min
= Tick
% 2000 * 60 / 2000;
2624 truth
NameOrderer (character
*C1
, character
*C2
) {
2625 return festring::IgnoreCaseCompare(C1
->GetName(UNARTICLED
), C2
->GetName(UNARTICLED
));
2629 truth
game::PolymorphControlKeyHandler (int Key
, festring
&String
) {
2631 felist
List(CONST_S("List of known creatures and their intelligence requirements"));
2632 SetStandardListAttributes(List
);
2633 List
.SetPageLength(15);
2634 List
.AddFlags(SELECTABLE
);
2635 protosystem::CreateEverySeenCharacter(CharacterDrawVector
);
2636 std::sort(CharacterDrawVector
.begin(), CharacterDrawVector
.end(), NameOrderer
);
2637 List
.SetEntryDrawer(CharacterEntryDrawer
);
2638 std::vector
<festring
> StringVector
;
2640 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) {
2641 character
*Char
= CharacterDrawVector
[c
];
2642 if (Char
->CanBeWished()) {
2644 Char
->AddName(Entry
, UNARTICLED
);
2645 StringVector
.push_back(Entry
);
2646 int Req
= Char
->GetPolymorphIntelligenceRequirement();
2647 if (Char
->IsSameAs(Player
) || (Player
->GetPolymorphBackup() && Player
->GetPolymorphBackup()->IsSameAs(Char
))) Req
= 0;
2648 Entry
<< " (" << Req
<< ')';
2649 int Int
= Player
->GetAttribute(INTELLIGENCE
);
2650 List
.AddEntry(Entry
, Req
> Int
? RED
: LIGHT_GRAY
, 0, c
);
2653 int Chosen
= List
.Draw();
2654 for (c
= 0; c
< CharacterDrawVector
.size(); ++c
) delete CharacterDrawVector
[c
];
2655 if (!(Chosen
& FELIST_ERROR_BIT
)) String
= StringVector
[Chosen
];
2656 CharacterDrawVector
.clear();
2663 outputfile
&operator << (outputfile
&SaveFile
, const killdata
&Value
) {
2664 SaveFile
<< Value
.Amount
<< Value
.DangerSum
<< Value
.Reason
;
2669 inputfile
&operator >> (inputfile
&SaveFile
, killdata
&Value
) {
2670 SaveFile
>> Value
.Amount
>> Value
.DangerSum
>> Value
.Reason
;
2675 outputfile
&operator << (outputfile
&SaveFile
, const killreason
&Value
) {
2676 SaveFile
<< Value
.Amount
<< Value
.String
;
2681 inputfile
&operator >> (inputfile
&SaveFile
, killreason
&Value
) {
2682 SaveFile
>> Value
.Amount
>> Value
.String
;
2687 truth
DistanceOrderer (character
*C1
, character
*C2
) {
2688 v2 PlayerPos
= PLAYER
->GetPos();
2689 v2 Pos1
= C1
->GetPos();
2690 v2 Pos2
= C2
->GetPos();
2691 int D1
= Max(abs(Pos1
.X
- PlayerPos
.X
), abs(Pos1
.Y
- PlayerPos
.Y
));
2692 int D2
= Max(abs(Pos2
.X
- PlayerPos
.X
), abs(Pos2
.Y
- PlayerPos
.Y
));
2693 if (D1
!= D2
) return D1
< D2
;
2694 if (Pos1
.Y
!= Pos2
.Y
) return Pos1
.Y
< Pos2
.Y
;
2695 return Pos1
.X
< Pos2
.X
;
2699 truth
game::FillPetVector (cchar
*Verb
) {
2701 team
*Team
= GetTeam(PLAYER_TEAM
);
2702 for (std::list
<character
*>::const_iterator i
= Team
->GetMember().begin(); i
!= Team
->GetMember().end(); ++i
)
2703 if ((*i
)->IsEnabled() && !(*i
)->IsPlayer() && (*i
)->CanBeSeenByPlayer()) PetVector
.push_back(*i
);
2704 if (PetVector
.empty()) {
2705 ADD_MESSAGE("You don't detect any friends to %s.", Verb
);
2708 std::sort(PetVector
.begin(), PetVector
.end(), DistanceOrderer
);
2709 LastPetUnderCursor
= PetVector
[0];
2714 truth
game::CommandQuestion () {
2715 if (!FillPetVector("command")) return false;
2717 if (PetVector
.size() == 1) Char
= PetVector
[0];
2719 v2 Pos
= PetVector
[0]->GetPos();
2720 Pos
= PositionQuestion(CONST_S("Whom do you wish to command? [direction keys/'+'/'-'/'a'll/space/esc]"), Pos
, &PetHandler
, &CommandKeyHandler
);
2721 if (Pos
== ERROR_V2
) return false;
2722 if (Pos
== ABORT_V2
) return true;
2723 Char
= CurrentArea
->GetSquare(Pos
)->GetCharacter();
2724 if (!Char
|| !Char
->CanBeSeenByPlayer()) {
2725 ADD_MESSAGE("You don't see anyone here to command.");
2728 if (Char
->IsPlayer()) {
2729 ADD_MESSAGE("You do that all the time.");
2732 if (!Char
->IsPet()) {
2733 ADD_MESSAGE("%s refuses to be commanded by you.", Char
->CHAR_NAME(DEFINITE
));
2737 return Char
->IssuePetCommands();
2741 void game::NameQuestion () {
2742 if (!FillPetVector("name")) return;
2743 if (PetVector
.size() == 1) PetVector
[0]->TryToName();
2744 else PositionQuestion(CONST_S("Who do you want to name? [direction keys/'+'/'-'/'n'ame/esc]"), PetVector
[0]->GetPos(), &PetHandler
, &NameKeyHandler
);
2748 void game::PetHandler (v2 CursorPos
) {
2749 character
*Char
= CurrentArea
->GetSquare(CursorPos
)->GetCharacter();
2750 if (Char
&& Char
->CanBeSeenByPlayer() && Char
->IsPet() && !Char
->IsPlayer()) CursorData
= RED_CURSOR
|CURSOR_TARGET
;
2751 else CursorData
= RED_CURSOR
;
2752 if (Char
&& !Char
->IsPlayer() && Char
->IsPet()) LastPetUnderCursor
= Char
;
2756 v2
game::CommandKeyHandler (v2 CursorPos
, int Key
) {
2757 if (SelectPet(Key
)) return LastPetUnderCursor
->GetPos();
2758 if (Key
== 'a' || Key
== 'A') return CommandAll() ? ABORT_V2
: ERROR_V2
;
2763 truth
game::SelectPet (int Key
) {
2765 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2766 if (PetVector
[c
] == LastPetUnderCursor
) {
2767 if (++c
== PetVector
.size()) c
= 0;
2768 LastPetUnderCursor
= PetVector
[c
];
2772 } else if (Key
== '-') {
2773 for (uInt c
= 0; c
< PetVector
.size(); ++c
) {
2774 if (PetVector
[c
] == LastPetUnderCursor
) {
2775 if (!c
) c
= PetVector
.size();
2776 LastPetUnderCursor
= PetVector
[--c
];
2785 void game::CommandScreen (cfestring
&Topic
, feuLong PossibleFlags
, feuLong ConstantFlags
, feuLong
&VaryFlags
, feuLong
&Flags
) {
2786 static cchar
*CommandDescription
[COMMAND_FLAGS
] = {
2788 "Flee from enemies",
2789 "Don't change your equipment",
2790 "Don't consume anything valuable"
2793 SetStandardListAttributes(List
);
2794 List
.AddFlags(SELECTABLE
);
2795 List
.AddDescription(CONST_S(""));
2796 List
.AddDescription(CONST_S("Command Active?"));
2799 for (c
= 0; c
< COMMAND_FLAGS
; ++c
) {
2800 if (1 << c
& PossibleFlags
) {
2801 truth Changeable
= !(1 << c
& ConstantFlags
);
2804 Entry
= CommandDescription
[c
];
2807 Entry
<< " " << CommandDescription
[c
];
2810 if (1 << c
& VaryFlags
) Entry
<< "varies"; else Entry
<< (1 << c
& Flags
? "yes" : "no");
2811 List
.AddEntry(Entry
, Changeable
? LIGHT_GRAY
: DARK_GRAY
, 0, NO_IMAGE
, Changeable
);
2814 int Chosen
= List
.Draw();
2815 if (Chosen
& FELIST_ERROR_BIT
) return;
2816 for (c
= 0, i
= 0; c
< COMMAND_FLAGS
; ++c
) {
2817 if (1 << c
& PossibleFlags
&& !(1 << c
& ConstantFlags
) && i
++ == Chosen
) {
2818 if (1 << c
& VaryFlags
) {
2819 VaryFlags
&= ~(1 << c
);
2821 } else Flags
^= 1 << c
;
2826 DrawEverythingNoBlit();
2831 truth
game::CommandAll () {
2832 feuLong PossibleFlags
= 0, ConstantFlags
= ALL_COMMAND_FLAGS
, VaryFlags
= 0, OldFlags
= 0;
2834 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2835 ConstantFlags
&= PetVector
[c1
]->GetConstantCommandFlags();
2836 feuLong C
= PetVector
[c1
]->GetCommandFlags();
2837 feuLong ThisPossible
= PetVector
[c1
]->GetPossibleCommandFlags();
2838 for (c2
= 0; c2
< COMMAND_FLAGS
; ++c2
)
2839 if (1 << c2
& PossibleFlags
& ThisPossible
&& (1 << c2
& C
) != (1 << c2
& OldFlags
)) VaryFlags
|= 1 << c2
;
2840 PossibleFlags
|= ThisPossible
;
2841 OldFlags
|= C
& ThisPossible
;
2843 if (!PossibleFlags
) {
2844 ADD_MESSAGE("Not a single creature in your visible team can be commanded.");
2847 feuLong NewFlags
= OldFlags
;
2848 CommandScreen(CONST_S("Issue commands to whole visible team"), PossibleFlags
, ConstantFlags
, VaryFlags
, NewFlags
);
2849 truth Change
= false;
2850 for (c1
= 0; c1
< PetVector
.size(); ++c1
) {
2851 character
*Char
= PetVector
[c1
];
2852 if (!Char
->IsConscious()) continue;
2853 feuLong OldC
= Char
->GetCommandFlags();
2854 feuLong ConstC
= Char
->GetConstantCommandFlags();
2855 feuLong ThisC
= (NewFlags
& Char
->GetPossibleCommandFlags() & ~(ConstC
|VaryFlags
)) | (OldC
& (ConstC
|VaryFlags
));
2856 if (ThisC
!= OldC
) Change
= true;
2857 Char
->SetCommandFlags(ThisC
);
2859 if (!Change
) return false;
2860 Player
->EditAP(-500);
2861 Player
->EditExperience(CHARISMA
, 50, 1 << 7);
2866 col16
game::GetAttributeColor (int I
) {
2867 int Delta
= GetTick()-LastAttributeChangeTick
[I
];
2868 if (OldAttribute
[I
] == NewAttribute
[I
] || Delta
>= 510) return WHITE
;
2869 if (OldAttribute
[I
] < NewAttribute
[I
]) return MakeRGB16(255, 255, Delta
>> 1);
2870 return MakeRGB16(255, Delta
>> 1, Delta
>> 1);
2874 void game::UpdateAttributeMemory () {
2875 for (int c
= 0; c
< ATTRIBUTES
; ++c
) {
2876 int A
= Player
->GetAttribute(c
);
2877 if (A
!= NewAttribute
[c
]) {
2878 OldAttribute
[c
] = NewAttribute
[c
];
2879 NewAttribute
[c
] = A
;
2880 LastAttributeChangeTick
[c
] = GetTick();
2886 void game::InitAttributeMemory () {
2887 for (int c
= 0; c
< ATTRIBUTES
; ++c
) OldAttribute
[c
] = NewAttribute
[c
] = Player
->GetAttribute(c
);
2891 void game::TeleportHandler (v2 CursorPos
) {
2892 if ((CursorPos
-Player
->GetPos()).GetLengthSquare() > Player
->GetTeleportRangeSquare())
2893 CursorData
= BLUE_CURSOR
|CURSOR_TARGET
;
2895 CursorData
= RED_CURSOR
|CURSOR_TARGET
;
2899 double game::GetGameSituationDanger () {
2900 double SituationDanger
= 0;
2901 character
*Player
= GetPlayer();
2902 truth PlayerStuck
= Player
->IsStuck();
2903 v2 PlayerPos
= Player
->GetPos();
2904 character
*TruePlayer
= Player
;
2905 if (PlayerStuck
) (Player
= Player
->Duplicate(IGNORE_PROHIBITIONS
))->ChangeTeam(0);
2906 for (int c1
= 0; c1
< GetTeams(); ++c1
)
2907 if (GetTeam(c1
)->GetRelation(GetTeam(PLAYER_TEAM
)) == HOSTILE
)
2908 for (std::list
<character
*>::const_iterator i1
= GetTeam(c1
)->GetMember().begin(); i1
!= GetTeam(c1
)->GetMember().end(); ++i1
) {
2909 character
*Enemy
= *i1
;
2910 if (Enemy
->IsEnabled() && Enemy
->CanAttack() && (Enemy
->CanMove() || Enemy
->GetPos().IsAdjacent(PlayerPos
))) {
2911 truth EnemyStuck
= Enemy
->IsStuck();
2912 v2 EnemyPos
= Enemy
->GetPos();
2913 truth Sees
= TruePlayer
->CanBeSeenBy(Enemy
);
2914 character
*TrueEnemy
= Enemy
;
2915 if (EnemyStuck
) Enemy
= Enemy
->Duplicate(IGNORE_PROHIBITIONS
);
2916 double PlayerTeamDanger
= 1/Enemy
->GetSituationDanger(Player
, EnemyPos
, PlayerPos
, Sees
);
2917 for (int c2
= 0; c2
< GetTeams(); ++c2
)
2918 if (GetTeam(c2
)->GetRelation(GetTeam(c1
)) == HOSTILE
)
2919 for (std::list
<character
*>::const_iterator i2
= GetTeam(c2
)->GetMember().begin(); i2
!= GetTeam(c2
)->GetMember().end(); ++i2
) {
2920 character
*Friend
= *i2
;
2921 if (Friend
->IsEnabled() && !Friend
->IsPlayer() && Friend
->CanAttack() && (Friend
->CanMove() || Friend
->GetPos().IsAdjacent(EnemyPos
))) {
2922 v2 FriendPos
= Friend
->GetPos();
2923 truth Sees
= TrueEnemy
->CanBeSeenBy(Friend
);
2924 if (Friend
->IsStuck()) {
2925 Friend
= Friend
->Duplicate(IGNORE_PROHIBITIONS
);
2926 PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
) * .2;
2928 } else PlayerTeamDanger
+= Friend
->GetSituationDanger(Enemy
, FriendPos
, EnemyPos
, Sees
);
2932 PlayerTeamDanger
*= 5;
2935 SituationDanger
+= 1 / PlayerTeamDanger
;
2938 Player
->ModifySituationDanger(SituationDanger
);
2940 SituationDanger
*= 2;
2943 return SituationDanger
;
2947 sLong
game::GetTimeSpent () {
2948 return time::TimeAdd(time::TimeDifference(time(0),LastLoad
), TimePlayedBeforeLastLoad
);
2952 outputfile
&operator << (outputfile
&SaveFile
, const massacreid
&MI
) {
2953 SaveFile
<< MI
.Type
<< MI
.Config
<< MI
.Name
;
2958 inputfile
&operator >> (inputfile
&SaveFile
, massacreid
&MI
) {
2959 SaveFile
>> MI
.Type
>> MI
.Config
>> MI
.Name
;
2964 truth
game::PlayerIsRunning () {
2965 return PlayerRunning
&& Player
->CanMove();
2969 void game::AddSpecialCursor (v2 Pos
, int Data
) {
2970 SpecialCursorPos
.push_back(Pos
);
2971 SpecialCursorData
.push_back(Data
);
2975 void game::RemoveSpecialCursors () {
2976 SpecialCursorPos
.clear();
2977 SpecialCursorData
.clear();
2981 void game::LearnAbout (god
*Who
) {
2982 Who
->SetIsKnown(true);
2983 /* slightly slow, but doesn't matter since this is run so rarely */
2984 if (PlayerKnowsAllGods() && !game::PlayerHasReceivedAllGodsKnownBonus
) {
2985 GetPlayer()->ApplyAllGodsKnownBonus();
2986 game::PlayerHasReceivedAllGodsKnownBonus
= true;
2991 truth
game::PlayerKnowsAllGods () {
2992 for (int c
= 1; c
<= GODS
; ++c
) if (!GetGod(c
)->IsKnown()) return false;
2997 void game::AdjustRelationsToAllGods (int Amount
) {
2998 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->AdjustRelation(Amount
);
3002 void game::SetRelationsToAllGods (int Amount
) {
3003 for (int c
= 1; c
<= GODS
; ++c
) GetGod(c
)->SetRelation(Amount
);
3007 void game::ShowDeathSmiley (bitmap
*Buffer
, truth
) {
3008 static blitdata B
= {
3011 { (RES
.X
>> 1) - 24, RES
.Y
* 4 / 7 - 24 },
3017 int Tick
= globalwindowhandler::UpdateTick();
3018 if (((Tick
>> 1) & 31) == 1) B
.Src
.X
= 48;
3019 else if (((Tick
>> 1) & 31) == 2) B
.Src
.X
= 96;
3022 igraph::GetSmileyGraphic()->NormalBlit(B
);
3023 if (Buffer
== DOUBLE_BUFFER
) graphics::BlitDBToScreen();
3027 static int doListSelector (felist
&list
, int defsel
, int cnt
) {
3028 game::SetStandardListAttributes(list
);
3029 list
.AddFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3030 if (defsel
> 0) list
.SetSelected(defsel
);
3031 uInt sel
= list
.Draw();
3033 list
.RemoveFlags(SELECTABLE
| FELIST_NO_BADKEY_EXIT
);
3034 if (sel
& FELIST_ERROR_BIT
) return -1;
3035 if (sel
>= (uInt
)cnt
) return -1;
3040 int game::ListSelector (int defsel
, cfestring
&title
, ...) {
3043 va_start(items
, title
);
3047 const char *s
= va_arg(items
, const char *);
3049 list
.AddEntry(s
, LIGHT_GRAY
);
3053 return doListSelector(list
, defsel
, cnt
);
3057 int game::ListSelectorArray (int defsel
, cfestring
&title
, const char *items
[]) {
3061 if (!items
[cnt
]) break;
3062 list
.AddEntry(items
[cnt
], LIGHT_GRAY
);
3065 return doListSelector(list
, defsel
, cnt
);
3069 void game::ClearEventData () {
3077 // '.': string or number
3080 // '*': collect all args
3081 int game::ParseFuncArgs (cfestring
&types
, std::vector
<FuncArg
> &args
, inputfile
*fl
, truth noterm
) {
3085 if (!fl
) fl
= mFEStack
.top();
3087 for (unsigned int f
= 0; f
< types
.GetSize(); f
++) {
3090 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3091 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3094 n
= fl
->ReadNumber(0xFF, true);
3095 args
.push_back(FuncArg(n
));
3099 s
= fl
->ReadStringOrNumber(&n
, &isStr
, true);
3100 if (isStr
) args
.push_back(FuncArg(s
)); else args
.push_back(FuncArg(n
));
3101 fl
->ReadWord(s
, true);
3102 if (s
== ";") return args
.size();
3103 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TellLine());
3108 s
= fl
->ReadWord(true);
3109 args
.push_back(FuncArg(s
));
3112 if (f
== types
.GetSize()-1) {
3114 fl
->ReadWord(s
, true);
3115 if (s
!= ";") ABORT("';' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TellLine());
3118 fl
->ReadWord(s
, true);
3119 if (s
!= ",") ABORT("',' expected in file %s line %d!", fl
->GetFileName().CStr(), fl
->TellLine());
3126 truth
game::GetWord (festring
&w
) {
3128 inputfile
*fl
= mFEStack
.top();
3129 fl
->ReadWord(w
, false);
3130 if (w
== "" && fl
->Eof()) {
3133 if (mFEStack
.empty()) return false;
3136 if (w
== "Include") {
3137 fl
->ReadWord(w
, true);
3138 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TellLine());
3139 w
= game::GetGameDir()+"Script/"+w
;
3140 inputfile
*fl
= new inputfile(w
, &game::GetGlobalValueMap(), true);
3141 fl
->setGetVarCB(game::ldrGetVar
);
3145 if (w
== "Message") {
3146 fl
->ReadWord(w
, true);
3147 if (fl
->ReadWord() != ";") ABORT("Invalid terminator in file %s at line %d!", fl
->GetFileName().CStr(), fl
->TellLine());
3148 fprintf(stderr
, "MESSAGE: %s\n", w
.CStr());
3156 void game::SkipBlock (truth brcEaten
) {
3159 mFEStack
.top()->ReadWord(w
, true);
3160 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3164 mFEStack
.top()->ReadWord(w
, true);
3165 if (w
== "{") cnt
++;
3166 else if (w
== "}") {
3167 if (--cnt
< 1) break;
3173 truth
game::DoOnEvent (truth brcEaten
, truth AllowScript
) {
3174 // do; only funcalls for now
3175 truth eaten
= AllowScript
? true : false;
3178 mFEStack
.top()->ReadWord(w
, true);
3179 if (w
!= "{") ABORT("'{' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3183 if (AllowScript
) break;
3184 ABORT("Unexpected end of file %s!", mFEStack
.top()->GetFileName().CStr());
3186 //fprintf(stderr, " :[%s]\n", w.CStr());
3188 if (AllowScript
) ABORT("Unexpected '}' in AllowScript file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3191 if (w
== ";") continue;
3193 mFEStack
.top()->ReadWord(w
, true);
3194 if (mFEStack
.top()->ReadWord(true) != "=") ABORT("'=' expected in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3195 //fprintf(stderr, "setvar: %s\n", w.CStr());
3197 sLong n
= mFEStack
.top()->ReadNumber(true);
3199 if (mChar
) mChar
->SetMoney(n
);
3202 if (w
== "result") {
3203 mResult
= mFEStack
.top()->ReadNumber(true);
3206 ABORT("Unknown var [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3208 //mFEStack.top()->ReadWord(w, true);
3209 std::vector
<FuncArg
> args
;
3210 //fprintf(stderr, "funcall: %s\n", w.CStr());
3211 if (w
== "SetMoney") {
3212 ParseFuncArgs("n", args
);
3213 sLong n
= args
[0].ival
;
3215 if (mChar
) mChar
->SetMoney(n
);
3218 if (w
== "EditMoney") {
3219 ParseFuncArgs("n", args
);
3220 sLong n
= args
[0].ival
;
3221 if (mChar
) mChar
->EditMoney(n
);
3224 if (w
== "AddMessage") {
3225 ParseFuncArgs("*", args
);
3227 for (uInt f
= 0; f
< args
.size(); f
++) {
3228 const FuncArg
&a
= args
[f
];
3229 if (a
.type
== FARG_STRING
) s
<< a
.sval
; else s
<< a
.ival
;
3231 ADD_MESSAGE("%s", s
.CStr());
3234 if (w
== "EatThisEvent") {
3235 if (AllowScript
) ABORT("'EatThisEvent' forbidden in AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3239 if (w
== "Disallow") {
3240 if (!AllowScript
) ABORT("'Disallow' forbidden in not-AllowScripts in file %s at line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3244 ABORT("Unknown function [%s] in file %s at line %d!", w
.CStr(), mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3245 //if (mFEStack.top()->ReadWord() != ";") ABORT("';' expected in file %s line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TellLine());
3247 //ABORT("Invalid term in file %s at line %d!", mFEStack.top()->GetFileName().CStr(), mFEStack.top()->TellLine());
3249 //fprintf(stderr, "------------\n");
3254 //TODO: cache event scripts
3255 truth
game::RunOnEvent (cfestring
&ename
) {
3256 static std::vector
<festring
> scriptFiles
;
3257 static truth cached
= false;
3260 character
*old
= mChar
;
3264 for (int fno
= 99; fno
>= -1; fno
--) {
3266 cfname
<< game::GetGameDir() << "Script/onevent";
3269 sprintf(bnum
, "_%02d", fno
);
3273 if (!inputfile::fileExists(cfname
)) continue;
3274 inputfile
*ifl
= new inputfile(cfname
, &game::GetGlobalValueMap(), false);
3275 if (!ifl
->IsOpen()) {
3279 scriptFiles
.push_back(cfname
);
3280 ifl
->setGetVarCB(game::ldrGetVar
);
3284 for (unsigned int f
= 0; f
< scriptFiles
.size(); ++f
) {
3285 festring cfname
= scriptFiles
[f
];
3286 inputfile
*ifl
= new inputfile(cfname
, &game::GetGlobalValueMap(), false);
3287 if (!ifl
->IsOpen()) {
3291 ifl
->setGetVarCB(game::ldrGetVar
);
3297 while (GetWord(w
)) {
3298 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3299 mFEStack
.top()->ReadWord(w
, true);
3300 truth doIt
= (w
==ename
);
3302 res
= DoOnEvent(false);
3313 truth
game::RunOnEventStr (cfestring
&ename
, cfestring
&str
) {
3315 if (str
.GetSize() < 1) return false;
3316 //fprintf(stderr, "=============\n%s=============\n", str.CStr());
3317 inputfile
*ifl
= new meminputfile(str
, &game::GetGlobalValueMap());
3318 ifl
->setGetVarCB(game::ldrGetVar
);
3321 //fprintf(stderr, "=============\n", str.CStr());
3322 //fprintf(stderr, "event: [%s]\n", ename.CStr());
3323 //fprintf(stderr, "---\n%s---\n", str.CStr());
3324 while (GetWord(w
)) {
3325 if (w
!= "on") ABORT("'on' expected in file %s line %d!", mFEStack
.top()->GetFileName().CStr(), mFEStack
.top()->TellLine());
3326 mFEStack
.top()->ReadWord(w
, true);
3327 //fprintf(stderr, "on: [%s]\n", w.CStr());
3328 truth doIt
= (w
==ename
);
3330 //fprintf(stderr, " do it\n");
3331 res
= DoOnEvent(false);
3334 //fprintf(stderr, " skip it\n");
3342 truth
game::RunOnCharEvent (character
*who
, cfestring
&ename
) {
3344 if (!who
) return false;
3345 character
*old
= mChar
;
3347 res
= RunOnEventStr(ename
, who
->mOnEvents
);
3348 if (!res
) res
= RunOnEventStr(ename
, who
->GetProtoType()->mOnEvents
);
3354 truth
game::RunOnItemEvent (item
*what
, cfestring
&ename
) {
3356 if (!what
) return false;
3359 res
= RunOnEventStr(ename
, what
->mOnEvents
);
3360 if (!res
) res
= RunOnEventStr(ename
, what
->GetProtoType()->mOnEvents
);
3366 festring
game::ldrGetVar (inputfile
*fl
, cfestring
&name
) {
3367 //fprintf(stderr, "GETVAR: [%s]\n", name.CStr());
3368 if (name
== "player_name") {
3369 return game::GetPlayerName();
3371 if (name
== "money") {
3373 if (!mChar
) return "0";
3374 res
<< mChar
->GetMoney();
3377 if (name
== "name") {
3378 if (!mChar
) return "";
3379 return mChar
->GetAssignedName();
3381 if (name
== "team") {
3383 if (!mChar
) return "";
3384 res
<< mChar
->GetTeam()->GetID();
3387 if (name
== "friendly") {
3389 if (!mChar
|| !PLAYER
|| mChar
->GetRelation(PLAYER
) != HOSTILE
) return "tan";
3392 if (name
== "hostile") {
3394 if (!mChar
|| !PLAYER
) return "";
3395 if (mChar
->GetRelation(PLAYER
) == HOSTILE
) return "tan";
3398 if (name
== "has_item") {
3399 std::vector
<FuncArg
> args
;
3400 ParseFuncArgs("s", args
, fl
, true);
3404 festring s
= args
[0].sval
;
3406 //fprintf(stderr, "looking for [%s]\n", s.CStr());
3407 PLAYER
->GetStack()->FillItemVector(items
);
3408 for (unsigned int f
= 0; f
< items
.size(); ++f
) {
3409 for (uInt c
= 0; c
< items
[f
]->GetDataBase()->Alias
.Size
; ++c
) {
3410 //fprintf(stderr, "%u:%u: [%s]\n", f, c, items[f]->GetDataBase()->Alias[c].CStr());
3411 if (s
.CompareIgnoreCase(items
[f
]->GetDataBase()->Alias
[c
]) == 0) {
3412 //fprintf(stderr, " FOUND!\n");
3417 //fprintf(stderr, "checking equipment...\n");
3418 for (int f
= 0; f
< PLAYER
->GetEquipments(); ++f
) {
3419 item
*it
= PLAYER
->GetEquipment(f
);
3422 for (uInt c
= 0; c
< it
->GetDataBase()->Alias
.Size
; ++c
) {
3423 //fprintf(stderr, "%u:%u: [%s]\n", f, c, it->GetDataBase()->Alias[c].CStr());
3424 if (s
.CompareIgnoreCase(it
->GetDataBase()->Alias
[c
]) == 0) {
3425 //fprintf(stderr, " FOUND!\n");
3434 //if (name == "type") return mVarType;
3435 ABORT("unknown variable: %s", name
.CStr());
3440 truth
game::CheckDropLeftover (item
*i
) {
3441 if (i
->IsBottle() && !ivanconfig::GetAutoDropBottles()) return false;
3442 if (i
->IsCan() && !ivanconfig::GetAutoDropCans()) return false;
3443 if (!ivanconfig::GetAutoDropLeftOvers()) return false;
3448 truth
game::RunAllowScriptStr (cfestring
&str
) {
3450 if (str
.GetSize() < 1) return true;
3451 //fprintf(stderr, "====\n%s\n====\n", str.CStr());
3452 inputfile
*ifl
= new meminputfile(str
, &game::GetGlobalValueMap());
3453 ifl
->setGetVarCB(game::ldrGetVar
);
3455 res
= DoOnEvent(true, true);
3456 //fprintf(stderr, "mFEStack: %u\n", mFEStack.size());