Files
fs2open.github.com/fred2/voiceactingmanager.cpp
Goober5000 a5f90ddcc1 fix a FRED compilation warning
One of the Voice Acting Manager dialog's strings is now too long for the .rc file and is truncated at 256 characters.  Fortunately this can be worked around by setting it at runtime.
2023-11-05 21:43:36 -05:00

1145 lines
30 KiB
C++

// VoiceActingManager.cpp : implementation file
//
#include "stdafx.h"
#include <string.h>
#include "fred.h"
#include "freddoc.h"
#include "VoiceActingManager.h"
#include "missionui/missioncmdbrief.h"
#include "mission/missionbriefcommon.h"
#include "mission/missionmessage.h"
#include "mission/missiongoals.h"
#include "hud/hudtarget.h"
#include "parse/sexp.h"
#include "iff_defs/iff_defs.h"
#include "mission/missiongoals.h"
#include <math.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define INVALID_MESSAGE ((MMessage*)SIZE_T_MAX)
// to keep track of data
char Voice_abbrev_briefing[NAME_LENGTH];
char Voice_abbrev_campaign[NAME_LENGTH];
char Voice_abbrev_command_briefing[NAME_LENGTH];
char Voice_abbrev_debriefing[NAME_LENGTH];
char Voice_abbrev_message[NAME_LENGTH];
char Voice_abbrev_mission[NAME_LENGTH];
bool Voice_no_replace_filenames;
char Voice_script_entry_format[NOTES_LENGTH];
int Voice_export_selection;
bool Voice_group_messages;
/////////////////////////////////////////////////////////////////////////////
// VoiceActingManager dialog
VoiceActingManager::VoiceActingManager(CWnd* pParent /*=NULL*/)
: CDialog(VoiceActingManager::IDD, pParent)
{
//{{AFX_DATA_INIT(VoiceActingManager)
m_abbrev_briefing = _T("");
m_abbrev_campaign = _T("");
m_abbrev_command_briefing = _T("");
m_abbrev_debriefing = _T("");
m_abbrev_message = _T("");
m_abbrev_mission = _T("");
m_use_sender_in_filename = FALSE;
m_example = _T("");
m_no_replace = FALSE;
m_script_entry_format = _T("");
m_export_everything = FALSE;
m_export_command_briefings = FALSE;
m_export_briefings = FALSE;
m_export_debriefings = FALSE;
m_export_messages = FALSE;
m_group_messages = FALSE;
//}}AFX_DATA_INIT
}
void VoiceActingManager::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(VoiceActingManager)
DDX_Text(pDX, IDC_ABBREV_BRIEFING, m_abbrev_briefing);
DDX_Text(pDX, IDC_ABBREV_CAMPAIGN, m_abbrev_campaign);
DDX_Text(pDX, IDC_ABBREV_COMMAND_BRIEFING, m_abbrev_command_briefing);
DDX_Text(pDX, IDC_ABBREV_DEBRIEFING, m_abbrev_debriefing);
DDX_Text(pDX, IDC_ABBREV_MESSAGE, m_abbrev_message);
DDX_Text(pDX, IDC_ABBREV_MISSION, m_abbrev_mission);
DDX_Text(pDX, IDC_EXAMPLE, m_example);
DDX_Check(pDX, IDC_NO_REPLACE, m_no_replace);
DDX_Check(pDX, IDC_INCLUDE_SENDER, m_use_sender_in_filename);
DDX_Text(pDX, IDC_ENTRY_FORMAT, m_script_entry_format);
DDX_Check(pDX, IDC_EXPORT_EVERYTHING, m_export_everything);
DDX_Check(pDX, IDC_EXPORT_COMMAND_BRIEFINGS, m_export_command_briefings);
DDX_Check(pDX, IDC_EXPORT_BRIEFINGS, m_export_briefings);
DDX_Check(pDX, IDC_EXPORT_DEBRIEFINGS, m_export_debriefings);
DDX_Check(pDX, IDC_EXPORT_MESSAGES, m_export_messages);
DDX_Check(pDX, IDC_GROUP_MESSAGES, m_group_messages);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(VoiceActingManager, CDialog)
//{{AFX_MSG_MAP(VoiceActingManager)
ON_WM_CLOSE()
ON_EN_SETFOCUS(IDC_ABBREV_BRIEFING, OnSetfocusAbbrevBriefing)
ON_EN_SETFOCUS(IDC_ABBREV_CAMPAIGN, OnSetfocusAbbrevCampaign)
ON_EN_SETFOCUS(IDC_ABBREV_COMMAND_BRIEFING, OnSetfocusAbbrevCommandBriefing)
ON_EN_SETFOCUS(IDC_ABBREV_DEBRIEFING, OnSetfocusAbbrevDebriefing)
ON_EN_SETFOCUS(IDC_ABBREV_MESSAGE, OnSetfocusAbbrevMessage)
ON_EN_SETFOCUS(IDC_ABBREV_MISSION, OnSetfocusAbbrevMission)
ON_CBN_SETFOCUS(IDC_SUFFIX, OnSetfocusSuffix)
ON_EN_CHANGE(IDC_ABBREV_BRIEFING, OnChangeAbbrevBriefing)
ON_EN_CHANGE(IDC_ABBREV_CAMPAIGN, OnChangeAbbrevCampaign)
ON_EN_CHANGE(IDC_ABBREV_COMMAND_BRIEFING, OnChangeAbbrevCommandBriefing)
ON_EN_CHANGE(IDC_ABBREV_DEBRIEFING, OnChangeAbbrevDebriefing)
ON_EN_CHANGE(IDC_ABBREV_MESSAGE, OnChangeAbbrevMessage)
ON_EN_CHANGE(IDC_ABBREV_MISSION, OnChangeAbbrevMission)
ON_CBN_SELCHANGE(IDC_SUFFIX, OnChangeOtherSuffix)
ON_BN_CLICKED(IDC_NO_REPLACE, OnChangeNoReplace)
ON_BN_CLICKED(IDC_GENERATE_FILE_NAMES, OnGenerateFileNames)
ON_BN_CLICKED(IDC_GENERATE_SCRIPT, OnGenerateScript)
ON_BN_CLICKED(IDC_EXPORT_EVERYTHING, OnExportEverything)
ON_BN_CLICKED(IDC_EXPORT_COMMAND_BRIEFINGS, OnExportCommandBriefings)
ON_BN_CLICKED(IDC_EXPORT_BRIEFINGS, OnExportBriefings)
ON_BN_CLICKED(IDC_EXPORT_DEBRIEFINGS, OnExportDebriefings)
ON_BN_CLICKED(IDC_EXPORT_MESSAGES, OnExportMessages)
ON_BN_CLICKED(IDC_INCLUDE_SENDER, OnBnClickedIncludeSender)
ON_BN_CLICKED(IDC_MESSAGE_PERSONAS_TO_SHIPS, OnCopyMessagePersonasToShips)
ON_BN_CLICKED(IDC_SHIP_PERSONAS_TO_MESSAGES, OnCopyShipPersonasToMessages)
ON_BN_CLICKED(IDC_SET_HEAD_ANIS_USING_MESSAGES_TBL, OnSetHeadANIsUsingMessagesTbl)
ON_BN_CLICKED(IDC_CLEAR_PERSONAS_FROM_NON_SENDERS, OnClearPersonasFromNonSenders)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// VoiceActingManager message handlers
BOOL VoiceActingManager::OnInitDialog()
{
CComboBox *box = ((CComboBox *) GetDlgItem(IDC_SUFFIX));
box->AddString(".WAV");
box->AddString(".OGG");
box->SetCurSel(0);
// this text is too long for the .rc file, so set it here
GetDlgItem(IDC_ENTRY_FORMAT_DESC)->SetWindowText(
"$name - name of the message\r\n"
"$filename - name of the message file\r\n"
"$message - text of the message\r\n"
"$persona - persona of the sender\r\n"
"$sender - name of the sender\r\n"
"$note - message notes\r\n\r\n"
"Note that $persona and $sender will only appear for the Message section."
);
// load saved data for file names
m_abbrev_briefing = _T(Voice_abbrev_briefing);
m_abbrev_campaign = _T(Voice_abbrev_campaign);
m_abbrev_command_briefing = _T(Voice_abbrev_command_briefing);
m_abbrev_debriefing = _T(Voice_abbrev_debriefing);
m_abbrev_message = _T(Voice_abbrev_message);
m_abbrev_mission = _T(Voice_abbrev_mission);
m_no_replace = Voice_no_replace_filenames;
// load saved data for script
m_script_entry_format = _T(Voice_script_entry_format);
m_export_everything = m_export_command_briefings = m_export_briefings = m_export_debriefings = m_export_messages = FALSE;
if (Voice_export_selection == 1)
m_export_command_briefings = TRUE;
else if (Voice_export_selection == 2)
m_export_briefings = TRUE;
else if (Voice_export_selection == 3)
m_export_debriefings = TRUE;
else if (Voice_export_selection == 4)
m_export_messages = TRUE;
else
m_export_everything = TRUE;
m_group_messages = Voice_group_messages;
CButton *button = ((CButton *) GetDlgItem(IDC_GROUP_MESSAGES));
button->EnableWindow(m_export_everything || m_export_messages);
UpdateData(FALSE);
return TRUE;
}
void VoiceActingManager::OnClose()
{
UpdateData(TRUE);
// save data for file names
strcpy_s(Voice_abbrev_briefing, m_abbrev_briefing);
strcpy_s(Voice_abbrev_campaign, m_abbrev_campaign);
strcpy_s(Voice_abbrev_command_briefing, m_abbrev_command_briefing);
strcpy_s(Voice_abbrev_debriefing, m_abbrev_debriefing);
strcpy_s(Voice_abbrev_message, m_abbrev_message);
strcpy_s(Voice_abbrev_mission, m_abbrev_mission);
Voice_no_replace_filenames = (m_no_replace == TRUE) ? true : false;
// save data for script
strcpy_s(Voice_script_entry_format, m_script_entry_format);
if (m_export_command_briefings)
Voice_export_selection = 1;
else if (m_export_briefings)
Voice_export_selection = 2;
else if (m_export_debriefings)
Voice_export_selection = 3;
else if (m_export_messages)
Voice_export_selection = 4;
else
Voice_export_selection = 0;
Voice_group_messages = (m_group_messages == TRUE) ? true : false;
CDialog::OnClose();
}
CString VoiceActingManager::get_suffix()
{
// assign suffix
if (((CComboBox *) GetDlgItem(IDC_SUFFIX))->GetCurSel() == 1)
return ".ogg";
else
return ".wav";
}
int VoiceActingManager::calc_digits(int size)
{
if (size >= 10000)
return 5;
else if (size >= 1000) // I hope we never hit this!
return 4;
else if (size >= 100)
return 3;
else
return 2;
}
void VoiceActingManager::build_example()
{
// pick a default
if (m_abbrev_command_briefing != "")
build_example(m_abbrev_command_briefing);
else if (m_abbrev_briefing != "")
build_example(m_abbrev_briefing);
else if (m_abbrev_debriefing != "")
build_example(m_abbrev_debriefing);
else if (m_abbrev_message != "")
build_example(m_abbrev_message);
else
build_example("");
}
void VoiceActingManager::build_example(CString section)
{
if (section == "")
{
m_example = "";
return;
}
m_example = generate_filename(section, 1, 2, INVALID_MESSAGE);
}
CString VoiceActingManager::generate_filename(CString section, int number, int digits, const MMessage *message)
{
if (section == "")
return "none.wav";
int i;
CString str = "";
CString num;
// build prefix
if (m_abbrev_campaign != "")
str = str + m_abbrev_campaign;
if (m_abbrev_mission != "")
str = str + m_abbrev_mission;
str = str + section;
// build number
num.Format("%d", number);;
digits -= num.GetLength();
for (i = 0; i < digits; i++)
{
num = "0" + num;
}
str = str + num;
const CString suffix = get_suffix();
// append sender name if supposed to and I have been passed a message
// to get the sender from
if ( message != NULL && m_use_sender_in_filename ) {
size_t allow_to_copy = NAME_LENGTH - suffix.GetLength() - str.GetLength();
char sender[NAME_LENGTH];
if ( message == INVALID_MESSAGE ) {
strcpy_s(sender, "Alpha 1");
} else {
get_valid_sender(sender, sizeof(sender), message);
}
// truncate sender to that we don't overflow filename
sender[allow_to_copy] = '\0';
size_t j;
for( j = 0; sender[j] != '\0'; j++ ) {
// lower case letter
sender[j] = SCP_tolower(sender[j]);
// replace any non alpha numeric with a underscore
if ( !isalnum( sender[j] ) )
sender[j] = '_';
}
// flatten multiple underscores
j = 1;
while( sender[j] != '\0' ) {
if ( sender[j-1] == '_' && sender[j] == '_' ) {
size_t k;
for (k = j + 1; sender[k] != '\0'; k++ )
sender[k - 1] = sender[k];
sender[k - 1] = '\0';
} else {
// only increment on rounds when I am not moving the string down
j++;
}
}
str = str + sender;
}
// suffix
str = str + get_suffix();
Assert( str.GetLength() < NAME_LENGTH );
return str;
}
void VoiceActingManager::OnGenerateFileNames()
{
int i;
int digits;
size_t modified_filenames = 0;
// stuff data to variables
UpdateData(TRUE);
// command briefings
digits = calc_digits(Cmd_briefs[0].num_stages);
for (i = 0; i < Cmd_briefs[0].num_stages; i++)
{
char *filename = Cmd_briefs[0].stage[i].wave_filename;
// generate only if we're replacing or if it has a replaceable name
if (!m_no_replace || !strlen(filename) || message_filename_is_generic(filename))
{
strcpy(filename, LPCTSTR(generate_filename(m_abbrev_command_briefing, i + 1, digits)));
modified_filenames++;
}
}
// briefings
digits = calc_digits(Briefings[0].num_stages);
for (i = 0; i < Briefings[0].num_stages; i++)
{
char *filename = Briefings[0].stages[i].voice;
// generate only if we're replacing or if it has a replaceable name
if (!m_no_replace || !strlen(filename) || message_filename_is_generic(filename))
{
strcpy(filename, LPCTSTR(generate_filename(m_abbrev_briefing, i + 1, digits)));
modified_filenames++;
}
}
// debriefings
digits = calc_digits(Debriefings[0].num_stages);
for (i = 0; i < Debriefings[0].num_stages; i++)
{
char *filename = Debriefings[0].stages[i].voice;
// generate only if we're replacing or if it has a replaceable name
if (!m_no_replace || !strlen(filename) || message_filename_is_generic(filename))
{
strcpy(filename, LPCTSTR(generate_filename(m_abbrev_debriefing, i + 1, digits)));
modified_filenames++;
}
}
// messages
digits = calc_digits(Num_messages - Num_builtin_messages);
for (i = 0; i < Num_messages - Num_builtin_messages; i++)
{
char *filename = Messages[i + Num_builtin_messages].wave_info.name;
MMessage *message = &Messages[i + Num_builtin_messages];
// generate only if we're replacing or if it has a replaceable name
if (!m_no_replace || !strlen(filename) || message_filename_is_generic(filename))
{
// free existing filename
if (filename != NULL)
free(filename);
// allocate new filename
Messages[i + Num_builtin_messages].wave_info.name = strdup(LPCTSTR(generate_filename(m_abbrev_message, i + 1, digits, message)));
modified_filenames++;
}
}
if ( modified_filenames > 0 ) {
// Tell FRED that we actually modified something
set_modified(TRUE);
}
// notify user that we are done and how many filenames were changed
char message[128] = { '\0' };
snprintf(message, sizeof(message)-1, "File name generation complete. Modified " SIZE_T_ARG " messages.", modified_filenames);
MessageBox(message, "Woohoo!");
}
void VoiceActingManager::OnGenerateScript()
{
char pathname[256];
// stuff data to variables
UpdateData(TRUE);
// prompt to save script
CFileDialog dlg(FALSE, "txt", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Text files (*.txt)|*.txt||");
if (dlg.DoModal() != IDOK)
return;
CString dlgPathName = dlg.GetPathName( );
string_copy(pathname, dlgPathName, 256 - 1);
fp = cfopen(pathname, "wt", CFILE_NORMAL);
if (!fp)
{
MessageBox("Can't open file to save.", "Error!");
return;
}
fout("%s\n", Mission_filename);
fout("%s\n\n", The_mission.name);
if (m_export_everything || m_export_command_briefings)
{
fout("\n\nCommand Briefings\n-----------------\n\n");
for (int i = 0; i < Cmd_briefs[0].num_stages; i++)
{
CString entry = m_script_entry_format;
entry.Replace("\r\n", "\n");
cmd_brief_stage *stage = &Cmd_briefs[0].stage[i];
entry.Replace("$filename", stage->wave_filename);
entry.Replace("$message", stage->text.c_str());
entry.Replace("$persona", "<no persona specified>");
entry.Replace("$sender", "<no sender specified>");
entry.Replace("$name", "<no name specified>");
entry.Replace("$note", "<no note specified>");
fout("%s\n\n\n", (char *) (LPCTSTR) entry);
}
}
if (m_export_everything || m_export_briefings)
{
fout("\n\nBriefings\n---------\n\n");
for (int i = 0; i < Briefings[0].num_stages; i++)
{
CString entry = m_script_entry_format;
entry.Replace("\r\n", "\n");
brief_stage *stage = &Briefings[0].stages[i];
entry.Replace("$filename", stage->voice);
entry.Replace("$message", stage->text.c_str());
entry.Replace("$persona", "<no persona specified>");
entry.Replace("$sender", "<no sender specified>");
entry.Replace("$name", "<no name specified>");
entry.Replace("$note", "<no note specified>");
fout("%s\n\n\n", (char *) (LPCTSTR) entry);
}
}
if (m_export_everything || m_export_debriefings)
{
fout("\n\nDebriefings\n-----------\n\n");
for (int i = 0; i < Debriefings[0].num_stages; i++)
{
CString entry = m_script_entry_format;
entry.Replace("\r\n", "\n");
debrief_stage *stage = &Debriefings[0].stages[i];
entry.Replace("$filename", stage->voice);
entry.Replace("$message", stage->text.c_str());
entry.Replace("$persona", "<no persona specified>");
entry.Replace("$sender", "<no sender specified>");
entry.Replace("$name", "<no name specified>");
entry.Replace("$note", "<no note specified>");
fout("%s\n\n\n", (char *) (LPCTSTR) entry);
}
}
if (m_export_everything || m_export_messages)
{
fout("\n\nMessages\n--------\n\n");
if (m_group_messages)
{
SCP_vector<int> message_indexes;
for (int i = 0; i < Num_messages - Num_builtin_messages; i++)
message_indexes.push_back(i + Num_builtin_messages);
group_message_indexes(message_indexes);
for (size_t index = 0; index < message_indexes.size(); index++)
{
MMessage *message = &Messages[message_indexes[index]];
export_one_message(message);
}
}
else
{
for (int i = 0; i < Num_messages - Num_builtin_messages; i++)
{
MMessage *message = &Messages[i + Num_builtin_messages];
export_one_message(message);
}
}
}
cfclose(fp);
// notify
MessageBox("Script generation complete.", "Woohoo!");
}
void VoiceActingManager::export_one_message(const MMessage *message)
{
CString entry = m_script_entry_format;
entry.Replace("\r\n", "\n");
entry.Replace("$name", message->name);
// replace file name
entry.Replace("$filename", message->wave_info.name);
// replace message
entry.Replace("$message", message->message);
entry.Replace("$note", message->note.c_str());
// determine and replace persona
if (message->persona_index >= 0)
entry.Replace("$persona", Personas[message->persona_index].name);
else
entry.Replace("$persona", "<none>");
// determine sender
char sender[NAME_LENGTH+1];
get_valid_sender(sender, sizeof(sender), message);
// replace sender (but print #Command as Command)
if (*sender == '#')
entry.Replace("$sender", &sender[1]);
else
entry.Replace("$sender", sender);
fout("%s\n\n\n", (char *) (LPCTSTR) entry);
}
/** Passed sender string will have either have the senders name
or '\<none\>'*/
void VoiceActingManager::get_valid_sender(char *sender, size_t sender_size, const MMessage *message, int *sender_shipnum)
{
Assert( sender != NULL );
Assert( message != NULL );
memset(sender, 0, sender_size);
strncpy(sender, get_message_sender(message), sender_size - 1);
// check if we're overriding #Command
if (The_mission.flags[Mission::Mission_Flags::Override_hashcommand] && !strcmp("#Command", sender))
{
memset(sender, 0, sender_size);
strncpy(sender, The_mission.command_sender, sender_size - 1);
}
// strip hash if present
if ( sender[0] == '#' )
{
size_t i = 1;
for(; sender[i] != '\0'; i++ ) {
sender[i-1] = sender[i];
}
sender[i-1] = '\0';
}
int shipnum = ship_name_lookup(sender, 1); // The player's ship is valid for this search.
if (sender_shipnum != nullptr)
*sender_shipnum = shipnum;
if (shipnum >= 0)
{
ship *shipp = &Ships[shipnum];
// we may have to use the callsign
if (*Fred_callsigns[shipnum])
{
hud_stuff_ship_callsign(sender, shipp);
}
// account for hidden ship names
else if ( ((Iff_info[shipp->team].flags & IFFF_WING_NAME_HIDDEN) && (shipp->wingnum != -1)) || (shipp->flags[Ship::Ship_Flags::Hide_ship_name]) )
{
hud_stuff_ship_class(sender, shipp);
}
// use the regular sender display name
else
{
memset(sender, 0, sender_size);
strncpy(sender, shipp->get_display_name(), sender_size - 1);
}
}
}
void VoiceActingManager::OnSetfocusAbbrevBriefing()
{
UpdateData(TRUE);
build_example(m_abbrev_briefing);
UpdateData(FALSE);
}
void VoiceActingManager::OnSetfocusAbbrevCampaign()
{
UpdateData(TRUE);
build_example();
UpdateData(FALSE);
}
void VoiceActingManager::OnSetfocusAbbrevCommandBriefing()
{
UpdateData(TRUE);
build_example(m_abbrev_command_briefing);
UpdateData(FALSE);
}
void VoiceActingManager::OnSetfocusAbbrevDebriefing()
{
UpdateData(TRUE);
build_example(m_abbrev_debriefing);
UpdateData(FALSE);
}
void VoiceActingManager::OnSetfocusAbbrevMessage()
{
UpdateData(TRUE);
build_example(m_abbrev_message);
UpdateData(FALSE);
}
void VoiceActingManager::OnSetfocusAbbrevMission()
{
UpdateData(TRUE);
build_example();
UpdateData(FALSE);
}
void VoiceActingManager::OnSetfocusSuffix()
{
UpdateData(TRUE);
build_example();
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeAbbrevBriefing()
{
UpdateData(TRUE);
build_example(m_abbrev_briefing);
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeAbbrevCampaign()
{
UpdateData(TRUE);
build_example();
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeAbbrevCommandBriefing()
{
UpdateData(TRUE);
build_example(m_abbrev_command_briefing);
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeAbbrevDebriefing()
{
UpdateData(TRUE);
build_example(m_abbrev_debriefing);
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeAbbrevMessage()
{
UpdateData(TRUE);
build_example(m_abbrev_message);
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeAbbrevMission()
{
UpdateData(TRUE);
build_example();
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeOtherSuffix()
{
UpdateData(TRUE);
build_example();
UpdateData(FALSE);
}
void VoiceActingManager::OnChangeNoReplace()
{
UpdateData(TRUE);
}
int VoiceActingManager::fout(const char *format, ...)
{
SCP_string str;
va_list args;
va_start(args, format);
vsprintf(str, format, args);
va_end(args);
cfputs(str.c_str(), fp);
return 0;
}
// Loops through all the sexps and finds the sender of the specified message. This assumes there is only one possible
// sender of the message, which is probably nearly always true (especially for voice-acted missions).
const char *VoiceActingManager::get_message_sender(const MMessage *message)
{
int i;
for (i = 0; i < Num_sexp_nodes; i++)
{
if (Sexp_nodes[i].type == SEXP_NOT_USED)
continue;
// stuff
int op = get_operator_const(i);
int n = CDR(i);
// find the message sexps
if (op == OP_SEND_MESSAGE)
{
// the first argument is the sender; the third is the message
if (!strcmp(message->name, Sexp_nodes[CDDR(n)].text))
return Sexp_nodes[n].text;
}
else if (op == OP_SEND_MESSAGE_LIST || op == OP_SEND_MESSAGE_CHAIN)
{
// skip the event argument
if (op == OP_SEND_MESSAGE_CHAIN)
n = CDR(n);
// check the argument list
while (n != -1)
{
// as before
if (!strcmp(message->name, Sexp_nodes[CDDR(n)].text))
return Sexp_nodes[n].text;
// iterate along the list
n = CDDDDR(n);
}
}
else if (op == OP_SEND_RANDOM_MESSAGE)
{
// as before, sort of
char *sender = Sexp_nodes[n].text;
// check the argument list
n = CDDR(n);
while (n != -1)
{
if (!strcmp(message->name, Sexp_nodes[n].text))
return sender;
// iterate along the list
n = CDR(n);
}
}
else if (op == OP_TRAINING_MSG)
{
// just check the message
if (!strcmp(message->name, Sexp_nodes[n].text))
return "Training Message";
}
}
return "<none>";
}
void VoiceActingManager::group_message_indexes(SCP_vector<int> &message_indexes)
{
#ifndef NDEBUG
size_t initial_size = message_indexes.size();
#endif
SCP_vector<int> temp_message_indexes = message_indexes;
message_indexes.clear();
// add all messages found in send-message-list or send-random-message node trees
for (const auto &event: Mission_events)
group_message_indexes_in_tree(event.formula, temp_message_indexes, message_indexes);
// add remaining messages
for (size_t index = 0; index < temp_message_indexes.size(); index++)
message_indexes.push_back(temp_message_indexes[index]);
#ifndef NDEBUG
if (initial_size > message_indexes.size())
{
Warning(LOCATION, "Initial size is greater than size after sorting!");
}
else if (initial_size < message_indexes.size())
{
Warning(LOCATION, "Initial size is less than size after sorting!");
}
#endif
}
void VoiceActingManager::group_message_indexes_in_tree(int node, SCP_vector<int> &source_list, SCP_vector<int> &destination_list)
{
int op, n;
if (node < 0)
return;
if (Sexp_nodes[node].type == SEXP_NOT_USED)
return;
// stuff
op = get_operator_const(node);
n = CDR(node);
if (op == OP_SEND_MESSAGE_LIST || op == OP_SEND_MESSAGE_CHAIN)
{
// skip the event argument
if (op == OP_SEND_MESSAGE_CHAIN)
n = CDR(n);
// check the argument list
while (n != -1)
{
// the third argument is a message
char *message_name = Sexp_nodes[CDDR(n)].text;
// check source messages
for (size_t i = 0; i < source_list.size(); i++)
{
if (!strcmp(message_name, Messages[source_list[i]].name))
{
// move it from source to destination
destination_list.push_back(source_list[i]);
source_list.erase(source_list.begin() + i);
break;
}
}
// iterate along the list
n = CDDDDR(n);
}
}
else if (op == OP_SEND_RANDOM_MESSAGE)
{
// check the argument list
n = CDDR(n);
while (n != -1)
{
// each argument from this point on is a message
char *message_name = Sexp_nodes[n].text;
// check source messages
for (size_t i = 0; i < source_list.size(); i++)
{
if (!strcmp(message_name, Messages[source_list[i]].name))
{
// move it from source to destination
destination_list.push_back(source_list[i]);
source_list.erase(source_list.begin() + i);
break;
}
}
// iterate along the list
n = CDR(n);
}
}
// iterate on first element
group_message_indexes_in_tree(CAR(node), source_list, destination_list);
// iterate on rest of elements
group_message_indexes_in_tree(CDR(node), source_list, destination_list);
}
void VoiceActingManager::OnExportEverything()
{
CButton *button = ((CButton *) GetDlgItem(IDC_GROUP_MESSAGES));
button->EnableWindow(TRUE);
}
void VoiceActingManager::OnExportCommandBriefings()
{
CButton *button = ((CButton *) GetDlgItem(IDC_GROUP_MESSAGES));
button->EnableWindow(FALSE);
}
void VoiceActingManager::OnExportBriefings()
{
CButton *button = ((CButton *) GetDlgItem(IDC_GROUP_MESSAGES));
button->EnableWindow(FALSE);
}
void VoiceActingManager::OnExportDebriefings()
{
CButton *button = ((CButton *) GetDlgItem(IDC_GROUP_MESSAGES));
button->EnableWindow(FALSE);
}
void VoiceActingManager::OnExportMessages()
{
CButton *button = ((CButton *) GetDlgItem(IDC_GROUP_MESSAGES));
button->EnableWindow(TRUE);
}
void VoiceActingManager::OnBnClickedIncludeSender()
{
UpdateData(TRUE);
build_example();
UpdateData(FALSE);
}
void VoiceActingManager::OnCopyPersonas(bool messages_to_ships)
{
char sender_buf[NAME_LENGTH];
int sender_shipnum;
SCP_unordered_set<int> already_assigned;
SCP_string inconsistent_copy_msg;
// go through all messages in the mission
for (int i = 0; i < Num_messages - Num_builtin_messages; i++)
{
auto message = &Messages[i + Num_builtin_messages];
// find whoever sent this message
get_valid_sender(sender_buf, NAME_LENGTH, message, &sender_shipnum);
// if it's a ship, copy the persona
if (sender_shipnum >= 0)
{
auto sender_shipp = &Ships[sender_shipnum];
auto persona_to_copy = (messages_to_ships) ? message->persona_index : sender_shipp->persona_index;
// don't copy None, and only copy wingman personas
if (persona_to_copy >= 0 && (Personas[persona_to_copy].flags & PERSONA_FLAG_WINGMAN))
{
if (messages_to_ships)
{
if (already_assigned.count(sender_shipnum) > 0 && sender_shipp->persona_index != persona_to_copy)
{
inconsistent_copy_msg += "\n\u2022 ";
inconsistent_copy_msg += sender_shipp->ship_name;
}
sender_shipp->persona_index = persona_to_copy;
already_assigned.insert(sender_shipnum);
}
else
{
if (already_assigned.count(i) > 0 && message->persona_index != persona_to_copy)
{
inconsistent_copy_msg += "\n\u2022 ";
inconsistent_copy_msg += message->name;
}
message->persona_index = persona_to_copy;
already_assigned.insert(i);
}
}
}
}
if (messages_to_ships)
{
if (!inconsistent_copy_msg.empty())
{
inconsistent_copy_msg = "Attempted to copy personas from messages to ships, but the following ships send messages with inconsistent personas. You may want to review them.\n" + inconsistent_copy_msg;
MessageBox(inconsistent_copy_msg.c_str(), "Voice Acting Manager");
}
else
MessageBox("Personas have been copied from messages to ships.", "Voice Acting Manager");
}
else
{
if (!inconsistent_copy_msg.empty())
{
inconsistent_copy_msg = "Attempted to copy personas from ships to messages, but the following messages are sent by ships with inconsistent personas. You may want to review them.\n" + inconsistent_copy_msg;
MessageBox(inconsistent_copy_msg.c_str(), "Voice Acting Manager");
}
else
MessageBox("Personas have been copied from ships to messages.", "Voice Acting Manager");
}
}
void VoiceActingManager::OnClearPersonasFromNonSenders()
{
SCP_unordered_set<int> all_senders;
char sender_buf[NAME_LENGTH];
int sender_shipnum;
// go through all messages in the mission
for (int i = 0; i < Num_messages - Num_builtin_messages; i++)
{
auto message = &Messages[i + Num_builtin_messages];
// find whoever sent this message
get_valid_sender(sender_buf, NAME_LENGTH, message, &sender_shipnum);
// if it's a ship, save the shipnum
if (sender_shipnum >= 0)
all_senders.insert(sender_shipnum);
}
// go through all ships in the mission
for (auto objp : list_range(&obj_used_list))
{
if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP))
{
// for ships that aren't message senders
if (all_senders.count(objp->instance) == 0)
{
// only clear wingman personas
if ((Ships[objp->instance].persona_index >= 0) && (Personas[Ships[objp->instance].persona_index].flags & PERSONA_FLAG_WINGMAN))
{
// clear the persona
Ships[objp->instance].persona_index = -1;
}
}
}
}
MessageBox("Personas have been cleared from all ships that do not send messages.", "Voice Acting Manager");
}
void VoiceActingManager::OnCopyMessagePersonasToShips()
{
OnCopyPersonas(true);
}
void VoiceActingManager::OnCopyShipPersonasToMessages()
{
OnCopyPersonas(false);
}
void VoiceActingManager::OnSetHeadANIsUsingMessagesTbl()
{
// go through all messages in the mission
for (int i = 0; i < Num_messages - Num_builtin_messages; i++)
{
auto message = &Messages[i + Num_builtin_messages];
// only messages with personas
if (message->persona_index < 0)
continue;
// only wingman personas
if (!(Personas[message->persona_index].flags & PERSONA_FLAG_WINGMAN))
continue;
// find the corresponding head for this persona
bool found = false;
for (int j = 0; j < Num_builtin_messages; j++)
{
auto builtin_message = &Messages[j];
if (message->persona_index == builtin_message->persona_index)
{
// either assign the correct head from scratch, or change the head to the correct one
if (message->avi_info.name == nullptr)
{
message->avi_info.name = strdup(builtin_message->avi_info.name);
}
else if (stricmp(message->avi_info.name, builtin_message->avi_info.name) != 0)
{
free(message->avi_info.name);
message->avi_info.name = strdup(builtin_message->avi_info.name);
}
// done searching
found = true;
break;
}
}
if (!found)
{
Warning(LOCATION, "Persona index %d was not found in list of messages.tbl personas!", message->persona_index);
}
}
MessageBox("Message head ANIs have been assigned from builtin messages.", "Voice Acting Manager");
}