/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** 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 . */ // EditConversationDialog.cpp : implementation file // #include "stdafx.h" #include "leveledit.h" #include "editconversationdialog.h" #include "conversation.h" #include "translatedb.h" #include "translateobj.h" #include "editconversationremarkdialog.h" #include "playertype.h" #include "orator.h" #include "oratortypes.h" #include "utils.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Constants ///////////////////////////////////////////////////////////////////////////// enum { COL_ORATOR = 0, COL_REMARK }; static const int ORATOR_MAX = 6; typedef struct { int button_id; int combo_id; int check_id; int icon_id; } ORATOR_UI_INFO; const ORATOR_UI_INFO ORATOR_CTRLS[ORATOR_MAX] = { { IDC_ORATOR1_CHECK, IDC_ORATOR1_COMBO, IDC_ORATOR1_VISBLE_CHECK, IDI_ORATOR1 }, { IDC_ORATOR2_CHECK, IDC_ORATOR2_COMBO, IDC_ORATOR2_VISBLE_CHECK, IDI_ORATOR2 }, { IDC_ORATOR3_CHECK, IDC_ORATOR3_COMBO, IDC_ORATOR3_VISBLE_CHECK, IDI_ORATOR3 }, { IDC_ORATOR4_CHECK, IDC_ORATOR4_COMBO, IDC_ORATOR4_VISBLE_CHECK, IDI_ORATOR4 }, { IDC_ORATOR5_CHECK, IDC_ORATOR5_COMBO, IDC_ORATOR5_VISBLE_CHECK, IDI_ORATOR5 }, { IDC_ORATOR6_CHECK, IDC_ORATOR6_COMBO, IDC_ORATOR6_VISBLE_CHECK, IDI_ORATOR6 }, }; ///////////////////////////////////////////////////////////////////////////// // // EditConversationDialogClass // ///////////////////////////////////////////////////////////////////////////// EditConversationDialogClass::EditConversationDialogClass(CWnd* pParent /*=NULL*/) : m_Conversation (NULL), CDialog(EditConversationDialogClass::IDD, pParent) { //{{AFX_DATA_INIT(EditConversationDialogClass) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT return ; } ///////////////////////////////////////////////////////////////////////////// // // ~EditConversationDialogClass // ///////////////////////////////////////////////////////////////////////////// EditConversationDialogClass::~EditConversationDialogClass (void) { REF_PTR_RELEASE (m_Conversation); return ; } ///////////////////////////////////////////////////////////////////////////// // // DoDataExchange // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::DoDataExchange (CDataExchange *pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(EditConversationDialogClass) DDX_Control(pDX, IDC_REMARK_LIST, m_ListCtrl); //}}AFX_DATA_MAP return ; } BEGIN_MESSAGE_MAP(EditConversationDialogClass, CDialog) //{{AFX_MSG_MAP(EditConversationDialogClass) ON_NOTIFY(NM_DBLCLK, IDC_REMARK_LIST, OnDblclkRemarkList) ON_NOTIFY(LVN_KEYDOWN, IDC_REMARK_LIST, OnKeydownRemarkList) ON_BN_CLICKED(IDC_ADD, OnAdd) ON_BN_CLICKED(IDC_INSERT, OnInsert) ON_NOTIFY(LVN_ITEMCHANGED, IDC_REMARK_LIST, OnItemchangedRemarkList) ON_NOTIFY(LVN_DELETEITEM, IDC_REMARK_LIST, OnDeleteitemRemarkList) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // // OnInitDialog // ///////////////////////////////////////////////////////////////////////////// BOOL EditConversationDialogClass::OnInitDialog (void) { CDialog::OnInitDialog (); // // Create a new conversation if we don't already have one to edit // if (m_Conversation == NULL) { m_Conversation = new ConversationClass; OratorClass orator; m_Conversation->Add_Orator (orator); } // // Configure the state combo box // SendDlgItemMessage (IDC_STATE_COMBO, CB_ADDSTRING, 0, (LPARAM)"Idle"); SendDlgItemMessage (IDC_STATE_COMBO, CB_ADDSTRING, 0, (LPARAM)"Idle (Secondary)"); SendDlgItemMessage (IDC_STATE_COMBO, CB_ADDSTRING, 0, (LPARAM)"Search"); SendDlgItemMessage (IDC_STATE_COMBO, CB_ADDSTRING, 0, (LPARAM)"Combat"); SendDlgItemMessage (IDC_STATE_COMBO, CB_SETCURSEL, (WPARAM)m_Conversation->Get_AI_State ()); // // Configure the orator controls // for (int index = 0; index < ORATOR_MAX; index ++) { // // Put the icon into the button control // HICON icon = ::LoadIcon (::AfxGetResourceHandle (), MAKEINTRESOURCE (ORATOR_CTRLS[index].icon_id)); SendDlgItemMessage (ORATOR_CTRLS[index].button_id, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon); // // Put the "visible" icon into the checkbox control // icon = (HICON)::LoadImage (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDI_EYE2), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); SendDlgItemMessage (ORATOR_CTRLS[index].check_id, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon); SendDlgItemMessage (ORATOR_CTRLS[index].check_id, BM_SETCHECK, (WPARAM)TRUE); // // Configure the orator type combo-box // int type_count = OratorTypeClass::Get_Count (); for (int type_index = 0; type_index < type_count; type_index ++) { // // Lookup information about this orator type // const char *type_name = OratorTypeClass::Get_Description (type_index); int type_id = OratorTypeClass::Get_ID (type_index); int combobox_id = ORATOR_CTRLS[index].combo_id; // // Add this orator type to the combobox // int item_index = SendDlgItemMessage (combobox_id, CB_ADDSTRING, 0, (LPARAM)type_name); SendDlgItemMessage (combobox_id, CB_SETITEMDATA, (WPARAM)item_index, (LPARAM)type_id); } // // Select the first item by default // SendDlgItemMessage (ORATOR_CTRLS[index].combo_id, CB_SETCURSEL, (WPARAM)0); } // // Configure the orator controls // bool is_locked = true; int orator_count = m_Conversation->Get_Orator_Count (); for (index = 0; index < orator_count; index ++) { OratorClass *orator = m_Conversation->Get_Orator (index); // // Check the checkbox control and enable the combobox control // SendDlgItemMessage (ORATOR_CTRLS[index].button_id, BM_SETCHECK, (WPARAM)TRUE); SendDlgItemMessage (ORATOR_CTRLS[index].check_id, BM_SETCHECK, (WPARAM)(!orator->Is_Invisible ())); ::EnableWindow (::GetDlgItem (m_hWnd, ORATOR_CTRLS[index].combo_id), TRUE); ::EnableWindow (::GetDlgItem (m_hWnd, ORATOR_CTRLS[index].check_id), TRUE); // // Select the current orator type in the combobox // int orator_type = orator->Get_Orator_Type (); int item_index = Find_Combobox_Entry (orator_type); if (item_index >= 0) { SendDlgItemMessage (ORATOR_CTRLS[index].combo_id, CB_SETCURSEL, (WPARAM)item_index); } // // If any of the orator types disagree, then don't lock the controls // if (orator_type != m_Conversation->Get_Orator (0)->Get_Orator_Type ()) { is_locked = false; } } // // Configure the "lock" checkbox // HICON lock_icon = (HICON)::LoadImage (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDI_LOCK), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); SendDlgItemMessage (IDC_LOCKED_CHECK, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)lock_icon); SendDlgItemMessage (IDC_LOCKED_CHECK, BM_SETCHECK, (WPARAM)is_locked); // // Set the check state of the "Is Innate" and "Is Key" checkboxes // SendDlgItemMessage (IDC_INNATE_CHECK, BM_SETCHECK, (WPARAM)m_Conversation->Is_Innate ()); SendDlgItemMessage (IDC_KEY_CONVESATION_CHECK, BM_SETCHECK, (WPARAM)m_Conversation->Is_Key ()); // // Configure the columns // m_ListCtrl.InsertColumn (COL_ORATOR, "Orator"); m_ListCtrl.InsertColumn (COL_REMARK, "Remark"); m_ListCtrl.SetExtendedStyle (m_ListCtrl.GetExtendedStyle () | LVS_EX_FULLROWSELECT); // // Choose an appropriate size for the columns // CRect rect; m_ListCtrl.GetClientRect (&rect); rect.right -= ::GetSystemMetrics (SM_CXVSCROLL) + 2; m_ListCtrl.SetColumnWidth (COL_ORATOR, rect.Width () / 4); m_ListCtrl.SetColumnWidth (COL_REMARK, (rect.Width () * 3) / 4); // // Fill in the conversation's name into the appropriate control // SetDlgItemText (IDC_CONVERSATION_NAME, m_Conversation->Get_Name ()); // // Add all the remark's to the list control // int count = m_Conversation->Get_Remark_Count (); for (index = 0; index < count; index ++) { ConversationRemarkClass remark; if (m_Conversation->Get_Remark_Info (index, remark)) { Add_Entry (remark); } } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // // OnDblclkRemarkList // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::OnDblclkRemarkList ( NMHDR * pNMHDR, LRESULT* pResult ) { (*pResult) = 0; // // Determine which entry the user double-clicked on // int sel_index = m_ListCtrl.GetNextItem (-1, LVNI_ALL | LVNI_SELECTED); if (sel_index != -1) { // // Lookup the information about this entry // ConversationRemarkClass *remark = Get_Entry_Data (sel_index); // // Make a bitmask of the orators that are configured // int orator_bitmask = 0; for (int index = 0; index < ORATOR_MAX; index ++) { if (SendDlgItemMessage (ORATOR_CTRLS[index].button_id, BM_GETCHECK) == 1) { orator_bitmask |= (1 << index); } } // // Show a dialog to the user that will let them choose an orator and // a remark // EditConversationRemarkDialogClass dialog (this); dialog.Set_Remark (*remark); dialog.Set_Orator_Bitmask (orator_bitmask); if (dialog.DoModal () == IDOK) { // // Get the new values from the dialog // (*remark) = dialog.Get_Remark (); // // Update the orator id in the list control // CString orator_text; orator_text.Format ("%d", remark->Get_Orator_ID () + 1); m_ListCtrl.SetItemText (sel_index, COL_ORATOR, orator_text); // // Update the text entry in the list control // TDBObjClass *translate_obj = TranslateDBClass::Find_Object (remark->Get_Text_ID ()); if (translate_obj != NULL) { m_ListCtrl.SetItemText (sel_index, COL_REMARK, translate_obj->Get_English_String ()); } } } return ; } ///////////////////////////////////////////////////////////////////////////// // // OnKeydownRemarkList // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::OnKeydownRemarkList ( NMHDR * pNMHDR, LRESULT * pResult ) { LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR; (*pResult) = 0; if (pLVKeyDow->wVKey == VK_DELETE) { // // Delete all the selected items // int index = -1; while ((index = m_ListCtrl.GetNextItem (-1, LVNI_SELECTED | LVNI_ALL)) >= 0) { m_ListCtrl.DeleteItem (index); } } return ; } ///////////////////////////////////////////////////////////////////////////// // // OnOK // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::OnOK (void) { CDialog::OnOK (); // // Read the state this conversation is active for // int ai_state = SendDlgItemMessage (IDC_STATE_COMBO, CB_GETCURSEL); if (ai_state != CB_ERR) { m_Conversation->Set_AI_State (SoldierAIState(ai_state)); } // // Pass the name onto the conversation // CString name; GetDlgItemText (IDC_CONVERSATION_NAME, name); m_Conversation->Set_Name (name); // // Start fresh // m_Conversation->Clear_Remarks (); m_Conversation->Clear_Orators (); // // Get all the conversations from the list control and add them to the // conversation manager // for (int index = 0; index < m_ListCtrl.GetItemCount (); index ++) { ConversationRemarkClass *remark = Get_Entry_Data (index); m_Conversation->Add_Remark (*remark); } // // Build a list of orators for the conversation // for (index = 0; index < ORATOR_MAX; index ++) { // // Is this orator configured? // if (SendDlgItemMessage (ORATOR_CTRLS[index].button_id, BM_GETCHECK) == 1) { bool is_invisible = (SendDlgItemMessage (ORATOR_CTRLS[index].check_id, BM_GETCHECK) != 1); // // Add the orator to the conversation // OratorClass orator; orator.Set_ID (index); orator.Set_Orator_Type (Get_Orator_Type (index)); orator.Set_Is_Invisible (is_invisible); m_Conversation->Add_Orator (orator); } } // // Get the ID of the object to look at (if any) // int obj_id = GetDlgItemInt (IDC_OBJECT_ID); m_Conversation->Set_Look_At_Obj_ID (obj_id); // // Read the users "innate" selection // bool is_innate = bool(SendDlgItemMessage (IDC_INNATE_CHECK, BM_GETCHECK) == 1); m_Conversation->Set_Is_Innate (is_innate); bool is_key = bool(SendDlgItemMessage (IDC_KEY_CONVESATION_CHECK, BM_GETCHECK) == 1); m_Conversation->Set_Is_Key (is_key); return ; } ///////////////////////////////////////////////////////////////////////////// // // OnAdd // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::OnAdd (void) { // // Make a bitmask of the orators that are configured // int orator_bitmask = 0; for (int index = 0; index < ORATOR_MAX; index ++) { if (SendDlgItemMessage (ORATOR_CTRLS[index].button_id, BM_GETCHECK) == 1) { orator_bitmask |= (1 << index); } } EditConversationRemarkDialogClass dialog (this); dialog.Set_Orator_Bitmask (orator_bitmask); if (dialog.DoModal () == IDOK) { // // Insert a new entry into the list control // Add_Entry (dialog.Get_Remark ()); } return ; } ///////////////////////////////////////////////////////////////////////////// // // Set_Conversation // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::Set_Conversation (ConversationClass *conversation) { REF_PTR_SET (m_Conversation, conversation); return ; } ///////////////////////////////////////////////////////////////////////////// // // Get_Entry_Data // ///////////////////////////////////////////////////////////////////////////// ConversationRemarkClass * EditConversationDialogClass::Get_Entry_Data (int index) { return (ConversationRemarkClass *)m_ListCtrl.GetItemData (index); } ///////////////////////////////////////////////////////////////////////////// // // Add_Entry // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::Add_Entry (const ConversationRemarkClass &remark, int insert_index) { // // Insert this item into the list control // CString orator_text; orator_text.Format ("%d", remark.Get_Orator_ID () + 1); int item_index = m_ListCtrl.InsertItem (insert_index, orator_text); if (item_index >= 0) { // // Lookup the text to display for this remark // TDBObjClass *translate_obj = TranslateDBClass::Find_Object (remark.Get_Text_ID ()); if (translate_obj != NULL) { m_ListCtrl.SetItemText (item_index, COL_REMARK, translate_obj->Get_English_String ()); // // Allocate a remark object to associate with this entry in the list control // ConversationRemarkClass *associated_remark = new ConversationRemarkClass (remark); m_ListCtrl.SetItemData (item_index, (DWORD)associated_remark); } } return ; } ///////////////////////////////////////////////////////////////////////////// // // OnInsert // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::OnInsert (void) { int sel_index = m_ListCtrl.GetNextItem (-1, LVNI_ALL | LVNI_SELECTED); if (sel_index != -1) { // // Make a bitmask of the orators that are configured // int orator_bitmask = 0; for (int index = 0; index < ORATOR_MAX; index ++) { if (SendDlgItemMessage (ORATOR_CTRLS[index].button_id, BM_GETCHECK) == 1) { orator_bitmask |= (1 << index); } } EditConversationRemarkDialogClass dialog (this); dialog.Set_Orator_Bitmask (orator_bitmask); if (dialog.DoModal () == IDOK) { // // Insert the new entry into the appropriate place in the list // Add_Entry (dialog.Get_Remark (), sel_index); } } return ; } ///////////////////////////////////////////////////////////////////////////// // // OnItemchangedRemarkList // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::OnItemchangedRemarkList ( NMHDR * pNMHDR, LRESULT * pResult ) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; (*pResult) = 0; Update_Button_States (); return ; } ///////////////////////////////////////////////////////////////////////////// // // Update_Button_States // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::Update_Button_States (void) { int sel_index = m_ListCtrl.GetNextItem (-1, LVNI_ALL | LVNI_SELECTED); ::EnableWindow (::GetDlgItem (m_hWnd, IDC_INSERT), static_cast(sel_index != -1)); return ; } ///////////////////////////////////////////////////////////////////////////// // // OnCommand // ///////////////////////////////////////////////////////////////////////////// BOOL EditConversationDialogClass::OnCommand ( WPARAM wParam, LPARAM lParam ) { switch (LOWORD (wParam)) { case IDC_ORATOR1_CHECK: case IDC_ORATOR2_CHECK: case IDC_ORATOR3_CHECK: case IDC_ORATOR4_CHECK: case IDC_ORATOR5_CHECK: case IDC_ORATOR6_CHECK: { Update_Enable_State (LOWORD (wParam) - IDC_ORATOR1_CHECK); Update_Remarks (LOWORD (wParam) - IDC_ORATOR1_CHECK); } break; case IDC_ORATOR1_COMBO: case IDC_ORATOR2_COMBO: case IDC_ORATOR3_COMBO: case IDC_ORATOR4_COMBO: case IDC_ORATOR5_COMBO: case IDC_ORATOR6_COMBO: { if (HIWORD (wParam) == CBN_SELCHANGE) { Update_Player_Type_Combos (LOWORD (wParam) - IDC_ORATOR1_COMBO); } } break; } return CDialog::OnCommand (wParam, lParam); } ///////////////////////////////////////////////////////////////////////////// // // Update_Remarks // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::Update_Remarks (int orator_index) { BOOL is_checked = (SendDlgItemMessage (ORATOR_CTRLS[orator_index].button_id, BM_GETCHECK) == 1); if (is_checked == false) { // // Remove any remarks made by the specified orator // int item_count = m_ListCtrl.GetItemCount (); for (int index = 0; index < item_count; index ++) { ConversationRemarkClass *remark = Get_Entry_Data (index); // // Remove this entry if it was made by the orator in question // if (remark != NULL && remark->Get_Orator_ID () == orator_index) { m_ListCtrl.DeleteItem (index); index --; item_count --; } } } return ; } ///////////////////////////////////////////////////////////////////////////// // // Update_Enable_State // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::Update_Enable_State (int orator_index) { BOOL is_checked = (SendDlgItemMessage (ORATOR_CTRLS[orator_index].button_id, BM_GETCHECK) == 1); ::EnableWindow (::GetDlgItem (m_hWnd, ORATOR_CTRLS[orator_index].combo_id), is_checked); ::EnableWindow (::GetDlgItem (m_hWnd, ORATOR_CTRLS[orator_index].check_id), is_checked); return ; } ///////////////////////////////////////////////////////////////////////////// // // Update_Player_Type_Combos // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::Update_Player_Type_Combos (int orator_index) { BOOL is_locked = (SendDlgItemMessage (IDC_LOCKED_CHECK, BM_GETCHECK) == 1); if (is_locked) { // // Get the current selection // int curr_sel = SendDlgItemMessage (ORATOR_CTRLS[orator_index].combo_id, CB_GETCURSEL); // // Update all the combo boxes to reflect the current selection // for (int index = 0; index < ORATOR_MAX; index ++) { SendDlgItemMessage (ORATOR_CTRLS[index].combo_id, CB_SETCURSEL, (WPARAM)curr_sel); } } return ; } ///////////////////////////////////////////////////////////////////////////// // // Get_Orator_Type // ///////////////////////////////////////////////////////////////////////////// int EditConversationDialogClass::Get_Orator_Type (int orator_index) { int retval = -1; // // Get the current selection // int curr_sel = SendDlgItemMessage (ORATOR_CTRLS[orator_index].combo_id, CB_GETCURSEL); if (curr_sel >= 0) { retval = SendDlgItemMessage (ORATOR_CTRLS[orator_index].combo_id, CB_GETITEMDATA, (WPARAM)curr_sel); } return retval; } ///////////////////////////////////////////////////////////////////////////// // // OnDeleteitemRemarkList // ///////////////////////////////////////////////////////////////////////////// void EditConversationDialogClass::OnDeleteitemRemarkList ( NMHDR * pNMHDR, LRESULT * pResult ) { NM_LISTVIEW *pNMListView = (NM_LISTVIEW *)pNMHDR; (*pResult) = 0; // // Lookup the associated remark object // ConversationRemarkClass *remark = NULL; remark = (ConversationRemarkClass *)m_ListCtrl.GetItemData (pNMListView->iItem); // // Free the object // SAFE_DELETE (remark); m_ListCtrl.SetItemData (pNMListView->iItem, 0); return ; } ///////////////////////////////////////////////////////////////////////////// // // Find_Combobox_Entry // ///////////////////////////////////////////////////////////////////////////// int EditConversationDialogClass::Find_Combobox_Entry (int orator_type) { int retval = -1; // // Loop over all the entries in the combobox until we've found // the one that's associated with the given orator type // int count = SendDlgItemMessage (IDC_ORATOR1_COMBO, CB_GETCOUNT); for (int index = 0; index < count; index ++) { // // Is this the orator we are looking for? // int item_data = SendDlgItemMessage (IDC_ORATOR1_COMBO, CB_GETITEMDATA, (WPARAM)index); if (item_data == orator_type) { retval = index; break; } } return retval; }