diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index f00e3f72f..3ddc669ce 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -786,6 +786,8 @@ set (PCH_SOURCES core/animlib.cpp core/mathutil.cpp core/rts.cpp + core/ct_chat.cpp + core/entercheat.cpp core/gameconfigfile.cpp core/gamecvars.cpp core/gamecontrol.cpp diff --git a/source/core/ct_chat.cpp b/source/core/ct_chat.cpp new file mode 100644 index 000000000..f27fee083 --- /dev/null +++ b/source/core/ct_chat.cpp @@ -0,0 +1,534 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1994-1996 Raven Software +// Copyright 1999-2016 Randy Heit +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// + + +#include +#include +#include "m_swap.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "v_text.h" +#include "d_gui.h" +#include "g_input.h" +#include "d_event.h" +#include "v_video.h" +#include "utf8.h" +#include "gstrings.h" +#include "vm.h" +#include "c_buttons.h" +#include "v_draw.h" +#include "menu.h" + +enum +{ + QUEUESIZE = 128 +}; + + +EXTERN_CVAR (Int, con_scaletext) + +EXTERN_CVAR (Bool, sb_cooperative_enable) +EXTERN_CVAR (Bool, sb_deathmatch_enable) +EXTERN_CVAR (Bool, sb_teamdeathmatch_enable) + +int active_con_scaletext(); + +// Public data + +void CT_Init (); +void CT_Drawer (); +bool CT_Responder (event_t *ev); +void CT_PasteChat(const char *clip); + +int chatmodeon; + +// Private data + +static void CT_ClearChatMessage (); +static void CT_AddChar (int c); +static void CT_BackSpace (); +static void ShoveChatStr (const char *str, uint8_t who); +static bool DoSubstitution (FString &out, const char *in); + +static TArray ChatQueue; + +#if 0 +CVAR (String, chatmacro1, "I'm ready to kick butt!", CVAR_ARCHIVE) +CVAR (String, chatmacro2, "I'm OK.", CVAR_ARCHIVE) +CVAR (String, chatmacro3, "I'm not looking too good!", CVAR_ARCHIVE) +CVAR (String, chatmacro4, "Help!", CVAR_ARCHIVE) +CVAR (String, chatmacro5, "You suck!", CVAR_ARCHIVE) +CVAR (String, chatmacro6, "Next time, scumbag...", CVAR_ARCHIVE) +CVAR (String, chatmacro7, "Come here!", CVAR_ARCHIVE) +CVAR (String, chatmacro8, "I'll take care of it.", CVAR_ARCHIVE) +CVAR (String, chatmacro9, "Yes", CVAR_ARCHIVE) +CVAR (String, chatmacro0, "No", CVAR_ARCHIVE) + +FStringCVar *chat_macros[10] = +{ + &chatmacro0, + &chatmacro1, + &chatmacro2, + &chatmacro3, + &chatmacro4, + &chatmacro5, + &chatmacro6, + &chatmacro7, + &chatmacro8, + &chatmacro9 +}; +#endif + +CVAR (Bool, chat_substitution, false, CVAR_ARCHIVE) + +//=========================================================================== +// +// CT_Init +// +// Initialize chat mode data +//=========================================================================== + +void CT_Init () +{ + ChatQueue.Clear(); + chatmodeon = 0; +} + +//=========================================================================== +// +// CT_Stop +// +//=========================================================================== + +void CT_Stop () +{ + chatmodeon = 0; +} + +//=========================================================================== +// +// CT_Responder +// +//=========================================================================== + +bool CT_Responder (event_t *ev) +{ + if (chatmodeon && ev->type == EV_GUI_Event) + { + if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) + { + if (ev->data1 == '\r') + { + ChatQueue.Push(0); + ShoveChatStr ((char *)ChatQueue.Data(), chatmodeon - 1); + ChatQueue.Pop(); + CT_Stop (); + return true; + } + else if (ev->data1 == GK_ESCAPE) + { + CT_Stop (); + return true; + } + else if (ev->data1 == '\b') + { + CT_BackSpace (); + return true; + } +#ifdef __APPLE__ + else if (ev->data1 == 'C' && (ev->data3 & GKM_META)) +#else // !__APPLE__ + else if (ev->data1 == 'C' && (ev->data3 & GKM_CTRL)) +#endif // __APPLE__ + { + ChatQueue.Push(0); + I_PutInClipboard ((char *)ChatQueue.Data()); + ChatQueue.Pop(); + return true; + } +#ifdef __APPLE__ + else if (ev->data1 == 'V' && (ev->data3 & GKM_META)) +#else // !__APPLE__ + else if (ev->data1 == 'V' && (ev->data3 & GKM_CTRL)) +#endif // __APPLE__ + { + CT_PasteChat(I_GetFromClipboard(false)); + } + } + else if (ev->subtype == EV_GUI_Char) + { + // send a macro +#if 0 + if (ev->data2 && (ev->data1 >= '0' && ev->data1 <= '9')) + { + ShoveChatStr (*chat_macros[ev->data1 - '0'], chatmodeon - 1); + CT_Stop (); + } + else +#endif + { + CT_AddChar (ev->data1); + } + return true; + } +#ifdef __unix__ + else if (ev->subtype == EV_GUI_MButtonDown) + { + CT_PasteChat(I_GetFromClipboard(true)); + } +#endif + } + + return false; +} + +//=========================================================================== +// +// CT_PasteChat +// +//=========================================================================== + +void CT_PasteChat(const char *clip) +{ + if (clip != nullptr && *clip != '\0') + { + auto p = (const uint8_t *)clip; + // Only paste the first line. + while (auto chr = GetCharFromString(p)) + { + if (chr == '\n' || chr == '\r' || chr == '\b') + { + break; + } + CT_AddChar (chr); + } + } +} + +//=========================================================================== +// +// CT_Drawer +// +//=========================================================================== + +void CT_Drawer (void) +{ + auto drawer = twod; + FFont *displayfont = NewConsoleFont; + + if (chatmodeon) + { + FStringf prompt("%s ", GStrings("TXT_SAY")); + int x, scalex, y, promptwidth; + + y = -displayfont->GetHeight()-2; + + scalex = 1; + int scale = active_con_scaletext(drawer); + int screen_width = twod->GetWidth() / scale; + int screen_height= twod->GetHeight() / scale; +#if 0 // stuff for later + int st_y = StatusBar->GetTopOfStatusbar() / scale; + + y += ((twod->GetHeight() == viewheight && viewactive) || gamestate != GS_LEVEL) ? screen_height : st_y; +#endif + promptwidth = displayfont->StringWidth (prompt) * scalex; + x = displayfont->GetCharWidth (displayfont->GetCursor()) * scalex * 2 + promptwidth; + + FString printstr = ChatQueue; + // figure out if the text is wider than the screen + // if so, only draw the right-most portion of it. + const uint8_t *textp = (const uint8_t*)printstr.GetChars(); + while(*textp) + { + auto textw = displayfont->StringWidth(textp); + if (x + textw * scalex < screen_width) break; + GetCharFromString(textp); + } + printstr += displayfont->GetCursor(); + + DrawText(drawer, displayfont, CR_GREEN, 0, y, prompt.GetChars(), + DTA_VirtualWidth, screen_width, DTA_VirtualHeight, screen_height, DTA_KeepRatio, true, TAG_DONE); + DrawText(drawer, displayfont, CR_GREY, promptwidth, y, printstr, + DTA_VirtualWidth, screen_width, DTA_VirtualHeight, screen_height, DTA_KeepRatio, true, TAG_DONE); + } +} + +//=========================================================================== +// +// CT_AddChar +// +//=========================================================================== + +static void CT_AddChar (int c) +{ + if (ChatQueue.Size() < QUEUESIZE-2) + { + int size; + auto encode = MakeUTF8(c, &size); + if (*encode) + { + for (int i = 0; i < size; i++) + { + ChatQueue.Push(encode[i]); + } + } + } +} + +//=========================================================================== +// +// CT_BackSpace +// +// Backs up a space, when the user hits (obviously) backspace +//=========================================================================== + +static void CT_BackSpace () +{ + if (ChatQueue.Size()) + { + int endpos = ChatQueue.Size() - 1; + while (endpos > 0 && ChatQueue[endpos] >= 0x80 && ChatQueue[endpos] < 0xc0) endpos--; + ChatQueue.Clamp(endpos); + } +} + +//=========================================================================== +// +// CT_ClearChatMessage +// +// Clears out the data for the chat message. +//=========================================================================== + +static void CT_ClearChatMessage () +{ + ChatQueue.Clear(); +} + +//=========================================================================== +// +// ShoveChatStr +// +// Sends the chat message across the network +// +//=========================================================================== + +static void ShoveChatStr (const char *str, uint8_t who) +{ +#if 0 + // Don't send empty messages + if (str == NULL || str[0] == '\0') + return; + + FString substBuff; + + if (str[0] == '/' && + (str[1] == 'm' || str[1] == 'M') && + (str[2] == 'e' || str[2] == 'E')) + { // This is a /me message + str += 3; + who |= 2; + } + + Net_WriteByte (DEM_SAY); + Net_WriteByte (who); + + if (!chat_substitution || !DoSubstitution (substBuff, str)) + { + Net_WriteString(MakeUTF8(str)); + } + else + { + Net_WriteString(MakeUTF8(substBuff)); + } +#endif +} + +//=========================================================================== +// +// DoSubstitution +// +// Replace certain special substrings with different values to reflect +// the player's current state. +// +//=========================================================================== + +static bool DoSubstitution (FString &out, const char *in) +{ +#if 0 + player_t *player = &players[consoleplayer]; + auto weapon = player->ReadyWeapon; + auto ammo1 = weapon ? weapon->PointerVar(NAME_Ammo1) : nullptr; + auto ammo2 = weapon ? weapon->PointerVar(NAME_Ammo2) : nullptr; + const char *a, *b; + + a = in; + out = ""; + while ( (b = strchr(a, '$')) ) + { + out.AppendCStrPart(a, b - a); + + a = ++b; + while (*b && isalpha(*b)) + { + ++b; + } + + ptrdiff_t ByteLen = b - a; + + if (ByteLen == 6) + { + if (strnicmp(a, "health", 6) == 0) + { + out.AppendFormat("%d", player->health); + } + else if (strnicmp(a, "weapon", 6) == 0) + { + if (weapon == NULL) + { + out += "no weapon"; + } + else + { + out += weapon->GetClass()->TypeName.GetChars(); + } + } + } + else if (ByteLen == 5) + { + if (strnicmp(a, "armor", 5) == 0) + { + auto armor = player->mo->FindInventory(NAME_BasicArmor); + out.AppendFormat("%d", armor != NULL ? armor->IntVar(NAME_Amount) : 0); + } + } + else if (ByteLen == 9) + { + if (strnicmp(a, "ammocount", 9) == 0) + { + if (weapon == NULL) + { + out += '0'; + } + else + { + out.AppendFormat("%d", ammo1 != NULL ? ammo1->IntVar(NAME_Amount) : 0); + if (ammo2 != NULL) + { + out.AppendFormat("/%d", ammo2->IntVar(NAME_Amount)); + } + } + } + } + else if (ByteLen == 4) + { + if (strnicmp(a, "ammo", 4) == 0) + { + if (ammo1 == NULL) + { + out += "no ammo"; + } + else + { + out.AppendFormat("%s", ammo1->GetClass()->TypeName.GetChars()); + if (ammo2 != NULL) + { + out.AppendFormat("/%s", ammo2->GetClass()->TypeName.GetChars()); + } + } + } + } + else if (ByteLen == 0) + { + out += '$'; + if (*b == '$') + { + b++; + } + } + else + { + out += '$'; + out.AppendCStrPart(a, ByteLen); + } + a = b; + } + + // Return false if no substitution was performed + if (a == in) + { + return false; + } + + out += a; + return true; +#else + return false; +#endif +} + +CCMD (messagemode) +{ + if (menuactive == MENU_Off) + { + chatmodeon = 1; + C_HideConsole (); + CT_ClearChatMessage (); + } +} + + +CCMD (messagemode2) +{ + if (menuactive == MENU_Off) + { + chatmodeon = 2; + C_HideConsole (); + CT_ClearChatMessage (); + } +} + +#if 0 +CCMD (say) +{ + if (argv.argc() == 1) + { + Printf ("Usage: say \n"); + } + else + { + ShoveChatStr (argv[1], 0); + } +} + +CCMD (say_team) +{ + if (argv.argc() == 1) + { + Printf ("Usage: say_team \n"); + } + else + { + ShoveChatStr (argv[1], 1); + } +} +#endif diff --git a/source/core/entercheat.cpp b/source/core/entercheat.cpp new file mode 100644 index 000000000..a9287d8bb --- /dev/null +++ b/source/core/entercheat.cpp @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1994-1996 Raven Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2020 Christoph Oelckers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// DESCRIPTION: +// Cheat code. See *_sbar.cpp for status bars. +// +//----------------------------------------------------------------------------- + +#include "gstrings.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "d_event.h" +#include "baselayer.h" +#include "gamecontrol.h" + +struct cheatseq_t +{ + const uint8_t *Sequence; + const uint8_t *Pos; + uint8_t CurrentArg; + uint8_t Args[5]; +}; + +static TArray cheats; + +static bool CheatCheckList (event_t *ev); +static bool CheatAddKey (cheatseq_t *cheat, uint8_t key, bool *eat); + + +CVAR(Bool, allcheats, false, CVAR_ARCHIVE) +CVAR(Bool, nocheats, false, CVAR_ARCHIVE) + +// Respond to keyboard input events, intercept cheats. +// [RH] Cheats eat the last keypress used to trigger them +bool Cheat_Responder (event_t *ev) +{ + bool eat = false; + + if (cheats.Size() == 0) + { + auto gcheats = gi->GetCheats(); + if (gcheats) + { + for (int i = 0; gcheats[i]; i++) + { + cheatseq_t cht = { (const uint8_t*)gcheats[i], nullptr }; + cheats.Push(cht); + } + } + } + + if (nocheats) + { + return false; + } + else + { + return CheatCheckList(ev); + } + return false; +} + +static bool CheatCheckList (event_t *ev) +{ + bool eat = false; + + if (ev->type == EV_KeyDown) + { + int i; + + for (auto &cht :cheats) + { + if (CheatAddKey (&cht, (uint8_t)ev->data2, &eat)) + { + int processed = gi->CheckCheat((const char*)cht.Sequence, (const char*)cht.Args); + if (processed = 1) cht.Pos = nullptr; + eat |= processed != 0; + } + else if (cht.Pos - cht.Sequence > 2) + { // If more than two characters into the sequence, + // eat the keypress, to reduce interference with game controls. + eat = true; + } + } + } + return eat; +} + +//-------------------------------------------------------------------------- +// +// FUNC CheatAddkey +// +// Returns true if the added key completed the cheat, false otherwise. +// +//-------------------------------------------------------------------------- + +static bool CheatAddKey (cheatseq_t *cheat, uint8_t key, bool *eat) +{ + if (cheat->Pos == NULL) + { + cheat->Pos = cheat->Sequence; + cheat->CurrentArg = 0; + cheat->Args[0] = 0; + } + if (*cheat->Pos == '#' && key >= '0' && key <= '9') + { + *eat = true; + cheat->Args[cheat->CurrentArg++] = key; + cheat->Args[cheat->CurrentArg] = 0; + cheat->Pos++; + return true; + } + else if (key == *cheat->Pos) + { + cheat->Pos++; + } + else + { + cheat->Pos = cheat->Sequence; + } + if (*cheat->Pos == 0) + { + cheat->Pos = cheat->Sequence; + return true; + } + return false; +} + diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 89ae47216..c450ed0df 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -123,7 +123,6 @@ void LoadScripts(); bool AppActive; -int chatmodeon; // needed by the common console code. FString currentGame; FString LumpFilter; @@ -159,6 +158,7 @@ static StringtableCallbacks stblcb = StrTable_GetGender }; +extern int chatmodeon; bool System_WantGuiCapture() { diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h index e20368a85..6e74fdebf 100644 --- a/source/core/gamestruct.h +++ b/source/core/gamestruct.h @@ -90,6 +90,7 @@ struct GameInterface virtual void SetAmbience(bool on) {} virtual FString GetCoordString() { return "'stat coord' not implemented"; } virtual int GetStringTile(int font, const char* t, int f) { return -1; } + virtual int CheckCheat(const char* cheat, const char* args) { return 0; } }; diff --git a/source/core/uiinput.h b/source/core/uiinput.h new file mode 100644 index 000000000..8dd4f49ae --- /dev/null +++ b/source/core/uiinput.h @@ -0,0 +1,12 @@ +#pragma once + +// +// Chat and cheat routines +// +struct event; + +void CT_Init (void); +bool CT_Responder (event_t* ev); +void CT_Drawer (void); +bool Cheat_Responder (event_t* ev); + \ No newline at end of file diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp new file mode 100644 index 000000000..2bac03601 --- /dev/null +++ b/source/games/duke/src/input.cpp @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2000, 2003 - Matt Saettler (EDuke Enhancements) + +This file is part of Enhanced Duke Nukem 3D version 1.5 - Atomic Edition + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms + +EDuke enhancements integrated: 04/13/2003 - Matt Saettler + +Note: EDuke source was in transition. Changes are in-progress in the +source as it is released. + +*/ +//------------------------------------------------------------------------- + + +#include "ns.h" +#include "global.h" + +BEGIN_DUKE_NS + +//--------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------