UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cp_messageoptions.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24 
25 #include "../../cl_shared.h"
26 #include "../../../shared/parse.h"
27 #include "cp_campaign.h"
28 #include "cp_messageoptions.h"
30 #include "cp_time.h"
32 
34 char const* const nt_strings[NT_NUM_NOTIFYTYPE] = {
35  N_("installation_installed"),
36  N_("installation_removed"),
37  N_("installation_replaced"),
38  N_("aircraft_refueled"),
39  N_("aircraft_cannotrefuel"),
40  N_("aircraft_arrivedhome"),
41  N_("installation_build_started"),
42  N_("installation_build_finished"),
43  N_("installation_destroyed"),
44  N_("research_new_proposed"),
45  N_("research_halted"),
46  N_("research_completed"),
47  N_("production_started"),
48  N_("production_finished"),
49  N_("production_failed"),
50  N_("production_queue_empty"),
51  N_("nation_happiness_changed"),
52  N_("nation_unhappy"),
53  N_("nation_pleased"),
54  N_("transfer_started"),
55  N_("transfer_completed_success"),
56  N_("transfer_lost"),
57  N_("transfer_aliens_defered"),
58  N_("transfer_uforecovery_finished"),
59  N_("ufo_spotted"),
60  N_("ufo_signal_lost"),
61  N_("ufo_attacking"),
62  N_("base_attack"),
63  N_("building_finished")
64 };
66 
80 void MSO_Set (const int listIndex, const notify_t type, const int optionType, const bool activate, const bool sendCommands)
81 {
82  messageSettings_t* settings = &messageSettings[type];
83 
84  if (activate) {
85  if ((optionType & NTMASK_PAUSE) == NTMASK_PAUSE)
86  settings->doPause = activate;
87  if ((optionType & NTMASK_SOUND)== NTMASK_SOUND)
88  settings->doSound = activate;
89  /* notification anyway*/
90  settings->doNotify = activate;
91  } else {
92  if ((optionType & NTMASK_PAUSE) == NTMASK_PAUSE)
93  settings->doPause = activate;
94  if ((optionType & NTMASK_SOUND)== NTMASK_SOUND)
95  settings->doSound = activate;
96  /* disable all if notify is disabled */
97  if (optionType == MSO_NOTIFY) {
98  settings->doNotify = activate;
99  settings->doPause = activate;
100  settings->doSound = activate;
101  }
102  }
103 
104  if (sendCommands)
105  cgi->UI_ExecuteConfunc("ms_btnstate %i %i %i %i", listIndex, settings->doPause, settings->doNotify, settings->doSound);
106  else
107  /* ensure that message buttons will be initialized correctly if menu is shown next time */
108  MSO_SetMenuState(MSO_MSTATE_PREPARED, false, false);
109 }
110 
115 static int MSO_ParseNotifyType (const char* name)
116 {
117  for (int idx = 0; idx < NT_NUM_NOTIFYTYPE; idx ++) {
118  if (Q_streq(name, nt_strings[idx])) {
119  return idx;
120  }
121  }
122  return -1;
123 }
124 
129 static int MSO_ParseOptionType (const char* type)
130 {
131  if (Q_strcaseeq(type, "pause"))
132  return MSO_PAUSE;
133  else if (Q_strcaseeq(type, "notify"))
134  return MSO_NOTIFY;
135  else if (Q_strcaseeq(type, "sound"))
136  return MSO_SOUND;
137 
138  cgi->Com_Printf("Unrecognized optionstype during set '%s' ignored\n", type);
139  return 0;
140 }
141 
146 static void MSO_Set_f (void)
147 {
148  if (cgi->Cmd_Argc() != 4) {
149  cgi->Com_Printf("Usage: %s <messagetypename> <pause|notify|sound> <0|1>\n", cgi->Cmd_Argv(0));
150  return;
151  }
152 
153  const int optionsType = MSO_ParseOptionType(cgi->Cmd_Argv(1));
154  if (optionsType == 0)
155  return;
156 
157  const char* messagetype = cgi->Cmd_Argv(1);
158  int type;
159  for (type = 0; type < NT_NUM_NOTIFYTYPE; type++) {
160  if (Q_streq(nt_strings[type], messagetype))
161  break;
162  }
163  if (type == NT_NUM_NOTIFYTYPE) {
164  cgi->Com_Printf("Unrecognized messagetype during set '%s' ignored\n", messagetype);
165  return;
166  }
167 
168  MSO_Set(0, (notify_t)type, optionsType, atoi(cgi->Cmd_Argv(3)), false);
169 }
170 
176 static void MSO_SetAll_f (void)
177 {
178  if (cgi->Cmd_Argc() != 3) {
179  cgi->Com_Printf("Usage: %s <pause|notify|sound> <0|1>\n", cgi->Cmd_Argv(0));
180  return;
181  }
182 
183  const bool activate = atoi(cgi->Cmd_Argv(2));
184  const int optionsType = MSO_ParseOptionType(cgi->Cmd_Argv(1));
185  if (optionsType == 0)
186  return;
187 
188  /* update settings for chosen type */
189  for (int type = 0; type < NT_NUM_NOTIFYTYPE; ++type) {
190  MSO_Set(0, (notify_t)type, optionsType, activate, false);
191  }
192  /* reinit menu */
194 }
195 
208 uiMessageListNodeMessage_t* MSO_CheckAddNewMessage (const notify_t messagecategory, const char* title, const char* text, messageType_t type, technology_t* pedia, bool popup)
209 {
210  uiMessageListNodeMessage_t* result = nullptr;
211  const messageSettings_t* settings = &messageSettings[messagecategory];
212 
213  if (settings->doNotify)
214  result = MS_AddNewMessage(title, text, type, pedia, popup, settings->doSound);
215  if (settings->doPause)
216  CP_GameTimeStop();
217  return result;
218 }
219 
225 {
227 
228  /* save positive values */
229  for (int type = 0; type < NT_NUM_NOTIFYTYPE; ++type) {
230  messageSettings_t actualSetting = messageSettings[type];
232 
233  cgi->XML_AddString(s, SAVE_MESSAGEOPTIONS_NAME, nt_strings[type]);
234  cgi->XML_AddBoolValue(s, SAVE_MESSAGEOPTIONS_NOTIFY, actualSetting.doNotify);
235  cgi->XML_AddBoolValue(s, SAVE_MESSAGEOPTIONS_PAUSE, actualSetting.doPause);
236  cgi->XML_AddBoolValue(s, SAVE_MESSAGEOPTIONS_SOUND, actualSetting.doSound);
237  }
238 
239  return true;
240 }
241 
247 {
248  xmlNode_t* n, *s;
249 
251  if (!n)
252  return false;
253 
255  const char* messagetype = cgi->XML_GetString(s, SAVE_MESSAGEOPTIONS_NAME);
256  int type;
257 
258  for (type = 0; type < NT_NUM_NOTIFYTYPE; type++) {
259  if (Q_streq(nt_strings[type], messagetype))
260  break;
261  }
262 
264  if (type == NT_NUM_NOTIFYTYPE) {
265  cgi->Com_Printf("Unrecognized messagetype '%s' ignored while loading\n", messagetype);
266  continue;
267  }
268  MSO_Set(0, (notify_t)type, MSO_NOTIFY, cgi->XML_GetBool(s, SAVE_MESSAGEOPTIONS_NOTIFY, false), false);
269  MSO_Set(0, (notify_t)type, MSO_PAUSE, cgi->XML_GetBool(s, SAVE_MESSAGEOPTIONS_PAUSE, false), false);
270  MSO_Set(0, (notify_t)type, MSO_SOUND, cgi->XML_GetBool(s, SAVE_MESSAGEOPTIONS_SOUND, false), false);
271  }
272 
273  MSO_SetMenuState(MSO_MSTATE_REINIT, false, false);
274  return true;
275 }
276 
280 static int MSO_ParseOption (const char* blockName, const char** text)
281 {
282  const char* errhead = "MSO_ParseSettings: unexpected end of file (names ";
283  const char* token;
284 
285  /* get name list body body */
286  token = Com_Parse(text);
287 
288  if (!*text || *token !='{') {
289  cgi->Com_Printf("MSO_ParseOption: settingslist \"%s\" without body ignored\n", blockName);
290  return -1;
291  }
292 
293  int messageType = -1;
294  linkedList_t* status = nullptr;
295 
296  do {
297  /* get the msg type*/
298  token = cgi->Com_EParse(text, errhead, blockName);
299  if (!*text) {
300  cgi->Com_Printf("MSO_ParseOption: end of file not expected \"%s\"\n", blockName);
301  return -1;
302  }
303  if (token[0] == '}')
304  break;
305 
306  if (Q_streq(token, "type")) {
307  token = cgi->Com_EParse(text, errhead, blockName);
308  messageType = MSO_ParseNotifyType(token);
309  } else if (Q_streq(token, "status")) {
310  if (status != nullptr) {
311  cgi->Com_Printf("MSO_ParseOption: status already defined. Previous definition ignored.\n");
312  cgi->LIST_Delete(&status);
313  } else if (!cgi->Com_ParseList(text, &status)) {
314  cgi->Com_Printf("MSO_ParseOption: error while parsing option status.\n");
315  return -1;
316  }
317  } else {
318  cgi->Com_Printf("MSO_ParseOption: token \"%s\" in \"%s\" not expected.\n", token, blockName);
319  return -1;
320  }
321  } while (*text);
322 
323  if (messageType == -1) {
324  cgi->Com_Printf("MSO_ParseOption: message option type undefined.\n");
325  return -1;
326  }
327 
328  for (linkedList_t* element = status; element != nullptr; element = element->next) {
329  const char* value = (const char*)element->data;
330  const int optionType = MSO_ParseOptionType(value);
331  if (optionType == 0) {
332  cgi->Com_Printf("MSO_ParseOption: message option type \"%s\" undefined.\n", value);
333  continue;
334  }
335  MSO_Set(0, (notify_t)messageType, optionType, 1, false);
336  }
337 
338  /* reset menu state, was updated by msgoptions_set */
339  MSO_SetMenuState(MSO_MSTATE_REINIT, false, false);
340 
341  return messageType;
342 }
343 
348 static bool MSO_ParseCategory (const char* blockName, const char** text)
349 {
350  const char* errhead = "MSO_ParseCategory: unexpected end of file (names ";
351  const char* token;
352  msgCategory_t* category;
353  msgCategoryEntry_t* categoryEntry;
354 
355  /* get name list body body */
356  token = Com_Parse(text);
357 
358  if (!*text || *token != '{') {
359  cgi->Com_Printf("MSO_ParseCategory: category without body\n");
360  return false;
361  }
362 
363  /* add category */
365  cgi->Com_Printf("MSO_ParseCategory: too many messagecategory defs\n");
366  return false;
367  }
368 
369  /* QUESTION this structure looks useless, categoryEntry is enough */
371 
372  OBJZERO(*category);
373  category->idx = ccs.numMsgCategories; /* set self-link */
374 
376 
377  /* first entry is category */
378  OBJZERO(*categoryEntry);
380  category->last = category->first = &ccs.msgCategoryEntries[ccs.numMsgCategoryEntries];
381  categoryEntry->previous = nullptr;
382  categoryEntry->next = nullptr;
383  categoryEntry->isCategory = true;
385 
386  do {
387  /* get entries and add them to category */
388  token = cgi->Com_EParse(text, errhead, blockName);
389  if (!*text) {
390  cgi->Com_Printf("MSO_ParseMessageSettings: end of file not expected\n");
391  return false;
392  }
393  if (token[0] == '}')
394  break;
395 
396  if (Q_streq(token, "option")) {
397  int optionId = MSO_ParseOption(blockName, text);
398  if (optionId == -1) {
399  cgi->Com_Error(ERR_DROP, "MSO_ParseMessageSettings: error while parsing option from \"%s\".\n", blockName);
400  }
401  /* prepare a new msgcategory entry */
404  OBJZERO(*entry);
406  previous->next = entry;
407 
409  entry->previous = previous;
410  entry->next = nullptr;
411  entry->notifyType = nt_strings[optionId];
412  entry->settings = &messageSettings[optionId];
414  } else if (Q_streq(token, "name")) {
415  token = cgi->Com_EParse(text, errhead, blockName);
416  if (!*text) {
417  cgi->Com_Printf("MSO_ParseMessageSettings: end of file not expected\n");
418  return false;
419  }
420  /* skip translation token */
421  if (token[0] == '_') {
422  token++;
423  }
424  category->name = cgi->PoolStrDup(token, cp_campaignPool, 0);
425  categoryEntry->notifyType = category->name;
426  } else {
427  cgi->Com_Error(ERR_DROP, "MSO_ParseMessageSettings: token \"%s\" in \"%s\" not expected.\n", token, blockName);
428  }
429  } while (*text);
430 
431  if (category->name == nullptr) {
432  cgi->Com_Printf("MSO_ParseMessageSettings: category do not have name\n");
433  return false;
434  }
435 
437  MSO_SetMenuState(MSO_MSTATE_REINIT, false, false);
438  return true;
439 }
440 
444 void MSO_ParseMessageSettings (const char* name, const char** text)
445 {
446  const char* errhead = "MSO_ParseMessageSettings: unexpected end of file (names ";
447  const char* token;
448 
449  /* settings available, reset previous settings */
450  OBJZERO(messageSettings);
451 
452  /* get name list body body */
453  token = Com_Parse(text);
454 
455  if (!*text || token[0] != '{') {
456  cgi->Com_Error(ERR_DROP, "MSO_ParseMessageSettings: msgoptions \"%s\" without body.\n", name);
457  return;
458  }
459 
460  while (*text) {
461  /* get entries and add them to category */
462  token = cgi->Com_EParse(text, errhead, name);
463  if (!*text)
464  cgi->Com_Error(ERR_DROP, "MSO_ParseMessageSettings: end of file not expected \"%s\".\n", name);
465  if (token[0] == '}')
466  break;
467 
468  if (Q_streq(token, "category")) {
469  if (!MSO_ParseCategory(name, text)) {
470  cgi->Com_Error(ERR_DROP, "MSO_ParseMessageSettings: error while parsing category from \"%s\".\n", name);
471  }
472  } else {
473  cgi->Com_Error(ERR_DROP, "MSO_ParseMessageSettings: token \"%s\" in \"%s\" not expected.\n", token, name);
474  }
475  }
476 
477 }
478 
479 static const cmdList_t msgOptionsCmds[] = {
480  {"msgoptions_setall", MSO_SetAll_f, "Sets pause, notification or sound setting for all message categories"},
481  {"msgoptions_set", MSO_Set_f, "Sets pause, notification or sound setting for a message category"},
482  {nullptr, nullptr, nullptr}
483 };
484 void MSO_Init (void)
485 {
486  cgi->Cmd_TableAddList(msgOptionsCmds);
488 }
489 
490 void MSO_Shutdown (void)
491 {
492  cgi->Cmd_TableRemoveList(msgOptionsCmds);
494 }
structure holding pause and notify settings for a notify type.
#define SAVE_MESSAGEOPTIONS_NOTIFY
msgCategory_t messageCategories[MAX_MESSAGECATEGORIES]
Definition: cp_campaign.h:358
uiMessageListNodeMessage_t * MSO_CheckAddNewMessage(const notify_t messagecategory, const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup)
Adds a new message to message stack. It uses message settings to verify whether sound should be playe...
#define SAVE_MESSAGEOPTIONS_NAME
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
int numMsgCategories
Definition: cp_campaign.h:359
#define MAX_MESSAGECATEGORIES
#define MSO_SOUND
notification type: play notification sound
void MSO_Shutdown(void)
char const *const nt_strings[NT_NUM_NOTIFYTYPE]
valid notification types that may cause pause / notice
msgCategoryEntry_t * first
static void MSO_Set_f(void)
Function callback used to initialize values for messageoptions and for manual setting changes...
void MSO_InitCallbacks(void)
messageType_t
Definition: cp_messages.h:30
void MSO_SetMenuState(const msoMenuState_t newState, const bool callInit, const bool preserveIndex)
Header file for messageoptions related stuff.
const char * data
Definition: cgame.h:145
struct msgCategoryEntry_s * previous
#define MSO_PAUSE
notification type: pause game
uiMessageListNodeMessage_t * MS_AddNewMessage(const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup, bool playSound)
Adds a new message to message stack.
Definition: cp_messages.cpp:61
memPool_t * cp_campaignPool
Definition: cp_campaign.cpp:61
void CP_GameTimeStop(void)
Stop game time speed.
Definition: cp_time.cpp:126
static void MSO_SetAll_f(void)
Function callback that sets all message options settings for one option type to given value...
#define xmlNode_t
Definition: xml.h:24
#define ERR_DROP
Definition: common.h:211
messageSettings_t messageSettings[NT_NUM_NOTIFYTYPE]
#define SAVE_MESSAGEOPTIONS_PAUSE
#define OBJZERO(obj)
Definition: shared.h:178
#define SAVE_MESSAGEOPTIONS_TYPE
void MSO_Init(void)
static int MSO_ParseOptionType(const char *type)
Parse option type.
const cgame_import_t * cgi
static int MSO_ParseNotifyType(const char *name)
Parse notify type.
static bool MSO_ParseCategory(const char *blockName, const char **text)
Parses a messagecategory script section. These categories are used to group notification types...
msgCategoryEntry_t * last
bool MSO_SaveXML(xmlNode_t *p)
saves current notification and pause settings
This is the technology parsed from research.ufo.
Definition: cp_research.h:137
ccs_t ccs
Definition: cp_campaign.cpp:62
Definition: cmd.h:86
const char * name
#define Q_strcaseeq(a, b)
Definition: shared.h:135
Campaign geoscape time header.
msgCategoryEntry_t msgCategoryEntries[NT_NUM_NOTIFYTYPE+MAX_MESSAGECATEGORIES]
Definition: cp_campaign.h:362
struct msgCategoryEntry_s * next
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition: parse.cpp:107
#define SAVE_MESSAGEOPTIONS_MESSAGEOPTIONS
#define SAVE_MESSAGEOPTIONS_SOUND
XML tag constants for savegame.
int numMsgCategoryEntries
Definition: cp_campaign.h:363
void MSO_ParseMessageSettings(const char *name, const char **text)
parses message options settings from file.
CASSERT(lengthof(nt_strings)==NT_NUM_NOTIFYTYPE)
static const cmdList_t msgOptionsCmds[]
xmlNode_t *IMPORT * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
void MSO_Set(const int listIndex, const notify_t type, const int optionType, const bool activate, const bool sendCommands)
Function updates pause or notification settings.
bool MSO_LoadXML(xmlNode_t *p)
Restores the notification and pause settings from savegame.
linkedList_t * next
Definition: list.h:32
Header file for single player campaign control.
enum nt_s notify_t
Notify types.
static int MSO_ParseOption(const char *blockName, const char **text)
parses message options settings from file.
xmlNode_t *IMPORT * XML_AddNode(xmlNode_t *parent, const char *name)
const char *IMPORT * Com_EParse(const char **text, const char *errhead, const char *errinfo)
#define N_(String)
Definition: cl_shared.h:45
#define lengthof(x)
Definition: shared.h:105
struct msgCategory_s * category
bool isCategory
#define Q_streq(a, b)
Definition: shared.h:136
char *IMPORT * PoolStrDup(const char *in, memPool_t *pool, const int tagNum)
messageSettings_t * settings
void MSO_ShutdownCallbacks(void)
const char * notifyType
#define MSO_NOTIFY
notification type: add notification message
Header file for menu related console command callbacks.
const char *IMPORT * Cmd_Argv(int n)
const char *IMPORT * XML_GetString(xmlNode_t *parent, const char *name)
xmlNode_t *IMPORT * XML_GetNode(xmlNode_t *parent, const char *name)