raze-gles/source/blood/src/messages.cpp

945 lines
27 KiB
C++
Raw Normal View History

2019-09-19 22:42:45 +00:00
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h" // Must come before everything else!
2019-09-19 22:42:45 +00:00
#include "build.h"
#include "mmulti.h"
#include "compat.h"
#include "keyboard.h"
#include "control.h"
#include "gamecontrol.h"
2019-09-19 22:42:45 +00:00
#include "common_game.h"
#include "blood.h"
#include "config.h"
#include "demo.h"
#include "eventq.h"
#include "globals.h"
#include "levels.h"
#include "loadsave.h"
#include "menu.h"
#include "messages.h"
#include "network.h"
#include "player.h"
#include "view.h"
BEGIN_BLD_NS
2019-09-19 22:42:45 +00:00
CPlayerMsg gPlayerMsg;
CCheatMgr gCheatMgr;
void sub_5A928(void)
{
for (int i = 0; i < NUMGAMEFUNCTIONS-1; i++)
CONTROL_ClearButton(i);
}
void sub_5A944(char key)
{
for (int i = 0; i < NUMGAMEFUNCTIONS-1; i++)
{
char key1, key2;
key1 = KeyboardKeys[i][0];
key2 = KeyboardKeys[i][1];
if (key1 == key || key2 == key)
CONTROL_ClearButton(i);
}
}
void SetGodMode(bool god)
{
playerSetGodMode(gMe, god);
if (gMe->godMode)
2019-09-19 22:42:45 +00:00
viewSetMessage("You are immortal.");
else
viewSetMessage("You are mortal.");
}
void SetClipMode(bool noclip)
{
gNoClip = noclip;
if (gNoClip)
viewSetMessage("Unclipped movement.");
else
viewSetMessage("Normal movement.");
}
void packStuff(PLAYER *pPlayer)
{
for (int i = 0; i < 5; i++)
packAddItem(pPlayer, i);
}
void packClear(PLAYER *pPlayer)
{
pPlayer->packItemId = 0;
2019-09-19 22:42:45 +00:00
for (int i = 0; i < 5; i++)
{
pPlayer->packSlots[i].isActive = 0;
pPlayer->packSlots[i].curAmount = 0;
2019-09-19 22:42:45 +00:00
}
}
void SetAmmo(bool stat)
{
if (stat)
{
for (int i = 0; i < 12; i++)
gMe->ammCount[i] = gAmmoInfo[i].max;
2019-09-19 22:42:45 +00:00
viewSetMessage("You have full ammo.");
}
else
{
for (int i = 0; i < 12; i++)
gMe->ammCount[i] = 0;
2019-09-19 22:42:45 +00:00
viewSetMessage("You have no ammo.");
}
}
void SetWeapons(bool stat)
{
for (int i = 0; i < 14; i++)
{
gMe->hasWeapon[i] = stat;
2019-09-19 22:42:45 +00:00
}
SetAmmo(stat);
if (stat)
viewSetMessage("You have all weapons.");
else
{
if (!VanillaMode())
{
// Keep the pitchfork to avoid freeze
gMe->hasWeapon[1] = 1;
gMe->curWeapon = 0;
gMe->nextWeapon = 1;
2019-09-19 22:42:45 +00:00
}
viewSetMessage("You have no weapons.");
}
}
void SetToys(bool stat)
{
if (stat)
{
packStuff(gMe);
viewSetMessage("Your inventory is full.");
}
else
{
packClear(gMe);
viewSetMessage("Your inventory is empty.");
}
}
void SetArmor(bool stat)
{
int nAmount;
if (stat)
{
viewSetMessage("You have full armor.");
nAmount = 3200;
}
else
{
viewSetMessage("You have no armor.");
nAmount = 0;
}
for (int i = 0; i < 3; i++)
gMe->armor[i] = nAmount;
2019-09-19 22:42:45 +00:00
}
void SetKeys(bool stat)
{
for (int i = 1; i <= 6; i++)
gMe->hasKey[i] = stat;
2019-09-19 22:42:45 +00:00
if (stat)
viewSetMessage("You have all keys.");
else
viewSetMessage("You have no keys.");
}
void SetInfiniteAmmo(bool stat)
{
gInfiniteAmmo = stat;
if (gInfiniteAmmo)
viewSetMessage("You have infinite ammo.");
else
viewSetMessage("You have limited ammo.");
}
void SetMap(bool stat)
{
gFullMap = stat;
if (gFullMap)
viewSetMessage("You have the map.");
else
viewSetMessage("You have no map.");
}
void SetWooMode(bool stat)
{
if (stat)
{
if (!powerupCheck(gMe, kPwUpTwoGuns))
powerupActivate(gMe, kPwUpTwoGuns);
2019-09-19 22:42:45 +00:00
}
else
{
if (powerupCheck(gMe, kPwUpTwoGuns))
2019-09-19 22:42:45 +00:00
{
if (!VanillaMode())
gMe->pwUpTime[kPwUpTwoGuns] = 0;
powerupDeactivate(gMe, kPwUpTwoGuns);
2019-09-19 22:42:45 +00:00
}
}
}
void ToggleWooMode(void)
{
SetWooMode(!(powerupCheck(gMe, kPwUpTwoGuns) != 0));
2019-09-19 22:42:45 +00:00
}
void ToggleBoots(void)
{
if (powerupCheck(gMe, kPwUpJumpBoots))
2019-09-19 22:42:45 +00:00
{
viewSetMessage("You have no Jumping Boots.");
if (!VanillaMode())
{
gMe->pwUpTime[kPwUpJumpBoots] = 0;
gMe->packSlots[4].curAmount = 0;
2019-09-19 22:42:45 +00:00
}
powerupDeactivate(gMe, kPwUpJumpBoots);
2019-09-19 22:42:45 +00:00
}
else
{
viewSetMessage("You have the Jumping Boots.");
if (!VanillaMode())
gMe->pwUpTime[kPwUpJumpBoots] = gPowerUpInfo[kPwUpJumpBoots].bonusTime;
powerupActivate(gMe, kPwUpJumpBoots);
2019-09-19 22:42:45 +00:00
}
}
void ToggleInvisibility(void)
{
if (powerupCheck(gMe, kPwUpShadowCloak))
2019-09-19 22:42:45 +00:00
{
viewSetMessage("You are visible.");
if (!VanillaMode())
gMe->pwUpTime[kPwUpShadowCloak] = 0;
powerupDeactivate(gMe, kPwUpShadowCloak);
2019-09-19 22:42:45 +00:00
}
else
{
viewSetMessage("You are invisible.");
powerupActivate(gMe, kPwUpShadowCloak);
2019-09-19 22:42:45 +00:00
}
}
void ToggleInvulnerability(void)
{
if (powerupCheck(gMe, kPwUpDeathMask))
2019-09-19 22:42:45 +00:00
{
viewSetMessage("You are vulnerable.");
if (!VanillaMode())
gMe->pwUpTime[kPwUpDeathMask] = 0;
powerupDeactivate(gMe, kPwUpDeathMask);
2019-09-19 22:42:45 +00:00
}
else
{
viewSetMessage("You are invulnerable.");
powerupActivate(gMe, kPwUpDeathMask);
2019-09-19 22:42:45 +00:00
}
}
void ToggleDelirium(void)
{
if (powerupCheck(gMe, kPwUpDeliriumShroom))
2019-09-19 22:42:45 +00:00
{
viewSetMessage("You are not delirious.");
if (!VanillaMode())
gMe->pwUpTime[kPwUpDeliriumShroom] = 0;
powerupDeactivate(gMe, kPwUpDeliriumShroom);
2019-09-19 22:42:45 +00:00
}
else
{
viewSetMessage("You are delirious.");
powerupActivate(gMe, kPwUpDeliriumShroom);
2019-09-19 22:42:45 +00:00
}
}
void LevelWarp(int nEpisode, int nLevel)
{
levelSetupOptions(nEpisode, nLevel);
StartLevel(&gGameOptions);
viewResizeView(gViewSize);
}
void LevelWarpAndRecord(int nEpisode, int nLevel)
{
char buffer[BMAX_PATH];
levelSetupOptions(nEpisode, nLevel);
gGameStarted = false;
strcpy(buffer, levelGetFilename(nEpisode, nLevel));
ChangeExtension(buffer, ".DEM");
gDemo.Create(buffer);
StartLevel(&gGameOptions);
viewResizeView(gViewSize);
}
CGameMessageMgr::CGameMessageMgr()
{
if (!VanillaMode())
Clear();
x = 1;
y = 0;
2019-09-19 22:42:45 +00:00
at9 = 0;
atd = 0;
nFont = 0;
fontHeight = 8;
maxNumberOfMessagesToDisplay = 4;
visibilityDurationInSecs = 5;
messageFlags = 15;
numberOfDisplayedMessages = 0;
nextMessagesIndex = messagesIndex = 0;
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::SetState(char state)
{
if (this->state && !state)
2019-09-19 22:42:45 +00:00
{
this->state = 0;
2019-09-19 22:42:45 +00:00
Clear();
}
else if (!this->state && state)
this->state = 1;
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::Add(const char *pText, char a2, const int pal, const MESSAGE_PRIORITY priority)
2019-09-19 22:42:45 +00:00
{
if (a2 && messageFlags)
2019-09-19 22:42:45 +00:00
{
messageStruct *pMessage = &messages[nextMessagesIndex];
strncpy(pMessage->text, pText, kMaxMessageTextLength-1);
pMessage->text[kMaxMessageTextLength-1] = 0;
pMessage->lastTickWhenVisible = gFrameClock + visibilityDurationInSecs*kTicRate;
2019-09-18 10:58:45 +00:00
pMessage->pal = pal;
pMessage->priority = priority;
pMessage->deleted = false;
nextMessagesIndex = (nextMessagesIndex+1)%kMessageLogSize;
if (VanillaMode())
2019-09-19 22:42:45 +00:00
{
numberOfDisplayedMessages++;
if (numberOfDisplayedMessages > maxNumberOfMessagesToDisplay)
{
messagesIndex = (messagesIndex+1)%kMessageLogSize;
atd = 0;
numberOfDisplayedMessages = maxNumberOfMessagesToDisplay;
at9 = fontHeight;
}
2019-09-19 22:42:45 +00:00
}
}
}
void CGameMessageMgr::Display(void)
{
if (VanillaMode())
2019-09-19 22:42:45 +00:00
{
if (numberOfDisplayedMessages && this->state && gInputMode != kInputMessage)
2019-09-19 22:42:45 +00:00
{
int initialNrOfDisplayedMsgs = numberOfDisplayedMessages;
int initialMessagesIndex = messagesIndex;
int shade = ClipHigh(initialNrOfDisplayedMsgs*8, 48);
int x = gViewMode == 3 ? gViewX0S : 0;
int y = (gViewMode == 3 ? this->y : 0) + (int)at9;
for (int i = 0; i < initialNrOfDisplayedMsgs; i++)
2019-09-19 22:42:45 +00:00
{
messageStruct* pMessage = &messages[(initialMessagesIndex+i)%kMessageLogSize];
if (pMessage->lastTickWhenVisible < gFrameClock)
{
messagesIndex = (messagesIndex+1)%kMessageLogSize;
numberOfDisplayedMessages--;
continue;
}
viewDrawText(nFont, pMessage->text, x, y, shade, pMessage->pal, 0, false, 256);
if (gViewMode == 3)
{
int height;
gMenuTextMgr.GetFontInfo(nFont, pMessage->text, &height, NULL);
if (x+height > gViewX1S)
viewUpdatePages();
}
y += fontHeight;
shade = ClipLow(shade-64/initialNrOfDisplayedMsgs, -128);
2019-09-19 22:42:45 +00:00
}
}
}
else
{
if (this->state && gInputMode != kInputMessage)
2019-09-19 22:42:45 +00:00
{
messageStruct* currentMessages[kMessageLogSize];
int currentMessagesCount = 0;
for (int i = 0; i < kMessageLogSize; i++)
{
messageStruct* pMessage = &messages[i];
if (gFrameClock < pMessage->lastTickWhenVisible && !pMessage->deleted)
{
currentMessages[currentMessagesCount++] = pMessage;
}
}
SortMessagesByPriority(currentMessages, currentMessagesCount);
messageStruct* messagesToDisplay[kMessageLogSize];
int messagesToDisplayCount = 0;
for (int i = 0; i < currentMessagesCount && messagesToDisplayCount < maxNumberOfMessagesToDisplay; i++)
{
messagesToDisplay[messagesToDisplayCount++] = currentMessages[i];
}
SortMessagesByTime(messagesToDisplay, messagesToDisplayCount);
int shade = ClipHigh(messagesToDisplayCount*8, 48);
int x = gViewMode == 3 ? gViewX0S : 0;
int y = (gViewMode == 3 ? this->y : 0) + (int)at9;
for (int i = 0; i < messagesToDisplayCount; i++)
{
messageStruct* pMessage = messagesToDisplay[i];
viewDrawText(nFont, pMessage->text, x, y, shade, pMessage->pal, 0, false, 256);
if (gViewMode == 3)
{
int height;
gMenuTextMgr.GetFontInfo(nFont, pMessage->text, &height, NULL);
if (x+height > gViewX1S)
viewUpdatePages();
}
y += fontHeight;
shade = ClipLow(shade-64/messagesToDisplayCount, -128);
}
2019-09-19 22:42:45 +00:00
}
}
if (at9 != 0)
{
at9 = fontHeight*at9/kTicRate;
atd += gFrameTicks;
}
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::Clear(void)
{
if (VanillaMode())
{
messagesIndex = nextMessagesIndex = numberOfDisplayedMessages = 0;
}
else
{
for (int i = 0; i < kMessageLogSize; i++)
{
messageStruct* pMessage = &messages[i];
pMessage->deleted = true;
}
}
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::SetMaxMessages(int nMessages)
{
maxNumberOfMessagesToDisplay = ClipRange(nMessages, 1, 16);
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::SetFont(int nFont)
{
this->nFont = nFont;
fontHeight = gFont[nFont].ySize;
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::SetCoordinates(int x, int y)
{
this->x = ClipRange(x, 0, gViewX1S);
this->y = ClipRange(y, 0, gViewY1S);
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::SetMessageTime(int nTime)
{
visibilityDurationInSecs = ClipRange(nTime, 1, 8);
2019-09-19 22:42:45 +00:00
}
void CGameMessageMgr::SetMessageFlags(unsigned int nFlags)
{
messageFlags = nFlags&0xf;
}
void CGameMessageMgr::SortMessagesByPriority(messageStruct** messages, int count) {
for (int i = 1; i < count; i++)
{
for (int j = 0; j < count - i; j++)
{
if (messages[j]->priority != messages[j + 1]->priority ? messages[j]->priority < messages[j + 1]->priority : messages[j]->lastTickWhenVisible < messages[j + 1]->lastTickWhenVisible)
{
messageStruct* temp = messages[j];
messages[j] = messages[j + 1];
messages[j + 1] = temp;
}
}
}
}
void CGameMessageMgr::SortMessagesByTime(messageStruct** messages, int count) {
for (int i = 1; i < count; i++)
{
for (int j = 0; j < count - i; j++)
{
if (messages[j]->lastTickWhenVisible > messages[j + 1]->lastTickWhenVisible)
{
messageStruct* temp = messages[j];
messages[j] = messages[j + 1];
messages[j + 1] = temp;
}
}
}
2019-09-19 22:42:45 +00:00
}
void CPlayerMsg::Clear(void)
{
text[0] = 0;
2019-09-19 22:42:45 +00:00
at0 = 0;
}
void CPlayerMsg::Term(void)
{
Clear();
gInputMode = kInputGame;
2019-09-19 22:42:45 +00:00
}
void CPlayerMsg::Draw(void)
{
char buffer[44];
strcpy(buffer, text);
if ((int)totalclock & 16)
2019-09-19 22:42:45 +00:00
strcat(buffer, "_");
int x = gViewMode == 3 ? gViewX0S : 0;
int y = gViewMode == 3 ? gViewY0S : 0;
if (gViewSize >= 1)
y += tilesiz[2229].y*((gNetPlayers+3)/4);
viewDrawText(0, buffer, x+1,y+1, -128, 0, 0, false, 256);
viewUpdatePages();
}
bool CPlayerMsg::AddChar(char ch)
{
if (at0 < 40)
{
text[at0++] = ch;
text[at0] = 0;
2019-09-19 22:42:45 +00:00
return true;
}
return false;
}
void CPlayerMsg::DelChar(void)
{
if (at0 > 0)
text[--at0] = 0;
2019-09-19 22:42:45 +00:00
}
void CPlayerMsg::Set(const char * pzString)
{
strncpy(text, pzString, 40);
2019-09-19 22:42:45 +00:00
at0 = ClipHigh(strlen(pzString), 40);
text[at0] = 0;
2019-09-19 22:42:45 +00:00
}
void CPlayerMsg::Send(void)
{
if (VanillaMode() || !IsWhitespaceOnly(text))
{
netBroadcastMessage(myconnectindex, text);
if (!VanillaMode())
{
char *myName = gProfile[myconnectindex].name;
char szTemp[128];
sprintf(szTemp, "%s: %s", myName, text);
viewSetMessage(szTemp, 10); // 10: dark blue
}
else
viewSetMessage(text);
}
2019-09-19 22:42:45 +00:00
Term();
keyFlushScans();
}
void CPlayerMsg::ProcessKeys(void)
{
int key = keyGetScan();
char ch;
if (key != 0)
{
bool UNUSED(alt) = keystatus[sc_LeftAlt] || keystatus[sc_RightAlt];
bool ctrl = keystatus[sc_LeftControl] || keystatus[sc_RightControl];
bool shift = keystatus[sc_LeftShift] || keystatus[sc_RightShift];
switch (key)
{
case sc_Escape:
Term();
break;
case sc_F1:
case sc_F2:
case sc_F3:
case sc_F4:
case sc_F5:
case sc_F6:
case sc_F7:
case sc_F8:
case sc_F9:
case sc_F10:
CONTROL_ClearButton(gamefunc_See_Chase_View);
Set(CommbatMacro[key-sc_F1]);
Send();
keystatus[key] = 0;
break;
case sc_BackSpace:
if (ctrl)
Clear();
else
DelChar();
break;
case sc_Enter:
case sc_kpad_Enter:
if (gCheatMgr.Check(text))
2019-09-19 22:42:45 +00:00
Term();
else
Send();
break;
default:
if (key < 128)
{
ch = shift ? g_keyAsciiTableShift[key] : g_keyAsciiTable[key];
if (ch)
AddChar(ch);
}
break;
}
sub_5A944(key);
}
}
bool CPlayerMsg::IsWhitespaceOnly(const char * const pzString)
{
const char *p = pzString;
while (*p != 0)
if (*p++ > 32)
return false;
return true;
}
2019-09-19 22:42:45 +00:00
CCheatMgr::CHEATINFO CCheatMgr::s_CheatInfo[] = {
{"NQLGB", kCheatMpkfa, 0 }, // MPKFA (Invincibility)
{"DBQJONZBTT", kCheatCapInMyAss, 0 }, // CAPINMYASS (Disable invincibility )
{"OPDBQJONZBTT", kCheatNoCapInMyAss, 0 }, // NOCAPINMYASS (Invincibility)
{"J!XBOOB!CF!MJLF!LFWJO", kCheatNoCapInMyAss, 0 }, // I WANNA BE LIKE KEVIN (Invincibility)
{"JEBIP", kCheatIdaho, 0 }, // IDAHO (All weapons and full ammo)
{"NPOUBOB", kCheatMontana, 0 }, // MONTANA (All weapons, full ammo and all items)
{"HSJTXPME", kCheatGriswold, 0 }, // GRISWOLD (Full armor (same effect as getting super armor))
{"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".)
{"UFRVJMB", kCheatTequila, 0 }, // TEQUILA (Guns akimbo power-up)
{"CVO[", kCheatBunz, 0 }, // BUNZ (All weapons, full ammo, and guns akimbo power-up)
{"GVOLZ!TIPFT", kCheatFunkyShoes, 0 }, // FUNKY SHOES (Gives jump boots item and activates it)
{"HBUFLFFQFS", kCheatGateKeeper, 0 }, // GATEKEEPER (Sets the you cheated flag to true, at the end of the level you will see that you have cheated)
{"LFZNBTUFS", kCheatKeyMaster, 0 }, // KEYMASTER (All keys)
{"KPKP", kCheatJoJo, 0 }, // JOJO (Drunk mode (same effect as getting bitten by red spider))
{"TBUDIFM", kCheatSatchel, 0 }, // SATCHEL (Full inventory)
{"TQPSL", kCheatSpork, 0 }, // SPORK (200% health (same effect as getting life seed))
{"POFSJOH", kCheatOneRing, 0 }, // ONERING (Cloak of invisibility power-up)
{"NBSJP", kCheatMario, 1 }, // MARIO (Warp to level E M, e.g.: MARIO 1 3 will take you to Phantom Express)
{"DBMHPO", kCheatCalgon, 1 }, // CALGON (Jumps to next level or can be used like MARIO with parameters)
{"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".)
{"NDHFF", kCheatMcGee, 0 }, // MCGEE (Sets you on fire. Displays the message "YOU'RE FIRED".)
{"LSVFHFS", kCheatKrueger, 0 }, // KRUEGER (200% health, but sets you on fire. Displays the message "FLAME RETARDANT".)
{"DIFFTFIFBE", kCheatCheeseHead, 0 }, // CHEESEHEAD (100% diving suit)
{"DPVTUFBV", kCheatCousteau, 0 }, // COUSTEAU (200% health and diving suit)
{"WPPSIFFT", kCheatVoorhees, 0 }, // VOORHEES (Death mask power-up)
{"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.)
{"IPOHLPOH", kCheatHongKong, 0 }, // HONGKONG (All weapons and infinite ammo)
{"GSBOLFOTUFJO", kCheatFrankenstein, 0 }, // FRANKENSTEIN (100% med-kit)
{"TUFSOP", kCheatSterno, 0 }, // STERNO (Temporary blindness (same effect as getting bitten by green spider))
{"DMBSJDF", kCheatClarice, 0 }, // CLARICE (Gives 100% body armor, 100% fire armor, 100% spirit armor)
{"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)
{"MJFCFSNBO", kCheatLieberMan, 0 }, // LIEBERMAN (Sets the you cheated flag to true, at the end of the level you will see that you have cheated)
{"FWB!HBMMJ", kCheatEvaGalli, 0 }, // EVA GALLI (Disable/enable clipping (grant the ability to walk through walls))
{"SBUF", kCheatRate, 0 }, // RATE (Display frame rate (doesn't count as a cheat))
{"HPPOJFT", kCheatGoonies, 0 }, // GOONIES (Enable full map. Displays the message "YOU HAVE THE MAP".)
{"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.)
};
bool CCheatMgr::m_bPlayerCheated = false;
bool CCheatMgr::Check(char *pzString)
{
char buffer[80];
strcpy(buffer, pzString);
Bstrupr(buffer);
for (size_t i = 0; i < strlen(pzString); i++)
buffer[i]++;
for (int i = 0; i < 36; i++)
{
int nCheatLen = strlen(s_CheatInfo[i].pzString);
if (s_CheatInfo[i].flags & 1)
{
if (!strncmp(buffer, s_CheatInfo[i].pzString, nCheatLen))
{
Process(s_CheatInfo[i].id, buffer+nCheatLen);
return true;
}
}
if (!strcmp(buffer, s_CheatInfo[i].pzString))
{
Process(s_CheatInfo[i].id, NULL);
return true;
}
}
return false;
}
int parseArgs(char *pzArgs, int *nArg1, int *nArg2)
{
if (!nArg1 || !nArg2)
return -1;
int nLength = strlen(pzArgs);
for (int i = 0; i < nLength; i++)
pzArgs[i]--;
int stat = sscanf(pzArgs, " %d %d", nArg1, nArg2);
if (stat == 2 && (*nArg1 == 0 || *nArg2 == 0))
return -1;
*nArg1 = ClipRange(*nArg1-1, 0, gEpisodeCount-1);
*nArg2 = ClipRange(*nArg2-1, 0, gEpisodeInfo[*nArg1].nLevels-1);
return stat;
}
void CCheatMgr::Process(CCheatMgr::CHEATCODE nCheatCode, char* pzArgs)
{
dassert(nCheatCode > kCheatNone && nCheatCode < kCheatMax);
if (gDemo.at0) return;
if (nCheatCode == kCheatRate)
{
r_showfps = !r_showfps;
2019-09-19 22:42:45 +00:00
return;
}
if (gGameOptions.nGameType != 0)
return;
int nEpisode, nLevel;
switch (nCheatCode)
{
case kCheatSpielberg:
if (parseArgs(pzArgs, &nEpisode, &nLevel) == 2)
LevelWarpAndRecord(nEpisode, nLevel);
break;
case kCheat1:
SetAmmo(true);
break;
case kCheatGriswold:
SetArmor(true);
break;
case kCheatSatchel:
SetToys(true);
break;
case kCheatEvaGalli:
SetClipMode(!gNoClip);
break;
case kCheatMpkfa:
SetGodMode(!gMe->godMode);
2019-09-19 22:42:45 +00:00
break;
case kCheatCapInMyAss:
SetGodMode(false);
break;
case kCheatNoCapInMyAss:
SetGodMode(true);
break;
case kCheatIdaho:
SetWeapons(true);
break;
case kCheatKevorkian:
actDamageSprite(gMe->nSprite, gMe->pSprite, DAMAGE_TYPE_2, 8000);
2019-09-19 22:42:45 +00:00
viewSetMessage("Kevorkian approves.");
break;
case kCheatMcGee:
{
if (!gMe->pXSprite->burnTime)
evPost(gMe->nSprite, 3, 0, kCallbackFXFlameLick);
actBurnSprite(actSpriteIdToOwnerId(gMe->nSprite), gMe->pXSprite, 2400);
2019-09-19 22:42:45 +00:00
viewSetMessage("You're fired!");
break;
}
case kCheatEdmark:
actDamageSprite(gMe->nSprite, gMe->pSprite, DAMAGE_TYPE_3, 8000);
2019-09-19 22:42:45 +00:00
viewSetMessage("Ahhh...those were the days.");
break;
case kCheatKrueger:
{
actHealDude(gMe->pXSprite, 200, 200);
gMe->armor[1] = VanillaMode() ? 200 : 3200;
2019-09-19 22:42:45 +00:00
if (!gMe->pXSprite->burnTime)
evPost(gMe->nSprite, 3, 0, kCallbackFXFlameLick);
actBurnSprite(actSpriteIdToOwnerId(gMe->nSprite), gMe->pXSprite, 2400);
2019-09-19 22:42:45 +00:00
viewSetMessage("Flame retardant!");
break;
}
case kCheatSterno:
gMe->blindEffect = 250;
2019-09-19 22:42:45 +00:00
break;
case kCheat14: // quakeEffect (causing a little flickerEffect), not used by any cheat code (dead code)
gMe->flickerEffect = 360;
2019-09-19 22:42:45 +00:00
break;
case kCheatSpork:
actHealDude(gMe->pXSprite, 200, 200);
break;
case kCheatGoonies:
SetMap(!gFullMap);
break;
case kCheatClarice:
if (!VanillaMode())
{
viewSetMessage("You have half armor.");
for (int i = 0; i < 3; i++)
gMe->armor[i] = 1600;
2019-09-19 22:42:45 +00:00
}
break;
case kCheatFrankenstein:
gMe->packSlots[0].curAmount = 100;
2019-09-19 22:42:45 +00:00
break;
case kCheatCheeseHead:
gMe->packSlots[1].curAmount = 100;
2019-09-19 22:42:45 +00:00
if (!VanillaMode())
gMe->pwUpTime[kPwUpDivingSuit] = gPowerUpInfo[kPwUpDivingSuit].bonusTime;
2019-09-19 22:42:45 +00:00
break;
case kCheatTequila:
ToggleWooMode();
break;
case kCheatFunkyShoes:
ToggleBoots();
break;
case kCheatKeyMaster:
SetKeys(true);
break;
case kCheatOneRing:
ToggleInvisibility();
break;
case kCheatVoorhees:
ToggleInvulnerability();
break;
case kCheatJoJo:
ToggleDelirium();
break;
case kCheatRate: // show FPS, handled before (dead code), leave here for safety
return;
case kCheatMario:
if (parseArgs(pzArgs, &nEpisode, &nLevel) == 2)
LevelWarp(nEpisode, nLevel);
break;
case kCheatCalgon:
if (parseArgs(pzArgs, &nEpisode, &nLevel) == 2)
LevelWarp(nEpisode, nLevel);
else
if (!VanillaMode())
levelEndLevel(0);
break;
case kCheatLaraCroft:
SetInfiniteAmmo(!gInfiniteAmmo);
SetWeapons(gInfiniteAmmo);
break;
case kCheatHongKong:
SetWeapons(true);
SetInfiniteAmmo(true);
break;
case kCheatMontana:
SetWeapons(true);
SetToys(true);
break;
case kCheatBunz:
SetWeapons(true);
SetWooMode(true);
break;
case kCheatCousteau:
actHealDude(gMe->pXSprite,200,200);
gMe->packSlots[1].curAmount = 100;
2019-09-19 22:42:45 +00:00
if (!VanillaMode())
gMe->pwUpTime[kPwUpDivingSuit] = gPowerUpInfo[kPwUpDivingSuit].bonusTime;
2019-09-19 22:42:45 +00:00
break;
case kCheatForkYou:
SetInfiniteAmmo(false);
SetMap(false);
SetWeapons(false);
SetAmmo(false);
SetArmor(false);
SetToys(false);
SetKeys(false);
SetWooMode(true);
powerupActivate(gMe, kPwUpDeliriumShroom);
2019-09-19 22:42:45 +00:00
gMe->pXSprite->health = 16;
gMe->hasWeapon[1] = 1;
gMe->curWeapon = 0;
gMe->nextWeapon = 1;
2019-09-19 22:42:45 +00:00
break;
default:
break;
}
m_bPlayerCheated = true;
}
void CCheatMgr::sub_5BCF4(void)
{
m_bPlayerCheated = 0;
playerSetGodMode(gMe, 0);
gNoClip = 0;
packClear(gMe);
gInfiniteAmmo = 0;
gFullMap = 0;
}
class MessagesLoadSave : public LoadSave
{
public:
virtual void Load();
virtual void Save();
};
void MessagesLoadSave::Load()
{
Read(&CCheatMgr::m_bPlayerCheated, sizeof(CCheatMgr::m_bPlayerCheated));
}
void MessagesLoadSave::Save()
{
Write(&CCheatMgr::m_bPlayerCheated, sizeof(CCheatMgr::m_bPlayerCheated));
}
static MessagesLoadSave *myLoadSave;
void MessagesLoadSaveConstruct(void)
{
myLoadSave = new MessagesLoadSave();
}
END_BLD_NS