Files
Descent3/Descent3/Briefing.cpp
2024-06-10 23:35:11 +02:00

653 lines
17 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/main/Briefing.cpp $
* $Revision: 57 $
* $Date: 4/14/99 2:50a $
* $Author: Jeff $
*
* Briefing system
*
* $Log: /DescentIII/main/Briefing.cpp $
*
* 57 4/14/99 2:50a Jeff
* fixed some case mismatched #includes
*
* 56 2/25/99 11:56a Jeff
* added embedded hottag support
*
* 55 1/04/99 6:12p Jeff
* put in handling of mission state flags
*
* 54 1/04/99 12:32p Jeff
* added support for mission flag parsing
*
* 53 12/15/98 4:28p Jeff
* don't die if the briefing file doesn't exist
*
* 52 10/12/98 8:32p Jeff
* changed the way focus is handled
*
* 51 9/11/98 10:34a Jeff
* Briefing Editor completed
*
* 50 9/09/98 7:02p Jeff
*
* 49 8/28/98 12:57p Jeff
* added sounds and some key functionality
*
* 48 8/27/98 2:51p Jeff
* New TelCom finally checked in
*
* 47 8/19/98 1:38p Jeff
* removed update2dsound
*
* 46 8/18/98 11:57a Jeff
* fixed sound problem
*
* 45 7/11/98 9:16p Jeff
* moved automatically drawing monitor overlay graphics from update() to a
* function of it's own, that way the TelCom API can be avoided if needed
*
* 44 7/09/98 8:33p Samir
* changes for new streaming interface.
*
* 43 6/26/98 2:49p Jeff
* fixed the bug where is you aborted parsing in the middle of a screen
* you'd crash
*
* 42 6/25/98 12:44p Jeff
* Added ESC to escape out of TelCom while it's initializing. Added
* DestroyScratch to Monitor
*
* 41 6/19/98 6:05p Jeff
* changes made due to voice system
*
* 40 6/18/98 5:18p Jeff
* use new streamaudio system
*
* 39 6/17/98 8:08p Jeff
* removed call to DoVoiceFrame
*
* 38 6/08/98 3:56p Jeff
* added voice support
*
* 37 5/19/98 3:40p Jeff
* poly models functional
*
* 36 5/18/98 4:20p Jeff
* setup TelCom so D3_FAST zips it through
*
* 35 5/18/98 3:37p Jeff
* added glow parameter to $button
*
* 34 5/15/98 5:16p Jeff
* various changes
*
* 33 5/11/98 6:20p Jeff
* added glitch and static keywords
*
* 32 5/08/98 12:39p Jeff
* various cleanups
*
* 31 5/06/98 6:34p Jeff
* capped framerate for mouse
*
* 30 5/04/98 5:29p Jeff
* Added sound frames and mouse cursor drawing
*
* 29 5/04/98 1:35p Jeff
* Changes made for mouse handling
*
* 28 5/04/98 11:01a Jeff
* added scroll param to text
*
* 27 5/03/98 7:59p Jeff
* little changes
*
* 26 5/01/98 2:16p Jeff
* added $title and $sound
*
* 25 4/30/98 7:16p Jeff
* added $font, exiting to mainmenu
*
* 24 4/29/98 4:36p Jeff
* increased text buffer size
*
* 23 4/28/98 6:58p Jeff
* Added new poly model effect driver
*
* 22 4/26/98 7:22p Jeff
* fixed fade ping pong bug, implemented new invert and stretch bitmaps
*
* 21 4/26/98 2:53a Jeff
* Made changes needed for new Effect driver system
*
* 20 4/24/98 6:35p Jeff
* Added space bar page forward
*
* 19 4/24/98 3:29p Jeff
* fixed powerup effect
*
* 18 4/23/98 7:09p Jeff
* added power up/down support
*
* 17 4/22/98 7:44p Jeff
* added flashing buttons
*
* 16 4/21/98 4:22p Jeff
* Call a general purpose function to draw entire telcom screen
*
* 15 4/20/98 6:45p Jeff
* added multiple screen handling and on screen buttons
*
* 14 4/17/98 6:55p Jeff
* Added button support
*
* 13 4/15/98 6:28p Jeff
* Got parsing working for most of the keywords
*
* 12 3/23/98 9:55a Jeff
* Made changes to remove old telcom
*
* 11 3/12/98 3:32p Jeff
* Initial changes started for New TelCom
*
* 10 2/03/98 12:21p Jeff
* changed default font to briefing font
*
* 9 1/26/98 4:19p Jeff
* Added "blur in" bitmap effect
*
* 8 1/20/98 9:16p Jeff
* Added back in fading bitmaps
*
* 7 1/20/98 6:04p Jeff
* Added support for fading fonts
*
* 6 1/20/98 10:43a Jeff
*
* 5 1/19/98 5:37p Jeff
* Got briefing up to par, and even better than before, added timer so
* scroll and type fonts are time based.
*
* 4 1/16/98 2:40p Jeff
* Adjusted so everything is displayed in correct spots, took out
* DefaultControl timer, added support for non fading bitmaps
*
* 3 1/15/98 11:14a Jeff
* Changed some code in TelComCallMission() to get Telcom working again
*
* 2 11/17/97 3:56p Matt
* Added the briefing system code
*
* 1 11/12/97 10:44a Matt
*
* $NoKeywords: $
*/
#include "Briefing.h"
#include "BriefingParse.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "game.h"
#include "mem.h"
#include "hlsoundlib.h"
#include "voice.h"
#include "streamaudio.h"
#include "pserror.h"
#include "ddio.h"
#include "descent.h"
#include "TelCom.h"
#include "TelComEffects.h"
#include "Mission.h"
struct tBriefingTag {
const char *name;
int id;
int length;
};
#define TAG_LEVELNUM 0
#define TAG_PLEVELNUM 1
#define TAG_NLEVELNUM 2
static tBriefingTag HotTags[] = {
{"#LEVELNUM#", TAG_LEVELNUM, -1},
{"#PLEVELNUM#", TAG_PLEVELNUM, -1},
{"#NLEVELNUM#", TAG_NLEVELNUM, -1},
};
static const int NumHotTags = sizeof(HotTags) / sizeof(tBriefingTag);
static int osb_xoff = 0, osb_yoff = 0;
static int current_screen = -1;
static tTelComInfo *pb_tcs = NULL;
static bool gottitle = false;
static bool pbfirst_call;
static char pbtitle[100];
static bool ok_to_parse_screen = false;
static int skipped_screens;
static bool IsMissionMaskOK(uint32_t set, uint32_t unset);
static void ReplaceHotTag(char *string, int tag);
static bool ParseForHotTags(const char *src, char **dest);
static bool PlayBriefing(tTelComInfo *tcs);
static void PBAddTextEffect(TCTEXTDESC* desc, char *text, char *description, int id);
static void PBAddBmpEffect(TCBMPDESC* desc, char *description);
static void PBAddMovieEffect(TCMOVIEDESC* desc, char *description);
static void PBAddBkgEffect(TCBKGDESC* desc, char *description);
static void PBAddPolyEffect(TCPOLYDESC* desc, char *description);
static void PBAddButtonEffect(TCBUTTONDESC* desc, char *description, int id);
static void PBStartScreen(int screen_num, char *description, char *layout, uint32_t mask_set, uint32_t mask_unset);
static void PBEndScreen();
static bool PBLoopCallback();
static void PBSetTitle(char *title);
static void PBSetStatic(float amount);
static void PBSetGlitch(float amount);
static void PBAddVoice(char *filename, int flags, char *description);
bool IsMissionMaskOK(uint32_t set, uint32_t unset) {
uint32_t Gamemissionmask = Current_mission.game_state_flags;
uint32_t fGamemissionmask;
fGamemissionmask = Gamemissionmask ^ 0xFFFFFFFF;
if (!(((set & Gamemissionmask) == set) && ((unset & fGamemissionmask) == unset))) {
return false;
}
return true;
}
void ReplaceHotTag(char *string, int tag) {
if (tag < 0 || tag >= NumHotTags) {
*string = '\0';
return;
}
switch (tag) {
case TAG_LEVELNUM:
sprintf(string, "%d", Current_mission.cur_level);
break;
case TAG_PLEVELNUM:
sprintf(string, "%d", Current_mission.cur_level - 1);
break;
case TAG_NLEVELNUM:
sprintf(string, "%d", Current_mission.cur_level + 1);
break;
}
}
// returns true if there were hot tags and it had to allocate memory for dest (so it needs to be freed)
#define MEMORY_BLOCK 50
bool ParseForHotTags(const char *src, char **dest) {
bool ret = false;
const char *curr_ptr = src;
char *dest_ptr;
*dest = NULL;
bool done = false;
int length = strlen(src) + 1;
int curr_size = length;
int curr_index = 0;
dest_ptr = *dest = (char *)mem_malloc(length);
if (!dest_ptr)
return false;
char replacement[256];
while (!done) {
if (*curr_ptr != '#') {
if (*curr_ptr == '\0') {
done = true;
} else {
*dest_ptr = *curr_ptr;
dest_ptr++;
curr_ptr++;
curr_index++;
length--;
if (length < 1) {
// we need to allocate some more memory
char *new_mem = (char *)mem_malloc(curr_size + MEMORY_BLOCK);
if (!new_mem) {
// out of memory
mem_free((*dest));
*dest = NULL;
return false;
}
memcpy(new_mem, (*dest), curr_index);
mem_free(*dest);
*dest = new_mem;
dest_ptr = &new_mem[curr_index];
curr_size += MEMORY_BLOCK;
length += MEMORY_BLOCK;
}
}
} else {
int is_hot_tag = -1;
// see if it's a hot tag
for (int i = 0; i < NumHotTags; i++) {
if (HotTags[i].length == -1)
HotTags[i].length = strlen(HotTags[i].name);
if (!strnicmp(curr_ptr, HotTags[i].name, HotTags[i].length)) {
// this is a hot tag!!!!!
mprintf(0, "Found Embedded HotTag: %s\n", HotTags[i].name);
is_hot_tag = i;
ret = true;
break;
}
}
if (is_hot_tag == -1) {
// it wasn't a hot tag continue on
*dest_ptr = *curr_ptr;
dest_ptr++;
curr_ptr++;
curr_index++;
length--;
if (length < 1) {
// we need to allocate some more memory
char *new_mem = (char *)mem_malloc(curr_size + MEMORY_BLOCK);
if (!new_mem) {
// out of memory
mem_free((*dest));
*dest = NULL;
return false;
}
memcpy(new_mem, (*dest), curr_index);
mem_free(*dest);
*dest = new_mem;
dest_ptr = &new_mem[curr_index];
curr_size += MEMORY_BLOCK;
length += MEMORY_BLOCK;
}
} else {
// we need to replace the hot tag
ReplaceHotTag(replacement, is_hot_tag);
curr_ptr += HotTags[is_hot_tag].length; // skip past hot tag
int dest_left = strlen(replacement);
char *r_ptr = replacement;
while (dest_left > 0) {
*dest_ptr = *r_ptr;
dest_ptr++;
curr_index++;
length--;
if (length < 1) {
// we need to allocate some more memory
char *new_mem = (char *)mem_malloc(curr_size + MEMORY_BLOCK);
if (!new_mem) {
// out of memory
mem_free((*dest));
*dest = NULL;
return false;
}
memcpy(new_mem, (*dest), curr_index);
mem_free(*dest);
*dest = new_mem;
dest_ptr = &new_mem[curr_index];
curr_size += MEMORY_BLOCK;
length += MEMORY_BLOCK;
}
dest_left--;
r_ptr++;
}
}
}
}
*dest_ptr = '\0';
if (!ret) {
mem_free((*dest));
*dest = NULL;
}
return ret;
}
bool PlayBriefing(tTelComInfo *tcs) {
TelcomRenderSetScreen(0);
bool done = false;
while (!done) {
Sound_system.BeginSoundFrame(Telcom_called_from_game);
if (tcs->state == TCS_POWEROFF || tcs->current_status != TS_MISSION) {
// we're done with the loop
done = true;
}
// Process all waiting events for the TelCom (we may only want to handle this once a frame!)
TelComHandleAllEvents(tcs);
TelcomRenderScreen();
Descent->defer();
if (KEY_STATE(KEY_ESC))
tcs->state = TCS_POWEROFF;
Sound_system.EndSoundFrame();
}
return true;
}
void PBAddTextEffect(TCTEXTDESC* desc, char *text, char *description, int id) {
if (IsMissionMaskOK(desc->mission_mask_set, desc->mission_mask_unset) && ok_to_parse_screen) {
char *new_text = NULL;
const bool new_stuff = ParseForHotTags(text, &new_text);
CreateTextEffect(desc, new_stuff ? new_text : text, MONITOR_MAIN, current_screen, id);
if (new_text) {
mem_free(new_text);
}
}
}
void PBAddBmpEffect(TCBMPDESC* desc, char *description) {
if (IsMissionMaskOK(desc->mission_mask_set, desc->mission_mask_unset) && ok_to_parse_screen)
CreateBitmapEffect(desc, MONITOR_MAIN, current_screen);
}
void PBAddMovieEffect(TCMOVIEDESC* desc, char *description) {
if (IsMissionMaskOK(desc->mission_mask_set, desc->mission_mask_unset) && ok_to_parse_screen)
CreateMovieEffect(desc, MONITOR_MAIN, current_screen);
}
void PBAddBkgEffect(TCBKGDESC* desc, char *description) {
if (IsMissionMaskOK(desc->mission_mask_set, desc->mission_mask_unset) && ok_to_parse_screen) {
mprintf(0, "PB: Add Bkg\n");
}
}
void PBAddPolyEffect(TCPOLYDESC* desc, char *description) {
if (IsMissionMaskOK(desc->mission_mask_set, desc->mission_mask_unset) && ok_to_parse_screen)
CreatePolyModelEffect(desc, MONITOR_MAIN, current_screen);
}
void PBAddSoundEffect(TCSNDDESC* desc, char *description) {
if (IsMissionMaskOK(desc->mission_mask_set, desc->mission_mask_unset) && ok_to_parse_screen)
CreateSoundEffect(desc, MONITOR_MAIN, current_screen);
}
void PBAddButtonEffect(TCBUTTONDESC* desc, char *description, int id) {
if (IsMissionMaskOK(desc->mission_mask_set, desc->mission_mask_unset) && ok_to_parse_screen) {
desc->x += osb_xoff;
desc->y += osb_yoff;
desc->tab_stop = false; // no briefing buttons can have focus
CreateButtonEffect(desc, MONITOR_MAIN, current_screen, id);
}
}
void PBStartScreen(int screen_num, char *description, char *layout, uint32_t mask_set, uint32_t mask_unset) {
if (!IsMissionMaskOK(mask_set, mask_unset)) {
ok_to_parse_screen = false;
skipped_screens++;
return;
}
int real_screen_num = screen_num - skipped_screens;
ok_to_parse_screen = true;
TelcomStartScreen(real_screen_num);
current_screen = real_screen_num;
TCBKGDESC backg;
backg.color = BACKGROUND_COLOR;
backg.caps = TCBGD_COLOR;
backg.type = TC_BACK_STATIC;
CreateBackgroundEffect(&backg, MONITOR_TOP, current_screen);
if (gottitle) {
TCTEXTDESC textd;
textd.color = GR_RGB(200, 200, 200);
textd.type = TC_TEXT_STATIC;
textd.font = BBRIEF_FONT_INDEX;
textd.textbox.left = 4;
textd.textbox.right = 300;
textd.textbox.top = 2;
textd.textbox.bottom = 80;
textd.caps = TCTD_COLOR | TCTD_FONT | TCTD_TEXTBOX;
CreateTextEffect(&textd, pbtitle, MONITOR_TOP, current_screen);
}
if (layout) {
TCBMPDESC bmpd;
bmpd.caps = TCBD_XY;
bmpd.type = TC_BMP_STATIC;
bmpd.flags = 0;
bmpd.x = bmpd.y = 0;
strcpy(bmpd.filename, layout);
CreateBitmapEffect(&bmpd, MONITOR_MAIN, current_screen);
}
}
void PBEndScreen() {
if (ok_to_parse_screen) {
TelcomEndScreen();
current_screen = -1;
}
}
bool PBLoopCallback() {
if (!pbfirst_call) {
Sound_system.EndSoundFrame();
} else
pbfirst_call = false;
bool ret = false;
Sound_system.BeginSoundFrame(Telcom_called_from_game);
if (pb_tcs->state == TCS_POWEROFF || pb_tcs->current_status != TS_MISSION) {
// we're done with the loop
ret = true;
}
// Process all waiting events for the TelCom (we may only want to handle this once a frame!)
TelComHandleAllEvents(pb_tcs);
TelcomRenderScreen();
Descent->defer();
if (KEY_STATE(KEY_ESC)) {
pb_tcs->state = TCS_POWEROFF;
ret = true;
}
return ret;
}
void PBSetTitle(char *title) {
gottitle = true;
strcpy(pbtitle, title);
}
void PBSetStatic(float amount) {
if (amount > 0)
TelcomEnableStatic(amount);
}
void PBSetGlitch(float amount) {
if (amount > 0)
TelcomEnableGlitch(amount);
}
void PBAddVoice(char *filename, int flags,char *description) {}
bool ParseBriefing(const char *filename, tTelComInfo *tcs) {
if (!cfexist(filename)) {
tcs->state = TCS_POWEROFF;
return false;
}
osb_xoff = tcs->Monitor_coords[MONITOR_MAIN].left;
osb_yoff = tcs->Monitor_coords[MONITOR_MAIN].top;
current_screen = -1;
pb_tcs = tcs;
gottitle = false;
pbtitle[0] = '\0';
pbfirst_call = true;
CBriefParse engine;
tBriefParseCallbacks pb_callbacks;
pb_callbacks.AddTextEffect = PBAddTextEffect;
pb_callbacks.AddBmpEffect = PBAddBmpEffect;
pb_callbacks.AddMovieEffect = PBAddMovieEffect;
pb_callbacks.AddBkgEffect = PBAddBkgEffect;
pb_callbacks.AddPolyEffect = PBAddPolyEffect;
pb_callbacks.AddSoundEffect = PBAddSoundEffect;
pb_callbacks.AddButtonEffect = PBAddButtonEffect;
pb_callbacks.StartScreen = PBStartScreen;
pb_callbacks.EndScreen = PBEndScreen;
pb_callbacks.LoopCallback = PBLoopCallback;
pb_callbacks.SetTitle = PBSetTitle;
pb_callbacks.SetStatic = PBSetStatic;
pb_callbacks.SetGlitch = PBSetGlitch;
pb_callbacks.AddVoice = PBAddVoice;
engine.SetCallbacks(&pb_callbacks);
DestroyAllScreens();
TelcomRenderSetScreen(DUMMY_SCREEN);
skipped_screens = 0;
int ret = engine.ParseBriefing(filename);
Sound_system.EndSoundFrame();
if (ret == PBERR_NOERR) {
PlayBriefing(tcs);
}
DestroyAllScreens(true);
TelcomCreateDummyScreen();
TelcomRenderSetScreen(DUMMY_SCREEN);
TelcomDisableStatic();
TelcomDisableGlitch();
return false;
}