Ticket #3994: t3994_gamesetup_GUI_rewrite_v3.patch
File t3994_gamesetup_GUI_rewrite_v3.patch, 54.7 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/gui/gamesetup/gamesetup.js
1 1 const g_MatchSettings_SP = "config/matchsettings.json"; 2 2 const g_MatchSettings_MP = "config/matchsettings.mp.json"; 3 3 4 const g_PlayerArray = Array(g_MaxPlayers).fill(0).map((v, i) => i + 1); // 1, 2, ..., MaxPlayers 4 5 const g_Ceasefire = prepareForDropdown(g_Settings && g_Settings.Ceasefire); 5 6 const g_GameSpeeds = prepareForDropdown(g_Settings && g_Settings.GameSpeeds.filter(speed => !speed.ReplayOnly)); 6 7 const g_MapSizes = prepareForDropdown(g_Settings && g_Settings.MapSizes); 7 8 const g_MapTypes = prepareForDropdown(g_Settings && g_Settings.MapTypes); 8 9 const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities); 9 10 const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources); 10 11 const g_VictoryConditions = prepareForDropdown(g_Settings && g_Settings.VictoryConditions); 11 12 const g_WonderDurations = prepareForDropdown(g_Settings && g_Settings.WonderDurations); 12 13 13 14 /** 15 * Highlight the "random" dropdownlist item. 16 */ 17 const g_ColorRandom = "orange"; 18 19 const g_TeamsArray = prepareForDropdown([{ 20 "label": translateWithContext("team", "None"), 21 "id": -1 22 }].concat( 23 Array(g_MaxTeams).fill(0).map((v, i) => ({ 24 "label": i + 1, 25 "id": i 26 })) 27 ) 28 ); 29 30 /** 31 * Offer users to select playable civs only. 32 * Load unselectable civs as they could appear in scenario maps. 33 */ 34 const g_CivData = loadCivData(); 35 36 const g_CivList = g_CivData && prepareForDropdown([{ 37 "name": '[color="' + g_ColorRandom + '"]' + translateWithContext("civilization", "Random") + '[/color]', 38 "code": "random" 39 }].concat( 40 Object.keys(g_CivData).filter( 41 civ => g_CivData[civ].SelectableInGameSetup 42 ).map(civ => ({ 43 "name": g_CivData[civ].Name, 44 "code": civ 45 })).sort(sortNameIgnoreCase) 46 ) 47 ); 48 49 /** 14 50 * All selectable playercolors except gaia. 15 51 */ 16 52 const g_PlayerColors = g_Settings && g_Settings.PlayerDefaults.slice(1).map(pData => pData.Color); 17 53 18 54 /** … … const g_MapPath = { 25 61 }; 26 62 27 63 /** 28 64 * Processes a CNetMessage (see NetMessage.h, NetMessages.h) sent by the CNetServer. 29 65 */ 30 constg_NetMessageTypes = {66 var g_NetMessageTypes = { 31 67 "netstatus": msg => handleNetStatusMessage(msg), 32 68 "netwarn": msg => addNetworkWarning(msg), 33 69 "gamesetup": msg => handleGamesetupMessage(msg), 34 70 "players": msg => handlePlayerAssignmentMessage(msg), 35 71 "ready": msg => handleReadyMessage(msg), … … const g_NetMessageTypes = { 37 73 "kicked": msg => addChatMessage({ "type": "kicked", "username": msg.username }), 38 74 "banned": msg => addChatMessage({ "type": "banned", "username": msg.username }), 39 75 "chat": msg => addChatMessage({ "type": "chat", "guid": msg.guid, "text": msg.text }) 40 76 }; 41 77 42 constg_FormatChatMessage = {78 var g_FormatChatMessage = { 43 79 "system": (msg, user) => systemMessage(msg.text), 44 80 "settings": (msg, user) => systemMessage(translate('Game settings have been changed')), 45 81 "connect": (msg, user) => systemMessage(sprintf(translate("%(username)s has joined"), { "username": user })), 46 82 "disconnect": (msg, user) => systemMessage(sprintf(translate("%(username)s has left"), { "username": user })), 47 83 "kicked": (msg, user) => systemMessage(sprintf(translate("%(username)s has been kicked"), { "username": user })), … … const g_FormatChatMessage = { 57 93 "username": user 58 94 }), 59 95 "clientlist": (msg, user) => getUsernameList() 60 96 }; 61 97 62 /** 63 * The dropdownlist items will appear in the order they are added. 64 */ 65 const g_MapFilters = [ 98 var g_MapFilters = prepareForDropdown([ 66 99 { 67 100 "id": "default", 68 101 "name": translate("Default"), 69 "filter": mapKeywords => mapKeywords.every(keyword => ["naval", "demo", "hidden"].indexOf(keyword) == -1) 102 "filter": mapKeywords => mapKeywords.every(keyword => ["naval", "demo", "hidden"].indexOf(keyword) == -1), 103 "Default": true 70 104 }, 71 105 { 72 106 "id": "naval", 73 107 "name": translate("Naval Maps"), 74 108 "filter": mapKeywords => mapKeywords.indexOf("naval") != -1 … … const g_MapFilters = [ 86 120 { 87 121 "id": "all", 88 122 "name": translate("All Maps"), 89 123 "filter": mapKeywords => true 90 124 } 91 ] ;125 ]); 92 126 93 127 /** 94 128 * Used for generating the botnames. 95 129 */ 96 130 const g_RomanNumbers = [undefined, "I", "II", "III", "IV", "V", "VI", "VII", "VIII"]; 97 131 98 132 /** 99 * Offer users to select playable civs only.100 * Load unselectable civs as they could appear in scenario maps.101 */102 const g_CivData = loadCivData();103 104 /**105 133 * Used for highlighting the sender of chat messages. 106 134 */ 107 135 const g_SenderFont = "sans-bold-13"; 108 136 109 137 /** 110 * Highlight the "random" dropdownlist item.111 */112 const g_ColorRandom = "orange";113 114 /**115 138 * Highlight AIs in the player-dropdownlist. 116 139 */ 117 140 const g_AIColor = "70 150 70"; 118 141 119 142 /** … … const g_ReadyColor = "green"; 135 158 * Highlights the victory condition in the game-description. 136 159 */ 137 160 const g_VictoryColor = "orange"; 138 161 139 162 /** 140 * Placeholder item for the map-dropdownlist.141 */142 const g_RandomMap = '[color="' + g_ColorRandom + '"]' + translateWithContext("map type", "Random") + "[/color]";143 144 /**145 * Placeholder item for the civ-dropdownlists.146 */147 const g_RandomCiv = '[color="' + g_ColorRandom + '"]' + translateWithContext("civilization", "Random") + '[/color]';148 149 /**150 163 * Whether this is a single- or multiplayer match. 151 164 */ 152 165 var g_IsNetworked; 153 166 154 167 /** … … var g_DefaultPlayerData = []; 198 211 var g_GameAttributes = { "settings": {} }; 199 212 200 213 var g_ChatMessages = []; 201 214 202 215 /** 203 * Cache containing the mapsettings for scenario/skirmish maps. Just-in-time loading. 216 * Filename and translated title of all maps, given the currently selected 217 * maptype and filter. Sorted by title, shown in the dropdown. 218 */ 219 var g_MapList = []; 220 221 /** 222 * Cache containing the mapsettings. Just-in-time loading. 204 223 */ 205 224 var g_MapData = {}; 206 225 207 226 /** 208 227 * Wait one tick before initializing the GUI objects and … … var g_LastGameStanza; 217 236 218 237 /** 219 238 * Remembers if the current player viewed the AI settings of some playerslot. 220 239 */ 221 240 var g_LastViewedAIPlayer = -1; 241 /** 242 * Contains the logic of all multiple-choice gamesettings. 243 * 244 * Hidden - if so, both label and dropdown won't be visible. 245 * Enabled - Only the label will be shown if it's disabled. 246 * Default - returns the index of the default value (not the value itself). 247 * 248 * NOTICE: The first three elements need to be initialized first. 249 * If the map is changed, missing values are supplemented with defaults. 250 */ 251 var g_Dropdowns = g_Settings && { 252 "mapType": { 253 "labels": () => g_MapTypes.Title, 254 "ids": () => g_MapTypes.Name, 255 "default": () => g_MapTypes.Default, 256 "defined": () => g_GameAttributes.mapType !== undefined, 257 "get": () => g_GameAttributes.mapType, 258 "select": idx => { 259 260 g_MapData = {}; 261 262 g_GameAttributes.mapType = g_MapTypes.Name[idx]; 263 g_GameAttributes.mapPath = g_MapPath[g_GameAttributes.mapType]; 264 delete g_GameAttributes.map; 265 266 if (g_GameAttributes.mapType != "scenario") 267 g_GameAttributes.settings = { 268 "PlayerData": g_DefaultPlayerData.slice(0, 4) 269 }; 270 271 reloadMapList(); 272 supplementDefaults(); 273 } 274 }, 275 "mapFilter": { 276 "labels": () => g_MapFilters.name, 277 "ids": () => g_MapFilters.id, 278 "default": () => g_MapFilters.Default, 279 "defined": () => g_GameAttributes.mapFilter !== undefined, 280 "get": () => g_GameAttributes.mapFilter, 281 "select": idx => { 282 g_GameAttributes.mapFilter = g_MapFilters.id[idx]; 283 delete g_GameAttributes.map; 284 reloadMapList(); 285 supplementDefaults(); 286 } 287 }, 288 "mapSelection": { 289 "labels": () => g_MapList.name, 290 "ids": () => g_MapList.file, 291 "default": () => 0, 292 "defined": () => g_GameAttributes.map !== undefined, 293 "get": () => g_GameAttributes.map, 294 "select": idx => { 295 selectMap(g_MapList.file[idx]); 296 supplementDefaults(); 297 } 298 }, 299 "mapSize": { 300 "labels": () => g_MapSizes.LongName, 301 "ids": () => g_MapSizes.Tiles, 302 "default": () => g_MapSizes.Default, 303 "defined": () => g_GameAttributes.settings.Size !== undefined, 304 "get": () => g_GameAttributes.settings.Size, 305 "select": idx => { 306 g_GameAttributes.settings.Size = g_MapSizes.Tiles[idx]; 307 }, 308 "maps": ["random"] 309 }, 310 "numPlayers": { 311 "labels": () => g_PlayerArray, 312 "ids": () => g_PlayerArray, 313 "default": () => g_MaxPlayers - 1, 314 "defined": () => g_GameAttributes.settings.PlayerData !== undefined, 315 "get": () => g_GameAttributes.settings.PlayerData.length, 316 "select": idx => { 317 selectNumPlayers(idx + 1); 318 }, 319 "maps": ["random"] 320 }, 321 "populationCap": { 322 "labels": () => g_PopulationCapacities.Title, 323 "ids": () => g_PopulationCapacities.Population, 324 "default": () => g_PopulationCapacities.Default, 325 "defined": () => g_GameAttributes.settings.PopulationCap !== undefined, 326 "get": () => g_GameAttributes.settings.PopulationCap, 327 "select": idx => { 328 g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[idx]; 329 }, 330 "maps": ["random", "skirmish"] 331 }, 332 "startingResources": { 333 "labels": () => g_StartingResources.Title, 334 "ids": () => g_StartingResources.Resources, 335 "default": () => g_StartingResources.Default, 336 "defined": () => g_GameAttributes.settings.StartingResources !== undefined, 337 "get": () => g_GameAttributes.settings.StartingResources, 338 "select": idx => { 339 g_GameAttributes.settings.StartingResources = g_StartingResources.Resources[idx]; 340 }, 341 "maps": ["random", "skirmish"] 342 }, 343 "ceasefire": { 344 "labels": () => g_Ceasefire.Title, 345 "ids": () => g_Ceasefire.Duration, 346 "default": () => g_Ceasefire.Default, 347 "defined": () => g_GameAttributes.settings.Ceasefire !== undefined, 348 "get": () => g_GameAttributes.settings.Ceasefire, 349 "select": idx => { 350 // TODO for (let ai of ) 351 g_GameAttributes.settings.Ceasefire = g_Ceasefire.Duration[idx]; 352 }, 353 "maps": ["random", "skirmish"] 354 }, 355 "victoryCondition": { 356 "labels": () => g_VictoryConditions.Title, 357 "ids": () => g_VictoryConditions.Name, 358 "default": () => g_VictoryConditions.Default, 359 "defined": () => g_GameAttributes.settings.GameType !== undefined, 360 "get": () => g_GameAttributes.settings.GameType, 361 "select": idx => { 362 g_GameAttributes.settings.GameType = g_VictoryConditions.Name[idx]; 363 g_GameAttributes.settings.VictoryScripts = g_VictoryConditions.Scripts[idx]; 364 }, 365 "maps": ["random", "skirmish"] 366 }, 367 "wonderDuration": { 368 "labels": () => g_WonderDurations.Title, 369 "ids": () => g_WonderDurations.Duration, 370 "default": () => g_WonderDurations.Default, 371 "defined": () => g_GameAttributes.settings.WonderDuration !== undefined, 372 "get": () => g_GameAttributes.settings.WonderDuration, 373 "select": idx => { 374 g_GameAttributes.settings.WonderDuration = g_WonderDurations.Duration[idx]; 375 }, 376 "maps": ["random", "skirmish"] 377 }, 378 "gameSpeed": { 379 "labels": () => g_GameSpeeds.Title, 380 "ids": () => g_GameSpeeds.Speed, 381 "default": () => g_GameSpeeds.Default, 382 "defined": () => g_GameAttributes.gameSpeed !== undefined, 383 "get": () => g_GameAttributes.gameSpeed, 384 "select": idx => { 385 g_GameAttributes.gameSpeed = g_GameSpeeds.Speed[idx]; 386 } 387 } 388 }; 389 var g_HostNameList = []; 390 var g_HostGUIDList = []; 391 392 var g_DropdownArrays = { 393 "playerAssignment": { 394 "labels": (idx) => g_HostNameList, 395 "ids": (idx) => g_HostGUIDList, 396 "default": (idx) => 0, // TODO: AI player 397 "defined": (idx) => true, 398 "get": (idx) => 0,// TODO 399 "select": (idx, selectedIdx) => { 400 401 // TODO: text had shown translate("Loading...") on init is that relevant? 402 let guid = g_HostGUIDList[selectedIdx]; 403 if (!guid || guid.substr(0, 3) == "ai:") 404 { 405 if (g_IsNetworked) 406 Engine.AssignNetworkPlayer(playerID, ""); 407 408 g_GameAttributes.settings.PlayerData[idx].AI = guid ? guid.substr(3) : ""; 409 } 410 else 411 swapPlayers(guid, idx); 412 413 updateGameAttributes(); 414 updateReadyUI(); 415 } 416 }, 417 "playerTeam": { 418 "labels": (idx) => g_TeamsArray.label, 419 "ids": (idx) => g_TeamsArray.id, 420 "default": (idx) => 0, 421 "defined": (idx) => g_GameAttributes.settings.PlayerData[idx].Team !== undefined, 422 "get": (idx) => warn(idx),//g_GameAttributes.settings.PlayerData[idx].Team, 423 "select": (idx, selectedIdx) => { 424 g_GameAttributes.settings.PlayerData[idx].Team = selectedIdx - 1; 425 }, 426 "maps": ["random", "skirmish"] 427 }, 428 "playerCiv": { 429 "labels": (idx) => g_CivList.name, 430 "ids": (idx) => g_CivList.code, 431 "default": (idx) => 0, 432 "defined": (idx) => g_GameAttributes.settings.PlayerData[idx].Civ !== undefined, 433 "get": (idx) => g_GameAttributes.settings.PlayerData[idx].Civ, 434 "select": (idx, selectedIdx) => { 435 g_GameAttributes.settings.PlayerData[idx].Civ = g_CivList.code[selectedIdx]; 436 }, 437 "maps": ["random", "skirmish"] 438 }, 439 "playerColorPicker": { 440 "labels": (idx) => g_PlayerColors.map(color => ' ' + '[color="' + rgbToGuiColor(color) + '"]■[/color]'), 441 "ids": (idx) => g_PlayerColors.map((color, index) => index), 442 "default": (idx) => idx, 443 "defined": (idx) => g_GameAttributes.settings.PlayerData[idx].Color !== undefined, 444 "get": (idx) => g_GameAttributes.settings.PlayerData[idx].Color, 445 "select": (idx, selectedIdx) => { 446 let playerData = g_GameAttributes.settings.PlayerData; 447 448 // If someone else has that color, give that player the old color 449 let pData = playerData.find(pData => sameColor(g_PlayerColors[selectedIdx], pData.Color)); 450 if (pData) 451 pData.Color = playerData[idx].Color; 452 453 playerData[idx].Color = g_PlayerColors[selectedIdx]; 454 ensureUniquePlayerColors(playerData); 455 }, 456 "maps": ["random", "skirmish"] 457 } 458 }; 459 460 /** 461 * Contains the logic of all boolean gamesettings. 462 */ 463 var g_Checkboxes = { 464 "revealMap": { 465 "default": () => false, 466 "defined": () => g_GameAttributes.settings.RevealMap !== undefined, 467 "get": () => g_GameAttributes.settings.RevealMap, 468 "set": checked => { 469 g_GameAttributes.settings.RevealMap = checked; 470 }, 471 "maps": ["random", "skirmish"] 472 }, 473 "exploreMap": { 474 "default": () => false, 475 "defined": () => g_GameAttributes.settings.ExploreMap !== undefined, 476 "get": () => g_GameAttributes.settings.ExploreMap, 477 "set": checked => { 478 g_GameAttributes.settings.ExploreMap = checked; 479 }, 480 "maps": ["random", "skirmish"] 481 }, 482 "disableTreasures": { 483 "default": () => false, 484 "defined": () => g_GameAttributes.settings.DisableTreasures !== undefined, 485 "get": () => g_GameAttributes.settings.DisableTreasures, 486 "set": checked => { 487 g_GameAttributes.settings.DisableTreasures = checked; 488 }, 489 "maps": ["random", "skirmish"] 490 }, 491 "lockTeams": { 492 "default": () => Engine.HasXmppClient(), 493 "defined": () => g_GameAttributes.settings.LockTeams !== undefined, 494 "get": () => g_GameAttributes.settings.LockTeams, 495 "set": checked => { 496 g_GameAttributes.settings.LockTeams = checked; 497 }, 498 "maps": ["random", "skirmish"], 499 "enabled": () => !g_GameAttributes.settings.RatingEnabled 500 }, 501 "enableCheats": { 502 "default": () => !g_IsNetworked, 503 "hidden": () => !g_IsNetworked, 504 "defined": () => g_GameAttributes.settings.CheatsEnabled !== undefined, 505 "get": () => g_GameAttributes.settings.CheatsEnabled, 506 "set": checked => { 507 g_GameAttributes.settings.CheatsEnabled = !g_IsNetworked || 508 checked && !g_GameAttributes.settings.RatingEnabled; 509 }, 510 "enabled": () => !g_GameAttributes.settings.RatingEnabled 511 }, 512 "enableRating": { 513 "default": () => Engine.HasXmppClient(), 514 "defined": () => g_GameAttributes.settings.RatingEnabled !== undefined, 515 "get": () => !!g_GameAttributes.settings.RatingEnabled, 516 "set": checked => { 517 g_GameAttributes.settings.RatingEnabled = Engine.HasXmppClient() ? checked : undefined; 518 Engine.SetRankedGame(!!g_GameAttributes.settings.RatingEnabled); 519 } 520 } 521 }; 522 523 /** 524 * For setting up some additional GUI objects. 525 */ 526 var g_MiscControls = { 527 "chatPanel": { 528 "hidden": () => !g_IsNetworked 529 }, 530 "optionCheats": { 531 "hidden": () => !g_IsNetworked 532 }, 533 "optionRating": { 534 "hidden": () => !Engine.HasXmppClient() 535 }, 536 "optionWonderDuration": { 537 "hidden": () => g_GameAttributes.settings.GameType != "wonder" 538 }, 539 "cheatWarningText": { 540 "hidden": () => !g_IsNetworked || !g_GameAttributes.settings.CheatsEnabled 541 }, 542 "mapSize": { 543 "hidden": () => g_GameAttributes.mapType != "random" || !g_IsController 544 }, 545 "mapSizeText": { 546 "hidden": () => g_GameAttributes.mapType != "random" || g_IsController 547 }, 548 "mapSizeDesc": { 549 "hidden": () => g_GameAttributes.mapType != "random" 550 }, 551 "cancelGame": { 552 "tooltip": () => Engine.HasXmppClient() ? 553 translate("Return to the lobby.") : 554 translate("Return to the main menu.") 555 }, 556 "startGame": { 557 "enabled": () => !g_IsController || 558 Object.keys(g_PlayerAssignments).every(guid => g_PlayerAssignments[guid].status || 559 g_PlayerAssignments[guid].player == -1) 560 }, 561 "civResetButton": { 562 "hidden": () => g_GameAttributes.mapType == "scenario" || !g_IsController 563 } 564 }; 565 566 var g_MiscControlArrays = { 567 "playerBox": { 568 "size": (idx) => ({ 569 "left": 0, 570 "right": "100%", 571 "top": 32 * idx, 572 "bottom": 32 * (idx + 1) 573 }) 574 }, 575 "playerName": { 576 "caption": (idx) => { 577 // TODO if (g_PlayerAssignments[message.guid].status) green 578 return translate(g_DefaultPlayerData[idx].Name); 579 } 580 }, 581 "playerColor": { 582 "sprite": (idx) => "color:" + rgbToGuiColor(g_GameAttributes.settings.PlayerData[idx].Color) + " 100" 583 }, 584 "playerConfig": { 585 "hidden": (idx) => g_GameAttributes.settings.PlayerData[idx].AI != "", 586 "onPress": (idx) => { 587 openAIConfig(idx); 588 } 589 } 590 }; 222 591 223 592 /** 224 593 * Initializes some globals without touching the GUI. 225 594 * 226 595 * @param {Object} attribs - context data sent by the lobby / mainmenu … … function init(attribs) 255 624 g_DefaultPlayerData = g_Settings.PlayerDefaults; 256 625 g_DefaultPlayerData.shift(); 257 626 for (let i in g_DefaultPlayerData) 258 627 g_DefaultPlayerData[i].Civ = "random"; 259 628 629 supplementDefaults(); 630 260 631 setTimeout(displayGamestateNotifications, 1000); 261 632 } 262 633 263 634 /** 635 * Sets default values for all g_GameAttribute settings which don't have a value set. 636 */ 637 function supplementDefaults() 638 { 639 for (let dropdown in g_Dropdowns) 640 if (!g_Dropdowns[dropdown].defined()) 641 g_Dropdowns[dropdown].select(g_Dropdowns[dropdown]["default"]()); 642 643 for (let checkbox in g_Checkboxes) 644 if (!g_Checkboxes[checkbox].defined()) 645 g_Checkboxes[checkbox].set(g_Checkboxes[checkbox]["default"]()); 646 647 for (let dropdown in g_DropdownArrays) 648 for (let i = 0; i < g_GameAttributes.settings.PlayerData.length; ++i) 649 if (!g_DropdownArrays[dropdown].defined(i)) 650 g_DropdownArrays[dropdown].select(g_DropdownArrays[dropdown]["default"](i)); 651 } 652 653 /** 264 654 * Called after the first tick. 265 655 */ 266 656 function initGUIObjects() 267 657 { 268 Engine.GetGUIObjectByName("cancelGame").tooltip = Engine.HasXmppClient() ? translate("Return to the lobby.") : translate("Return to the main menu."); 269 270 initCivNameList(); 271 initMapTypes(); 272 initMapFilters(); 273 274 if (g_IsController) 275 { 276 g_GameAttributes.settings.CheatsEnabled = !g_IsNetworked; 277 g_GameAttributes.settings.RatingEnabled = Engine.IsRankedGame() || undefined; 658 for (let dropdown in g_Dropdowns) 659 initDropdown(dropdown); 278 660 279 initMapNameList(); 280 initNumberOfPlayers(); 281 initGameSpeed(); 282 initPopulationCaps(); 283 initStartingResources(); 284 initCeasefire(); 285 initWonderDurations(); 286 initVictoryConditions(); 287 initMapSizes(); 288 initRadioButtons(); 289 } 290 else 291 hideControls(); 661 for (let checkbox in g_Checkboxes) 662 initCheckbox(checkbox); 292 663 293 initMultiplayerSettings();294 initPlayerAssignments();664 for (let dropdown in g_DropdownArrays) 665 initDropdownArray(dropdown); 295 666 296 667 resizeMoreOptionsWindow(); 297 668 298 669 if (g_IsNetworked) 299 670 Engine.GetGUIObjectByName("chatInput").focus(); … … function initGUIObjects() 305 676 warn("initGUIObjects() called while in GUI update"); 306 677 updateGameAttributes(); 307 678 } 308 679 } 309 680 310 function init MapTypes()681 function initDropdown(name, idx) 311 682 { 312 let mapTypes = Engine.GetGUIObjectByName("mapType"); 313 mapTypes.list = g_MapTypes.Title; 314 mapTypes.list_data = g_MapTypes.Name; 315 mapTypes.onSelectionChange = function() { 316 if (this.selected != -1) 317 selectMapType(this.list_data[this.selected]); 318 }; 319 if (g_IsController) 320 mapTypes.selected = g_MapTypes.Default; 683 let idxName = idx === undefined ? "": "[" + idx + "]"; 684 let data = (idx === undefined ? g_Dropdowns : g_DropdownArrays)[name]; 685 686 let dropdown = Engine.GetGUIObjectByName(name + idxName); 687 dropdown.list = data.labels(idx); 688 dropdown.list_data = data.ids(idx); 689 690 //function(data) { 691 dropdown.onSelectionChange = function() { 692 693 if (!g_IsController || 694 g_IsInGuiUpdate || 695 !this.list_data[this.selected] || 696 data.hidden && data.hidden(idx) || 697 data.enabled && !data.enabled(idx) || 698 data.maps && data.maps.indexOf(g_GameAttributes.mapType) == -1) 699 return; 700 701 data.select(this.selected); 702 updateGameAttributes(); 703 }; 704 //}(data, idx); 321 705 } 322 706 323 function init MapFilters()707 function initCheckbox(name) 324 708 { 325 let mapFilters = Engine.GetGUIObjectByName("mapFilter"); 326 mapFilters.list = g_MapFilters.map(mapFilter => mapFilter.name); 327 mapFilters.list_data = g_MapFilters.map(mapFilter => mapFilter.id); 328 mapFilters.onSelectionChange = function() { 329 if (this.selected != -1) 330 selectMapFilter(this.list_data[this.selected]); 709 Engine.GetGUIObjectByName(name).onPress = function() { 710 711 let obj = g_Checkboxes[this.name]; 712 713 if (!g_IsController || 714 g_IsInGuiUpdate || 715 obj.enabled && !obj.enabled() || 716 obj.hidden && obj.hidden()) 717 return; 718 719 obj.set(this.checked); 720 updateGameAttributes(); 331 721 }; 332 if (g_IsController) 333 mapFilters.selected = 0; 334 g_GameAttributes.mapFilter = "default"; 722 } 723 724 function initDropdownArray(name) 725 { 726 for (let i = 0; i < g_MaxPlayers; ++i) 727 initDropdown(name, i); 335 728 } 336 729 337 730 /** 338 731 * Remove empty space in case of hidden options (like cheats, rating or wonder duration) 339 732 */ 340 733 function resizeMoreOptionsWindow() 341 734 { 342 735 const elementHeight = 30; 343 736 737 let moreOptions = Engine.GetGUIObjectByName("moreOptions"); 344 738 let yPos = undefined; 345 739 346 for (let guiOption of Engine.GetGUIObjectByName("moreOptions").children)740 for (let guiOption of moreOptions.children) 347 741 { 348 742 if (guiOption.name == "moreOptionsLabel") 349 743 continue; 350 744 351 745 let gSize = guiOption.size; … … function resizeMoreOptionsWindow() 360 754 361 755 yPos += elementHeight; 362 756 } 363 757 364 758 // Resize the vertically centered window containing the options 365 let moreOptions = Engine.GetGUIObjectByName("moreOptions");366 759 let mSize = moreOptions.size; 367 760 mSize.bottom = mSize.top + yPos + 20; 368 761 moreOptions.size = mSize; 369 762 } 370 763 371 function initNumberOfPlayers()372 {373 let playersArray = Array(g_MaxPlayers).fill(0).map((v, i) => i + 1); // 1, 2, ..., MaxPlayers374 let numPlayers = Engine.GetGUIObjectByName("numPlayers");375 numPlayers.list = playersArray;376 numPlayers.list_data = playersArray;377 numPlayers.onSelectionChange = function() {378 if (this.selected != -1)379 selectNumPlayers(this.list_data[this.selected]);380 };381 numPlayers.selected = g_MaxPlayers - 1;382 }383 384 function initGameSpeed()385 {386 let gameSpeed = Engine.GetGUIObjectByName("gameSpeed");387 gameSpeed.hidden = false;388 Engine.GetGUIObjectByName("gameSpeedText").hidden = true;389 gameSpeed.list = g_GameSpeeds.Title;390 gameSpeed.list_data = g_GameSpeeds.Speed;391 gameSpeed.onSelectionChange = function() {392 if (this.selected != -1)393 g_GameAttributes.gameSpeed = g_GameSpeeds.Speed[this.selected];394 395 updateGameAttributes();396 };397 gameSpeed.selected = g_GameSpeeds.Default;398 }399 400 function initPopulationCaps()401 {402 let populationCaps = Engine.GetGUIObjectByName("populationCap");403 populationCaps.list = g_PopulationCapacities.Title;404 populationCaps.list_data = g_PopulationCapacities.Population;405 populationCaps.selected = g_PopulationCapacities.Default;406 populationCaps.onSelectionChange = function() {407 if (this.selected != -1)408 g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[this.selected];409 410 updateGameAttributes();411 };412 }413 414 function initStartingResources()415 {416 let startingResourcesL = Engine.GetGUIObjectByName("startingResources");417 startingResourcesL.list = g_StartingResources.Title;418 startingResourcesL.list_data = g_StartingResources.Resources;419 startingResourcesL.selected = g_StartingResources.Default;420 startingResourcesL.onSelectionChange = function() {421 if (this.selected != -1)422 g_GameAttributes.settings.StartingResources = g_StartingResources.Resources[this.selected];423 424 updateGameAttributes();425 };426 }427 428 function initCeasefire()429 {430 let ceasefireL = Engine.GetGUIObjectByName("ceasefire");431 ceasefireL.list = g_Ceasefire.Title;432 ceasefireL.list_data = g_Ceasefire.Duration;433 ceasefireL.selected = g_Ceasefire.Default;434 ceasefireL.onSelectionChange = function() {435 if (this.selected != -1)436 g_GameAttributes.settings.Ceasefire = g_Ceasefire.Duration[this.selected];437 438 updateGameAttributes();439 };440 }441 442 function initVictoryConditions()443 {444 let victoryConditions = Engine.GetGUIObjectByName("victoryCondition");445 victoryConditions.list = g_VictoryConditions.Title;446 victoryConditions.list_data = g_VictoryConditions.Name;447 victoryConditions.onSelectionChange = function() {448 if (this.selected != -1)449 {450 g_GameAttributes.settings.GameType = g_VictoryConditions.Name[this.selected];451 g_GameAttributes.settings.VictoryScripts = g_VictoryConditions.Scripts[this.selected];452 }453 454 updateGameAttributes();455 };456 victoryConditions.selected = g_VictoryConditions.Default;457 }458 459 function initWonderDurations()460 {461 let wonderConditions = Engine.GetGUIObjectByName("wonderDuration");462 wonderConditions.list = g_WonderDurations.Title;463 wonderConditions.list_data = g_WonderDurations.Duration;464 wonderConditions.onSelectionChange = function()465 {466 if (this.selected != -1)467 g_GameAttributes.settings.WonderDuration = g_WonderDurations.Duration[this.selected];468 469 updateGameAttributes();470 };471 wonderConditions.selected = g_WonderDurations.Default;472 }473 474 function initMapSizes()475 {476 let mapSize = Engine.GetGUIObjectByName("mapSize");477 mapSize.list = g_MapSizes.LongName;478 mapSize.list_data = g_MapSizes.Tiles;479 mapSize.onSelectionChange = function() {480 if (this.selected != -1)481 g_GameAttributes.settings.Size = g_MapSizes.Tiles[this.selected];482 updateGameAttributes();483 };484 mapSize.selected = 0;485 }486 487 /**488 * Assign update-functions to all checkboxes.489 */490 function initRadioButtons()491 {492 let options = {493 "RevealMap": "revealMap",494 "ExploreMap": "exploreMap",495 "DisableTreasures": "disableTreasures",496 "LockTeams": "lockTeams",497 "CheatsEnabled": "enableCheats"498 };499 500 Object.keys(options).forEach(attribute => {501 Engine.GetGUIObjectByName(options[attribute]).onPress = function() {502 g_GameAttributes.settings[attribute] = this.checked;503 updateGameAttributes();504 };505 });506 507 Engine.GetGUIObjectByName("enableRating").onPress = function() {508 g_GameAttributes.settings.RatingEnabled = this.checked;509 Engine.SetRankedGame(this.checked);510 Engine.GetGUIObjectByName("enableCheats").enabled = !this.checked;511 Engine.GetGUIObjectByName("lockTeams").enabled = !this.checked;512 updateGameAttributes();513 };514 }515 516 /**517 * If we're a network client, hide the controls and show the text instead.518 */519 function hideControls()520 {521 for (let ctrl of ["mapType", "mapFilter", "mapSelection", "victoryCondition", "gameSpeed", "numPlayers"])522 hideControl(ctrl, ctrl + "Text");523 524 // TODO: Shouldn't players be able to choose their own assignment?525 for (let i = 0; i < g_MaxPlayers; ++i)526 {527 Engine.GetGUIObjectByName("playerAssignment["+i+"]").hidden = true;528 Engine.GetGUIObjectByName("playerCiv["+i+"]").hidden = true;529 Engine.GetGUIObjectByName("playerTeam["+i+"]").hidden = true;530 }531 532 Engine.GetGUIObjectByName("startGame").enabled = true;533 }534 535 /**536 * Hides the GUI controls for clients and shows the read-only label instead.537 *538 * @param {string} control - name of the GUI object able to change a setting539 * @param {string} label - name of the GUI object displaying a setting540 * @param {boolean} [allowControl] - Whether the current user is allowed to change the control.541 */542 function hideControl(control, label, allowControl = g_IsController)543 {544 Engine.GetGUIObjectByName(control).hidden = !allowControl;545 Engine.GetGUIObjectByName(label).hidden = allowControl;546 }547 548 /**549 * Checks a boolean checkbox for the host and sets the text of the label for the client.550 *551 * @param {string} control - name of the GUI object able to change a setting552 * @param {string} label - name of the GUI object displaying a setting553 * @param {boolean} checked - Whether the setting is active / enabled.554 */555 function setGUIBoolean(control, label, checked)556 {557 Engine.GetGUIObjectByName(control).checked = checked;558 Engine.GetGUIObjectByName(label).caption = checked ? translate("Yes") : translate("No");559 }560 561 /**562 * Hide and set some elements depending on whether we play single- or multiplayer.563 */564 function initMultiplayerSettings()565 {566 Engine.GetGUIObjectByName("chatPanel").hidden = !g_IsNetworked;567 Engine.GetGUIObjectByName("optionCheats").hidden = !g_IsNetworked;568 Engine.GetGUIObjectByName("optionRating").hidden = !Engine.HasXmppClient();569 570 Engine.GetGUIObjectByName("enableCheats").enabled = !Engine.IsRankedGame();571 Engine.GetGUIObjectByName("lockTeams").enabled = !Engine.IsRankedGame();572 573 Engine.GetGUIObjectByName("enableCheats").checked = g_GameAttributes.settings.CheatsEnabled;574 Engine.GetGUIObjectByName("enableRating").checked = !!g_GameAttributes.settings.RatingEnabled;575 576 for (let ctrl of ["enableCheats", "enableRating"])577 hideControl(ctrl, ctrl + "Text");578 }579 580 /**581 * Populate team-, color- and civ-dropdowns.582 */583 function initPlayerAssignments()584 {585 let boxSpacing = 32;586 for (let i = 0; i < g_MaxPlayers; ++i)587 {588 let box = Engine.GetGUIObjectByName("playerBox["+i+"]");589 let boxSize = box.size;590 let h = boxSize.bottom - boxSize.top;591 boxSize.top = i * boxSpacing;592 boxSize.bottom = i * boxSpacing + h;593 box.size = boxSize;594 595 let team = Engine.GetGUIObjectByName("playerTeam["+i+"]");596 let teamsArray = Array(g_MaxTeams).fill(0).map((v, i) => i + 1); // 1, 2, ... MaxTeams597 team.list = [translateWithContext("team", "None")].concat(teamsArray); // "None", 1, 2, ..., maxTeams598 team.list_data = [-1].concat(teamsArray.map(team => team - 1)); // -1, 0, ..., (maxTeams-1)599 team.selected = 0;600 601 let playerSlot = i; // declare for inner function use602 team.onSelectionChange = function() {603 if (this.selected != -1)604 g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;605 606 updateGameAttributes();607 };608 609 let colorPicker = Engine.GetGUIObjectByName("playerColorPicker["+i+"]");610 colorPicker.list = g_PlayerColors.map(color => ' ' + '[color="' + rgbToGuiColor(color) + '"]■[/color]');611 colorPicker.list_data = g_PlayerColors.map((color, index) => index);612 colorPicker.selected = -1;613 colorPicker.onSelectionChange = function() { selectPlayerColor(playerSlot, this.selected); };614 615 Engine.GetGUIObjectByName("playerCiv["+i+"]").onSelectionChange = function() {616 if ((this.selected != -1)&&(g_GameAttributes.mapType !== "scenario"))617 g_GameAttributes.settings.PlayerData[playerSlot].Civ = this.list_data[this.selected];618 619 updateGameAttributes();620 };621 }622 }623 624 764 /** 625 765 * Called when the client disconnects. 626 766 * The other cases from NetClient should never occur in the gamesetup. 627 767 * @param {Object} message 628 768 */ … … function getSetting(settings, defaults, 790 930 791 931 return undefined; 792 932 } 793 933 794 934 /** 795 * Initialize the dropdowns containing all selectable civs (including random).796 */797 function initCivNameList()798 {799 let civList = Object.keys(g_CivData).filter(civ => g_CivData[civ].SelectableInGameSetup).map(civ => ({ "name": g_CivData[civ].Name, "code": civ })).sort(sortNameIgnoreCase);800 let civListNames = [g_RandomCiv].concat(civList.map(civ => civ.name));801 let civListCodes = ["random"].concat(civList.map(civ => civ.code));802 803 for (let i = 0; i < g_MaxPlayers; ++i)804 {805 let civ = Engine.GetGUIObjectByName("playerCiv["+i+"]");806 civ.list = civListNames;807 civ.list_data = civListCodes;808 civ.selected = 0;809 }810 }811 812 /**813 935 * Initialize the dropdown containing all maps for the selected maptype and mapfilter. 814 936 */ 815 function initMapNameList()937 function reloadMapList() 816 938 { 817 939 if (!g_MapPath[g_GameAttributes.mapType]) 818 940 { 819 941 error("Unexpected map type: " + g_GameAttributes.mapType); 820 942 return; … … function initMapNameList() 822 944 823 945 let mapFiles = g_GameAttributes.mapType == "random" ? 824 946 getJSONFileList(g_GameAttributes.mapPath) : 825 947 getXMLFileList(g_GameAttributes.mapPath); 826 948 827 // Apply map filter, if any defined 949 if (g_GameAttributes.mapType == "random") 950 mapList.push({ 951 "file": "random", 952 "name": '[color="' + g_ColorRandom + '"]' + translateWithContext("map type", "Random") + "[/color]" 953 }); 954 828 955 // TODO: Should verify these are valid maps before adding to list 956 829 957 let mapList = []; 830 958 for (let mapFile of mapFiles) 831 959 { 832 960 let file = g_GameAttributes.mapPath + mapFile; 833 961 let mapData = loadMapData(file); 834 let mapFilter = g_MapFilters.find(mapFilter => mapFilter.id == (g_GameAttributes.mapFilter || "all")); 962 let filterID = g_MapFilters.id.find(filter => filter == g_GameAttributes.mapFilter); 963 let mapFilter = g_MapFilters.filter[filterID] || undefined; 835 964 836 if (!!mapData.settings && mapFilter && mapFilter.filter(mapData.settings.Keywords || [])) 837 mapList.push({ "name": getMapDisplayName(file), "file": file }); 838 } 839 840 translateObjectKeys(mapList, ["name"]); 841 mapList.sort(sortNameIgnoreCase); 842 843 let mapListNames = mapList.map(map => map.name); 844 let mapListFiles = mapList.map(map => map.file); 965 if (!mapData.settings || mapFilter && !mapFilter(mapData.settings.Keywords || [])) 966 continue; 845 967 846 // Scenario/skirmish maps have a fixed playercount 847 if (g_GameAttributes.mapType == "random") 848 { 849 mapListNames.unshift(g_RandomMap); 850 mapListFiles.unshift("random"); 968 mapList.push({ 969 "file": file, 970 "name": translate(getMapDisplayName(file)) 971 }); 851 972 } 852 973 853 let mapSelectionBox = Engine.GetGUIObjectByName("mapSelection"); 854 mapSelectionBox.list = mapListNames; 855 mapSelectionBox.list_data = mapListFiles; 856 mapSelectionBox.onSelectionChange = function() { 857 if (this.selected != -1) 858 selectMap(this.list_data[this.selected]); 859 }; 860 mapSelectionBox.selected = Math.max(0, mapListFiles.indexOf(g_GameAttributes.map || "")); 974 g_MapList = prepareForDropdown(mapList.sort(sortNameIgnoreCase)); 975 initDropdown("mapSelection") 861 976 } 862 977 863 978 function loadMapData(name) 864 979 { 865 980 if (!name || !g_MapPath[g_GameAttributes.mapType]) … … function loadPersistMatchSettings() 922 1037 923 1038 if (mapSettings.PlayerData) 924 1039 sanitizePlayerData(mapSettings.PlayerData); 925 1040 926 1041 // Reload, as the maptype or mapfilter might have changed 927 initMapNameList();1042 reloadMapList(); 928 1043 929 1044 g_GameAttributes.settings.RatingEnabled = Engine.HasXmppClient(); 930 1045 Engine.SetRankedGame(g_GameAttributes.settings.RatingEnabled); 931 1046 932 1047 updateGUIObjects(); … … function onTick() 1028 1143 * Called when the host choses the number of players on a random map. 1029 1144 * @param {Number} num 1030 1145 */ 1031 1146 function selectNumPlayers(num) 1032 1147 { 1033 if (g_ IsInGuiUpdate || !g_IsController || g_GameAttributes.mapType != "random")1148 if (g_GameAttributes.mapType != "random") 1034 1149 return; 1035 1150 1036 1151 // Unassign players from nonexistent slots 1037 1152 if (g_IsNetworked) 1038 1153 { … … function selectNumPlayers(num) 1048 1163 g_GameAttributes.settings.PlayerData = pData.slice(0, num); 1049 1164 else 1050 1165 for (let i = pData.length; i < num; ++i) 1051 1166 g_GameAttributes.settings.PlayerData.push(g_DefaultPlayerData[i]); 1052 1167 1053 updateGameAttributes(); 1054 } 1055 1056 /** 1057 * Assigns the given color to that player. 1058 */ 1059 function selectPlayerColor(playerSlot, colorIndex) 1060 { 1061 if (colorIndex == -1) 1062 return; 1063 1064 let playerData = g_GameAttributes.settings.PlayerData; 1065 1066 // If someone else has that color, give that player the old color 1067 let pData = playerData.find(pData => sameColor(g_PlayerColors[colorIndex], pData.Color)); 1068 if (pData) 1069 pData.Color = playerData[playerSlot].Color; 1070 1071 // Assign the new color 1072 playerData[playerSlot].Color = g_PlayerColors[colorIndex]; 1073 1074 // Ensure colors are not used twice after increasing the number of players 1075 ensureUniquePlayerColors(playerData); 1076 1077 if (!g_IsInGuiUpdate) 1078 updateGameAttributes(); 1168 supplementDefaults(); 1079 1169 } 1080 1170 1081 1171 function ensureUniquePlayerColors(playerData) 1082 1172 { 1083 1173 for (let i = playerData.length - 1; i >= 0; --i) 1084 1174 // If someone else has that color, assign an unused color 1085 1175 if (playerData.some((pData, j) => i != j && sameColor(playerData[i].Color, pData.Color))) 1086 1176 playerData[i].Color = g_PlayerColors.find(color => playerData.every(pData => !sameColor(color, pData.Color))); 1087 1177 } 1088 1178 1089 /**1090 * Called when the user selects a map type from the list.1091 *1092 * @param {string} type - scenario, skirmish or random1093 */1094 function selectMapType(type)1095 {1096 if (g_IsInGuiUpdate || !g_IsController)1097 return;1098 1099 if (!g_MapPath[type])1100 {1101 error("selectMapType: Unexpected map type " + type);1102 return;1103 }1104 1105 g_MapData = {};1106 g_GameAttributes.map = "";1107 g_GameAttributes.mapType = type;1108 g_GameAttributes.mapPath = g_MapPath[type];1109 1110 if (type != "scenario")1111 g_GameAttributes.settings = {1112 "PlayerData": g_DefaultPlayerData.slice(0, 4),1113 "CheatsEnabled": g_GameAttributes.settings.CheatsEnabled1114 };1115 1116 initMapNameList();1117 1118 updateGameAttributes();1119 }1120 1121 function selectMapFilter(id)1122 {1123 if (g_IsInGuiUpdate || !g_IsController)1124 return;1125 1126 g_GameAttributes.mapFilter = id;1127 1128 initMapNameList();1129 1130 updateGameAttributes();1131 }1132 1133 1179 function selectMap(name) 1134 1180 { 1135 if (g_IsInGuiUpdate || !g_IsController || !name)1136 return;1137 1138 1181 // Reset some map specific properties which are not necessarily redefined on each map 1139 1182 for (let prop of ["TriggerScripts", "CircularMap", "Garrison"]) 1140 1183 g_GameAttributes.settings[prop] = undefined; 1141 1184 1142 1185 let mapData = loadMapData(name); … … function selectMap(name) 1164 1207 g_GameAttributes.settings[prop] = mapSettings[prop]; 1165 1208 1166 1209 // Use default AI if the map doesn't specify any explicitly 1167 1210 for (let i in g_GameAttributes.settings.PlayerData) 1168 1211 { 1169 if (!('AI' in g_GameAttributes.settings.PlayerData[i])) 1212 // TODO: check for undefined? 1213 if (!g_GameAttributes.settings.PlayerData[i].AI) 1170 1214 g_GameAttributes.settings.PlayerData[i].AI = g_DefaultPlayerData[i].AI; 1171 if (! ('AIDiff' in g_GameAttributes.settings.PlayerData[i]))1215 if (!g_GameAttributes.settings.PlayerData[i].AIDiff) 1172 1216 g_GameAttributes.settings.PlayerData[i].AIDiff = g_DefaultPlayerData[i].AIDiff; 1173 1217 } 1174 1218 1175 1219 if (g_IsNetworked) 1176 1220 // Unassign excess players … … function selectMap(name) 1188 1232 "civ": "", 1189 1233 "team": -1, 1190 1234 "ready": 0 1191 1235 } 1192 1236 }; 1193 1194 updateGameAttributes();1195 1237 } 1196 1238 1197 1239 function launchGame() 1198 1240 { 1199 1241 if (!g_IsController) … … function launchGame() 1298 1340 }); 1299 1341 } 1300 1342 } 1301 1343 1302 1344 /** 1345 * Check can be moved to hidden() once it's not identical for all control-arrays anymore 1346 */ 1347 function hideControlArrayElement(idx) 1348 { 1349 return idx !== undefined && idx >= g_GameAttributes.settings.PlayerData.length; 1350 } 1351 1352 /** 1353 * @param idx - Only specified for dropdown arrays. 1354 */ 1355 function updateGUIDropdown(name, idx = undefined) 1356 { 1357 let idxName = idx === undefined ? "": "[" + idx + "]"; 1358 let obj = (idx === undefined ? g_Dropdowns : g_DropdownArrays)[name]; 1359 1360 // Naming convention 1361 let dropdown = Engine.GetGUIObjectByName(name + idxName); 1362 let label = Engine.GetGUIObjectByName(name + "Text" + idxName); 1363 1364 let indexHidden = hideControlArrayElement(idx); 1365 1366 // TODO use hideControlArrayElement to skip evil indices 1367 1368 let selected = indexHidden ? -1 : dropdown.list_data.indexOf(String(obj.get(idx))); 1369 let enabled = !indexHidden && (!obj.enabled || obj.enabled(idx)); 1370 let hidden = indexHidden || obj.hidden && obj.hidden(idx); 1371 1372 dropdown.hidden = !g_IsController || !enabled || hidden; 1373 dropdown.selected = selected; 1374 1375 if (label) 1376 { 1377 label.hidden = g_IsController && enabled || hidden; 1378 label.caption = selected == -1 ? translate("Unknown") : dropdown.list[selected]; 1379 } 1380 } 1381 1382 function updateGUICheckbox(name) 1383 { 1384 let obj = g_Checkboxes[name]; 1385 1386 let checked = obj.get(); 1387 let hidden = obj.hidden && obj.hidden(); 1388 let enabled = !obj.enabled || obj.enabled(); 1389 1390 // Naming convention 1391 let checkbox = Engine.GetGUIObjectByName(name); 1392 let label = Engine.GetGUIObjectByName(name + "Text"); 1393 1394 checkbox.checked = checked; 1395 checkbox.enabled = enabled; 1396 checkbox.hidden = hidden || !g_IsController; 1397 1398 label.caption = checked ? translate("Yes") : translate("No"); 1399 label.hidden = hidden || g_IsController; 1400 } 1401 1402 function updateGUIMiscControl(name, idx) 1403 { 1404 let idxName = idx === undefined ? "": "[" + idx + "]"; 1405 1406 let obj = g_MiscControls[name]; 1407 let control = Engine.GetGUIObjectByName(name + idxName); 1408 1409 for (let property in obj) 1410 control[property] = obj[property](); 1411 1412 if (hideControlArrayElement(idx)) 1413 control.hidden = true; 1414 } 1415 1416 /** 1303 1417 * Don't set any attributes here, just show the changes in the GUI. 1304 1418 * 1305 1419 * Unless the mapsettings don't specify a property and the user didn't set it in g_GameAttributes previously. 1306 1420 */ 1307 1421 function updateGUIObjects() 1308 1422 { 1309 1423 g_IsInGuiUpdate = true; 1310 1424 1311 let mapSettings = g_GameAttributes.settings; 1312 1313 // These dropdowns don't set values while g_IsInGuiUpdate 1314 let mapName = g_GameAttributes.map || ""; 1315 let mapFilterIdx = g_MapFilters.findIndex(mapFilter => mapFilter.id == (g_GameAttributes.mapFilter || "default")); 1316 let mapTypeIdx = g_GameAttributes.mapType !== undefined ? g_MapTypes.Name.indexOf(g_GameAttributes.mapType) : g_MapTypes.Default; 1317 let gameSpeedIdx = g_GameAttributes.gameSpeed !== undefined ? g_GameSpeeds.Speed.indexOf(g_GameAttributes.gameSpeed) : g_GameSpeeds.Default; 1318 1319 // These dropdowns might set the default (as they ignore g_IsInGuiUpdate) 1320 let mapSizeIdx = mapSettings.Size !== undefined ? g_MapSizes.Tiles.indexOf(mapSettings.Size) : g_MapSizes.Default; 1321 let victoryIdx = mapSettings.GameType !== undefined ? g_VictoryConditions.Name.indexOf(mapSettings.GameType) : g_VictoryConditions.Default; 1322 let wonderDurationIdx = mapSettings.WonderDuration !== undefined ? g_WonderDurations.Duration.indexOf(mapSettings.WonderDuration) : g_WonderDurations.Default; 1323 let popIdx = mapSettings.PopulationCap !== undefined ? g_PopulationCapacities.Population.indexOf(mapSettings.PopulationCap) : g_PopulationCapacities.Default; 1324 let startingResIdx = mapSettings.StartingResources !== undefined ? g_StartingResources.Resources.indexOf(mapSettings.StartingResources) : g_StartingResources.Default; 1325 let ceasefireIdx = mapSettings.Ceasefire !== undefined ? g_Ceasefire.Duration.indexOf(mapSettings.Ceasefire) : g_Ceasefire.Default; 1326 let numPlayers = mapSettings.PlayerData ? mapSettings.PlayerData.length : g_MaxPlayers; 1425 for (let name in g_Dropdowns) 1426 updateGUIDropdown(name); 1327 1427 1328 if (g_IsController) 1329 { 1330 Engine.GetGUIObjectByName("mapType").selected = mapTypeIdx; 1331 Engine.GetGUIObjectByName("mapFilter").selected = mapFilterIdx; 1332 Engine.GetGUIObjectByName("mapSelection").selected = Engine.GetGUIObjectByName("mapSelection").list_data.indexOf(mapName); 1333 Engine.GetGUIObjectByName("mapSize").selected = mapSizeIdx; 1334 Engine.GetGUIObjectByName("numPlayers").selected = numPlayers - 1; 1335 Engine.GetGUIObjectByName("victoryCondition").selected = victoryIdx; 1336 Engine.GetGUIObjectByName("wonderDuration").selected = wonderDurationIdx; 1337 Engine.GetGUIObjectByName("populationCap").selected = popIdx; 1338 Engine.GetGUIObjectByName("gameSpeed").selected = gameSpeedIdx; 1339 Engine.GetGUIObjectByName("ceasefire").selected = ceasefireIdx; 1340 Engine.GetGUIObjectByName("startingResources").selected = startingResIdx; 1341 } 1342 else 1343 { 1344 Engine.GetGUIObjectByName("mapTypeText").caption = g_MapTypes.Title[mapTypeIdx]; 1345 Engine.GetGUIObjectByName("mapFilterText").caption = g_MapFilters[mapFilterIdx].name; 1346 Engine.GetGUIObjectByName("mapSelectionText").caption = mapName == "random" ? g_RandomMap : translate(getMapDisplayName(mapName)); 1347 initMapNameList(); 1348 } 1349 1350 // Can be visible to both host and clients 1351 Engine.GetGUIObjectByName("mapSizeText").caption = g_GameAttributes.mapType == "random" ? g_MapSizes.LongName[mapSizeIdx] : translate("Default"); 1352 Engine.GetGUIObjectByName("numPlayersText").caption = numPlayers; 1353 Engine.GetGUIObjectByName("victoryConditionText").caption = g_VictoryConditions.Title[victoryIdx]; 1354 Engine.GetGUIObjectByName("wonderDurationText").caption = g_WonderDurations.Title[wonderDurationIdx]; 1355 Engine.GetGUIObjectByName("populationCapText").caption = g_PopulationCapacities.Title[popIdx]; 1356 Engine.GetGUIObjectByName("startingResourcesText").caption = g_StartingResources.Title[startingResIdx]; 1357 Engine.GetGUIObjectByName("ceasefireText").caption = g_Ceasefire.Title[ceasefireIdx]; 1358 Engine.GetGUIObjectByName("gameSpeedText").caption = g_GameSpeeds.Title[gameSpeedIdx]; 1359 1360 setGUIBoolean("enableCheats", "enableCheatsText", !!mapSettings.CheatsEnabled); 1361 setGUIBoolean("disableTreasures", "disableTreasuresText", !!mapSettings.DisableTreasures); 1362 setGUIBoolean("exploreMap", "exploreMapText", !!mapSettings.ExploreMap); 1363 setGUIBoolean("revealMap", "revealMapText", !!mapSettings.RevealMap); 1364 setGUIBoolean("lockTeams", "lockTeamsText", !!mapSettings.LockTeams); 1365 setGUIBoolean("enableRating", "enableRatingText", !!mapSettings.RatingEnabled); 1366 1367 Engine.GetGUIObjectByName("optionWonderDuration").hidden = 1368 g_GameAttributes.settings.GameType && 1369 g_GameAttributes.settings.GameType != "wonder"; 1370 1371 Engine.GetGUIObjectByName("cheatWarningText").hidden = !g_IsNetworked || !mapSettings.CheatsEnabled; 1372 1373 Engine.GetGUIObjectByName("enableCheats").enabled = !mapSettings.RatingEnabled; 1374 Engine.GetGUIObjectByName("lockTeams").enabled = !mapSettings.RatingEnabled; 1375 1376 // Mapsize completely hidden for non-random maps 1377 let isRandom = g_GameAttributes.mapType == "random"; 1378 Engine.GetGUIObjectByName("mapSizeDesc").hidden = !isRandom; 1379 Engine.GetGUIObjectByName("mapSize").hidden = !isRandom || !g_IsController; 1380 Engine.GetGUIObjectByName("mapSizeText").hidden = !isRandom || g_IsController; 1381 hideControl("numPlayers", "numPlayersText", isRandom && g_IsController); 1382 1383 let notScenario = g_GameAttributes.mapType != "scenario" && g_IsController ; 1384 1385 for (let ctrl of ["victoryCondition", "wonderDuration", "populationCap", 1386 "startingResources", "ceasefire", "revealMap", 1387 "exploreMap", "disableTreasures", "lockTeams"]) 1388 hideControl(ctrl, ctrl + "Text", notScenario); 1428 for (let name in g_Checkboxes) 1429 updateGUICheckbox(name); 1389 1430 1390 Engine.GetGUIObjectByName("civResetButton").hidden = !notScenario; 1431 for (let name in g_MiscControls) 1432 updateGUIMiscControl(name); 1391 1433 1392 1434 for (let i = 0; i < g_MaxPlayers; ++i) 1393 1435 { 1394 Engine.GetGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers); 1395 1396 if (i >= numPlayers) 1397 continue; 1436 for (let name in g_DropdownArrays) 1437 updateGUIDropdown(name, i); 1398 1438 1399 let pName = Engine.GetGUIObjectByName("playerName["+i+"]"); 1400 let pAssignment = Engine.GetGUIObjectByName("playerAssignment["+i+"]"); 1401 let pAssignmentText = Engine.GetGUIObjectByName("playerAssignmentText["+i+"]"); 1402 let pCiv = Engine.GetGUIObjectByName("playerCiv["+i+"]"); 1403 let pCivText = Engine.GetGUIObjectByName("playerCivText["+i+"]"); 1404 let pTeam = Engine.GetGUIObjectByName("playerTeam["+i+"]"); 1405 let pTeamText = Engine.GetGUIObjectByName("playerTeamText["+i+"]"); 1406 let pColor = Engine.GetGUIObjectByName("playerColor["+i+"]"); 1407 1408 let pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {}; 1409 let pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {}; 1410 1411 let color = getSetting(pData, pDefs, "Color"); 1412 pColor.sprite = "color:" + rgbToGuiColor(color) + " 100"; 1413 pName.caption = translate(getSetting(pData, pDefs, "Name")); 1414 1415 let team = getSetting(pData, pDefs, "Team"); 1416 let civ = getSetting(pData, pDefs, "Civ"); 1417 1418 pAssignmentText.caption = pAssignment.list[0] ? pAssignment.list[Math.max(0, pAssignment.selected)] : translate("Loading..."); 1419 pCivText.caption = civ == "random" ? g_RandomCiv : (g_CivData[civ] ? g_CivData[civ].Name : "Unknown"); 1420 pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-"; 1421 1422 pCiv.selected = civ ? pCiv.list_data.indexOf(civ) : 0; 1423 pTeam.selected = team !== undefined && team >= 0 ? team+1 : 0; 1424 1425 hideControl("playerAssignment["+i+"]", "playerAssignmentText["+i+"]", g_IsController); 1426 hideControl("playerCiv["+i+"]", "playerCivText["+i+"]", notScenario); 1427 hideControl("playerTeam["+i+"]", "playerTeamText["+i+"]", notScenario); 1428 1429 // Allow host to chose player colors on non-scenario maps 1430 let pColorPicker = Engine.GetGUIObjectByName("playerColorPicker["+i+"]"); 1431 let pColorPickerHeading = Engine.GetGUIObjectByName("playerColorHeading"); 1432 let canChangeColors = g_IsController && g_GameAttributes.mapType != "scenario"; 1433 pColorPicker.hidden = !canChangeColors; 1434 pColorPickerHeading.hidden = !canChangeColors; 1435 if (canChangeColors) 1436 pColorPicker.selected = g_PlayerColors.findIndex(col => sameColor(col, color)); 1439 for (let name in g_MiscControlArrays) 1440 updateGUIMiscControl(name, i); 1437 1441 } 1438 1442 1439 1443 updateMapDescription(); 1444 1440 1445 resizeMoreOptionsWindow(); 1441 1446 1442 1447 g_IsInGuiUpdate = false; 1443 1448 1444 1449 // Game attributes include AI settings, so update the player list … … function AIConfigCallback(ai) 1557 1562 g_GameAttributes.settings.PlayerData[ai.playerSlot].AIDiff = ai.difficulty; 1558 1563 1559 1564 updateGameAttributes(); 1560 1565 } 1561 1566 1567 /** 1568 * TODO: delete 1569 */ 1562 1570 function updatePlayerList() 1563 1571 { 1564 1572 g_IsInGuiUpdate = true; 1565 1573 1566 let hostNameList = []; 1567 let hostGuidList = []; 1574 let playerChoices = sortGUIDsByPlayerID().map(guid => ({ 1575 "id": "guid:" + guid, 1576 "label": 1577 g_PlayerAssignments[guid].player == -1 ? 1578 "[color=\""+ g_UnassignedPlayerColor + "\"]" + g_PlayerAssignments[guid].name + "[/color]" : 1579 g_PlayerAssignments[guid].name 1580 })); 1581 1582 let aiChoices = g_Settings.AIDescriptions 1583 .filter(ai => !ai.data.hidden || !!g_GameAttributes.settings.PlayerData.every(pData => pData.AI != ai.id)) 1584 .map(ai => ({ 1585 "id": "ai:" + ai.id, 1586 "label": "[color=\""+ g_AIColor + "\"]" + 1587 sprintf(translate("AI: %(ai)s"), { 1588 "ai": translate(ai.data.name) 1589 }) + "[/color]" 1590 })); 1591 1592 let unassignedSlot = [{ 1593 "id": "", 1594 "label": "[color=\""+ g_UnassignedColor + "\"]" + translate("Unassigned") + "[/color]", 1595 }]; 1596 m_PlayerAssignmentChoices = playerChoices.concat(aiChoices).concat(unassignedSlot); 1597 1568 1598 let assignments = []; 1569 1599 let aiAssignments = {}; 1570 let noAssignment;1571 let assignedCount = 0;1572 1600 for (let guid of sortGUIDsByPlayerID()) 1573 { 1574 let player = g_PlayerAssignments[guid].player; 1575 1576 if (player != -1) 1577 hostNameList.push(g_PlayerAssignments[guid].name); 1578 else 1579 hostNameList.push("[color=\""+ g_UnassignedPlayerColor + "\"]" + g_PlayerAssignments[guid].name + "[/color]"); 1580 1581 hostGuidList.push(guid); 1582 assignments[player] = hostNameList.length-1; 1583 1584 if (player != -1) 1585 ++assignedCount; 1586 } 1587 1588 // Only enable start button if we have enough assigned players 1589 if (g_IsController) 1590 Engine.GetGUIObjectByName("startGame").enabled = assignedCount > 0; 1591 1592 for (let ai of g_Settings.AIDescriptions) 1593 { 1594 // If the map uses a hidden AI then don't hide it 1595 if (ai.data.hidden && g_GameAttributes.settings.PlayerData.every(pData => pData.AI != ai.id)) 1596 continue; 1601 assignments[g_PlayerAssignments[guid].player] = g_HostNameList.length - 1; 1597 1602 1598 aiAssignments[ai.id] = hostNameList.length; 1599 hostNameList.push("[color=\""+ g_AIColor + "\"]" + sprintf(translate("AI: %(ai)s"), { "ai": translate(ai.data.name) })); 1600 hostGuidList.push("ai:" + ai.id); 1601 } 1602 1603 noAssignment = hostNameList.length; 1604 hostNameList.push("[color=\""+ g_UnassignedColor + "\"]" + translate("Unassigned")); 1605 hostGuidList.push(""); 1606 1607 for (let i = 0; i < g_MaxPlayers; ++i) 1608 { 1609 let playerSlot = i; 1610 let playerID = i+1; // we don't show Gaia, so first slot is ID 1 1611 1612 let selection = assignments[playerID]; 1613 1614 let configButton = Engine.GetGUIObjectByName("playerConfig["+i+"]"); 1615 configButton.hidden = true; 1616 1617 // Look for valid player slots 1618 if (playerSlot >= g_GameAttributes.settings.PlayerData.length) 1619 continue; 1620 1621 // If no human is assigned, look for an AI instead 1622 if (selection === undefined) 1623 { 1624 let aiId = g_GameAttributes.settings.PlayerData[playerSlot].AI; 1625 if (aiId) 1626 { 1627 // Check for a valid AI 1628 if (aiId in aiAssignments) 1629 { 1630 selection = aiAssignments[aiId]; 1631 configButton.hidden = false; 1632 configButton.onpress = function() 1633 { 1634 openAIConfig(playerSlot); 1635 }; 1636 } 1637 else 1638 { 1639 g_GameAttributes.settings.PlayerData[playerSlot].AI = ""; 1640 warn("AI \"" + aiId + "\" not present. Defaulting to unassigned."); 1641 } 1642 } 1643 1644 if (!selection) 1645 selection = noAssignment; 1646 } 1647 // There was a human, so make sure we don't have any AI left 1648 // over in their slot, if we're in charge of the attributes 1649 else if (g_IsController && g_GameAttributes.settings.PlayerData[playerSlot].AI) 1650 { 1651 g_GameAttributes.settings.PlayerData[playerSlot].AI = ""; 1652 if (g_IsNetworked) 1653 Engine.SetNetworkGameAttributes(g_GameAttributes); 1654 } 1603 g_GameAttributes.settings.PlayerData[playerSlot].AI = g_GameAttributes.settings.PlayerData[playerSlot].AI || ""; 1655 1604 1656 let assignBox = Engine.GetGUIObjectByName("playerAssignment["+i+"]"); 1657 let assignBoxText = Engine.GetGUIObjectByName("playerAssignmentText["+i+"]"); 1658 assignBox.list = hostNameList; 1659 assignBox.list_data = hostGuidList; 1660 if (assignBox.selected != selection) 1661 assignBox.selected = selection; 1662 assignBoxText.caption = hostNameList[selection]; 1663 1664 if (g_IsController) 1665 assignBox.onselectionchange = function() { 1666 if (g_IsInGuiUpdate) 1667 return; 1668 1669 let guid = hostGuidList[this.selected]; 1670 if (!guid) 1671 { 1672 if (g_IsNetworked) 1673 // Unassign any host from this player slot 1674 Engine.AssignNetworkPlayer(playerID, ""); 1675 // Remove AI from this player slot 1676 g_GameAttributes.settings.PlayerData[playerSlot].AI = ""; 1677 } 1678 else if (guid.substr(0, 3) == "ai:") 1679 { 1680 if (g_IsNetworked) 1681 // Unassign any host from this player slot 1682 Engine.AssignNetworkPlayer(playerID, ""); 1683 // Set the AI for this player slot 1684 g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3); 1685 } 1686 else 1687 swapPlayers(guid, playerSlot); 1688 1689 if (g_IsNetworked) 1690 Engine.SetNetworkGameAttributes(g_GameAttributes); 1691 else 1692 updatePlayerList(); 1693 updateReadyUI(); 1694 }; 1695 } 1605 initDropdownArray("playerAssignment"); 1696 1606 1697 1607 g_IsInGuiUpdate = false; 1698 1608 } 1699 1609 1700 1610 function swapPlayers(guid, newSlot) … … function updateReadyUI() 1875 1785 1876 1786 // The host is not allowed to start until everyone is ready. 1877 1787 if (g_IsNetworked && g_IsController) 1878 1788 { 1879 1789 let startGameButton = Engine.GetGUIObjectByName("startGame"); 1880 startGameButton.enabled = allReady; 1790 1881 1791 // Add a explanation on to the tooltip if disabled. 1882 1792 let disabledIndex = startGameButton.tooltip.indexOf('Disabled'); 1883 1793 if (disabledIndex != -1 && allReady) 1884 1794 startGameButton.tooltip = startGameButton.tooltip.substring(0, disabledIndex - 2); 1885 1795 else if (disabledIndex == -1 && !allReady)