UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_messageoptions.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23*/
24
25#include "../../cl_shared.h"
27#include "cp_campaign.h"
28#include "cp_messageoptions.h"
30#include "cp_time.h"
32
34char 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
68
80void MSO_Set (const int listIndex, const notify_t type, const int optionType, const bool activate, const bool sendCommands)
81{
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 */
109}
110
115static 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
129static 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
146static 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
176static 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
208uiMessageListNodeMessage_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)
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];
231 xmlNode_t* s = cgi->XML_AddNode(n, SAVE_MESSAGEOPTIONS_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
250 n = cgi->XML_GetNode(p, SAVE_MESSAGEOPTIONS_MESSAGEOPTIONS);
251 if (!n)
252 return false;
253
254 for (s = cgi->XML_GetNode(n, SAVE_MESSAGEOPTIONS_TYPE); s; s = cgi->XML_GetNextNode(s, n, SAVE_MESSAGEOPTIONS_TYPE)) {
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
280static 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
348static 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 */
364 if (ccs.numMsgCategories >= MAX_MESSAGECATEGORIES) {
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 */
370 category = &ccs.messageCategories[ccs.numMsgCategories];
371
372 OBJZERO(*category);
373 category->idx = ccs.numMsgCategories; /* set self-link */
374
375 categoryEntry = &ccs.msgCategoryEntries[ccs.numMsgCategoryEntries];
376
377 /* first entry is category */
378 OBJZERO(*categoryEntry);
379 categoryEntry->category = &ccs.messageCategories[ccs.numMsgCategories];
380 category->last = category->first = &ccs.msgCategoryEntries[ccs.numMsgCategoryEntries];
381 categoryEntry->previous = nullptr;
382 categoryEntry->next = nullptr;
383 categoryEntry->isCategory = true;
384 ccs.numMsgCategoryEntries++;
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 */
402 msgCategoryEntry_t* previous = ccs.messageCategories[ccs.numMsgCategories].last;
403 msgCategoryEntry_t* entry = &ccs.msgCategoryEntries[ccs.numMsgCategoryEntries];
404 OBJZERO(*entry);
405 ccs.messageCategories[ccs.numMsgCategories].last = entry;
406 previous->next = entry;
407
408 entry->category = &ccs.messageCategories[ccs.numMsgCategories];
409 entry->previous = previous;
410 entry->next = nullptr;
411 entry->notifyType = nt_strings[optionId];
412 entry->settings = &messageSettings[optionId];
413 ccs.numMsgCategoryEntries++;
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
436 ccs.numMsgCategories++;
437 MSO_SetMenuState(MSO_MSTATE_REINIT, false, false);
438 return true;
439}
440
444void 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 */
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
479static 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};
484void MSO_Init (void)
485{
486 cgi->Cmd_TableAddList(msgOptionsCmds);
488}
489
490void MSO_Shutdown (void)
491{
492 cgi->Cmd_TableRemoveList(msgOptionsCmds);
494}
Share stuff between the different cgame implementations.
#define N_(String)
Definition cl_shared.h:46
#define ERR_DROP
Definition common.h:211
memPool_t * cp_campaignPool
ccs_t ccs
Header file for single player campaign control.
const cgame_import_t * cgi
static bool MSO_ParseCategory(const char *blockName, const char **text)
Parses a messagecategory script section. These categories are used to group notification types.
static void MSO_SetAll_f(void)
Function callback that sets all message options settings for one option type to given value.
static int MSO_ParseOption(const char *blockName, const char **text)
parses message options settings from file.
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.
void MSO_Init(void)
static const cmdList_t msgOptionsCmds[]
messageSettings_t messageSettings[NT_NUM_NOTIFYTYPE]
bool MSO_SaveXML(xmlNode_t *p)
saves current notification and pause settings
static int MSO_ParseOptionType(const char *type)
Parse option type.
bool MSO_LoadXML(xmlNode_t *p)
Restores the notification and pause settings from savegame.
static int MSO_ParseNotifyType(const char *name)
Parse notify type.
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...
void MSO_Shutdown(void)
void MSO_ParseMessageSettings(const char *name, const char **text)
parses message options settings from file.
static void MSO_Set_f(void)
Function callback used to initialize values for messageoptions and for manual setting changes.
char const *const nt_strings[NT_NUM_NOTIFYTYPE]
valid notification types that may cause pause / notice
Header file for messageoptions related stuff.
#define MAX_MESSAGECATEGORIES
notify_t
Notify types.
@ NT_NUM_NOTIFYTYPE
@ NTMASK_SOUND
@ NTMASK_PAUSE
#define MSO_PAUSE
notification type: pause game
#define MSO_NOTIFY
notification type: add notification message
#define MSO_SOUND
notification type: play notification sound
void MSO_InitCallbacks(void)
void MSO_ShutdownCallbacks(void)
void MSO_SetMenuState(const msoMenuState_t newState, const bool callInit, const bool preserveIndex)
Header file for menu related console command callbacks.
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.
messageType_t
Definition cp_messages.h:32
void CP_GameTimeStop(void)
Stop game time speed.
Definition cp_time.cpp:126
Campaign geoscape time header.
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
Shared parsing functions.
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
XML tag constants for savegame.
#define SAVE_MESSAGEOPTIONS_SOUND
#define SAVE_MESSAGEOPTIONS_NAME
#define SAVE_MESSAGEOPTIONS_TYPE
#define SAVE_MESSAGEOPTIONS_PAUSE
#define SAVE_MESSAGEOPTIONS_NOTIFY
#define SAVE_MESSAGEOPTIONS_MESSAGEOPTIONS
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#define Q_strcaseeq(a, b)
Definition shared.h:135
#define lengthof(x)
Definition shared.h:105
#define CASSERT(x)
Definition shared.h:107
linkedList_t * next
Definition list.h:32
structure holding pause and notify settings for a notify type.
msgCategoryEntry_t * first
msgCategoryEntry_t * last
const char * name
const char * notifyType
bool isCategory
struct msgCategoryEntry_s * previous
struct msgCategoryEntry_s * next
struct msgCategory_s * category
messageSettings_t * settings
This is the technology parsed from research.ufo.
#define xmlNode_t
Definition xml.h:24