diff --git a/MissionEditor/IniHelper.h b/MissionEditor/IniHelper.h index eb0c571..f3828c0 100644 --- a/MissionEditor/IniHelper.h +++ b/MissionEditor/IniHelper.h @@ -62,4 +62,22 @@ public: buffer[sizeof buffer - 1] = '\0'; return buffer; } + + static inline std::vector Split(CString str, char ch = ',') { + std::vector ret; + int start = 0; + int end = 0; + + while (end != -1) { + end = str.Find(ch, start); + if (end == -1) { + ret.push_back(str.Mid(start)); + } else { + ret.push_back(str.Mid(start, end - start)); + start = end + 1; + } + } + + return ret; + } }; \ No newline at end of file diff --git a/MissionEditor/MissionEditor.rc b/MissionEditor/MissionEditor.rc index 14c0996..b195ae3 100644 --- a/MissionEditor/MissionEditor.rc +++ b/MissionEditor/MissionEditor.rc @@ -1051,22 +1051,29 @@ CAPTION "Scripts" FONT 8, "Tahoma", 0, 0, 0x0 BEGIN LTEXT "Scripttypes let you, for example, define that a team moves from one point to another. A scripttype is attached to a TeamType (not a TaskForce!)",IDC_SCTIPTTYPE_INRO,7,7,288,36 - LTEXT "Scripttype",IDC_SCRIPTTYPE_TYPE,5,50,65,12 - COMBOBOX IDC_SCRIPTTYPE,70,51,223,196,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Add",IDC_ADD,165,70,59,14 - PUSHBUTTON "Delete",IDC_DELETE,232,70,59,14 - LTEXT "Name:",IDC_SCRIPTTYPE_NAME,6,89,65,12 - EDITTEXT IDC_NAME,71,89,223,13,ES_AUTOHSCROLL - LTEXT "Actions:",IDC_SCRIPTTYPE_ACTIONS,6,105,65,11 - LISTBOX IDC_ACTION,71,105,223,60,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - LTEXT "Type of action:",IDC_SCRIPTTYPE_ACTIONTYPE,5,199,65,12 - LTEXT "Parameter of action:",IDC_PDESC,5,215,65,17 - COMBOBOX IDC_TYPE,70,200,223,192,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_PARAM,70,215,223,111,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Delete",IDC_DELETEACTION,233,172,60,14 - PUSHBUTTON "Add",IDC_ADDACTION,165,172,60,14 - LTEXT "Description:",IDC_SCRIPTTYPE_DESC,5,230,62,13 - EDITTEXT IDC_DESCRIPTION,70,230,223,50,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL + LTEXT "Scripttype",IDC_SCRIPTTYPE_TYPE,5,84,65,12 + COMBOBOX IDC_SCRIPTTYPE,70,84,223,196,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_ADD,234,64,59,14 + PUSHBUTTON "Delete",IDC_DELETE,232,106,59,14 + LTEXT "Name:",IDC_SCRIPTTYPE_NAME,6,123,65,12 + EDITTEXT IDC_NAME,71,123,223,13,ES_AUTOHSCROLL + LTEXT "Actions:",IDC_SCRIPTTYPE_ACTIONS,6,139,65,11 + LISTBOX IDC_ACTION,71,139,223,60,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "Type of action:",IDC_SCRIPTTYPE_ACTIONTYPE,5,221,65,12 + LTEXT "Parameter of action:",IDC_PDESC,5,240,65,17 + COMBOBOX IDC_TYPE,70,221,223,192,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_PARAM,70,240,223,111,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Delete",IDC_DELETEACTION,233,201,60,14 + PUSHBUTTON "Add",IDC_ADDACTION,97,201,60,14 + LTEXT "Description:",IDC_SCRIPTTYPE_DESC,5,280,62,13 + EDITTEXT IDC_DESCRIPTION,70,280,223,52,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL + COMBOBOX IDC_SCRIPT_EXTRA,70,258,223,111,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + LTEXT "Extra value of action:",IDC_SCRIPT_EXDESC,5,257,65,17 + COMBOBOX IDC_SCRIPT_TEMPLATE,70,64,159,196,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Template:",IDC_SCRIPT_TEMPLATE_DSC,5,64,65,12 + PUSHBUTTON "Copy",IDC_SCRIPT_COPY,165,106,59,14 + CONTROL "Insert Mode",IDC_SCRIPT_CK_INSERT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,201,61,15 + PUSHBUTTON "Copy",IDC_COPYACTION,165,201,60,14 END IDD_AITRIGGERTYPESENABLE DIALOG 0, 0, 303, 204 @@ -1847,15 +1854,17 @@ BEGIN RIGHTMARGIN, 302 VERTGUIDE, 70 VERTGUIDE, 165 - BOTTOMMARGIN, 230 - HORZGUIDE, 50 - HORZGUIDE, 70 - HORZGUIDE, 89 - HORZGUIDE, 105 - HORZGUIDE, 172 - HORZGUIDE, 199 - HORZGUIDE, 215 - HORZGUIDE, 230 + VERTGUIDE, 293 + BOTTOMMARGIN, 280 + HORZGUIDE, 64 + HORZGUIDE, 84 + HORZGUIDE, 106 + HORZGUIDE, 123 + HORZGUIDE, 139 + HORZGUIDE, 201 + HORZGUIDE, 240 + HORZGUIDE, 257 + HORZGUIDE, 280 END IDD_TERRAINBAR, DIALOG diff --git a/MissionEditor/ScriptTypes.cpp b/MissionEditor/ScriptTypes.cpp index 1eaa059..d0ba7fa 100644 --- a/MissionEditor/ScriptTypes.cpp +++ b/MissionEditor/ScriptTypes.cpp @@ -29,7 +29,7 @@ #include "variables.h" #include "functions.h" #include "inlines.h" - +#include #ifdef _DEBUG #define new DEBUG_NEW @@ -37,220 +37,57 @@ static char THIS_FILE[] = __FILE__; #endif - -#define TMISSION_COUNT 59 -#define TPROPERTY_COUNT 4 -#define UNLOAD_COUNT 4 - - -// The various missions that a team can have. -enum TeamMissionType { - TMISSION_ATTACK, - TMISSION_ATT_WAYPT, - TMISSION_BERZERK, - TMISSION_MOVE, - TMISSION_MOVECELL, - TMISSION_GUARD, - TMISSION_LOOP, - TMISSION_WIN, - TMISSION_UNLOAD, - TMISSION_DEPLOY, - TMISSION_HOUND_DOG, - TMISSION_DO, - TMISSION_SET_GLOBAL, - TMISSION_IDLE_ANIM, - TMISSION_LOAD, - TMISSION_SPY, - TMISSION_PATROL, - TMISSION_SCRIPT, - TMISSION_TEAMCHANGE, - TMISSION_PANIC, - TMISSION_CHANGE_HOUSE, - TMISSION_SCATTER, - TMISSION_GOTO_SHROUD, - TMISSION_LOSE, - TMISSION_PLAY_SPEECH, - TMISSION_PLAY_SOUND, - TMISSION_PLAY_MOVIE, - TMISSION_PLAY_MUSIC, - TMISSION_REDUCE_TIBERIUM, - TMISSION_BEGIN_PRODUCTION, - TMISSION_FIRE_SALE, - TMISSION_SELF_DESTRUCT, - TMISSION_ION_STORM_START, - TMISSION_ION_STORM_END, - TMISSION_CENTER_VIEWPOINT, - TMISSION_RESHROUD, - TMISSION_REVEAL, - TMISSION_DESTROY_MEMBERS, - TMISSION_CLEAR_GLOBAL, - TMISSION_SET_LOCAL, - TMISSION_CLEAR_LOCAL, - TMISSION_UNPANIC, - TMISSION_FORCE_FACING, - TMISSION_FULLY_LOADED, - TMISSION_UNLOAD_TRUCK, - TMISSION_LOAD_TRUCK, - TMISSION_ATTACK_BUILDING_WITH_PROPERTY, - TMISSION_MOVETO_BUILDING_WITH_PROPERTY, - TMISSION_SCOUT, - TMISSION_SUCCESS, - TMISSION_FLASH, - TMISSION_PLAY_ANIM, - TMISSION_TALK_BUBBLE, - TMISSION_GATHER_FAR, - TMISSION_GATHER_NEAR, - TMISSION_ACTIVATE_CURTAIN, - TMISSION_CHRONO_ATTACK_BUILDING_WITH_PROPERTY, - TMISSION_CHRONO_ATTACK, - TMISSION_MOVETO_OWN_BUILDING_WITH_PROPERTY, -}; - -char const* TMissions[TMISSION_COUNT] = { - "Attack...", - "Attack Waypoint...", - "Go Berzerk", - "Move to waypoint...", - "Move to Cell...", - "Guard area (timer ticks)...", - "Jump to line #...", - "Player wins", - "Unload...", - "Deploy", - "Follow friendlies", - "Do this...", - "Set global...", - "Idle Anim...", - "Load onto Transport", - "Spy on bldg @ waypt...", - "Patrol to waypoint...", - "Change script...", - "Change team...", - "Panic", - "Change house...", - "Scatter", - "Goto nearby shroud", - "Player loses", - "Play speech...", - "Play sound...", - "Play movie...", - "Play music...", - "Reduce tiberium", - "Begin production", - "Fire sale", - "Self destruct", - "Ion storm start in...", - "Ion storn end", - "Center view on team (speed)...", - "Reshroud map", - "Reveal map", - "Delete team members", - "Clear global...", - "Set local...", - "Clear local...", - "Unpanic", - "Force facing...", - "Wait till fully loaded", - "Truck unload", - "Truck load", - "Attack enemy building", - "Moveto enemy building", - "Scout", - "Success", - "Flash", - "Play Anim", - "Talk Bubble", - "Gather at Enemy", - "Gather at Base", - "Iron Curtain Me", - "Chrono Prep for ABwP", - "Chrono Prep for AQ", - "Move to own building", -}; - -char const* TMissionsHelp[TMISSION_COUNT] = { - "Attack some general target", - "Attack anything nearby the specified waypoint", - "Cyborg members of the team will go berzerk.", - "Orders the team to move to a waypoint on the map", - "Orders the team to move to a specific cell on the map", - "Guard an area for a specified amount of time", - "Move to a new line number in the script. Used for loops.", - "Duh", - "Unloads all loaded units. The command parameter specifies which units should stay a part of the team, and which should be severed from the team.", - "Causes all deployable units in the team to deploy", - "Causes the team to follow the nearest friendly unit", - "Give all team members the specified mission", - "Sets a global variable", - "Causes team members to enter their idle animation", - "Causes all units to load into transports, if able", - "**OBSOLETE**", - "Move to a waypoint while scanning for enemies", - "Causes the team to start using a new script", - "Causes the team to switch team types", - "Causes all units in the team to panic", - "All units in the team switch houses", - "Tells all units to scatter", - "Causes units to flee to a shrouded cell", - "Causes the player to lose", - "Plays the specified voice file", - "Plays the specified sound file", - "Plays the specified movie file", - "Plays the specified theme", - "Reduces the amount of tiberium around team members", - "Signals the owning house to begin production", - "Causes an AI house to sell all of its buildings and do a Braveheart", - "Causes all team members to self destruct", - "Causes an ion storm to begin at the specified time", - "Causes an ion storm to end", - "Center view on team (speed)...", - "Reshrouds the map", - "Reveals the map", - "Delete all members from the team", - "Clears the specified global variable", - "Sets the specified local variable", - "Clears the specified local variable", - "Causes all team members to stop panicking", - "Forces team members to face a certain direction", - "Waits until all transports are full", - "Causes all trucks to unload their crates (ie, change imagery)", - "Causes all trucks to load crates (ie, change imagery)", - "Attack a specific type of building with the specified property", - "Move to a specific type of building with the specified property", - "The team will scout the bases of the players that have not been scouted", - "Record a team as having successfully accomplished its mission. Used for AI trigger weighting. Put this at the end of every AITrigger script.", - "Flashes a team for a period of team.", - "Plays an anim over every unit in the team.", - "Displays talk bubble over first unit in the team.", - "Uses AISafeDistance to find a spot close to enemy's base to gather close.", - "Gathers outside own base perimeter.", - "Calls (and waits if nearly ready) for House to deliver Iron Curtain to Team.", - "Teleports team to Building With Property, but needs similar attack order as next mission.", - "Teleports team to Attack Quarry, but needs similar attack order as next mission.", - "A BwP move that will only search through buildings owned by this house.", -}; - - -char const* TargetProperties[TPROPERTY_COUNT] = { - "Least Threat", - "Greatest Threat", - "Nearest", - "Farthest", -}; - -char const* UnloadTypeNames[UNLOAD_COUNT] = { - "Keep Transports, Keep Units", - "Keep Transports, Lose Units", - "Lose Transports, Keep Units", - "Lose Transports, Lose Units", -}; - - ///////////////////////////////////////////////////////////////////////////// // Eigenschaftenseite CScriptTypes IMPLEMENT_DYNCREATE(CScriptTypes, CDialog) +int scriptTypeIndexToComboBoxIndex(CComboBox& comboBox, int scriptTypeIndex) +{ + auto const totalCount = comboBox.GetCount(); + for (auto idx = 0; idx < totalCount; ++idx) { + const int MData = comboBox.GetItemData(idx); + if (MData == scriptTypeIndex) { + return idx; + } + } + + return 0; +} +ExtraParameterType getExtraParamType(ParameterType paramType) +{ + switch (paramType) { + default: + return ExtraParameterType::None; + case PRM_BuildingType: + return ExtraParameterType::ScanType; + } +} + +std::vector ScriptTemplate::parse(const CString& content) +{ + auto const init = INIHelper::Split(content); + auto const count = std::min(atoi(init[0]), init.size() / 2); + return parse(init, count, 1); +} + +std::vector ScriptTemplate::parse(const std::vector& content, size_t count, size_t offset) +{ + std::vector ret; + ret.reserve(count); + for (auto i = 0; i < count; i++) { + auto const& action = content[static_cast(2 * i) + offset]; + auto param = content[static_cast(2 * i) + 1 + offset]; + if (param == "EMPTY") { + param = "0"; + } + ret.emplace_back(action + + ',' + + param); + } + return ret; +} + CScriptTypes::CScriptTypes() : CDialog(CScriptTypes::IDD) { //{{AFX_DATA_INIT(CScriptTypes) @@ -265,16 +102,21 @@ CScriptTypes::~CScriptTypes() void CScriptTypes::UpdateStrings() { TranslateDlgItem(*this, IDC_ADD, "ScriptTypesAddScript"); + TranslateDlgItem(*this, IDC_SCRIPT_COPY, "ScriptTypesCopyScript"); TranslateDlgItem(*this, IDC_DELETE, "ScriptTypesDelScript"); TranslateDlgItem(*this, IDC_SCTIPTTYPE_INRO, "ScriptTypesDesc"); + TranslateDlgItem(*this, IDC_SCRIPT_TEMPLATE_DSC, "ScriptTypesSelectedTemplate"); TranslateDlgItem(*this, IDC_SCRIPTTYPE_TYPE, "ScriptTypesSelectedScript"); TranslateDlgItem(*this, IDC_SCRIPTTYPE_NAME, "ScriptTypesName"); TranslateDlgItem(*this, IDC_SCRIPTTYPE_ACTIONS, "ScriptTypesActions"); TranslateDlgItem(*this, IDC_SCRIPTTYPE_ACTIONTYPE, "ScriptTypesActionType"); TranslateDlgItem(*this, IDC_PDESC, "ScriptTypesActionParam"); + TranslateDlgItem(*this, IDC_SCRIPT_EXDESC, "ScriptTypesParamExtDesc"); TranslateDlgItem(*this, IDC_SCRIPTTYPE_DESC, "ScriptTypesActionDesc"); TranslateDlgItem(*this, IDC_ADDACTION, "ScriptTypesAddAction"); + TranslateDlgItem(*this, IDC_COPYACTION, "ScriptTypesCopyAction"); + TranslateDlgItem(*this, IDC_SCRIPT_CK_INSERT, "ScriptTypesInsertMode"); TranslateDlgItem(*this, IDC_DELETEACTION, "ScriptTypesDelAction"); TranslateWindowCaption(*this, "ScriptsCaption"); @@ -284,56 +126,115 @@ void CScriptTypes::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CScriptTypes) - DDX_Control(pDX, IDC_DESCRIPTION, m_DescriptionEx); - DDX_Control(pDX, IDC_PDESC, m_Desc); - DDX_Control(pDX, IDC_TYPE, m_Type); + DDX_Control(pDX, IDC_DESCRIPTION, m_Description); + DDX_Control(pDX, IDC_TYPE, m_ActionType); DDX_Control(pDX, IDC_SCRIPTTYPE, m_ScriptType); + DDX_Control(pDX, IDC_SCRIPT_TEMPLATE, m_Template); DDX_Control(pDX, IDC_PARAM, m_Param); - DDX_Control(pDX, IDC_ACTION, m_Action); + DDX_Control(pDX, IDC_SCRIPT_EXTRA, m_ParamExt); + DDX_Control(pDX, IDC_ACTION, m_Actions); DDX_Text(pDX, IDC_NAME, m_Name); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CScriptTypes, CDialog) - //{{AFX_MSG_MAP(CScriptTypes) - ON_CBN_KILLFOCUS(IDC_SCRIPTTYPE, OnEditchangeScripttype) ON_CBN_SELCHANGE(IDC_SCRIPTTYPE, OnSelchangeScripttype) - ON_LBN_SELCHANGE(IDC_ACTION, OnSelchangeAction) + ON_LBN_SELCHANGE(IDC_ACTION, OnSelchangeActionList) ON_EN_KILLFOCUS(IDC_NAME, OnChangeName) - ON_CBN_KILLFOCUS(IDC_TYPE, OnEditchangeType) - ON_CBN_SELCHANGE(IDC_TYPE, OnSelchangeType) + ON_CBN_KILLFOCUS(IDC_TYPE, OnEditchangeActionType) + ON_CBN_SELCHANGE(IDC_TYPE, OnSelchangeActionType) ON_CBN_KILLFOCUS(IDC_PARAM, OnEditchangeParam) ON_CBN_SELCHANGE(IDC_PARAM, OnSelchangeParam) ON_BN_CLICKED(IDC_ADDACTION, OnAddaction) ON_BN_CLICKED(IDC_DELETEACTION, OnDeleteaction) ON_BN_CLICKED(IDC_ADD, OnAdd) ON_BN_CLICKED(IDC_DELETE, OnDelete) - //}}AFX_MSG_MAP + ON_CBN_SELCHANGE(IDC_SCRIPT_EXTRA, &CScriptTypes::OnCbnSelchangeScriptExtra) + ON_BN_CLICKED(IDC_SCRIPT_COPY, &CScriptTypes::OnBnClickedScriptCopy) + ON_BN_CLICKED(IDC_COPYACTION, &CScriptTypes::OnBnClickedCopyaction) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Behandlungsroutinen für Nachrichten CScriptTypes +void CScriptTypes::reloadTemplates() +{ + auto const& sec = g_data["ScriptTemplates"]; + auto const count = sec.GetInteger("Counts"); + auto const defName = sec.GetStringOr("DefaultName", "Default"); + m_scriptTemplates.push_back( + ScriptTemplate{ defName, "New Script", "1,0,0" } + ); + + auto offset = 0; + + auto parseTemplate = [this, &sec](const CString& id) -> bool { + auto const& record = sec.GetString(id); + if (record.IsEmpty()) { + return false; + } + auto&& parts = INIHelper::Split(record, ','); + if (parts.size() <= 3) { + return false; + } + auto const contentCount = atoi(parts[2]); + if (contentCount * 2 != parts.size() - 3) { + errstream << "record count mismatches content size" << std::endl; + return false; + } + std::vector remaining(parts.begin() + 3, parts.end()); + this->m_scriptTemplates.emplace_back(ScriptTemplate{ parts[0], parts[1], remaining }); + return true; + }; + + // try from 0. If no 0, try from 1 + if (!parseTemplate("0")) { + errstream << "[script template] could not parse content from index 0" << std::endl; + offset = 1; + } + + CString idStr; + for (auto idx = 0; idx < count; ++idx) { + idStr.Format("%d", idx + offset); + if (!parseTemplate(idStr)) { + errstream << "[script template] could not parse content from index " << idStr << std::endl; + } + } + + // load template into list + ASSERT(m_scriptTemplates.size() > 0); + for (auto const& templ : m_scriptTemplates) { + m_Template.AddString(templ.Desc()); + } + m_Template.SetCurSel(0); +} + void CScriptTypes::UpdateDialog() { + if (!m_scriptTemplates.size()) { + reloadTemplates(); + } + CIniFile& ini = Map->GetIniFile(); - int sel = m_ScriptType.GetCurSel(); + int lastScriptSel = m_ScriptType.GetCurSel(); while (m_ScriptType.DeleteString(0) != CB_ERR); // MW 07/24/01: clear dialog - m_DescriptionEx.SetWindowText(""); + m_Description.SetWindowText(""); m_Name = ""; m_Param.SetWindowText(""); - m_Action.SetWindowText(""); - m_Type.SetCurSel(-1); + m_ParamExt.SetWindowText(""); + m_Actions.SetWindowText(""); + m_ActionType.SetCurSel(-1); UpdateData(FALSE); + // load script type into list for (auto const& [seq, type] : ini["ScriptTypes"]) { CString desc = type; desc += " ("; @@ -343,28 +244,25 @@ void CScriptTypes::UpdateDialog() } m_ScriptType.SetCurSel(0); - if (sel >= 0) { - m_ScriptType.SetCurSel(sel); + if (lastScriptSel >= 0) { + m_ScriptType.SetCurSel(lastScriptSel); } OnSelchangeScripttype(); -} - -void CScriptTypes::OnEditchangeScripttype() -{ - } void CScriptTypes::OnSelchangeScripttype() { CIniFile& ini = Map->GetIniFile(); - int sel = m_Action.GetCurSel(); - while (m_Action.DeleteString(0) != CB_ERR); + int sel = m_Actions.GetCurSel(); + while (m_Actions.DeleteString(0) != CB_ERR); CString Scripttype; - if (m_ScriptType.GetCurSel() < 0) return; + if (m_ScriptType.GetCurSel() < 0) { + return; + } m_ScriptType.GetLBText(m_ScriptType.GetCurSel(), Scripttype); TruncSpace(Scripttype); @@ -375,41 +273,48 @@ void CScriptTypes::OnSelchangeScripttype() for (i = 0; i < count; i++) { char c[50]; itoa(i, c, 10); - m_Action.AddString(c); + m_Actions.AddString(c); } - m_Action.SetCurSel(0); - if (sel >= 0) m_Action.SetCurSel(sel); - OnSelchangeAction(); + m_Actions.SetCurSel(0); + if (sel >= 0) { + m_Actions.SetCurSel(sel); + } + OnSelchangeActionList(); UpdateData(FALSE); } -void CScriptTypes::OnSelchangeAction() +void CScriptTypes::OnSelchangeActionList() { - CIniFile& ini = Map->GetIniFile(); + auto& doc = Map->GetIniFile(); + CString scriptId, buffer, paramNumStr; + int listIndex, actionIndex, selectIndex; - CString Scripttype; - char action[50]; - if (m_ScriptType.GetCurSel() < 0) { - return; + auto const scriptIndex = this->m_ScriptType.GetCurSel(); + listIndex = this->m_Actions.GetCurSel(); + if (scriptIndex >= 0 && listIndex >= 0) { + this->m_ScriptType.GetLBText(scriptIndex, scriptId); + TruncSpace(scriptId); + buffer.Format("%d", listIndex); + buffer = doc.GetStringOr(scriptId, buffer, "0,0"); + actionIndex = buffer.Find(','); + if (actionIndex < 0) { + buffer += ",0"; + actionIndex = buffer.GetLength() - 2; + } + paramNumStr = buffer.Mid(actionIndex + 1); + TruncSpace(paramNumStr); + + actionIndex = atoi(buffer.Mid(0, actionIndex)); + + selectIndex = scriptTypeIndexToComboBoxIndex(this->m_ActionType, actionIndex); + + this->m_ActionType.SetCurSel(selectIndex); + this->UpdateParams(actionIndex, ¶mNumStr); + this->m_Param.SetWindowText(paramNumStr); } - if (m_Action.GetCurSel() < 0) { - return; - } - m_ScriptType.GetLBText(m_ScriptType.GetCurSel(), Scripttype); - TruncSpace(Scripttype); - - itoa(m_Action.GetCurSel(), action, 10); - //m_Type.SetWindowText(GetParam(ini.sections[(LPCTSTR)Scripttype].values[action],0)); - m_Type.SetCurSel(atoi(GetParam(ini.GetString(Scripttype, action), 0))); - - OnSelchangeType(); - - m_Param.SetWindowText(GetParam(ini.GetString(Scripttype, action), 1)); - - } void CScriptTypes::OnChangeName() @@ -436,149 +341,111 @@ void CScriptTypes::OnChangeName() n->SetSel(pos); } -void CScriptTypes::OnEditchangeType() +void CScriptTypes::OnEditchangeActionType() { - CIniFile& ini = Map->GetIniFile(); + CIniFile& doc = Map->GetIniFile(); - while (m_Param.DeleteString(0) != CB_ERR); - CString Scripttype; - char action[50]; - if (m_Action.GetCurSel() < 0) return; - if (m_ScriptType.GetCurSel() < 0) return; - m_ScriptType.GetLBText(m_ScriptType.GetCurSel(), Scripttype); - TruncSpace(Scripttype); - - //CString type; - //m_Type.GetWindowText(type); - //TruncSpace(type); - //MessageBox("beep"); - int type = m_Type.GetCurSel(); - - int i; - char tmp[50]; - - switch (type) { - case 0: - ListTargets(m_Param); - m_Desc.SetWindowText("Target:"); - break; - case 39: - case 40: - ListGlobals(m_Param); - break; - - case 11: - ListBehaviours(m_Param); - break; - case 1: - case 3: - case 16: - ListWaypoints(m_Param); - m_Desc.SetWindowText("Waypoint:"); - break; - case 4: - m_Desc.SetWindowText("Cell:"); - break; - case 5: - m_Desc.SetWindowText("Time units to guard:"); - break; - case 6: - m_Desc.SetWindowText("Script action #:"); - while (m_Param.DeleteString(0) != CB_ERR); - for (i = 1; i <= ini[Scripttype].Size() - 1; i++) { - m_Param.AddString(itoa(i, tmp, 10)); + const int scriptIndex = this->m_ScriptType.GetCurSel(); + const int listIndex = this->m_Actions.GetCurSel(); + if (scriptIndex >= 0 && listIndex >= 0) { + CString scriptId; + CString buffer; + //_showCStr(scriptId); + //_showCStr(buffer); + this->m_ScriptType.GetLBText(scriptIndex, scriptId); + TruncSpace(scriptId); + //_showCStr(scriptId); + buffer.Format("%d", listIndex); + // _showCStr(buffer); + buffer = doc.GetStringOr(scriptId, buffer, "0,0"); + // _showCStr(buffer); + int commaPos = buffer.Find(','); + if (commaPos < 0) { + buffer = "0"; + } else { + buffer = buffer.Mid(commaPos + 1); } - break; - case 8: - m_Desc.SetWindowText("Split groups:"); - while (m_Param.DeleteString(0) != CB_ERR); - int i; - for (i = 0; i < UNLOAD_COUNT; i++) { - CString p; - char c[50]; - itoa(i, c, 10); - p = c; - p += " - "; - p += UnloadTypeNames[i]; - m_Param.AddString(p); + int actionIndex = this->m_ActionType.GetCurSel(); + if (actionIndex >= 0) { + const int actionData = this->m_ActionType.GetItemData(actionIndex); + this->UpdateParams(actionData); + actionIndex = this->m_Param.FindString(0, buffer); + if (actionIndex != CB_ERR) { + this->m_Param.SetCurSel(actionIndex); + } + CString listStr; + CString tmp; + tmp.Format("%d,%s", actionData, buffer.operator LPCSTR()); + listStr.Format("%d", listIndex); + doc.SetString(scriptId, listStr, tmp); + // _showCStr(tmp); } - break; - case 9: - case 14: - case 37: - m_Desc.SetWindowText("Use 0:"); - break; - case 12: - m_Desc.SetWindowText("Global:"); - break; - case 20: - ListHouses(m_Param, TRUE); - m_Desc.SetWindowText("House:"); - break; - case 46: - case 47: - { - m_Desc.SetWindowText("Type to move/attack:"); - auto const& bldTypeSec = rules["BuildingTypes"]; - for (i = 0; i < bldTypeSec.Size(); i++) { - char c[50]; - itoa(i, c, 10); - CString s = c; - - s += " "; - //s+=rules.sections[*rules.sections["BuildingTypes"].GetValue(i)].values["Name"]; - s += Map->GetUnitName(bldTypeSec.Nth(i).second); - m_Param.AddString(s); - } - break; } - - default: - m_Desc.SetWindowText("Parameter of action:"); - } - - itoa(m_Action.GetCurSel(), action, 10); - - char types[50]; - itoa(type, types, 10); - ini.SetString(Scripttype, action, SetParam(ini.GetString(Scripttype, action), 0, types)); } -void CScriptTypes::OnSelchangeType() +void CScriptTypes::OnSelchangeActionType() { - CString str; - if (m_Type.GetCurSel() > -1) { - //m_Type.GetLBText(m_Type.GetCurSel(), str); - //m_Type.SetWindowText(str); + int curActionIdx = this->m_ActionType.GetCurSel(); + if (curActionIdx >= 0) { + int curActionData = this->m_ActionType.GetItemData(curActionIdx); + auto const& dict = m_actionDefinitions; + auto itr = dict.find(curActionData); + if (itr != dict.end()) { + this->OnEditchangeActionType(); - m_DescriptionEx.SetWindowText(TMissionsHelp[m_Type.GetCurSel()]); + this->m_Description.SetWindowTextA(itr->second.Description); + this->m_Description.EnableWindow(itr->second.Editable); + this->m_Param.EnableWindow(itr->second.Editable); + } } +} - - - OnEditchangeType(); +int CScriptTypes::getExtraValue() +{ + auto const curActionSel = this->m_ActionType.GetCurSel(); + const int actionData = this->m_ActionType.GetItemData(curActionSel); + auto const paramType = getParameterType(actionData); + auto const extParamType = getExtraParamType(paramType); + CString curExtParamContent; + m_ParamExt.GetWindowText(curExtParamContent); + //errstream << " curExtParamContent = " << curExtParamContent; + return atoi(curExtParamContent); } void CScriptTypes::OnEditchangeParam() { - CIniFile& ini = Map->GetIniFile(); + auto& doc = Map->GetIniFile(); + CString scriptId, buffer, listStr, paramStr, tmp; + int scriptIndex, listIndex, actionIndex; - CString Scripttype; - char action[50]; - if (m_Action.GetCurSel() < 0) return; - if (m_ScriptType.GetCurSel() < 0) return; - m_ScriptType.GetLBText(m_ScriptType.GetCurSel(), Scripttype); - TruncSpace(Scripttype); - - CString param; - m_Param.GetWindowText(param); - TruncSpace(param); - - param = TranslateHouse(param); - - itoa(m_Action.GetCurSel(), action, 10); - ini.SetString(Scripttype, action, SetParam(ini.GetString(Scripttype, action), 1, param)); + scriptIndex = this->m_ScriptType.GetCurSel(); + listIndex = this->m_Actions.GetCurSel(); + if (scriptIndex >= 0 && listIndex >= 0) { + this->m_ScriptType.GetLBText(scriptIndex, scriptId); + TruncSpace(scriptId); + buffer.Format("%d", listIndex); + buffer = doc.GetStringOr(scriptId, buffer, "0,0"); + actionIndex = buffer.Find(','); + if (actionIndex == CB_ERR) { + actionIndex = buffer.GetLength(); + } + buffer = buffer.Mid(0, actionIndex); + this->m_Param.GetWindowText(paramStr); + //paramStr = this->m_Param.GetText(); + TruncSpace(paramStr); + // + //LogDebug(" actionData = %d, paramType = %d, extParamType = %d", actionData, paramType, extParamType); + if (auto const extValue = getExtraValue()) { + auto const paramInt = MAKEWPARAM(atoi(paramStr), extValue); + paramStr.Format("%d", paramInt); + } + // + tmp.Format("%s,%s", buffer, paramStr); + listStr.Format("%d", listIndex); + doc.SetString(scriptId, listStr, tmp); + //_showCStr(tmp); + } } void CScriptTypes::OnSelchangeParam() @@ -587,22 +454,68 @@ void CScriptTypes::OnSelchangeParam() OnEditchangeParam(); } +CString CScriptTypes::getCurrentTypeID() +{ + CString scriptTypeId; + this->m_ScriptType.GetLBText(this->m_ScriptType.GetCurSel(), scriptTypeId); + TruncSpace(scriptTypeId); + return scriptTypeId; +} + + +void CScriptTypes::insertAction(int curSel, const CString& scriptTypeId, const CString& value) +{ + CString idxStr; + std::vector oldItems; + auto& doc = Map->GetIniFile(); + //record old values from next line + for (auto idx = curSel + 1; idx < this->m_Actions.GetCount(); ++idx) { + idxStr.Format("%d", idx); + oldItems.push_back(doc.GetStringOr(scriptTypeId, idxStr, "0,0")); + } + idxStr.Format("%d", curSel + 1); + doc.SetString(scriptTypeId, idxStr, value); + auto newIdx = curSel + 2; + for (auto& val : oldItems) { + idxStr.Format("%d", newIdx); + doc.SetString(scriptTypeId, idxStr, val); + newIdx++; + } + this->m_Actions.InsertString(this->m_Actions.GetCount(), idxStr); + this->m_Actions.SetCurSel(curSel + 1); + this->OnSelchangeActionList(); +} + void CScriptTypes::OnAddaction() { - CIniFile& ini = Map->GetIniFile(); + HWND ScriptWnd = this->GetSafeHwnd(); + HWND CheckBox = ::GetDlgItem(ScriptWnd, IDC_SCRIPT_CK_INSERT); + bool isInsert = ::SendMessageA(CheckBox, BM_GETCHECK, NULL, NULL) == BST_CHECKED; + auto const curSel = this->m_Actions.GetCurSel(); + if (curSel < 0) { + isInsert = false; + } + //get selected value : + auto const insertIdx = (isInsert ? curSel : this->m_Actions.GetCount()) - 1; + insertAction(insertIdx, getCurrentTypeID(), "0,0"); +} - CString Scripttype; - if (m_ScriptType.GetCurSel() < 0) return; - m_ScriptType.GetLBText(m_ScriptType.GetCurSel(), Scripttype); - TruncSpace(Scripttype); - - - char action[20]; - int count = ini[Scripttype].Size() - 1; - itoa(count, action, 10); - ini.SetString(Scripttype, action, "0,0"); - - UpdateDialog(); +void CScriptTypes::OnBnClickedCopyaction() +{ + HWND ScriptWnd = this->GetSafeHwnd(); + HWND CheckBox = ::GetDlgItem(ScriptWnd, IDC_SCRIPT_CK_INSERT); + bool isInsert = ::SendMessageA(CheckBox, BM_GETCHECK, NULL, NULL) == BST_CHECKED; + auto const curSel = this->m_Actions.GetCurSel(); + if (curSel < 0) { + return; + } + //get selected value : + CString idxStr; + auto& doc = Map->GetIniFile(); + CString scriptTypeId = getCurrentTypeID(); + idxStr.Format("%d", curSel); + auto value = doc.GetStringOr(scriptTypeId, idxStr, "0,0"); + insertAction(isInsert ? curSel : this->m_Actions.GetCount() - 1, scriptTypeId, value); } void CScriptTypes::OnDeleteaction() @@ -610,15 +523,19 @@ void CScriptTypes::OnDeleteaction() CIniFile& ini = Map->GetIniFile(); CString Scripttype; - if (m_Action.GetCurSel() < 0) return; - if (m_ScriptType.GetCurSel() < 0) return; + if (m_Actions.GetCurSel() < 0) { + return; + } + if (m_ScriptType.GetCurSel() < 0) { + return; + } m_ScriptType.GetLBText(m_ScriptType.GetCurSel(), Scripttype); TruncSpace(Scripttype); // okay, action is now the deleted one... int i; - for (i = m_Action.GetCurSel(); i < m_Action.GetCount() - 1; i++) { + for (i = m_Actions.GetCurSel(); i < m_Actions.GetCount() - 1; i++) { // okay, now move every action one number up. char current[50]; char next[50]; @@ -629,7 +546,7 @@ void CScriptTypes::OnDeleteaction() ini.SetString(Scripttype, current, ini.GetString(Scripttype, next)); } char last[50]; - itoa(m_Action.GetCount() - 1, last, 10); + itoa(m_Actions.GetCount() - 1, last, 10); ini.RemoveValueByKey(Scripttype, last); UpdateDialog(); @@ -637,20 +554,25 @@ void CScriptTypes::OnDeleteaction() CString GetFree(const char* section); -void CScriptTypes::OnAdd() +void CScriptTypes::insertScriptType(const CString& name, const std::vector& items) { - CIniFile& ini = Map->GetIniFile(); - CString ID = GetFreeID(); - CString p = GetFree("ScriptTypes"); + + CIniFile& ini = Map->GetIniFile(); + auto& sec = ini.AddSection(ID); ini.SetString("ScriptTypes", p, ID); - ini.SetString(ID, "Name", "New script"); + sec.SetString("Name", name); - - - int i; - for (i = 0; i < m_ScriptType.GetCount(); i++) { + // template value copy + auto idx = 0; + CString idStr; + for (auto const& line : items) { + idStr.Format("%d", idx++); + sec.SetString(idStr, line); + } + ((CFinalSunDlg*)theApp.m_pMainWnd)->UpdateDialogs(TRUE); + for (auto i = 0; i < m_ScriptType.GetCount(); i++) { CString data; m_ScriptType.GetLBText(i, data); TruncSpace(data); @@ -661,9 +583,15 @@ void CScriptTypes::OnAdd() break; } } +} - ((CFinalSunDlg*)theApp.m_pMainWnd)->UpdateDialogs(TRUE); - //UpdateDialog(); +void CScriptTypes::OnAdd() +{ + errstream << "Add Script" << std::endl; + int curTemplateIndex = m_Template.GetCurSel(); + auto const& curTemplate = m_scriptTemplates[curTemplateIndex]; + errstream << "Now using Script Template: " << curTemplate.Name() << std::endl; + insertScriptType(curTemplate.Name(), curTemplate.Content()); } void CScriptTypes::OnDelete() @@ -671,7 +599,9 @@ void CScriptTypes::OnDelete() CIniFile& ini = Map->GetIniFile(); CString Scripttype; - if (m_ScriptType.GetCurSel() < 0) return; + if (m_ScriptType.GetCurSel() < 0) { + return; + } m_ScriptType.GetLBText(m_ScriptType.GetCurSel(), Scripttype); TruncSpace(Scripttype); @@ -693,39 +623,39 @@ void CScriptTypes::ListBehaviours(CComboBox& cb) { while (cb.DeleteString(0) != CB_ERR); - cb.AddString("0 - Sleep"); - cb.AddString("1 - Attack nearest enemy"); - cb.AddString("2 - Move"); - cb.AddString("3 - QMove"); - cb.AddString("4 - Retreat home for R&R"); - cb.AddString("5 - Guard"); - cb.AddString("6 - Sticky (never recruit)"); - cb.AddString("7 - Enter object"); - cb.AddString("8 - Capture object"); - cb.AddString("9 - Move into & get eaten"); - cb.AddString("10 - Harvest"); - cb.AddString("11 - Area Guard"); - cb.AddString("12 - Return (to refinery)"); - cb.AddString("13 - Stop"); - cb.AddString("14 - Ambush (wait until discovered)"); - cb.AddString("15 - Hunt"); - cb.AddString("16 - Unload"); - cb.AddString("17 - Sabotage (move in & destroy)"); - cb.AddString("18 - Construction"); - cb.AddString("19 - Deconstruction"); - cb.AddString("20 - Repair"); - cb.AddString("21 - Rescue"); - cb.AddString("22 - Missile"); - cb.AddString("23 - Harmless"); - cb.AddString("24 - Open"); - cb.AddString("25 - Patrol"); - cb.AddString("26 - Paradrop approach drop zone"); - cb.AddString("27 - Paradrop overlay drop zone"); - cb.AddString("28 - Wait"); - cb.AddString("29 - Attack move"); + cb.AddString(TranslateStringACP("0 - Sleep")); + cb.AddString(TranslateStringACP("1 - Attack nearest enemy")); + cb.AddString(TranslateStringACP("2 - Move")); + cb.AddString(TranslateStringACP("3 - QMove")); + cb.AddString(TranslateStringACP("4 - Retreat home for R&R")); + cb.AddString(TranslateStringACP("5 - Guard")); + cb.AddString(TranslateStringACP("6 - Sticky (never recruit)")); + cb.AddString(TranslateStringACP("7 - Enter object")); + cb.AddString(TranslateStringACP("8 - Capture object")); + cb.AddString(TranslateStringACP("9 - Move into & get eaten")); + cb.AddString(TranslateStringACP("10 - Harvest")); + cb.AddString(TranslateStringACP("11 - Area Guard")); + cb.AddString(TranslateStringACP("12 - Return (to refinery)")); + cb.AddString(TranslateStringACP("13 - Stop")); + cb.AddString(TranslateStringACP("14 - Ambush (wait until discovered)")); + cb.AddString(TranslateStringACP("15 - Hunt")); + cb.AddString(TranslateStringACP("16 - Unload")); + cb.AddString(TranslateStringACP("17 - Sabotage (move in & destroy)")); + cb.AddString(TranslateStringACP("18 - Construction")); + cb.AddString(TranslateStringACP("19 - Deconstruction")); + cb.AddString(TranslateStringACP("20 - Repair")); + cb.AddString(TranslateStringACP("21 - Rescue")); + cb.AddString(TranslateStringACP("22 - Missile")); + cb.AddString(TranslateStringACP("23 - Harmless")); + cb.AddString(TranslateStringACP("24 - Open")); + cb.AddString(TranslateStringACP("25 - Patrol")); + cb.AddString(TranslateStringACP("26 - Paradrop approach drop zone")); + cb.AddString(TranslateStringACP("27 - Paradrop overlay drop zone")); + cb.AddString(TranslateStringACP("28 - Wait")); + cb.AddString(TranslateStringACP("29 - Attack again")); if (yuri_mode) { - // cb.AddString("30 - Spyplane approach"); - // cb.AddString("31 - Spyplane retreat"); + cb.AddString(TranslateStringACP("30 - Spyplane approach")); + cb.AddString(TranslateStringACP("31 - Spyplane retreat")); } } @@ -735,27 +665,354 @@ BOOL CScriptTypes::OnInitDialog() UpdateStrings(); - while (m_Type.DeleteString(0) != CB_ERR); + while (m_ActionType.DeleteString(0) != CB_ERR); - int i; - for (i = 0; i < TMISSION_COUNT; i++) { - CString p; - char c[50]; - itoa(i, c, 10); - - //p=c; - - - //p+=" - "; - p += TMissions[i]; - - if (strlen(TMissions[i]) > 0) { - m_Type.AddString(p); + char* pParseBuffer[2]; + for (auto& [idStr, content] : g_data["ScriptParams"]) { + int id = atoi(idStr); + if (id < 0) { + continue; + } + auto const param1 = GetParam(content, 0); + // no valid value at all + if (param1.IsEmpty()) { + continue; + } + m_paramDefinitions[id].Label = param1; + auto const param2 = GetParam(content, 1); + if (!param2.IsEmpty()) { + m_paramDefinitions[id].Type = ParameterType(atoi(param2)); } } + for (auto& pair : g_data["ScriptsRA2"]) { + int id = atoi(pair.first); + //LogDebug(" id = %d", id); + if (id < 0) { + continue; + } + auto const& strings = INIHelper::Split(pair.second); + //LogDebug("pair.second = %s, count = %d", pair.second, strings.size()); + switch (strings.size()) { + case 5: + m_actionDefinitions[id].Description = strings[4]; + //LogDebug(" Description = %s", m_actionDefinitions[id].Description); + case 4: + m_actionDefinitions[id].Editable = INIHelper::StringToBool(strings[3], false); + case 3: + m_actionDefinitions[id].Hide = INIHelper::StringToBool(strings[2], false); + case 2: + m_actionDefinitions[id].ParamTypeIndex = atoi(strings[1]); + case 1: + m_actionDefinitions[id].Name = strings[0]; + case 0: + default: + continue; + } + } + + int counter = 0; + for (auto& ent : m_actionDefinitions) { + if (!ent.second.Hide) { + int data = m_ActionType.AddString(ent.second.Name); + m_ActionType.SetItemData(data, counter); + } + ++counter; + } return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX-Eigenschaftenseiten sollten FALSE zurückgeben } + +void CScriptTypes::OnCbnSelchangeScriptExtra() +{ + // TODO: Add your control notification handler code here +} + + +void CScriptTypes::OnBnClickedScriptCopy() +{ + auto& doc = Map->GetIniFile(); + + int nCurSel = this->m_ScriptType.GetCurSel(); + if (nCurSel < 0) { + return; + } + CString label; + this->m_ScriptType.GetLBText(nCurSel, label); + TruncSpace(label); + + auto const& sec = doc[label]; + auto const name = sec.GetString("Name") + " Clone"; + std::vector contents; + + // don't try infinitely + CString idxStr; + for (auto idx = 0; idx < std::numeric_limits::max(); ++idx) { + idxStr.Format("%d", idx); + auto const& item = sec.GetString(idxStr); + if (!item.IsEmpty()) { + contents.emplace_back(item); + continue; + } + break; + } + this->insertScriptType(name, contents); +} + +void CScriptTypes::updateExtraParamComboBox(ExtraParameterType type, int value) +{ + //LogDebug(" type = %d", type); + HWND text = ::GetDlgItem(this->m_hWnd, IDC_SCRIPT_EXDESC); + switch (type) { + default: + case ExtraParameterType::None: + ::EnableWindow(text, FALSE); + ComboBoxHelper::Clear(m_ParamExt); + m_ParamExt.EnableWindow(false); + m_ParamExt.SetWindowText(""); + break; + case ExtraParameterType::ScanType: + { + ::EnableWindow(text, TRUE); + ComboBoxHelper::Clear(m_ParamExt); + m_ParamExt.EnableWindow(true); + m_ParamExt.AddString(TranslateStringACP("0 - Least threat")); + m_ParamExt.AddString(TranslateStringACP("1 - Most threat")); + m_ParamExt.AddString(TranslateStringACP("2 - Least distant")); + m_ParamExt.AddString(TranslateStringACP("3 - Most distant")); + m_ParamExt.SetCurSel(value); + char buffer[0x20]; + _itoa_s(value, buffer, 10); + m_ParamExt.SetWindowTextA(buffer); + //LogDebug(" [%X] window enabled", m_ParamExt.GetHWND()); + } + break; + case ExtraParameterType::Counter: + { + ::EnableWindow(text, TRUE); + ComboBoxHelper::Clear(m_ParamExt); + m_ParamExt.EnableWindow(true); + char buffer[0x20]; + _itoa_s(value, buffer, 10); + m_ParamExt.SetWindowTextA(buffer); + break; + } + } +} + +const CScriptTypes::CScriptTypeAction& CScriptTypes::getActionData(int actionCbIndex) const +{ + return m_actionDefinitions.at(actionCbIndex); +} + +const CScriptTypes::CScriptTypeParam& CScriptTypes::getParamData(int paramIndex) const +{ + return m_paramDefinitions.at(paramIndex); +} +ParameterType CScriptTypes::getParameterType(int actionCbIndex) const +{ + return getParamData(getActionData(actionCbIndex).ParamTypeIndex).Type; +} + +void CScriptTypes::updateExtraValue(ParameterType paramType, CString* paramNumStr) +{ + int extraValue = 0; + if (paramType == PRM_BuildingType) { + if (paramNumStr) { + DWORD rawNum = atoi(*paramNumStr); + paramNumStr->Format("%d", LOWORD(rawNum)); + extraValue = HIWORD(rawNum); + } + } + errstream << "paramType " << paramType << std::endl; + updateExtraParamComboBox(getExtraParamType(paramType), extraValue); +} + +static void ListSplitGroup(CComboBox& comboBox) +{ + while (comboBox.DeleteString(0) != -1); + + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("0 - Keep Transports, Keep Units")), 0); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("1 - Keep Transports, Lose Units")), 1); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("2 - Lose Transports, Keep Units")), 2); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("3 - Lose Transports, Lose Units")), 3); +} + +static void ListScriptLine(const CScriptTypes::ActionDefinitionMap& actionDef, CComboBox& comboBox, CComboBox& currentScript, CListBox& listBox) +{ + int itemNum = listBox.GetCount(); + // up to 50 + if (itemNum > 50) { + itemNum = 50; + } + + while (comboBox.DeleteString(0) != -1); + + auto const& doc = Map->GetIniFile(); + + CString buffer, scriptName, parambuf; + currentScript.GetLBText(currentScript.GetCurSel(), scriptName); + TruncSpace(scriptName); + + for (int i = 0; i < itemNum; ++i) { + buffer.Format("%d", i); + buffer = doc.GetStringOr(scriptName, buffer, "0,0"); + int actionIndex = buffer.Find(','); + if (actionIndex == CB_ERR) { + actionIndex = -1; + } else { + actionIndex = atoi(buffer.Mid(0, actionIndex)); + } + buffer.Format("%d - %s", i + 1, actionDef.at(actionIndex).Name); + int idx = comboBox.AddString(buffer); + comboBox.SetItemData(idx, i); + } +} + +static void ListTypeList(CComboBox& comboBox, const CString& listID) +{ + while (comboBox.DeleteString(0) != -1); + + auto const& doc = Map->GetIniFile(); + + for (auto const& [idxStr, id] : doc[listID]) { + CString text; + if (doc.TryGetSection(id)) { + int idx = atoi(idxStr); + text = doc.GetString(id, "Name"); + text.Format("%d - %s - %s", idx, id, text); + comboBox.SetItemData(comboBox.AddString(text), idx); + } + } +} + +static void listScriptTypes(CComboBox& comboBox) +{ + ListTypeList(comboBox, "ScriptTypes"); +} + +static void listTeamTypes(CComboBox& comboBox) +{ + ListTypeList(comboBox, "TeamTypes"); +} + +static void listFacing(CComboBox& comboBox) +{ + while (comboBox.DeleteString(0) != -1); + + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("0 - NE")), 0); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("1 - E")), 1); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("2 - SE")), 2); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("3 - S")), 3); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("4 - SW")), 4); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("5 - W")), 5); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("6 - NW")), 6); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("7 - N")), 7); +} + +static void listTalkBubble(CComboBox& comboBox) +{ + while (comboBox.DeleteString(0) != -1); + + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("0 - None")), 0); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("1 - Asterisk(*)")), 1); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("2 - Question mark(?)")), 2); + comboBox.SetItemData(comboBox.AddString(TranslateStringACP("3 - Exclamation mark(!)")), 3); +} + +void CScriptTypes::UpdateParams(int actionIndex, CString* paramNumStr) +{ + static int LastActionID = -1; + auto const& actionDefinition = getActionData(actionIndex); + auto const& paramDefinition = getParamData(actionDefinition.ParamTypeIndex); + auto const paramType = paramDefinition.Type; + auto const lastActionID = std::exchange(LastActionID, actionIndex); + + //LogDebug( + // " LastActionID = " + std::to_string(lastActionID) + + // " actionIndex = " + std::to_string(actionIndex) + + // " paramType = " + std::to_string(paramType) + //); + if (lastActionID == actionIndex) { + return; + } + updateExtraValue(paramType, paramNumStr); + switch (paramType) { + default: + case PRM_None: + while (this->m_Param.DeleteString(0) != -1); + break; + case 1: + ListTargets(this->m_Param); + break; + case 2: + ListWaypoints(this->m_Param); + break; + case 3: + ListScriptLine( + m_actionDefinitions, + this->m_Param, + this->m_ScriptType, + this->m_Actions + ); + break; + case 4: + ListSplitGroup(this->m_Param); + break; + case 5: + ListRulesGlobals(this->m_Param); + break; + case 6: + listScriptTypes(this->m_Param); + break; + case 7: + listTeamTypes(this->m_Param); + break; + case 8: + ListHouses(this->m_Param, true, true, true); + break; + case 9: + ListSpeeches(this->m_Param); + break; + case 10: + ListSounds(this->m_Param); + break; + case 11: + ListMovies(this->m_Param, true); + break; + case 12: + ListThemes(this->m_Param); + break; + case 13: + ListHouses(this->m_Param, true, true, false); + break; + case 14: + ListMapVariables(this->m_Param); + break; + case 15: + listFacing(this->m_Param); + break; + case PRM_BuildingType: + ListBuildings(this->m_Param); + break; + case 17: + ListAnimations(this->m_Param); + break; + case 18: + listTalkBubble(this->m_Param); + break; + case 19: + ListBehaviours(this->m_Param); + break; + case 20: + ComboBoxHelper::ListBoolean(this->m_Param); + break; + } + HWND paramDesc = ::GetDlgItem(this->m_hWnd, IDC_PDESC); + ::SetWindowText(paramDesc, paramDefinition.Label); + ::EnableWindow(paramDesc, actionDefinition.Editable); + this->m_Param.EnableWindow(actionDefinition.Editable); + this->m_Description.SetWindowText(actionDefinition.Description); +} \ No newline at end of file diff --git a/MissionEditor/ScriptTypes.h b/MissionEditor/ScriptTypes.h index eb54998..1320130 100644 --- a/MissionEditor/ScriptTypes.h +++ b/MissionEditor/ScriptTypes.h @@ -30,12 +30,81 @@ ///////////////////////////////////////////////////////////////////////////// // Dialogfeld CScriptTypes +enum /*class*/ ActionType +{ + ACT_None = 0, + ACT_DoMission = 11, +}; + +enum /*class*/ ParameterType +{ + PRM_None = 0, + PRM_BuildingType = 16, +}; + +enum class ExtraParameterType +{ + None = 0, + ScanType, + Counter, +}; + +class ScriptTemplate { +public: + ScriptTemplate(const CString& desc, const CString& name, const std::vector& content) : + ScriptTemplate(desc, name, parse(content)) + { + } + ScriptTemplate(const CString& desc, const CString& name, const CString& content) : + ScriptTemplate(desc, name, parse(content)) + { + } + + auto const& Desc() const { return m_desc; } + auto const& Name() const { return m_name; } + auto const& Content() const { return m_content; } + +private: + ScriptTemplate(const CString& desc, const CString& name, std::vector&& content) : + m_desc(desc), + m_name(name), + m_content(std::move(content)) + { + } + + static std::vector parse(const CString& content); + static std::vector parse(const std::vector& content, size_t count, size_t offset = 0); + static inline std::vector parse(const std::vector& content) { + return parse(content, content.size() / 2); + } + + CString m_desc; + CString m_name; + std::vector m_content; +}; + class CScriptTypes : public CDialog { - DECLARE_DYNCREATE(CScriptTypes) + struct CScriptTypeAction { + CString Name{}; + CString Description{}; + int ParamTypeIndex{};//!< index linked to specific CScriptTypeParam, can be user defined + bool Hide{}; + bool Editable{}; + }; + + struct CScriptTypeParam { + ParameterType Type{};//!< internal predefined paramter type + CString Label{};//!< the string displayed for such parameter type + }; + + + DECLARE_DYNCREATE(CScriptTypes) // Konstruktion public: + using ActionDefinitionMap = std::map; + void UpdateDialog(); CScriptTypes(); ~CScriptTypes(); @@ -43,13 +112,6 @@ public: // Dialogfelddaten //{{AFX_DATA(CScriptTypes) enum { IDD = IDD_SCRIPTTYPES }; - CEdit m_DescriptionEx; - CStatic m_Desc; - CComboBox m_Type; - CComboBox m_ScriptType; - CComboBox m_Param; - CListBox m_Action; - CString m_Name; //}}AFX_DATA // Überschreibungen // Der Klassen-Assistent generiert virtuelle Funktionsüberschreibungen @@ -59,26 +121,56 @@ protected: //}}AFX_VIRTUAL void UpdateStrings(); -// Implementierung + void updateExtraParamComboBox(ExtraParameterType type, int value); + const CScriptTypeAction& getActionData(int actionCbIndex) const; + const CScriptTypeParam& getParamData(int paramIndex) const; + ParameterType getParameterType(int actionCbIndex) const; + void updateExtraValue(ParameterType paramType, CString* paramNumStr); + void UpdateParams(int actionIndex, CString* paramNumStr = nullptr); + + // Implementierung protected: + DECLARE_MESSAGE_MAP() + + virtual BOOL OnInitDialog() override; + void ListBehaviours(CComboBox& cb); // Generierte Nachrichtenzuordnungsfunktionen - //{{AFX_MSG(CScriptTypes) - afx_msg void OnEditchangeScripttype(); afx_msg void OnSelchangeScripttype(); - afx_msg void OnSelchangeAction(); + afx_msg void OnSelchangeActionList(); afx_msg void OnChangeName(); - afx_msg void OnEditchangeType(); - afx_msg void OnSelchangeType(); + afx_msg void OnEditchangeActionType(); + afx_msg void OnSelchangeActionType(); afx_msg void OnEditchangeParam(); afx_msg void OnSelchangeParam(); afx_msg void OnAddaction(); afx_msg void OnDeleteaction(); afx_msg void OnAdd(); afx_msg void OnDelete(); - virtual BOOL OnInitDialog(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() + afx_msg void OnCbnSelchangeScriptExtra(); + afx_msg void OnBnClickedScriptCopy(); + afx_msg void OnBnClickedCopyaction(); + + + void reloadTemplates(); + void insertScriptType(const CString& name, const std::vector& items); + int getExtraValue(); + CString getCurrentTypeID(); + void insertAction(int curSel, const CString& scriptTypeId, const CString& value); + + CEdit m_Description; + CComboBox m_Template; + CComboBox m_ActionType; + CComboBox m_ScriptType; + CComboBox m_Param; + CComboBox m_ParamExt; + CListBox m_Actions; + CString m_Name; + + ActionDefinitionMap m_actionDefinitions; + std::map m_paramDefinitions; + std::vector m_scriptTemplates; + }; diff --git a/MissionEditor/data/FinalAlert2/FAData.ini b/MissionEditor/data/FinalAlert2/FAData.ini index a302f10..df4a84f 100644 --- a/MissionEditor/data/FinalAlert2/FAData.ini +++ b/MissionEditor/data/FinalAlert2/FAData.ini @@ -31,7 +31,19 @@ RenderPlainHouseColor=yes [ForceIsoPalettePrefix] +;===ScriptTemplates=== +;X=UIName, Name, Count, Key 1 ,Value 1 , Key 2 ,Value 2 , ..., Key Count ,Value Count +;EMPTY will be considered as a CONST, with it FA2Copy will ignore the value +;By prry +[ScriptTemplates] +DefaultName=默认 +Counts=5 +1=两点间巡逻,New patrol 2 script,5,16,0,5,3,16,0,5,3,6,1 +2=三点间巡逻,New patrol 3 script,7,16,0,5,3,16,0,5,3,16,0,5,3,6,1 +3=四点间巡逻,New patrol 4 script,9,16,0,5,3,16,0,5,3,16,0,5,3,16,0,5,3,6,1 +4=装载部队卸载攻击,New unload script,6,3,0,8,2,39,0,20,0,50,120,0,0 +5=装载入载具并攻击,New load script,3,21,EMPTY,14,EMPTY,0,0 ; not NOSURFACES: ;[BuildingVoxelTurretsRA2] @@ -943,6 +955,107 @@ vertic_cornerleft_0=21 47=Building,28 48=Number,0,2 +[ScriptParams] +0=占位,0 +1=目标,1 +2=路径点,2 +3=跳转至行#,3 +4=分离小队,4 +5=全局变量,5 +6=脚本,6 +7=小队,7 +8=所属方,8 +9=语音,9 +10=音效,10 +11=影片,11 +12=音乐,12 +13=国家,13 +14=局部变量,14 +15=朝向,15 +16=建筑,16 +17=动画,17 +18=对话气泡,18 +19=进入状态,19 +20=整数,0 +21=布尔,20 + +;Index=Name,ParamType,Obsolete,YROnly,Description +[ScriptsRA2] +0=0 - 攻击,1,0,1,攻击某类目标。 +1=1 - 攻击路径点,2,0,1,攻击指定路径点(上的物体)。 +2=2 - 暴走(无效),0,1,0,小队的半机械人成员会暴走(无效脚本)。 +3=3 - 移动到路径点,2,0,1,移动到指定路径点。 +4=4 - 移动到坐标,20,0,1,移动到坐标点 (int x+y*128)。 +5=5 - 警戒(时间计数),20,0,1,区域警戒 X 秒。(填5与计时器流逝5秒时间相同) +6=6 - 脚本转到行号 #,3,0,1,跳转到脚本的第#行,用来循环。 +7=7 - 玩家胜利,0,0,0,使玩家胜利。 +8=8 - 卸载,4,0,1,所有有载员的运载单位卸客。命令参数可以分别指定两个部分是否依然是小队成员或者要离开小队。 +9=9 - 部署,0,0,0,所有可部署单位进行部署。 +10=10 - 跟随友军单位,0,0,0,令小队跟随最近的友军单位。 +11=11 - 进入...状态,19,0,1,小队所有成员执行指定任务。 +12=12 - 设置全局变量,5,0,1,设置一个全局变量。 +13=13 - 进入空闲动画,0,0,0,让小队中的步兵单位闲置(播放闲置动画) +14=14 - 装载入运输载具,0,0,0,让所有单位进入运输载具(如果可能的话) +15=15 - 间谍进入路径点的建筑物,0,1,0,(废弃脚本) +16=16 - 巡逻至路径点,2,0,1,小队成员移动攻击到指定路径点。 +17=17 - 更改应用脚本,6,0,1,让小队使用新的脚本(参数为动作脚本ID) +18=18 - 更改小队,7,0,1,让小队更改小队类型(参数为作战小队ID) +19=19 - 惊慌,0,0,0,让小队所有单位惊慌。 +20=20 - 更改所属方,13,0,1,小队成员全部更改为所属方(参数为所属方编号) +21=21 - 分散,0,0,0,让所有单位分散。 +22=22 - 进入周围的黑幕,0,0,0,让所有单位逃到有黑幕的地方。 +23=23 - 玩家失败,0,0,0,使玩家失败。 +24=24 - 播放语音,9,0,1,播放特定的语音文件。 +25=25 - 播放音效,10,0,1,播放特定的音效文件。 +26=26 - 播放电影,11,0,1,播放特定的电影文件。 +27=27 - 播放音乐,12,0,1,播放特定的音乐文件。 +28=28 - 减少矿石,0,0,0,减少小队成员周围的矿石数量。 +29=29 - 开始生产,0,0,0,让所属方开始生产。 +30=30 - 变卖乌拉,0,0,0,让AI所属方变卖所有建筑并进行最后总攻。 +31=31 - 小队自毁,0,0,0,让该小队所有成员自毁。 +32=32 - 闪电风暴开始(延时),20,0,1,经过指定的时间后开始闪电风暴。 +33=33 - 闪电风暴结束,0,0,0,使闪电风暴结束。 +34=34 - 镜头聚焦小队,20,0,1,镜头聚焦小队(参数为速度) +35=35 - 重置地图黑幕,0,0,0,重置地图黑幕。 +36=36 - 显示全地图,0,0,0,显示全地图。 +37=37 - 删除小队成员,0,0,0,删除该小队所有成员。 +38=38 - 清除全局变量,5,0,1,清除指定全局变量。 +39=39 - 设置局部变量,14,0,1,设置指定局部变量。 +40=40 - 清除局部变量,14,0,1,清除指定局部变量。 +41=41 - 镇定,0,0,0,使所有小队成员停止惊慌。 +42=42 - 强制面向,15,0,1,强制小队成员面向一个特定的方向。 +43=43 - 等待到满载,0,0,0,等到所有运输器满载。 +44=44 - 卡车卸载,0,0,0,让所有卡车卸载箱子(即改变类型为未装载单位) +45=45 - 卡车装载,0,0,0,让所有卡车装载箱子(即改变类型为装载的单位) +46=46 - 攻击敌方建筑物,16,0,1,攻击指定类型的建筑,参数为建筑序号,额外参数可以用于指定搜索目标时的策略。 +47=47 - 移动到敌方建筑物,16,0,1,移动到指定类型的建筑,参数为建筑序号+系数(+0:攻击威胁值最小的,+65536 :攻击威胁值最大的,+131072:攻击距离最近的,+262144:攻击距离最远的)。 +48=48 - 侦察,0,0,0,小队会侦察玩家未探索的区域。 +49=49 - 成功,0,0,0,记录小队完成了任务。用于AI触发分量(权重)。在每一个AI触发后添加此行为。 +50=50 - 闪烁,20,0,1,闪烁小队一段时间(参数为闪烁的帧数) +51=51 - 播放动画,17,0,1,在每个小队单位上播放动画。 +52=52 - 对话气泡,18,0,1,在小队第一个单位上显示对话气泡。 +53=53 - 敌方基地外集合,20,0,1,在敌方基地安全距离外集合。Ares2.0起,数值参数的正负将影响Rulesmd全局中安全距离 (AISafeDistance=) 的伸长或缩短。 +54=54 - 己方基地外集合,20,0,1,在己方基地一定距离外集合。Ares2.0起,数值参数的正负将影响Rulesmd全局中「友军基地」安全距离 (AIFriendlyDistance=,不存在这条则用AISafeDistance=) 的伸长或缩短。 +55=55 - 请求超武...,20,0,1,请求小队所属方对当前小队使用超级武器,默认为铁幕。Ares2.0起,数值参数决定AI使用哪一组超武(对应SW.Group=,默认为0)。 +56=56 - 超时空传送至建筑,16,0,1,若所属方超时空传送就绪,将该小队超时空传送至特定建筑。但是此后需要额外使用攻击命令。 +57=57 - 超时空传送至目标,1,0,1,若所属方超时空传送就绪,将该小队超时空传送至目标。但是此后需要额外使用攻击命令。 +;YR Only +58=58 - 移动到己方建筑,16,0,1,移动到友方的特定建筑,额外参数可以用于指定搜索目标时的策略 +59=59 - 攻占路径点上的建筑,2,0,1,攻击占据该路径点的建筑物。AI单位通常会优先尝试驻扎(若能)。 +60=60 - 进入回收厂,0,0,0,让小队进入该所属方就近的部队回收厂。 +61=61 - 进入坦克碉堡,0,0,0,让小队的每个单位进入空的坦克碉堡。 +62=62 - 进入生化反应炉,0,0,0,让小队进入生化反应炉。 +63=63 - 进入战斗碉堡,0,0,0,让小队进入战斗碉堡。 +64=64 - 进入可驻军建筑,0,0,0,让小队进驻中立建筑物。 +;Hares Only +65=65 - 攻击特定类型的单位,20,0,1,攻击特定类型的科技单位,数值为全局TeamTargetTechnoTypes列表中的序号,从0开始 +66=66 - 驻扎路径点位置的建筑,2,0,1,派遣步兵单位驻扎路径点位置的建筑(如果有),不区分小队所属是人类或AI玩家。 +67=67 - 摄像机视角跟随,20,0,1,激活/取消摄像机追逐视角设置为该小队成员(所有成员都会标记为跟随,多成员小队慎用)。 +68=68 - 小队成员被玩家选中,20,0,1,激活/取消设置该小队成员为选中状态(所有成员都会标记为选中)。 +69=69 - 上一脚本执行成功,20,0,1,上一次脚本执行结果为成功 +70=70 - 上一脚本执行失败,20,0,1,上一次脚本执行结果为失败 +71=71 - 等待N游戏秒,20,0,1,等待N游戏秒,以保证上一条脚本执行完毕 + [DontSaveAsWP] ; which code values indicate that the waypoint param must be saved as normal integer? 0=5 1=9 diff --git a/MissionEditor/data/FinalAlert2/FALanguage.ini b/MissionEditor/data/FinalAlert2/FALanguage.ini index f608892..ba678db 100644 --- a/MissionEditor/data/FinalAlert2/FALanguage.ini +++ b/MissionEditor/data/FinalAlert2/FALanguage.ini @@ -1346,6 +1346,7 @@ TriggerOptionDisableTip=被禁止的触发必须由其它触发激活后才能 ; team scripts ScriptTypesAddScript=添加 +ScriptTypesCopyScript=复制 ScriptTypesDelScript=删除 ScriptsCaption=小队脚本 ScriptTypesDesc=脚本类型允许你定义如一作战小队从一个路径点移动到另一路径点。一个脚本类型与小队类型相关联(不是特遣部队!)\n欲应用模板,请在下拉列表里选中一个预置模板,然后点击“新建”。\n如果预置列表里啥都没有,可以点击“刷新”以重新载入。 @@ -1355,8 +1356,12 @@ ScriptTypesActions=行为: ScriptTypesActionType=行为类型: ScriptTypesActionParam=行为参数: ScriptTypesAddAction=添加 +ScriptTypesCopyAction=复制 ScriptTypesDelAction=删除 +ScriptTypesInsertMode=插入模式 ScriptTypesActionDesc=说明: +ScriptTypesSelectedTemplate=模板: +ScriptTypesParamExtDesc=附加参数: ; iso view IsoCaption=地图视图 @@ -1506,6 +1511,72 @@ InfP5=允许AI重组:: ; ScriptTypesDelScript=确定删除脚本吗?该操作不能撤销 ; Now some translations from english to the used language (used for some combo-boxes, menus/listviews and of course unit names) ; currently used for: menu, iso-view status bar, unit/building/stuff list, overlay browser, terrain browser +; Do Something +0 - Sleep=0 - 休眠 +1 - Attack nearest enemy=1 - 攻击最近的敌人 +2 - Move=2 - 移动 +3 - QMove=3 - 快速移动 +4 - Retreat home for R&R=4 - 撤离地图 +5 - Guard=5 - 警戒 +6 - Sticky (never recruit)=6 - 原地锁定(避免招募) +7 - Enter object=7 - 进入物体 +8 - Capture object=8 - 占领物体 +9 - Move into & get eaten=9 - 进入并被吸收 +10 - Harvest=10 - 采矿 +11 - Area Guard=11 - 区域警戒 +12 - Return (to refinery)=12 - 返回(矿厂) +13 - Stop=13 - 停止 +14 - Ambush (wait until discovered)=14 - 埋伏(等待被发现) +15 - Hunt=15 - 穷追不舍 +16 - Unload=16 - 卸载/部署 +17 - Sabotage (move in & destroy)=17 - 爆破(进入并摧毁) +18 - Construction=18 - 建造状态 +19 - Deconstruction=19 - 变卖状态 +20 - Repair=20 - 维修 +21 - Rescue=21 - 救援 +22 - Missile=22 - 发射 +23 - Harmless=23 - 无害化 +24 - Open=24 - (闸门)开启 +25 - Patrol=25 - 巡逻 +26 - Paradrop approach drop zone=26 - 找位置空投 +27 - Paradrop overlay drop zone=27 - 空投结束离开 +28 - Wait=28 - 等待 +29 - Attack again=29 - 再次攻击 +30 - Spyplane approach=30 - 侦察机侦查 +31 - Spyplane retreat=31 - 侦察机离开 +; Facings +0 - NE=0 - 东北 +1 - E=1 - 东 +2 - SE=2 - 东南 +3 - S=3 - 南 +4 - SW=4 - 西南 +5 - W=5 - 西 +6 - NW=6 - 西北 +7 - N=7 - 北 +; Talk Bubble +0 - None=0 - 无 +1 - Asterisk(*)=1 - 星号(*) +2 - Question mark(?)=2 - 问号(?) +3 - Exclamation mark(!)=3 - 感叹号(!) +; Unload passenger +0 - Keep Transports, Keep Units=0 - 保留载具和乘客 +1 - Keep Transports, Lose Units=1 - 保留载具,解散乘客 +2 - Lose Transports, Keep Units=2 - 解散载具,保留乘客 +3 - Lose Transports, Lose Units=3 - 载具和乘客都解散 +; Building Target Extra Param +0 - Least threat=0 - 最小威胁 +1 - Most threat=1 - 最大威胁 +2 - Least distant=2 - 最近距离 +3 - Most distant=3 - 最远距离 +; Target +1 - Not specified=1 - 任意 +2 - Buildings=2 - 建筑 +3 - Harvesters=3 - 矿车/矿厂 +4 - Infantry=4 - 步兵 +5 - Vehicles=5 - 载具 +6 - Factories=6 - 工厂 +7 - Base defenses=7 - 防御建筑 +9 - Power plants=9 - 电厂 Tiberium=矿石 Run Tiberian Sun=运行尤里的复仇 diff --git a/MissionEditor/functions.cpp b/MissionEditor/functions.cpp index 080a5de..6b288c4 100644 --- a/MissionEditor/functions.cpp +++ b/MissionEditor/functions.cpp @@ -27,6 +27,7 @@ #include "functions.h" #include "inlines.h" #include "mmsystem.h" +#include "IniMega.h" #include @@ -229,84 +230,84 @@ void HandleParamList(CComboBox& cb, int type) cb.GetWindowText(oldText); switch (type) { - case PARAMTYPE_NOTHING: - { - while (cb.DeleteString(0) != CB_ERR); - cb.SetWindowText(oldText); + case PARAMTYPE_NOTHING: + { + while (cb.DeleteString(0) != CB_ERR); + cb.SetWindowText(oldText); - //cb.AddString("0"); - } - break; - case PARAMTYPE_HOUSES: - ListHouses(cb, TRUE, TRUE, TRUE); - break; - case PARAMTYPE_WAYPOINTS: - ListWaypoints(cb); - break; - case PARAMTYPE_TEAMTYPES: - ListTeamTypes(cb, FALSE); - break; - case PARAMTYPE_UNITTYPES: - ListUnits(cb); - break; - case PARAMTYPE_INFANTRYTYPES: - ListInfantry(cb); - break; - case PARAMTYPE_AIRCRAFTTYPES: - ListAircraft(cb); - break; - case PARAMTYPE_BUILDINGTYPES: - ListBuildings(cb); - break; - case PARAMTYPE_VIDEOS: - ListMovies(cb, FALSE, TRUE); - break; - case PARAMTYPE_TUTORIALTEXTS: - ListTutorial(cb); - break; - case PARAMTYPE_TRIGGERS: - ListTriggers(cb); - break; - case PARAMTYPE_YESNO: - ListYesNo(cb); - break; - case PARAMTYPE_SOUNDS: - ListSounds(cb); - break; - case PARAMTYPE_THEMES: - ListThemes(cb); - break; - case PARAMTYPE_SPEECHES: - ListSpeeches(cb); - break; - case PARAMTYPE_SPECIALWEAPONS: - ListSpecialWeapons(cb); - break; - case PARAMTYPE_ANIMATIONS: - ListAnimations(cb); - break; - case PARAMTYPE_PARTICLES: - ListParticles(cb); - break; - case PARAMTYPE_CRATETYPES: - ListCrateTypes(cb); - break; - case PARAMTYPE_SPEECHBUBBLETYPES: - ListSpeechBubbleTypes(cb); - break; - case PARAMTYPE_GLOBALS: - ListGlobals(cb); - break; - case PARAMTYPE_RULESGLOBALS: - ListRulesGlobals(cb); - break; - case PARAMTYPE_BUILDINGTYPESINI: - ListBuildings(cb, true); - break; - case PARAMTYPE_TECHTYPES: - ListTechtypes(cb); + //cb.AddString("0"); + } break; + case PARAMTYPE_HOUSES: + ListHouses(cb, TRUE, TRUE, TRUE); + break; + case PARAMTYPE_WAYPOINTS: + ListWaypoints(cb); + break; + case PARAMTYPE_TEAMTYPES: + ListTeamTypes(cb, FALSE); + break; + case PARAMTYPE_UNITTYPES: + ListUnits(cb); + break; + case PARAMTYPE_INFANTRYTYPES: + ListInfantry(cb); + break; + case PARAMTYPE_AIRCRAFTTYPES: + ListAircraft(cb); + break; + case PARAMTYPE_BUILDINGTYPES: + ListBuildings(cb); + break; + case PARAMTYPE_VIDEOS: + ListMovies(cb, FALSE, TRUE); + break; + case PARAMTYPE_TUTORIALTEXTS: + ListTutorial(cb); + break; + case PARAMTYPE_TRIGGERS: + ListTriggers(cb); + break; + case PARAMTYPE_YESNO: + ListYesNo(cb); + break; + case PARAMTYPE_SOUNDS: + ListSounds(cb); + break; + case PARAMTYPE_THEMES: + ListThemes(cb); + break; + case PARAMTYPE_SPEECHES: + ListSpeeches(cb); + break; + case PARAMTYPE_SPECIALWEAPONS: + ListSpecialWeapons(cb); + break; + case PARAMTYPE_ANIMATIONS: + ListAnimations(cb); + break; + case PARAMTYPE_PARTICLES: + ListParticles(cb); + break; + case PARAMTYPE_CRATETYPES: + ListCrateTypes(cb); + break; + case PARAMTYPE_SPEECHBUBBLETYPES: + ListSpeechBubbleTypes(cb); + break; + case PARAMTYPE_GLOBALS: + ListMapVariables(cb); + break; + case PARAMTYPE_RULESGLOBALS: + ListRulesGlobals(cb); + break; + case PARAMTYPE_BUILDINGTYPESINI: + ListBuildings(cb, true); + break; + case PARAMTYPE_TECHTYPES: + ListTechtypes(cb); + break; } } @@ -671,24 +672,24 @@ bool HSVToRGB(const float h, const float s, const float v, float& r, float& g, f const float x = c * (1 - fabs(fmod(h / 60.0, 2.0) - 1)); const float m = v - c; switch (h_) { - case 0: - r = c, g = x, b = 0.0; - break; - case 1: - r = x, g = c, b = 0.0; - break; - case 2: - r = 0.0, g = c, b = x; - break; - case 3: - r = 0.0, g = x, b = c; - break; - case 4: - r = x, g = 0.0, b = c; - break; - case 5: - r = c, g = 0.0, b = x; - break; + case 0: + r = c, g = x, b = 0.0; + break; + case 1: + r = x, g = c, b = 0.0; + break; + case 2: + r = 0.0, g = c, b = x; + break; + case 3: + r = 0.0, g = x, b = c; + break; + case 4: + r = x, g = 0.0, b = c; + break; + case 5: + r = c, g = 0.0, b = x; + break; } r += m; g += m; @@ -804,7 +805,7 @@ void listLocalVariables(CComboBox& cb, const CIniFile& ini) } // should be ListLocals() -void ListGlobals(CComboBox& cb) +void ListMapVariables(CComboBox& cb) { listLocalVariables(cb, Map->GetIniFile()); } @@ -1266,19 +1267,55 @@ void ListTargets(CComboBox& cb) while (cb.DeleteString(0) != CB_ERR); - cb.AddString("1 - Not specified"); - cb.AddString("2 - Buildings"); - cb.AddString("3 - Harvesters"); - cb.AddString("4 - Infantry"); - cb.AddString("5 - Vehicles"); - cb.AddString("6 - Factories"); - cb.AddString("7 - Base defenses"); - cb.AddString("9 - Power plants"); - - if (sel >= 0) cb.SetCurSel(sel); + cb.AddString(TranslateStringACP("1 - Not specified")); + cb.AddString(TranslateStringACP("2 - Buildings")); + cb.AddString(TranslateStringACP("3 - Harvesters")); + cb.AddString(TranslateStringACP("4 - Infantry")); + cb.AddString(TranslateStringACP("5 - Vehicles")); + cb.AddString(TranslateStringACP("6 - Factories")); + cb.AddString(TranslateStringACP("7 - Base defenses")); + cb.AddString(TranslateStringACP("9 - Power plants")); + if (sel >= 0) { + cb.SetCurSel(sel); + } } +void ComboBoxHelper::Clear(CComboBox& combobox) +{ + while (combobox.DeleteString(0) != -1); +} + +void ComboBoxHelper::ListCountries(CComboBox& combobox, bool bShowIndex) +{ + ComboBoxHelper::Clear(combobox); + auto& doc = Map->GetIniFile(); + bool bMultiOnly = doc.GetBool("Basic", "MultiplayerOnly"); + if (bMultiOnly) { + ListHouses(combobox, bShowIndex); + return; + } + auto const& rules = IniMegaFile::GetRules(); + auto const& items = rules.GetSection("Countries"); + CString buffer; + for (auto it = items.begin(); it != items.end(); ++it) { + auto const& [idxStr, id] = *it; + auto const idx = atoi(idxStr); + if (bShowIndex) { + buffer.Format("%u - %s", idx, id.operator LPCSTR()); + } else { + buffer = id; + } + combobox.SetItemData(combobox.AddString(buffer), idx); + } +} + +void ComboBoxHelper::ListBoolean(CComboBox& combobox) +{ + ComboBoxHelper::Clear(combobox); + combobox.SetItemData(combobox.AddString(TranslateStringACP("0 - FALSE")), 0); + combobox.SetItemData(combobox.AddString(TranslateStringACP("1 - TRUE")), 1); +} CString GetHouseSectionName(CString lpHouse) { diff --git a/MissionEditor/functions.h b/MissionEditor/functions.h index 7eaa457..2789aa4 100644 --- a/MissionEditor/functions.h +++ b/MissionEditor/functions.h @@ -101,7 +101,7 @@ void ListAnimations(CComboBox& cb); void ListParticles(CComboBox& cb); void ListCrateTypes(CComboBox& cb); void ListSpeechBubbleTypes(CComboBox& cb); -void ListGlobals(CComboBox& cb); +void ListMapVariables(CComboBox& cb); void ListRulesGlobals(CComboBox& cb); void ListTechtypes(CComboBox& cb); @@ -157,5 +157,12 @@ std::unique_ptr BitmapFromFile(const CString& filepath); CComPtr BitmapToSurface(IDirectDraw4* pDD, const CBitmap& bitmap); +class ComboBoxHelper +{ +public: + static void Clear(CComboBox& combobox); + static void ListCountries(CComboBox& combobox, bool bShowIndex = false); + static void ListBoolean(CComboBox& combobox); +}; #endif \ No newline at end of file diff --git a/MissionEditor/resource.h b/MissionEditor/resource.h index 32e3042..ab6e75a 100644 --- a/MissionEditor/resource.h +++ b/MissionEditor/resource.h @@ -1,12 +1,11 @@ //{{NO_DEPENDENCIES}} -// Von Microsoft Visual C++ generierte Includedatei. -// Verwendet durch MissionEditor.rc +// Microsoft Visual C++ generated include file. +// Used by MissionEditor.rc // #define IDD_TIBERIANSUNMISSIONEDITOR_DIALOG 102 #define IDD_FINALSUN_DIALOG 102 #define IDB_LIGHTBULB 103 #define IDD_TIP 104 - #define IDR_MAINFRAME 128 #define IDR_MAIN 129 #define IDD_BASIC 135 @@ -172,6 +171,7 @@ #define IDC_LEVEL2 1067 #define IDC_INITIALVETERAN 1067 #define IDC_POS 1067 +#define IDC_SCRIPT_COPY 1067 #define IDC_FIXEDALLIANCE 1068 #define IDC_HARVESTERIMMUNE 1069 #define IDC_FOGOFWAR 1070 @@ -281,6 +281,7 @@ #define IDC_ADDACTION 1173 #define IDC_DELETEACTION 1174 #define IDC_EVENTTYPE 1175 +#define IDC_COPYACTION 1175 #define IDC_ACTIONPARAM1 1176 #define IDC_ACTIONWAYPOINT 1177 #define IDC_ACTIONTYPE 1178 @@ -299,9 +300,12 @@ #define IDC_LABEL_A6 1191 #define IDC_TRIGGER2 1192 #define IDC_SCRIPTTYPE 1193 +#define IDC_SCRIPT_TEMPLATE 1194 #define IDC_PARAM 1196 +#define IDC_SCRIPT_EXTRA 1197 #define IDC_PDESC 1198 #define IDC_AITRIGGERTYPE 1199 +#define IDC_SCRIPT_EXDESC 1199 #define IDC_ENABLEALL 1200 #define IDC_TEAMTYPE1 1204 #define IDC_OWNER 1205 @@ -514,20 +518,22 @@ #define IDC_PREFER_LOCAL_THEATER_INI_FILES 1462 #define IDC_PREFER_LOCAL_THEATER_FILES 1463 #define IDC_TRIGGER_OPTION_TYPE_STR 1464 -#define IDC_TRIGGER_OPTION_NAME 1465 -#define IDC_TRIGGER_OPTION_HOUSE 1466 -#define IDC_TRIGGER_OPTION_ATTACHED_TRIGGER 1467 -#define IDC_TRIGGER_OPTION_TRIGGER_DIS_TIP 1468 -#define IDC_MAP_D_WIDTH 1469 -#define IDC_MAP_D_HEIGHT 1470 -#define IDC_SCTIPTTYPE_INRO 1471 -#define IDC_SCRIPTTYPE_TYPE 1472 -#define IDC_SCRIPTTYPE_NAME 1473 -#define IDC_SCRIPTTYPE_ACTIONS 1474 -#define IDC_SCRIPTTYPE_ACTIONTYPE 1475 -#define IDC_SCRIPTTYPE_DESC 1476 -#define IDD_TERRAINBAR_TG 1477 -#define IDD_TERRAINBAR_OS 1478 +#define IDC_SCRIPT_CK_INSERT 1464 +#define IDC_TRIGGER_OPTION_NAME 1465 +#define IDC_TRIGGER_OPTION_HOUSE 1466 +#define IDC_TRIGGER_OPTION_ATTACHED_TRIGGER 1467 +#define IDC_TRIGGER_OPTION_TRIGGER_DIS_TIP 1468 +#define IDC_MAP_D_WIDTH 1469 +#define IDC_MAP_D_HEIGHT 1470 +#define IDC_SCTIPTTYPE_INRO 1471 +#define IDC_SCRIPTTYPE_TYPE 1472 +#define IDC_SCRIPTTYPE_NAME 1473 +#define IDC_SCRIPTTYPE_ACTIONS 1474 +#define IDC_SCRIPTTYPE_ACTIONTYPE 1475 +#define IDC_SCRIPTTYPE_DESC 1476 +#define IDD_TERRAINBAR_TG 1477 +#define IDD_TERRAINBAR_OS 1478 +#define IDC_SCRIPT_TEMPLATE_DSC 1479 #define ID_FILE_OPENMAP 40001 #define ID_FILE_SAVEAS 40002 #define ID_FILE_QUIT 40003 @@ -605,18 +611,16 @@ #define ID_HELP_SHOWLOGS40140 40130 #define ID_OPTIONS_SMOOTHZOOM 40131 #define ID_OPTIONS_USEDEFAULTMOUSECURSOR 40132 - #define ID_FILE_FILE1 40139 #define ID_FILE_FILE2 40140 #define ID_FILE_FILE3 40141 #define ID_FILE_FILE4 40142 -#define ID_FILE_FILE5 40143 -#define ID_FILE_FILE6 40144 -#define ID_FILE_FILE7 40145 -#define ID_FILE_FILE8 40146 -#define ID_FILE_FILE9 40147 -#define ID_FILE_FILE10 40148 - +#define ID_FILE_FILE5 40143 +#define ID_FILE_FILE6 40144 +#define ID_FILE_FILE7 40145 +#define ID_FILE_FILE8 40146 +#define ID_FILE_FILE9 40147 +#define ID_FILE_FILE10 40148 #define IDS_LINEARVERSION 57604 #define IDC_TOOLTIPCENTER 65535 @@ -626,7 +630,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 300 #define _APS_NEXT_COMMAND_VALUE 40144 -#define _APS_NEXT_CONTROL_VALUE 1464 +#define _APS_NEXT_CONTROL_VALUE 1465 #define _APS_NEXT_SYMED_VALUE 111 #endif #endif diff --git a/googletest b/googletest index 9e3d285..550fcd0 160000 --- a/googletest +++ b/googletest @@ -1 +1 @@ -Subproject commit 9e3d285bde1b82319091aefb3e336fd836658627 +Subproject commit 550fcd02092c3fea05ce8c7f6111565db1d0afd2