1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5
6 This file is part of NBlood.
7
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include "build.h"
24 #include "mmulti.h"
25 #include "compat.h"
26 #include "keyboard.h"
27 #include "control.h"
28 #include "function.h"
29 #include "common_game.h"
30 #include "blood.h"
31 #include "config.h"
32 #include "demo.h"
33 #include "eventq.h"
34 #include "globals.h"
35 #include "levels.h"
36 #include "loadsave.h"
37 #include "menu.h"
38 #include "messages.h"
39 #include "network.h"
40 #include "player.h"
41 #include "view.h"
42
43 CPlayerMsg gPlayerMsg;
44 CCheatMgr gCheatMgr;
45
sub_5A928(void)46 void sub_5A928(void)
47 {
48 for (int i = 0; i < NUMGAMEFUNCTIONS-1; i++)
49 CONTROL_ClearButton(i);
50 }
51
52 extern uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2];
sub_5A944(char key)53 void sub_5A944(char key)
54 {
55 for (int i = 0; i < NUMGAMEFUNCTIONS-1; i++)
56 {
57 char key1, key2;
58 key1 = KeyboardKeys[i][0];
59 key2 = KeyboardKeys[i][1];
60 if (key1 == key || key2 == key)
61 CONTROL_ClearButton(i);
62 }
63 }
64
SetGodMode(bool god)65 void SetGodMode(bool god)
66 {
67 playerSetGodMode(gMe, god);
68 if (gMe->godMode)
69 viewSetMessage("You are immortal.");
70 else
71 viewSetMessage("You are mortal.");
72 }
73
SetClipMode(bool noclip)74 void SetClipMode(bool noclip)
75 {
76 gNoClip = noclip;
77 if (gNoClip)
78 viewSetMessage("Unclipped movement.");
79 else
80 viewSetMessage("Normal movement.");
81 }
82
packStuff(PLAYER * pPlayer)83 void packStuff(PLAYER *pPlayer)
84 {
85 for (int i = 0; i < 5; i++)
86 packAddItem(pPlayer, i);
87 }
88
packClear(PLAYER * pPlayer)89 void packClear(PLAYER *pPlayer)
90 {
91 pPlayer->packItemId = 0;
92 for (int i = 0; i < 5; i++)
93 {
94 pPlayer->packSlots[i].isActive = 0;
95 pPlayer->packSlots[i].curAmount = 0;
96 }
97 }
98
SetAmmo(bool stat)99 void SetAmmo(bool stat)
100 {
101 if (stat)
102 {
103 for (int i = 0; i < 12; i++)
104 gMe->ammoCount[i] = gAmmoInfo[i].max;
105 viewSetMessage("You have full ammo.");
106 }
107 else
108 {
109 for (int i = 0; i < 12; i++)
110 gMe->ammoCount[i] = 0;
111 viewSetMessage("You have no ammo.");
112 }
113 }
114
SetWeapons(bool stat)115 void SetWeapons(bool stat)
116 {
117 for (int i = 0; i < 14; i++)
118 {
119 gMe->hasWeapon[i] = stat;
120 }
121 SetAmmo(stat);
122 if (stat)
123 viewSetMessage("You have all weapons.");
124 else
125 {
126 if (!VanillaMode())
127 {
128 // Keep the pitchfork to avoid freeze
129 gMe->hasWeapon[1] = 1;
130 gMe->curWeapon = 0;
131 gMe->nextWeapon = 1;
132 }
133 viewSetMessage("You have no weapons.");
134 }
135 }
136
SetToys(bool stat)137 void SetToys(bool stat)
138 {
139 if (stat)
140 {
141 packStuff(gMe);
142 viewSetMessage("Your inventory is full.");
143 }
144 else
145 {
146 packClear(gMe);
147 viewSetMessage("Your inventory is empty.");
148 }
149 }
150
SetArmor(bool stat)151 void SetArmor(bool stat)
152 {
153 int nAmount;
154 if (stat)
155 {
156 viewSetMessage("You have full armor.");
157 nAmount = 3200;
158 }
159 else
160 {
161 viewSetMessage("You have no armor.");
162 nAmount = 0;
163 }
164 for (int i = 0; i < 3; i++)
165 gMe->armor[i] = nAmount;
166 }
167
SetKeys(bool stat)168 void SetKeys(bool stat)
169 {
170 for (int i = 1; i <= 6; i++)
171 gMe->hasKey[i] = stat;
172 if (stat)
173 viewSetMessage("You have all keys.");
174 else
175 viewSetMessage("You have no keys.");
176 }
177
SetInfiniteAmmo(bool stat)178 void SetInfiniteAmmo(bool stat)
179 {
180 gInfiniteAmmo = stat;
181 if (gInfiniteAmmo)
182 viewSetMessage("You have infinite ammo.");
183 else
184 viewSetMessage("You have limited ammo.");
185 }
186
SetMap(bool stat)187 void SetMap(bool stat)
188 {
189 gFullMap = stat;
190 if (gFullMap)
191 viewSetMessage("You have the map.");
192 else
193 viewSetMessage("You have no map.");
194 }
195
SetWooMode(bool stat)196 void SetWooMode(bool stat)
197 {
198 if (stat)
199 {
200 if (!powerupCheck(gMe, kPwUpTwoGuns))
201 powerupActivate(gMe, kPwUpTwoGuns);
202 }
203 else
204 {
205 if (powerupCheck(gMe, kPwUpTwoGuns))
206 {
207 if (!VanillaMode())
208 gMe->pwUpTime[kPwUpTwoGuns] = 0;
209 powerupDeactivate(gMe, kPwUpTwoGuns);
210 }
211 }
212 }
213
ToggleWooMode(void)214 void ToggleWooMode(void)
215 {
216 SetWooMode(!(powerupCheck(gMe, kPwUpTwoGuns) != 0));
217 }
218
ToggleBoots(void)219 void ToggleBoots(void)
220 {
221 if (powerupCheck(gMe, kPwUpJumpBoots))
222 {
223 viewSetMessage("You have no Jumping Boots.");
224 if (!VanillaMode())
225 {
226 gMe->pwUpTime[kPwUpJumpBoots] = 0;
227 gMe->packSlots[4].curAmount = 0;
228 }
229 powerupDeactivate(gMe, kPwUpJumpBoots);
230 }
231 else
232 {
233 viewSetMessage("You have the Jumping Boots.");
234 if (!VanillaMode())
235 gMe->pwUpTime[kPwUpJumpBoots] = gPowerUpInfo[kPwUpJumpBoots].bonusTime;
236 powerupActivate(gMe, kPwUpJumpBoots);
237 }
238 }
239
ToggleInvisibility(void)240 void ToggleInvisibility(void)
241 {
242 if (powerupCheck(gMe, kPwUpShadowCloak))
243 {
244 viewSetMessage("You are visible.");
245 if (!VanillaMode())
246 gMe->pwUpTime[kPwUpShadowCloak] = 0;
247 powerupDeactivate(gMe, kPwUpShadowCloak);
248 }
249 else
250 {
251 viewSetMessage("You are invisible.");
252 powerupActivate(gMe, kPwUpShadowCloak);
253 }
254 }
255
ToggleInvulnerability(void)256 void ToggleInvulnerability(void)
257 {
258 if (powerupCheck(gMe, kPwUpDeathMask))
259 {
260 viewSetMessage("You are vulnerable.");
261 if (!VanillaMode())
262 gMe->pwUpTime[kPwUpDeathMask] = 0;
263 powerupDeactivate(gMe, kPwUpDeathMask);
264 }
265 else
266 {
267 viewSetMessage("You are invulnerable.");
268 powerupActivate(gMe, kPwUpDeathMask);
269 }
270 }
271
ToggleDelirium(void)272 void ToggleDelirium(void)
273 {
274 if (powerupCheck(gMe, kPwUpDeliriumShroom))
275 {
276 viewSetMessage("You are not delirious.");
277 if (!VanillaMode())
278 gMe->pwUpTime[kPwUpDeliriumShroom] = 0;
279 powerupDeactivate(gMe, kPwUpDeliriumShroom);
280 }
281 else
282 {
283 viewSetMessage("You are delirious.");
284 powerupActivate(gMe, kPwUpDeliriumShroom);
285 }
286 }
287
LevelWarp(int nEpisode,int nLevel)288 void LevelWarp(int nEpisode, int nLevel)
289 {
290 levelSetupOptions(nEpisode, nLevel);
291 StartLevel(&gGameOptions);
292 viewResizeView(gViewSize);
293 }
294
LevelWarpAndRecord(int nEpisode,int nLevel)295 void LevelWarpAndRecord(int nEpisode, int nLevel)
296 {
297 char buffer[BMAX_PATH];
298 levelSetupOptions(nEpisode, nLevel);
299 gGameStarted = false;
300 strcpy(buffer, levelGetFilename(nEpisode, nLevel));
301 ChangeExtension(buffer, ".DEM");
302 gDemo.Create(buffer);
303 StartLevel(&gGameOptions);
304 viewResizeView(gViewSize);
305 }
306
CGameMessageMgr()307 CGameMessageMgr::CGameMessageMgr()
308 {
309 if (!VanillaMode())
310 Clear();
311 x = 1;
312 y = 0;
313 at9 = 0;
314 atd = 0;
315 nFont = 0;
316 fontHeight = 8;
317 maxNumberOfMessagesToDisplay = 4;
318 visibilityDurationInSecs = 5;
319 messageFlags = 15;
320 numberOfDisplayedMessages = 0;
321 nextMessagesIndex = messagesIndex = 0;
322 }
323
SetState(char state)324 void CGameMessageMgr::SetState(char state)
325 {
326 if (this->state && !state)
327 {
328 this->state = 0;
329 Clear();
330 }
331 else if (!this->state && state)
332 this->state = 1;
333 }
334
Add(const char * pText,char a2,const int pal,const MESSAGE_PRIORITY priority)335 void CGameMessageMgr::Add(const char *pText, char a2, const int pal, const MESSAGE_PRIORITY priority)
336 {
337 if (a2 && messageFlags)
338 {
339 messageStruct *pMessage = &messages[nextMessagesIndex];
340 strncpy(pMessage->text, pText, kMaxMessageTextLength-1);
341 pMessage->text[kMaxMessageTextLength-1] = 0;
342 pMessage->lastTickWhenVisible = gFrameClock + visibilityDurationInSecs*kTicRate;
343 pMessage->pal = pal;
344 pMessage->priority = priority;
345 pMessage->deleted = false;
346 nextMessagesIndex = (nextMessagesIndex+1)%kMessageLogSize;
347 if (VanillaMode())
348 {
349 numberOfDisplayedMessages++;
350 if (numberOfDisplayedMessages > maxNumberOfMessagesToDisplay)
351 {
352 messagesIndex = (messagesIndex+1)%kMessageLogSize;
353 atd = 0;
354 numberOfDisplayedMessages = maxNumberOfMessagesToDisplay;
355 at9 = fontHeight;
356 }
357 }
358 }
359 }
360
Display(void)361 void CGameMessageMgr::Display(void)
362 {
363 if (VanillaMode())
364 {
365 if (numberOfDisplayedMessages && this->state && gInputMode != INPUT_MODE_2)
366 {
367 int initialNrOfDisplayedMsgs = numberOfDisplayedMessages;
368 int initialMessagesIndex = messagesIndex;
369 int shade = ClipHigh(initialNrOfDisplayedMsgs*8, 48);
370 int x = gViewMode == 3 ? gViewX0S : 0;
371 int y = (gViewMode == 3 ? this->y : 0) + (int)at9;
372 for (int i = 0; i < initialNrOfDisplayedMsgs; i++)
373 {
374 messageStruct* pMessage = &messages[(initialMessagesIndex+i)%kMessageLogSize];
375 if (pMessage->lastTickWhenVisible < gFrameClock)
376 {
377 messagesIndex = (messagesIndex+1)%kMessageLogSize;
378 numberOfDisplayedMessages--;
379 continue;
380 }
381 viewDrawText(nFont, pMessage->text, x, y, shade, pMessage->pal, 0, false, 256);
382 if (gViewMode == 3)
383 {
384 int height;
385 gMenuTextMgr.GetFontInfo(nFont, pMessage->text, &height, NULL);
386 if (x+height > gViewX1S)
387 viewUpdatePages();
388 }
389 y += fontHeight;
390 shade = ClipLow(shade-64/initialNrOfDisplayedMsgs, -128);
391 }
392 }
393 }
394 else
395 {
396 if (this->state && gInputMode != INPUT_MODE_2)
397 {
398 messageStruct* currentMessages[kMessageLogSize];
399 int currentMessagesCount = 0;
400 for (int i = 0; i < kMessageLogSize; i++)
401 {
402 messageStruct* pMessage = &messages[i];
403 if (gFrameClock < pMessage->lastTickWhenVisible && !pMessage->deleted)
404 {
405 currentMessages[currentMessagesCount++] = pMessage;
406 }
407 }
408
409 SortMessagesByPriority(currentMessages, currentMessagesCount);
410
411 messageStruct* messagesToDisplay[kMessageLogSize];
412 int messagesToDisplayCount = 0;
413 for (int i = 0; i < currentMessagesCount && messagesToDisplayCount < maxNumberOfMessagesToDisplay; i++)
414 {
415 messagesToDisplay[messagesToDisplayCount++] = currentMessages[i];
416 }
417
418 SortMessagesByTime(messagesToDisplay, messagesToDisplayCount);
419
420 int shade = ClipHigh(messagesToDisplayCount*8, 48);
421 int x = gViewMode == 3 ? gViewX0S : 0;
422 int y = (gViewMode == 3 ? this->y : 0) + (int)at9;
423 for (int i = 0; i < messagesToDisplayCount; i++)
424 {
425 messageStruct* pMessage = messagesToDisplay[i];
426 viewDrawText(nFont, pMessage->text, x, y, shade, pMessage->pal, 0, false, 256);
427 if (gViewMode == 3)
428 {
429 int height;
430 gMenuTextMgr.GetFontInfo(nFont, pMessage->text, &height, NULL);
431 if (x+height > gViewX1S)
432 viewUpdatePages();
433 }
434 y += fontHeight;
435 shade = ClipLow(shade-64/messagesToDisplayCount, -128);
436 }
437 }
438 }
439 if (at9 != 0)
440 {
441 at9 = fontHeight*at9/kTicRate;
442 atd += gFrameTicks;
443 }
444 }
445
Clear(void)446 void CGameMessageMgr::Clear(void)
447 {
448 if (VanillaMode())
449 {
450 messagesIndex = nextMessagesIndex = numberOfDisplayedMessages = 0;
451 }
452 else
453 {
454 for (int i = 0; i < kMessageLogSize; i++)
455 {
456 messageStruct* pMessage = &messages[i];
457 pMessage->deleted = true;
458 }
459 }
460 }
461
SetMaxMessages(int nMessages)462 void CGameMessageMgr::SetMaxMessages(int nMessages)
463 {
464 maxNumberOfMessagesToDisplay = ClipRange(nMessages, 1, 16);
465 }
466
SetFont(int nFont)467 void CGameMessageMgr::SetFont(int nFont)
468 {
469 this->nFont = nFont;
470 fontHeight = gFont[nFont].ySize;
471 }
472
SetCoordinates(int x,int y)473 void CGameMessageMgr::SetCoordinates(int x, int y)
474 {
475 this->x = ClipRange(x, 0, gViewX1S);
476 this->y = ClipRange(y, 0, gViewY1S);
477 }
478
SetMessageTime(int nTime)479 void CGameMessageMgr::SetMessageTime(int nTime)
480 {
481 visibilityDurationInSecs = ClipRange(nTime, 1, 8);
482 }
483
SetMessageFlags(unsigned int nFlags)484 void CGameMessageMgr::SetMessageFlags(unsigned int nFlags)
485 {
486 messageFlags = nFlags&0xf;
487 }
488
SortMessagesByPriority(messageStruct ** messages,int count)489 void CGameMessageMgr::SortMessagesByPriority(messageStruct** messages, int count) {
490 for (int i = 1; i < count; i++)
491 {
492 for (int j = 0; j < count - i; j++)
493 {
494 if (messages[j]->priority != messages[j + 1]->priority ? messages[j]->priority < messages[j + 1]->priority : messages[j]->lastTickWhenVisible < messages[j + 1]->lastTickWhenVisible)
495 {
496 messageStruct* temp = messages[j];
497 messages[j] = messages[j + 1];
498 messages[j + 1] = temp;
499 }
500 }
501 }
502 }
503
SortMessagesByTime(messageStruct ** messages,int count)504 void CGameMessageMgr::SortMessagesByTime(messageStruct** messages, int count) {
505 for (int i = 1; i < count; i++)
506 {
507 for (int j = 0; j < count - i; j++)
508 {
509 if (messages[j]->lastTickWhenVisible > messages[j + 1]->lastTickWhenVisible)
510 {
511 messageStruct* temp = messages[j];
512 messages[j] = messages[j + 1];
513 messages[j + 1] = temp;
514 }
515 }
516 }
517 }
518
Clear(void)519 void CPlayerMsg::Clear(void)
520 {
521 text[0] = 0;
522 at0 = 0;
523 }
524
Term(void)525 void CPlayerMsg::Term(void)
526 {
527 Clear();
528 gInputMode = INPUT_MODE_0;
529 }
530
Draw(void)531 void CPlayerMsg::Draw(void)
532 {
533 char buffer[44];
534 strcpy(buffer, text);
535 if ((int)totalclock & 16)
536 strcat(buffer, "_");
537 int x = gViewMode == 3 ? gViewX0S : 0;
538 int y = gViewMode == 3 ? gViewY0S : 0;
539 if (gViewSize >= 1)
540 y += tilesiz[2229].y*((gNetPlayers+3)/4);
541 viewDrawText(0, buffer, x+1,y+1, -128, 0, 0, false, 256);
542 viewUpdatePages();
543 }
544
AddChar(char ch)545 bool CPlayerMsg::AddChar(char ch)
546 {
547 if (at0 < 40)
548 {
549 text[at0++] = ch;
550 text[at0] = 0;
551 return true;
552 }
553 return false;
554 }
555
DelChar(void)556 void CPlayerMsg::DelChar(void)
557 {
558 if (at0 > 0)
559 text[--at0] = 0;
560 }
561
Set(const char * pzString)562 void CPlayerMsg::Set(const char * pzString)
563 {
564 strncpy(text, pzString, 40);
565 at0 = ClipHigh(strlen(pzString), 40);
566 text[at0] = 0;
567 }
568
Send(void)569 void CPlayerMsg::Send(void)
570 {
571 if (VanillaMode() || !IsWhitespaceOnly(text))
572 {
573 netBroadcastMessage(myconnectindex, text);
574 if (!VanillaMode())
575 {
576 char *myName = gProfile[myconnectindex].name;
577 char szTemp[128];
578 sprintf(szTemp, "%s: %s", myName, text);
579 viewSetMessage(szTemp, 10); // 10: dark blue
580 }
581 else
582 viewSetMessage(text);
583 }
584
585 Term();
586 keyFlushScans();
587 }
588
ProcessKeys(void)589 void CPlayerMsg::ProcessKeys(void)
590 {
591 int key = keyGetScan();
592 char ch;
593 if (key != 0)
594 {
595 bool UNUSED(alt) = keystatus[sc_LeftAlt] || keystatus[sc_RightAlt];
596 bool ctrl = keystatus[sc_LeftControl] || keystatus[sc_RightControl];
597 bool shift = keystatus[sc_LeftShift] || keystatus[sc_RightShift];
598 switch (key)
599 {
600 case sc_Escape:
601 Term();
602 break;
603 case sc_F1:
604 case sc_F2:
605 case sc_F3:
606 case sc_F4:
607 case sc_F5:
608 case sc_F6:
609 case sc_F7:
610 case sc_F8:
611 case sc_F9:
612 case sc_F10:
613 CONTROL_ClearButton(gamefunc_See_Chase_View);
614 Set(CommbatMacro[key-sc_F1]);
615 Send();
616 keystatus[key] = 0;
617 break;
618 case sc_BackSpace:
619 if (ctrl)
620 Clear();
621 else
622 DelChar();
623 break;
624 case sc_Enter:
625 case sc_kpad_Enter:
626 if (gCheatMgr.Check(text))
627 Term();
628 else
629 Send();
630 break;
631 default:
632 if (key < 128)
633 {
634 ch = shift ? g_keyAsciiTableShift[key] : g_keyAsciiTable[key];
635 if (ch)
636 AddChar(ch);
637 }
638 break;
639 }
640 sub_5A944(key);
641 }
642 }
643
IsWhitespaceOnly(const char * const pzString)644 bool CPlayerMsg::IsWhitespaceOnly(const char * const pzString)
645 {
646 const char *p = pzString;
647 while (*p != 0)
648 if (*p++ > 32)
649 return false;
650 return true;
651 }
652
653 CCheatMgr::CHEATINFO CCheatMgr::s_CheatInfo[] = {
654 {"NQLGB", kCheatMpkfa, 0 }, // MPKFA (Invincibility)
655 {"DBQJONZBTT", kCheatCapInMyAss, 0 }, // CAPINMYASS (Disable invincibility )
656 {"OPDBQJONZBTT", kCheatNoCapInMyAss, 0 }, // NOCAPINMYASS (Invincibility)
657 {"J!XBOOB!CF!MJLF!LFWJO", kCheatNoCapInMyAss, 0 }, // I WANNA BE LIKE KEVIN (Invincibility)
658 {"JEBIP", kCheatIdaho, 0 }, // IDAHO (All weapons and full ammo)
659 {"NPOUBOB", kCheatMontana, 0 }, // MONTANA (All weapons, full ammo and all items)
660 {"HSJTXPME", kCheatGriswold, 0 }, // GRISWOLD (Full armor (same effect as getting super armor))
661 {"FENBSL", kCheatEdmark, 0 }, // EDMARK (Does a lot of fire damage to you (if you have 200HP and 200 fire armor then you can survive). Displays the message "THOSE WERE THE DAYS".)
662 {"UFRVJMB", kCheatTequila, 0 }, // TEQUILA (Guns akimbo power-up)
663 {"CVO[", kCheatBunz, 0 }, // BUNZ (All weapons, full ammo, and guns akimbo power-up)
664 {"GVOLZ!TIPFT", kCheatFunkyShoes, 0 }, // FUNKY SHOES (Gives jump boots item and activates it)
665 {"HBUFLFFQFS", kCheatGateKeeper, 0 }, // GATEKEEPER (Sets the you cheated flag to true, at the end of the level you will see that you have cheated)
666 {"LFZNBTUFS", kCheatKeyMaster, 0 }, // KEYMASTER (All keys)
667 {"KPKP", kCheatJoJo, 0 }, // JOJO (Drunk mode (same effect as getting bitten by red spider))
668 {"TBUDIFM", kCheatSatchel, 0 }, // SATCHEL (Full inventory)
669 {"TQPSL", kCheatSpork, 0 }, // SPORK (200% health (same effect as getting life seed))
670 {"POFSJOH", kCheatOneRing, 0 }, // ONERING (Cloak of invisibility power-up)
671 {"NBSJP", kCheatMario, 1 }, // MARIO (Warp to level E M, e.g.: MARIO 1 3 will take you to Phantom Express)
672 {"DBMHPO", kCheatCalgon, 1 }, // CALGON (Jumps to next level or can be used like MARIO with parameters)
673 {"LFWPSLJBO", kCheatKevorkian, 0 }, // KEVORKIAN (Does a lot of physical damage to you (if you have 200HP and 200 fire armor then you can survive). Displays the message "KEVORKIAN APPROVES".)
674 {"NDHFF", kCheatMcGee, 0 }, // MCGEE (Sets you on fire. Displays the message "YOU'RE FIRED".)
675 {"LSVFHFS", kCheatKrueger, 0 }, // KRUEGER (200% health, but sets you on fire. Displays the message "FLAME RETARDANT".)
676 {"DIFFTFIFBE", kCheatCheeseHead, 0 }, // CHEESEHEAD (100% diving suit)
677 {"DPVTUFBV", kCheatCousteau, 0 }, // COUSTEAU (200% health and diving suit)
678 {"WPPSIFFT", kCheatVoorhees, 0 }, // VOORHEES (Death mask power-up)
679 {"MBSB!DSPGU", kCheatLaraCroft, 0 }, // LARA CROFT (All weapons and infinite ammo. Displays the message "LARA RULES". Typing it the second time will lose all weapons and ammo.)
680 {"IPOHLPOH", kCheatHongKong, 0 }, // HONGKONG (All weapons and infinite ammo)
681 {"GSBOLFOTUFJO", kCheatFrankenstein, 0 }, // FRANKENSTEIN (100% med-kit)
682 {"TUFSOP", kCheatSterno, 0 }, // STERNO (Temporary blindness (same effect as getting bitten by green spider))
683 {"DMBSJDF", kCheatClarice, 0 }, // CLARICE (Gives 100% body armor, 100% fire armor, 100% spirit armor)
684 {"GPSL!ZPV", kCheatForkYou, 0 }, // FORK YOU (Drunk mode, 1HP, no armor, no weapons, no ammo, no items, no keys, no map, guns akimbo power-up)
685 {"MJFCFSNBO", kCheatLieberMan, 0 }, // LIEBERMAN (Sets the you cheated flag to true, at the end of the level you will see that you have cheated)
686 {"FWB!HBMMJ", kCheatEvaGalli, 0 }, // EVA GALLI (Disable/enable clipping (grant the ability to walk through walls))
687 {"SBUF", kCheatRate, 0 }, // RATE (Display frame rate (doesn't count as a cheat))
688 {"HPPOJFT", kCheatGoonies, 0 }, // GOONIES (Enable full map. Displays the message "YOU HAVE THE MAP".)
689 {"TQJFMCFSH", kCheatSpielberg, 1 }, // SPIELBERG (Disables all cheats. If number values corresponding to a level and episode number are entered after the cheat word (i.e. "spielberg 1 3" for Phantom Express), you will be spawned to said level and the game will begin recording a demo from your actions.)
690 };
691
692 bool CCheatMgr::m_bPlayerCheated = false;
693
Check(char * pzString)694 bool CCheatMgr::Check(char *pzString)
695 {
696 char buffer[80];
697 strcpy(buffer, pzString);
698 Bstrupr(buffer);
699 for (size_t i = 0; i < strlen(pzString); i++)
700 buffer[i]++;
701 for (int i = 0; i < 36; i++)
702 {
703 int nCheatLen = strlen(s_CheatInfo[i].pzString);
704 if (s_CheatInfo[i].flags & 1)
705 {
706 if (!strncmp(buffer, s_CheatInfo[i].pzString, nCheatLen))
707 {
708 Process(s_CheatInfo[i].id, buffer+nCheatLen);
709 return true;
710 }
711 }
712 if (!strcmp(buffer, s_CheatInfo[i].pzString))
713 {
714 Process(s_CheatInfo[i].id, NULL);
715 return true;
716 }
717 }
718 return false;
719 }
720
parseArgs(char * pzArgs,int * nArg1,int * nArg2)721 int parseArgs(char *pzArgs, int *nArg1, int *nArg2)
722 {
723 if (!nArg1 || !nArg2)
724 return -1;
725 int nLength = strlen(pzArgs);
726 for (int i = 0; i < nLength; i++)
727 pzArgs[i]--;
728 int stat = sscanf(pzArgs, " %d %d", nArg1, nArg2);
729 if (stat == 2 && (*nArg1 == 0 || *nArg2 == 0))
730 return -1;
731 *nArg1 = ClipRange(*nArg1-1, 0, gEpisodeCount-1);
732 *nArg2 = ClipRange(*nArg2-1, 0, gEpisodeInfo[*nArg1].nLevels-1);
733 return stat;
734 }
735
Process(CCheatMgr::CHEATCODE nCheatCode,char * pzArgs)736 void CCheatMgr::Process(CCheatMgr::CHEATCODE nCheatCode, char* pzArgs)
737 {
738 dassert(nCheatCode > kCheatNone && nCheatCode < kCheatMax);
739
740 if (gDemo.at0) return;
741 if (nCheatCode == kCheatRate)
742 {
743 gShowFps = !gShowFps;
744 return;
745 }
746 if (gGameOptions.nGameType != 0)
747 return;
748 int nEpisode, nLevel;
749 switch (nCheatCode)
750 {
751 case kCheatSpielberg:
752 if (parseArgs(pzArgs, &nEpisode, &nLevel) == 2)
753 LevelWarpAndRecord(nEpisode, nLevel);
754 break;
755 case kCheat1:
756 SetAmmo(true);
757 break;
758 case kCheatGriswold:
759 SetArmor(true);
760 break;
761 case kCheatSatchel:
762 SetToys(true);
763 break;
764 case kCheatEvaGalli:
765 SetClipMode(!gNoClip);
766 break;
767 case kCheatMpkfa:
768 SetGodMode(!gMe->godMode);
769 break;
770 case kCheatCapInMyAss:
771 SetGodMode(false);
772 break;
773 case kCheatNoCapInMyAss:
774 SetGodMode(true);
775 break;
776 case kCheatIdaho:
777 SetWeapons(true);
778 break;
779 case kCheatKevorkian:
780 actDamageSprite(gMe->nSprite, gMe->pSprite, kDamageBullet, 8000);
781 viewSetMessage("Kevorkian approves.");
782 break;
783 case kCheatMcGee:
784 {
785 if (!gMe->pXSprite->burnTime)
786 evPost(gMe->nSprite, 3, 0, kCallbackFXFlameLick);
787 actBurnSprite(actSpriteIdToOwnerId(gMe->nSprite), gMe->pXSprite, 2400);
788 viewSetMessage("You're fired!");
789 break;
790 }
791 case kCheatEdmark:
792 actDamageSprite(gMe->nSprite, gMe->pSprite, kDamageExplode, 8000);
793 viewSetMessage("Ahhh...those were the days.");
794 break;
795 case kCheatKrueger:
796 {
797 actHealDude(gMe->pXSprite, 200, 200);
798 gMe->armor[1] = VanillaMode() ? 200 : 3200;
799 if (!gMe->pXSprite->burnTime)
800 evPost(gMe->nSprite, 3, 0, kCallbackFXFlameLick);
801 actBurnSprite(actSpriteIdToOwnerId(gMe->nSprite), gMe->pXSprite, 2400);
802 viewSetMessage("Flame retardant!");
803 break;
804 }
805 case kCheatSterno:
806 gMe->blindEffect = 250;
807 break;
808 case kCheat14: // quakeEffect (causing a little flickerEffect), not used by any cheat code (dead code)
809 gMe->flickerEffect = 360;
810 break;
811 case kCheatSpork:
812 actHealDude(gMe->pXSprite, 200, 200);
813 break;
814 case kCheatGoonies:
815 SetMap(!gFullMap);
816 break;
817 case kCheatClarice:
818 if (!VanillaMode())
819 {
820 viewSetMessage("You have half armor.");
821 for (int i = 0; i < 3; i++)
822 gMe->armor[i] = 1600;
823 }
824 break;
825 case kCheatFrankenstein:
826 gMe->packSlots[0].curAmount = 100;
827 break;
828 case kCheatCheeseHead:
829 gMe->packSlots[1].curAmount = 100;
830 if (!VanillaMode())
831 gMe->pwUpTime[kPwUpDivingSuit] = gPowerUpInfo[kPwUpDivingSuit].bonusTime;
832 break;
833 case kCheatTequila:
834 ToggleWooMode();
835 break;
836 case kCheatFunkyShoes:
837 ToggleBoots();
838 break;
839 case kCheatKeyMaster:
840 SetKeys(true);
841 break;
842 case kCheatOneRing:
843 ToggleInvisibility();
844 break;
845 case kCheatVoorhees:
846 ToggleInvulnerability();
847 break;
848 case kCheatJoJo:
849 ToggleDelirium();
850 break;
851 case kCheatRate: // show FPS, handled before (dead code), leave here for safety
852 return;
853 case kCheatMario:
854 if (parseArgs(pzArgs, &nEpisode, &nLevel) == 2)
855 LevelWarp(nEpisode, nLevel);
856 break;
857 case kCheatCalgon:
858 if (parseArgs(pzArgs, &nEpisode, &nLevel) == 2)
859 LevelWarp(nEpisode, nLevel);
860 else
861 if (!VanillaMode())
862 levelEndLevel(kLevelExitNormal);
863 break;
864 case kCheatLaraCroft:
865 SetInfiniteAmmo(!gInfiniteAmmo);
866 SetWeapons(gInfiniteAmmo);
867 break;
868 case kCheatHongKong:
869 SetWeapons(true);
870 SetInfiniteAmmo(true);
871 break;
872 case kCheatMontana:
873 SetWeapons(true);
874 SetToys(true);
875 break;
876 case kCheatBunz:
877 SetWeapons(true);
878 SetWooMode(true);
879 break;
880 case kCheatCousteau:
881 actHealDude(gMe->pXSprite,200,200);
882 gMe->packSlots[1].curAmount = 100;
883 if (!VanillaMode())
884 gMe->pwUpTime[kPwUpDivingSuit] = gPowerUpInfo[kPwUpDivingSuit].bonusTime;
885 break;
886 case kCheatForkYou:
887 SetInfiniteAmmo(false);
888 SetMap(false);
889 SetWeapons(false);
890 SetAmmo(false);
891 SetArmor(false);
892 SetToys(false);
893 SetKeys(false);
894 SetWooMode(true);
895 powerupActivate(gMe, kPwUpDeliriumShroom);
896 gMe->pXSprite->health = 16;
897 gMe->hasWeapon[1] = 1;
898 gMe->curWeapon = 0;
899 gMe->nextWeapon = 1;
900 break;
901 default:
902 break;
903 }
904 m_bPlayerCheated = true;
905 }
906
sub_5BCF4(void)907 void CCheatMgr::sub_5BCF4(void)
908 {
909 m_bPlayerCheated = 0;
910 playerSetGodMode(gMe, 0);
911 gNoClip = 0;
912 packClear(gMe);
913 gInfiniteAmmo = 0;
914 gFullMap = 0;
915 }
916
917 class MessagesLoadSave : public LoadSave
918 {
919 public:
920 virtual void Load();
921 virtual void Save();
922 };
923
Load()924 void MessagesLoadSave::Load()
925 {
926 Read(&CCheatMgr::m_bPlayerCheated, sizeof(CCheatMgr::m_bPlayerCheated));
927 }
928
Save()929 void MessagesLoadSave::Save()
930 {
931 Write(&CCheatMgr::m_bPlayerCheated, sizeof(CCheatMgr::m_bPlayerCheated));
932 }
933
934 static MessagesLoadSave *myLoadSave;
935
MessagesLoadSaveConstruct(void)936 void MessagesLoadSaveConstruct(void)
937 {
938 myLoadSave = new MessagesLoadSave();
939 }
940