Index: binaries/data/mods/public/campaigns/new_maps.json =================================================================== --- binaries/data/mods/public/campaigns/new_maps.json +++ binaries/data/mods/public/campaigns/new_maps.json @@ -123,5 +123,6 @@ "syria_2p", "white_cliffs_of_dover_5p" ], - "ShowUnavailable": true + "ShowUnavailable": true, + "Difficulty": "PerLevel" } Index: binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js =================================================================== --- binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js +++ binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js @@ -12,6 +12,8 @@ this.run = campaignRun; + this.difficulty = new CampaignDifficulty(this.run); + this.selectedLevel = -1; this.levelSelection = Engine.GetGUIObjectByName("levelSelection"); this.levelSelection.onSelectionChange = () => { this.selectedLevel = this.levelSelection.selected; }; @@ -28,14 +30,25 @@ this._ready = true; } - goBackToMainMenu() + saveRun() { + if (this.difficulty.isPerLevel()) + this.difficulty.serialize(); this.run.save(); + } + + goBackToMainMenu() + { + this.saveRun(); Engine.SwitchGuiPage("page_pregame.xml", {}); } startScenario() { + // Save some settings + if (this.difficulty.isPerLevel()) + this.saveRun(); + const level = this.getSelectedLevelData(); if (!meetsRequirements(this.run, level)) return; @@ -69,7 +82,7 @@ { const bot = { "bot": g_Settings.PlayerDefaults[i + 1].AI, - "difficulty": +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty"), + "difficulty": this.difficulty.getAIDifficulty(), "behavior": Engine.ConfigDB_GetValue("user", "gui.gamesetup.aibehavior"), }; if (typeof level.AI === "string") @@ -78,6 +91,17 @@ } } + const triggerDiffs = gameSettings.triggerDifficulty.getAvailableSettings(); + if (triggerDiffs.length) + { + const diff = this.difficulty.getTriggerDifficulty(); + if (triggerDiffs.filter(x => x.Difficulty === diff).length > 0) + gameSettings.triggerDifficulty.setValue(diff); + else + // TODO: should match closest, ideally. + gameSettings.triggerDifficulty.setValue(triggerDiffs.find(x => x.Default).Difficulty); + } + if (level.Preview) gameSettings.mapPreview.setCustom("cropped:" + 400/512 + "," + 300/512 + ":" + level.Preview); gameSettings.mapName.set(this.getLevelName(level)); @@ -163,6 +187,16 @@ displayLevelDetails() { + // Render the difficulty & adjust the size of the description box if needed. + const scenarioInfoSize = Engine.GetGUIObjectByName("scenarioInfoBox").size; + + const diffObject = this.difficulty.render(); + if (diffObject.hidden) + scenarioInfoSize.bottom = 0; + else + scenarioInfoSize.bottom = Engine.GetGUIObjectByName("campaignSettings").size.top; + Engine.GetGUIObjectByName("scenarioInfoBox").size = scenarioInfoSize; + if (this.selectedLevel === -1) { Engine.GetGUIObjectByName("startButton").enabled = false; @@ -170,11 +204,11 @@ return; } - let level = this.getSelectedLevelData(); + const level = this.getSelectedLevelData(); Engine.GetGUIObjectByName("scenarioName").caption = this.getLevelName(level); Engine.GetGUIObjectByName("scenarioDesc").caption = this.getLevelDescription(level); - Engine.GetGUIObjectByName('levelPreviewBox').sprite = this.getLevelPreview(level); + Engine.GetGUIObjectByName("levelPreviewBox").sprite = this.getLevelPreview(level); Engine.GetGUIObjectByName("startButton").enabled = meetsRequirements(this.run, level); Engine.GetGUIObjectByName("startButton").hidden = false; Index: binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml =================================================================== --- binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml +++ binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml @@ -40,7 +40,7 @@ - + No scenario selected @@ -49,6 +49,14 @@ + + + + + + + + Index: binaries/data/mods/public/gui/campaigns/default_menu/Difficulty.js =================================================================== --- /dev/null +++ binaries/data/mods/public/gui/campaigns/default_menu/Difficulty.js @@ -0,0 +1,89 @@ +/** + * Split off for easier moddability - combines AI & trigger difficulties. + */ +var g_CampaignDifficulties = [ + { + "name": "Very easy", + "aidiff": 1, + "triggerdiff": 1 + }, + { + "name": "Easy", + "aidiff": 2, + "triggerdiff": 2 + }, + { + "name": "Medium", + "aidiff": 3, + "triggerdiff": 3 + }, + { + "name": "Hard", + "aidiff": 4, + "triggerdiff": 4 + }, + { + "name": "Very hard", + "aidiff": 5, + "triggerdiff": 5 + } +]; + +class CampaignDifficulty +{ + constructor(run) + { + this.run = run; + this.perLevel = this.run.template?.Difficulty === "PerLevel"; + + this.difficulty = this.run?.data?.difficulty ?? 2; // Defaults to 'Medium'. + + this.object = Engine.GetGUIObjectByName("perLevelDifficulty"); + this.label = Engine.GetGUIObjectByName("perLevelDifficultyLabel"); + this.input = Engine.GetGUIObjectByName("perLevelDifficultyInput"); + + this.input.onSelectionChange = () => { + this.difficulty = this.input.selected; + }; + } + + isPerLevel() + { + return this.perLevel; + } + + serialize() + { + this.run.data.difficulty = this.difficulty; + } + + getAIDifficulty() + { + if (this.input.selected !== -1) + return g_CampaignDifficulties[this.input.list_data[this.input.selected]].aidiff; + return 3; // Defaults to medium. + } + + getTriggerDifficulty() + { + if (this.input.selected !== -1) + return g_CampaignDifficulties[this.input.list_data[this.input.selected]].triggerdiff; + return 3; // Defaults to medium. + } + + render() + { + if (!this.isPerLevel()) + { + this.object.hidden = true; + return this.object; + } + this.object.hidden = false; + this.label.caption = translate("Difficulty"); + this.input.list = g_CampaignDifficulties.map(x => x.name); + this.input.list_data = g_CampaignDifficulties.map((_, i) => i); + if (this.input.selected === -1 && this.difficulty < this.input.list.length) + this.input.selected = this.difficulty; + return this.object; + } +} Index: binaries/data/mods/public/gui/gamesettings/attributes/TriggerDifficulty.js =================================================================== --- binaries/data/mods/public/gui/gamesettings/attributes/TriggerDifficulty.js +++ binaries/data/mods/public/gui/gamesettings/attributes/TriggerDifficulty.js @@ -22,7 +22,7 @@ getAvailableSettings() { - return this.difficulties.filter(x => this.available.indexOf(x.Name) !== -1); + return this.difficulties.filter(x => this.available && this.available.indexOf(x.Name) !== -1); } onMapChange() @@ -40,6 +40,7 @@ setValue(val) { + // TODO: should probably validate that they fit one of the known schemes. this.value = val; }