openmohaa/code/fgame/bg_voteoptions.cpp

742 lines
21 KiB
C++
Raw Normal View History

/*
===========================================================================
Copyright (C) 2023 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "bg_voteoptions.h"
#include "../qcommon/script.h"
#if defined(GAME_DLL)
# define VO_FS_FreeFile gi.FS_FreeFile
# define VO_FS_ReadFile(a, b) gi.FS_ReadFile(a, b, true)
# define VO_SendRemoteCommand(s) // ?
# define VO_ExecuteCommand(s) gi.Cmd_Execute(EXEC_NOW, s)
# define VO_Cvar_Set(name, value) gi.cvar_set(name, value)
# define VO_Cvar_Get(name, value, flags) gi.Cvar_Get(name, value, flags)
# define VO_LV_ConvertString(s) gi.LV_ConvertString(s)
#elif defined(CGAME_DLL)
# define VO_FS_FreeFile cgi.FS_FreeFile
# define VO_FS_ReadFile(a, b) cgi.FS_ReadFile(a, b, qtrue)
# define VO_SendRemoteCommand(s) cgi.SendClientCommand(s)
# define VO_ExecuteCommand(s) cgi.Cmd_Execute(EXEC_NOW, s)
# define VO_Cvar_Set(name, value) cgi.Cvar_Set(name, value)
# define VO_Cvar_Get(name, value, flags) cgi.Cvar_Get(name, value, flags)
# define VO_LV_ConvertString(s) cgi.LV_ConvertString(s)
#else
# pragma error("VoteOptions can must be used with fgame or cgame")
#endif
CLASS_DECLARATION(Class, VoteOptions, NULL) {
{NULL, NULL}
};
VoteOptionListItem::VoteOptionListItem()
: m_pNext(NULL)
{}
SingleVoteOption::SingleVoteOption()
: m_optionType(VOTE_NO_CHOICES)
, m_pListItem(NULL)
, m_pNext(NULL)
{}
SingleVoteOption::~SingleVoteOption()
{
VoteOptionListItem *item;
VoteOptionListItem *next;
for (item = m_pListItem; item; item = next) {
next = item->m_pNext;
delete item;
}
}
VoteOptions::VoteOptions()
{
m_pHeadOption = NULL;
}
VoteOptions::~VoteOptions()
{
ClearOptions();
}
void VoteOptions::ClearOptions()
{
SingleVoteOption *option;
SingleVoteOption *next;
for (option = m_pHeadOption; option; option = next) {
next = option->m_pNext;
delete option;
}
m_pHeadOption = NULL;
}
void VoteOptions::SetupVoteOptions(const char *configFileName)
{
char *buffer;
int compressedLength;
long length;
length = VO_FS_ReadFile(configFileName, (void **)&buffer);
if (length == -1 || !length) {
Com_Printf("WARNING: Couldn't find voting options file: %s\n", configFileName);
return;
}
// compress the buffer before setting up
compressedLength = COM_Compress(buffer);
SetupVoteOptions(configFileName, compressedLength, buffer);
VO_FS_FreeFile(buffer);
}
void VoteOptions::SetupVoteOptions(const char *configFileName, int length, const char *buffer)
{
if (length >= MAX_VOTEOPTIONS_BUFFER_LENGTH) {
Com_Error(
ERR_DROP,
2024-06-07 20:34:13 +02:00
"VoteOptions: Options file '%s' is too big. Max size is %lu bytes\n",
configFileName,
MAX_VOTEOPTIONS_BUFFER_LENGTH
);
return;
}
m_sFileName = configFileName;
m_sBuffer = buffer;
ParseVoteOptions();
}
void VoteOptions::ParseVoteOptions()
{
SingleVoteOption *option;
SingleVoteOption *newOption;
VoteOptionListItem *listItem;
VoteOptionListItem *newListItem;
str token;
Script script;
ClearOptions();
script.LoadFile(m_sFileName.c_str(), m_sBuffer.length(), m_sBuffer.c_str());
option = m_pHeadOption;
while (script.TokenAvailable(true)) {
token = script.GetToken(true);
if (!str::icmp(token, "{")) {
Com_Error(
ERR_DROP,
"Vote Options %s: Found choices list without option header on line %d.\n",
m_sFileName.c_str(),
script.GetLineNumber()
);
return;
}
if (!str::icmp(token, "}")) {
Com_Error(
ERR_DROP,
"Vote Options %s: Illegal end of choices list without list being started on line %d.\n",
m_sFileName.c_str(),
script.GetLineNumber()
);
return;
}
if (!token.length()) {
Com_Error(
ERR_DROP,
"Vote Options %s: Empty option name on line %d.\n",
m_sFileName.c_str(),
script.GetLineNumber()
);
return;
}
newOption = new SingleVoteOption();
if (option) {
option->m_pNext = newOption;
} else {
m_pHeadOption = newOption;
}
option = newOption;
newOption->m_sOptionName = token;
if (!script.TokenAvailable(false)) {
Com_Error(
ERR_DROP,
"Vote Options %s: Option without a command specified on line %d.\n",
m_sFileName.c_str(),
script.GetLineNumber()
);
return;
}
newOption->m_sCommand = script.GetToken(false);
if (script.TokenAvailable(false)) {
token = script.GetToken(false);
if (!str::icmp(token, "nochoices")) {
newOption->m_optionType = VOTE_NO_CHOICES;
} else if (!str::icmp(token, "list")) {
newOption->m_optionType = VOTE_OPTION_LIST;
} else if (!str::icmp(token, "text")) {
newOption->m_optionType = VOTE_OPTION_TEXT;
} else if (!str::icmp(token, "integer")) {
newOption->m_optionType = VOTE_OPTION_INTEGER;
} else if (!str::icmp(token, "float")) {
newOption->m_optionType = VOTE_OPTION_FLOAT;
} else if (!str::icmp(token, "client")) {
newOption->m_optionType = VOTE_OPTION_CLIENT;
} else if (!str::icmp(token, "clientnotself")) {
newOption->m_optionType = VOTE_OPTION_CLIENT_NOT_SELF;
} else {
Com_Error(
ERR_DROP,
"Vote Options %s: Illegal option type '%s' specified on line %d.\n"
" Valid types are nochoices, list, text, & number.\n",
m_sFileName.c_str(),
token.c_str(),
script.GetLineNumber()
);
return;
}
}
if (newOption->m_optionType == VOTE_OPTION_LIST) {
if (!script.TokenAvailable(true) || Q_stricmp(script.GetToken(true), "{")) {
Com_Error(
ERR_DROP,
"Vote Options %s: Missing '{'. No choices list specified for list option on line %d.\n",
m_sFileName.c_str(),
script.GetLineNumber()
);
return;
}
listItem = NULL;
while (script.TokenAvailable(true)) {
token = script.GetToken(true);
if (!str::icmp(token, "}")) {
break;
}
newListItem = new VoteOptionListItem();
if (listItem) {
listItem->m_pNext = newListItem;
} else {
newOption->m_pListItem = newListItem;
}
2024-10-02 14:09:36 +02:00
listItem = newListItem;
2023-12-31 00:53:26 +01:00
newListItem->m_sItemName = token;
if (!script.TokenAvailable(false)) {
Com_Error(
ERR_DROP,
"Vote Options %s: List choice without vote string specified on line %d.\n",
m_sFileName.c_str(),
script.GetLineNumber()
);
return;
}
newListItem->m_sCommand = script.GetToken(false);
}
} else if (script.TokenAvailable(true)) {
token = script.GetToken(true);
if (!str::icmp(token, "{")) {
Com_Error(
ERR_DROP,
"Vote Options %s: Choices list specified for non-list option on line %d.\n",
m_sFileName.c_str(),
script.GetLineNumber()
);
return;
}
script.UnGetToken();
}
}
}
const char *VoteOptions::GetVoteOptionsFile(int *outLen) const
{
if (outLen) {
*outLen = m_sBuffer.length();
}
return m_sBuffer.c_str();
}
bool VoteOptions::GetVoteOptionsMain(int index, str *outOptionCommand, voteoptiontype_t *outOptionType) const
{
SingleVoteOption *option;
int optionIndex;
if (index < 1) {
return false;
}
optionIndex = 1;
for (option = m_pHeadOption; optionIndex < index && option != NULL; option = option->m_pNext) {
optionIndex++;
}
if (!option) {
return false;
}
*outOptionCommand = option->m_sCommand;
*outOptionType = option->m_optionType;
return true;
}
bool VoteOptions::GetVoteOptionSub(int index, int listIndex, str *outCommand) const
{
SingleVoteOption *option;
VoteOptionListItem *item;
int optionIndex;
int itemIndex;
if (index < 1 || listIndex < 1) {
return false;
}
optionIndex = 1;
for (option = m_pHeadOption; optionIndex < index && option != NULL; option = option->m_pNext) {
optionIndex++;
}
if (!option) {
return false;
}
if (option->m_optionType != VOTE_OPTION_LIST) {
return false;
}
itemIndex = 1;
for (item = option->m_pListItem; itemIndex < listIndex && option != NULL; item = item->m_pNext) {
itemIndex++;
}
if (!item) {
return false;
}
*outCommand = item->m_sCommand;
return true;
}
bool VoteOptions::GetVoteOptionMainName(int index, str *outVoteName) const
{
SingleVoteOption *option;
int optionIndex;
if (index < 1) {
return false;
}
optionIndex = 1;
for (option = m_pHeadOption; optionIndex < index && option != NULL; option = option->m_pNext) {
optionIndex++;
}
if (!option) {
return false;
}
*outVoteName = option->m_sOptionName;
return true;
}
bool VoteOptions::GetVoteOptionSubName(int index, int listIndex, str *outName) const
{
SingleVoteOption *option;
VoteOptionListItem *item;
int optionIndex;
int itemIndex;
if (index < 1 || listIndex < 1) {
return false;
}
optionIndex = 1;
for (option = m_pHeadOption; optionIndex < index && option != NULL; option = option->m_pNext) {
optionIndex++;
}
if (!option) {
return false;
}
if (option->m_optionType != VOTE_OPTION_LIST) {
return false;
}
itemIndex = 1;
for (item = option->m_pListItem; itemIndex < listIndex && option != NULL; item = item->m_pNext) {
itemIndex++;
}
if (!item) {
return false;
}
*outName = item->m_sItemName;
return true;
}
#if defined(CGAME_DLL)
static str g_sVoteString;
static VoteOptions g_voteOptions;
void CG_VoteOptions_StartReadFromServer(const char *string)
{
g_sVoteString = string;
}
void CG_VoteOptions_ContinueReadFromServer(const char *string)
{
if (g_sVoteString.length() >= MAX_VOTEOPTIONS_BUFFER_LENGTH) {
return;
}
g_sVoteString += string;
}
void CG_VoteOptions_FinishReadFromServer(const char *string)
{
int i;
if (g_sVoteString.length() >= MAX_VOTEOPTIONS_BUFFER_LENGTH) {
return;
}
g_sVoteString += va("%s\n", string);
if (!str::cmp(g_sVoteString, "\n")) {
VO_SendRemoteCommand("wait 250;gvo\n");
return;
}
for (i = 0; i < g_sVoteString.length(); i++) {
if (g_sVoteString[i] == 1) {
g_sVoteString[i] = '"';
}
}
g_voteOptions.SetupVoteOptions("ServerVoteOptions", g_sVoteString.length(), g_sVoteString.c_str());
g_sVoteString = "";
g_voteOptions.SetupMainOptionsList();
}
2023-12-31 00:39:43 +01:00
void CG_PushCallVote_f(void)
{
g_voteOptions.SetupMainOptionsList();
}
2023-12-31 00:39:43 +01:00
void CG_PushCallVoteSubList_f(void)
{
g_voteOptions.SetupSubOptionsList(atoi(cgi.Argv(1)));
}
2023-12-31 00:39:43 +01:00
void CG_PushCallVoteSubText_f(void)
{
str command;
str name;
voteoptiontype_t type;
if (!g_voteOptions.GetVoteOptionsMain(atoi(cgi.Argv(1)), &command, &type)) {
return;
}
if (type != voteoptiontype_t::VOTE_OPTION_TEXT) {
return;
}
g_voteOptions.GetVoteOptionMainName(atoi(cgi.Argv(1)), &name);
VO_ExecuteCommand("forcemenu votesubtext\n");
VO_Cvar_Set("ui_votesubtitle", name.c_str());
VO_Cvar_Set("ui_votestringentry", "");
}
2023-12-31 00:39:43 +01:00
void CG_PushCallVoteSubInteger_f(void)
{
str command;
str name;
voteoptiontype_t type;
if (!g_voteOptions.GetVoteOptionsMain(atoi(cgi.Argv(1)), &command, &type)) {
return;
}
if (type != voteoptiontype_t::VOTE_OPTION_INTEGER) {
return;
}
g_voteOptions.GetVoteOptionMainName(atoi(cgi.Argv(1)), &name);
VO_ExecuteCommand("forcemenu votesubinteger\n");
VO_Cvar_Set("ui_votesubtitle", name.c_str());
VO_Cvar_Set("ui_votestringentry", "");
}
2023-12-31 00:39:43 +01:00
void CG_PushCallVoteSubFloat_f(void)
{
str command;
str name;
voteoptiontype_t type;
if (!g_voteOptions.GetVoteOptionsMain(atoi(cgi.Argv(1)), &command, &type)) {
return;
}
if (type != voteoptiontype_t::VOTE_OPTION_FLOAT) {
return;
}
g_voteOptions.GetVoteOptionMainName(atoi(cgi.Argv(1)), &name);
VO_ExecuteCommand("forcemenu votesubfloat\n");
VO_Cvar_Set("ui_votesubtitle", name.c_str());
VO_Cvar_Set("ui_votestringentry", "");
}
2023-12-31 00:39:43 +01:00
void CG_PushCallVoteSubClient_f(void)
{
str command;
str name;
int index;
int i;
voteoptiontype_t type;
index = atoi(cgi.Argv(1));
if (!g_voteOptions.GetVoteOptionsMain(index, &command, &type)) {
return;
}
if (type != voteoptiontype_t::VOTE_OPTION_CLIENT && type != voteoptiontype_t::VOTE_OPTION_CLIENT_NOT_SELF) {
return;
}
g_voteOptions.GetVoteOptionMainName(index, &name);
VO_ExecuteCommand("forcemenu votesubclient\n");
VO_Cvar_Set("ui_votesubtitle", name.c_str());
VO_ExecuteCommand("globalwidgetcommand voteclientlist deleteallitems\n");
for (i = 0; i < cgs.maxclients; i++) {
if (type == VOTE_OPTION_CLIENT_NOT_SELF && cg.snap && i == cg.snap->ps.clientNum) {
continue;
}
if (!strlen(cg.clientinfo[i].name)) {
continue;
}
2024-10-02 14:09:36 +02:00
VO_ExecuteCommand(
va("globalwidgetcommand voteclientlist additem \"%i: %s\" \"callvote %i %i;popmenu 0\"\n",
i,
cg.clientinfo[i].name,
index,
i)
);
}
VO_ExecuteCommand(
va("globalwidgetcommand voteclientlist additem \"%s\" \"popmenu 0\"\n", VO_LV_ConvertString("[Cancel Vote]"))
);
}
2023-12-31 00:39:43 +01:00
void CG_PushVote_f(void)
{
VO_Cvar_Set("ui_votesubtitle", cgs.voteString);
VO_ExecuteCommand("forcemenu votecast\n");
}
2023-12-31 00:39:43 +01:00
void CG_CallEntryVote_f(void)
{
str command;
str name;
const char *entry;
int index;
voteoptiontype_t type;
index = VO_Cvar_Get("ui_votetype", "0", 0)->integer;
if (!g_voteOptions.GetVoteOptionsMain(index, &command, &type)) {
return;
}
if (type != voteoptiontype_t::VOTE_OPTION_TEXT && type != voteoptiontype_t::VOTE_OPTION_INTEGER
&& type != voteoptiontype_t::VOTE_OPTION_FLOAT) {
return;
}
entry = VO_Cvar_Get("ui_votestringentry", "", 0)->string;
VO_ExecuteCommand(va("callvote %i \"%s\"\n", index, entry));
}
2023-12-31 00:39:43 +01:00
void VoteOptions::SetupMainOptionsList(void)
{
SingleVoteOption *option;
int count;
VO_ExecuteCommand("forcemenu votemain\n");
VO_ExecuteCommand("globalwidgetcommand votelistmain deleteallitems\n");
if (!IsSetup()) {
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"popmenu 0\"\n",
VO_LV_ConvertString("Retrieving voting options from server..."))
);
VO_SendRemoteCommand("gvo\n");
return;
}
count = 1;
for (option = m_pHeadOption; option; option = option->m_pNext, count++) {
switch (option->m_optionType) {
case VOTE_NO_CHOICES:
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"callvote %i;popmenu 0\"\n",
option->m_sOptionName.c_str(),
count)
);
break;
case VOTE_OPTION_LIST:
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"popmenu 0;wait 100;pushcallvotesublist %i\"\n",
option->m_sOptionName.c_str(),
count)
);
break;
case VOTE_OPTION_TEXT:
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"popmenu 0;wait 100;set ui_votetype "
"%i;pushcallvotesubtext %i\"\n",
option->m_sOptionName.c_str(),
count,
count)
);
break;
case VOTE_OPTION_INTEGER:
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"popmenu 0;wait 100;set ui_votetype "
"%i;pushcallvotesubinteger %i\"\n",
option->m_sOptionName.c_str(),
count,
count)
);
break;
case VOTE_OPTION_FLOAT:
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"popmenu 0;wait 100;set ui_votetype "
"%i;pushcallvotesubfloat %i\"\n",
option->m_sOptionName.c_str(),
count,
count)
);
break;
case VOTE_OPTION_CLIENT:
case VOTE_OPTION_CLIENT_NOT_SELF:
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"popmenu 0;wait 100;set ui_votetype "
"%i;pushcallvotesubclient %i\"\n",
option->m_sOptionName.c_str(),
count,
count)
);
break;
}
}
VO_ExecuteCommand(
va("globalwidgetcommand votelistmain additem \"%s\" \"popmenu 0\"\n", VO_LV_ConvertString("[Cancel Vote]"))
);
}
void VoteOptions::SetupSubOptionsList(int index)
{
SingleVoteOption *option;
VoteOptionListItem *item;
int count;
int numItems;
if (index < 1) {
return;
}
count = 1;
for (option = m_pHeadOption; count < index && option; option = option->m_pNext) {
count++;
}
if (!option) {
return;
}
if (option->m_optionType != VOTE_OPTION_LIST || !option->m_pListItem) {
return;
}
VO_ExecuteCommand("forcemenu votesublist\n");
VO_Cvar_Set("ui_votesubtitle", option->m_sOptionName.c_str());
VO_ExecuteCommand("globalwidgetcommand votelistsub deleteallitems\n");
if (IsSetup()) {
numItems = 1;
for (item = option->m_pListItem; item; item = item->m_pNext, numItems++) {
VO_ExecuteCommand(
va("globalwidgetcommand votelistsub additem \"%s\" \"callvote %i %i;popmenu 0\"\n",
item->m_sItemName.c_str(),
index,
numItems)
);
}
VO_ExecuteCommand(
va("globalwidgetcommand votelistsub additem \"%s\" \"popmenu 0\"\n", VO_LV_ConvertString("[Cancel Vote]"))
);
} else {
VO_ExecuteCommand(
"globalwidgetcommand votelistsub additem \"Retrieving voting options from server...\" \"popmenu 0\"\n"
);
VO_SendRemoteCommand("gvo\n");
}
}
#endif