Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -125,6 +125,9 @@ ; Color of the sky (in "r g b" format) skycolor = "0 0 0" +[adaptivefps] +session = 60 ; Throttle FPS in running games (prevents 100% CPU workload). +menu = 30 ; Throttle FPS in menus only. [hotkey] ; Each one of the specified keys will trigger the action on the left @@ -161,6 +164,7 @@ session.showstatusbars = Tab ; Toggle display of status bars session.highlightguarding = PgDn ; Toggle highlight of guarding units session.highlightguarded = PgUp ; Toggle highlight of guarded units +session.toggleaurarange = "Alt+V" ; Toggle rendering of aura range overlays of selected units and structures ; > HOTKEYS ONLY chat = Return ; Toggle chat window @@ -329,13 +333,11 @@ [gui.gamesetup] enabletips = true ; Enable/Disable tips during gamesetup (for newcomers) -[gui.menu] -limitfps = true ; Limit FPS in the menus and loading screen - [gui.session] camerajump.threshold = 40 ; How close do we have to be to the actual location in order to jump back to the previous one? timeelapsedcounter = false ; Show the game duration in the top right corner batchtrainingsize = 5 ; Number of units to be trained per batch (when pressing the hotkey) +aurarange = true [gui.session.minimap] blinkduration = 1.7 ; The blink duration while pinging Index: binaries/data/mods/public/art/actors/props/special/common/marker_object_sound.xml =================================================================== --- binaries/data/mods/public/art/actors/props/special/common/marker_object_sound.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - props/special/marker_object_sound.dae - - - - - - - - - - player_trans.xml - Index: binaries/data/mods/public/art/actors/props/structures/hellenes/fortress_props.xml =================================================================== --- /dev/null +++ binaries/data/mods/public/art/actors/props/structures/hellenes/fortress_props.xml @@ -0,0 +1,15 @@ + + + + + + props/hele_fortress_props.dae + + + + + + + + player_trans_parallax_spec.xml + Index: binaries/data/mods/public/art/actors/special/old/athenian_fortress.xml =================================================================== --- binaries/data/mods/public/art/actors/special/old/athenian_fortress.xml +++ binaries/data/mods/public/art/actors/special/old/athenian_fortress.xml @@ -5,13 +5,15 @@ structural/hele_fortress.dae - + - + + + @@ -30,7 +32,7 @@ - + Index: binaries/data/mods/public/art/meshes/props/special/marker_object_sound.dae =================================================================== --- binaries/data/mods/public/art/meshes/props/special/marker_object_sound.dae +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Blender User - Blender 2.62.0 r44136 - - 2014-06-03T23:26:08 - 2014-06-03T23:26:08 - - Z_UP - - - - - - 0 0 -0.0999999 0 0 3.9 0 1 2.9 0.8660254 -0.5000001 2.9 -0.8660255 -0.4999999 2.9 0.8423598 -0.3242053 3.120853 0.7019498 -0.5674024 3.120853 -0.14041 0.8916076 3.039157 0.14041 0.8916076 3.039157 -0.7019498 -0.5674022 3.039157 -0.8423598 -0.324205 3.039157 -0.8797824 -0.61603 3.039157 0.9733891 -0.4538989 3.039157 0.09360665 1.069929 3.039157 0 0 -0.1011943 -0.9733891 -0.4538986 3.039157 0.8797824 -0.6160303 3.039157 -0.09360665 1.069929 3.039157 - - - - - - - - - - 0 -0.8944272 0.4472136 -0.7745966 0.4472136 0.4472136 0.7745967 0.4472136 0.4472136 0.8542422 0.4931969 -0.164399 -0.8542422 0.493197 -0.164399 0 -0.986394 -0.164399 0.9608703 0.2521959 -0.1145654 0 0 1 -0.6988431 0.7060405 -0.1145654 0 0 1 -0.3050505 -0.9470379 -0.1003155 0.3607088 -0.2082554 0.9091308 0.8330979 0.4809892 0.2731246 0 -0.9619787 0.2731247 -0.834658 0.4818901 0.266698 0.6676338 0.7377004 -0.1003155 -0.9608703 0.2521961 -0.1145654 0.8197531 -0.473285 -0.3224997 0.2620272 -0.9582361 -0.1145653 -0.8197531 -0.473285 -0.3224996 0 0.9465696 -0.3224996 - - - - - - - - - - 7.3488e-4 0.002862751 0.1166772 0.08773553 7.34873e-4 0.1726058 7.3488e-4 0.002862751 0.1166772 0.08773553 7.34873e-4 0.1726058 7.3488e-4 0.002862751 0.1166772 0.08773553 7.34873e-4 0.1726058 0.2970088 0.9987176 0.1837491 0.5304416 0.4109398 0.5304416 0.2970088 0.9987176 0.1837491 0.5304416 0.4109398 0.5304416 0.2970088 0.9987176 0.1837491 0.5304416 0.4109398 0.5304416 0.1156439 0.9985879 0.06852585 0.9985879 0.1391689 0.1666454 0.09618312 0.166791 0.1112316 0.2391953 0.009796082 0.2391953 0.02484452 0.166791 0.1156439 0.9985879 0.06852585 0.9985879 0.1391689 0.1666454 0.09618312 0.166791 0.1112316 0.2391953 0.009796082 0.2391953 0.02484452 0.166791 0.1156439 0.9985879 0.06852585 0.9985879 0.1391689 0.1666454 0.09618312 0.166791 0.1112316 0.2391953 0.009796082 0.2391953 0.02484452 0.166791 0.1112316 0.2391953 0.06054741 0.9985166 0.009796082 0.2391953 0.1112316 0.2391953 0.06054741 0.9985166 0.009796082 0.2391953 0.1112316 0.2391953 0.06054741 0.9985166 0.009796082 0.2391953 0.1626949 0.9985879 0.1391689 0.1666454 0.209811 0.9985879 0.1626949 0.9985879 0.1391689 0.1666454 0.209811 0.9985879 0.1391689 0.1666454 0.1626949 0.9985879 0.1156439 0.9985879 0.209811 0.9985879 0.1626949 0.9985879 0.1391689 0.1666454 0.1391689 0.1666454 0.1626949 0.9985879 0.1156439 0.9985879 0.1391689 0.1666454 0.1626949 0.9985879 0.1156439 0.9985879 - - - - - - - - - - - - - - - 3 3 3 3 3 3 3 4 3 4 3 4 3 3 3 3 3 3 3 3 3 -

3 0 0 1 0 1 4 0 2 4 1 3 1 1 4 2 1 5 2 2 6 1 2 7 3 2 8 0 3 9 2 3 10 3 3 11 0 4 12 4 4 13 2 4 14 0 5 15 3 5 16 4 5 17 13 6 18 8 6 19 14 6 20 17 7 21 7 7 22 8 7 23 13 7 24 15 8 25 10 8 26 14 8 27 11 9 28 9 9 29 10 9 30 15 9 31 16 10 32 6 10 33 14 10 34 12 11 35 5 11 36 6 11 37 16 11 38 9 12 39 14 12 40 10 12 41 7 13 42 14 13 43 8 13 44 5 14 45 14 14 46 6 14 47 12 15 48 14 15 49 5 15 50 17 16 51 14 16 52 7 16 53 14 17 54 12 17 55 16 17 56 9 18 57 11 18 58 14 18 59 14 19 60 11 19 61 15 19 62 14 20 63 17 20 64 13 20 65

-
-
-
-
- - - - 0 0 0 - 0 0 1 0 - 0 1 0 0 - 1 0 0 0 - 3 3 3 - - - - - - - -
\ No newline at end of file Index: binaries/data/mods/public/globalscripts/Templates.js =================================================================== --- binaries/data/mods/public/globalscripts/Templates.js +++ binaries/data/mods/public/globalscripts/Templates.js @@ -184,19 +184,19 @@ for (let auraID of template.Auras._string.split(/\s+/)) { let aura = auraTemplates[auraID]; - if (aura.auraName) - ret.auras[auraID] = { + ret.auras[auraID] = { "name": aura.auraName, - "description": aura.auraDescription || null + "description": aura.auraDescription || null, + "radius": aura.radius || null }; } } if (template.BuildingAI) ret.buildingAI = { - "defaultArrowCount": getEntityValue("BuildingAI/DefaultArrowCount"), + "defaultArrowCount": Math.round(getEntityValue("BuildingAI/DefaultArrowCount")), "garrisonArrowMultiplier": getEntityValue("BuildingAI/GarrisonArrowMultiplier"), - "maxArrowCount": getEntityValue("BuildingAI/MaxArrowCount") + "maxArrowCount": Math.round(getEntityValue("BuildingAI/MaxArrowCount")) }; if (template.BuildRestrictions) Index: binaries/data/mods/public/gui/common/gamedescription.js =================================================================== --- binaries/data/mods/public/gui/common/gamedescription.js +++ binaries/data/mods/public/gui/common/gamedescription.js @@ -219,7 +219,8 @@ { "min": g_GameAttributes.settings.VictoryDuration } ); - else if (g_VictoryConditions.Name[victoryIdx] == "capture_the_relic") + let isCaptureTheRelic = g_VictoryConditions.Name[victoryIdx] == "capture_the_relic"; + if (isCaptureTheRelic) title = sprintf( translatePluralWithContext( "victory condition", @@ -234,6 +235,12 @@ "label": title, "value": g_VictoryConditions.Description[victoryIdx] }); + + if (isCaptureTheRelic) + titles.push({ + "label": translate("Relic Count"), + "value": g_GameAttributes.settings.RelicCount + }); } if (g_GameAttributes.settings.RatingEnabled && Index: binaries/data/mods/public/gui/common/tooltips.js =================================================================== --- binaries/data/mods/public/gui/common/tooltips.js +++ binaries/data/mods/public/gui/common/tooltips.js @@ -285,8 +285,8 @@ let limit = Math.min( template.buildingAI.maxArrowCount || Infinity, template.buildingAI.defaultArrowCount + - template.buildingAI.garrisonArrowMultiplier * - template.garrisonHolder.capacity + Math.round(template.buildingAI.garrisonArrowMultiplier * + template.garrisonHolder.capacity) ); if (!limit) @@ -305,7 +305,7 @@ sprintf(translate("%(label)s: %(value)s"), { "label": headerFont(translateWithContext("projectiles", "Per Unit")), - "value": template.buildingAI.garrisonArrowMultiplier + "value": +template.buildingAI.garrisonArrowMultiplier.toFixed(2) }) ].join(commaFont(translate(", "))); } @@ -353,14 +353,14 @@ /** * Helper function for getEntityCostTooltip. */ -function getEntityCostComponentsTooltipString(template, trainNum, entity) +function getEntityCostComponentsTooltipString(template, entity, buildingsCountToTrainFullBatch = 1, fullBatchSize = 1, remainderBatch = 0) { - if (!trainNum) - trainNum = 1; - - let totalCosts = multiplyEntityCosts(template, trainNum); + let totalCosts = multiplyEntityCosts(template, buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch); if (template.cost.time) - totalCosts.time = Math.ceil(template.cost.time * (entity ? Engine.GuiInterfaceCall("GetBatchTime", { "entity": entity, "batchSize": trainNum }) : 1)); + totalCosts.time = Math.ceil(template.cost.time * (entity ? Engine.GuiInterfaceCall("GetBatchTime", { + "entity": entity, + "batchSize": buildingsCountToTrainFullBatch > 0 ? fullBatchSize : remainderBatch + }) : 1)); let costs = []; for (let type in template.cost) @@ -493,7 +493,7 @@ /** * Returns the cost information to display in the specified entity's construction button tooltip. */ -function getEntityCostTooltip(template, trainNum, entity) +function getEntityCostTooltip(template, entity, buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) { // Entities with a wallset component are proxies for initiating wall placement and as such do not have a cost of // their own; the individual wall pieces within it do. @@ -512,7 +512,7 @@ } if (template.cost) - return getEntityCostComponentsTooltipString(template, trainNum, entity).join(" "); + return getEntityCostComponentsTooltipString(template, entity, buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch).join(" "); return ""; } @@ -622,13 +622,24 @@ if (!template.auras) return ""; - let tooltips = Object.keys(template.auras).map( - aura => sprintf(translate("%(auralabel)s %(aurainfo)s"), { + let tooltips = []; + for (let auraID in template.auras) + { + let tooltip = sprintf(translate("%(auralabel)s %(aurainfo)s"), { "auralabel": headerFont(sprintf(translate("%(auraname)s:"), { - "auraname": translate(template.auras[aura].name) + "auraname": translate(template.auras[auraID].name) })), - "aurainfo": bodyFont(translate(template.auras[aura].description)) - })); + "aurainfo": bodyFont(translate(template.auras[auraID].description)) + }); + let radius = +template.auras[auraID].radius; + if (radius) + tooltip += " " + sprintf(translatePlural("%(label)s %(val)s %(unit)s", "%(label)s %(val)s %(unit)s", radius), { + "label": translateWithContext("aura", "Range:"), + "val": radius, + "unit": unitFont(translatePlural("meter", "meters", radius)) + }); + tooltips.push(tooltip); + } return tooltips.join("\n"); } Index: binaries/data/mods/public/gui/credits/texts/programming.json =================================================================== --- binaries/data/mods/public/gui/credits/texts/programming.json +++ binaries/data/mods/public/gui/credits/texts/programming.json @@ -86,6 +86,7 @@ {"nick": "godlikeldh"}, {"nick": "greybeard", "name": "Joe Cocovich"}, {"nick": "grillaz"}, + {"nick": "Grugnas", "name": "Giuseppe Tranchese"}, {"nick": "gudo"}, {"nick": "Guuts", "name": "Matthew Guttag"}, {"name": "Samuel Guarnieri"}, Index: binaries/data/mods/public/gui/gamesetup/gamesetup.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -16,19 +16,19 @@ var g_ColorRandom = "orange"; /** - * Highlight AIs in the player-dropdownlist. + * Color for regular dropdownlist items. */ -var g_AIColor = "70 150 70"; +var g_ColorRegular = "white"; /** * Color for "Unassigned"-placeholder item in the dropdownlist. */ -var g_UnassignedColor = "140 140 140"; - -/** - * Highlight observer players in the dropdownlist. - */ -var g_UnassignedPlayerColor = "170 170 250"; +var g_PlayerAssignmentColors = { + "player": g_ColorRegular, + "observer": "170 170 250", + "unassigned": "140 140 140", + "AI": "70 150 70" +}; /** * Used for highlighting the sender of chat messages. @@ -62,14 +62,21 @@ */ var g_CivData = loadCivData(); +/** + * Number of relics: [1, ..., NumCivs] + */ +var g_RelicCountList = Object.keys(g_CivData).map((civ, i) => i + 1); + var g_PlayerCivList = g_CivData && prepareForDropdown([{ - "name": '[color="' + g_ColorRandom + '"]' + translateWithContext("civilization", "Random") + '[/color]', + "name": translateWithContext("civilization", "Random"), + "color": g_ColorRandom, "code": "random" }].concat( Object.keys(g_CivData).filter( civ => g_CivData[civ].SelectableInGameSetup ).map(civ => ({ "name": g_CivData[civ].Name, + "color": g_ColorRegular, "code": civ })).sort(sortNameIgnoreCase) ) @@ -97,7 +104,7 @@ var g_ReadyData = [ { - "color": "white", + "color": g_ColorRegular, "chat": translate("* %(username)s is not ready."), "caption": translate("I'm ready"), "tooltip": translate("State that you are ready to play.") @@ -307,6 +314,7 @@ "Dropdown": [ "gameSpeed", "victoryCondition", + "relicCount", "victoryDuration", "populationCap", "startingResources", @@ -352,6 +360,7 @@ * title - The caption shown in the label. * tooltip - A description shown when hovering the option. * labels - Array of translated strings selectable for this dropdown. + * colors - Optional array of colors to tint the according dropdown items with. * hidden - If hidden, both the label and dropdown won't be visible. (default: false) * enabled - Only the label will be shown if the setting is disabled. (default: true) * autocomplete - Whether to autocomplete translated values of the string. (default: false) @@ -400,6 +409,7 @@ "title": () => translate("Select Map"), "tooltip": () => translate("Select a map to play on."), "labels": () => g_MapSelectionList.name, + "colors": () => g_MapSelectionList.color, "ids": () => g_MapSelectionList.file, "default": () => 0, "defined": () => g_GameAttributes.map !== undefined, @@ -498,6 +508,20 @@ "enabled": () => g_GameAttributes.mapType != "scenario", "autocomplete": true, }, + "relicCount": { + "title": () => translate("Relic Count"), + "tooltip": () => translate("Total number of relics spawned on the map."), + "labels": () => g_RelicCountList, + "ids": () => g_RelicCountList, + "default": () => g_RelicCountList.indexOf(5), + "defined": () => g_GameAttributes.settings.RelicCount !== undefined, + "get": () => g_GameAttributes.settings.RelicCount, + "select": (idx) => { + g_GameAttributes.settings.RelicCount = g_RelicCountList[idx]; + }, + "hidden": () => g_GameAttributes.settings.GameType != "capture_the_relic", + "enabled": () => g_GameAttributes.mapType != "scenario", + }, "victoryDuration": { "title": () => translate("Victory Duration"), "tooltip": () => translate("Number of minutes until the player has won."), @@ -535,6 +559,7 @@ var g_PlayerDropdowns = { "playerAssignment": { "labels": (idx) => g_PlayerAssignmentList.Name || [], + "colors": (idx) => g_PlayerAssignmentList.Color || [], "ids": (idx) => g_PlayerAssignmentList.Choice || [], "default": (idx) => "ai:petra", "defined": (idx) => idx < g_GameAttributes.settings.PlayerData.length, @@ -579,16 +604,20 @@ }, "playerCiv": { "labels": (idx) => g_PlayerCivList.name, + "colors": (idx) => g_PlayerCivList.color, "ids": (idx) => g_PlayerCivList.code, "default": (idx) => 0, "defined": (idx) => g_GameAttributes.settings.PlayerData[idx].Civ !== undefined, "get": (idx) => g_GameAttributes.settings.PlayerData[idx].Civ, - "select": (selectedIdx, idx) => g_GameAttributes.settings.PlayerData[idx].Civ = g_PlayerCivList.code[selectedIdx], + "select": (selectedIdx, idx) => { + g_GameAttributes.settings.PlayerData[idx].Civ = g_PlayerCivList.code[selectedIdx]; + }, "enabled": () => g_GameAttributes.mapType != "scenario", "autocomplete": true, }, "playerColorPicker": { - "labels": (idx) => g_PlayerColorPickerList.map(color => ' ' + '[color="' + rgbToGuiColor(color) + '"]■[/color]'), + "labels": (idx) => g_PlayerColorPickerList.map(color => "■"), + "colors": (idx) => g_PlayerColorPickerList.map(color => rgbToGuiColor(color)), "ids": (idx) => g_PlayerColorPickerList.map((color, index) => index), "default": (idx) => idx, "defined": (idx) => g_GameAttributes.settings.PlayerData[idx].Color !== undefined, @@ -866,8 +895,12 @@ g_DefaultPlayerData = g_Settings.PlayerDefaults; g_DefaultPlayerData.shift(); + // Don't change the underlying defaults file, as Atlas uses that file too for (let i in g_DefaultPlayerData) + { g_DefaultPlayerData[i].Civ = "random"; + g_DefaultPlayerData[i].Teams = -1; + } } /** @@ -950,7 +983,12 @@ let data = (idx === undefined ? g_Dropdowns : g_PlayerDropdowns)[name]; let dropdown = Engine.GetGUIObjectByName(guiName + guiIdx + idxName); - dropdown.list = data.labels(idx); + + dropdown.list = data.labels(idx).map((label, id) => + data.colors && data.colors(idx) ? + '[color="' + data.colors(idx)[id] + '"]' + label + "[/color]" : + label); + dropdown.list_data = data.ids(idx); dropdown.onSelectionChange = function() { @@ -1227,11 +1265,6 @@ // Apply map filter, if any defined let mapList = []; - if (g_GameAttributes.mapType == "random") - mapList.push({ - "file": "random", - "name": '[color="' + g_ColorRandom + '"]' + translateWithContext("map selection", "Random") + "[/color]" - }); // TODO: Should verify these are valid maps before adding to list for (let mapFile of mapFiles) @@ -1246,11 +1279,21 @@ mapList.push({ "file": file, + "color": g_ColorRegular, "name": translate(getMapDisplayName(file)) }); } - g_MapSelectionList = prepareForDropdown(mapList.sort(sortNameIgnoreCase)); + mapList = mapList.sort(sortNameIgnoreCase) + + if (g_GameAttributes.mapType == "random") + mapList.unshift({ + "file": "random", + "name": translateWithContext("map selection", "Random"), + "color": g_ColorRandom + }); + + g_MapSelectionList = prepareForDropdown(mapList); initDropdown("mapSelection"); } @@ -1340,19 +1383,11 @@ if (playerData.length && !playerData[0]) playerData.shift(); + // Use defaults if the map doesn't specify a value playerData.forEach((pData, index) => { - pData.Color = pData.Color || g_PlayerColorPickerList[index]; - pData.Civ = pData.Civ || "random"; - - if (!("Team" in pData)) - pData.Team = -1; - - // Use default AI if the map doesn't specify any explicitly - if (!("AI" in pData)) - pData.AI = g_DefaultPlayerData[index].AI; - - if (!("AIDiff" in pData)) - pData.AIDiff = g_DefaultPlayerData[index].AIDiff; + for (let prop in g_DefaultPlayerData[index]) + if (!(prop in pData)) + pData[prop] = g_DefaultPlayerData[index][prop]; }); // Replace colors with the best matching color of PlayerDefaults @@ -1822,10 +1857,8 @@ { let playerChoices = sortGUIDsByPlayerID().map(guid => ({ "Choice": "guid:" + guid, - "Name": - g_PlayerAssignments[guid].player == -1 ? - "[color=\""+ g_UnassignedPlayerColor + "\"]" + g_PlayerAssignments[guid].name + "[/color]" : - g_PlayerAssignments[guid].name + "Color": g_PlayerAssignments[guid].player == -1 ? g_PlayerAssignmentColors.observer : g_PlayerAssignmentColors.player, + "Name": g_PlayerAssignments[guid].name })); // Only display hidden AIs if the map preselects them @@ -1833,15 +1866,16 @@ .filter(ai => !ai.data.hidden || g_GameAttributes.settings.PlayerData.some(pData => pData.AI == ai.id)) .map(ai => ({ "Choice": "ai:" + ai.id, - "Name": "[color=\""+ g_AIColor + "\"]" + - sprintf(translate("AI: %(ai)s"), { - "ai": translate(ai.data.name) - }) + "[/color]" + "Name": sprintf(translate("AI: %(ai)s"), { + "ai": translate(ai.data.name) + }), + "Color": g_PlayerAssignmentColors.AI })); let unassignedSlot = [{ "Choice": "unassigned", - "Name": "[color=\""+ g_UnassignedColor + "\"]" + translate("Unassigned") + "[/color]", + "Name": translate("Unassigned"), + "Color": g_PlayerAssignmentColors.unassigned }]; g_PlayerAssignmentList = prepareForDropdown(playerChoices.concat(aiChoices).concat(unassignedSlot)); @@ -1923,7 +1957,7 @@ username = g_PlayerAssignments[guid] ? escapeText(g_PlayerAssignments[guid].name) : translate("Unknown Player"); let playerID = g_PlayerAssignments[guid] ? g_PlayerAssignments[guid].player : -1; - let color = "white"; + let color = g_ColorRegular; if (playerID > 0) { color = g_GameAttributes.settings.PlayerData[playerID - 1].Color; Index: binaries/data/mods/public/gui/loading/loading.xml =================================================================== --- binaries/data/mods/public/gui/loading/loading.xml +++ binaries/data/mods/public/gui/loading/loading.xml @@ -44,10 +44,7 @@ - - Quote of the Day: - - + Index: binaries/data/mods/public/gui/lobby/lobby.js =================================================================== --- binaries/data/mods/public/gui/lobby/lobby.js +++ binaries/data/mods/public/gui/lobby/lobby.js @@ -53,6 +53,12 @@ "unknown": { "color": "178 178 178", "status": translateWithContext("lobby presence", "Unknown") } }; +const g_RoleNames = { + "moderator": translate("Moderator"), + "participant": translate("Player"), + "visitor": translate("Muted Player") +}; + /** * Color for error messages in the chat. */ @@ -136,6 +142,7 @@ for (let button of ["host", "leaderboard", "userprofile", "toggleBuddy"]) Engine.GetGUIObjectByName(button + "Button").enabled = false; + Engine.GetGUIObjectByName("chatInput").hidden = true; if (!g_Kicked) addChatMessage({ @@ -175,6 +182,40 @@ }, "presence": msg => { }, + "role": msg => { + Engine.GetGUIObjectByName("chatInput").hidden = Engine.LobbyGetPlayerRole(g_Username) == "visitor"; + + let me = g_Username == msg.text; + let role = Engine.LobbyGetPlayerRole(msg.text); + let txt = + role == "visitor" ? + me ? + translate("You have been muted.") : + translate("%(nick)s has been muted.") : + role == "moderator" ? + me ? + translate("You are now a moderator.") : + translate("%(nick)s is now a moderator.") : + msg.data == "visitor" ? + me ? + translate("You have been unmuted.") : + translate("%(nick)s has been unmuted.") : + me ? + translate("You are not a moderator anymore.") : + translate("%(nick)s is not a moderator anymore."); + + addChatMessage({ + "text": "/special " + sprintf(txt, { "nick": msg.text }), + "isSpecial": true + }); + + // Update status information if that player is selected + if (g_SelectedPlayer == msg.text) + { + let playersBox = Engine.GetGUIObjectByName("playersBox"); + playersBox.selected = playersBox.list.indexOf(g_SelectedPlayer); + } + }, "nick": msg => { addChatMessage({ "text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { @@ -657,9 +698,8 @@ Engine.SendGetProfile(playerName); - let isModerator = Engine.LobbyGetPlayerRole(playerName) == "moderator"; Engine.GetGUIObjectByName("usernameText").caption = playerName; - Engine.GetGUIObjectByName("roleText").caption = isModerator ? translate("Moderator") : translate("Player"); + Engine.GetGUIObjectByName("roleText").caption = g_RoleNames[Engine.LobbyGetPlayerRole(playerName)] Engine.GetGUIObjectByName("rankText").caption = translate("N/A"); Engine.GetGUIObjectByName("highestRatingText").caption = translate("N/A"); Engine.GetGUIObjectByName("totalGamesText").caption = translate("N/A"); Index: binaries/data/mods/public/gui/manual/intro.txt =================================================================== --- binaries/data/mods/public/gui/manual/intro.txt +++ binaries/data/mods/public/gui/manual/intro.txt @@ -68,8 +68,10 @@ Shift + Delete: Delete currently selected units/buildings without confirmation / (ForwardSlash): Select idle fighter Shift + /: add idle fighter to selection +Alt + /: Select all idle fighters . (Period): Select idle worker (including citizen soldiers) Shift + .: add idle worker to selection (including citizen soldiers) +Alt + .: Select all idle workers (including citizen soldiers) H: Stop (halt) the currently selected units. Y: The unit will go back to work U: Unload the garrisoned units of the selected buildings @@ -106,6 +108,7 @@ Alt + W: Toggle wireframe mode (press once to get wireframes overlaid over the textured models, twice to get just the wireframes colored by the textures, thrice to get back to normal textured mode) Alt + S: Toggle unit silhouettes (might give a small performance boost) Alt + Z: Toggle sky +Alt + V: Toggle aura range visualizations of selected units and structures [font="sans-bold-14"]Camera manipulation [font="sans-14"]W or \[up]: Pan screen up Index: binaries/data/mods/public/gui/options/options.js =================================================================== --- binaries/data/mods/public/gui/options/options.js +++ binaries/data/mods/public/gui/options/options.js @@ -171,6 +171,7 @@ if (callbackFunction) Engine[callbackFunction](+this.value); updateOptionPanel(); + control.tooltip = this.value; }; }(key, callbackFunction, minvalue, maxvalue); Index: binaries/data/mods/public/gui/options/options.json =================================================================== --- binaries/data/mods/public/gui/options/options.json +++ binaries/data/mods/public/gui/options/options.json @@ -205,10 +205,22 @@ "parameters": { "config": "vsync" } }, { + "type": "slider", + "label": "FPS throttling in menus", + "tooltip": "To save CPU workload, throttle render frequency in all menus. Set to maximum to disable throttling.", + "parameters": { "config": "adaptivefps.menu", "min": 20, "max": 100 } + }, + { + "type": "slider", + "label": "FPS throttling in games", + "tooltip": "To save CPU workload, throttle render frequency in running games. Set to maximum to disable throttling.", + "parameters": { "config": "adaptivefps.session", "min": 20, "max": 100 } + }, + { "type": "boolean", - "label": "Limit FPS in Menus", - "tooltip": "Limit FPS to 50 in all menus, to save power.", - "parameters": { "config": "gui.menu.limitfps" } + "label": "Aura Range Visualization", + "tooltip": "Display the range of auras of selected units and structures (can also be toggled in-game with the hotkey).", + "parameters": { "config": "gui.session.aurarange" } } ], "soundSetting": Index: binaries/data/mods/public/gui/pregame/mainmenu.js =================================================================== --- binaries/data/mods/public/gui/pregame/mainmenu.js +++ binaries/data/mods/public/gui/pregame/mainmenu.js @@ -212,7 +212,7 @@ var submenu = Engine.GetGUIObjectByName("submenu"); var top = position - MARGIN; var bottom = position + ((buttonHeight + MARGIN) * numButtons); - submenu.size = submenu.size.left + " " + top + " " + submenu.size.right + " " + bottom; + submenu.size = new GUISize(submenu.size.left, top, submenu.size.right, bottom); // Blend in right border of main menu into the left border of the submenu blendSubmenuIntoMain(top, bottom); Index: binaries/data/mods/public/gui/session/hotkeys/camera.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/camera.xml +++ binaries/data/mods/public/gui/session/hotkeys/camera.xml @@ -6,7 +6,7 @@ - performCommand(GetExtendedEntityState(g_Selection.getFirstSelected()), "focus-rally"); + performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "focus-rally"); Index: binaries/data/mods/public/gui/session/hotkeys/misc.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/misc.xml +++ binaries/data/mods/public/gui/session/hotkeys/misc.xml @@ -51,11 +51,7 @@ - - let ent = g_Selection.getFirstSelected(); - if (ent) - performCommand(GetExtendedEntityState(ent), "delete"); - + performCommand(g_Selection.toList().map(ent => GetExtendedEntityState(ent)), "delete"); @@ -96,4 +92,8 @@ clearSelection(); + + toggleRangeOverlay("Aura"); + + Index: binaries/data/mods/public/gui/session/input.js =================================================================== --- binaries/data/mods/public/gui/session/input.js +++ binaries/data/mods/public/gui/session/input.js @@ -423,7 +423,7 @@ let scale = +Engine.ConfigDB_GetValue("user", "gui.scale"); - bandbox.size = [x0 * scale, y0 * scale, x1 * scale, y1 * scale].join(" "); + bandbox.size = new GUISize(x0 * scale, y0 * scale, x1 * scale, y1 * scale); bandbox.hidden = hidden; return [x0, y0, x1, y1]; @@ -1423,7 +1423,7 @@ /** * Returns the number of units that will be present in a batch if the user clicks - * the training button with shift down + * the training button depending on the batch training modifier hotkey */ function getTrainingStatus(playerState, trainEntType, selection) { @@ -1445,10 +1445,13 @@ limits = getEntityLimitAndCount(playerState, trainEntType); // We need to calculate count after the next increment if it's possible - if (limits.canBeAddedCount == undefined || - limits.canBeAddedCount > nextBatchTrainingCount * appropriateBuildings.length) + if ((limits.canBeAddedCount == undefined || + limits.canBeAddedCount > nextBatchTrainingCount * appropriateBuildings.length) && + Engine.HotkeyIsPressed("session.batchtrain")) nextBatchTrainingCount += batchIncrementSize; + nextBatchTrainingCount = Math.max(nextBatchTrainingCount, 1); + // If training limits don't allow us to train batchTrainingCount in each appropriate building // train as many full batches as we can and remainer in one more building. var buildingsCountToTrainFullBatch = appropriateBuildings.length; @@ -1475,17 +1478,19 @@ g_Selection.makePrimarySelection(templateName, Engine.HotkeyIsPressed("session.deselectgroup") || deselectGroup); } -function performCommand(entState, commandName) +function performCommand(entStates, commandName) { - if (!entState) + if (!entStates.length) return; - if (!controlsPlayer(entState.player) && + // Don't check all entities, because we assume a player cannot + // select entities from more than one player + if (!controlsPlayer(entStates[0].player) && !(g_IsObserver && commandName == "focus-rally")) return; if (g_EntityCommands[commandName]) - g_EntityCommands[commandName].execute(entState); + g_EntityCommands[commandName].execute(entStates); } function performAllyCommand(entity, commandName) Index: binaries/data/mods/public/gui/session/selection_panels.js =================================================================== --- binaries/data/mods/public/gui/session/selection_panels.js +++ binaries/data/mods/public/gui/session/selection_panels.js @@ -133,15 +133,11 @@ for (let command in g_EntityCommands) { - for (let state of unitEntStates) + let info = g_EntityCommands[command].getInfo(unitEntStates); + if (info) { - let info = g_EntityCommands[command].getInfo(state); - if (info) - { - info.name = command; - commands.push(info); - break; - } + info.name = command; + commands.push(info); } } return commands; @@ -154,7 +150,7 @@ if (data.item.callback) data.item.callback(data.item); else - performCommand(data.unitEntStates[0], data.item.name); + performCommand(data.unitEntStates, data.item.name); }; data.countDisplay.caption = data.item.count || ""; @@ -231,7 +227,6 @@ size.right = size.left + size.bottom; data.button.size = size; - setPanelObjectPosition(data.button, data.i, data.rowLength); return true; } }; @@ -991,9 +986,7 @@ let [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] = getTrainingStatus(data.playerState, data.item, data.unitEntStates.map(status => status.id)); - let trainNum = buildingsCountToTrainFullBatch || 1; - if (Engine.HotkeyIsPressed("session.batchtrain")) - trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch; + let trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch; let neededResources; if (template.cost) @@ -1015,7 +1008,7 @@ getVisibleEntityClassesFormatted(template), getAurasTooltip(template), getEntityTooltip(template), - getEntityCostTooltip(template, trainNum, data.unitEntStates[0].id) + getEntityCostTooltip(template, data.unitEntStates[0].id, buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) ]; let limits = getEntityLimitAndCount(data.playerState, data.item); Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -792,6 +792,11 @@ handleNotifications(); updateGUIObjects(); + Engine.GuiInterfaceCall("EnableVisualRangeOverlayType", { + "type": "Aura", + "enabled": Engine.ConfigDB_GetValue("user", "gui.session.aurarange") == "true" + }); + if (g_ConfirmExit) confirmExit(); } @@ -973,7 +978,8 @@ getCurrentHealthTooltip, getAttackTooltip, getArmorTooltip, - getEntityTooltip + getEntityTooltip, + getAurasTooltip ].map(tooltip => tooltip(panelEntState)).filter(tip => tip).join("\n"); } @@ -1250,6 +1256,32 @@ }); } +/** + * Toggles the display of range overlays of selected entities for the given range type. + * @param {string} type - for example "Aura" + */ +function toggleRangeOverlay(type, currentValue) +{ + let configString = "gui.session." + type.toLowerCase() + "range"; + let enabled = Engine.ConfigDB_GetValue("user", configString) != "true"; + Engine.ConfigDB_CreateValue("user", configString, String(enabled)); + Engine.ConfigDB_WriteValueToFile("user", configString, String(enabled), "config/user.cfg"); + + Engine.GuiInterfaceCall("EnableVisualRangeOverlayType", { + "type": type, + "enabled": enabled + }); + + let selected = g_Selection.toList(); + for (let ent in g_Selection.highlighted) + selected.push(g_Selection.highlighted[ent]); + + Engine.GuiInterfaceCall("SetRangeOverlays", { + "entities": selected, + "enabled": enabled + }); +} + // Update the additional list of entities to be highlighted. function updateAdditionalHighlight() { @@ -1467,6 +1499,15 @@ "resourcesBought" ]; + let misc = [ + "tradeIncome", + "tributesSent", + "tributesReceived", + "treasuresCollected", + "lootCollected", + "percentMapExplored" + ]; + let playerStatistics = {}; // Unit Stats @@ -1501,19 +1542,13 @@ } playerStatistics.resourcesGathered.vegetarianFood = ""; - playerStatistics.tradeIncome = ""; - // Tribute - playerStatistics.tributesSent = ""; - playerStatistics.tributesReceived = ""; + for (let type of misc) + playerStatistics[type] = ""; + // Total playerStatistics.economyScore = ""; playerStatistics.militaryScore = ""; playerStatistics.totalScore = ""; - // Various - playerStatistics.treasuresCollected = ""; - playerStatistics.lootCollected = ""; - playerStatistics.feminisation = ""; - playerStatistics.percentMapExplored = ""; let mapName = g_GameAttributes.settings.Name; let playerStates = ""; @@ -1526,6 +1561,7 @@ for (let i = 1; i < extendedSimState.players.length; ++i) { let player = extendedSimState.players[i]; + let maxIndex = player.sequences.time.length - 1; playerStates += player.state + ","; playerCivs += player.civ + ","; @@ -1533,31 +1569,28 @@ teamsLocked = teamsLocked && player.teamsLocked; for (let resourcesCounterType of resourcesCounterTypes) for (let resourcesType of resourcesTypes) - playerStatistics[resourcesCounterType][resourcesType] += player.statistics[resourcesCounterType][resourcesType] + ","; - playerStatistics.resourcesGathered.vegetarianFood += player.statistics.resourcesGathered.vegetarianFood + ","; + playerStatistics[resourcesCounterType][resourcesType] += player.sequences[resourcesCounterType][resourcesType][maxIndex] + ","; + playerStatistics.resourcesGathered.vegetarianFood += player.sequences.resourcesGathered.vegetarianFood[maxIndex] + ","; for (let unitCounterType of unitsCountersTypes) for (let unitsClass of unitsClasses) - playerStatistics[unitCounterType][unitsClass] += player.statistics[unitCounterType][unitsClass] + ","; + playerStatistics[unitCounterType][unitsClass] += player.sequences[unitCounterType][unitsClass][maxIndex] + ","; for (let buildingCounterType of buildingsCountersTypes) for (let buildingsClass of buildingsClasses) - playerStatistics[buildingCounterType][buildingsClass] += player.statistics[buildingCounterType][buildingsClass] + ","; + playerStatistics[buildingCounterType][buildingsClass] += player.sequences[buildingCounterType][buildingsClass][maxIndex] + ","; let total = 0; - for (let type in player.statistics.resourcesGathered) - total += player.statistics.resourcesGathered[type]; + for (let type in player.sequences.resourcesGathered) + total += player.sequences.resourcesGathered[type][maxIndex]; playerStatistics.economyScore += total + ","; - playerStatistics.militaryScore += Math.round((player.statistics.enemyUnitsKilledValue + - player.statistics.enemyBuildingsDestroyedValue) / 10) + ","; - playerStatistics.totalScore += (total + Math.round((player.statistics.enemyUnitsKilledValue + - player.statistics.enemyBuildingsDestroyedValue) / 10)) + ","; - playerStatistics.tradeIncome += player.statistics.tradeIncome + ","; - playerStatistics.tributesSent += player.statistics.tributesSent + ","; - playerStatistics.tributesReceived += player.statistics.tributesReceived + ","; - playerStatistics.percentMapExplored += player.statistics.percentMapExplored + ","; - playerStatistics.treasuresCollected += player.statistics.treasuresCollected + ","; - playerStatistics.lootCollected += player.statistics.lootCollected + ","; + playerStatistics.militaryScore += Math.round((player.sequences.enemyUnitsKilledValue[maxIndex] + + player.sequences.enemyBuildingsDestroyedValue[maxIndex]) / 10) + ","; + playerStatistics.totalScore += (total + Math.round((player.sequences.enemyUnitsKilledValue[maxIndex] + + player.sequences.enemyBuildingsDestroyedValue[maxIndex]) / 10)) + ","; + + for (let type of misc) + playerStatistics[type] += player.sequences[type][maxIndex] + ","; } // Send the report with serialized data @@ -1595,12 +1628,8 @@ reportObject[(type.substr(0,1)).toLowerCase()+type.substr(1)+"BuildingsLost"] = playerStatistics.buildingsLost[type]; reportObject["enemy"+type+"BuildingsDestroyed"] = playerStatistics.enemyBuildingsDestroyed[type]; } - reportObject.tributesSent = playerStatistics.tributesSent; - reportObject.tributesReceived = playerStatistics.tributesReceived; - reportObject.percentMapExplored = playerStatistics.percentMapExplored; - reportObject.treasuresCollected = playerStatistics.treasuresCollected; - reportObject.lootCollected = playerStatistics.lootCollected; - reportObject.tradeIncome = playerStatistics.tradeIncome; + for (let type of misc) + reportObject[type] = playerStatistics[type]; Engine.SendGameReport(reportObject); } Index: binaries/data/mods/public/gui/session/unit_actions.js =================================================================== --- binaries/data/mods/public/gui/session/unit_actions.js +++ binaries/data/mods/public/gui/session/unit_actions.js @@ -136,9 +136,10 @@ return false; return { - "possible": Engine.GuiInterfaceCall("CanCapture", { + "possible": Engine.GuiInterfaceCall("CanAttack", { "entity": entState.id, - "target": targetState.id + "target": targetState.id, + "types": ["Capture"] }) }; }, @@ -183,7 +184,8 @@ return { "possible": Engine.GuiInterfaceCall("CanAttack", { "entity": entState.id, - "target": targetState.id + "target": targetState.id, + "types": ["!Capture"] }) }; }, @@ -443,7 +445,7 @@ return true; }, - "getActionInfo": function(entState, targetState) + "getActionInfo": function(entState, targetState) { if (!targetState.resourceSupply) return false; @@ -1034,18 +1036,15 @@ var g_EntityCommands = { "unload-all": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.garrisonHolder) - return false; - let count = 0; - for (let ent in g_Selection.selected) - { - let state = GetEntityState(+ent); - if (state.garrisonHolder) - count += state.garrisonHolder.entities.length; - } + for (let entState of entStates) + if (entState.garrisonHolder) + count += entState.garrisonHolder.entities.length; + + if (!count) + return false; return { "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.unload") + @@ -1054,20 +1053,16 @@ "count": count, }; }, - "execute": function(entState) + "execute": function() { unloadAll(); }, }, + "delete": { - "getInfo": function(entState) + "getInfo": function(entStates) { - let deleteReason = isUndeletable(entState); - return deleteReason ? - { - "tooltip": deleteReason, - "icon": "kill_small_disabled.png" - } : + return entStates.some(entState => !isUndeletable(entState)) ? { "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.kill") + @@ -1077,30 +1072,35 @@ "session.noconfirmation" ), "icon": "kill_small.png" + } : + { + // Get all delete reasons and remove duplications + "tooltip": entStates.map(entState => isUndeletable(entState)) + .filter((reason, pos, self) => + self.indexOf(reason) == pos && reason + ).join("\n"), + "icon": "kill_small_disabled.png" }; }, - "execute": function(entState) + "execute": function(entStates) { - if (isUndeletable(entState)) - return; - - let selection = g_Selection.toList(); - if (!selection.length) + if (!entStates.length || entStates.every(entState => isUndeletable(entState))) return; if (Engine.HotkeyIsPressed("session.noconfirmation")) Engine.PostNetworkCommand({ "type": "delete-entities", - "entities": selection + "entities": entStates.map(entState => entState.id) }); else - openDeleteDialog(selection); + openDeleteDialog(entStates.map(entState => entState.id)); }, }, + "stop": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.unitAI) + if (entStates.every(entState => !entState.unitAI)) return false; return { @@ -1109,18 +1109,17 @@ "icon": "stop.png" }; }, - "execute": function(entState) + "execute": function(entStates) { - let selection = g_Selection.toList(); - if (selection.length) - stopUnits(selection); + if (entStates.length) + stopUnits(entStates.map(entState => entState.id)); }, }, "garrison": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.unitAI || entState.turretParent) + if (entStates.every(entState => !entState.unitAI || entState.turretParent)) return false; return { @@ -1129,7 +1128,7 @@ "icon": "garrison.png" }; }, - "execute": function(entState) + "execute": function() { inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_GARRISON; @@ -1137,13 +1136,14 @@ }, "unload": { - "getInfo": function(entState) - { - if (!entState.unitAI || !entState.turretParent) - return false; - - let p = GetEntityState(entState.turretParent); - if (!p.garrisonHolder || p.garrisonHolder.entities.indexOf(entState.id) == -1) + "getInfo": function(entStates) + { + if (entStates.every(entState => { + if (!entState.unitAI || !entState.turretParent) + return true; + let parent = GetEntityState(entState.turretParent); + return !parent || !parent.garrisonHolder || parent.garrisonHolder.entities.indexOf(entState.id) == -1; + })) return false; return { @@ -1151,16 +1151,16 @@ "icon": "garrison-out.png" }; }, - "execute": function(entState) + "execute": function() { unloadSelection(); }, }, "repair": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.builder) + if (entStates.every(entState => !entState.builder)) return false; return { @@ -1169,7 +1169,7 @@ "icon": "repair.png" }; }, - "execute": function(entState) + "execute": function() { inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_REPAIR; @@ -1177,9 +1177,9 @@ }, "focus-rally": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.rallyPoint) + if (entStates.every(entState => !entState.rallyPoint)) return false; return { @@ -1188,13 +1188,23 @@ "icon": "focus-rally.png" }; }, - "execute": function(entState) + "execute": function(entStates) { + // TODO: Would be nicer to cycle between the rallypoints of multiple entities instead of just using the first let focusTarget; - if (entState.rallyPoint && entState.rallyPoint.position) - focusTarget = entState.rallyPoint.position; - else if (entState.position) - focusTarget = entState.position; + for (let entState of entStates) + if (entState.rallyPoint && entState.rallyPoint.position) + { + focusTarget = entState.rallyPoint.position; + break; + } + if (!focusTarget) + for (let entState of entStates) + if (entState.position) + { + focusTarget = entState.position; + break; + } if (focusTarget) Engine.CameraMoveTo(focusTarget.x, focusTarget.z); @@ -1202,9 +1212,9 @@ }, "back-to-work": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.unitAI || !entState.unitAI.hasWorkOrders) + if (entStates.every(entState => !entState.unitAI || !entState.unitAI.hasWorkOrders)) return false; return { @@ -1213,17 +1223,17 @@ "icon": "production.png" }; }, - "execute": function(entState) + "execute": function() { backToWork(); }, }, "add-guard": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.unitAI || !entState.unitAI.canGuard || - entState.unitAI.isGuarding) + if (entStates.every(entState => + !entState.unitAI || !entState.unitAI.canGuard || entState.unitAI.isGuarding)) return false; return { @@ -1232,7 +1242,7 @@ "icon": "add-guard.png" }; }, - "execute": function(entState) + "execute": function() { inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_GUARD; @@ -1240,9 +1250,9 @@ }, "remove-guard": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.unitAI || !entState.unitAI.isGuarding) + if (entStates.every(entState => !entState.unitAI || !entState.unitAI.isGuarding)) return false; return { @@ -1250,16 +1260,16 @@ "icon": "remove-guard.png" }; }, - "execute": function(entState) + "execute": function() { removeGuard(); }, }, "select-trading-goods": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.market) + if (entStates.every(entState => !entState.market)) return false; return { @@ -1267,16 +1277,16 @@ "icon": "economics.png" }; }, - "execute": function(entState) + "execute": function() { toggleTrade(); }, }, "patrol": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!someCanPatrol(g_Selection.toList())) + if (!entStates.some(entState => entState.unitAI && entState.unitAI.canPatrol)) return false; return { @@ -1286,7 +1296,7 @@ "icon": "patrol.png" }; }, - "execute": function(entState) + "execute": function() { inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_PATROL; @@ -1294,32 +1304,36 @@ }, "share-dropsite": { - "getInfo": function(entState) + "getInfo": function(entStates) { - if (!entState.resourceDropsite || !entState.resourceDropsite.sharable) + let sharableEntities = entStates.filter( + entState => entState.resourceDropsite && entState.resourceDropsite.sharable); + if (!sharableEntities.length) return false; - let playerState = GetSimState().players[entState.player]; - if (!playerState.isMutualAlly.some((e, i) => e && i != entState.player)) + // Returns if none of the entities belong to a player with a mutual ally + if (entStates.every(entState => !GetSimState().players[entState.player].isMutualAlly.some( + (isAlly, playerId) => isAlly && playerId != entState.player))) return false; - if (entState.resourceDropsite.shared) - return { + return sharableEntities.some(entState => !entState.resourceDropsite.shared) ? + { + "tooltip": translate("Press to allow allies to use this dropsite"), + "icon": "locked_small.png" + } : + { "tooltip": translate("Press to prevent allies from using this dropsite"), "icon": "unlocked_small.png" }; - - return { - "tooltip": translate("Press to allow allies to use this dropsite"), - "icon": "locked_small.png" - }; }, - "execute": function(entState) + "execute": function(entStates) { + let sharableEntities = entStates.filter( + entState => entState.resourceDropsite && entState.resourceDropsite.sharable); Engine.PostNetworkCommand({ "type": "set-dropsite-sharing", - "entities": g_Selection.toList(), - "shared": !entState.resourceDropsite.shared + "entities": sharableEntities.map(entState => entState.id), + "shared": sharableEntities.some(entState => !entState.resourceDropsite.shared) }); }, } Index: binaries/data/mods/public/gui/summary/counters.js =================================================================== --- binaries/data/mods/public/gui/summary/counters.js +++ binaries/data/mods/public/gui/summary/counters.js @@ -5,44 +5,27 @@ g_TeamHelperData = []; } -function formatTrained(trained, killed, lost) +function calculatePercent(divident, divisor) { - return g_TrainedColor + trained + '[/color] / ' + - g_KilledColor + killed + '[/color] / ' + - g_LostColor + lost + '[/color]'; + return { "percent": divisor ? Math.floor(100 * divident / divisor) : 0 }; } -function formatCaptured(constructed, destroyed, captured, lost) +function calculateRatio(divident, divisor) { - return g_TrainedColor + constructed + '[/color] / ' + - g_KilledColor + destroyed + '[/color]\n' + - g_CapturedColor + captured + '[/color] / ' + - g_LostColor + lost + '[/color]\n'; + return divident ? +((divident / divisor).toFixed(2)) : 0; } -function formatIncome(income, outcome) +function formatSummaryValue(values) { - return g_IncomeColor + income + '[/color] / ' + - g_OutcomeColor + outcome + '[/color]'; -} - -function formatPercent(divident, divisor) -{ - if (!divisor) - return "0%"; - - return Math.floor(100 * divident / divisor) + "%"; -} - -function formatRatio(divident, divisor) -{ - if (!divident) - return "0.00"; + if (typeof values != "object") + return values === Infinity ? g_InfinitySymbol : values; - if (!divisor) - return g_InfiniteSymbol; - - return Math.round(divident / divisor * 100) / 100; + let ret = ""; + for (let type in values) + ret += (g_SummaryTypes[type].color ? + '[color="' + g_SummaryTypes[type].color + '"]' + values[type] + '[/color]' : + values[type]) + g_SummaryTypes[type].postfix; + return ret; } /** @@ -59,83 +42,90 @@ return caption.replace(/\[([\w\' \\\"\/\=]*)\]|[\t\r \f]/g, ""); } -function updateCountersPlayer(playerState, counters, idGUI) +function updateCountersPlayer(playerState, counters, headings, idGUI) { + let index = playerState.sequences.time.length - 1; for (let w in counters) { let fn = counters[w].fn; - Engine.GetGUIObjectByName(idGUI + "[" + w + "]").caption = fn && fn(playerState, w); + Engine.GetGUIObjectByName(idGUI + "[" + w + "]").caption = formatSummaryValue(fn && fn(playerState, index, headings[+w+1].identifier)); } } +/** + * Add two arrays element-wise. So addArray([1, 2], [7, 42]) will result in [8, 44]. + * + * @param {Array} array1 - first summand array. + * @param {Array} array2 - second summand array. + * @returns {Array} the element-wise sum of array1 and array2. + */ +function addArray(array1, array2) +{ + array1 = array1.map((value, index) => value + array2[index]); +} + // Updates g_TeamHelperData by appending some data from playerState function calculateTeamCounters(playerState) { if (!g_TeamHelperData[playerState.team]) - g_TeamHelperData[playerState.team] = { - "food": 0, - "vegetarianFood": 0, - "femaleCitizen": 0, - "worker": 0, - "enemyUnitsKilled": 0, - "unitsLost": 0, - "percentMapControlled": 0, - "peakPercentMapControlled": 0, - "percentMapExplored": 0, - "totalBought": 0, - "totalSold": 0 - }; + { + g_TeamHelperData[playerState.team] = {}; + for (let value of ["food", "vegetarianFood", "femaleCitizen", "worker", "enemyUnitsKilled", + "unitsLost", "percentMapControlled", "peakPercentMapControlled", + "percentMapExplored", "totalBought", "totalSold"]) + g_TeamHelperData[playerState.team][value] = new Array(playerState.sequences.time.length).fill(0); + } - g_TeamHelperData[playerState.team].food += playerState.statistics.resourcesGathered.food; - g_TeamHelperData[playerState.team].vegetarianFood += playerState.statistics.resourcesGathered.vegetarianFood; + addArray(g_TeamHelperData[playerState.team].food, playerState.sequences.resourcesGathered.food); + addArray(g_TeamHelperData[playerState.team].vegetarianFood, playerState.sequences.resourcesGathered.vegetarianFood); - g_TeamHelperData[playerState.team].femaleCitizen += playerState.statistics.unitsTrained.FemaleCitizen; - g_TeamHelperData[playerState.team].worker += playerState.statistics.unitsTrained.Worker; + addArray(g_TeamHelperData[playerState.team].femaleCitizen, playerState.sequences.unitsTrained.FemaleCitizen); + addArray(g_TeamHelperData[playerState.team].worker, playerState.sequences.unitsTrained.Worker); - g_TeamHelperData[playerState.team].enemyUnitsKilled += playerState.statistics.enemyUnitsKilled.total; - g_TeamHelperData[playerState.team].unitsLost += playerState.statistics.unitsLost.total; + addArray(g_TeamHelperData[playerState.team].enemyUnitsKilled, playerState.sequences.enemyUnitsKilled.total); + addArray(g_TeamHelperData[playerState.team].unitsLost, playerState.sequences.unitsLost.total); - g_TeamHelperData[playerState.team].percentMapControlled = playerState.statistics.teamPercentMapControlled; - g_TeamHelperData[playerState.team].peakPercentMapControlled = playerState.statistics.teamPeakPercentMapControlled; + g_TeamHelperData[playerState.team].percentMapControlled = playerState.sequences.teamPercentMapControlled; + g_TeamHelperData[playerState.team].peakPercentMapControlled = playerState.sequences.teamPeakPercentMapControlled; - g_TeamHelperData[playerState.team].percentMapExplored = playerState.statistics.teamPercentMapExplored; + g_TeamHelperData[playerState.team].percentMapExplored = playerState.sequences.teamPercentMapExplored; - for (let type in playerState.statistics.resourcesBought) - g_TeamHelperData[playerState.team].totalBought += playerState.statistics.resourcesBought[type]; + for (let type in playerState.sequences.resourcesBought) + addArray(g_TeamHelperData[playerState.team].totalBought, playerState.sequences.resourcesBought[type]); - for (let type in playerState.statistics.resourcesSold) - g_TeamHelperData[playerState.team].totalSold += playerState.statistics.resourcesSold[type]; + for (let type in playerState.sequences.resourcesSold) + addArray(g_TeamHelperData[playerState.team].totalSold, playerState.sequences.resourcesSold[type]); } -function calculateEconomyScore(playerState) +function calculateEconomyScore(playerState, index) { let total = 0; - for (let type in playerState.statistics.resourcesGathered) - total += playerState.statistics.resourcesGathered[type]; + for (let type in playerState.sequences.resourcesGathered) + total += playerState.sequences.resourcesGathered[type][index]; return Math.round(total / 10); } -function calculateMilitaryScore(playerState) +function calculateMilitaryScore(playerState, index) { - return Math.round((playerState.statistics.enemyUnitsKilledValue + - playerState.statistics.enemyBuildingsDestroyedValue + - playerState.statistics.buildingsCapturedValue) / 10); + return Math.round((playerState.sequences.enemyUnitsKilledValue[index] + + playerState.sequences.enemyBuildingsDestroyedValue[index] + + playerState.sequences.buildingsCapturedValue[index]) / 10); } -function calculateExplorationScore(playerState) +function calculateExplorationScore(playerState, index) { - return playerState.statistics.percentMapExplored * 10; + return playerState.sequences.percentMapExplored[index] * 10; } -function calculateScoreTotal(playerState) +function calculateScoreTotal(playerState, index) { - return calculateEconomyScore(playerState) + - calculateMilitaryScore(playerState) + - calculateExplorationScore(playerState); + return calculateEconomyScore(playerState, index) + + calculateMilitaryScore(playerState, index) + + calculateExplorationScore(playerState, index); } -function calculateScoreTeam(counters) +function calculateScoreTeam(counters, index) { for (let t in g_Teams) { @@ -148,7 +138,7 @@ let total = 0; if (w == 2) // Team exploration score (not additive) - total = g_TeamHelperData[t].percentMapExplored * 10; + total = g_TeamHelperData[t].percentMapExplored[index] * 10; else for (let p = 0; p < g_Teams[t]; ++p) total += +Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + p + "][" + w + "]").caption; @@ -164,17 +154,17 @@ } } -function calculateBuildings(playerState, position) +function calculateBuildings(playerState, index, type) { - let type = g_BuildingsTypes[position]; - return formatCaptured( - playerState.statistics.buildingsConstructed[type], - playerState.statistics.enemyBuildingsDestroyed[type], - playerState.statistics.buildingsCaptured[type], - playerState.statistics.buildingsLost[type]); + return { + "constructed": playerState.sequences.buildingsConstructed[type][index], + "destroyed": playerState.sequences.enemyBuildingsDestroyed[type][index], + "captured": playerState.sequences.buildingsCaptured[type][index], + "lost": playerState.sequences.buildingsLost[type][index] + }; } -function calculateBuildingsTeam(counters) +function calculateBuildingsTeam(counters, index) { for (let t in g_Teams) { @@ -203,12 +193,12 @@ } Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = - formatCaptured(total.constructed, total.destroyed, total.captured, total.lost); + formatSummaryValue(total); } } } -function calculateUnitsTeam(counters) +function calculateUnitsTeam(counters, index) { for (let t in g_Teams) { @@ -227,100 +217,89 @@ for (let p = 0; p < g_Teams[t]; ++p) { + let splitCaption = cleanGUICaption(t, p, w, false).split("\n"); + let first = splitCaption[0].split("/"); + total.trained += +first[0]; + total.killed += +first[1]; + if (w == 0 || w == 6) { - let splitCaption = cleanGUICaption(t, p, w, false).split("\n"); - let first = splitCaption[0].split("/"); let second = splitCaption[1].split("/"); - - total.trained += +first[0]; - total.killed += +first[1]; total.captured += +second[0]; total.lost += +second[1]; } else - { - let splitCaption = cleanGUICaption(t, p, w).split("/"); - total.trained += +splitCaption[0]; - total.killed += +splitCaption[1]; - total.lost += +splitCaption[2]; - } + total.lost += +splitCaption[1]; } - let formattedCaption = ""; + if (w != 0 && w != 6) + delete total.captured; - if (w == 0 || w == 6) - formattedCaption = formatCaptured(total.trained, total.killed, total.captured, total.lost); - else - formattedCaption = formatTrained(total.trained, total.killed, total.lost); - - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formattedCaption; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(total); } } } -function calculateUnitsWithCaptured(playerState, position) +function calculateUnitsWithCaptured(playerState, index, type) { - let type = g_UnitsTypes[position]; - - return formatCaptured( - playerState.statistics.unitsTrained[type], - playerState.statistics.enemyUnitsKilled[type], - playerState.statistics.unitsCaptured[type], - playerState.statistics.unitsLost[type]); + return { + "trained": playerState.sequences.unitsTrained[type][index], + "killed": playerState.sequences.enemyUnitsKilled[type][index], + "captured": playerState.sequences.unitsCaptured[type][index], + "lost": playerState.sequences.unitsLost[type][index] + }; } -function calculateUnits(playerState, position) +function calculateUnits(playerState, index, type) { - let type = g_UnitsTypes[position]; - - return formatTrained( - playerState.statistics.unitsTrained[type], - playerState.statistics.enemyUnitsKilled[type], - playerState.statistics.unitsLost[type]); + return { + "trained": playerState.sequences.unitsTrained[type][index], + "killed": playerState.sequences.enemyUnitsKilled[type][index], + "lost": playerState.sequences.unitsLost[type][index] + }; } -function calculateResources(playerState, position) +function calculateResources(playerState, index, type) { - let type = g_ResourceData.GetCodes()[position]; - - return formatIncome( - playerState.statistics.resourcesGathered[type], - playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type]); + return { + "gathered": playerState.sequences.resourcesGathered[type][index], + "used": playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index] + }; } -function calculateTotalResources(playerState) +function calculateTotalResources(playerState, index) { let totalGathered = 0; let totalUsed = 0; for (let type of g_ResourceData.GetCodes()) { - totalGathered += playerState.statistics.resourcesGathered[type]; - totalUsed += playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type]; + totalGathered += playerState.sequences.resourcesGathered[type][index]; + totalUsed += playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index]; } - return formatIncome(totalGathered, totalUsed); + return { "gathered": totalGathered, "used": totalUsed }; } -function calculateTreasureCollected(playerState) +function calculateTreasureCollected(playerState, index) { - return playerState.statistics.treasuresCollected; + return playerState.sequences.treasuresCollected[index]; } -function calculateLootCollected(playerState) +function calculateLootCollected(playerState, index) { - return playerState.statistics.lootCollected; + return playerState.sequences.lootCollected[index]; } -function calculateTributeSent(playerState) +function calculateTributeSent(playerState, index) { - return formatIncome( - playerState.statistics.tributesSent, - playerState.statistics.tributesReceived); + return { + "sent": playerState.sequences.tributesSent[index], + "received": playerState.sequences.tributesReceived[index] + }; } -function calculateResourcesTeam(counters) +function calculateResourcesTeam(counters, index) { for (let t in g_Teams) { @@ -352,43 +331,44 @@ let teamTotal; if (w >= 6) teamTotal = total.income; + else if (w == 5) + teamTotal = { "sent": total.income, "received": total.outcome }; else - teamTotal = formatIncome(total.income, total.outcome); + teamTotal = { "gathered": total.income, "used": total.outcome }; - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(teamTotal); } } } -function calculateResourceExchanged(playerState, position) +function calculateResourceExchanged(playerState, index, type) { - let type = g_ResourceData.GetCodes()[position]; - - return formatIncome( - playerState.statistics.resourcesBought[type], - playerState.statistics.resourcesSold[type]); + return { + "bought": playerState.sequences.resourcesBought[type][index], + "sold": playerState.sequences.resourcesSold[type][index] + }; } -function calculateBarterEfficiency(playerState) +function calculateBarterEfficiency(playerState, index) { let totalBought = 0; let totalSold = 0; - for (let type in playerState.statistics.resourcesBought) - totalBought += playerState.statistics.resourcesBought[type]; + for (let type in playerState.sequences.resourcesBought) + totalBought += playerState.sequences.resourcesBought[type][index]; - for (let type in playerState.statistics.resourcesSold) - totalSold += playerState.statistics.resourcesSold[type]; + for (let type in playerState.sequences.resourcesSold) + totalSold += playerState.sequences.resourcesSold[type][index]; - return formatPercent(totalBought, totalSold); + return calculatePercent(totalBought, totalSold); } -function calculateTradeIncome(playerState) +function calculateTradeIncome(playerState, index) { - return playerState.statistics.tradeIncome; + return playerState.sequences.tradeIncome[index]; } -function calculateMarketTeam(counters) +function calculateMarketTeam(counters, index) { for (let t in g_Teams) { @@ -418,54 +398,54 @@ let teamTotal; if (w == 4) - teamTotal = formatPercent(g_TeamHelperData[t].totalBought, g_TeamHelperData[t].totalSold); + teamTotal = calculatePercent(g_TeamHelperData[t].totalBought[index], g_TeamHelperData[t].totalSold[index]); else if (w > 4) teamTotal = total.income; else - teamTotal = formatIncome(total.income, total.outcome); + teamTotal = total; - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(teamTotal); } } } -function calculateVegetarianRatio(playerState) +function calculateVegetarianRatio(playerState, index) { - return formatPercent( - playerState.statistics.resourcesGathered.vegetarianFood, - playerState.statistics.resourcesGathered.food); + return calculatePercent( + playerState.sequences.resourcesGathered.vegetarianFood[index], + playerState.sequences.resourcesGathered.food[index]); } -function calculateFeminization(playerState) +function calculateFeminization(playerState, index) { - return formatPercent( - playerState.statistics.unitsTrained.FemaleCitizen, - playerState.statistics.unitsTrained.Worker); + return calculatePercent( + playerState.sequences.unitsTrained.FemaleCitizen[index], + playerState.sequences.unitsTrained.Worker[index]); } -function calculateKillDeathRatio(playerState) +function calculateKillDeathRatio(playerState, index) { - return formatRatio( - playerState.statistics.enemyUnitsKilled.total, - playerState.statistics.unitsLost.total); + return calculateRatio( + playerState.sequences.enemyUnitsKilled.total[index], + playerState.sequences.unitsLost.total[index]); } -function calculateMapExploration(playerState) +function calculateMapExploration(playerState, index) { - return playerState.statistics.percentMapExplored + "%"; + return { "percent": playerState.sequences.percentMapExplored[index] }; } -function calculateMapFinalControl(playerState) +function calculateMapFinalControl(playerState, index) { - return playerState.statistics.percentMapControlled + "%"; + return { "percent": playerState.sequences.percentMapControlled[index] }; } -function calculateMapPeakControl(playerState) +function calculateMapPeakControl(playerState, index) { - return playerState.statistics.peakPercentMapControlled + "%"; + return { "percent": playerState.sequences.peakPercentMapControlled[index] }; } -function calculateMiscellaneous(counters) +function calculateMiscellaneousTeam(counters, index) { for (let t in g_Teams) { @@ -477,19 +457,19 @@ let teamTotal; if (w == 0) - teamTotal = formatPercent(g_TeamHelperData[t].vegetarianFood, g_TeamHelperData[t].food); + teamTotal = calculatePercent(g_TeamHelperData[t].vegetarianFood[index], g_TeamHelperData[t].food[index]); else if (w == 1) - teamTotal = formatPercent(g_TeamHelperData[t].femaleCitizen, g_TeamHelperData[t].worker); + teamTotal = calculatePercent(g_TeamHelperData[t].femaleCitizen[index], g_TeamHelperData[t].worker[index]); else if (w == 2) - teamTotal = formatRatio(g_TeamHelperData[t].enemyUnitsKilled, g_TeamHelperData[t].unitsLost); + teamTotal = calculateRatio(g_TeamHelperData[t].enemyUnitsKilled[index], g_TeamHelperData[t].unitsLost[index]); else if (w == 3) - teamTotal = g_TeamHelperData[t].percentMapExplored + "%"; + teamTotal = { "percent": g_TeamHelperData[t].percentMapExplored[index] }; else if (w == 4) - teamTotal = g_TeamHelperData[t].peakPercentMapControlled + "%"; + teamTotal = { "percent": g_TeamHelperData[t].peakPercentMapControlled[index] }; else if (w == 5) - teamTotal = g_TeamHelperData[t].percentMapControlled + "%"; + teamTotal = { "percent": g_TeamHelperData[t].percentMapControlled[index] }; - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(teamTotal); } } } Index: binaries/data/mods/public/gui/summary/layout.js =================================================================== --- binaries/data/mods/public/gui/summary/layout.js +++ binaries/data/mods/public/gui/summary/layout.js @@ -1,11 +1,12 @@ var g_ScorePanelsData = { "score": { + "caption": translate("Score"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Economy score"), "yStart": 16, "width": 100 }, - { "caption": translate("Military score"), "yStart": 16, "width": 100 }, - { "caption": translate("Exploration score"), "yStart": 16, "width": 100 }, - { "caption": translate("Total score"), "yStart": 16, "width": 100 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "economyScore", "caption": translate("Economy score"), "yStart": 16, "width": 100 }, + { "identifier": "militaryScore", "caption": translate("Military score"), "yStart": 16, "width": 100 }, + { "identifier": "explorationScore", "caption": translate("Exploration score"), "yStart": 16, "width": 100 }, + { "identifier": "totalScore", "caption": translate("Total score"), "yStart": 16, "width": 100 } ], "titleHeadings": [], "counters": [ @@ -17,28 +18,29 @@ "teamCounterFn": calculateScoreTeam }, "buildings": { + "caption": translate("Buildings"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Total"), "yStart": 34, "width": 105 }, - { "caption": translate("Houses"), "yStart": 34, "width": 85 }, - { "caption": translate("Economic"), "yStart": 34, "width": 85 }, - { "caption": translate("Outposts"), "yStart": 34, "width": 85 }, - { "caption": translate("Military"), "yStart": 34, "width": 85 }, - { "caption": translate("Fortresses"), "yStart": 34, "width": 85 }, - { "caption": translate("Civ centers"), "yStart": 34, "width": 85 }, - { "caption": translate("Wonders"), "yStart": 34, "width": 85 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 105 }, + { "identifier": "House", "caption": translate("Houses"), "yStart": 34, "width": 85 }, + { "identifier": "Economic", "caption": translate("Economic"), "yStart": 34, "width": 85 }, + { "identifier": "Outpost", "caption": translate("Outposts"), "yStart": 34, "width": 85 }, + { "identifier": "Military", "caption": translate("Military"), "yStart": 34, "width": 85 }, + { "identifier": "Fortress", "caption": translate("Fortresses"), "yStart": 34, "width": 85 }, + { "identifier": "CivCentre", "caption": translate("Civ centers"), "yStart": 34, "width": 85 }, + { "identifier": "Wonder", "caption": translate("Wonders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Buildings Statistics (%(constructed)s / %(destroyed)s / %(captured)s / %(lost)s)"), { - "constructed": g_TrainedColor + translate("Constructed") + '[/color]', - "destroyed": g_KilledColor + translate("Destroyed") + '[/color]', - "captured": g_CapturedColor + translate("Captured") + '[/color]', - "lost": g_LostColor + translate("Lost") + '[/color]' + "constructed": getColoredTypeTranslation("constructed"), + "destroyed": getColoredTypeTranslation("destroyed"), + "captured": getColoredTypeTranslation("captured"), + "lost": getColoredTypeTranslation("lost") }), "yStart": 16, - "width": (85 * 7 + 105) + "width": 85 * 7 + 105 }, // width = 700 ], "counters": [ @@ -54,71 +56,75 @@ "teamCounterFn": calculateBuildingsTeam }, "units": { + "caption": translate("Units"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Total"), "yStart": 34, "width": 105 }, - { "caption": translate("Infantry"), "yStart": 34, "width": 85 }, - { "caption": translate("Worker"), "yStart": 34, "width": 85 }, - { "caption": translate("Cavalry"), "yStart": 34, "width": 85 }, - { "caption": translate("Champion"), "yStart": 34, "width": 85 }, - { "caption": translate("Heroes"), "yStart": 34, "width": 85 }, - { "caption": translate("Siege"), "yStart": 34, "width": 85 }, - { "caption": translate("Navy"), "yStart": 34, "width": 85 }, - { "caption": translate("Traders"), "yStart": 34, "width": 85 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 105 }, + { "identifier": "Infantry", "caption": translate("Infantry"), "yStart": 34, "width": 85 }, + { "identifier": "Worker", "caption": translate("Worker"), "yStart": 34, "width": 85 }, + { "identifier": "Cavalry", "caption": translate("Cavalry"), "yStart": 34, "width": 85 }, + { "identifier": "Champion", "caption": translate("Champion"), "yStart": 34, "width": 85 }, + { "identifier": "Hero", "caption": translate("Heroes"), "yStart": 34, "width": 85 }, + { "identifier": "Siege", "caption": translate("Siege"), "yStart": 34, "width": 85 }, + { "identifier": "Ship", "caption": translate("Navy"), "yStart": 34, "width": 85 }, + { "identifier": "Trader", "caption": translate("Traders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Units Statistics (%(trained)s / %(killed)s / %(captured)s / %(lost)s)"), { - "trained": g_TrainedColor + translate("Trained") + '[/color]', - "killed": g_KilledColor + translate("Killed") + '[/color]', - "captured": g_CapturedColor + translate("Captured") + '[/color]', - "lost": g_LostColor + translate("Lost") + '[/color]' + "trained": getColoredTypeTranslation("trained"), + "killed": getColoredTypeTranslation("killed"), + "captured": getColoredTypeTranslation("captured"), + "lost": getColoredTypeTranslation("lost") }), "yStart": 16, - "width": (100 * 7 + 120) - }, // width = 820 + "width": 85 * 8 + 105 + }, // width = 785 ], "counters": [ { "width": 105, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 105, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 } + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 } ], "teamCounterFn": calculateUnitsTeam }, "resources": { + "caption": translate("Resources"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, ...g_ResourceData.GetResources().map(res => ({ + "identifier": res.code, "caption": translateWithContext("firstWord", res.name), "yStart": 34, "width": 100 })), - { "caption": translate("Total"), "yStart": 34, "width": 110 }, + { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 110 }, { + "identifier": "tributes", "caption": sprintf(translate("Tributes \n(%(sent)s / %(received)s)"), { - "sent": g_IncomeColor + translate("Sent") + '[/color]', - "received": g_OutcomeColor + translate("Received") + '[/color]' + "sent": getColoredTypeTranslation("sent"), + "received": getColoredTypeTranslation("received") }), "yStart": 16, "width": 121 }, - { "caption": translate("Treasures collected"), "yStart": 16, "width": 100 }, - { "caption": translate("Loot"), "yStart": 16, "width": 100 } + { "identifier": "treasuresCollected", "caption": translate("Treasures collected"), "yStart": 16, "width": 100 }, + { "identifier": "loot", "caption": translate("Loot"), "yStart": 16, "width": 100 } ], "titleHeadings": [ { "caption": sprintf(translate("Resource Statistics (%(gathered)s / %(used)s)"), { - "gathered": g_IncomeColor + translate("Gathered") + '[/color]', - "used": g_OutcomeColor + translate("Used") + '[/color]' + "gathered": getColoredTypeTranslation("gathered"), + "used": getColoredTypeTranslation("used") }), "yStart": 16, "width": 100 * g_ResourceData.GetCodes().length + 110 @@ -138,10 +144,12 @@ "teamCounterFn": calculateResourcesTeam }, "market": { + "caption": translate("Market"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, ...g_ResourceData.GetResources().map(res => { return { + "identifier": res.code, "caption": // Translation: use %(resourceWithinSentence)s if needed sprintf(translate("%(resourceFirstWord)s exchanged"), { @@ -152,8 +160,8 @@ "width": 100 }; }), - { "caption": translate("Barter efficiency"), "yStart": 16, "width": 100 }, - { "caption": translate("Trade income"), "yStart": 16, "width": 100 } + { "identifier": "barterEfficency", "caption": translate("Barter efficiency"), "yStart": 16, "width": 100 }, + { "identifier": "tradeIncome", "caption": translate("Trade income"), "yStart": 16, "width": 100 } ], "titleHeadings": [], "counters": [ @@ -168,18 +176,17 @@ "teamCounterFn": calculateMarketTeam }, "misc": { + "caption": translate("Miscellaneous"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Vegetarian\nratio"), "yStart": 16, "width": 100 }, - { "caption": translate("Feminization"), "yStart": 16, "width": 100 }, - { "caption": translate("Kill / Death\nratio"), "yStart": 16, "width": 100 }, - { "caption": translate("Map\nexploration"), "yStart": 16, "width": 100 }, - { "caption": translate("At peak"), "yStart": 34, "width": 100 }, - { "caption": translate("At finish"), "yStart": 34, "width": 100 } - ], - "titleHeadings": [ - { "caption": translate("Map control"), "xOffset": 400, "yStart": 16, "width": 200 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "vegetarianRatio", "caption": translate("Vegetarian ratio"), "yStart": 16, "width": 100 }, + { "identifier": "feminization", "caption": translate("Feminization"), "yStart": 16, "width": 100 }, + { "identifier": "killDeath", "caption": translate("Kill / Death ratio"), "yStart": 16, "width": 100 }, + { "identifier": "mapExploration", "caption": translate("Map exploration"), "yStart": 16, "width": 100 }, + { "identifier": "mapControlPeak", "caption": translate("Map control (peak)"), "yStart": 16, "width": 100 }, + { "identifier": "mapControlFinish", "caption": translate("Map control (finish)"), "yStart": 16, "width": 100 } ], + "titleHeadings": [], "counters": [ { "width": 100, "fn": calculateVegetarianRatio, "verticalOffset": 12 }, { "width": 100, "fn": calculateFeminization, "verticalOffset": 12 }, @@ -188,10 +195,15 @@ { "width": 100, "fn": calculateMapPeakControl, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapFinalControl, "verticalOffset": 12 } ], - "teamCounterFn": calculateMiscellaneous + "teamCounterFn": calculateMiscellaneousTeam } }; +function getColoredTypeTranslation(type) +{ + return g_SummaryTypes[type].color ? '[color="' + g_SummaryTypes[type].color + '"]' + g_SummaryTypes[type].caption + '[/color]' : g_SummaryTypes[type].caption; +} + function resetGeneralPanel() { for (let h = 0; h < g_MaxHeadingTitle; ++h) Index: binaries/data/mods/public/gui/summary/summary.js =================================================================== --- binaries/data/mods/public/gui/summary/summary.js +++ binaries/data/mods/public/gui/summary/summary.js @@ -9,20 +9,95 @@ const g_PlayerColorBoxAlpha = " 255"; const g_TeamsBoxYStart = 40; -// Colors used for units and buildings -const g_TrainedColor = '[color="201 255 200"]'; -const g_LostColor = '[color="255 213 213"]'; -const g_KilledColor = '[color="196 198 255"]'; -const g_CapturedColor = '[color="255 255 157"]'; - -const g_BuildingsTypes = [ "total", "House", "Economic", "Outpost", "Military", "Fortress", "CivCentre", "Wonder" ]; -const g_UnitsTypes = [ "total", "Infantry", "Worker", "Cavalry", "Champion", "Hero", "Siege", "Ship", "Trader" ]; +const g_TypeColors = { + "blue": "196 198 255", + "green": "201 255 200", + "red": "255 213 213", + "yellow": "255 255 157" +} -// Colors used for gathered and traded resources -const g_IncomeColor = '[color="201 255 200"]'; -const g_OutcomeColor = '[color="255 213 213"]'; +/** + * Colors, captions and format used for units, buildings, etc. types + */ +var g_SummaryTypes = { + "percent": { + "color": "", + "caption": "%", + "postfix": "%" + }, + "trained": { + "color": g_TypeColors.green, + "caption": translate("Trained"), + "postfix": " / " + }, + "constructed": { + "color": g_TypeColors.green, + "caption": translate("Constructed"), + "postfix": " / " + }, + "gathered": { + "color": g_TypeColors.green, + "caption": translate("Gathered"), + "postfix": " / " + }, + "sent": { + "color": g_TypeColors.green, + "caption": translate("Sent"), + "postfix": " / " + }, + "bought": { + "color": g_TypeColors.green, + "caption": translate("Bought"), + "postfix": " / " + }, + "income": { + "color": g_TypeColors.green, + "caption": translate("Income"), + "postfix": " / " + }, + "captured": { + "color": g_TypeColors.yellow, + "caption": translate("Captured"), + "postfix": " / " + }, + "destroyed": { + "color": g_TypeColors.blue, + "caption": translate("Destroyed"), + "postfix": "\n" + }, + "killed": { + "color": g_TypeColors.blue, + "caption": translate("Killed"), + "postfix": "\n" + }, + "lost": { + "color": g_TypeColors.red, + "caption": translate("Lost"), + "postfix": "\n" + }, + "used": { + "color": g_TypeColors.red, + "caption": translate("Used"), + "postfix": "\n" + }, + "received": { + "color": g_TypeColors.red, + "caption": translate("Recieved"), + "postfix": "\n" + }, + "sold": { + "color": g_TypeColors.red, + "caption": translate("Sold"), + "postfix": "\n" + }, + "outcome": { + "color": g_TypeColors.red, + "caption": translate("Outcome"), + "postfix": "\n" + } +}; -const g_InfiniteSymbol = "\u221E"; +const g_InfinitySymbol = "\u221E"; var g_CivData = loadCivData(); var g_Teams = []; @@ -47,7 +122,132 @@ adjustTabDividers(panel.size); - updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]); + let generalPanel = Engine.GetGUIObjectByName("generalPanel"); + let chartsPanel = Engine.GetGUIObjectByName("chartsPanel"); + let chartsHidden = panel.name != "chartsPanelButton"; + generalPanel.hidden = !chartsHidden; + chartsPanel.hidden = chartsHidden; + if (chartsHidden) + updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]); + else + [0, 1].forEach(updateCategoryDropdown); +} + +function initCharts() +{ + let player_colors = []; + for (let i = 1; i <= g_PlayerCount; ++i) + { + let playerState = g_GameData.sim.playerStates[i]; + player_colors.push( + Math.floor(playerState.color.r * 255) + " " + + Math.floor(playerState.color.g * 255) + " " + + Math.floor(playerState.color.b * 255) + ); + } + + [0, 1].forEach(i => Engine.GetGUIObjectByName("chart[" + i + "]").series_color = player_colors); + + let chartLegend = Engine.GetGUIObjectByName("chartLegend"); + chartLegend.caption = g_GameData.sim.playerStates.slice(1).map( + (state, index) => '[color="' + player_colors[index] + '"]■[/color] ' + state.name + ).join(" "); + + let chart1Part = Engine.GetGUIObjectByName("chart[1]Part"); + let chart1PartSize = chart1Part.size; + chart1PartSize.rright += 50; + chart1PartSize.rleft += 50; + chart1PartSize.right -= 5; + chart1PartSize.left -= 5; + chart1Part.size = chart1PartSize; +} + +function resizeDropdown(dropdown) +{ + let size = dropdown.size; + size.bottom = dropdown.size.top + + (Engine.GetTextWidth(dropdown.font, dropdown.list[dropdown.selected]) > + dropdown.size.right - dropdown.size.left - 32 ? 42 : 27); + dropdown.size = size; +} + +function updateCategoryDropdown(number) +{ + let chartCategory = Engine.GetGUIObjectByName("chart[" + number + "]CategorySelection"); + chartCategory.list_data = Object.keys(g_ScorePanelsData); + chartCategory.list = Object.keys(g_ScorePanelsData).map(panel => g_ScorePanelsData[panel].caption); + chartCategory.onSelectionChange = function() { + if (!this.list_data[this.selected]) + return; + resizeDropdown(this); + updateValueDropdown(number, this.list_data[this.selected]); + }; + chartCategory.selected = 0; +} + +function updateValueDropdown(number, category) +{ + let chartValue = Engine.GetGUIObjectByName("chart[" + number + "]ValueSelection"); + let list = g_ScorePanelsData[category].headings.map(heading => heading.caption); + list.shift(); + chartValue.list = list; + let list_data = g_ScorePanelsData[category].headings.map(heading => heading.identifier); + list_data.shift(); + chartValue.list_data = list_data; + chartValue.onSelectionChange = function() { + if (!this.list_data[this.selected]) + return; + resizeDropdown(this); + updateTypeDropdown(number, category, this.list_data[this.selected], this.selected); + }; + chartValue.selected = 0; +} + +function updateTypeDropdown(number, category, item, itemNumber) +{ + let testValue = g_ScorePanelsData[category].counters[itemNumber].fn(g_GameData.sim.playerStates[1], 0, item); + let hide = !g_ScorePanelsData[category].counters[itemNumber].fn || + typeof testValue != "object" || Object.keys(testValue).length < 2; + Engine.GetGUIObjectByName("chart[" + number + "]TypeLabel").hidden = hide; + let chartType = Engine.GetGUIObjectByName("chart[" + number + "]TypeSelection"); + chartType.hidden = hide; + if (hide) + { + updateChart(number, category, item, itemNumber, Object.keys(testValue)[0] || undefined); + return; + } + + chartType.list = Object.keys(testValue).map(type => g_SummaryTypes[type].caption); + chartType.list_data = Object.keys(testValue); + chartType.onSelectionChange = function() { + if (!this.list_data[this.selected]) + return; + resizeDropdown(this); + updateChart(number, category, item, itemNumber, this.list_data[this.selected]); + }; + chartType.selected = 0; +} + +function updateChart(number, category, item, itemNumber, type) +{ + if (!g_ScorePanelsData[category].counters[itemNumber].fn) + return; + let chart = Engine.GetGUIObjectByName("chart[" + number + "]"); + let series = []; + for (let j = 1; j <= g_PlayerCount; ++j) + { + let playerState = g_GameData.sim.playerStates[j]; + let data = []; + for (let index in playerState.sequences.time) + { + let value = g_ScorePanelsData[category].counters[itemNumber].fn(playerState, index, item); + if (type) + value = value[type]; + data.push([playerState.sequences.time[index], value]); + } + series.push(data); + } + chart.series = series; } function adjustTabDividers(tabSize) @@ -126,14 +326,14 @@ civIcon.sprite = "stretched:" + g_CivData[playerState.civ].Emblem; civIcon.tooltip = g_CivData[playerState.civ].Name; - updateCountersPlayer(playerState, panelInfo.counters, playerCounterValue); + updateCountersPlayer(playerState, panelInfo.counters, panelInfo.headings, playerCounterValue); calculateTeamCounters(playerState); } let teamCounterFn = panelInfo.teamCounterFn; if (g_Teams && teamCounterFn) - teamCounterFn(panelInfo.counters); + teamCounterFn(panelInfo.counters, g_GameData.sim.playerStates[1].sequences.time.length - 1); } function confirmStartReplay() @@ -256,5 +456,6 @@ g_WithoutTeam -= g_Teams[i] ? g_Teams[i] : 0; } + initCharts(); selectPanel(Engine.GetGUIObjectByName("scorePanelButton")); } Index: binaries/data/mods/public/gui/summary/summary.xml =================================================================== --- binaries/data/mods/public/gui/summary/summary.xml +++ binaries/data/mods/public/gui/summary/summary.xml @@ -99,6 +99,13 @@ + + selectPanel(this); + + Charts + + + @@ -152,6 +159,48 @@ + + + + + + Category: + + + Category + + + + Value: + + + Value + + + + Type: + + + + + + + + + + Replay Index: binaries/data/mods/public/gui/text/quotes.txt =================================================================== --- binaries/data/mods/public/gui/text/quotes.txt +++ binaries/data/mods/public/gui/text/quotes.txt @@ -1,178 +1,200 @@ -"A prosperous fool is a grievous burden." - Aeschylus -"From him \[Death] alone of all the powers of heaven Persuasion holds aloof." - Aeschylus -"To be rather than to seem." - Aeschylus, "Seven Against Thebes" -"It is not the oath that makes us believe the man, but the man the oath." - Aeschylus -"In every tyrant's heart there springs in the end this poison, that he cannot trust a friend." - Aeschylus, "Prometheus Bound" -"Time as he grows old teaches all things." - Aeschylus, "Prometheus Bound" -"Wisdom comes through suffering... Favours come to us from gods." - Aeschylus, "Agamemnon" -"She \[Helen] brought to Ilium her dowry, destruction." - Aeschylus, "Agamemnon" -"It is in the character of very few men to honor without envy a friend who has prospered." - Aeschylus, "Agamemnon" -"For a deadly blow let him pay with a deadly blow; it is for him who has done a deed to suffer." - Aeschylus -"Any excuse will serve a tyrant." - Aesop -"Better be wise by the misfortunes of others than by your own." - Aesop -"Familiarity breeds contempt; acquaintance softens prejudices." - Aesop -"It is easy to be brave from a safe distance." - Aesop -"It is thrifty to prepare today for the wants of tomorrow." - Aesop -"It is not only fine feathers that make fine birds." - Aesop -"Never trust advice from a man in the throes of his own difficulty." - Aesop -"Persuasion is often more effectual than force." - Aesop -"Self-conceit may lead to self-destruction." - Aesop -"Slow and steady wins the race." - Aesop -"The gods help them that help themselves." - Aesop -"The shaft of the arrow had been feathered with one of the eagle's own plumes. We often give our enemies the means of our own destruction." - Aesop -"Union gives strength." - Aesop, "The Bundle of Sticks" -"Enemies' promises were made to be broken." - Aesop -"Spartans do not ask how many, only where the enemy are." - Agis II of Sparta -"Weep not for me, suffering as I do unjustly, I am in a happier case than my murderers." - Agis IV, 24th Spartan king of the Eurypontid dynasty -"He who doeth good shall meet with good; and he who doeth evil shall meet with evil, for the Lord judgeth a man according to the measure of his work." - Ahiqar, Assyrian sage -"If I were not Alexander, I should wish to be Diogenes." - Alexander the Great -"I do not steal victory." - Alexander the Great -"Are you still to learn that the end and perfection of our victories is to avoid the vices and infirmities of those whom we subdue?" - Alexander the Great -"To the strongest!" - Alexander, on his death bed, when asked who should succeed him as king -"There is nothing impossible to him who will try" - Alexander the Great -"Sex and sleep alone make me conscious that I am mortal." - Alexander the Great -"The agora is an established place for men to cheat one another, and behave covetously." - Anacharsis -"Written laws are like spiders’ webs; they will catch, it is true, the weak and poor, but would be torn in pieces by the rich and powerful." - Anacharsis -"The fox knows many tricks; the hedgehog one good one." - Archilochus -"States are doomed when they are unable to distinguish good men from bad." - Antisthenes -"Give me a place to stand, and I shall move the world." - Archimedes, on his usage of the lever. -"It is from their foes, not their friends, that cities learn the lesson of building high walls and ships of war." - Aristophanes -"Words give wings to the mind and make a man soar to heaven." - Aristophanes -"It is the compelling power of great thoughts and ideas to engender language of equal greatness." - Aristophanes -"Hunger knows no friend but its feeder." - Aristophanes -"I count him braver who overcomes his desires than him who overcomes his enemies." - Aristotle -"Our characters are the result of our conduct." - Aristotle -"Man is by nature a political animal." - Aristotle -"If liberty and equality, as is thought by some, are chiefly to be found in democracy, they will be best attained when all persons alike share in the government to the utmost." - Aristotle -"Both oligarch and tyrant mistrust the people, and therefore deprive them of their arms." - Aristotle -"Wit is well-bred insolence." - Aristotle -"It is simplicity that makes the uneducated more effective than the educated when addressing popular audiences." - Aristotle -"Poetry demands a man with a special gift for it, or else one with a touch of madness in him." - Aristotle -"I have gained this by philosophy: that I do without being commanded what others do only from fear of the law." - Aristotle -"Choose the course which you adopt with deliberation; but when you have adopted it, then persevere in it with firmness." - Bias of Priene -"How stupid it was for the king to tear out his hair in grief, as if baldness were a cure for sorrow." - Bion of Borysthenes -"He has not acquired a fortune; the fortune has acquired him." - Bion of Borysthenes -"Woe to the Defeated" - Brennus -"Thus always to tyrants." - Marcus Junius Brutus, after assassinating Gaius Julius Caesar -"Escape, yes, but this time with my hands, not my feet." - Marcus Junius Brutus, before committing suicide -"I came, I saw, I conquered." - Gaius Julius Caesar -"Men willingly believe what they wish." - Gaius Julius Caesar -"The die is cast." - Gaius Julius Caesar -"It is not the well-fed long-haired man I fear, but the pale and the hungry looking." - Gaius Julius Caesar -"Set a thief to catch a thief." - Callimachus -"Wise men learn more from fools than fools from the wise." - Cato the Elder -"Moreover, I consider that Carthage should be destroyed." - Cato the Elder -"All mankind rules its women, and we rule all mankind, but our women rule us." - Cato the Elder -"Be firm or mild as the occasion may require." - Cato the Elder -"The worst ruler is one who cannot rule himself." - Cato the Elder -"Whoever imposes severe punishment becomes repulsive to the people; while he who awards mild punishment becomes contemptible. But whoever imposes punishment as deserved becomes respectable." - Chanakya -"If a king is energetic, his subjects will be equally energetic." - Chanakya -"For there is but one essential justice which cements society, and one law which establishes this justice. This law is right reason, which is the true rule of all commandments and prohibitions." - Marcus Tullius Cicero -"O, the times, O, the customs!" - Marcus Tullius Cicero -"No one is so old as to think that he cannot live one more year." - Marcus Tullius Cicero -"We are not born, we do not live for ourselves alone; our country, our friends, have a share in us." - Marcus Tullius Cicero -"Yield, ye arms, to the toga; to civic praise, ye laurels." - Marcus Tullius Cicero -"At my signal, unleash Hell." - Maximus Decimus Meridius -"Time heals all wounds." - Marcus Tullius Cicero -"That, Senators, is what a favour from gangs amounts to. They refrain from murdering someone; then they boast that they have spared him!" - Marcus Tullius Cicero -"Genius is fostered by energy." - Marcus Tullius Cicero -"While there's life, there's hope." - Marcus Tullius Cicero -"We must not say that every mistake is a foolish one." - Marcus Tullius Cicero -"Let the punishment match the offense." - Marcus Tullius Cicero -"Let the welfare of the people be the ultimate law." - Marcus Tullius Cicero -"Endless money forms the sinews of war." - Marcus Tullius Cicero -"Laws are silent in time of war." - Marcus Tullius Cicero -"A war is never undertaken by the ideal State, except in defense of its honor or its safety." - Marcus Tullius Cicero -"The first duty of a man is the seeking after and the investigation of truth." - Marcus Tullius Cicero -"In peace the sons bury their fathers, but in war the fathers bury their sons." - Croesus (Greek: Kroisos), king of Lydia -"I am Cyrus, king of the world..." - Cyrus the Great -"Diversity in counsel, unity in command." - Cyrus the Great -"Do not therefore begrudge me this bit of earth that covers my bones." - Epitaph of Cyrus the Great -"Success always calls for greater generosity — though most people, lost in the darkness of their own egos, treat it as an occasion for greater greed." - Cyrus the Great -"Whether men lie, or say true, it is with one and the same object." - Darius I -"Good breeding in cattle depends on physical health, but in men on a well-formed character." - Democritus -"If your desires are not great, a little will seem much to you; for small appetite makes poverty equivalent to wealth." - Democritus -"Delivery, delivery, delivery." - Demosthenes, when asked what were the three most important elements of rhetoric -"It is not possible to found a lasting power upon injustice, perjury, and treachery." - Demosthenes -"Small opportunities often presage great enterprises." - Demosthenes -"Every advantage in the past is judged in the light of the final issue." - Demosthenes -"Yes, stand a little out of my sunshine." - Diogenes of Sinope, when Alexander the Great found him sunbathing and asked if he could help in in any way -"I am a citizen of the world." - Diogenes of Sinope -"It is not that I am mad, it is only that my head is different from yours." - Diogenes of Sinope -"Let thy speech be better than silence, or be silent." - Dionysius I of Syracuse -"Nothing has more strength than dire necessity." - Euripides -"A coward turns away, but a brave man's choice is danger." - Euripides -"Cowards do not count in battle; they are there, but not in it." - Euripides -"Chance fights ever on the side of the prudent." - Euripides -"If a man put out the eye of another man, his eye shall be put out." - Hammurabi -"I have come not to make war on the Italians, but to aid the Italians against Rome." - Hannibal Barca -"I will either find a way, or make one." - Hannibal Barca -"My ancestors yielded to Roman valour. I am endeavouring that others, in their turn, will be obliged to yield to my good fortune, and my valour." - Hannibal Barca -"Everything flows, nothing stands still." - Herakleitos -"Nothing endures but change." - Herakleitos -"You could not step twice into the same river." - Herakleitos -"Character is destiny." - Herakleitos -"Circumstances rule men; men do not rule circumstances." - Herodotus -"The Lacedaemonians fought a memorable battle; they made it quite clear that they were the experts, and that they were fighting against amateurs." - Herodotus -"This is the bitterest pain among men, to have much knowledge but no power." - Herodotus -"In soft regions are born soft men." - Herodotus -"It is sweet and honorable to die for one's country." - Horace -"We are but dust and shadow." - Horace -"I am not bound over to swear allegiance to any master; where the storm drives me I turn in for shelter." - Horace -"Conquered Greece took captive her savage conqueror and brought her arts into rustic Latium." - Horace -"Guard yourself against accusations, even if they are false; for the multitude are ignorant of the truth and look only to reputation." - Isocrates -"I never learned how to tune a harp, or play upon a lute; but I know how to raise a small and inconsiderable city to glory and greatness." - Themistocles -"I have with me two gods, Persuasion and Compulsion." - Themistocles -"For the Athenians command the rest of Greece, I command the Athenians; your mother commands me, and you command your mother." - Themistocles, in a statement to his son -"Strike, if you will, but listen." - Themistocles -"Marry a good man, and bear good children." - Leonidas, to his wife before he left for Thermopylae -"Come and get them!" - Leonidas, to the Persian messenger who demanded that he and his men lay down their arms -"For a thinking man is where Wisdom is at home." - Zoroaster -"I call a fig a fig, a spade a spade." - Menander -"The man who runs may fight again." - Menander -"At times discretion should be thrown aside, and with the foolish we should play the fool." - Menander -"Freedom is the sure possession of those alone who have the courage to defend it." - Pericles -"What you leave behind is not what is engraved in stone monuments, but what is woven into the lives of others." - Pericles -"We do not say that a man who takes no interest in politics is a man who minds his own business; we say that he has no business here at all." - Pericles -"They gave her their lives, to Her and to all of us, and for their own selves they won praises that never grow old, the most splendid of sepulchers..." - Pericles, Funeral Oration -"Make up your minds that happiness depends on being free, and freedom depends on being courageous." - Pericles -"Your empire is now like a tyranny: it may have been wrong to take it; it is certainly dangerous to let it go." - Pericles -"If Athens shall appear great to you, consider then that her glories were purchased by valiant men, and by men who learned their duty." - Pericles -"Just because you do not take an interest in politics doesn't mean politics won't take an interest in you." - Pericles -"War is sweet to those who have no experience of it, but the experienced man trembles exceedingly at heart on its approach." - Pindar -"The hour of departure has arrived, and we go our ways — I to die, and you to live. Which is better God only knows." - Plato -"Life without examination is not worth living." - Plato -"Let every man remind their descendants that they also are soldiers who must not desert the ranks of their ancestors, or from cowardice fall behind." - Plato -"False words are not only evil in themselves, but they infect the soul with evil." - Plato -"Democracy, which is a charming form of government, full of variety and disorder, and dispensing a sort of equality to equals and unequals alike." - Plato -"Death is not the worst that can happen to men." - Plato -"Only the dead have seen the end of war." - Unknown -"Our need will be the real creator." - Plato -"A man with courage has every blessing." - Plautus -"No guest is so welcome in a friend's house that he will not become a nuisance after three days." - Plautus -"He whom the gods love dies young." - Plautus -"Practice yourself what you preach." - Plautus -"Drink, live like the Greeks, eat, gorge." - Plautus -"Stop quoting laws, we carry weapons!" - Gnaeus Pompeius Magnus (Pompey the Great) -"Man is the measure of all things: of things which are, that they are, and of things which are not, that they are not." - Protagoras -"We ought so to behave to one another as to avoid making enemies of our friends, and at the same time to make friends of our enemies." - Pythagoras -"In anger we should refrain both from speech and action." - Pythagoras -"Power is the near neighbor of necessity." - Pythagoras -"Numbers rule the Universe." - Pythagoras -"Rest satisfied with doing well, and leave others to talk of you as they please." - Pythagoras -"Anger begins in folly, and ends in repentance." - Pythagoras -"There is no word or action but has its echo in Eternity." - Pythagoras -"There is geometry in the humming of the strings, there is music in the spacing of the spheres." - Pythagoras -"Practice justice in word and deed, and do not get in the habit of acting thoughtlessly about anything." - Pythagoras -"Do not even think of doing what ought not to be done." - Pythagoras -"When the wise man opens his mouth, the beauties of his soul present themselves to the view, like the statues in a temple." - Pythagoras -"Remind yourself that all men assert that wisdom is the greatest good, but that there are few who strenuously seek out that greatest good." - Pythagoras -"Without Justice, no realm may prosper." - Pythagoras -"For harmony makes small states great, while discord undermines the mightiest empires." - Sallust -"I am mindful of human weakness, and I reflect upon the might of Fortune and know that everything that we do is exposed to a thousand chances." - Scipio Africanus -"Prepare for war, since you have been unable to endure a peace." - Scipio Africanus -"Go, tell the Spartans, stranger passing by that here, obedient to their laws, we lie." - Simonides of Ceos, epitaph on the Cenotaph of Thermopylae -"Not even the gods fight against necessity." - Simonides of Ceos -"We did not flinch but gave our lives to save Greece when her fate hung on a razor's edge." - Simonides of Ceos -"The bravest are surely those who have the clearest vision of what is before them, glory and danger alike, and yet notwithstanding, go out to meet it." - Thucydides - +"Zeus \[...] established his law: wisdom comes through suffering. \[...] So men against their will learn to practice moderation. \[...] Such grace is harsh and violent." - Aeschylus ("Oresteia", I. 176-183) +"She \[Helen] brought to Ilium her dowry, destruction." - Aeschylus ("Oresteia", I. 406) +"In every tyrant's heart there springs in the end this poison, that he cannot trust a friend." - Aeschylus ("Prometheus Bound", 224-225) +"Time in the long run teaches all things." - Aeschylus ("Prometheus Bound", 981) +"His resolve is not to seem, but to be, the best." - Aeschylus ("Seven Against Thebes", 592) +"A prosperous fool is a grievous burden." - Aeschylus (fragment 383) +"The gods help those that help themselves." - Aesop ("Hercules and the Wagoner") +"It is thrifty to prepare today for the wants of tomorrow." - Aesop ("The Ant and the Grasshopper") +"Union gives strength." - Aesop ("The Bundle of Sticks") +"Never trust advice from a man in the throes of his own difficulty." - Aesop ("The Fox and the Goat") +"Familiarity breeds contempt; acquaintance softens prejudices." - Aesop ("The Fox and the Lion") +"Self-conceit may lead to self-destruction." - Aesop ("The Frog and the Ox") +"Slow and steady wins the race." - Aesop ("The Hare and the Tortoise") +"Better be wise by the misfortunes of others than by your own." - Aesop ("The Lion, the Ass, and the Fox Hunting") +"Enemies' promises were made to be broken." - Aesop ("The Nurse and the Wolf") +"Any excuse will serve a tyrant." - Aesop ("The Wolf and the Lamb") +"If I have done anything noble, that is a sufficient memorial; if I have not, all the statues in the world will not preserve my memory." - Agesilaos II of Sparta (Plutarch, "Moralia", XVI. "Sayings of Spartans", 215a) +"Spartans do not ask how many, only where the enemy are." - Agis II of Sparta (Plutarch, "Moralia", XVI. "Sayings of Spartans", 215d) +"Weep not for me, as I suffer unjustly, I am in a happier situation than my murderers." - Agis IV of Sparta upon seeing one of his executioners cry (Plutarch, "Parallel Lives", "Agis", sec. 20) +"Sex and sleep alone make me conscious that I am mortal." - Alexander the Great (Plutarch, "Parallel Lives", "Alexander", sec. 22) +"It is very servile to live in luxury, but very royal to toil. \[...] Don't you know that the end and object of conquest is to avoid the vices and infirmities of the subdued?" - Alexander the Great (Plutarch, "Parallel Lives", "Alexander", sec. 40) +"Glorious are the deeds of those who undergo labour and run the risk of danger; and it is delightful to live a life of valor and to die leaving behind immortal glory." - Alexander the Great, addressing his troops (Arrian, "The Anabasis of Alexander", 4.26) +"I for one think that to a brave man there is no end to labours except the labours themselves, provided they lead to glorious achievements." - Alexander the Great, addressing his troops (Arrian, "The Anabasis of Alexander", 5.26) +"If I were not Alexander, I should wish to be Diogenes \[of Sinope]." - Alexander the Great, impressed by the simplicity of the philosopher he had met (Plutarch, "Moralia", XXII. "On the Fortunes of Alexander the Great", 332a-b) +"To the strongest!" - Alexander the Great, on his death bed, when asked who should succeed him as king (Arrian, "The Anabasis of Alexander", 7.26) +"I do not steal victory." - Alexander the Great, when suggested to raid the Persians at night (Plutarch, "Parallel Lives", "Alexander", sec. 31) +"Written laws are like spiders' webs; they will catch, it is true, the weak and poor, but will be torn in pieces by the rich and powerful." - Anacharsis (Plutarch, "Parallel Lives", "Solon", sec. 5) +"The agora is an established place for men to cheat one another, and behave covetously." - Anacharsis, a Scythian philosopher who travelled to Greece (Diogenes Laertius, "The Lives and Opinions of Eminent Philosophers", Anarchsis, sec. 5) +"It was not by taking care of the fields, but of ourselves, that we acquired those fields." - Anaxandridas II of Sparta (Plutarch, "Moralia", XVI. "Sayings of Spartans", 217a) +"States are doomed when they are unable to distinguish good men from bad." - Antisthenes (Diogenes Laertius, "The Lives and Opinions of Eminent Philosophers", Antisthenes, sec. 5) +"The fox knows many tricks; the hedgehog one good one." - Archilochus (fragment 201) +"Give me a place to stand, and I shall move the world." - Archimedes, on his usage of the lever (Diodorus Siculus, "The Library of History", fragments of book XXVI, sec. 18) +"It is from their foes, not their friends, that cities learn the lesson of building high walls and ships of war." - Aristophanes ("Birds") +"It is obligatory, especially for a philosopher, to sacrifice even one's closest personal ties in defense of the truth." - Aristotle ("Nicomachean Ethics", I. 1096a.11) +"Happiness depends on leisure; for we are busy to have leisure, and make war to live in peace." - Aristotle ("Nicomachean Ethics", X. 1177b.4) +"Man is by nature a political animal." - Aristotle ("Politics", I. 1253a.2) +"Both oligarch and tyrant mistrust the people, and therefore deprive them of their arms." - Aristotle ("Politics, V. 1311a.11) +"I have gained this by philosophy: that I do without being commanded what others do only from fear of the law." - Aristotle (Diogenes Laertius, "The Lives and Opinions of Eminent Philosophers", Aristotle, sec. 20) +"I count him braver who overcomes his desires than him who conquers his enemies, for the hardest victory is over the self." - Aristotle (Stobaeus, "Florilegium", 223) +"Alexander himself, plagued by thirst, with great pain and difficulty nevertheless led the army on foot \[...]. At this time a few of the light-armed soldiers \[...] found some water \[...], poured the water into a helmet and carried it to him. He took it, and commending the men who brought it, immediately poured it upon the ground in the sight of all." - Arrian about Alexander's march through the Gedrosian desert ("The Anabasis of Alexander", 6.26) +"Thrusting his spear into Mithridates' face, he \[Alexander] hurled him to the ground. Then Rhoesaces \[a Persian] \[...] struck him on the head with his sword. \[...] Alexander hurled him too to the ground, piercing with his lance through his breastplate into his chest. Sphithridates \[a Persian] had already raised his sword against Alexander from behind when Clitus \[...] cut his arm off." - Arrian about the Battle of the Granicus ("The Anabasis of Alexander", 1.15) +"Let every man remind their descendants that they also are soldiers who must not desert the ranks of their ancestors, or retreat out of cowardice." - Aspasia (Plato, "Menexenus", 246b) +"Quintilius Varus, give me back my legions!" - Augustus, after three legions were annihilated in the Battle of the Teutoburg Forest (Suetonius, "Divus Augustus", sec. 23) +"In my nineteenth year, on my own initiative and at my own expense, I raised an army with which I liberated the state, which was oppressed by the tyranny of a faction." - Augustus, in his autobiography ("Res Gestae Divi Augusti", sec. 1) +"Wars, both civil and foreign, I waged throughout the world, on sea and land, and when victorious I spared all citizens who sued for pardon. The foreign nations which could with safety be pardoned I preferred to save rather than to destroy." - Augustus, in his autobiography ("Res Gestae Divi Augusti", sec. 3) +"Choose the course which you adopt with deliberation; but when you have adopted it, then persevere in it with firmness." - Bias of Priene (Diogenes Laertius, "The Lives and Opinions of Eminent Philosophers", Bias, sec. 5) +"How stupid it was for the king to tear out his hair in grief, as if baldness were a cure for sorrow." - Bion of Borysthenes (Cicero, "Tusculan Disputations", III. 26) +"He has not acquired a fortune; the fortune has acquired him." - Bion of Borysthenes, referring to a wealthy miser (Diogenes Laertius, "The Lives and Opinions of Eminent Philosophers", Bion, sec. 50) +"Woe to the Defeated!" - Brennus, Gaulish chieftain who had seized Rome (with the exception of a garrison on Capitoline Hill). When Camillus arrived from Veii and besieged him, he negotiated his withdrawal for 1000 pounds of gold, but not without using false weights and adding the weight of his sword on the scale when the Romans complained (Polybius, "Histories", II. 18) +"Robbery, slaughter, plunder, they \[the Romans] deceivingly name empire; they make a wasteland and call it peace." - Calgacus, Caledonian chieftain in a speech before the Battle of Mons Graupius (Tacitus, "Agricola", 30) +"Set a thief to catch a thief." - Callimachus ("Epigrams", 44) +"All mankind rules its women, and we rule all mankind, but our women rule us." - Cato the Elder (Plutarch, "Moralia", III. "Sayings of Romans", 198e) +"The worst ruler is one who cannot rule himself." - Cato the Elder (Plutarch, "Moralia", III. "Sayings of Romans", 198f) +"Wise men learn more from fools than fools from the wise." - Cato the Elder (Plutarch, "Parallel Lives", "Cato the Elder", sec. 9) +"Moreover, I consider that Carthage should be destroyed." - Cato the Elder, who ended all speeches in his later life with this statement (Plutarch, "Parallel Lives", "Cato the Elder", sec. 27) +"If a king is energetic, his subjects will be equally energetic." - Chanakya ("Arthashastra", I. "Concerning Discipline", chapter 19) +"Whoever imposes severe punishment becomes repulsive to the people; while he who awards mild punishment becomes contemptible. But whoever imposes punishment as deserved becomes respectable." - Chanakya ("Arthashastra", I. "Concerning Discipline", chapter 4) +"We did not flinch but gave our lives to save Greece when her fate hung on a razor's edge." - Corinthian epitaph to their fallen of the Persian Wars (Plutarch, "Moralia", XI. "On the Malice of Herodotus", 870e) +"Then the blood really flowed, for the two lines were so close that shield struck against shield, and they drove their swords into each other's faces. It was impossible for the weak or cowardly to retreat; man to man they fought like in single combat." - Curtius Rufus about the Battle of Issos ("Histories of Alexander the Great", III. 11.5) +"I am Cyrus, who won for the Persians their empire. Therefore do not begrudge me this bit of earth that covers my bones." - Cyrus the Great's epitaph (Plutarch, "Parallel Lives", "Alexander", sec. 69) +"I am Darius, the great king, king of kings, the king of Persia, the king of countries, \[...] 23 lands in total." - Darius I. (Behistun inscription, column I, 1-6) +"Phraortes was captured and brought before me. I cut off his nose, his ears, and his tongue, and I put out one eye, and he was kept in chains at my palace entrance, and all the people saw him. Then I crucified him in Ecbatana; and the men who were his foremost followers \[...] I flayed and hung out their skins, stuffed with straw." - Darius I. (Behistun inscription, column II, 32) +"By desiring little, a poor man makes himself rich." - Democritus (fragment) +"It is hard to be governed by one's inferior." - Democritus (fragment) +"Physical strength is only noble in cattle, it is strength of character that is noble in men." - Democritus (fragment) +"It is not possible to found a lasting power upon injustice, perjury, and treachery." - Demosthenes, in one of his many speeches against the rising Phillip II of Macedon ("Olynthiac II", 10) +"Delivery, delivery, delivery." - Demosthenes, when asked what were the three most important elements of rhetoric (Cicero, "De Oratore", 3.213) +"The Macedonians first raised an unearthly shout followed by the Persians answering, so that the whole hillside bordering the battlefield echoed back the sound, and that second roar was louder than the Macedonian war cry as five hundred thousand men shouted with one voice." - Diodorus Siculus about the Battle of Issos ("The Library of History", XVII., sec. 33) +"Brasidas, taking his stand on the gangway, fought off from there the multitude of Athenians who converged upon him. And at the outset he slew many as they came at him, but after a while, as numerous missiles assailed him, he suffered many wounds on the front of his body." - Diodorus Siculus, on a brave Spartan at the Battle of Pylos ("The Library of History", XXII., sec. 62) +"Plato had defined man as an animal, biped and featherless, and was applauded. Diogenes \[of Sinope] plucked a fowl and brought it into the lecture-room with the words: Here is Plato's man. In consequence of which there was added to the definition: having broad nails." - Diogenes Laertius ("The Lives and Opinions of Eminent Philosophers", "Diogenes", sec. 40) +"I am a citizen of the world." - Diogenes of Sinope (Diogenes Laertius, "The Lives and Opinions of Eminent Philosophers", "Diogenes", sec. 63) +"It is not that I am mad, it is only that my head is different from yours." - Diogenes of Sinope (Stobaeus, "Florilegium", 51) +"Yes, stand a little out of my sunshine." - Diogenes of Sinope to Alexander the Great, who asked if he could help in in any way (Plutarch, "Parallel Lives", "Alexander", sec. 14) +"The victor is not victorious if the vanquished does not consider himself so." - Ennius ("Annales", fragment 31.493) +"Stranger, go tell the Spartans that we lie here, obedient to their laws." - Epitaph at Thermopylae for Leonidas and his men (Herodotus, "The Histories", VII. 228) +"A coward turns away, but a brave man's choice is danger." - Euripides ("Iphigenia in Tauris") +"Brave men are made bolder by ordeals, but cowards achieve nothing." - Euripides ("Iphigenia in Tauris") +"Cowards do not count in battle; they are there, but not in it." - Euripides ("Meleager") +"Chance fights ever on the side of the prudent." - Euripides ("Pirithous") +"Return with your shield, or on it." - Farewell of Spartan women to their warriors, implying that cowards would throw away their shield in battle to flee (Plutarch, "Moralia", XVIII. "Sayings of Spartan Women", 241f) +"I came, I saw, I conquered." - Gaius Julius Caesar, after routing Pharnaces II of Pontus in the first assault (Plutarch, "Parallel Lives", "Caesar", sec. 50) +"Men willingly believe what they wish." - Gaius Julius Caesar ("De Bello Gallico", III. 18) +"It is not the well-fed long-haired man I fear, but the pale and the hungry looking." - Gaius Julius Caesar (Plutarch, "Parallel Lives", "Antony", sec. 11) +"After fighting from noon almost to sunset, with victory doubtful, the Germans, on one side charged the enemy in a compact body, and drove them back; and, when they were put to flight, the archers were surrounded and cut to pieces." - Gaius Julius Caesar about the Battle of Alesia ("De Bello Gallico", VII. 80) +"All the centurions of the fourth cohort were slain, and the standard-bearer killed, the standard itself lost, almost all the centurions of the other cohorts either wounded or slain, and among them the chief centurion of the legion, Publius Sextius Baculus, a very valiant man, who was so exhausted by many and severe wounds, that he was already unable to support himself." - Gaius Julius Caesar about the Battle of the Sabis ("De Bello Gallico", II. 25) +"But the enemy \[...] displayed such great courage, that when the front rank had fallen the men behind them stood on them and continue the fight from on top of the corpses; when these were killed the pile of bodies grew higher, while the survivors used the heap as a vantage point for throwing missiles at our men, or catching our spears and throwing them back." - Gaius Julius Caesar about the Battle of the Sabis ("De Bello Gallico", II. 27) +"The die is cast." - Gaius Julius Caesar, when crossing the Rubicon river with his legion into Italy, a capital offense that led to his civil war against Pompey (Suetonius, "The Lives of the Twelve Caesars", 32) +"I'd rather be the first man here than the second man in Rome." - Gaius Julius Caesar, when passing through a barbarian village in the Alps (Plutarch, "Parallel Lives", "Caesar", sec. 11) +"Stop quoting laws, we carry weapons!" - Gnaeus Pompeius Magnus (Plutarch, "Parallel Lives", "Pompey", sec. 10) +"If a man put out the eye of another man, his eye shall be put out." - Hammurabi (Hammurabi's Code, sec. 196) +"I have come not to make war on the Italians, but to aid the Italians against Rome." - Hannibal Barca (Polybius, "Histories", III. 85) +"Let us now end the anxiety of the Romans, who can't wait for the death of an old man." - Hannibal Barca's last words before his suicide, in exile with Flaminius pressuring the local ruler to hand him over (Livius, "Ab Urbe Condita", XXXIX. 51) +"Most inhuman and most arrogant of nations, they \[the Romans] reckon the world as theirs and subject to their pleasure. With whom we are to be at war, with whom at peace, they think it right that they should determine." - Hannibal Barca, addressing his troops (Livius, "Ab Urbe Condita", XXI. 44) +"You must be brave and discard all hopes of anything but victory or death." - Hannibal Barca, addressing his troops (Livius, "Ab Urbe Condita", XXI. 44) +"War is the father and king of all things: some he has made gods, and some men; some slaves and some free." - Herakleitos (Hippolytus, "The Refutation of all Heresies", IX. 4) +"You could not step twice into the same river." - Herakleitos (Plato, "Cratylos", 402a) +"It is better to be envied than to be pitied." - Herodotus ("The Histories", III. 52) +"In soft regions are born soft men." - Herodotus ("The Histories", IX. 122) +"This is the bitterest pain among men, to have much knowledge but no power." - Herodotus ("The Histories", IX. 16) +"Although he \[Xerces] had plenty of troops he had few men." - Herodotus ("The Histories", VII. 210) +"The Lacedaemonians [Spartans] fought a memorable battle; they made it quite clear that they were the experts, and that they were fighting against amateurs." - Herodotus ("The Histories", VII. 211) +"Being informed \[...] that when the Barbarians discharged their arrows they obscured the light of the sun by the multitude of the arrows, he \[Dienekes] \[...] said that their guest \[...] brought them very good news, for if the Medes obscured the light of the sun, the battle against them would be in the shade and not in the sun." - Herodotus describing Dienekes, reputedly the bravest Spartan soldier at Thermopylae (Polybius, "Histories", VII. 226) +"The judgement given to Kroisus by each of the two oracles \[Delphi and Thebes] was the same: If he sent an army against the Persians, he would destroy a great empire." - Herodotus, later mentioning that the empire Kroisos destroyed was his own ("The Histories", I. 53) +"He \[King Darius] asked who the Athenians were, and, being informed, called for his bow, and placing an arrow on the string, shot upward into the sky, saying, as he let fly the shaft: Grant me, Zeus, to revenge myself on the Athenians!" - Herodotus, narrating how the Athenian support for the Ionian revolt caught the wrath of Darius I., the Persian king ("The Histories", V. 105) +"He \[King Darius] asked one of his servants every day, when his dinner was spread, three times to repeat to him: Master, remember the Athenians!" - Herodotus, narrating how the Athenian support for the Ionian revolt lead to the Persian Wars ("The Histories", V. 105) +"Conquered Greece took captive her savage conqueror and brought her arts into rustic Latium." - Horace ("Epistles", epistle I., 156-157) +"Anger is a momentary madness, so control your passion or it will control you." - Horace ("Epistles", epistle II., 62) +"It is your concern when your neighbour's wall is on fire." - Horace ("Epistles", epistle XVIII., 84) +"It is sweet and honorable to die for one's country." - Horace ("Odes", III., ode II., 13) +"I am Cyrus, king of the world..." - Inscription (Cyrus Cylinder) +"In peace the sons bury their fathers, but in war the fathers bury their sons." - Kroisos, king of Lydia (Herodotus, "The Histories", I. 87) +"Marry a good man, and bear good children." - Leonidas, to his wife who asked what to do if he died, before he left for Thermopylae (Plutarch, "Moralia", XVIII. "Sayings of Spartan Women", 220e) +"Come and get them!" - Leonidas, to the Persian messenger who demanded that he and his men lay down their arms (Plutarch, "Moralia", XVI. "Sayings of Spartans", 225c) +"Some were discovered lying there alive, with thighs and tendons slashed, baring their necks and throats and bidding their conquerors drain the remnant of their blood. Others were found with their heads buried in holes dug in the ground. They had apparently made these pits for themselves." - Livius, describing the aftermath of the Battle of Cannae, where Hannibal inflicted the greatest defeat on the Romans in all their history ("Ab Urbe Condita", XXII. 51) +"There lay thousands upon thousands of Romans \[...]. Here and there amidst the slain rose a gory figure whose wounds had begun to throb with the chill of dawn, and was cut down by his enemies." - Livius, describing the aftermath of the Battle of Cannae, where Hannibal inflicted the greatest defeat on the Romans in all their history ("Ab Urbe Condita", XXII. 51) +"A city is well-fortified which has a wall of men instead of brick." - Lycurgus of Sparta (Plutarch, "Parallel Lives", "Lycurgus", sec. 19) +"Escape, yes, but this time with my hands, not my feet." - Marcus Junius Brutus, before committing suicide after losing a battle against Caesar's avengers (Plutarch, "Parallel Lives", "Brutus", sec. 52) +"O, the times, O, the customs!" - Marcus Tullius Cicero ("Against Catiline", speech I) +"A war is never undertaken by the ideal State, except in defense of its honor or its safety." - Marcus Tullius Cicero ("De Re Publica", III., 23) +"The first duty of a man is the seeking after and the investigation of truth." - Marcus Tullius Cicero ("On Duties", I., 13) +"No one is so old as to think that he cannot live one more year." - Marcus Tullius Cicero ("On Old Age", sec. 24) +"Let the welfare of the people be the ultimate law." - Marcus Tullius Cicero ("On the Laws", III., sec. 3) +"Endless money forms the sinews of war." - Marcus Tullius Cicero ("Philippics", Philippica V., sec. 5) +"Laws are silent in time of war." - Marcus Tullius Cicero ("Pro Milone", IV., sec. 11) +"That, Senators, is what a favour from gangs amounts to. They refrain from murdering someone; then they boast that they have spared him!" - Marcus Tullius Cicero, condemning Mark Anthony who had not killed him (yet) ("Philippics", Philippica II, sec. 5) +"He did not even stand up to review his fleet when the ships were already at their fighting stations, but lay on his back and gazed up at the sky, never rising to show that he was alive until Marcus Agrippa had routed the enemy." - Mark Antony, taunting Augustus who delegated his duties as naval commander (Suetonius, "Divus Augustus", sec. 16) +"We live, not as we wish to, but as we can." - Menander ("Lady of Andros", fragment 50) +"The man who runs may fight again." - Menander ("Monosticha") +"Whom the Gods love dies young." - Menander ("The Double Deceiver", fragment 4) +"I call a fig a fig, a spade a spade." - Menander (fragment 545 K) +"The greatest glory is won from the greatest dangers. When our fathers faced the Persians their resources could not compare to ours. In fact, they gave up even what they had. Then by wise counsels and daring deeds, not fortune and material advantages, they drove out the invaders and made our city what it is now." - Pericles (Thucydides, "History of the Peloponnesian War", I. 144.3-4) +"Instead of looking on discussion as a stumbling block in the way of action, we think it an indispensable preliminary to any wise action at all." - Pericles in his Funeral Oration for Athenians that died in the first year of the war (Thucydides, "History of the Peloponnesian War", II. 40.2) +"We alone do not think that a man ignorant of politics interferes with nothing, we think he is good for nothing." - Pericles in his Funeral Oration for Athenians that died in the first year of the war (Thucydides, "History of the Peloponnesian War", II. 40.2) +"Future ages will wonder at us, as the present age wonders at us now." - Pericles in his Funeral Oration for Athenians that died in the first year of the war (Thucydides, "History of the Peloponnesian War", II. 41.5) +"When you realise the power of Athens, consider it was won by valiant men who knew their duty, had a sense of dishonor in fight and, if their enterprises failed, would rather give their lives than lack in civic virtue." - Pericles in his Funeral Oration for Athenians that died in the first year of the war (Thucydides, "History of the Peloponnesian War", II. 43.2) +"To heroes all earth is their tomb, and their virtues are remembered far from home where an epitaph declares them, in an unwritten record of the mind that will outlast any monument." - Pericles in his Funeral Oration for Athenians that died in the first year of the war (Thucydides, "History of the Peloponnesian War", II. 43.3) +"Understand that happiness depends on freedom, and freedom depends on courage." - Pericles in his Funeral Oration for Athenians that died in the first year of the war (Thucydides, "History of the Peloponnesian War", II. 43.4) +"The greatest glory for women is to be least talked about by men, whether for good or ill." - Pericles in his Funeral Oration for Athenians that died in the first year of the war (Thucydides, "History of the Peloponnesian War", II. 45.2) +"Wait for the wisest of all counsellors, time." - Pericles, a cautious politician who avoided war (Plutarch, "Parallel Lives", "Pericles", sec. 18) +"Your empire is now like a tyranny: it may have been wrong to take it; it is certainly dangerous to let it go." - Pericles, addressing the Athenian assembly after a plague had weakened the city (Thucydides, "History of the Peloponnesian War", II. 63.3) +"War is sweet to those who have no experience of it, but the experienced man fears its approach in his heart." - Pindar (fragment 110) +"Themistocles robbed his fellow-citizens of spear and shield, and degraded the people of Athens to the rowing-pad and the oar." - Plato, no friend of the Athenian navy (Plutarch, "Parallel Lives", "Themistocles", sec. 3) +"No guest is so welcome in a friend's house that he will not become a nuisance after three days." - Plautus ("The Swaggering Soldier", Act III, scene 1, 146) +"You cannot eat your cake and have it too, unless you think your money is immortal." - Plautus: ("Trinummus", Act II, scene 4, 12) +"He [Alexander] thought nothing invincible for the courageous, and nothing secure for the cowardly." - Plutarch ("Parallel Lives", "Alexander", sec. 58) +"One \[...] shot an arrow at him with such accuracy and force that it pierced his breastplate and got stuck in his ribs. \[...] Alexander recoiled and sank to his knees. \[...] At last Alexander killed the barbarian. But he received many wounds, at last was struck on the neck with a mace, and leaned against the city wall, his eyes still fixed upon his foes." - Plutarch about the Mallian Campaign ("Parallel Lives", "Alexander", sec. 63) +"When the pirates demanded a ransom of twenty talents for him, Caesar burst out laughing. They did not know, he said, who it was that they had captured, and he volunteered to pay fifty." - Plutarch, who mentions later that Caesar got his money back and had his captors crucified ("Parallel Lives", "Caesar", sec. 2) +"They \[the Romans] want the centurions not so much to be adventurous and daredevils, as to be natural leaders, of a steady and reliable spirit. They do not so much want men who will initiate attacks and open the battle, but men who will hold their ground when beaten and hard-pressed, and will be ready to die at their posts." - Polybius ("Histories", VI. 24) +"The Roman battle line is hard to break, since it allows every man to fight both individually and collectively; so that a formation can fight in any direction, with the maniples nearest to the point of danger wheeling around to face it." - Polybius ("Histories", XV. 15) +"The Athenian people are always in the position of a ship without a commander. Fear of the enemy or a storm make the crew be of one mind and obey the helmsman, everything goes well; but if they recover \[...] they quarrel with each other \[...], and the result has often been that, after escaping the dangers of the widest seas and the most violent storms, they wreck their ship in harbour and close to shore." - Polybius on the Athenian constitution ("Histories", VI. 44) +"Most of the Romans were trampled to death by the enormous weight of the elephants; the rest were shot down in their ranks by the numerous cavalry: and there were only a very few who attempted to save themselves by flight." - Polybius on the Battle of Bagradas where a Roman army was annihilated during the First Punic War ("Histories", I. 34) +"Hannibal gave the signal for attack; and at the same time sent orders to the troops lying in ambush on the hills to do the same, and thus delivered an assault upon the enemy at every point at once." - Polybius on the beginning of a Roman disaster at the Trasymene Lake ("Histories", III. 84) +"In the phalanx, the men cannot turn around singly and defend themselves: this tribune, therefore, charged them \[from behind] and killed all he could get at; until, unable to resist, they were forced to throw away their shields and flee." - Polybius, describing the defeat of Philip V. of Macedon by Flaminius in the Battle of Cynoscephalae ("Histories", XVIII. 26) +"The Roman order on the other hand is flexible: for every Roman, once armed and on the field, is equally well equipped for every place, time, or appearance of the enemy. He is, moreover, quite ready and needs to make no change, whether he is required to fight in the main body, or in a detachment, or in a single maniple, or even by himself." - Polybius, explaining how the Romans can defeat the Macedonian phalanx ("Histories", XVIII. 32) +"Scipio \[Aeminialus], when he looked upon the city \[Carthage] as it was utterly perishing and in the last throes of its complete destruction, is said to have shed tears and wept openly for his enemies. And realized that all cities, nations, and authorities must, like men, meet their doom." - Polybius, eyewitness to the destruction of Carthage ("Histories", XXXVIII. 22) +"One more such victory and the cause is lost!" - Pyrrhus of Epirus after the Battle of Asculum, in which the Romans lost twice as many men but he lost a greater share of his armed forces (Plutarch, "Parallel Lives", "Pyrrhus", sec. 21) +"None can be free who is a slave to, and ruled by, his passions." - Pythagoras (Stobaeus, "Florilegium", 18) +"Do not say few things in many words, but many things in few words." - Pythagoras (Stobaeus, "Florilegium", 24) +"Let your speech be better than silence, or be silent." - Pythagoras (Stobaeus, "Florilegium", 24) +"Unity strengthens even small states, while discord undermines the mightiest empires." - Sallust ("The Jugurthine War", 10.6) +"Ungrateful fatherland, you will not even have my bones!" - Scipio Africanus in his epitaph, after he who defeated Hannibal was repeatedly accused of crimes by the Roman Senate (Valerius Maximus, "Nine books on memorable deeds and sayings", 5.3.2) +"Prepare for war, since you have been unable to endure a peace." - Scipio Africanus, replying to Hannibal's offer of peace terms before the Battle of Zama ("Ab Urbe Condita", XXX. 31) +"But tactical science is only one part of generalship. A general must be capable of equipping his forces and providing for his men. He must also be inventive, hardworking, and watchful, bullheaded and brilliant, friendly and fierce, straightforward and subtle." - Socrates (Xenophon, "Memorabilia", 3.1.6) +"It is necessary to know the strength of the city and of the enemy, so that, if the city is stronger, one may recommend her to go to war, but if weaker than the enemy, may persuade her to beware." - Socrates (Xenophon, "Memorabilia", 3.6.9) +"The unexamined life is not worth living." - Socrates, in his defense when trialled for corrupting the youth and not worshipping the proper gods (he later drank hemlock after the death sentence) - Plato ("Apology", 38a) +"The hour of departure has arrived, and we go our ways - I to die, and you to live. Which is better God only knows." - Socrates, in his defense when trialled for corrupting the youth and not worshipping the proper gods (he later drank hemlock after the death sentence) - Plato ("Apology", 42a) +"Walls and ships are nothing without men living together inside them." - Sophocles ("Oedipus Rex") +"We accepted an empire that was offered to us and refused to give it up under the pressure of three of the strongest motives: fear, honor and interest. It was not we who set the example, for it has always been the law that the weak should be subject to the strong." - Speech of an Athenian embassy in Sparta (Thucydides, "History of the Peloponnesian War", I. 76.2) +"He could boast that he found a city of brick and left it a city of marble." - Suetonius, commenting on the many building projects of Augustus in Rome ("Divus Augustus", sec 38) +"Moderation in all things." - Terence ("The Girl from Andros", 61) +"Fortune favors the bold." - Terence in a play about a great Athenian admiral ("Phormio", 203) +"I do not know how to tune the lyre or play the harp, but I do know how to raise a city that was small and unimportant to glory and greatness." - Themistocles, defending his lack of cultural sophistication (Plutarch, "Parallel Lives", "Themistocles", sec. 2) +"Strike, if you will, but listen." - Themistocles, in a heated discussion with the Spartan fleet commander who threatened to beat him with his staff, before the Battle of Salamis (Plutarch, "Parallel Lives", "Themistocles", sec. 11) +"The Athenians command the rest of Greece, I command the Athenians; your mother commands me, and you command your mother." - Themistocles, jokingly to his infant son (Plutarch, "Parallel Lives", "Themistocles", sec. 18) +"So little pains does the mob take in finding out the truth, accepting readily the first story at hand." - Thucydides ("History of the Peloponnesian War", I. 21.3) +"The growth of the power of Athens, and the alarm which this caused in Sparta, made war inevitable." - Thucydides ("History of the Peloponnesian War", I. 23.6) +"War is a matter not so much of arms as of money." - Thucydides ("History of the Peloponnesian War", I. 83.2) +"It is a general rule of human nature that people despise those who treat them well, and look up to those who make no concessions." - Thucydides ("History of the Peloponnesian War", III. 39.5) +"This was the greatest action that happened in all this war, and all others that we have heard of amongst the Greeks, being to the victors most glorious and most calamitous to the vanquished. For they were utterly and at all points defeated, and their sufferings were many. Army and fleet and all they ever had perished, nothing was saved and few of so many ever returned home. Thus ended the Sicilian expedition." - Thucydides ("History of the Peloponnesian War", VII. 87.6-7) +"As the world goes, justice is only a matter between equals, while the strong do what they can and the weak suffer what they must." - Thucydides, describing Athenians addressing the defeated Melians who are unwilling to surrender ("History of the Peloponnesian War", V. 89.1) +"When the Lacedaemonians were no longer able to run after them, the skirmishers \[...] all charged them at once, casting stones, arrows, and darts to the closest man at hand." - Thucydides, describing the Spartan disaster at the Battle of Sphacteria ("History of the Peloponnesian War", IV. 34.2) +"The soldiers fight and die to support others in wealth and luxury and they are called masters of the world without owning a single piece of farmland of their own." - Tiberius Gracchus, advocating for land reform to the benefit of homeless and unemployed veterans whose lands had often been bought up why they were on campaign (Plutarch, "Parallel Lives", "Tiberius Gracchus", sec. 9) +"The wild beasts of Italy have their caves to retire to, but the brave veterans who spilled their blood in her cause have nothing left but air and light. They wander around homeless with their wives and children." - Tiberius Gracchus, advocating for land reform to the benefit of homeless and unemployed veterans whose lands had often been bought up why they were on campaign (Plutarch, "Parallel Lives", "Tiberius Gracchus", sec. 9) +"Do not trust the horse, Trojans! I fear the Greeks even when they bring gifts." - Virgil ("Aeneid", II. 48-49) +"Prepared for either alternative." - Virgil ("Aeneid", II. 61) +"Homer and Hesiod ascribed to their Gods all things that are a disgrace among mortals: stealing, adultery, deceiving one another." - Xenophanes (fragment 11) +"If oxen and horses and lions had hands, and could paint, and produce works of art as men do, horses would paint the forms of the gods like horses, and oxen like oxen, and make their God's bodies each in their own image." - Xenophanes (fragment 15) +"The Ethiopians make their gods black and snub-nosed, the Thracians say theirs have blue eyes and red hair." - Xenophanes (fragment 16) +"These are the right questions to ask, in winter around the fire \[...]: Who are you, friend? What is your land? And how old were you when the Medes \[Persians] came?" - Xenophanes, likely referring to a punitive expedition against Greek cities in Ionia (fragment 17) +"A prudent commander will never take risks unnecessarily, except when it is clear beforehand that he will have the advantage." - Xenophon ("The Cavalry General", 4.13) +"Attack the enemy where he is weakest, even if that is a long way off, since hard work is less dangerous than a struggle against superior forces." - Xenophon ("The Cavalry General", sec. 4.14) +"He should be inventive, ready to exploit all circumstances, to make a small force appear large and a large one small, to appear absent when close at hand, and within striking distance when a long way off." - Xenophon ("The Cavalry General", sec. 5) +"People are glad to obey the man whom they believe to be wiser than themselves in pursuing their interests." - Xenophon ("The Education of Cyrus", 1.6.22) +"In his campaigns during summer the general must show that he can endure the sun better than the soldiers, in winter he must show he can endure cold better; and throughout all difficulties that he can endure hardships better. This will help to make him loved by his men." - Xenophon ("The Education of Cyrus", 1.6.25) +"Battles are decided more by the morale of men than their physical strength." - Xenophon ("The Education of Cyrus", 3.3.20) +"Let's not give them enough time to arrange a defense, or to even recognise that we are human beings! We've got to appear to them like an uncontrollable nightmare of shields, swords, battle-axes and spears!" - Xenophon ("The Education of Cyrus", 4.2.22) +"I suppose you understand, men, that pursuing, dealing blows and death, plunder, fame, freedom, power - all these are prizes for the winners; the cowardly, of course, suffer the reverse." - Xenophon ("The Education of Cyrus", 7.1.13) +"The man who wants that must be scheming and cunning, wily and deceitful, a thief and a robber, overreaching the enemy at every point." - Xenophon on how best to gain advantage over the enemy ("The Education of Cyrus", 1.6.26) +"My men have turned into women, and my women into men!" - Xerxes, watching Artemisia ram a ship while most of his fleet suffered the reverse, not knowing that the sunk vessel was his own (Herodotus, "The Histories", VIII. 88) +"For a thinking man is where Wisdom is at home." - Zoroaster, founder of the Zoroastrian religion ("Ahunuvaiti Gatha", yasna 30.9) Index: binaries/data/mods/public/l10n/public-gui-gamesetup.pot =================================================================== --- binaries/data/mods/public/l10n/public-gui-gamesetup.pot +++ binaries/data/mods/public/l10n/public-gui-gamesetup.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-04-10 09:05+0200\n" -"PO-Revision-Date: 2017-04-10 09:05+0200\n" +"POT-Creation-Date: 2017-05-08 09:02+0200\n" +"PO-Revision-Date: 2017-05-08 09:02+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -21,1407 +21,1912 @@ msgid "AI will be disabled for this player." msgstr "" -#: gui/gamesetup/gamesetup.js:36 +#: gui/gamesetup/gamesetup.js:49 +msgctxt "team" +msgid "None" +msgstr "" + +#: gui/gamesetup/gamesetup.js:71 +msgctxt "civilization" +msgid "Random" +msgstr "" + +#: gui/gamesetup/gamesetup.js:106 #, javascript-format msgid "* %(username)s is not ready." msgstr "" -#: gui/gamesetup/gamesetup.js:37 +#: gui/gamesetup/gamesetup.js:107 msgid "I'm ready" msgstr "" -#: gui/gamesetup/gamesetup.js:38 +#: gui/gamesetup/gamesetup.js:108 msgid "State that you are ready to play." msgstr "" -#: gui/gamesetup/gamesetup.js:42 +#: gui/gamesetup/gamesetup.js:112 #, javascript-format msgid "* %(username)s is ready!" msgstr "" -#: gui/gamesetup/gamesetup.js:43 +#: gui/gamesetup/gamesetup.js:113 msgid "Stay ready" msgstr "" -#: gui/gamesetup/gamesetup.js:44 +#: gui/gamesetup/gamesetup.js:114 msgid "Stay ready even when the game settings change." msgstr "" -#: gui/gamesetup/gamesetup.js:49 +#: gui/gamesetup/gamesetup.js:119 msgid "I'm not ready!" msgstr "" -#: gui/gamesetup/gamesetup.js:50 +#: gui/gamesetup/gamesetup.js:120 msgid "State that you are not ready to play." msgstr "" -#: gui/gamesetup/gamesetup.js:73 +#: gui/gamesetup/gamesetup.js:143 msgid "Game settings have been changed" msgstr "" -#: gui/gamesetup/gamesetup.js:74 +#: gui/gamesetup/gamesetup.js:144 #, javascript-format msgid "%(username)s has joined" msgstr "" -#: gui/gamesetup/gamesetup.js:75 +#: gui/gamesetup/gamesetup.js:145 #, javascript-format msgid "%(username)s has left" msgstr "" -#: gui/gamesetup/gamesetup.js:76 +#: gui/gamesetup/gamesetup.js:146 #, javascript-format msgid "%(username)s has been kicked" msgstr "" -#: gui/gamesetup/gamesetup.js:77 +#: gui/gamesetup/gamesetup.js:147 #, javascript-format msgid "%(username)s has been banned" msgstr "" -#: gui/gamesetup/gamesetup.js:78 +#: gui/gamesetup/gamesetup.js:148 #, javascript-format msgid "%(username)s %(message)s" msgstr "" -#: gui/gamesetup/gamesetup.js:79 +#: gui/gamesetup/gamesetup.js:149 #, javascript-format msgid "<%(username)s>" msgstr "" -#: gui/gamesetup/gamesetup.js:92 +#: gui/gamesetup/gamesetup.js:159 msgctxt "map filter" msgid "Default" msgstr "" -#: gui/gamesetup/gamesetup.js:97 +#: gui/gamesetup/gamesetup.js:165 msgid "Naval Maps" msgstr "" -#: gui/gamesetup/gamesetup.js:102 +#: gui/gamesetup/gamesetup.js:170 msgid "Demo Maps" msgstr "" -#: gui/gamesetup/gamesetup.js:107 +#: gui/gamesetup/gamesetup.js:175 msgid "New Maps" msgstr "" -#: gui/gamesetup/gamesetup.js:112 +#: gui/gamesetup/gamesetup.js:180 msgid "Trigger Maps" msgstr "" -#: gui/gamesetup/gamesetup.js:117 +#: gui/gamesetup/gamesetup.js:185 msgid "All Maps" msgstr "" -#: gui/gamesetup/gamesetup.js:161 -msgctxt "map selection" -msgid "Random" +#: gui/gamesetup/gamesetup.js:368 +msgid "Map Type" msgstr "" -#: gui/gamesetup/gamesetup.js:166 -msgctxt "civilization" -msgid "Random" +#: gui/gamesetup/gamesetup.js:369 +msgid "Select a map type." msgstr "" -#: gui/gamesetup/gamesetup.js:292 -msgid "Return to the lobby." +#: gui/gamesetup/gamesetup.js:391 +msgid "Map Filter" msgstr "" -#: gui/gamesetup/gamesetup.js:292 -msgid "Return to the main menu." +#: gui/gamesetup/gamesetup.js:392 +msgid "Select a map filter." msgstr "" -#: gui/gamesetup/gamesetup.js:639 -msgid "Yes" +#: gui/gamesetup/gamesetup.js:406 +msgid "Select Map" msgstr "" -#: gui/gamesetup/gamesetup.js:639 -msgid "No" +#: gui/gamesetup/gamesetup.js:407 +msgid "Select a map to play on." msgstr "" -#: gui/gamesetup/gamesetup.js:678 -msgctxt "team" -msgid "None" +#: gui/gamesetup/gamesetup.js:419 +msgid "Map Size" msgstr "" -#: gui/gamesetup/gamesetup.js:1338 -#, javascript-format -msgid "%(playerName)s %(romanNumber)s" +#: gui/gamesetup/gamesetup.js:420 +msgid "Select map size. (Larger sizes may reduce performance.)" msgstr "" -#: gui/gamesetup/gamesetup.js:1430 -msgctxt "map size" -msgid "Default" +#: gui/gamesetup/gamesetup.js:433 +msgid "Number of Players" msgstr "" -#: gui/gamesetup/gamesetup.js:1501 -msgid "Loading..." +#: gui/gamesetup/gamesetup.js:434 +msgid "Select number of players." msgstr "" -#: gui/gamesetup/gamesetup.js:1634 -#, javascript-format -msgid "AI: %(ai)s" +#: gui/gamesetup/gamesetup.js:453 +msgid "Population Cap" msgstr "" -#: gui/gamesetup/gamesetup.js:1639 -msgid "Unassigned" +#: gui/gamesetup/gamesetup.js:454 +msgid "Select population cap." msgstr "" -#: gui/gamesetup/gamesetup.js:1799 -#, javascript-format -msgid "== %(message)s" +#: gui/gamesetup/gamesetup.js:466 +msgid "Starting Resources" msgstr "" -#: gui/gamesetup/gamesetup.js:1806 -msgid "Unknown Player" +#: gui/gamesetup/gamesetup.js:467 +msgid "Select the game's starting resources." msgstr "" -#: gui/gamesetup/gamesetup.js:1845 -#, javascript-format -msgid "%(time)s %(message)s" +#: gui/gamesetup/gamesetup.js:480 +msgid "Ceasefire" msgstr "" -#: gui/gamesetup/gamesetup.js:1846 -#, javascript-format -msgid "\\[%(time)s]" +#: gui/gamesetup/gamesetup.js:481 +msgid "Set time where no attacks are possible." msgstr "" -#: gui/gamesetup/gamesetup.js:1847 -msgid "HH:mm" +#: gui/gamesetup/gamesetup.js:493 +msgid "Victory Condition" msgstr "" -#: gui/loading/loading.js:48 -#, javascript-format -msgid "Loading “%(map)s”" +#: gui/gamesetup/gamesetup.js:494 +msgid "Select victory condition." msgstr "" -#: gui/loading/loading.js:52 -#, javascript-format -msgid "Generating “%(map)s”" +#: gui/gamesetup/gamesetup.js:508 +msgid "Relic Count" msgstr "" -#: gui/aiconfig/aiconfig.xml:(caption):16 -msgid "AI Configuration" +#: gui/gamesetup/gamesetup.js:509 +msgid "Total number of relics spawned on the map." msgstr "" -#: gui/aiconfig/aiconfig.xml:(caption):21 -msgid "AI Player:" +#: gui/gamesetup/gamesetup.js:522 +msgid "Victory Duration" msgstr "" -#: gui/aiconfig/aiconfig.xml:(caption):30 -msgid "AI Difficulty:" +#: gui/gamesetup/gamesetup.js:523 +msgid "Number of minutes until the player has won." msgstr "" -#: gui/aiconfig/aiconfig.xml:(caption):41 -msgid "Cancel" +#: gui/gamesetup/gamesetup.js:538 +msgid "Game Speed" msgstr "" -#: gui/aiconfig/aiconfig.xml:(caption):46 -#: gui/gamesetup/gamesetup.xml:(caption):469 -msgid "OK" +#: gui/gamesetup/gamesetup.js:539 +msgid "Select game speed." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):21 -msgid "Match Setup" +#. Translation: Make sure to differentiate between the revealed map and explored map options! +#: gui/gamesetup/gamesetup.js:638 +msgid "Revealed Map" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):27 -msgid "Loading" +#. Translation: Make sure to differentiate between the revealed map and explored map options! +#: gui/gamesetup/gamesetup.js:641 +msgid "Toggle revealed map (see everything)." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):31 -msgid "Loading map data. Please wait..." +#. Translation: Make sure to differentiate between the revealed map and explored map options! +#: gui/gamesetup/gamesetup.js:656 +msgid "Explored Map" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):47 -msgid "Player Name" +#. Translation: Make sure to differentiate between the revealed map and explored map options! +#: gui/gamesetup/gamesetup.js:659 +msgid "Toggle explored map (see initial map)." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):51 -msgid "Color" +#: gui/gamesetup/gamesetup.js:669 +msgid "Disable Treasures" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):55 -msgid "Player Placement" +#: gui/gamesetup/gamesetup.js:670 +msgid "Disable all treasures on the map." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):59 -msgid "Civilization" +#: gui/gamesetup/gamesetup.js:680 +msgid "Disable Spies" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):87 -msgid "Team" +#: gui/gamesetup/gamesetup.js:681 +msgid "Disable spies during the game." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):141 -msgid "Show this message in the future" +#: gui/gamesetup/gamesetup.js:691 +msgid "Teams Locked" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):151 -msgid "Map Type:" +#: gui/gamesetup/gamesetup.js:692 +msgid "Toggle locked teams." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):154 -msgid "Map Filter:" +#: gui/gamesetup/gamesetup.js:705 +msgid "Last Man Standing" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):157 -msgid "Select Map:" +#: gui/gamesetup/gamesetup.js:706 +msgid "" +"Toggle whether the last remaining player or the last remaining set of allies " +"wins." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):160 -msgid "Number of Players:" +#: gui/gamesetup/gamesetup.js:718 +msgid "Cheats" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):163 -msgid "Map Size:" +#: gui/gamesetup/gamesetup.js:719 +msgid "Toggle the usability of cheats." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):242 -msgid "Send" +#: gui/gamesetup/gamesetup.js:731 +msgid "Rated Game" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):260 -msgid "Cheats enabled." +#: gui/gamesetup/gamesetup.js:732 +msgid "Toggle if this game will be rated for the leaderboard." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):273 -msgid "Start game!" +#: gui/gamesetup/gamesetup.js:757 +#, javascript-format +msgid "Press %(hotkey)s to autocomplete playernames or settings." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):291 -msgid "Back" +#: gui/gamesetup/gamesetup.js:765 +msgid "Return to the lobby." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):305 -#: gui/gamesetup/gamesetup.xml:(caption):317 -msgid "More Options" +#: gui/gamesetup/gamesetup.js:766 +msgid "Return to the main menu." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):322 -msgid "Game Speed:" +#: gui/gamesetup/gamesetup.js:770 +msgid "Start game!" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):332 -msgid "Victory Condition:" +#: gui/gamesetup/gamesetup.js:776 +msgid "Start a new game with the current settings." msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):342 -msgid "Victory Duration:" +#: gui/gamesetup/gamesetup.js:777 +msgid "" +"Start a new game with the current settings (disabled until all players are " +"ready)" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):352 -msgid "Population Cap:" +#: gui/gamesetup/gamesetup.js:1257 +msgctxt "map selection" +msgid "Random" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):362 -msgid "Starting Resources:" +#: gui/gamesetup/gamesetup.js:1543 gui/gamesetup/gamesetup.js:1582 +#, javascript-format +msgid "%(option)s:" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):372 -msgid "Ceasefire:" +#: gui/gamesetup/gamesetup.js:1548 +msgctxt "option value" +msgid "Unknown" msgstr "" -#. Make sure to differentiate between the revealed map and explored map options! -#: gui/gamesetup/gamesetup.xml:(caption):382 -msgid "Revealed Map:" +#: gui/gamesetup/gamesetup.js:1575 +msgid "Yes" msgstr "" -#. Make sure to differentiate between the revealed map and explored map options! -#: gui/gamesetup/gamesetup.xml:(caption):392 -msgid "Explored Map:" +#: gui/gamesetup/gamesetup.js:1575 +msgid "No" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):402 -msgid "Disable Treasures:" +#: gui/gamesetup/gamesetup.js:1662 +#, javascript-format +msgid "%(playerName)s %(romanNumber)s" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):412 -msgid "Disable Spies:" +#: gui/gamesetup/gamesetup.js:1853 +#, javascript-format +msgid "AI: %(ai)s" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):422 -msgid "Teams Locked:" +#: gui/gamesetup/gamesetup.js:1860 +msgid "Unassigned" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):432 -msgid "Last Man Standing:" +#: gui/gamesetup/gamesetup.js:1932 +#, javascript-format +msgid "== %(message)s" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):442 -msgid "Cheats:" +#: gui/gamesetup/gamesetup.js:1939 +msgid "Unknown Player" msgstr "" -#: gui/gamesetup/gamesetup.xml:(caption):452 -msgid "Rated Game:" +#: gui/gamesetup/gamesetup.js:1978 +#, javascript-format +msgid "%(time)s %(message)s" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):69 -msgid "View civilization info" +#: gui/gamesetup/gamesetup.js:1979 +#, javascript-format +msgid "\\[%(time)s]" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):82 -msgid "Reset any civilizations that have been selected to the default (random)" +#: gui/gamesetup/gamesetup.js:1980 +msgid "HH:mm" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):97 -msgid "Reset all teams to the default." +#: gui/loading/loading.js:48 +#, javascript-format +msgid "Loading “%(map)s”" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):108 -msgid "Pick a color." +#: gui/loading/loading.js:52 +#, javascript-format +msgid "Generating “%(map)s”" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):111 -msgid "Select player." +#: gui/aiconfig/aiconfig.xml:(caption):16 +msgid "AI Configuration" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):121 -msgid "Configure AI settings." +#: gui/aiconfig/aiconfig.xml:(caption):21 +msgid "AI Player:" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):124 -msgid "Select player's civilization." +#: gui/aiconfig/aiconfig.xml:(caption):30 +msgid "AI Difficulty:" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):128 -msgid "Select player's team." +#: gui/aiconfig/aiconfig.xml:(caption):41 +msgid "Cancel" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):180 -msgid "Select a map type." +#: gui/aiconfig/aiconfig.xml:(caption):46 +#: gui/gamesetup/gamesetup.xml:(caption):356 +msgid "OK" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):188 -msgid "Select a map filter." +#: gui/gamesetup/gamesetup.xml:(caption):21 +msgid "Match Setup" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):197 -msgid "Select a map to play on." +#: gui/gamesetup/gamesetup.xml:(caption):27 +msgid "Loading" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):207 -msgid "Select number of players." +#: gui/gamesetup/gamesetup.xml:(caption):31 +msgid "Loading map data. Please wait..." msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):211 -msgid "Select map size. (Larger sizes may reduce performance.)" +#: gui/gamesetup/gamesetup.xml:(caption):47 +msgid "Player Name" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):274 -msgid "Start a new game with the current settings." +#: gui/gamesetup/gamesetup.xml:(caption):51 +msgid "Color" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):306 -msgid "See more game options" +#: gui/gamesetup/gamesetup.xml:(caption):55 +msgid "Player Placement" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):326 -msgid "Select game speed." +#: gui/gamesetup/gamesetup.xml:(caption):59 +msgid "Civilization" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):336 -msgid "Select victory condition." +#: gui/gamesetup/gamesetup.xml:(caption):87 +msgid "Team" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):346 -msgid "Number of minutes until the player has won." +#: gui/gamesetup/gamesetup.xml:(caption):141 +msgid "Show this message in the future" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):356 -msgid "Select population cap." +#: gui/gamesetup/gamesetup.xml:(caption):225 +msgid "Send" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):366 -msgid "Select the game's starting resources." +#: gui/gamesetup/gamesetup.xml:(caption):242 +msgid "Cheats enabled." msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):376 -msgid "Set time where no attacks are possible." +#: gui/gamesetup/gamesetup.xml:(caption):270 +msgid "Back" msgstr "" -#. Make sure to differentiate between the revealed map and explored map options! -#: gui/gamesetup/gamesetup.xml:(tooltip):386 -msgid "Toggle revealed map (see everything)." +#: gui/gamesetup/gamesetup.xml:(caption):284 +#: gui/gamesetup/gamesetup.xml:(caption):296 +msgid "More Options" msgstr "" -#. Make sure to differentiate between the revealed map and explored map options! -#: gui/gamesetup/gamesetup.xml:(tooltip):396 -msgid "Toggle explored map (see initial map)." +#: gui/gamesetup/gamesetup.xml:(tooltip):69 +msgid "View civilization info" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):406 -msgid "Disable all treasures on the map." +#: gui/gamesetup/gamesetup.xml:(tooltip):82 +msgid "Reset any civilizations that have been selected to the default (random)" msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):416 -msgid "Disable spies during the game." +#: gui/gamesetup/gamesetup.xml:(tooltip):97 +msgid "Reset all teams to the default." msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):426 -msgid "Toggle locked teams." +#: gui/gamesetup/gamesetup.xml:(tooltip):108 +msgid "Pick a color." msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):436 -msgid "" -"Toggle whether the last remaining player or the last remaining set of allies " -"wins." +#: gui/gamesetup/gamesetup.xml:(tooltip):111 +msgid "Select player." msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):446 -msgid "Toggle the usability of cheats." +#: gui/gamesetup/gamesetup.xml:(tooltip):121 +msgid "Configure AI settings." msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):456 -msgid "Toggle if this game will be rated for the leaderboard." +#: gui/gamesetup/gamesetup.xml:(tooltip):124 +msgid "Select player's civilization." msgstr "" -#: gui/gamesetup/gamesetup.xml:(tooltip):470 -msgid "Close more game options window" +#: gui/gamesetup/gamesetup.xml:(tooltip):128 +msgid "Select player's team." msgstr "" -#: gui/loading/loading.xml:(caption):48 -msgid "Quote of the Day:" +#: gui/gamesetup/gamesetup.xml:(tooltip):285 +msgid "See more game options" +msgstr "" + +#: gui/gamesetup/gamesetup.xml:(tooltip):357 +msgid "Close more game options window" msgstr "" #: gui/text/quotes.txt:1 -msgid "\"A prosperous fool is a grievous burden.\" - Aeschylus" +msgid "" +"\"Zeus \\[...] established his law: wisdom comes through suffering. \\[...] " +"So men against their will learn to practice moderation. \\[...] Such grace " +"is harsh and violent.\" - Aeschylus (\"Oresteia\", I. 176-183)" msgstr "" #: gui/text/quotes.txt:2 msgid "" -"\"From him \\[Death] alone of all the powers of heaven Persuasion holds " -"aloof.\" - Aeschylus" +"\"She \\[Helen] brought to Ilium her dowry, destruction.\" - Aeschylus " +"(\"Oresteia\", I. 406)" msgstr "" #: gui/text/quotes.txt:3 -msgid "\"To be rather than to seem.\" - Aeschylus, \"Seven Against Thebes\"" +msgid "" +"\"In every tyrant's heart there springs in the end this poison, that he " +"cannot trust a friend.\" - Aeschylus (\"Prometheus Bound\", 224-225)" msgstr "" #: gui/text/quotes.txt:4 msgid "" -"\"It is not the oath that makes us believe the man, but the man the oath.\" " -"- Aeschylus" +"\"Time in the long run teaches all things.\" - Aeschylus (\"Prometheus " +"Bound\", 981)" msgstr "" #: gui/text/quotes.txt:5 msgid "" -"\"In every tyrant's heart there springs in the end this poison, that he " -"cannot trust a friend.\" - Aeschylus, \"Prometheus Bound\"" +"\"His resolve is not to seem, but to be, the best.\" - Aeschylus (\"Seven " +"Against Thebes\", 592)" msgstr "" #: gui/text/quotes.txt:6 -msgid "" -"\"Time as he grows old teaches all things.\" - Aeschylus, \"Prometheus " -"Bound\"" +msgid "\"A prosperous fool is a grievous burden.\" - Aeschylus (fragment 383)" msgstr "" #: gui/text/quotes.txt:7 msgid "" -"\"Wisdom comes through suffering... Favours come to us from gods.\" - " -"Aeschylus, \"Agamemnon\"" +"\"The gods help those that help themselves.\" - Aesop (\"Hercules and the " +"Wagoner\")" msgstr "" #: gui/text/quotes.txt:8 msgid "" -"\"She \\[Helen] brought to Ilium her dowry, destruction.\" - Aeschylus, " -"\"Agamemnon\"" +"\"It is thrifty to prepare today for the wants of tomorrow.\" - Aesop (\"The " +"Ant and the Grasshopper\")" msgstr "" #: gui/text/quotes.txt:9 -msgid "" -"\"It is in the character of very few men to honor without envy a friend who " -"has prospered.\" - Aeschylus, \"Agamemnon\"" +msgid "\"Union gives strength.\" - Aesop (\"The Bundle of Sticks\")" msgstr "" #: gui/text/quotes.txt:10 msgid "" -"\"For a deadly blow let him pay with a deadly blow; it is for him who has " -"done a deed to suffer.\" - Aeschylus" +"\"Never trust advice from a man in the throes of his own difficulty.\" - " +"Aesop (\"The Fox and the Goat\")" msgstr "" #: gui/text/quotes.txt:11 -msgid "\"Any excuse will serve a tyrant.\" - Aesop" +msgid "" +"\"Familiarity breeds contempt; acquaintance softens prejudices.\" - Aesop " +"(\"The Fox and the Lion\")" msgstr "" #: gui/text/quotes.txt:12 msgid "" -"\"Better be wise by the misfortunes of others than by your own.\" - Aesop" +"\"Self-conceit may lead to self-destruction.\" - Aesop (\"The Frog and the " +"Ox\")" msgstr "" #: gui/text/quotes.txt:13 msgid "" -"\"Familiarity breeds contempt; acquaintance softens prejudices.\" - Aesop" +"\"Slow and steady wins the race.\" - Aesop (\"The Hare and the Tortoise\")" msgstr "" #: gui/text/quotes.txt:14 -msgid "\"It is easy to be brave from a safe distance.\" - Aesop" +msgid "" +"\"Better be wise by the misfortunes of others than by your own.\" - Aesop " +"(\"The Lion, the Ass, and the Fox Hunting\")" msgstr "" #: gui/text/quotes.txt:15 -msgid "\"It is thrifty to prepare today for the wants of tomorrow.\" - Aesop" +msgid "" +"\"Enemies' promises were made to be broken.\" - Aesop (\"The Nurse and the " +"Wolf\")" msgstr "" #: gui/text/quotes.txt:16 -msgid "\"It is not only fine feathers that make fine birds.\" - Aesop" +msgid "\"Any excuse will serve a tyrant.\" - Aesop (\"The Wolf and the Lamb\")" msgstr "" #: gui/text/quotes.txt:17 msgid "" -"\"Never trust advice from a man in the throes of his own difficulty.\" - " -"Aesop" +"\"If I have done anything noble, that is a sufficient memorial; if I have " +"not, all the statues in the world will not preserve my memory.\" - Agesilaos " +"II of Sparta (Plutarch, \"Moralia\", XVI. \"Sayings of Spartans\", 215a)" msgstr "" #: gui/text/quotes.txt:18 -msgid "\"Persuasion is often more effectual than force.\" - Aesop" +msgid "" +"\"Spartans do not ask how many, only where the enemy are.\" - Agis II of " +"Sparta (Plutarch, \"Moralia\", XVI. \"Sayings of Spartans\", 215d)" msgstr "" #: gui/text/quotes.txt:19 -msgid "\"Self-conceit may lead to self-destruction.\" - Aesop" +msgid "" +"\"Weep not for me, as I suffer unjustly, I am in a happier situation than my " +"murderers.\" - Agis IV of Sparta upon seeing one of his executioners cry " +"(Plutarch, \"Parallel Lives\", \"Agis\", sec. 20)" msgstr "" #: gui/text/quotes.txt:20 -msgid "\"Slow and steady wins the race.\" - Aesop" +msgid "" +"\"Sex and sleep alone make me conscious that I am mortal.\" - Alexander the " +"Great (Plutarch, \"Parallel Lives\", \"Alexander\", sec. 22)" msgstr "" #: gui/text/quotes.txt:21 -msgid "\"The gods help them that help themselves.\" - Aesop" +msgid "" +"\"It is very servile to live in luxury, but very royal to toil. \\[...] " +"Don't you know that the end and object of conquest is to avoid the vices and " +"infirmities of the subdued?\" - Alexander the Great (Plutarch, \"Parallel " +"Lives\", \"Alexander\", sec. 40)" msgstr "" #: gui/text/quotes.txt:22 msgid "" -"\"The shaft of the arrow had been feathered with one of the eagle's own " -"plumes. We often give our enemies the means of our own destruction.\" - Aesop" +"\"Glorious are the deeds of those who undergo labour and run the risk of " +"danger; and it is delightful to live a life of valor and to die leaving " +"behind immortal glory.\" - Alexander the Great, addressing his troops " +"(Arrian, \"The Anabasis of Alexander\", 4.26)" msgstr "" #: gui/text/quotes.txt:23 -msgid "\"Union gives strength.\" - Aesop, \"The Bundle of Sticks\"" +msgid "" +"\"I for one think that to a brave man there is no end to labours except the " +"labours themselves, provided they lead to glorious achievements.\" - " +"Alexander the Great, addressing his troops (Arrian, \"The Anabasis of " +"Alexander\", 5.26)" msgstr "" #: gui/text/quotes.txt:24 -msgid "\"Enemies' promises were made to be broken.\" - Aesop" +msgid "" +"\"If I were not Alexander, I should wish to be Diogenes \\[of Sinope].\" - " +"Alexander the Great, impressed by the simplicity of the philosopher he had " +"met (Plutarch, \"Moralia\", XXII. \"On the Fortunes of Alexander the " +"Great\", 332a-b)" msgstr "" #: gui/text/quotes.txt:25 msgid "" -"\"Spartans do not ask how many, only where the enemy are.\" - Agis II of " -"Sparta" +"\"To the strongest!\" - Alexander the Great, on his death bed, when asked " +"who should succeed him as king (Arrian, \"The Anabasis of Alexander\", 7.26)" msgstr "" #: gui/text/quotes.txt:26 msgid "" -"\"Weep not for me, suffering as I do unjustly, I am in a happier case than " -"my murderers.\" - Agis IV, 24th Spartan king of the Eurypontid dynasty" +"\"I do not steal victory.\" - Alexander the Great, when suggested to raid " +"the Persians at night (Plutarch, \"Parallel Lives\", \"Alexander\", sec. 31)" msgstr "" #: gui/text/quotes.txt:27 msgid "" -"\"He who doeth good shall meet with good; and he who doeth evil shall meet " -"with evil, for the Lord judgeth a man according to the measure of his work." -"\" - Ahiqar, Assyrian sage" +"\"Written laws are like spiders' webs; they will catch, it is true, the weak " +"and poor, but will be torn in pieces by the rich and powerful.\" - " +"Anacharsis (Plutarch, \"Parallel Lives\", \"Solon\", sec. 5)" msgstr "" #: gui/text/quotes.txt:28 msgid "" -"\"If I were not Alexander, I should wish to be Diogenes.\" - Alexander the " -"Great" +"\"The agora is an established place for men to cheat one another, and behave " +"covetously.\" - Anacharsis, a Scythian philosopher who travelled to Greece " +"(Diogenes Laertius, \"The Lives and Opinions of Eminent Philosophers\", " +"Anarchsis, sec. 5)" msgstr "" #: gui/text/quotes.txt:29 -msgid "\"I do not steal victory.\" - Alexander the Great" +msgid "" +"\"It was not by taking care of the fields, but of ourselves, that we " +"acquired those fields.\" - Anaxandridas II of Sparta (Plutarch, \"Moralia\", " +"XVI. \"Sayings of Spartans\", 217a)" msgstr "" #: gui/text/quotes.txt:30 msgid "" -"\"Are you still to learn that the end and perfection of our victories is to " -"avoid the vices and infirmities of those whom we subdue?\" - Alexander the " -"Great" +"\"States are doomed when they are unable to distinguish good men from bad.\" " +"- Antisthenes (Diogenes Laertius, \"The Lives and Opinions of Eminent " +"Philosophers\", Antisthenes, sec. 5)" msgstr "" #: gui/text/quotes.txt:31 msgid "" -"\"To the strongest!\" - Alexander, on his death bed, when asked who should " -"succeed him as king" +"\"The fox knows many tricks; the hedgehog one good one.\" - Archilochus " +"(fragment 201)" msgstr "" #: gui/text/quotes.txt:32 msgid "" -"\"There is nothing impossible to him who will try\" - Alexander the Great" +"\"Give me a place to stand, and I shall move the world.\" - Archimedes, on " +"his usage of the lever (Diodorus Siculus, \"The Library of History\", " +"fragments of book XXVI, sec. 18)" msgstr "" #: gui/text/quotes.txt:33 msgid "" -"\"Sex and sleep alone make me conscious that I am mortal.\" - Alexander the " -"Great" +"\"It is from their foes, not their friends, that cities learn the lesson of " +"building high walls and ships of war.\" - Aristophanes (\"Birds\")" msgstr "" #: gui/text/quotes.txt:34 msgid "" -"\"The agora is an established place for men to cheat one another, and behave " -"covetously.\" - Anacharsis" +"\"It is obligatory, especially for a philosopher, to sacrifice even one's " +"closest personal ties in defense of the truth.\" - Aristotle (\"Nicomachean " +"Ethics\", I. 1096a.11)" msgstr "" #: gui/text/quotes.txt:35 msgid "" -"\"Written laws are like spiders’ webs; they will catch, it is true, the weak " -"and poor, but would be torn in pieces by the rich and powerful.\" - " -"Anacharsis" +"\"Happiness depends on leisure; for we are busy to have leisure, and make " +"war to live in peace.\" - Aristotle (\"Nicomachean Ethics\", X. 1177b.4)" msgstr "" #: gui/text/quotes.txt:36 -msgid "\"The fox knows many tricks; the hedgehog one good one.\" - Archilochus" +msgid "" +"\"Man is by nature a political animal.\" - Aristotle (\"Politics\", I. 1253a." +"2)" msgstr "" #: gui/text/quotes.txt:37 msgid "" -"\"States are doomed when they are unable to distinguish good men from bad.\" " -"- Antisthenes" +"\"Both oligarch and tyrant mistrust the people, and therefore deprive them " +"of their arms.\" - Aristotle (\"Politics, V. 1311a.11)" msgstr "" #: gui/text/quotes.txt:38 msgid "" -"\"Give me a place to stand, and I shall move the world.\" - Archimedes, on " -"his usage of the lever." +"\"I have gained this by philosophy: that I do without being commanded what " +"others do only from fear of the law.\" - Aristotle (Diogenes Laertius, \"The " +"Lives and Opinions of Eminent Philosophers\", Aristotle, sec. 20)" msgstr "" #: gui/text/quotes.txt:39 msgid "" -"\"It is from their foes, not their friends, that cities learn the lesson of " -"building high walls and ships of war.\" - Aristophanes" +"\"I count him braver who overcomes his desires than him who conquers his " +"enemies, for the hardest victory is over the self.\" - Aristotle (Stobaeus, " +"\"Florilegium\", 223)" msgstr "" #: gui/text/quotes.txt:40 msgid "" -"\"Words give wings to the mind and make a man soar to heaven.\" - " -"Aristophanes" +"\"Alexander himself, plagued by thirst, with great pain and difficulty " +"nevertheless led the army on foot \\[...]. At this time a few of the light-" +"armed soldiers \\[...] found some water \\[...], poured the water into a " +"helmet and carried it to him. He took it, and commending the men who brought " +"it, immediately poured it upon the ground in the sight of all.\" - Arrian " +"about Alexander's march through the Gedrosian desert (\"The Anabasis of " +"Alexander\", 6.26)" msgstr "" #: gui/text/quotes.txt:41 msgid "" -"\"It is the compelling power of great thoughts and ideas to engender " -"language of equal greatness.\" - Aristophanes" +"\"Thrusting his spear into Mithridates' face, he \\[Alexander] hurled him to " +"the ground. Then Rhoesaces \\[a Persian] \\[...] struck him on the head with " +"his sword. \\[...] Alexander hurled him too to the ground, piercing with his " +"lance through his breastplate into his chest. Sphithridates \\[a Persian] " +"had already raised his sword against Alexander from behind when Clitus " +"\\[...] cut his arm off.\" - Arrian about the Battle of the Granicus (\"The " +"Anabasis of Alexander\", 1.15)" msgstr "" #: gui/text/quotes.txt:42 -msgid "\"Hunger knows no friend but its feeder.\" - Aristophanes" +msgid "" +"\"Let every man remind their descendants that they also are soldiers who " +"must not desert the ranks of their ancestors, or retreat out of cowardice.\" " +"- Aspasia (Plato, \"Menexenus\", 246b)" msgstr "" #: gui/text/quotes.txt:43 msgid "" -"\"I count him braver who overcomes his desires than him who overcomes his " -"enemies.\" - Aristotle" +"\"Quintilius Varus, give me back my legions!\" - Augustus, after three " +"legions were annihilated in the Battle of the Teutoburg Forest (Suetonius, " +"\"Divus Augustus\", sec. 23)" msgstr "" #: gui/text/quotes.txt:44 -msgid "\"Our characters are the result of our conduct.\" - Aristotle" +msgid "" +"\"In my nineteenth year, on my own initiative and at my own expense, I " +"raised an army with which I liberated the state, which was oppressed by the " +"tyranny of a faction.\" - Augustus, in his autobiography (\"Res Gestae Divi " +"Augusti\", sec. 1)" msgstr "" #: gui/text/quotes.txt:45 -msgid "\"Man is by nature a political animal.\" - Aristotle" +msgid "" +"\"Wars, both civil and foreign, I waged throughout the world, on sea and " +"land, and when victorious I spared all citizens who sued for pardon. The " +"foreign nations which could with safety be pardoned I preferred to save " +"rather than to destroy.\" - Augustus, in his autobiography (\"Res Gestae " +"Divi Augusti\", sec. 3)" msgstr "" #: gui/text/quotes.txt:46 msgid "" -"\"If liberty and equality, as is thought by some, are chiefly to be found in " -"democracy, they will be best attained when all persons alike share in the " -"government to the utmost.\" - Aristotle" +"\"Choose the course which you adopt with deliberation; but when you have " +"adopted it, then persevere in it with firmness.\" - Bias of Priene (Diogenes " +"Laertius, \"The Lives and Opinions of Eminent Philosophers\", Bias, sec. 5)" msgstr "" #: gui/text/quotes.txt:47 msgid "" -"\"Both oligarch and tyrant mistrust the people, and therefore deprive them " -"of their arms.\" - Aristotle" +"\"How stupid it was for the king to tear out his hair in grief, as if " +"baldness were a cure for sorrow.\" - Bion of Borysthenes (Cicero, \"Tusculan " +"Disputations\", III. 26)" msgstr "" #: gui/text/quotes.txt:48 -msgid "\"Wit is well-bred insolence.\" - Aristotle" +msgid "" +"\"He has not acquired a fortune; the fortune has acquired him.\" - Bion of " +"Borysthenes, referring to a wealthy miser (Diogenes Laertius, \"The Lives " +"and Opinions of Eminent Philosophers\", Bion, sec. 50)" msgstr "" #: gui/text/quotes.txt:49 msgid "" -"\"It is simplicity that makes the uneducated more effective than the " -"educated when addressing popular audiences.\" - Aristotle" +"\"Woe to the Defeated!\" - Brennus, Gaulish chieftain who had seized Rome " +"(with the exception of a garrison on Capitoline Hill). When Camillus arrived " +"from Veii and besieged him, he negotiated his withdrawal for 1000 pounds of " +"gold, but not without using false weights and adding the weight of his sword " +"on the scale when the Romans complained (Polybius, \"Histories\", II. 18)" msgstr "" #: gui/text/quotes.txt:50 msgid "" -"\"Poetry demands a man with a special gift for it, or else one with a touch " -"of madness in him.\" - Aristotle" +"\"Robbery, slaughter, plunder, they \\[the Romans] deceivingly name empire; " +"they make a wasteland and call it peace.\" - Calgacus, Caledonian chieftain " +"in a speech before the Battle of Mons Graupius (Tacitus, \"Agricola\", 30)" msgstr "" #: gui/text/quotes.txt:51 -msgid "" -"\"I have gained this by philosophy: that I do without being commanded what " -"others do only from fear of the law.\" - Aristotle" +msgid "\"Set a thief to catch a thief.\" - Callimachus (\"Epigrams\", 44)" msgstr "" #: gui/text/quotes.txt:52 msgid "" -"\"Choose the course which you adopt with deliberation; but when you have " -"adopted it, then persevere in it with firmness.\" - Bias of Priene" +"\"All mankind rules its women, and we rule all mankind, but our women rule " +"us.\" - Cato the Elder (Plutarch, \"Moralia\", III. \"Sayings of Romans\", " +"198e)" msgstr "" #: gui/text/quotes.txt:53 msgid "" -"\"How stupid it was for the king to tear out his hair in grief, as if " -"baldness were a cure for sorrow.\" - Bion of Borysthenes" +"\"The worst ruler is one who cannot rule himself.\" - Cato the Elder " +"(Plutarch, \"Moralia\", III. \"Sayings of Romans\", 198f)" msgstr "" #: gui/text/quotes.txt:54 msgid "" -"\"He has not acquired a fortune; the fortune has acquired him.\" - Bion of " -"Borysthenes" +"\"Wise men learn more from fools than fools from the wise.\" - Cato the " +"Elder (Plutarch, \"Parallel Lives\", \"Cato the Elder\", sec. 9)" msgstr "" #: gui/text/quotes.txt:55 -msgid "\"Woe to the Defeated\" - Brennus" +msgid "" +"\"Moreover, I consider that Carthage should be destroyed.\" - Cato the " +"Elder, who ended all speeches in his later life with this statement " +"(Plutarch, \"Parallel Lives\", \"Cato the Elder\", sec. 27)" msgstr "" #: gui/text/quotes.txt:56 msgid "" -"\"Thus always to tyrants.\" - Marcus Junius Brutus, after assassinating " -"Gaius Julius Caesar" +"\"If a king is energetic, his subjects will be equally energetic.\" - " +"Chanakya (\"Arthashastra\", I. \"Concerning Discipline\", chapter 19)" msgstr "" #: gui/text/quotes.txt:57 msgid "" -"\"Escape, yes, but this time with my hands, not my feet.\" - Marcus Junius " -"Brutus, before committing suicide" +"\"Whoever imposes severe punishment becomes repulsive to the people; while " +"he who awards mild punishment becomes contemptible. But whoever imposes " +"punishment as deserved becomes respectable.\" - Chanakya (\"Arthashastra\", " +"I. \"Concerning Discipline\", chapter 4)" msgstr "" #: gui/text/quotes.txt:58 -msgid "\"I came, I saw, I conquered.\" - Gaius Julius Caesar" +msgid "" +"\"We did not flinch but gave our lives to save Greece when her fate hung on " +"a razor's edge.\" - Corinthian epitaph to their fallen of the Persian Wars " +"(Plutarch, \"Moralia\", XI. \"On the Malice of Herodotus\", 870e)" msgstr "" #: gui/text/quotes.txt:59 -msgid "\"Men willingly believe what they wish.\" - Gaius Julius Caesar" +msgid "" +"\"Then the blood really flowed, for the two lines were so close that shield " +"struck against shield, and they drove their swords into each other's faces. " +"It was impossible for the weak or cowardly to retreat; man to man they " +"fought like in single combat.\" - Curtius Rufus about the Battle of Issos " +"(\"Histories of Alexander the Great\", III. 11.5)" msgstr "" #: gui/text/quotes.txt:60 -msgid "\"The die is cast.\" - Gaius Julius Caesar" +msgid "" +"\"I am Cyrus, who won for the Persians their empire. Therefore do not " +"begrudge me this bit of earth that covers my bones.\" - Cyrus the Great's " +"epitaph (Plutarch, \"Parallel Lives\", \"Alexander\", sec. 69)" msgstr "" #: gui/text/quotes.txt:61 msgid "" -"\"It is not the well-fed long-haired man I fear, but the pale and the hungry " -"looking.\" - Gaius Julius Caesar" +"\"I am Darius, the great king, king of kings, the king of Persia, the king " +"of countries, \\[...] 23 lands in total.\" - Darius I. (Behistun " +"inscription, column I, 1-6)" msgstr "" #: gui/text/quotes.txt:62 -msgid "\"Set a thief to catch a thief.\" - Callimachus" +msgid "" +"\"Phraortes was captured and brought before me. I cut off his nose, his " +"ears, and his tongue, and I put out one eye, and he was kept in chains at my " +"palace entrance, and all the people saw him. Then I crucified him in " +"Ecbatana; and the men who were his foremost followers \\[...] I flayed and " +"hung out their skins, stuffed with straw.\" - Darius I. (Behistun " +"inscription, column II, 32)" msgstr "" #: gui/text/quotes.txt:63 msgid "" -"\"Wise men learn more from fools than fools from the wise.\" - Cato the Elder" +"\"By desiring little, a poor man makes himself rich.\" - Democritus " +"(fragment)" msgstr "" #: gui/text/quotes.txt:64 msgid "" -"\"Moreover, I consider that Carthage should be destroyed.\" - Cato the Elder" +"\"It is hard to be governed by one's inferior.\" - Democritus (fragment)" msgstr "" #: gui/text/quotes.txt:65 msgid "" -"\"All mankind rules its women, and we rule all mankind, but our women rule " -"us.\" - Cato the Elder" +"\"Physical strength is only noble in cattle, it is strength of character " +"that is noble in men.\" - Democritus (fragment)" msgstr "" #: gui/text/quotes.txt:66 -msgid "\"Be firm or mild as the occasion may require.\" - Cato the Elder" +msgid "" +"\"It is not possible to found a lasting power upon injustice, perjury, and " +"treachery.\" - Demosthenes, in one of his many speeches against the rising " +"Phillip II of Macedon (\"Olynthiac II\", 10)" msgstr "" #: gui/text/quotes.txt:67 -msgid "\"The worst ruler is one who cannot rule himself.\" - Cato the Elder" +msgid "" +"\"Delivery, delivery, delivery.\" - Demosthenes, when asked what were the " +"three most important elements of rhetoric (Cicero, \"De Oratore\", 3.213)" msgstr "" #: gui/text/quotes.txt:68 msgid "" -"\"Whoever imposes severe punishment becomes repulsive to the people; while " -"he who awards mild punishment becomes contemptible. But whoever imposes " -"punishment as deserved becomes respectable.\" - Chanakya" +"\"The Macedonians first raised an unearthly shout followed by the Persians " +"answering, so that the whole hillside bordering the battlefield echoed back " +"the sound, and that second roar was louder than the Macedonian war cry as " +"five hundred thousand men shouted with one voice.\" - Diodorus Siculus about " +"the Battle of Issos (\"The Library of History\", XVII., sec. 33)" msgstr "" #: gui/text/quotes.txt:69 msgid "" -"\"If a king is energetic, his subjects will be equally energetic.\" - " -"Chanakya" +"\"Brasidas, taking his stand on the gangway, fought off from there the " +"multitude of Athenians who converged upon him. And at the outset he slew " +"many as they came at him, but after a while, as numerous missiles assailed " +"him, he suffered many wounds on the front of his body.\" - Diodorus Siculus, " +"on a brave Spartan at the Battle of Pylos (\"The Library of History\", " +"XXII., sec. 62)" msgstr "" #: gui/text/quotes.txt:70 msgid "" -"\"For there is but one essential justice which cements society, and one law " -"which establishes this justice. This law is right reason, which is the true " -"rule of all commandments and prohibitions.\" - Marcus Tullius Cicero" +"\"Plato had defined man as an animal, biped and featherless, and was " +"applauded. Diogenes \\[of Sinope] plucked a fowl and brought it into the " +"lecture-room with the words: Here is Plato's man. In consequence of which " +"there was added to the definition: having broad nails.\" - Diogenes Laertius " +"(\"The Lives and Opinions of Eminent Philosophers\", \"Diogenes\", sec. 40)" msgstr "" #: gui/text/quotes.txt:71 -msgid "\"O, the times, O, the customs!\" - Marcus Tullius Cicero" +msgid "" +"\"I am a citizen of the world.\" - Diogenes of Sinope (Diogenes Laertius, " +"\"The Lives and Opinions of Eminent Philosophers\", \"Diogenes\", sec. 63)" msgstr "" #: gui/text/quotes.txt:72 msgid "" -"\"No one is so old as to think that he cannot live one more year.\" - Marcus " -"Tullius Cicero" +"\"It is not that I am mad, it is only that my head is different from yours." +"\" - Diogenes of Sinope (Stobaeus, \"Florilegium\", 51)" msgstr "" #: gui/text/quotes.txt:73 msgid "" -"\"We are not born, we do not live for ourselves alone; our country, our " -"friends, have a share in us.\" - Marcus Tullius Cicero" +"\"Yes, stand a little out of my sunshine.\" - Diogenes of Sinope to " +"Alexander the Great, who asked if he could help in in any way (Plutarch, " +"\"Parallel Lives\", \"Alexander\", sec. 14)" msgstr "" #: gui/text/quotes.txt:74 msgid "" -"\"Yield, ye arms, to the toga; to civic praise, ye laurels.\" - Marcus " -"Tullius Cicero" +"\"The victor is not victorious if the vanquished does not consider himself " +"so.\" - Ennius (\"Annales\", fragment 31.493)" msgstr "" #: gui/text/quotes.txt:75 -msgid "\"At my signal, unleash Hell.\" - Maximus Decimus Meridius" +msgid "" +"\"Stranger, go tell the Spartans that we lie here, obedient to their laws.\" " +"- Epitaph at Thermopylae for Leonidas and his men (Herodotus, \"The " +"Histories\", VII. 228)" msgstr "" #: gui/text/quotes.txt:76 -msgid "\"Time heals all wounds.\" - Marcus Tullius Cicero" +msgid "" +"\"A coward turns away, but a brave man's choice is danger.\" - Euripides " +"(\"Iphigenia in Tauris\")" msgstr "" #: gui/text/quotes.txt:77 msgid "" -"\"That, Senators, is what a favour from gangs amounts to. They refrain from " -"murdering someone; then they boast that they have spared him!\" - Marcus " -"Tullius Cicero" +"\"Brave men are made bolder by ordeals, but cowards achieve nothing.\" - " +"Euripides (\"Iphigenia in Tauris\")" msgstr "" #: gui/text/quotes.txt:78 -msgid "\"Genius is fostered by energy.\" - Marcus Tullius Cicero" +msgid "" +"\"Cowards do not count in battle; they are there, but not in it.\" - " +"Euripides (\"Meleager\")" msgstr "" #: gui/text/quotes.txt:79 -msgid "\"While there's life, there's hope.\" - Marcus Tullius Cicero" +msgid "" +"\"Chance fights ever on the side of the prudent.\" - Euripides " +"(\"Pirithous\")" msgstr "" #: gui/text/quotes.txt:80 msgid "" -"\"We must not say that every mistake is a foolish one.\" - Marcus Tullius " -"Cicero" +"\"Return with your shield, or on it.\" - Farewell of Spartan women to their " +"warriors, implying that cowards would throw away their shield in battle to " +"flee (Plutarch, \"Moralia\", XVIII. \"Sayings of Spartan Women\", 241f)" msgstr "" #: gui/text/quotes.txt:81 -msgid "\"Let the punishment match the offense.\" - Marcus Tullius Cicero" +msgid "" +"\"I came, I saw, I conquered.\" - Gaius Julius Caesar, after routing " +"Pharnaces II of Pontus in the first assault (Plutarch, \"Parallel Lives\", " +"\"Caesar\", sec. 50)" msgstr "" #: gui/text/quotes.txt:82 msgid "" -"\"Let the welfare of the people be the ultimate law.\" - Marcus Tullius " -"Cicero" +"\"Men willingly believe what they wish.\" - Gaius Julius Caesar (\"De Bello " +"Gallico\", III. 18)" msgstr "" #: gui/text/quotes.txt:83 -msgid "\"Endless money forms the sinews of war.\" - Marcus Tullius Cicero" +msgid "" +"\"It is not the well-fed long-haired man I fear, but the pale and the hungry " +"looking.\" - Gaius Julius Caesar (Plutarch, \"Parallel Lives\", \"Antony\", " +"sec. 11)" msgstr "" #: gui/text/quotes.txt:84 -msgid "\"Laws are silent in time of war.\" - Marcus Tullius Cicero" +msgid "" +"\"After fighting from noon almost to sunset, with victory doubtful, the " +"Germans, on one side charged the enemy in a compact body, and drove them " +"back; and, when they were put to flight, the archers were surrounded and cut " +"to pieces.\" - Gaius Julius Caesar about the Battle of Alesia (\"De Bello " +"Gallico\", VII. 80)" msgstr "" #: gui/text/quotes.txt:85 msgid "" -"\"A war is never undertaken by the ideal State, except in defense of its " -"honor or its safety.\" - Marcus Tullius Cicero" +"\"All the centurions of the fourth cohort were slain, and the standard-" +"bearer killed, the standard itself lost, almost all the centurions of the " +"other cohorts either wounded or slain, and among them the chief centurion of " +"the legion, Publius Sextius Baculus, a very valiant man, who was so " +"exhausted by many and severe wounds, that he was already unable to support " +"himself.\" - Gaius Julius Caesar about the Battle of the Sabis (\"De Bello " +"Gallico\", II. 25)" msgstr "" #: gui/text/quotes.txt:86 msgid "" -"\"The first duty of a man is the seeking after and the investigation of " -"truth.\" - Marcus Tullius Cicero" +"\"But the enemy \\[...] displayed such great courage, that when the front " +"rank had fallen the men behind them stood on them and continue the fight " +"from on top of the corpses; when these were killed the pile of bodies grew " +"higher, while the survivors used the heap as a vantage point for throwing " +"missiles at our men, or catching our spears and throwing them back.\" - " +"Gaius Julius Caesar about the Battle of the Sabis (\"De Bello Gallico\", II. " +"27)" msgstr "" #: gui/text/quotes.txt:87 msgid "" -"\"In peace the sons bury their fathers, but in war the fathers bury their " -"sons.\" - Croesus (Greek: Kroisos), king of Lydia" +"\"The die is cast.\" - Gaius Julius Caesar, when crossing the Rubicon river " +"with his legion into Italy, a capital offense that led to his civil war " +"against Pompey (Suetonius, \"The Lives of the Twelve Caesars\", 32)" msgstr "" #: gui/text/quotes.txt:88 -msgid "\"I am Cyrus, king of the world...\" - Cyrus the Great" +msgid "" +"\"I'd rather be the first man here than the second man in Rome.\" - Gaius " +"Julius Caesar, when passing through a barbarian village in the Alps " +"(Plutarch, \"Parallel Lives\", \"Caesar\", sec. 11)" msgstr "" #: gui/text/quotes.txt:89 -msgid "\"Diversity in counsel, unity in command.\" - Cyrus the Great" +msgid "" +"\"Stop quoting laws, we carry weapons!\" - Gnaeus Pompeius Magnus (Plutarch, " +"\"Parallel Lives\", \"Pompey\", sec. 10)" msgstr "" #: gui/text/quotes.txt:90 msgid "" -"\"Do not therefore begrudge me this bit of earth that covers my bones.\" - " -"Epitaph of Cyrus the Great" +"\"If a man put out the eye of another man, his eye shall be put out.\" - " +"Hammurabi (Hammurabi's Code, sec. 196)" msgstr "" #: gui/text/quotes.txt:91 msgid "" -"\"Success always calls for greater generosity — though most people, lost in " -"the darkness of their own egos, treat it as an occasion for greater greed.\" " -"- Cyrus the Great" +"\"I have come not to make war on the Italians, but to aid the Italians " +"against Rome.\" - Hannibal Barca (Polybius, \"Histories\", III. 85)" msgstr "" #: gui/text/quotes.txt:92 msgid "" -"\"Whether men lie, or say true, it is with one and the same object.\" - " -"Darius I" +"\"Let us now end the anxiety of the Romans, who can't wait for the death of " +"an old man.\" - Hannibal Barca's last words before his suicide, in exile " +"with Flaminius pressuring the local ruler to hand him over (Livius, \"Ab " +"Urbe Condita\", XXXIX. 51)" msgstr "" #: gui/text/quotes.txt:93 msgid "" -"\"Good breeding in cattle depends on physical health, but in men on a well-" -"formed character.\" - Democritus" +"\"Most inhuman and most arrogant of nations, they \\[the Romans] reckon the " +"world as theirs and subject to their pleasure. With whom we are to be at " +"war, with whom at peace, they think it right that they should determine.\" - " +"Hannibal Barca, addressing his troops (Livius, \"Ab Urbe Condita\", XXI. 44)" msgstr "" #: gui/text/quotes.txt:94 msgid "" -"\"If your desires are not great, a little will seem much to you; for small " -"appetite makes poverty equivalent to wealth.\" - Democritus" +"\"You must be brave and discard all hopes of anything but victory or death." +"\" - Hannibal Barca, addressing his troops (Livius, \"Ab Urbe Condita\", " +"XXI. 44)" msgstr "" #: gui/text/quotes.txt:95 msgid "" -"\"Delivery, delivery, delivery.\" - Demosthenes, when asked what were the " -"three most important elements of rhetoric" +"\"War is the father and king of all things: some he has made gods, and some " +"men; some slaves and some free.\" - Herakleitos (Hippolytus, \"The " +"Refutation of all Heresies\", IX. 4)" msgstr "" #: gui/text/quotes.txt:96 msgid "" -"\"It is not possible to found a lasting power upon injustice, perjury, and " -"treachery.\" - Demosthenes" +"\"You could not step twice into the same river.\" - Herakleitos (Plato, " +"\"Cratylos\", 402a)" msgstr "" #: gui/text/quotes.txt:97 -msgid "\"Small opportunities often presage great enterprises.\" - Demosthenes" +msgid "" +"\"It is better to be envied than to be pitied.\" - Herodotus (\"The " +"Histories\", III. 52)" msgstr "" #: gui/text/quotes.txt:98 msgid "" -"\"Every advantage in the past is judged in the light of the final issue.\" - " -"Demosthenes" +"\"In soft regions are born soft men.\" - Herodotus (\"The Histories\", IX. " +"122)" msgstr "" #: gui/text/quotes.txt:99 msgid "" -"\"Yes, stand a little out of my sunshine.\" - Diogenes of Sinope, when " -"Alexander the Great found him sunbathing and asked if he could help in in " -"any way" +"\"This is the bitterest pain among men, to have much knowledge but no power." +"\" - Herodotus (\"The Histories\", IX. 16)" msgstr "" #: gui/text/quotes.txt:100 -msgid "\"I am a citizen of the world.\" - Diogenes of Sinope" +msgid "" +"\"Although he \\[Xerces] had plenty of troops he had few men.\" - Herodotus " +"(\"The Histories\", VII. 210)" msgstr "" #: gui/text/quotes.txt:101 msgid "" -"\"It is not that I am mad, it is only that my head is different from yours." -"\" - Diogenes of Sinope" +"\"The Lacedaemonians [Spartans] fought a memorable battle; they made it " +"quite clear that they were the experts, and that they were fighting against " +"amateurs.\" - Herodotus (\"The Histories\", VII. 211)" msgstr "" #: gui/text/quotes.txt:102 msgid "" -"\"Let thy speech be better than silence, or be silent.\" - Dionysius I of " -"Syracuse" +"\"Being informed \\[...] that when the Barbarians discharged their arrows " +"they obscured the light of the sun by the multitude of the arrows, he " +"\\[Dienekes] \\[...] said that their guest \\[...] brought them very good " +"news, for if the Medes obscured the light of the sun, the battle against " +"them would be in the shade and not in the sun.\" - Herodotus describing " +"Dienekes, reputedly the bravest Spartan soldier at Thermopylae (Polybius, " +"\"Histories\", VII. 226)" msgstr "" #: gui/text/quotes.txt:103 -msgid "\"Nothing has more strength than dire necessity.\" - Euripides" +msgid "" +"\"The judgement given to Kroisus by each of the two oracles \\[Delphi and " +"Thebes] was the same: If he sent an army against the Persians, he would " +"destroy a great empire.\" - Herodotus, later mentioning that the empire " +"Kroisos destroyed was his own (\"The Histories\", I. 53)" msgstr "" #: gui/text/quotes.txt:104 msgid "" -"\"A coward turns away, but a brave man's choice is danger.\" - Euripides" +"\"He \\[King Darius] asked who the Athenians were, and, being informed, " +"called for his bow, and placing an arrow on the string, shot upward into the " +"sky, saying, as he let fly the shaft: Grant me, Zeus, to revenge myself on " +"the Athenians!\" - Herodotus, narrating how the Athenian support for the " +"Ionian revolt caught the wrath of Darius I., the Persian king (\"The " +"Histories\", V. 105)" msgstr "" #: gui/text/quotes.txt:105 msgid "" -"\"Cowards do not count in battle; they are there, but not in it.\" - " -"Euripides" +"\"He \\[King Darius] asked one of his servants every day, when his dinner " +"was spread, three times to repeat to him: Master, remember the Athenians!\" " +"- Herodotus, narrating how the Athenian support for the Ionian revolt lead " +"to the Persian Wars (\"The Histories\", V. 105)" msgstr "" #: gui/text/quotes.txt:106 -msgid "\"Chance fights ever on the side of the prudent.\" - Euripides" +msgid "" +"\"Conquered Greece took captive her savage conqueror and brought her arts " +"into rustic Latium.\" - Horace (\"Epistles\", epistle I., 156-157)" msgstr "" #: gui/text/quotes.txt:107 msgid "" -"\"If a man put out the eye of another man, his eye shall be put out.\" - " -"Hammurabi" +"\"Anger is a momentary madness, so control your passion or it will control " +"you.\" - Horace (\"Epistles\", epistle II., 62)" msgstr "" #: gui/text/quotes.txt:108 msgid "" -"\"I have come not to make war on the Italians, but to aid the Italians " -"against Rome.\" - Hannibal Barca" +"\"It is your concern when your neighbour's wall is on fire.\" - Horace " +"(\"Epistles\", epistle XVIII., 84)" msgstr "" #: gui/text/quotes.txt:109 -msgid "\"I will either find a way, or make one.\" - Hannibal Barca" +msgid "" +"\"It is sweet and honorable to die for one's country.\" - Horace (\"Odes\", " +"III., ode II., 13)" msgstr "" #: gui/text/quotes.txt:110 -msgid "" -"\"My ancestors yielded to Roman valour. I am endeavouring that others, in " -"their turn, will be obliged to yield to my good fortune, and my valour.\" - " -"Hannibal Barca" +msgid "\"I am Cyrus, king of the world...\" - Inscription (Cyrus Cylinder)" msgstr "" #: gui/text/quotes.txt:111 -msgid "\"Everything flows, nothing stands still.\" - Herakleitos" +msgid "" +"\"In peace the sons bury their fathers, but in war the fathers bury their " +"sons.\" - Kroisos, king of Lydia (Herodotus, \"The Histories\", I. 87)" msgstr "" #: gui/text/quotes.txt:112 -msgid "\"Nothing endures but change.\" - Herakleitos" +msgid "" +"\"Marry a good man, and bear good children.\" - Leonidas, to his wife who " +"asked what to do if he died, before he left for Thermopylae (Plutarch, " +"\"Moralia\", XVIII. \"Sayings of Spartan Women\", 220e)" msgstr "" #: gui/text/quotes.txt:113 -msgid "\"You could not step twice into the same river.\" - Herakleitos" +msgid "" +"\"Come and get them!\" - Leonidas, to the Persian messenger who demanded " +"that he and his men lay down their arms (Plutarch, \"Moralia\", XVI. " +"\"Sayings of Spartans\", 225c)" msgstr "" #: gui/text/quotes.txt:114 -msgid "\"Character is destiny.\" - Herakleitos" +msgid "" +"\"Some were discovered lying there alive, with thighs and tendons slashed, " +"baring their necks and throats and bidding their conquerors drain the " +"remnant of their blood. Others were found with their heads buried in holes " +"dug in the ground. They had apparently made these pits for themselves.\" - " +"Livius, describing the aftermath of the Battle of Cannae, where Hannibal " +"inflicted the greatest defeat on the Romans in all their history (\"Ab Urbe " +"Condita\", XXII. 51)" msgstr "" #: gui/text/quotes.txt:115 -msgid "\"Circumstances rule men; men do not rule circumstances.\" - Herodotus" +msgid "" +"\"There lay thousands upon thousands of Romans \\[...]. Here and there " +"amidst the slain rose a gory figure whose wounds had begun to throb with the " +"chill of dawn, and was cut down by his enemies.\" - Livius, describing the " +"aftermath of the Battle of Cannae, where Hannibal inflicted the greatest " +"defeat on the Romans in all their history (\"Ab Urbe Condita\", XXII. 51)" msgstr "" #: gui/text/quotes.txt:116 msgid "" -"\"The Lacedaemonians fought a memorable battle; they made it quite clear " -"that they were the experts, and that they were fighting against amateurs.\" " -"- Herodotus" +"\"A city is well-fortified which has a wall of men instead of brick.\" - " +"Lycurgus of Sparta (Plutarch, \"Parallel Lives\", \"Lycurgus\", sec. 19)" msgstr "" #: gui/text/quotes.txt:117 msgid "" -"\"This is the bitterest pain among men, to have much knowledge but no power." -"\" - Herodotus" +"\"Escape, yes, but this time with my hands, not my feet.\" - Marcus Junius " +"Brutus, before committing suicide after losing a battle against Caesar's " +"avengers (Plutarch, \"Parallel Lives\", \"Brutus\", sec. 52)" msgstr "" #: gui/text/quotes.txt:118 -msgid "\"In soft regions are born soft men.\" - Herodotus" +msgid "" +"\"O, the times, O, the customs!\" - Marcus Tullius Cicero (\"Against " +"Catiline\", speech I)" msgstr "" #: gui/text/quotes.txt:119 -msgid "\"It is sweet and honorable to die for one's country.\" - Horace" +msgid "" +"\"A war is never undertaken by the ideal State, except in defense of its " +"honor or its safety.\" - Marcus Tullius Cicero (\"De Re Publica\", III., 23)" msgstr "" #: gui/text/quotes.txt:120 -msgid "\"We are but dust and shadow.\" - Horace" +msgid "" +"\"The first duty of a man is the seeking after and the investigation of " +"truth.\" - Marcus Tullius Cicero (\"On Duties\", I., 13)" msgstr "" #: gui/text/quotes.txt:121 msgid "" -"\"I am not bound over to swear allegiance to any master; where the storm " -"drives me I turn in for shelter.\" - Horace" +"\"No one is so old as to think that he cannot live one more year.\" - Marcus " +"Tullius Cicero (\"On Old Age\", sec. 24)" msgstr "" #: gui/text/quotes.txt:122 msgid "" -"\"Conquered Greece took captive her savage conqueror and brought her arts " -"into rustic Latium.\" - Horace" +"\"Let the welfare of the people be the ultimate law.\" - Marcus Tullius " +"Cicero (\"On the Laws\", III., sec. 3)" msgstr "" #: gui/text/quotes.txt:123 msgid "" -"\"Guard yourself against accusations, even if they are false; for the " -"multitude are ignorant of the truth and look only to reputation.\" - " -"Isocrates" +"\"Endless money forms the sinews of war.\" - Marcus Tullius Cicero " +"(\"Philippics\", Philippica V., sec. 5)" msgstr "" #: gui/text/quotes.txt:124 msgid "" -"\"I never learned how to tune a harp, or play upon a lute; but I know how to " -"raise a small and inconsiderable city to glory and greatness.\" - " -"Themistocles" +"\"Laws are silent in time of war.\" - Marcus Tullius Cicero (\"Pro Milone\", " +"IV., sec. 11)" msgstr "" #: gui/text/quotes.txt:125 -msgid "\"I have with me two gods, Persuasion and Compulsion.\" - Themistocles" +msgid "" +"\"That, Senators, is what a favour from gangs amounts to. They refrain from " +"murdering someone; then they boast that they have spared him!\" - Marcus " +"Tullius Cicero, condemning Mark Anthony who had not killed him (yet) " +"(\"Philippics\", Philippica II, sec. 5)" msgstr "" #: gui/text/quotes.txt:126 msgid "" -"\"For the Athenians command the rest of Greece, I command the Athenians; " -"your mother commands me, and you command your mother.\" - Themistocles, in a " -"statement to his son" +"\"He did not even stand up to review his fleet when the ships were already " +"at their fighting stations, but lay on his back and gazed up at the sky, " +"never rising to show that he was alive until Marcus Agrippa had routed the " +"enemy.\" - Mark Antony, taunting Augustus who delegated his duties as naval " +"commander (Suetonius, \"Divus Augustus\", sec. 16)" msgstr "" #: gui/text/quotes.txt:127 -msgid "\"Strike, if you will, but listen.\" - Themistocles" +msgid "" +"\"We live, not as we wish to, but as we can.\" - Menander (\"Lady of " +"Andros\", fragment 50)" msgstr "" #: gui/text/quotes.txt:128 -msgid "" -"\"Marry a good man, and bear good children.\" - Leonidas, to his wife before " -"he left for Thermopylae" +msgid "\"The man who runs may fight again.\" - Menander (\"Monosticha\")" msgstr "" #: gui/text/quotes.txt:129 msgid "" -"\"Come and get them!\" - Leonidas, to the Persian messenger who demanded " -"that he and his men lay down their arms" +"\"Whom the Gods love dies young.\" - Menander (\"The Double Deceiver\", " +"fragment 4)" msgstr "" #: gui/text/quotes.txt:130 -msgid "\"For a thinking man is where Wisdom is at home.\" - Zoroaster" +msgid "\"I call a fig a fig, a spade a spade.\" - Menander (fragment 545 K)" msgstr "" #: gui/text/quotes.txt:131 -msgid "\"I call a fig a fig, a spade a spade.\" - Menander" +msgid "" +"\"The greatest glory is won from the greatest dangers. When our fathers " +"faced the Persians their resources could not compare to ours. In fact, they " +"gave up even what they had. Then by wise counsels and daring deeds, not " +"fortune and material advantages, they drove out the invaders and made our " +"city what it is now.\" - Pericles (Thucydides, \"History of the " +"Peloponnesian War\", I. 144.3-4)" msgstr "" #: gui/text/quotes.txt:132 -msgid "\"The man who runs may fight again.\" - Menander" +msgid "" +"\"Instead of looking on discussion as a stumbling block in the way of " +"action, we think it an indispensable preliminary to any wise action at all." +"\" - Pericles in his Funeral Oration for Athenians that died in the first " +"year of the war (Thucydides, \"History of the Peloponnesian War\", II. 40.2)" msgstr "" #: gui/text/quotes.txt:133 msgid "" -"\"At times discretion should be thrown aside, and with the foolish we should " -"play the fool.\" - Menander" +"\"We alone do not think that a man ignorant of politics interferes with " +"nothing, we think he is good for nothing.\" - Pericles in his Funeral " +"Oration for Athenians that died in the first year of the war (Thucydides, " +"\"History of the Peloponnesian War\", II. 40.2)" msgstr "" #: gui/text/quotes.txt:134 msgid "" -"\"Freedom is the sure possession of those alone who have the courage to " -"defend it.\" - Pericles" +"\"Future ages will wonder at us, as the present age wonders at us now.\" - " +"Pericles in his Funeral Oration for Athenians that died in the first year of " +"the war (Thucydides, \"History of the Peloponnesian War\", II. 41.5)" msgstr "" #: gui/text/quotes.txt:135 msgid "" -"\"What you leave behind is not what is engraved in stone monuments, but what " -"is woven into the lives of others.\" - Pericles" +"\"When you realise the power of Athens, consider it was won by valiant men " +"who knew their duty, had a sense of dishonor in fight and, if their " +"enterprises failed, would rather give their lives than lack in civic virtue." +"\" - Pericles in his Funeral Oration for Athenians that died in the first " +"year of the war (Thucydides, \"History of the Peloponnesian War\", II. 43.2)" msgstr "" #: gui/text/quotes.txt:136 msgid "" -"\"We do not say that a man who takes no interest in politics is a man who " -"minds his own business; we say that he has no business here at all.\" - " -"Pericles" +"\"To heroes all earth is their tomb, and their virtues are remembered far " +"from home where an epitaph declares them, in an unwritten record of the mind " +"that will outlast any monument.\" - Pericles in his Funeral Oration for " +"Athenians that died in the first year of the war (Thucydides, \"History of " +"the Peloponnesian War\", II. 43.3)" msgstr "" #: gui/text/quotes.txt:137 msgid "" -"\"They gave her their lives, to Her and to all of us, and for their own " -"selves they won praises that never grow old, the most splendid of " -"sepulchers...\" - Pericles, Funeral Oration" +"\"Understand that happiness depends on freedom, and freedom depends on " +"courage.\" - Pericles in his Funeral Oration for Athenians that died in the " +"first year of the war (Thucydides, \"History of the Peloponnesian War\", II. " +"43.4)" msgstr "" #: gui/text/quotes.txt:138 msgid "" -"\"Make up your minds that happiness depends on being free, and freedom " -"depends on being courageous.\" - Pericles" +"\"The greatest glory for women is to be least talked about by men, whether " +"for good or ill.\" - Pericles in his Funeral Oration for Athenians that died " +"in the first year of the war (Thucydides, \"History of the Peloponnesian " +"War\", II. 45.2)" msgstr "" #: gui/text/quotes.txt:139 msgid "" -"\"Your empire is now like a tyranny: it may have been wrong to take it; it " -"is certainly dangerous to let it go.\" - Pericles" +"\"Wait for the wisest of all counsellors, time.\" - Pericles, a cautious " +"politician who avoided war (Plutarch, \"Parallel Lives\", \"Pericles\", sec. " +"18)" msgstr "" #: gui/text/quotes.txt:140 msgid "" -"\"If Athens shall appear great to you, consider then that her glories were " -"purchased by valiant men, and by men who learned their duty.\" - Pericles" +"\"Your empire is now like a tyranny: it may have been wrong to take it; it " +"is certainly dangerous to let it go.\" - Pericles, addressing the Athenian " +"assembly after a plague had weakened the city (Thucydides, \"History of the " +"Peloponnesian War\", II. 63.3)" msgstr "" #: gui/text/quotes.txt:141 msgid "" -"\"Just because you do not take an interest in politics doesn't mean politics " -"won't take an interest in you.\" - Pericles" +"\"War is sweet to those who have no experience of it, but the experienced " +"man fears its approach in his heart.\" - Pindar (fragment 110)" msgstr "" #: gui/text/quotes.txt:142 msgid "" -"\"War is sweet to those who have no experience of it, but the experienced " -"man trembles exceedingly at heart on its approach.\" - Pindar" +"\"Themistocles robbed his fellow-citizens of spear and shield, and degraded " +"the people of Athens to the rowing-pad and the oar.\" - Plato, no friend of " +"the Athenian navy (Plutarch, \"Parallel Lives\", \"Themistocles\", sec. 3)" msgstr "" #: gui/text/quotes.txt:143 msgid "" -"\"The hour of departure has arrived, and we go our ways — I to die, and you " -"to live. Which is better God only knows.\" - Plato" +"\"No guest is so welcome in a friend's house that he will not become a " +"nuisance after three days.\" - Plautus (\"The Swaggering Soldier\", Act III, " +"scene 1, 146)" msgstr "" #: gui/text/quotes.txt:144 -msgid "\"Life without examination is not worth living.\" - Plato" +msgid "" +"\"You cannot eat your cake and have it too, unless you think your money is " +"immortal.\" - Plautus: (\"Trinummus\", Act II, scene 4, 12)" msgstr "" #: gui/text/quotes.txt:145 msgid "" -"\"Let every man remind their descendants that they also are soldiers who " -"must not desert the ranks of their ancestors, or from cowardice fall behind." -"\" - Plato" +"\"He [Alexander] thought nothing invincible for the courageous, and nothing " +"secure for the cowardly.\" - Plutarch (\"Parallel Lives\", \"Alexander\", " +"sec. 58)" msgstr "" #: gui/text/quotes.txt:146 msgid "" -"\"False words are not only evil in themselves, but they infect the soul with " -"evil.\" - Plato" +"\"One \\[...] shot an arrow at him with such accuracy and force that it " +"pierced his breastplate and got stuck in his ribs. \\[...] Alexander " +"recoiled and sank to his knees. \\[...] At last Alexander killed the " +"barbarian. But he received many wounds, at last was struck on the neck with " +"a mace, and leaned against the city wall, his eyes still fixed upon his foes." +"\" - Plutarch about the Mallian Campaign (\"Parallel Lives\", \"Alexander\", " +"sec. 63)" msgstr "" #: gui/text/quotes.txt:147 msgid "" -"\"Democracy, which is a charming form of government, full of variety and " -"disorder, and dispensing a sort of equality to equals and unequals alike.\" " -"- Plato" +"\"When the pirates demanded a ransom of twenty talents for him, Caesar burst " +"out laughing. They did not know, he said, who it was that they had captured, " +"and he volunteered to pay fifty.\" - Plutarch, who mentions later that " +"Caesar got his money back and had his captors crucified (\"Parallel Lives\", " +"\"Caesar\", sec. 2)" msgstr "" #: gui/text/quotes.txt:148 -msgid "\"Death is not the worst that can happen to men.\" - Plato" +msgid "" +"\"They \\[the Romans] want the centurions not so much to be adventurous and " +"daredevils, as to be natural leaders, of a steady and reliable spirit. They " +"do not so much want men who will initiate attacks and open the battle, but " +"men who will hold their ground when beaten and hard-pressed, and will be " +"ready to die at their posts.\" - Polybius (\"Histories\", VI. 24)" msgstr "" #: gui/text/quotes.txt:149 -msgid "\"Only the dead have seen the end of war.\" - Unknown" +msgid "" +"\"The Roman battle line is hard to break, since it allows every man to fight " +"both individually and collectively; so that a formation can fight in any " +"direction, with the maniples nearest to the point of danger wheeling around " +"to face it.\" - Polybius (\"Histories\", XV. 15)" msgstr "" #: gui/text/quotes.txt:150 -msgid "\"Our need will be the real creator.\" - Plato" +msgid "" +"\"The Athenian people are always in the position of a ship without a " +"commander. Fear of the enemy or a storm make the crew be of one mind and " +"obey the helmsman, everything goes well; but if they recover \\[...] they " +"quarrel with each other \\[...], and the result has often been that, after " +"escaping the dangers of the widest seas and the most violent storms, they " +"wreck their ship in harbour and close to shore.\" - Polybius on the Athenian " +"constitution (\"Histories\", VI. 44)" msgstr "" #: gui/text/quotes.txt:151 -msgid "\"A man with courage has every blessing.\" - Plautus" +msgid "" +"\"Most of the Romans were trampled to death by the enormous weight of the " +"elephants; the rest were shot down in their ranks by the numerous cavalry: " +"and there were only a very few who attempted to save themselves by flight.\" " +"- Polybius on the Battle of Bagradas where a Roman army was annihilated " +"during the First Punic War (\"Histories\", I. 34)" msgstr "" #: gui/text/quotes.txt:152 msgid "" -"\"No guest is so welcome in a friend's house that he will not become a " -"nuisance after three days.\" - Plautus" +"\"Hannibal gave the signal for attack; and at the same time sent orders to " +"the troops lying in ambush on the hills to do the same, and thus delivered " +"an assault upon the enemy at every point at once.\" - Polybius on the " +"beginning of a Roman disaster at the Trasymene Lake (\"Histories\", III. 84)" msgstr "" #: gui/text/quotes.txt:153 -msgid "\"He whom the gods love dies young.\" - Plautus" +msgid "" +"\"In the phalanx, the men cannot turn around singly and defend themselves: " +"this tribune, therefore, charged them \\[from behind] and killed all he " +"could get at; until, unable to resist, they were forced to throw away their " +"shields and flee.\" - Polybius, describing the defeat of Philip V. of " +"Macedon by Flaminius in the Battle of Cynoscephalae (\"Histories\", XVIII. " +"26)" msgstr "" #: gui/text/quotes.txt:154 -msgid "\"Practice yourself what you preach.\" - Plautus" +msgid "" +"\"The Roman order on the other hand is flexible: for every Roman, once armed " +"and on the field, is equally well equipped for every place, time, or " +"appearance of the enemy. He is, moreover, quite ready and needs to make no " +"change, whether he is required to fight in the main body, or in a " +"detachment, or in a single maniple, or even by himself.\" - Polybius, " +"explaining how the Romans can defeat the Macedonian phalanx (\"Histories\", " +"XVIII. 32)" msgstr "" #: gui/text/quotes.txt:155 -msgid "\"Drink, live like the Greeks, eat, gorge.\" - Plautus" +msgid "" +"\"Scipio \\[Aeminialus], when he looked upon the city \\[Carthage] as it was " +"utterly perishing and in the last throes of its complete destruction, is " +"said to have shed tears and wept openly for his enemies. And realized that " +"all cities, nations, and authorities must, like men, meet their doom.\" - " +"Polybius, eyewitness to the destruction of Carthage (\"Histories\", XXXVIII. " +"22)" msgstr "" #: gui/text/quotes.txt:156 msgid "" -"\"Stop quoting laws, we carry weapons!\" - Gnaeus Pompeius Magnus (Pompey " -"the Great)" +"\"One more such victory and the cause is lost!\" - Pyrrhus of Epirus after " +"the Battle of Asculum, in which the Romans lost twice as many men but he " +"lost a greater share of his armed forces (Plutarch, \"Parallel Lives\", " +"\"Pyrrhus\", sec. 21)" msgstr "" #: gui/text/quotes.txt:157 msgid "" -"\"Man is the measure of all things: of things which are, that they are, and " -"of things which are not, that they are not.\" - Protagoras" +"\"None can be free who is a slave to, and ruled by, his passions.\" - " +"Pythagoras (Stobaeus, \"Florilegium\", 18)" msgstr "" #: gui/text/quotes.txt:158 msgid "" -"\"We ought so to behave to one another as to avoid making enemies of our " -"friends, and at the same time to make friends of our enemies.\" - Pythagoras" +"\"Do not say few things in many words, but many things in few words.\" - " +"Pythagoras (Stobaeus, \"Florilegium\", 24)" msgstr "" #: gui/text/quotes.txt:159 msgid "" -"\"In anger we should refrain both from speech and action.\" - Pythagoras" +"\"Let your speech be better than silence, or be silent.\" - Pythagoras " +"(Stobaeus, \"Florilegium\", 24)" msgstr "" #: gui/text/quotes.txt:160 -msgid "\"Power is the near neighbor of necessity.\" - Pythagoras" +msgid "" +"\"Unity strengthens even small states, while discord undermines the " +"mightiest empires.\" - Sallust (\"The Jugurthine War\", 10.6)" msgstr "" #: gui/text/quotes.txt:161 -msgid "\"Numbers rule the Universe.\" - Pythagoras" +msgid "" +"\"Ungrateful fatherland, you will not even have my bones!\" - Scipio " +"Africanus in his epitaph, after he who defeated Hannibal was repeatedly " +"accused of crimes by the Roman Senate (Valerius Maximus, \"Nine books on " +"memorable deeds and sayings\", 5.3.2)" msgstr "" #: gui/text/quotes.txt:162 msgid "" -"\"Rest satisfied with doing well, and leave others to talk of you as they " -"please.\" - Pythagoras" +"\"Prepare for war, since you have been unable to endure a peace.\" - Scipio " +"Africanus, replying to Hannibal's offer of peace terms before the Battle of " +"Zama (\"Ab Urbe Condita\", XXX. 31)" msgstr "" #: gui/text/quotes.txt:163 -msgid "\"Anger begins in folly, and ends in repentance.\" - Pythagoras" +msgid "" +"\"But tactical science is only one part of generalship. A general must be " +"capable of equipping his forces and providing for his men. He must also be " +"inventive, hardworking, and watchful, bullheaded and brilliant, friendly and " +"fierce, straightforward and subtle.\" - Socrates (Xenophon, \"Memorabilia\", " +"3.1.6)" msgstr "" #: gui/text/quotes.txt:164 msgid "" -"\"There is no word or action but has its echo in Eternity.\" - Pythagoras" +"\"It is necessary to know the strength of the city and of the enemy, so " +"that, if the city is stronger, one may recommend her to go to war, but if " +"weaker than the enemy, may persuade her to beware.\" - Socrates (Xenophon, " +"\"Memorabilia\", 3.6.9)" msgstr "" #: gui/text/quotes.txt:165 msgid "" -"\"There is geometry in the humming of the strings, there is music in the " -"spacing of the spheres.\" - Pythagoras" +"\"The unexamined life is not worth living.\" - Socrates, in his defense when " +"trialled for corrupting the youth and not worshipping the proper gods (he " +"later drank hemlock after the death sentence) - Plato (\"Apology\", 38a)" msgstr "" #: gui/text/quotes.txt:166 msgid "" -"\"Practice justice in word and deed, and do not get in the habit of acting " -"thoughtlessly about anything.\" - Pythagoras" +"\"The hour of departure has arrived, and we go our ways - I to die, and you " +"to live. Which is better God only knows.\" - Socrates, in his defense when " +"trialled for corrupting the youth and not worshipping the proper gods (he " +"later drank hemlock after the death sentence) - Plato (\"Apology\", 42a)" msgstr "" #: gui/text/quotes.txt:167 -msgid "\"Do not even think of doing what ought not to be done.\" - Pythagoras" +msgid "" +"\"Walls and ships are nothing without men living together inside them.\" - " +"Sophocles (\"Oedipus Rex\")" msgstr "" #: gui/text/quotes.txt:168 msgid "" -"\"When the wise man opens his mouth, the beauties of his soul present " -"themselves to the view, like the statues in a temple.\" - Pythagoras" +"\"We accepted an empire that was offered to us and refused to give it up " +"under the pressure of three of the strongest motives: fear, honor and " +"interest. It was not we who set the example, for it has always been the law " +"that the weak should be subject to the strong.\" - Speech of an Athenian " +"embassy in Sparta (Thucydides, \"History of the Peloponnesian War\", I. 76.2)" msgstr "" #: gui/text/quotes.txt:169 msgid "" -"\"Remind yourself that all men assert that wisdom is the greatest good, but " -"that there are few who strenuously seek out that greatest good.\" - " -"Pythagoras" +"\"He could boast that he found a city of brick and left it a city of marble." +"\" - Suetonius, commenting on the many building projects of Augustus in Rome " +"(\"Divus Augustus\", sec 38)" msgstr "" #: gui/text/quotes.txt:170 -msgid "\"Without Justice, no realm may prosper.\" - Pythagoras" +msgid "\"Moderation in all things.\" - Terence (\"The Girl from Andros\", 61)" msgstr "" #: gui/text/quotes.txt:171 msgid "" -"\"For harmony makes small states great, while discord undermines the " -"mightiest empires.\" - Sallust" +"\"Fortune favors the bold.\" - Terence in a play about a great Athenian " +"admiral (\"Phormio\", 203)" msgstr "" #: gui/text/quotes.txt:172 msgid "" -"\"I am mindful of human weakness, and I reflect upon the might of Fortune " -"and know that everything that we do is exposed to a thousand chances.\" - " -"Scipio Africanus" +"\"I do not know how to tune the lyre or play the harp, but I do know how to " +"raise a city that was small and unimportant to glory and greatness.\" - " +"Themistocles, defending his lack of cultural sophistication (Plutarch, " +"\"Parallel Lives\", \"Themistocles\", sec. 2)" msgstr "" #: gui/text/quotes.txt:173 msgid "" -"\"Prepare for war, since you have been unable to endure a peace.\" - Scipio " -"Africanus" +"\"Strike, if you will, but listen.\" - Themistocles, in a heated discussion " +"with the Spartan fleet commander who threatened to beat him with his staff, " +"before the Battle of Salamis (Plutarch, \"Parallel Lives\", " +"\"Themistocles\", sec. 11)" msgstr "" #: gui/text/quotes.txt:174 msgid "" -"\"Go, tell the Spartans, stranger passing by that here, obedient to their " -"laws, we lie.\" - Simonides of Ceos, epitaph on the Cenotaph of Thermopylae" +"\"The Athenians command the rest of Greece, I command the Athenians; your " +"mother commands me, and you command your mother.\" - Themistocles, jokingly " +"to his infant son (Plutarch, \"Parallel Lives\", \"Themistocles\", sec. 18)" msgstr "" #: gui/text/quotes.txt:175 -msgid "\"Not even the gods fight against necessity.\" - Simonides of Ceos" +msgid "" +"\"So little pains does the mob take in finding out the truth, accepting " +"readily the first story at hand.\" - Thucydides (\"History of the " +"Peloponnesian War\", I. 21.3)" msgstr "" #: gui/text/quotes.txt:176 msgid "" -"\"We did not flinch but gave our lives to save Greece when her fate hung on " -"a razor's edge.\" - Simonides of Ceos" +"\"The growth of the power of Athens, and the alarm which this caused in " +"Sparta, made war inevitable.\" - Thucydides (\"History of the Peloponnesian " +"War\", I. 23.6)" msgstr "" #: gui/text/quotes.txt:177 msgid "" -"\"The bravest are surely those who have the clearest vision of what is " -"before them, glory and danger alike, and yet notwithstanding, go out to meet " -"it.\" - Thucydides" +"\"War is a matter not so much of arms as of money.\" - Thucydides (\"History " +"of the Peloponnesian War\", I. 83.2)" +msgstr "" + +#: gui/text/quotes.txt:178 +msgid "" +"\"It is a general rule of human nature that people despise those who treat " +"them well, and look up to those who make no concessions.\" - Thucydides " +"(\"History of the Peloponnesian War\", III. 39.5)" +msgstr "" + +#: gui/text/quotes.txt:179 +msgid "" +"\"This was the greatest action that happened in all this war, and all others " +"that we have heard of amongst the Greeks, being to the victors most glorious " +"and most calamitous to the vanquished. For they were utterly and at all " +"points defeated, and their sufferings were many. Army and fleet and all they " +"ever had perished, nothing was saved and few of so many ever returned home. " +"Thus ended the Sicilian expedition.\" - Thucydides (\"History of the " +"Peloponnesian War\", VII. 87.6-7)" +msgstr "" + +#: gui/text/quotes.txt:180 +msgid "" +"\"As the world goes, justice is only a matter between equals, while the " +"strong do what they can and the weak suffer what they must.\" - Thucydides, " +"describing Athenians addressing the defeated Melians who are unwilling to " +"surrender (\"History of the Peloponnesian War\", V. 89.1)" +msgstr "" + +#: gui/text/quotes.txt:181 +msgid "" +"\"When the Lacedaemonians were no longer able to run after them, the " +"skirmishers \\[...] all charged them at once, casting stones, arrows, and " +"darts to the closest man at hand.\" - Thucydides, describing the Spartan " +"disaster at the Battle of Sphacteria (\"History of the Peloponnesian War\", " +"IV. 34.2)" +msgstr "" + +#: gui/text/quotes.txt:182 +msgid "" +"\"The soldiers fight and die to support others in wealth and luxury and they " +"are called masters of the world without owning a single piece of farmland of " +"their own.\" - Tiberius Gracchus, advocating for land reform to the benefit " +"of homeless and unemployed veterans whose lands had often been bought up why " +"they were on campaign (Plutarch, \"Parallel Lives\", \"Tiberius Gracchus\", " +"sec. 9)" +msgstr "" + +#: gui/text/quotes.txt:183 +msgid "" +"\"The wild beasts of Italy have their caves to retire to, but the brave " +"veterans who spilled their blood in her cause have nothing left but air and " +"light. They wander around homeless with their wives and children.\" - " +"Tiberius Gracchus, advocating for land reform to the benefit of homeless and " +"unemployed veterans whose lands had often been bought up why they were on " +"campaign (Plutarch, \"Parallel Lives\", \"Tiberius Gracchus\", sec. 9)" +msgstr "" + +#: gui/text/quotes.txt:184 +msgid "" +"\"Do not trust the horse, Trojans! I fear the Greeks even when they bring " +"gifts.\" - Virgil (\"Aeneid\", II. 48-49)" +msgstr "" + +#: gui/text/quotes.txt:185 +msgid "\"Prepared for either alternative.\" - Virgil (\"Aeneid\", II. 61)" +msgstr "" + +#: gui/text/quotes.txt:186 +msgid "" +"\"Homer and Hesiod ascribed to their Gods all things that are a disgrace " +"among mortals: stealing, adultery, deceiving one another.\" - Xenophanes " +"(fragment 11)" +msgstr "" + +#: gui/text/quotes.txt:187 +msgid "" +"\"If oxen and horses and lions had hands, and could paint, and produce works " +"of art as men do, horses would paint the forms of the gods like horses, and " +"oxen like oxen, and make their God's bodies each in their own image.\" - " +"Xenophanes (fragment 15)" +msgstr "" + +#: gui/text/quotes.txt:188 +msgid "" +"\"The Ethiopians make their gods black and snub-nosed, the Thracians say " +"theirs have blue eyes and red hair.\" - Xenophanes (fragment 16)" +msgstr "" + +#: gui/text/quotes.txt:189 +msgid "" +"\"These are the right questions to ask, in winter around the fire \\[...]: " +"Who are you, friend? What is your land? And how old were you when the Medes " +"\\[Persians] came?\" - Xenophanes, likely referring to a punitive expedition " +"against Greek cities in Ionia (fragment 17)" +msgstr "" + +#: gui/text/quotes.txt:190 +msgid "" +"\"A prudent commander will never take risks unnecessarily, except when it is " +"clear beforehand that he will have the advantage.\" - Xenophon (\"The " +"Cavalry General\", 4.13)" +msgstr "" + +#: gui/text/quotes.txt:191 +msgid "" +"\"Attack the enemy where he is weakest, even if that is a long way off, " +"since hard work is less dangerous than a struggle against superior forces.\" " +"- Xenophon (\"The Cavalry General\", sec. 4.14)" +msgstr "" + +#: gui/text/quotes.txt:192 +msgid "" +"\"He should be inventive, ready to exploit all circumstances, to make a " +"small force appear large and a large one small, to appear absent when close " +"at hand, and within striking distance when a long way off.\" - Xenophon " +"(\"The Cavalry General\", sec. 5)" +msgstr "" + +#: gui/text/quotes.txt:193 +msgid "" +"\"People are glad to obey the man whom they believe to be wiser than " +"themselves in pursuing their interests.\" - Xenophon (\"The Education of " +"Cyrus\", 1.6.22)" +msgstr "" + +#: gui/text/quotes.txt:194 +msgid "" +"\"In his campaigns during summer the general must show that he can endure " +"the sun better than the soldiers, in winter he must show he can endure cold " +"better; and throughout all difficulties that he can endure hardships better. " +"This will help to make him loved by his men.\" - Xenophon (\"The Education " +"of Cyrus\", 1.6.25)" +msgstr "" + +#: gui/text/quotes.txt:195 +msgid "" +"\"Battles are decided more by the morale of men than their physical strength." +"\" - Xenophon (\"The Education of Cyrus\", 3.3.20)" +msgstr "" + +#: gui/text/quotes.txt:196 +msgid "" +"\"Let's not give them enough time to arrange a defense, or to even recognise " +"that we are human beings! We've got to appear to them like an uncontrollable " +"nightmare of shields, swords, battle-axes and spears!\" - Xenophon (\"The " +"Education of Cyrus\", 4.2.22)" +msgstr "" + +#: gui/text/quotes.txt:197 +msgid "" +"\"I suppose you understand, men, that pursuing, dealing blows and death, " +"plunder, fame, freedom, power - all these are prizes for the winners; the " +"cowardly, of course, suffer the reverse.\" - Xenophon (\"The Education of " +"Cyrus\", 7.1.13)" +msgstr "" + +#: gui/text/quotes.txt:198 +msgid "" +"\"The man who wants that must be scheming and cunning, wily and deceitful, a " +"thief and a robber, overreaching the enemy at every point.\" - Xenophon on " +"how best to gain advantage over the enemy (\"The Education of Cyrus\", 1.6." +"26)" +msgstr "" + +#: gui/text/quotes.txt:199 +msgid "" +"\"My men have turned into women, and my women into men!\" - Xerxes, watching " +"Artemisia ram a ship while most of his fleet suffered the reverse, not " +"knowing that the sunk vessel was his own (Herodotus, \"The Histories\", " +"VIII. 88)" +msgstr "" + +#: gui/text/quotes.txt:200 +msgid "" +"\"For a thinking man is where Wisdom is at home.\" - Zoroaster, founder of " +"the Zoroastrian religion (\"Ahunuvaiti Gatha\", yasna 30.9)" msgstr "" Index: binaries/data/mods/public/l10n/public-gui-lobby.pot =================================================================== --- binaries/data/mods/public/l10n/public-gui-lobby.pot +++ binaries/data/mods/public/l10n/public-gui-lobby.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-05-01 10:19+0200\n" -"PO-Revision-Date: 2017-05-01 10:19+0200\n" +"POT-Creation-Date: 2017-05-05 09:14+0200\n" +"PO-Revision-Date: 2017-05-05 09:14+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -33,251 +33,291 @@ msgid "Unknown" msgstr "" -#: gui/lobby/lobby.js:141 +#: gui/lobby/lobby.js:57 +msgid "Moderator" +msgstr "" + +#: gui/lobby/lobby.js:58 +msgid "Player" +msgstr "" + +#: gui/lobby/lobby.js:59 +msgid "Muted Player" +msgstr "" + +#: gui/lobby/lobby.js:150 msgid "Disconnected." msgstr "" -#: gui/lobby/lobby.js:157 +#: gui/lobby/lobby.js:166 #, javascript-format msgid "%(nick)s has joined." msgstr "" -#: gui/lobby/lobby.js:165 +#: gui/lobby/lobby.js:174 #, javascript-format msgid "%(nick)s has left." msgstr "" -#: gui/lobby/lobby.js:178 +#: gui/lobby/lobby.js:193 +msgid "You have been muted." +msgstr "" + +#: gui/lobby/lobby.js:194 +#, javascript-format +msgid "%(nick)s has been muted." +msgstr "" + +#: gui/lobby/lobby.js:197 +msgid "You are now a moderator." +msgstr "" + +#: gui/lobby/lobby.js:198 +#, javascript-format +msgid "%(nick)s is now a moderator." +msgstr "" + +#: gui/lobby/lobby.js:201 +msgid "You have been unmuted." +msgstr "" + +#: gui/lobby/lobby.js:202 +#, javascript-format +msgid "%(nick)s has been unmuted." +msgstr "" + +#: gui/lobby/lobby.js:204 +msgid "You are not a moderator anymore." +msgstr "" + +#: gui/lobby/lobby.js:205 +#, javascript-format +msgid "%(nick)s is not a moderator anymore." +msgstr "" + +#: gui/lobby/lobby.js:221 #, javascript-format msgid "%(oldnick)s is now known as %(newnick)s." msgstr "" -#: gui/lobby/lobby.js:223 +#: gui/lobby/lobby.js:266 msgid "Set your state to 'Away'." msgstr "" -#: gui/lobby/lobby.js:230 +#: gui/lobby/lobby.js:273 msgid "Set your state to 'Online'." msgstr "" -#: gui/lobby/lobby.js:237 +#: gui/lobby/lobby.js:280 msgid "Kick a specified user from the lobby. Usage: /kick nick reason" msgstr "" -#: gui/lobby/lobby.js:245 +#: gui/lobby/lobby.js:288 msgid "Ban a specified user from the lobby. Usage: /ban nick reason" msgstr "" -#: gui/lobby/lobby.js:253 +#: gui/lobby/lobby.js:296 msgid "Show this help." msgstr "" -#: gui/lobby/lobby.js:256 +#: gui/lobby/lobby.js:299 msgid "Chat commands:" msgstr "" #. Translation: Chat command help format -#: gui/lobby/lobby.js:260 +#: gui/lobby/lobby.js:303 #, javascript-format msgid "%(command)s - %(description)s" msgstr "" -#: gui/lobby/lobby.js:273 +#: gui/lobby/lobby.js:316 msgid "Send a chat message about yourself. Example: /me goes swimming." msgstr "" -#: gui/lobby/lobby.js:277 +#: gui/lobby/lobby.js:320 msgid "" "Send text as a chat message (even if it starts with slash). Example: /say /" "help is a great command." msgstr "" -#: gui/lobby/lobby.js:281 +#: gui/lobby/lobby.js:324 msgid "Clear all chat scrollback." msgstr "" -#: gui/lobby/lobby.js:288 +#: gui/lobby/lobby.js:331 msgid "Return to the main menu." msgstr "" -#: gui/lobby/lobby.js:333 +#: gui/lobby/lobby.js:376 msgctxt "map size" msgid "Any" msgstr "" -#: gui/lobby/lobby.js:338 +#: gui/lobby/lobby.js:381 msgctxt "player number" msgid "Any" msgstr "" -#: gui/lobby/lobby.js:342 +#: gui/lobby/lobby.js:385 msgctxt "map" msgid "Any" msgstr "" -#: gui/lobby/lobby.js:400 +#: gui/lobby/lobby.js:443 msgid "You have been banned from the lobby!" msgstr "" -#: gui/lobby/lobby.js:401 +#: gui/lobby/lobby.js:444 msgid "You have been kicked from the lobby!" msgstr "" -#: gui/lobby/lobby.js:403 +#: gui/lobby/lobby.js:446 #, javascript-format msgid "%(nick)s has been banned from the lobby." msgstr "" -#: gui/lobby/lobby.js:404 +#: gui/lobby/lobby.js:447 #, javascript-format msgid "%(nick)s has been kicked from the lobby." msgstr "" -#: gui/lobby/lobby.js:407 +#: gui/lobby/lobby.js:450 #, javascript-format msgctxt "lobby kick" msgid "Reason: %(reason)s" msgstr "" -#: gui/lobby/lobby.js:432 +#: gui/lobby/lobby.js:475 msgid "BANNED" msgstr "" -#: gui/lobby/lobby.js:432 +#: gui/lobby/lobby.js:475 msgid "KICKED" msgstr "" -#: gui/lobby/lobby.js:645 -msgid "Moderator" -msgstr "" - -#: gui/lobby/lobby.js:645 -msgid "Player" -msgstr "" - -#: gui/lobby/lobby.js:646 gui/lobby/lobby.js:647 gui/lobby/lobby.js:648 -#: gui/lobby/lobby.js:649 gui/lobby/lobby.js:650 gui/lobby/lobby.js:651 +#: gui/lobby/lobby.js:703 gui/lobby/lobby.js:704 gui/lobby/lobby.js:705 +#: gui/lobby/lobby.js:706 gui/lobby/lobby.js:707 gui/lobby/lobby.js:708 msgid "N/A" msgstr "" -#: gui/lobby/lobby.js:672 +#: gui/lobby/lobby.js:729 #, javascript-format msgid "Player \"%(nick)s\" not found." msgstr "" #. Translation: %(time)s is the hour and minute here. -#: gui/lobby/lobby.js:871 +#: gui/lobby/lobby.js:928 #, javascript-format msgid "Game started at %(time)s" msgstr "" -#: gui/lobby/lobby.js:872 gui/lobby/lobby.js:1220 +#: gui/lobby/lobby.js:929 gui/lobby/lobby.js:1277 msgid "HH:mm" msgstr "" -#: gui/lobby/lobby.js:876 +#: gui/lobby/lobby.js:933 #, javascript-format msgid "Players: %(current)s/%(total)s" msgstr "" -#: gui/lobby/lobby.js:918 +#: gui/lobby/lobby.js:975 msgid "The game has already started. Do you want to join as observer?" msgstr "" -#: gui/lobby/lobby.js:919 +#: gui/lobby/lobby.js:976 msgid "Confirmation" msgstr "" -#: gui/lobby/lobby.js:920 +#: gui/lobby/lobby.js:977 msgid "No" msgstr "" -#: gui/lobby/lobby.js:920 +#: gui/lobby/lobby.js:977 msgid "Yes" msgstr "" -#: gui/lobby/lobby.js:939 +#: gui/lobby/lobby.js:996 #, javascript-format msgid "This game's address '%(ip)s' does not appear to be valid." msgstr "" -#: gui/lobby/lobby.js:1036 +#: gui/lobby/lobby.js:1093 #, javascript-format msgid "The command '%(cmd)s' is not supported." msgstr "" -#: gui/lobby/lobby.js:1048 +#: gui/lobby/lobby.js:1105 #, javascript-format msgid "The command '%(cmd)s' is restricted to moderators." msgstr "" #. Translation: IRC message prefix when the sender uses the /me command. -#: gui/lobby/lobby.js:1127 +#: gui/lobby/lobby.js:1184 #, javascript-format msgid "* %(sender)s" msgstr "" #. Translation: IRC message issued using the ‘/me’ command. -#: gui/lobby/lobby.js:1132 +#: gui/lobby/lobby.js:1189 #, javascript-format msgid "%(sender)s %(action)s" msgstr "" #. Translation: IRC message prefix. -#: gui/lobby/lobby.js:1141 gui/lobby/lobby.js:1162 gui/lobby/lobby.js:1188 +#: gui/lobby/lobby.js:1198 gui/lobby/lobby.js:1219 gui/lobby/lobby.js:1245 #, javascript-format msgid "<%(sender)s>" msgstr "" #. Translation: IRC message. -#: gui/lobby/lobby.js:1146 gui/lobby/lobby.js:1167 gui/lobby/lobby.js:1193 +#: gui/lobby/lobby.js:1203 gui/lobby/lobby.js:1224 gui/lobby/lobby.js:1250 #, javascript-format msgid "%(sender)s %(message)s" msgstr "" #. Translation: IRC system message. -#: gui/lobby/lobby.js:1156 +#: gui/lobby/lobby.js:1213 #, javascript-format msgid "== %(message)s" msgstr "" -#: gui/lobby/lobby.js:1182 +#: gui/lobby/lobby.js:1239 #, javascript-format msgctxt "lobby private message" msgid "(%(private)s) <%(sender)s>" msgstr "" -#: gui/lobby/lobby.js:1184 +#: gui/lobby/lobby.js:1241 msgid "Private" msgstr "" #. Translation: Time prefix as shown in the multiplayer lobby (when you enable it in the options page). -#: gui/lobby/lobby.js:1223 +#: gui/lobby/lobby.js:1280 #, javascript-format msgid "\\[%(time)s]" msgstr "" #. Translation: IRC message format when there is a time prefix. -#: gui/lobby/lobby.js:1228 +#: gui/lobby/lobby.js:1285 #, javascript-format msgid "%(time)s %(message)s" msgstr "" -#: gui/lobby/lobby.js:1289 +#: gui/lobby/lobby.js:1346 msgid "Please do not spam. You have been blocked for thirty seconds." msgstr "" -#: gui/lobby/lobby.js:1350 +#: gui/lobby/lobby.js:1407 #, javascript-format msgid "%(nick)s (%(rating)s)" msgstr "" -#: gui/lobby/lobby.js:1365 +#: gui/lobby/lobby.js:1422 msgctxt "Used for an undefined winning rate" msgid "-" msgstr "" -#: gui/lobby/lobby.js:1367 +#: gui/lobby/lobby.js:1424 #, javascript-format msgid "%(percentage)s%%" msgstr "" @@ -352,27 +392,27 @@ msgid "Multiplayer Lobby" msgstr "" -#: gui/lobby/lobby.xml:(caption):65 gui/lobby/lobby.xml:(caption):337 +#: gui/lobby/lobby.xml:(caption):65 gui/lobby/lobby.xml:(caption):334 msgid "Current Rank:" msgstr "" -#: gui/lobby/lobby.xml:(caption):69 gui/lobby/lobby.xml:(caption):341 +#: gui/lobby/lobby.xml:(caption):69 gui/lobby/lobby.xml:(caption):338 msgid "Highest Rating:" msgstr "" -#: gui/lobby/lobby.xml:(caption):73 gui/lobby/lobby.xml:(caption):345 +#: gui/lobby/lobby.xml:(caption):73 gui/lobby/lobby.xml:(caption):342 msgid "Total Games:" msgstr "" -#: gui/lobby/lobby.xml:(caption):77 gui/lobby/lobby.xml:(caption):349 +#: gui/lobby/lobby.xml:(caption):77 gui/lobby/lobby.xml:(caption):346 msgid "Wins:" msgstr "" -#: gui/lobby/lobby.xml:(caption):81 gui/lobby/lobby.xml:(caption):353 +#: gui/lobby/lobby.xml:(caption):81 gui/lobby/lobby.xml:(caption):350 msgid "Losses:" msgstr "" -#: gui/lobby/lobby.xml:(caption):85 gui/lobby/lobby.xml:(caption):357 +#: gui/lobby/lobby.xml:(caption):85 gui/lobby/lobby.xml:(caption):354 msgid "Win Rate:" msgstr "" @@ -381,56 +421,56 @@ msgid "Toggle Buddy" msgstr "" -#: gui/lobby/lobby.xml:(caption):100 gui/lobby/lobby.xml:(caption):285 +#: gui/lobby/lobby.xml:(caption):100 gui/lobby/lobby.xml:(caption):282 msgid "Leaderboard" msgstr "" -#: gui/lobby/lobby.xml:(caption):109 gui/lobby/lobby.xml:(caption):320 +#: gui/lobby/lobby.xml:(caption):106 gui/lobby/lobby.xml:(caption):317 msgid "User Profile Lookup" msgstr "" -#: gui/lobby/lobby.xml:(caption):138 +#: gui/lobby/lobby.xml:(caption):134 msgid "Map Type:" msgstr "" -#: gui/lobby/lobby.xml:(caption):150 +#: gui/lobby/lobby.xml:(caption):146 msgid "Map Size:" msgstr "" #. Join the game currently selected in the list. -#: gui/lobby/lobby.xml:(caption):175 +#: gui/lobby/lobby.xml:(caption):171 msgid "Join Game" msgstr "" -#: gui/lobby/lobby.xml:(caption):181 +#: gui/lobby/lobby.xml:(caption):177 msgid "Host Game" msgstr "" -#: gui/lobby/lobby.xml:(caption):188 +#: gui/lobby/lobby.xml:(caption):184 msgid "Main Menu" msgstr "" -#: gui/lobby/lobby.xml:(caption):254 +#: gui/lobby/lobby.xml:(caption):250 msgid "Show full games" msgstr "" -#: gui/lobby/lobby.xml:(caption):305 gui/lobby/lobby.xml:(caption):367 +#: gui/lobby/lobby.xml:(caption):302 gui/lobby/lobby.xml:(caption):364 msgid "Back" msgstr "" -#: gui/lobby/lobby.xml:(caption):313 +#: gui/lobby/lobby.xml:(caption):308 msgid "Update" msgstr "" -#: gui/lobby/lobby.xml:(caption):323 +#: gui/lobby/lobby.xml:(caption):320 msgid "Enter username:" msgstr "" -#: gui/lobby/lobby.xml:(caption):329 +#: gui/lobby/lobby.xml:(caption):326 msgid "View Profile" msgstr "" -#: gui/lobby/lobby.xml:(caption):362 +#: gui/lobby/lobby.xml:(caption):359 msgid "Please enter a player name." msgstr "" @@ -438,34 +478,34 @@ msgid "Status" msgstr "" -#: gui/lobby/lobby.xml:(heading):44 gui/lobby/lobby.xml:(heading):212 -#: gui/lobby/lobby.xml:(heading):295 +#: gui/lobby/lobby.xml:(heading):44 gui/lobby/lobby.xml:(heading):208 +#: gui/lobby/lobby.xml:(heading):292 msgid "Name" msgstr "" -#: gui/lobby/lobby.xml:(heading):47 gui/lobby/lobby.xml:(heading):298 +#: gui/lobby/lobby.xml:(heading):47 gui/lobby/lobby.xml:(heading):295 msgid "Rating" msgstr "" -#: gui/lobby/lobby.xml:(heading):215 +#: gui/lobby/lobby.xml:(heading):211 msgid "Map Name" msgstr "" -#: gui/lobby/lobby.xml:(heading):218 +#: gui/lobby/lobby.xml:(heading):214 msgctxt "map" msgid "Size" msgstr "" -#: gui/lobby/lobby.xml:(heading):221 +#: gui/lobby/lobby.xml:(heading):217 msgctxt "map" msgid "Type" msgstr "" -#: gui/lobby/lobby.xml:(heading):224 +#: gui/lobby/lobby.xml:(heading):220 msgid "Players" msgstr "" -#: gui/lobby/lobby.xml:(heading):292 +#: gui/lobby/lobby.xml:(heading):289 msgid "Rank" msgstr "" Index: binaries/data/mods/public/l10n/public-gui-manual.pot =================================================================== --- binaries/data/mods/public/l10n/public-gui-manual.pot +++ binaries/data/mods/public/l10n/public-gui-manual.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-03-06 09:29+0100\n" -"PO-Revision-Date: 2017-03-06 09:29+0100\n" +"POT-Creation-Date: 2017-05-08 09:02+0200\n" +"PO-Revision-Date: 2017-05-08 09:02+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -309,282 +309,295 @@ msgstr "" #: gui/manual/intro.txt:71 -msgid ". (Period): Select idle worker (including citizen soldiers)" +msgid "Alt + /: Select all idle fighters" msgstr "" #: gui/manual/intro.txt:72 -msgid "Shift + .: add idle worker to selection (including citizen soldiers)" +msgid ". (Period): Select idle worker (including citizen soldiers)" msgstr "" #: gui/manual/intro.txt:73 -msgid "H: Stop (halt) the currently selected units." +msgid "Shift + .: add idle worker to selection (including citizen soldiers)" msgstr "" #: gui/manual/intro.txt:74 -msgid "Y: The unit will go back to work" +msgid "Alt + .: Select all idle workers (including citizen soldiers)" msgstr "" #: gui/manual/intro.txt:75 -msgid "U: Unload the garrisoned units of the selected buildings" +msgid "H: Stop (halt) the currently selected units." msgstr "" #: gui/manual/intro.txt:76 +msgid "Y: The unit will go back to work" +msgstr "" + +#: gui/manual/intro.txt:77 +msgid "U: Unload the garrisoned units of the selected buildings" +msgstr "" + +#: gui/manual/intro.txt:78 msgid "" "Ctrl + 1 (and so on up to Ctrl + 0): Create control group 1 (to 0) from the " "selected units/buildings" msgstr "" -#: gui/manual/intro.txt:77 +#: gui/manual/intro.txt:79 msgid "" "1 (and so on up to 0): Select the units/buildings in control group 1 (to 0)" msgstr "" -#: gui/manual/intro.txt:78 +#: gui/manual/intro.txt:80 msgid "" "Shift + 1 (to 0): Add control group 1 (to 0) to the selected units/buildings" msgstr "" -#: gui/manual/intro.txt:79 +#: gui/manual/intro.txt:81 msgid "" "Ctrl + F5 (and so on up to F8): Mark the current camera position, for " "jumping back to later." msgstr "" -#: gui/manual/intro.txt:80 +#: gui/manual/intro.txt:82 msgid "" "F5, F6, F7, and F8: Move the camera to a marked position. Jump back to the " "last location if the camera is already over the marked position." msgstr "" -#: gui/manual/intro.txt:81 +#: gui/manual/intro.txt:83 msgid "" "Z, X, C, V, B, N, M: With training buildings selected. Add the 1st, 2nd, ... " "unit shown to the training queue for all the selected buildings." msgstr "" -#: gui/manual/intro.txt:82 +#: gui/manual/intro.txt:84 msgid "" "PageUp with units selected: Highlights the units/buildings guarded by the " "selection." msgstr "" -#: gui/manual/intro.txt:83 +#: gui/manual/intro.txt:85 msgid "" "PageDown with units/buildings selected: Highlights the units guarding the " "selection." msgstr "" -#: gui/manual/intro.txt:84 +#: gui/manual/intro.txt:86 msgid "Tab: See all status bars (which would also show the building progress)" msgstr "" -#: gui/manual/intro.txt:86 +#: gui/manual/intro.txt:88 msgid "[font=\"sans-bold-14\"]Modify mouse action" msgstr "" -#: gui/manual/intro.txt:87 +#: gui/manual/intro.txt:89 msgid "[font=\"sans-14\"]Ctrl + Right Click on building: Garrison" msgstr "" -#: gui/manual/intro.txt:88 +#: gui/manual/intro.txt:90 msgid "J + Right Click on building: Repair" msgstr "" -#: gui/manual/intro.txt:89 +#: gui/manual/intro.txt:91 msgid "P + Right Click: Patrol" msgstr "" -#: gui/manual/intro.txt:90 +#: gui/manual/intro.txt:92 msgid "Shift + Right Click: Queue the move/build/gather/etc order" msgstr "" -#: gui/manual/intro.txt:91 +#: gui/manual/intro.txt:93 msgid "Shift + Left click when training unit/s: Add units in batches of five" msgstr "" -#: gui/manual/intro.txt:92 +#: gui/manual/intro.txt:94 msgid "Shift + Left Click or Left Drag over unit on map: Add unit to selection" msgstr "" -#: gui/manual/intro.txt:93 +#: gui/manual/intro.txt:95 msgid "" "Ctrl + Left Click or Left Drag over unit on map: Remove unit from selection" msgstr "" -#: gui/manual/intro.txt:94 +#: gui/manual/intro.txt:96 msgid "Alt + Left Drag over units on map: Only select military units" msgstr "" -#: gui/manual/intro.txt:95 +#: gui/manual/intro.txt:97 msgid "I + Left Drag over units on map: Only select idle units" msgstr "" -#: gui/manual/intro.txt:96 +#: gui/manual/intro.txt:98 msgid "" "Ctrl + Left Click on unit/group icon with multiple units selected: Deselect" msgstr "" -#: gui/manual/intro.txt:97 +#: gui/manual/intro.txt:99 msgid "" "Right Click with a building/buildings selected: sets a rally point for units " "created/ungarrisoned from that building." msgstr "" -#: gui/manual/intro.txt:98 +#: gui/manual/intro.txt:100 msgid "Ctrl + Right Click with units selected:" msgstr "" -#: gui/manual/intro.txt:99 +#: gui/manual/intro.txt:101 msgid " - If the cursor is over an allied structure: Garrison" msgstr "" -#: gui/manual/intro.txt:100 +#: gui/manual/intro.txt:102 msgid "" " - If the cursor is over a non-allied unit or building: Attack (instead " "of capture or gather)" msgstr "" -#: gui/manual/intro.txt:101 +#: gui/manual/intro.txt:103 msgid "" " - Otherwise: Attack move (by default all enemy units and structures " "along the way are targeted, use Ctrl + Q + Right Click to target only " "units). " msgstr "" -#: gui/manual/intro.txt:103 +#: gui/manual/intro.txt:105 msgid "[font=\"sans-bold-14\"]Overlays" msgstr "" -#: gui/manual/intro.txt:104 +#: gui/manual/intro.txt:106 msgid "[font=\"sans-14\"]Alt + G: Hide/show the GUI" msgstr "" -#: gui/manual/intro.txt:105 +#: gui/manual/intro.txt:107 msgid "Alt + D: Show/hide developer overlay (with developer options)" msgstr "" -#: gui/manual/intro.txt:106 +#: gui/manual/intro.txt:108 msgid "" "Alt + W: Toggle wireframe mode (press once to get wireframes overlaid over " "the textured models, twice to get just the wireframes colored by the " "textures, thrice to get back to normal textured mode)" msgstr "" -#: gui/manual/intro.txt:107 +#: gui/manual/intro.txt:109 msgid "Alt + S: Toggle unit silhouettes (might give a small performance boost)" msgstr "" -#: gui/manual/intro.txt:108 +#: gui/manual/intro.txt:110 msgid "Alt + Z: Toggle sky" msgstr "" -#: gui/manual/intro.txt:110 +#: gui/manual/intro.txt:111 +msgid "" +"Alt + V: Toggle aura range visualizations of selected units and structures" +msgstr "" + +#: gui/manual/intro.txt:113 msgid "[font=\"sans-bold-14\"]Camera manipulation" msgstr "" -#: gui/manual/intro.txt:111 +#: gui/manual/intro.txt:114 msgid "[font=\"sans-14\"]W or \\[up]: Pan screen up" msgstr "" -#: gui/manual/intro.txt:112 +#: gui/manual/intro.txt:115 msgid "S or \\[down]: Pan screen down" msgstr "" -#: gui/manual/intro.txt:113 +#: gui/manual/intro.txt:116 msgid "A or \\[left]: Pan screen left" msgstr "" -#: gui/manual/intro.txt:114 +#: gui/manual/intro.txt:117 msgid "D or \\[right]: Pan screen right" msgstr "" -#: gui/manual/intro.txt:115 +#: gui/manual/intro.txt:118 msgid "Ctrl + W or \\[up]: Rotate camera to look upward" msgstr "" -#: gui/manual/intro.txt:116 +#: gui/manual/intro.txt:119 msgid "Ctrl + S or \\[down]: Rotate camera to look downward" msgstr "" -#: gui/manual/intro.txt:117 +#: gui/manual/intro.txt:120 msgid "Ctrl + A or \\[left]: Rotate camera clockwise around terrain" msgstr "" -#: gui/manual/intro.txt:118 +#: gui/manual/intro.txt:121 msgid "Ctrl + D or \\[right]: Rotate camera anticlockwise around terrain" msgstr "" -#: gui/manual/intro.txt:119 +#: gui/manual/intro.txt:122 msgid "Q: Rotate camera clockwise around terrain" msgstr "" -#: gui/manual/intro.txt:120 +#: gui/manual/intro.txt:123 msgid "E: Rotate camera anticlockwise around terrain" msgstr "" -#: gui/manual/intro.txt:121 +#: gui/manual/intro.txt:124 msgid "Shift + Mouse Wheel Rotate Up: Rotate camera clockwise around terrain" msgstr "" -#: gui/manual/intro.txt:122 +#: gui/manual/intro.txt:125 msgid "" "Shift + Mouse Wheel Rotate Down: Rotate camera anticlockwise around terrain" msgstr "" -#: gui/manual/intro.txt:123 +#: gui/manual/intro.txt:126 msgid "" "F: Follow the selected unit (move the camera to stop the camera from " "following the unit/s)" msgstr "" -#: gui/manual/intro.txt:124 +#: gui/manual/intro.txt:127 msgid "R: Reset camera zoom/orientation" msgstr "" -#: gui/manual/intro.txt:125 +#: gui/manual/intro.txt:128 msgid "+: Zoom in (keep pressed for continuous zoom)" msgstr "" -#: gui/manual/intro.txt:126 +#: gui/manual/intro.txt:129 msgid "-: Zoom out (keep pressed for continuous zoom)" msgstr "" -#: gui/manual/intro.txt:127 +#: gui/manual/intro.txt:130 msgid "Alt + W: Toggle through wireframe modes" msgstr "" -#: gui/manual/intro.txt:128 +#: gui/manual/intro.txt:131 msgid "Middle Mouse Button: Keep pressed and move the mouse to pan" msgstr "" -#: gui/manual/intro.txt:130 +#: gui/manual/intro.txt:133 msgid "[font=\"sans-bold-14\"]During Building Placement" msgstr "" -#: gui/manual/intro.txt:131 +#: gui/manual/intro.txt:134 msgid "[font=\"sans-14\"]\\[: Rotate building 15 degrees counter-clockwise" msgstr "" -#: gui/manual/intro.txt:132 +#: gui/manual/intro.txt:135 msgid "]: Rotate building 15 degrees clockwise" msgstr "" -#: gui/manual/intro.txt:133 +#: gui/manual/intro.txt:136 msgid "" "Left Drag: Rotate building using mouse (foundation will be placed on mouse " "release)" msgstr "" -#: gui/manual/intro.txt:135 +#: gui/manual/intro.txt:138 msgid "[font=\"sans-bold-14\"]When loading a saved game" msgstr "" -#: gui/manual/intro.txt:136 +#: gui/manual/intro.txt:139 msgid "" "[font=\"sans-14\"]Delete: delete the selected saved game asking confirmation" msgstr "" -#: gui/manual/intro.txt:137 +#: gui/manual/intro.txt:140 msgid "Shift: do not ask confirmation when deleting a saved game" msgstr "" Index: binaries/data/mods/public/l10n/public-gui-other.pot =================================================================== --- binaries/data/mods/public/l10n/public-gui-other.pot +++ binaries/data/mods/public/l10n/public-gui-other.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-05-01 10:19+0200\n" -"PO-Revision-Date: 2017-05-01 10:19+0200\n" +"POT-Creation-Date: 2017-05-08 09:02+0200\n" +"PO-Revision-Date: 2017-05-08 09:02+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -39,7 +39,7 @@ msgstr[0] "" msgstr[1] "" -#: gui/civinfo/civinfo.js:124 gui/summary/layout.js:64 +#: gui/civinfo/civinfo.js:124 gui/summary/layout.js:67 msgid "Heroes" msgstr "" @@ -119,18 +119,18 @@ msgstr "" #: gui/common/functions_utility_loadsave.js:89 gui/common/tooltips.js:246 -#: gui/options/options.js:320 gui/options/options.js:387 +#: gui/options/options.js:321 gui/options/options.js:388 #: gui/pregame/mainmenu.js:262 gui/replaymenu/replay_actions.js:159 #: gui/savedgames/load.js:188 gui/savedgames/save.js:66 -#: gui/summary/summary.js:146 +#: gui/summary/summary.js:346 msgid "No" msgstr "" #: gui/common/functions_utility_loadsave.js:89 gui/common/tooltips.js:246 -#: gui/options/options.js:320 gui/options/options.js:387 +#: gui/options/options.js:321 gui/options/options.js:388 #: gui/pregame/mainmenu.js:262 gui/replaymenu/replay_actions.js:159 #: gui/savedgames/load.js:188 gui/savedgames/save.js:66 -#: gui/summary/summary.js:146 +#: gui/summary/summary.js:346 msgid "Yes" msgstr "" @@ -148,7 +148,7 @@ msgid "A randomly selected map." msgstr "" -#: gui/common/gamedescription.js:36 gui/common/gamedescription.js:310 +#: gui/common/gamedescription.js:36 gui/common/gamedescription.js:317 msgid "Sorry, no description available." msgstr "" @@ -257,7 +257,7 @@ msgstr[0] "" msgstr[1] "" -#: gui/common/gamedescription.js:224 +#: gui/common/gamedescription.js:225 #, javascript-format msgctxt "victory condition" msgid "Capture The Relic (%(min)s minute)" @@ -265,115 +265,119 @@ msgstr[0] "" msgstr[1] "" -#: gui/common/gamedescription.js:242 +#: gui/common/gamedescription.js:241 +msgid "Relic Count" +msgstr "" + +#: gui/common/gamedescription.js:249 msgid "Rated game" msgstr "" -#: gui/common/gamedescription.js:243 +#: gui/common/gamedescription.js:250 msgid "" "When the winner of this match is determined, the lobby score will be adapted." msgstr "" -#: gui/common/gamedescription.js:248 +#: gui/common/gamedescription.js:255 msgid "Locked Teams" msgstr "" -#: gui/common/gamedescription.js:249 +#: gui/common/gamedescription.js:256 msgid "Players can't change the initial teams." msgstr "" -#: gui/common/gamedescription.js:253 +#: gui/common/gamedescription.js:260 msgid "Diplomacy" msgstr "" -#: gui/common/gamedescription.js:254 +#: gui/common/gamedescription.js:261 msgid "Players can make alliances and declare war on allies." msgstr "" -#: gui/common/gamedescription.js:259 +#: gui/common/gamedescription.js:266 msgid "Last Man Standing" msgstr "" -#: gui/common/gamedescription.js:260 +#: gui/common/gamedescription.js:267 msgid "" "Only one player can win the game. If the remaining players are allies, the " "game continues until only one remains." msgstr "" -#: gui/common/gamedescription.js:264 +#: gui/common/gamedescription.js:271 msgid "Allied Victory" msgstr "" -#: gui/common/gamedescription.js:265 +#: gui/common/gamedescription.js:272 msgid "" "If one player wins, his or her allies win too. If one group of allies " "remains, they win." msgstr "" -#: gui/common/gamedescription.js:271 +#: gui/common/gamedescription.js:278 msgid "Ceasefire" msgstr "" -#: gui/common/gamedescription.js:274 gui/pregame/mainmenu.js:91 +#: gui/common/gamedescription.js:281 gui/pregame/mainmenu.js:91 msgid "disabled" msgstr "" -#: gui/common/gamedescription.js:275 +#: gui/common/gamedescription.js:282 msgid "For the first minute, enemies will stay neutral." msgid_plural "For the first %(min)s minutes, enemies will stay neutral." msgstr[0] "" msgstr[1] "" -#: gui/common/gamedescription.js:283 +#: gui/common/gamedescription.js:290 msgid "Map Name" msgstr "" -#: gui/common/gamedescription.js:288 +#: gui/common/gamedescription.js:295 msgid "Map Type" msgstr "" -#: gui/common/gamedescription.js:297 +#: gui/common/gamedescription.js:304 msgid "Map Size" msgstr "" -#: gui/common/gamedescription.js:304 +#: gui/common/gamedescription.js:311 msgid "Map Description" msgstr "" -#: gui/common/gamedescription.js:307 +#: gui/common/gamedescription.js:314 msgid "Randomly selects a map from the list" msgstr "" -#: gui/common/gamedescription.js:316 +#: gui/common/gamedescription.js:323 msgid "Starting Resources" msgstr "" -#: gui/common/gamedescription.js:317 +#: gui/common/gamedescription.js:324 #, javascript-format msgid "%(startingResourcesTitle)s (%(amount)s)" msgstr "" -#: gui/common/gamedescription.js:327 +#: gui/common/gamedescription.js:334 msgid "Population Limit" msgstr "" -#: gui/common/gamedescription.js:335 +#: gui/common/gamedescription.js:342 msgid "Disable Treasure" msgstr "" -#: gui/common/gamedescription.js:340 +#: gui/common/gamedescription.js:347 msgid "Revealed Map" msgstr "" -#: gui/common/gamedescription.js:345 +#: gui/common/gamedescription.js:352 msgid "Explored Map" msgstr "" -#: gui/common/gamedescription.js:350 +#: gui/common/gamedescription.js:357 msgid "Cheats" msgstr "" -#: gui/common/gamedescription.js:355 gui/common/tooltips.js:86 +#: gui/common/gamedescription.js:362 gui/common/tooltips.js:86 #: gui/common/tooltips.js:141 gui/common/tooltips.js:182 #: gui/common/tooltips.js:404 gui/common/tooltips.js:425 #: gui/common/tooltips.js:707 @@ -381,21 +385,21 @@ msgid "%(label)s %(details)s" msgstr "" -#: gui/common/gamedescription.js:358 +#: gui/common/gamedescription.js:365 msgctxt "gamesetup option" msgid "enabled" msgstr "" -#: gui/common/gamedescription.js:359 +#: gui/common/gamedescription.js:366 msgctxt "gamesetup option" msgid "disabled" msgstr "" -#: gui/common/gamedescription.js:376 +#: gui/common/gamedescription.js:383 msgid "Victorious" msgstr "" -#: gui/common/gamedescription.js:381 +#: gui/common/gamedescription.js:388 msgid "Defeated" msgstr "" @@ -854,7 +858,7 @@ msgstr "" #: gui/common/tooltips.js:323 gui/common/tooltips.js:337 -#: gui/summary/layout.js:61 +#: gui/summary/layout.js:64 msgid "Worker" msgstr "" @@ -1016,18 +1020,18 @@ msgid "OK" msgstr "" -#: gui/options/options.js:318 +#: gui/options/options.js:319 msgid "" "Resetting the options will erase your saved settings. Do you want to " "continue?" msgstr "" -#: gui/options/options.js:319 gui/options/options.js:386 +#: gui/options/options.js:320 gui/options/options.js:387 #: gui/savedgames/load.js:187 msgid "Warning" msgstr "" -#: gui/options/options.js:385 +#: gui/options/options.js:386 msgid "You have unsaved changes, do you want to close this window?" msgstr "" @@ -1105,7 +1109,7 @@ msgid "Are you sure you want to quit 0 A.D.?" msgstr "" -#: gui/pregame/mainmenu.js:261 gui/summary/summary.js:145 +#: gui/pregame/mainmenu.js:261 gui/summary/summary.js:345 msgid "Confirmation" msgstr "" @@ -1324,246 +1328,277 @@ msgid "%(val1)s to %(val2)s" msgstr "" -#: gui/summary/layout.js:4 gui/summary/layout.js:21 gui/summary/layout.js:58 -#: gui/summary/layout.js:97 gui/summary/layout.js:142 -#: gui/summary/layout.js:172 gui/summary/summary.xml:(caption):105 +#: gui/summary/layout.js:3 gui/summary/summary.xml:(caption):63 +msgid "Score" +msgstr "" + +#: gui/summary/layout.js:5 gui/summary/layout.js:23 gui/summary/layout.js:61 +#: gui/summary/layout.js:101 gui/summary/layout.js:149 +#: gui/summary/layout.js:181 gui/summary/summary.xml:(caption):112 msgid "Player name" msgstr "" -#: gui/summary/layout.js:5 +#: gui/summary/layout.js:6 msgid "Economy score" msgstr "" -#: gui/summary/layout.js:6 +#: gui/summary/layout.js:7 msgid "Military score" msgstr "" -#: gui/summary/layout.js:7 +#: gui/summary/layout.js:8 msgid "Exploration score" msgstr "" -#: gui/summary/layout.js:8 +#: gui/summary/layout.js:9 msgid "Total score" msgstr "" -#: gui/summary/layout.js:22 gui/summary/layout.js:59 gui/summary/layout.js:103 +#: gui/summary/layout.js:21 gui/summary/summary.xml:(caption):70 +msgid "Buildings" +msgstr "" + +#: gui/summary/layout.js:24 gui/summary/layout.js:62 gui/summary/layout.js:108 msgid "Total" msgstr "" -#: gui/summary/layout.js:23 +#: gui/summary/layout.js:25 msgid "Houses" msgstr "" -#: gui/summary/layout.js:24 +#: gui/summary/layout.js:26 msgid "Economic" msgstr "" -#: gui/summary/layout.js:25 +#: gui/summary/layout.js:27 msgid "Outposts" msgstr "" -#: gui/summary/layout.js:26 +#: gui/summary/layout.js:28 msgid "Military" msgstr "" -#: gui/summary/layout.js:27 +#: gui/summary/layout.js:29 msgid "Fortresses" msgstr "" -#: gui/summary/layout.js:28 +#: gui/summary/layout.js:30 msgid "Civ centers" msgstr "" -#: gui/summary/layout.js:29 +#: gui/summary/layout.js:31 msgid "Wonders" msgstr "" -#: gui/summary/layout.js:33 +#: gui/summary/layout.js:35 #, javascript-format msgid "" "Buildings Statistics (%(constructed)s / %(destroyed)s / %(captured)s / %" "(lost)s)" msgstr "" -#: gui/summary/layout.js:35 -msgid "Constructed" -msgstr "" - -#: gui/summary/layout.js:36 -msgid "Destroyed" -msgstr "" - -#: gui/summary/layout.js:37 gui/summary/layout.js:75 -msgid "Captured" -msgstr "" - -#: gui/summary/layout.js:38 gui/summary/layout.js:76 -msgid "Lost" +#: gui/summary/layout.js:59 gui/summary/summary.xml:(caption):77 +msgid "Units" msgstr "" -#: gui/summary/layout.js:60 +#: gui/summary/layout.js:63 msgid "Infantry" msgstr "" -#: gui/summary/layout.js:62 +#: gui/summary/layout.js:65 msgid "Cavalry" msgstr "" -#: gui/summary/layout.js:63 +#: gui/summary/layout.js:66 msgid "Champion" msgstr "" -#: gui/summary/layout.js:65 +#: gui/summary/layout.js:68 msgid "Siege" msgstr "" -#: gui/summary/layout.js:66 +#: gui/summary/layout.js:69 msgid "Navy" msgstr "" -#: gui/summary/layout.js:67 +#: gui/summary/layout.js:70 msgid "Traders" msgstr "" -#: gui/summary/layout.js:71 +#: gui/summary/layout.js:74 #, javascript-format msgid "Units Statistics (%(trained)s / %(killed)s / %(captured)s / %(lost)s)" msgstr "" -#: gui/summary/layout.js:73 -msgid "Trained" -msgstr "" - -#: gui/summary/layout.js:74 -msgid "Killed" +#: gui/summary/layout.js:99 gui/summary/summary.xml:(caption):84 +msgid "Resources" msgstr "" -#: gui/summary/layout.js:105 +#: gui/summary/layout.js:111 #, javascript-format msgid "" "Tributes \n" "(%(sent)s / %(received)s)" msgstr "" -#: gui/summary/layout.js:107 -msgid "Sent" -msgstr "" - -#: gui/summary/layout.js:108 -msgid "Received" -msgstr "" - -#: gui/summary/layout.js:113 +#: gui/summary/layout.js:119 msgid "Treasures collected" msgstr "" -#: gui/summary/layout.js:114 +#: gui/summary/layout.js:120 msgid "Loot" msgstr "" -#: gui/summary/layout.js:118 +#: gui/summary/layout.js:124 #, javascript-format msgid "Resource Statistics (%(gathered)s / %(used)s)" msgstr "" -#: gui/summary/layout.js:120 -msgid "Gathered" -msgstr "" - -#: gui/summary/layout.js:121 -msgid "Used" +#: gui/summary/layout.js:147 gui/summary/summary.xml:(caption):91 +msgid "Market" msgstr "" #. Translation: use %(resourceWithinSentence)s if needed -#: gui/summary/layout.js:147 +#: gui/summary/layout.js:155 #, javascript-format msgid "%(resourceFirstWord)s exchanged" msgstr "" -#: gui/summary/layout.js:155 +#: gui/summary/layout.js:163 msgid "Barter efficiency" msgstr "" -#: gui/summary/layout.js:156 +#: gui/summary/layout.js:164 msgid "Trade income" msgstr "" -#: gui/summary/layout.js:173 -msgid "" -"Vegetarian\n" -"ratio" +#: gui/summary/layout.js:179 gui/summary/summary.xml:(caption):98 +#: gui/credits/texts/misc.json:Title +msgid "Miscellaneous" msgstr "" -#: gui/summary/layout.js:174 -msgid "Feminization" +#: gui/summary/layout.js:182 +msgid "Vegetarian ratio" msgstr "" -#: gui/summary/layout.js:175 -msgid "" -"Kill / Death\n" -"ratio" +#: gui/summary/layout.js:183 +msgid "Feminization" msgstr "" -#: gui/summary/layout.js:176 -msgid "" -"Map\n" -"exploration" +#: gui/summary/layout.js:184 +msgid "Kill / Death ratio" msgstr "" -#: gui/summary/layout.js:177 -msgid "At peak" +#: gui/summary/layout.js:185 +msgid "Map exploration" msgstr "" -#: gui/summary/layout.js:178 -msgid "At finish" +#: gui/summary/layout.js:186 +msgid "Map control (peak)" msgstr "" -#: gui/summary/layout.js:181 -msgid "Map control" +#: gui/summary/layout.js:187 +msgid "Map control (finish)" msgstr "" -#: gui/summary/layout.js:326 +#: gui/summary/layout.js:338 msgid "Team total" msgstr "" -#: gui/summary/summary.js:144 +#: gui/summary/summary.js:30 +msgid "Trained" +msgstr "" + +#: gui/summary/summary.js:35 +msgid "Constructed" +msgstr "" + +#: gui/summary/summary.js:40 +msgid "Gathered" +msgstr "" + +#: gui/summary/summary.js:45 +msgid "Sent" +msgstr "" + +#: gui/summary/summary.js:50 +msgid "Bought" +msgstr "" + +#: gui/summary/summary.js:55 +msgid "Income" +msgstr "" + +#: gui/summary/summary.js:60 +msgid "Captured" +msgstr "" + +#: gui/summary/summary.js:65 +msgid "Destroyed" +msgstr "" + +#: gui/summary/summary.js:70 +msgid "Killed" +msgstr "" + +#: gui/summary/summary.js:75 +msgid "Lost" +msgstr "" + +#: gui/summary/summary.js:80 +msgid "Used" +msgstr "" + +#: gui/summary/summary.js:85 +msgid "Recieved" +msgstr "" + +#: gui/summary/summary.js:90 +msgid "Sold" +msgstr "" + +#: gui/summary/summary.js:95 +msgid "Outcome" +msgstr "" + +#: gui/summary/summary.js:344 msgid "Are you sure you want to quit the lobby?" msgstr "" -#: gui/summary/summary.js:195 +#: gui/summary/summary.js:395 msgid "Current Scores" msgstr "" -#: gui/summary/summary.js:197 +#: gui/summary/summary.js:397 msgid "Scores at the end of the game." msgstr "" -#: gui/summary/summary.js:199 +#: gui/summary/summary.js:399 msgid "You have been disconnected." msgstr "" -#: gui/summary/summary.js:201 +#: gui/summary/summary.js:401 msgid "You have left the game." msgstr "" -#: gui/summary/summary.js:203 +#: gui/summary/summary.js:403 msgid "You have won the battle!" msgstr "" -#: gui/summary/summary.js:205 +#: gui/summary/summary.js:405 msgid "You have been defeated..." msgstr "" -#: gui/summary/summary.js:206 +#: gui/summary/summary.js:406 msgid "You have abandoned the game." msgstr "" -#: gui/summary/summary.js:211 +#: gui/summary/summary.js:411 #, javascript-format msgid "Game time elapsed: %(time)s" msgstr "" -#: gui/summary/summary.js:219 +#: gui/summary/summary.js:419 #, javascript-format msgid "%(mapName)s - %(mapType)s" msgstr "" @@ -2068,36 +2103,46 @@ msgid "Trainer Units" msgstr "" -#: gui/summary/summary.xml:(caption):63 -msgid "Score" +#: gui/summary/summary.xml:(caption):105 +msgid "Charts" msgstr "" -#: gui/summary/summary.xml:(caption):70 -msgid "Buildings" +#: gui/summary/summary.xml:(caption):167 +msgctxt "summary chart" +msgid "Category:" msgstr "" -#: gui/summary/summary.xml:(caption):77 -msgid "Units" +#: gui/summary/summary.xml:(caption):177 +msgctxt "summary chart" +msgid "Value:" msgstr "" -#: gui/summary/summary.xml:(caption):84 -msgid "Resources" +#: gui/summary/summary.xml:(caption):187 +msgctxt "summary chart" +msgid "Type:" msgstr "" -#: gui/summary/summary.xml:(caption):91 -msgid "Market" +#: gui/summary/summary.xml:(caption):206 +msgid "Replay" msgstr "" -#: gui/summary/summary.xml:(caption):98 gui/credits/texts/misc.json:Title -msgid "Miscellaneous" +#: gui/summary/summary.xml:(caption):211 +msgid "Continue" msgstr "" -#: gui/summary/summary.xml:(caption):157 -msgid "Replay" +#: gui/summary/summary.xml:(tooltip):173 +msgctxt "summary chart" +msgid "Category" msgstr "" -#: gui/summary/summary.xml:(caption):162 -msgid "Continue" +#: gui/summary/summary.xml:(tooltip):183 +msgctxt "summary chart" +msgid "Value" +msgstr "" + +#: gui/summary/summary.xml:(tooltip):194 +msgctxt "summary chart" +msgid "Type" msgstr "" #: gui/credits/texts/art.json:Content[0].Content[0].Subtitle @@ -2651,11 +2696,33 @@ msgstr "" #: gui/options/options.json:graphicsSetting[16].tooltip -msgid "Limit FPS to 50 in all menus, to save power." +msgid "" +"To save CPU workload, throttle render frequency in all menus. Set to maximum " +"to disable throttling." msgstr "" #: gui/options/options.json:graphicsSetting[16].label -msgid "Limit FPS in Menus" +msgid "FPS throttling in menus" +msgstr "" + +#: gui/options/options.json:graphicsSetting[17].tooltip +msgid "" +"To save CPU workload, throttle render frequency in running games. Set to " +"maximum to disable throttling." +msgstr "" + +#: gui/options/options.json:graphicsSetting[17].label +msgid "FPS throttling in games" +msgstr "" + +#: gui/options/options.json:graphicsSetting[18].tooltip +msgid "" +"Display the range of auras of selected units and structures (can also be " +"toggled in-game with the hotkey)." +msgstr "" + +#: gui/options/options.json:graphicsSetting[18].label +msgid "Aura Range Visualization" msgstr "" #: simulation/data/resources/food.json:description Index: binaries/data/mods/public/l10n/public-maps.pot =================================================================== --- binaries/data/mods/public/l10n/public-maps.pot +++ binaries/data/mods/public/l10n/public-maps.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-04-28 09:02+0200\n" -"PO-Revision-Date: 2017-04-28 09:02+0200\n" +"POT-Creation-Date: 2017-05-08 09:04+0200\n" +"PO-Revision-Date: 2017-05-08 09:04+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -642,6 +642,19 @@ msgid "Pyrenean Sierra" msgstr "" +#: maps/random/ratumacos.json:settings.Description +msgid "" +"Players start on the banks of the River Sequana (Seine) in Northern Gaul " +"near the settlement of Ratumacos. Destined to become one of the largest and " +"most prosperous cities of Medieval Europe and one of the Anglo-Norman " +"dynasty capitals under the new name of Rouen, Ratumacos is still a peaceful " +"land - but not for long." +msgstr "" + +#: maps/random/ratumacos.json:settings.Name +msgid "Ratumacos" +msgstr "" + #: maps/random/red_sea.json:settings.Description msgid "" "Historically, the Red Sea was a sea of many nations. Ideally situated for " @@ -672,18 +685,6 @@ msgid "Rivers" msgstr "" -#: maps/random/rouen.json:settings.Description -msgid "" -"Players start on the banks of the River Sequana (Seine) in the north of Gaul " -"in the place of the modern-day city of Rouen. Destined to become one of the " -"largest and most prosperous cities of medieval Europe and one of the Anglo-" -"Norman dynasty capitals, Rouen is still a peaceful land - but not for long." -msgstr "" - -#: maps/random/rouen.json:settings.Name -msgid "Rouen" -msgstr "" - #: maps/random/saharan_oases.json:settings.Description msgid "Each players starts near a lush oasis in a large, desolate desert." msgstr "" Index: binaries/data/mods/public/l10n/public-simulation-auras.pot =================================================================== --- binaries/data/mods/public/l10n/public-simulation-auras.pot +++ binaries/data/mods/public/l10n/public-simulation-auras.pot @@ -5,15 +5,15 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-05-01 10:20+0200\n" -"PO-Revision-Date: 2017-05-01 10:20+0200\n" +"POT-Creation-Date: 2017-05-08 09:03+0200\n" +"PO-Revision-Date: 2017-05-08 09:03+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: simulation/data/auras/structures/cart_super_dock_repair.json:auraName -msgid "Repairing Ship aura" +msgid "Dockyard Repairs" msgstr "" #: simulation/data/auras/structures/cart_super_dock_repair.json:auraDescription @@ -21,8 +21,7 @@ msgstr "" #: simulation/data/auras/structures/epic_temple_heal.json:auraName -#: simulation/data/auras/structures/temple_heal.json:auraName -msgid "Healing Aura" +msgid "Blessing of the Gods" msgstr "" #: simulation/data/auras/structures/epic_temple_heal.json:auraDescription @@ -30,7 +29,7 @@ msgstr "" #: simulation/data/auras/structures/iber_monument.json:auraName -msgid "Defensive Aura" +msgid "Religious Fervor" msgstr "" #: simulation/data/auras/structures/iber_monument.json:auraDescription @@ -39,7 +38,7 @@ msgstr "" #: simulation/data/auras/structures/library.json:auraName -msgid "Library Aura" +msgid "Power of Knowledge" msgstr "" #: simulation/data/auras/structures/library.json:auraDescription @@ -48,7 +47,7 @@ msgstr "" #: simulation/data/auras/structures/loyalty_regen.json:auraName -msgid "Loyalty Aura" +msgid "Loyalty" msgstr "" #: simulation/data/auras/structures/loyalty_regen.json:auraDescription @@ -71,12 +70,16 @@ msgid "Boosts nearby farming with +25% gathering rate." msgstr "" +#: simulation/data/auras/structures/temple_heal.json:auraName +msgid "Medical Treatment" +msgstr "" + #: simulation/data/auras/structures/temple_heal.json:auraDescription msgid "Heals nearby units at 1 HP per second." msgstr "" #: simulation/data/auras/structures/theatron.json:auraName -msgid "Hellenization." +msgid "Hellenization" msgstr "" #: simulation/data/auras/structures/theatron.json:auraDescription @@ -92,7 +95,7 @@ msgstr "" #: simulation/data/auras/structures/wonder_pop_1.json:auraName -msgid "Wonder Aura" +msgid "Symbol of Greatness" msgstr "" #: simulation/data/auras/structures/wonder_pop_1.json:auraDescription @@ -100,7 +103,7 @@ msgstr "" #: simulation/data/auras/structures/wonder_pop_2.json:auraName -msgid "Glorious Expansion Aura" +msgid "Glorious Expansion" msgstr "" #: simulation/data/auras/structures/wonder_pop_2.json:auraDescription @@ -184,7 +187,7 @@ msgstr "" #: simulation/data/auras/units/female_inspiration.json:auraName -msgid "Inspiration Aura" +msgid "Inspiration" msgstr "" #: simulation/data/auras/units/female_inspiration.json:auraDescription @@ -362,7 +365,7 @@ msgstr "" #: simulation/data/auras/units/catafalques/rome_catafalque_2.json:auraName -msgid "Founder and Defender of the Republic Aura" +msgid "Founder and Defender of the Republic" msgstr "" #: simulation/data/auras/units/catafalques/rome_catafalque_2.json:auraDescription @@ -396,7 +399,7 @@ msgstr "" #: simulation/data/auras/units/catafalques/sele_catafalque_3.json:auraName -msgid "Basileus Megas (Great King)" +msgid "Basileus Megas" msgstr "" #: simulation/data/auras/units/catafalques/sele_catafalque_3.json:auraDescription @@ -452,7 +455,7 @@ msgstr "" #: simulation/data/auras/units/heroes/athen_hero_pericles_1.json:auraName -msgid "Builder Aura" +msgid "Periclean Building Program" msgstr "" #: simulation/data/auras/units/heroes/athen_hero_pericles_1.json:auraDescription @@ -460,7 +463,7 @@ msgstr "" #: simulation/data/auras/units/heroes/athen_hero_pericles_2.json:auraName -msgid "Acropolis Aura" +msgid "Temple to Athena" msgstr "" #: simulation/data/auras/units/heroes/athen_hero_pericles_2.json:auraDescription @@ -468,7 +471,7 @@ msgstr "" #: simulation/data/auras/units/heroes/athen_hero_themistocles_1.json:auraName -msgid "Naval Commander Aura" +msgid "Naval Commander" msgstr "" #: simulation/data/auras/units/heroes/athen_hero_themistocles_1.json:auraDescription @@ -476,7 +479,7 @@ msgstr "" #: simulation/data/auras/units/heroes/athen_hero_themistocles_2.json:auraName -msgid "Naval Architect Aura" +msgid "Naval Architect" msgstr "" #: simulation/data/auras/units/heroes/athen_hero_themistocles_2.json:auraDescription @@ -492,12 +495,7 @@ msgstr "" #: simulation/data/auras/units/heroes/brit_hero_caratacos.json:auraName -#: simulation/data/auras/units/heroes/brit_hero_cunobelin.json:auraName -#: simulation/data/auras/units/heroes/gaul_hero_brennus.json:auraName -#: simulation/data/auras/units/heroes/gaul_hero_britomartus.json:auraName -#: simulation/data/auras/units/heroes/gaul_hero_vercingetorix.json:auraName -#: simulation/data/auras/units/heroes/mace_hero_philip.json:auraName -msgid "Hero Aura" +msgid "Guerrilla Chief" msgstr "" #: simulation/data/auras/units/heroes/brit_hero_caratacos.json:auraDescription @@ -505,16 +503,20 @@ msgid "All soldiers and siege engines +15% speed." msgstr "" +#: simulation/data/auras/units/heroes/brit_hero_cunobelin.json:auraName +msgid "Britannorum Rex" +msgstr "" + #: simulation/data/auras/units/heroes/brit_hero_cunobelin.json:auraDescription msgid "+1 HP per second healing rate. Effect Range: 30 meters." msgstr "" #: simulation/data/auras/units/heroes/cart_hero_hamilcar.json:auraName -msgid "Lightning Aura" +msgid "Lightning General" msgstr "" #: simulation/data/auras/units/heroes/cart_hero_hannibal.json:auraName -msgid "Tactician Aura" +msgid "Tactician" msgstr "" #: simulation/data/auras/units/heroes/cart_hero_hannibal.json:auraDescription @@ -524,21 +526,33 @@ msgstr "" #: simulation/data/auras/units/heroes/cart_hero_maharbal.json:auraName -msgid "Commander Aura" +msgid "Cavalry Commander" msgstr "" #: simulation/data/auras/units/heroes/cart_hero_maharbal.json:auraDescription msgid "+20% cavalry melee attack within his aura." msgstr "" +#: simulation/data/auras/units/heroes/gaul_hero_brennus.json:auraName +msgid "Sacker of Rome" +msgstr "" + #: simulation/data/auras/units/heroes/gaul_hero_brennus.json:auraDescription msgid "+10 Metal loot for every enemy unit killed." msgstr "" +#: simulation/data/auras/units/heroes/gaul_hero_britomartus.json:auraName +msgid "Preparation for War" +msgstr "" + #: simulation/data/auras/units/heroes/gaul_hero_britomartus.json:auraDescription msgid "Gathering rates increased with +15% during his lifetime." msgstr "" +#: simulation/data/auras/units/heroes/gaul_hero_vercingetorix.json:auraName +msgid "Celtic Warlord" +msgstr "" + #: simulation/data/auras/units/heroes/gaul_hero_vercingetorix.json:auraDescription msgid "" "+20% attack and +1 capture for all soldiers and siege engines within his " @@ -546,7 +560,7 @@ msgstr "" #: simulation/data/auras/units/heroes/hero_garrison.json:auraName -msgid "Garrisoned Capture Aura" +msgid "Inspired Defense" msgstr "" #: simulation/data/auras/units/heroes/hero_garrison.json:auraDescription @@ -556,7 +570,7 @@ msgstr "" #: simulation/data/auras/units/heroes/mace_hero_alexander.json:auraName -msgid "Imperialism Aura" +msgid "Imperialism" msgstr "" #: simulation/data/auras/units/heroes/mace_hero_alexander.json:auraDescription @@ -572,13 +586,17 @@ msgstr "" #: simulation/data/auras/units/heroes/mace_hero_demetrius.json:auraName -msgid "Besieger Aura" +msgid "Besieger" msgstr "" #: simulation/data/auras/units/heroes/mace_hero_demetrius.json:auraDescription msgid "+15% range and +10% crush attack for siege engines." msgstr "" +#: simulation/data/auras/units/heroes/mace_hero_philip.json:auraName +msgid "Rise of Macedon" +msgstr "" + #: simulation/data/auras/units/heroes/mace_hero_philip.json:auraDescription msgid "+20% attack and +2 capture for champion units." msgstr "" @@ -593,7 +611,7 @@ msgstr "" #: simulation/data/auras/units/heroes/maur_hero_ashoka.json:auraName -msgid "Buddhism Aura" +msgid "Buddhism" msgstr "" #: simulation/data/auras/units/heroes/maur_hero_ashoka.json:auraDescription @@ -601,7 +619,7 @@ msgstr "" #: simulation/data/auras/units/heroes/maur_hero_chanakya.json:auraName -msgid "Teacher Aura" +msgid "Teacher" msgstr "" #: simulation/data/auras/units/heroes/maur_hero_chanakya.json:auraDescription @@ -610,7 +628,7 @@ msgstr "" #: simulation/data/auras/units/heroes/pers_hero_cyrus.json:auraName -msgid "Lead from the Front Aura" +msgid "Lead from the Front" msgstr "" #: simulation/data/auras/units/heroes/pers_hero_cyrus.json:auraDescription @@ -618,7 +636,7 @@ msgstr "" #: simulation/data/auras/units/heroes/pers_hero_darius.json:auraName -msgid "Leadership Aura" +msgid "Leadership" msgstr "" #: simulation/data/auras/units/heroes/pers_hero_darius.json:auraDescription @@ -626,7 +644,7 @@ msgstr "" #: simulation/data/auras/units/heroes/pers_hero_xerxes.json:auraName -msgid "Administrator Aura" +msgid "Administrator" msgstr "" #: simulation/data/auras/units/heroes/pers_hero_xerxes.json:auraDescription @@ -634,7 +652,7 @@ msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_cleopatra_1.json:auraName -msgid "Patriot Aura" +msgid "Patriot" msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_cleopatra_1.json:auraDescription @@ -656,7 +674,7 @@ msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_ptolemy_IV.json:auraName -msgid "Raphia Aura" +msgid "Raphia" msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_ptolemy_IV.json:auraDescription @@ -664,7 +682,7 @@ msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_1.json:auraName -msgid "Construction Aura" +msgid "Patron of Construction" msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_1.json:auraDescription @@ -672,7 +690,7 @@ msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_2.json:auraName -msgid "Mercenary Patron Aura" +msgid "Mercenary Patron" msgstr "" #: simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_2.json:auraDescription @@ -680,7 +698,7 @@ msgstr "" #: simulation/data/auras/units/heroes/rome_hero_marcellus.json:auraName -msgid "Sword of Rome Aura" +msgid "Sword of Rome" msgstr "" #: simulation/data/auras/units/heroes/rome_hero_marcellus.json:auraDescription @@ -690,7 +708,7 @@ msgstr "" #: simulation/data/auras/units/heroes/rome_hero_maximus.json:auraName -msgid "Shield of Rome Aura" +msgid "Shield of Rome" msgstr "" #: simulation/data/auras/units/heroes/rome_hero_maximus.json:auraDescription @@ -698,7 +716,7 @@ msgstr "" #: simulation/data/auras/units/heroes/sele_hero_antiochus_great.json:auraName -msgid "Ilarchès Aura" +msgid "Ilarchès" msgstr "" #: simulation/data/auras/units/heroes/sele_hero_antiochus_great.json:auraDescription @@ -706,7 +724,7 @@ msgstr "" #: simulation/data/auras/units/heroes/sele_hero_antiochus_righteous.json:auraName -msgid "Conquest Aura" +msgid "Renowned Conqueror" msgstr "" #: simulation/data/auras/units/heroes/sele_hero_antiochus_righteous.json:auraDescription @@ -716,7 +734,7 @@ msgstr "" #: simulation/data/auras/units/heroes/sele_hero_seleucus_victor.json:auraName -msgid "Zooiarchos Aura" +msgid "Zooiarchos" msgstr "" #: simulation/data/auras/units/heroes/sele_hero_seleucus_victor.json:auraDescription @@ -732,7 +750,7 @@ msgstr "" #: simulation/data/auras/units/heroes/spart_hero_leonidas.json:auraName -msgid "Last Stand Aura" +msgid "Last Stand" msgstr "" #: simulation/data/auras/units/heroes/spart_hero_leonidas.json:auraDescription Index: binaries/data/mods/public/l10n/public-templates-buildings.pot =================================================================== --- binaries/data/mods/public/l10n/public-templates-buildings.pot +++ binaries/data/mods/public/l10n/public-templates-buildings.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-03-27 09:02+0200\n" -"PO-Revision-Date: 2017-03-27 09:02+0200\n" +"POT-Creation-Date: 2017-05-05 09:15+0200\n" +"PO-Revision-Date: 2017-05-05 09:15+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -59,14 +59,14 @@ msgstr "" #: simulation/templates/template_structure_civic_hellenic_royal_stoa.xml:39 -#: simulation/templates/template_structure_civic_temple.xml:33 +#: simulation/templates/template_structure_civic_temple.xml:35 #: simulation/templates/template_structure_defense_defense_tower.xml:57 #: simulation/templates/template_structure_economic_market.xml:24 #: simulation/templates/template_structure_military_blacksmith.xml:32 #: simulation/templates/template_structure_military_embassy.xml:26 #: simulation/templates/structures/brit_blacksmith.xml:11 #: simulation/templates/structures/brit_kennel.xml:39 -#: simulation/templates/structures/cart_super_dock.xml:37 +#: simulation/templates/structures/cart_super_dock.xml:39 #: simulation/templates/structures/gaul_blacksmith.xml:11 #: simulation/templates/structures/gaul_tavern.xml:29 #: simulation/templates/structures/maur_elephant_stables.xml:29 @@ -94,14 +94,14 @@ msgid "House" msgstr "" -#: simulation/templates/template_structure_civic_temple.xml:32 +#: simulation/templates/template_structure_civic_temple.xml:34 msgid "" "Train healers. Garrison units to heal them at a quick rate (3 HP per " "second). Research healing and religious improvements." msgstr "" +#: simulation/templates/template_structure_civic_temple.xml:35 #: simulation/templates/template_structure_civic_temple.xml:33 -#: simulation/templates/template_structure_civic_temple.xml:31 msgid "Temple" msgstr "" @@ -171,11 +171,11 @@ "Allow units access through a city wall. Can be locked to prevent access." msgstr "" -#: simulation/templates/template_structure_defense_wall_long.xml:40 +#: simulation/templates/template_structure_defense_wall_long.xml:42 msgid "Long wall segments can be converted to gates." msgstr "" -#: simulation/templates/template_structure_defense_wall_long.xml:45 +#: simulation/templates/template_structure_defense_wall_long.xml:47 msgid "This will allow you to let units circulate through your fortifications." msgstr "" @@ -298,7 +298,7 @@ #: simulation/templates/template_structure_military_fortress.xml:65 #: simulation/templates/template_structure_special.xml:45 -#: simulation/templates/template_structure_wonder.xml:49 +#: simulation/templates/template_structure_wonder.xml:52 msgid "City" msgstr "" @@ -339,37 +339,37 @@ msgid "This is a special building unique to a particular civilization." msgstr "" -#: simulation/templates/template_structure_special_library.xml:15 +#: simulation/templates/template_structure_special_library.xml:17 msgid "Library" msgstr "" -#: simulation/templates/template_structure_special_library.xml:17 +#: simulation/templates/template_structure_special_library.xml:19 msgid "Research special technologies." msgstr "" -#: simulation/templates/template_structure_special_rotarymill.xml:20 +#: simulation/templates/template_structure_special_rotarymill.xml:22 msgid "Rotary Mill" msgstr "" -#: simulation/templates/template_structure_special_rotarymill.xml:21 +#: simulation/templates/template_structure_special_rotarymill.xml:23 msgid "Melonas" msgstr "" -#: simulation/templates/template_structure_special_theatron.xml:23 +#: simulation/templates/template_structure_special_theatron.xml:25 msgid "Greek Theater" msgstr "" -#: simulation/templates/template_structure_special_theatron.xml:24 +#: simulation/templates/template_structure_special_theatron.xml:26 msgid "Théātron" msgstr "" -#: simulation/templates/template_structure_wonder.xml:48 +#: simulation/templates/template_structure_wonder.xml:51 msgid "" "Bring glory to your civilization and add large tracts of land to your empire." msgstr "" -#: simulation/templates/template_structure_wonder.xml:49 -#: simulation/templates/template_structure_wonder.xml:47 +#: simulation/templates/template_structure_wonder.xml:52 +#: simulation/templates/template_structure_wonder.xml:50 msgid "Wonder" msgstr "" @@ -534,7 +534,7 @@ #: simulation/templates/structures/cart_wonder.xml:17 #: simulation/templates/structures/mace_wonder.xml:17 #: simulation/templates/structures/ptol_wonder.xml:17 -#: simulation/templates/structures/rome_wonder.xml:14 +#: simulation/templates/structures/rome_wonder.xml:16 msgid "" "Bring glory to your civilization and add large tracts of land to your " "empire. Garrison units to heal them at an extremely quick rate." @@ -765,19 +765,19 @@ msgid "Maḥṣabah" msgstr "" -#: simulation/templates/structures/cart_super_dock.xml:35 +#: simulation/templates/structures/cart_super_dock.xml:37 msgid "Naval Shipyard" msgstr "" -#: simulation/templates/structures/cart_super_dock.xml:36 +#: simulation/templates/structures/cart_super_dock.xml:38 msgid "Cothon" msgstr "" -#: simulation/templates/structures/cart_super_dock.xml:37 +#: simulation/templates/structures/cart_super_dock.xml:39 msgid "Shipyard" msgstr "" -#: simulation/templates/structures/cart_super_dock.xml:39 +#: simulation/templates/structures/cart_super_dock.xml:41 msgid "Construct and repair mighty warships." msgstr "" @@ -791,11 +791,6 @@ "units to heal them at a quick rate." msgstr "" -#: simulation/templates/structures/cart_wall.xml:9 -#: simulation/templates/structures/cart_wallset_stone.xml:5 -msgid "Jdar" -msgstr "" - #: simulation/templates/structures/cart_wall_gate.xml:9 msgid "Mijdil-šaʿar" msgstr "" @@ -806,6 +801,10 @@ msgid "Homah" msgstr "" +#: simulation/templates/structures/cart_wallset_stone.xml:5 +msgid "Jdar" +msgstr "" + #: simulation/templates/structures/cart_wonder.xml:15 msgid "Temple of Ba'al Hammon" msgstr "" @@ -883,19 +882,19 @@ msgid "Arruga" msgstr "" -#: simulation/templates/structures/iber_monument.xml:7 +#: simulation/templates/structures/iber_monument.xml:9 msgid "Monument" msgstr "" -#: simulation/templates/structures/iber_monument.xml:32 +#: simulation/templates/structures/iber_monument.xml:34 msgid "Revered Monument" msgstr "" -#: simulation/templates/structures/iber_monument.xml:33 +#: simulation/templates/structures/iber_monument.xml:35 msgid "Gur Oroigarri" msgstr "" -#: simulation/templates/structures/iber_monument.xml:36 +#: simulation/templates/structures/iber_monument.xml:38 msgid "" "All units within vision of this monument will fight harder. Buildings in the " "territory of the monument do not decay." @@ -913,7 +912,10 @@ msgid "Loki" msgstr "" -#: simulation/templates/structures/iber_wall.xml:9 +#: simulation/templates/structures/iber_wall_gate.xml:9 +msgid "Biko Sarbide" +msgstr "" + #: simulation/templates/structures/iber_wall_long.xml:29 #: simulation/templates/structures/iber_wall_medium.xml:23 #: simulation/templates/structures/iber_wall_short.xml:10 @@ -921,10 +923,6 @@ msgid "Zabal Horma" msgstr "" -#: simulation/templates/structures/iber_wall_gate.xml:9 -msgid "Biko Sarbide" -msgstr "" - #: simulation/templates/structures/iber_wonder.xml:9 msgid "Cancho Roano" msgstr "" @@ -1027,19 +1025,19 @@ msgid "Uparaksana" msgstr "" -#: simulation/templates/structures/maur_pillar_ashoka.xml:7 +#: simulation/templates/structures/maur_pillar_ashoka.xml:9 msgid "Pillar" msgstr "" -#: simulation/templates/structures/maur_pillar_ashoka.xml:32 +#: simulation/templates/structures/maur_pillar_ashoka.xml:34 msgid "Edict Pillar of Ashoka" msgstr "" -#: simulation/templates/structures/maur_pillar_ashoka.xml:33 +#: simulation/templates/structures/maur_pillar_ashoka.xml:35 msgid "Śāsana Stambha Aśokā" msgstr "" -#: simulation/templates/structures/maur_pillar_ashoka.xml:36 +#: simulation/templates/structures/maur_pillar_ashoka.xml:38 msgid "" "The famous pillar of Ashoka. Increases the walk speed of traders. Buildings " "in the territory of the monument do not decay." @@ -1053,7 +1051,10 @@ msgid "Devalaya" msgstr "" -#: simulation/templates/structures/maur_wall.xml:9 +#: simulation/templates/structures/maur_wall_gate.xml:9 +msgid "Dwara" +msgstr "" + #: simulation/templates/structures/maur_wall_long.xml:35 #: simulation/templates/structures/maur_wall_medium.xml:29 #: simulation/templates/structures/maur_wall_short.xml:16 @@ -1061,10 +1062,6 @@ msgid "Shilabanda" msgstr "" -#: simulation/templates/structures/maur_wall_gate.xml:9 -msgid "Dwara" -msgstr "" - #: simulation/templates/structures/maur_wall_tower.xml:18 msgid "Puratta" msgstr "" @@ -1170,15 +1167,15 @@ msgid "Huvādā" msgstr "" -#: simulation/templates/structures/pers_ishtar_gate.xml:28 +#: simulation/templates/structures/pers_ishtar_gate.xml:30 msgid "Persian Special Building" msgstr "" -#: simulation/templates/structures/pers_ishtar_gate.xml:29 +#: simulation/templates/structures/pers_ishtar_gate.xml:31 msgid "Ishtar Gate of Babylon" msgstr "" -#: simulation/templates/structures/pers_ishtar_gate.xml:30 +#: simulation/templates/structures/pers_ishtar_gate.xml:32 msgid "" "Increases the loyalty regeneration of nearby structures, making them harder " "to capture." @@ -1220,7 +1217,10 @@ msgid "Ayadana" msgstr "" -#: simulation/templates/structures/pers_wall.xml:9 +#: simulation/templates/structures/pers_wall_gate.xml:9 +msgid "Duvitaparnam" +msgstr "" + #: simulation/templates/structures/pers_wall_long.xml:29 #: simulation/templates/structures/pers_wall_medium.xml:23 #: simulation/templates/structures/pers_wall_short.xml:10 @@ -1228,10 +1228,6 @@ msgid "Para" msgstr "" -#: simulation/templates/structures/pers_wall_gate.xml:9 -msgid "Duvitaparnam" -msgstr "" - #: simulation/templates/structures/pers_wonder.xml:12 msgid "Hanging Gardens of Babylon" msgstr "" @@ -1358,15 +1354,19 @@ msgid "Special Imperial Roman building." msgstr "" -#: simulation/templates/structures/rome_army_camp.xml:59 +#: simulation/templates/structures/rome_army_camp.xml:35 +msgid "ArmyCamp" +msgstr "" + +#: simulation/templates/structures/rome_army_camp.xml:68 msgid "Entrenched Army Camp" msgstr "" -#: simulation/templates/structures/rome_army_camp.xml:60 +#: simulation/templates/structures/rome_army_camp.xml:69 msgid "Castrum Vallum" msgstr "" -#: simulation/templates/structures/rome_army_camp.xml:63 +#: simulation/templates/structures/rome_army_camp.xml:72 msgid "" "Build anywhere on the map, even in enemy territory. Construct siege weapons " "and train citizen-soldiers. Heal garrisoned units slowly." @@ -1476,11 +1476,11 @@ msgid "Train healers. Garrison units to heal them at a quick rate." msgstr "" -#: simulation/templates/structures/rome_temple_vesta.xml:15 +#: simulation/templates/structures/rome_temple_vesta.xml:17 msgid "Temple of Vesta" msgstr "" -#: simulation/templates/structures/rome_temple_vesta.xml:17 +#: simulation/templates/structures/rome_temple_vesta.xml:19 msgid "Aedes Vestae" msgstr "" @@ -1496,7 +1496,10 @@ msgid "A temporary shelter for soldiers." msgstr "" -#: simulation/templates/structures/rome_wall.xml:9 +#: simulation/templates/structures/rome_wall_gate.xml:9 +msgid "Porta" +msgstr "" + #: simulation/templates/structures/rome_wall_long.xml:29 #: simulation/templates/structures/rome_wall_medium.xml:23 #: simulation/templates/structures/rome_wall_short.xml:10 @@ -1504,10 +1507,6 @@ msgid "Moenia" msgstr "" -#: simulation/templates/structures/rome_wall_gate.xml:9 -msgid "Porta" -msgstr "" - #: simulation/templates/structures/rome_wall_tower.xml:9 msgid "Turris Lapidea" msgstr "" @@ -1516,7 +1515,7 @@ msgid "Murus Latericius" msgstr "" -#: simulation/templates/structures/rome_wonder.xml:13 +#: simulation/templates/structures/rome_wonder.xml:15 msgid "Aedes Iovis Optimi Maximi" msgstr "" Index: binaries/data/mods/public/l10n/public-templates-other.pot =================================================================== --- binaries/data/mods/public/l10n/public-templates-other.pot +++ binaries/data/mods/public/l10n/public-templates-other.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: 0 A.D. — Empires Ascendant\n" -"POT-Creation-Date: 2017-04-28 09:02+0200\n" -"PO-Revision-Date: 2017-04-28 09:02+0200\n" +"POT-Creation-Date: 2017-05-08 09:03+0200\n" +"PO-Revision-Date: 2017-05-08 09:03+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -1034,11 +1034,6 @@ "other civs" msgstr "" -#: simulation/templates/special/marker_object_sound.xml:15 -#: simulation/templates/special/marker_object_sound.xml:16 -msgid "Marker" -msgstr "" - #: simulation/templates/special/player.xml:57 msgid "Player" msgstr "" Index: binaries/data/mods/public/maps/random/botswanan_haven.js =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/botswanan_haven.js @@ -0,0 +1,476 @@ +RMS.LoadLibrary("rmgen"); + +const tGrassA = "savanna_shrubs_a_wetseason"; +const tGrassB = "savanna_shrubs_a"; +const tCliff = "savanna_cliff_a"; +const tHill = "savanna_grass_a_wetseason"; +const tMud = "savanna_mud_a"; +const tShoreBlend = "savanna_grass_b_wetseason"; +const tShore = "savanna_riparian_wet"; +const tWater = "savanna_mud_a"; +const tCityTile = "savanna_tile_a"; + +const oBush = "gaia/flora_bush_temperate"; +const oBaobab = "gaia/flora_tree_baobab"; +const oToona = "gaia/flora_tree_toona"; +const oBerryBush = "gaia/flora_bush_berry"; +const oGazelle = "gaia/fauna_gazelle"; +const oZebra = "gaia/fauna_zebra"; +const oWildebeest = "gaia/fauna_wildebeest"; +const oLion = "gaia/fauna_lion"; +const oRhino = "gaia/fauna_rhino"; +const oCrocodile = "gaia/fauna_crocodile"; +const oElephant = "gaia/fauna_elephant_north_african"; +const oElephantInfant = "gaia/fauna_elephant_african_infant"; +const oLioness = "gaia/fauna_lioness"; +const oRabbit = "gaia/fauna_rabbit"; +const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; +const oStoneSmall = "gaia/geology_stone_savanna_small"; +const oMetalLarge = "gaia/geology_metal_savanna_slabs"; + +const aGrass = "actor|props/flora/grass_field_lush_tall.xml"; +const aGrass2 = "actor|props/flora/grass_tropic_field_tall.xml"; +const aGrassShort = "actor|props/flora/grass_soft_large.xml"; +const aRockLarge = "actor|geology/stone_granite_med.xml"; +const aRockMedium = "actor|geology/stone_granite_med.xml"; +const aReeds = "actor|props/flora/reeds_pond_lush_a.xml"; +const aReeds2 = "actor|props/flora/reeds_pond_lush_b.xml"; +const aLillies = "actor|props/flora/water_lillies.xml"; +const aBushMedium = "actor|props/flora/bush_tropic_b.xml"; +const aBushSmall = "actor|props/flora/bush_tropic_a.xml"; +const aShrub = "actor|props/flora/shrub_tropic_plant_flower.xml"; +const aFlower = "actor|props/flora/flower_bright.xml"; +const aPalm = "actor|props/flora/shrub_fanpalm.xml"; + +log("Initializing map..."); +InitMap(); + +const numPlayers = getNumPlayers(); +const mapSize = getMapSize(); + +var clPlayer = createTileClass(); +var clHill = createTileClass(); +var clWater = createTileClass(); +var clDirt = createTileClass(); +var clRock = createTileClass(); +var clMetal = createTileClass(); +var clForest = createTileClass(); +var clFood = createTileClass(); +var clBaseResource = createTileClass(); + +// Randomize player order +var playerIDs = []; +for (let i = 0; i < numPlayers; ++i) + playerIDs.push(i + 1); +playerIDs = sortPlayers(playerIDs); + +var startAngle = randFloat(0, 2 * PI); +for (let i = 0; i < numPlayers; ++i) +{ + let playerAngle = startAngle + i * 2 * PI / numPlayers; + let id = playerIDs[i]; + log("Creating base for player " + id + "..."); + let radius = scaleByMapSize(15, 25); + + // Get the x and z in tiles + let fx = fractionToTiles(0.5 + 0.35 * Math.cos(playerAngle)); + let fz = fractionToTiles(0.5 + 0.35 * Math.sin(playerAngle)); + let ix = Math.round(fx); + let iz = Math.round(fz); + + addToClass(ix, iz, clPlayer); + addToClass(ix+5, iz, clPlayer); + addToClass(ix, iz+5, clPlayer); + addToClass(ix-5, iz, clPlayer); + addToClass(ix, iz-5, clPlayer); + + // Create the city patch + let cityRadius = radius / 3; + createArea( + new ClumpPlacer(PI * cityRadius * cityRadius, 0.6, 0.3, 10, ix, iz), + new LayeredPainter([tCityTile, tCityTile], [1]), + null); + + placeCivDefaultEntities(fx, fz, id); + + placeDefaultChicken(fx, fz, clBaseResource); + + // Create berry bushes + let bbAngle = randFloat(0, 2 * PI); + let bbDist = 12; + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oBerryBush, 5, 5, 0, 3)], + true, + clBaseResource, + Math.round(fx + bbDist * Math.cos(bbAngle)), + Math.round(fz + bbDist * Math.sin(bbAngle))), + 0); + + // Create metal mine + let mAngle = bbAngle; + while (abs(mAngle - bbAngle) < PI/3) + mAngle = randFloat(0, 2 * PI); + + let mDist = radius - 4; + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oMetalLarge, 1, 1, 0, 0)], + true, + clBaseResource, + Math.round(fx + mDist * Math.cos(mAngle)), + Math.round(fz + mDist * Math.sin(mAngle))), + 0); + + // Create stone mine + mAngle += randFloat(PI/8, PI/4); + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oStoneLarge, 1, 1, 0, 2)], + true, + clBaseResource, + Math.round(fx + mDist * Math.cos(mAngle)), + Math.round(fz + mDist * Math.sin(mAngle))), + 0); + + // Create starting trees + let hillSize = PI * radius * radius; + let num = Math.floor(hillSize / 100); + let tAngle = randFloat(-PI/3, 4 * PI/3); + let tDist = 12; + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oBaobab, num, num, 0, 5)], + false, + clBaseResource, + Math.round(fx + tDist * Math.cos(tAngle)), + Math.round(fz + tDist * Math.sin(tAngle))), + 0, + avoidClasses(clBaseResource, 2)); + + // Create grass tufts + num = hillSize / 250; + for (let j = 0; j < num; ++j) + { + let gAngle = randFloat(0, 2 * PI); + let gDist = radius - randIntInclusive(5, 11); + createObjectGroup( + new SimpleGroup( + [new SimpleObject(aGrassShort, 2, 5, 0, 1, -PI/8, PI/8)], + false, + clBaseResource, + Math.round(fx + gDist * Math.cos(gAngle)), + Math.round(fz + gDist * Math.sin(gAngle))), + 0); + } +} +RMS.SetProgress(15); + +log("Creating bumps..."); +createAreas( + new ClumpPlacer(scaleByMapSize(20, 50), 0.6, 0.1, 1), + new SmoothElevationPainter(ELEVATION_MODIFY, 2, 2), + avoidClasses(clPlayer, 13), + scaleByMapSize(300, 800)); + +log("Creating hills..."); +createAreas( + new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.5), + [ + new LayeredPainter([tCliff, tHill], [2]), + new SmoothElevationPainter(ELEVATION_SET, 15, 2), + paintClass(clHill) + ], + avoidClasses(clPlayer, 20, clHill, 15, clWater, 0), + scaleByMapSize(1, 4) * numPlayers * 3); + +log("Creating marshes..."); +for (let i = 0; i < 2; ++i) + createAreas( + new ChainPlacer(1, Math.floor(scaleByMapSize(6, 12)), Math.floor(scaleByMapSize(15, 60)), 0.8), + [ + new LayeredPainter([tShoreBlend, tShore, tWater], [1, 1]), + new SmoothElevationPainter(ELEVATION_SET, -2, 3), + paintClass(clWater) + ], + avoidClasses(clPlayer, 25, clWater, Math.round(scaleByMapSize(7, 16) * randFloat(0.8, 1.35))), + scaleByMapSize(4, 20)); + +log("Creating reeds..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(aReeds, 20, 40, 0, 4), + new SimpleObject(aReeds2, 20, 40, 0, 4), + new SimpleObject(aLillies, 10, 30, 0, 4) + ], + true), + 0, + stayClasses(clWater, 1), + scaleByMapSize(400, 1000), + 100); +RMS.SetProgress(40); + +log("Creating bumps..."); +createAreas( + new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), + new SmoothElevationPainter(ELEVATION_MODIFY, 1, 2), + stayClasses(clWater, 2), + scaleByMapSize(50, 100)); + +log("Creating mud patches..."); +for (let size of [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)]) + createAreas( + new ChainPlacer(2, Math.floor(scaleByMapSize(3, 6)), size, 1), + [ + new LayeredPainter([tGrassA, tGrassB, tMud], [1, 1]), + paintClass(clDirt) + ], + avoidClasses(clWater, 1, clHill, 0, clDirt, 5, clPlayer, 8), + scaleByMapSize(15, 45)); +RMS.SetProgress(50); + +log("Creating stone mines..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(oStoneSmall, 0, 2, 0, 4), + new SimpleObject(oStoneLarge, 1, 1, 0, 4) + ], + true, + clRock), + 0, + [avoidClasses(clWater, 0, clPlayer, 20, clRock, 10, clHill, 1)], + scaleByMapSize(4, 16), + 100); + +createObjectGroups( + new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock), + 0, + [avoidClasses(clWater, 0, clPlayer, 20, clRock, 10, clHill, 1)], + scaleByMapSize(4, 16), + 100); + +log("Creating metal mines..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), + 0, + [avoidClasses(clWater, 0, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 1)], + scaleByMapSize(4, 16), + 100); +RMS.SetProgress(60); + +log("Creating small decorative rocks..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true), + 0, + avoidClasses(clPlayer, 1), + scaleByMapSize(16, 262), + 50); +RMS.SetProgress(65); + +log("Creating large decorative rocks..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(aRockLarge, 1, 2, 0, 1), + new SimpleObject(aRockMedium, 1, 3, 0, 2) + ], + true), + 0, + avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), + scaleByMapSize(8, 131), + 50); +RMS.SetProgress(70); + +log("Creating lions..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(oLion, 0, 1, 0, 4), + new SimpleObject(oLioness, 2, 3, 0, 4) + ], + true, + clFood), + 0, + avoidClasses(clWater, 1, clPlayer, 20, clFood, 11, clHill, 1), + scaleByMapSize(4, 12), + 50); + +log("Creating zebras..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oZebra, 4, 6, 0, 4)], + true, + clFood), + 0, + avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 1, clFood, 13), + 3 * numPlayers, + 50); + +log("Creating wildebeest..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oWildebeest, 2, 4, 0, 4)], + true, + clFood), + 0, + avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 1, clFood, 13), + 3 * numPlayers, + 50); + +log("Creating crocodiles..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oCrocodile, 2, 3, 0, 4)], + true, + clFood), + 0, + [avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clFood, 13), stayClasses(clWater, 3)], + 5 * numPlayers, + 200); + +log("Creating gazelles..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oGazelle, 4, 6, 0, 4)], + true, + clFood), + 0, + avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 1, clFood, 13), + 3 * numPlayers, + 50); +RMS.SetProgress(75); + +log("Creating rabbits..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oRabbit, 6, 8, 0, 2)], + true, + clFood), + 0, + avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 1, clFood, 13), + 6 * numPlayers, + 50); + +log("Creating rhinos..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oRhino, 1, 1, 0, 2)], + true, + clFood), + 0, + avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 1, clFood, 13), + 3 * numPlayers, + 50); + +log("Creating elephants..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oElephant, 2, 3, 0, 4), new SimpleObject(oElephantInfant, 1, 1, 0, 4)], + true, + clFood), + 0, + avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 1, clFood, 13), + 3 * numPlayers, + 50); + +log("Creating berry bushes..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oBerryBush, 5, 7, 0, 4)], + true, + clFood), + 0, + avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), + randIntInclusive(1, 4) * numPlayers + 2, + 50); +RMS.SetProgress(80); + +log("Creating straggler trees..."); +let treeTypes = [oToona, oBaobab, oBush, oBush]; +for (let treeType of treeTypes) + createObjectGroups( + new SimpleGroup( + [new SimpleObject(treeType, 1, 3, 0, 3)], + true, + clForest), + 0, + avoidClasses(clForest, 1, clWater, 1, clHill, 1, clPlayer, 13, clMetal, 1, clRock, 1), + Math.floor(scaleByMapSize(60, 500) / treeTypes.length)); +RMS.SetProgress(85); + +log("Creating small grass tufts..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1, -PI/8, PI/8)]), + 0, + avoidClasses(clWater, 2, clPlayer, 13, clDirt, 0), + scaleByMapSize(13, 200)); +RMS.SetProgress(90); + +log("Creating large grass tufts..."); +createObjectGroups( + new SimpleGroup([ + new SimpleObject(aGrass, 2, 4, 0, 1.8, -PI/8, PI/8), + new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5, -PI/8, PI/8) + ]), + 0, + avoidClasses(clWater, 3, clPlayer, 13, clDirt, 1, clForest, 0), + scaleByMapSize(13, 200)); +RMS.SetProgress(95); + +log("Creating bushes..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(aBushMedium, 1, 2, 0, 2), + new SimpleObject(aBushSmall, 2, 4, 0, 2) + ]), + 0, + avoidClasses(clWater, 1, clPlayer, 13, clDirt, 1), + scaleByMapSize(13, 200), + 50); + +log("Creating flowering shrubs..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(aShrub, 1, 1, 0, 2)]), + 0, + avoidClasses(clWater, 1, clPlayer, 13, clDirt, 1), + scaleByMapSize(13, 200), + 50); + +log("Creating decorative palms..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(aPalm, 1, 3, 0, 2)]), + 0, + avoidClasses(clWater, 2, clPlayer, 12, clDirt, 1), + scaleByMapSize(13, 200), + 50); + +log("Creating shrubs,flowers and other decorations..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(aFlower, 0, 6, 0, 2), + new SimpleObject(aGrass2, 2, 5, 0, 2) + ]), + 0, + avoidClasses(clWater, 1, clHill, 1, clPlayer, 13, clDirt, 1), + scaleByMapSize(13, 200), + 50); + +setSkySet("cirrus"); +setWaterColor(0.553, 0.635, 0.345); +setWaterTint(0.161, 0.514, 0.635); +setWaterMurkiness(0.8); +setWaterWaviness(1.0); +setWaterType("clap"); + +setFogThickness(0.25); +setFogFactor(0.6); + +setPPEffect("hdr"); +setPPSaturation(0.44); +setPPBloom(0.3); + +ExportMap(); Index: binaries/data/mods/public/maps/random/botswanan_haven.json =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/botswanan_haven.json @@ -0,0 +1,12 @@ +{ + "settings" : { + "Name" : "Botswanan Haven", + "Script" : "botswanan_haven.js", + "Description" : "Botswanan Africa during the wet season, a land which was arid and inhospitable just weeks before has come to life totally transformed. Herds of zebras graze amid the tall, lush grasses in which lions lie waiting, while in the shallow pools lurk fearsome crocodiles.", + "BaseTerrain" : ["savanna_grass_b_wetseason"], + "BaseHeight" : 3, + "Keywords": ["new"], + "CircularMap" : true, + "Preview" : "botswanan_haven.png" + } +} Index: binaries/data/mods/public/maps/random/extinct_volcano.js =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/extinct_volcano.js @@ -0,0 +1,513 @@ +RMS.LoadLibrary("rmgen"); + +const tHillDark = "cliff volcanic light"; +const tHillMedium1 = "ocean_rock_a"; +const tHillMedium2 = "ocean_rock_b"; +const tHillVeryDark = ["cliff volcanic coarse", "cave_walls"]; +const tRoad = "road1"; +const tRoadWild = "road1"; +const tForestFloor1 = tHillMedium1; +const tForestFloor2 = tHillMedium2; +const tGrassA = "cliff volcanic light"; +const tGrassB = "ocean_rock_a"; +const tGrass3 = "temp_grass_plants"; +const tGrassPatchBlend = "temp_grass_long_b"; +const tGrassPatch = ["temp_grass_d", "temp_grass_clovers"]; +const tShoreBlend = "cliff volcanic light"; +const tShore = "ocean_rock_a"; +const tWater = "ocean_rock_b"; + +// Gaia entities +const oTree = "gaia/flora_tree_dead"; +const oTree2 = "gaia/flora_tree_euro_beech"; +const oTree3 = "gaia/flora_tree_oak"; +const oTree4 = "gaia/flora_tree_oak_dead"; +const oBush = "gaia/flora_bush_temperate"; +const oFruitBush = "gaia/flora_bush_berry"; +const oRabbit = "gaia/fauna_rabbit"; +const oGoat = "gaia/fauna_goat"; +const oBear = "gaia/fauna_bear"; +const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; +const oStoneSmall = "gaia/geology_stone_temperate"; +const oMetalLarge = "gaia/geology_metal_temperate_slabs"; +const oTower = "other/palisades_rocks_fort"; + +// Decorative props +const aRockLarge = "actor|geology/stone_granite_med.xml"; +const aRockMedium = "actor|geology/stone_granite_med.xml"; +const aBushMedium = "actor|props/flora/bush_tempe_me.xml"; +const aBushSmall = "actor|props/flora/bush_tempe_sm.xml"; +const aGrass = "actor|props/flora/grass_soft_large_tall.xml"; +const aGrassShort = "actor|props/flora/grass_soft_large.xml"; + +const pForestD = [ + tForestFloor1 + TERRAIN_SEPARATOR + oTree, + tForestFloor2 + TERRAIN_SEPARATOR + oTree2, + tForestFloor1 +]; + +const pForestP = [ + tForestFloor1 + TERRAIN_SEPARATOR + oTree3, + tForestFloor2 + TERRAIN_SEPARATOR + oTree4, + tForestFloor1 +]; + +log("Initializing map..."); +InitMap(); + +var P_FOREST = 0.7; +var totalTrees = scaleByMapSize(1200, 3000); +var numForest = totalTrees * P_FOREST; +var numStragglers = totalTrees * (1.0 - P_FOREST); + +var numPlayers = getNumPlayers(); +var mapSize = getMapSize(); +var mapArea = mapSize * mapSize; + +// create tile classes +var clPlayer = createTileClass(); +var clHill = createTileClass(); +var clFood = createTileClass(); +var clForest = createTileClass(); +var clWater = createTileClass(); +var clDirt = createTileClass(); +var clGrass = createTileClass(); +var clRock = createTileClass(); +var clMetal = createTileClass(); +var clBaseResource = createTileClass(); +var clBumps = createTileClass(); +var clTower = createTileClass(); + +var ccMountainHeight = 25; + +// randomize player order +var playerIDs = []; +for (let i = 0; i < numPlayers; ++i) + playerIDs.push(i+1); +playerIDs = sortPlayers(playerIDs); + +// Place players +var startAngle = randFloat(0, 2 * PI); +for (let i = 0; i < numPlayers; ++i) +{ + let playerAngle = startAngle + i * 2 * PI / numPlayers; + let playerX = 0.5 + 0.35 * Math.cos(playerAngle); + let playerZ = 0.5 + 0.35 * Math.sin(playerAngle); + + let id = playerIDs[i]; + log("Creating base for player " + id + "..."); + let radius = scaleByMapSize(15, 25); + + let fx = fractionToTiles(playerX); + let fz = fractionToTiles(playerZ); + let ix = Math.round(fx); + let iz = Math.round(fz); + + // This one consists of many bumps, creating an omnidirectional ramp + createMountain( + ccMountainHeight, + Math.floor(scaleByMapSize(15, 15)), + Math.floor(scaleByMapSize(15, 15)), + Math.floor(scaleByMapSize(4, 10)), + avoidClasses(), + ix, + iz, + tHillDark, + clPlayer, + 14); + + // Flatten the initial CC area + let hillSize = PI * radius * radius; + createArea( + new ClumpPlacer(hillSize, 0.95, 0.6, 10, ix, iz), + [ + new LayeredPainter([tHillVeryDark, tHillMedium1], [radius]), + new SmoothElevationPainter(ELEVATION_SET, ccMountainHeight, radius), + paintClass(clPlayer) + ], + null); + + // Create the city patch + let cityRadius = radius / 3; + createArea( + new ClumpPlacer(PI * cityRadius * cityRadius, 0.6, 0.3, 10, ix, iz), + new LayeredPainter([tRoadWild, tRoad], [1]), + null); + + placeCivDefaultEntities(fx, fz, id, { 'iberWall': 'towers' }); + + // Create metal mine + let mAngle = randFloat(0, 2 * PI); + let mDist = 12; + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oMetalLarge, 1, 1, 0, 0)], + true, + clBaseResource, + Math.round(fx + mDist * Math.cos(mAngle)), + Math.round(fz + mDist * Math.sin(mAngle))), + 0); + + // Create stone mines + mAngle += randFloat(PI/4, PI/3); + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oStoneLarge, 1, 1, 0, 2)], + true, + clBaseResource, + Math.round(fx + mDist * Math.cos(mAngle)), + Math.round(fz + mDist * Math.sin(mAngle))), + 0); + + placeDefaultChicken(fx, fz, clBaseResource); + + // Create berry bushes + mAngle += randFloat(PI/4, PI/2); + let bbDist = 12; + createObjectGroup( + new SimpleGroup( + [new SimpleObject(oFruitBush, 5, 5, 0, 3)], + true, + clBaseResource, + Math.round(fx + bbDist * Math.cos(mAngle)), + Math.round(fz + bbDist * Math.sin(mAngle))), + 0); + + // Create starting trees + let num = Math.floor(hillSize / 60); + let tries = 10; + for (let x = 0; x < tries; ++x) + { + let tAngle = mAngle + randFloat(PI/3, PI/4); + let tDist = randFloat(10, 12); + if (createObjectGroup( + new SimpleGroup( + [new SimpleObject(oTree2, num, num, 0, 3)], + false, + clBaseResource, + Math.round(fx + tDist * Math.cos(tAngle)), + Math.round(fz + tDist * Math.sin(tAngle))), + 0, + avoidClasses(clBaseResource, 3))) + { + break; + } + } +} +RMS.SetProgress(15); + +createVolcano(0.5, 0.5, clHill, tHillVeryDark, undefined, false, ELEVATION_SET); +RMS.SetProgress(20); + +log("Creating lakes..."); +createAreas( + new ChainPlacer(5, 6, Math.floor(scaleByMapSize(10, 14)), 0.1), + [ + new LayeredPainter([tShoreBlend, tShore, tWater], [1, 1]), + new SmoothElevationPainter(ELEVATION_SET, -4, 3), + paintClass(clWater) + ], + avoidClasses(clPlayer, 0, clHill, 2, clWater, 12), + Math.round(scaleByMapSize(6, 12))); +RMS.SetProgress(25); + +createBumps(avoidClasses(clPlayer, 0, clHill, 0), scaleByMapSize(50, 300), 1, 10, 3, 0, scaleByMapSize(4, 10)); +paintTileClassBasedOnHeight(10, 100, 0, clBumps); +RMS.SetProgress(30); + +log("Creating hills..."); +createAreas( + new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), + [ + new LayeredPainter([tHillDark, tHillDark, tHillDark], [2, 2]), + new SmoothElevationPainter(ELEVATION_SET, 18, 2), + paintClass(clHill) + ], + avoidClasses(clPlayer, 0, clHill, 15, clWater, 2, clBaseResource, 2), + scaleByMapSize(2, 8) * numPlayers); +RMS.SetProgress(35); + +log("Creating forests..."); +var types = [ + [[tGrassB, tGrassA, pForestD], [tGrassB, pForestD]], + [[tGrassB, tGrassA, pForestP], [tGrassB, pForestP]] +]; +var size = numForest / (scaleByMapSize(4, 12) * numPlayers); +var num = Math.floor(size / types.length); +for (let i = 0; i < types.length; ++i) + createAreas( + new ClumpPlacer(numForest / num, 0.1, 0.1, 1), + [ + new LayeredPainter(types[i], [2]), + paintClass(clForest) + ], + avoidClasses( + clPlayer, 4, + clForest, 10, + clHill, 0, + clWater, 2), + num); +RMS.SetProgress(40); + +log("Creating hill patches..."); +for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) + for (let type of [[tHillMedium1, tHillDark], [tHillDark, tHillMedium2], [tHillMedium1, tHillMedium2]]) + createAreas( + new ClumpPlacer(size, 0.3, 0.06, 0.5), + [ + new LayeredPainter(type, [1]), + paintClass(clGrass) + ], + avoidClasses( + clWater, 3, + clForest, 0, + clHill, 0, + clBumps, 0, + clPlayer, 0), + scaleByMapSize(20, 80)); +RMS.SetProgress(45); + +log("Creating grass patches..."); +createLayeredPatches( + [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], + [tGrassPatchBlend, tGrassPatch], + [1], + avoidClasses( + clWater, 1, + clForest, 0, + clHill, 0, + clGrass, 5, + clBumps, 0, + clPlayer, 0), + clDirt); +RMS.SetProgress(50); + +log("Creating stone mines..."); +createObjectGroups( + new SimpleGroup( + [ + new SimpleObject(oStoneSmall, 0, 2, 0, 4), + new SimpleObject(oStoneLarge, 1, 1, 0, 4) + ], + true, + clRock), + 0, + [ + stayClasses(clBumps, 1), + avoidClasses( + clWater, 3, + clForest, 1, + clPlayer, 0, + clRock, 15, + clHill, 0) + ], + 100, + 100); +RMS.SetProgress(55); + +log("Creating small stone quarries..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock), + 0, + [ + stayClasses(clBumps, 1), + avoidClasses( + clWater, 3, + clForest, 1, + clPlayer, 0, + clRock, 15, + clHill, 0) + ], + 100, + 100); +RMS.SetProgress(60); + +log("Creating metal mines..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), + 0, + [ + stayClasses(clBumps, 1), + avoidClasses( + clWater, 3, + clForest, 1, + clPlayer, 0, + clMetal, 15, + clRock, 10, + clHill, 0) + ], + 100, + 100); +RMS.SetProgress(65); + +log("Creating towers..."); +createObjectGroups( + new SimpleGroup([new SimpleObject(oTower, 1, 1, 0, 4)], true, clTower), + 0, + [ + stayClasses(clBumps, 3), + avoidClasses( + clMetal, 5, + clRock, 5, + clHill, 0, + clTower, 60, + clPlayer, 10, + clForest, 2) + ], + 500, + 1); +RMS.SetProgress(67); + +createDecoration( + [ + [new SimpleObject(aGrassShort, 1, 2, 0, 1)], + [ + new SimpleObject(aGrass, 2, 4, 0, 1.8), + new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5) + ], + [ + new SimpleObject(aBushMedium, 1, 2, 0, 2), + new SimpleObject(aBushSmall, 2, 4, 0, 2) + ] + ], + [ + scaleByMapSize(15, 200), + scaleByMapSize(15, 200), + scaleByMapSize(15, 200) + ], + [ + stayClasses(clGrass, 0), + avoidClasses( + clWater, 0, + clForest, 0, + clPlayer, 0, + clHill, 0) + ]); +RMS.SetProgress(70); + +createDecoration( + [ + [ + new SimpleObject(aRockMedium, 1, 3, 0, 1) + ], + [ + new SimpleObject(aRockLarge, 1, 2, 0, 1), + new SimpleObject(aRockMedium, 1, 3, 0, 2) + ] + ], + [ + scaleByMapSize(15, 250), + scaleByMapSize(15, 150) + ], + avoidClasses( + clWater, 0, + clForest, 0, + clPlayer, 0, + clHill, 0 + )); +RMS.SetProgress(75); + +createFood( + [ + [new SimpleObject(oRabbit, 5, 7, 2, 4)], + [new SimpleObject(oGoat, 3, 5, 2, 4)] + ], + [ + scaleByMapSize(1, 6) * numPlayers, + scaleByMapSize(3, 10) * numPlayers + ], + [ + avoidClasses( + clWater, 1, + clForest, 0, + clPlayer, 0, + clHill, 1, + clFood, 20) + ], + clFood); +RMS.SetProgress(78); + +createFood( + [ + [new SimpleObject(oBear, 1, 1, 0, 2)] + ], + [ + 3 * numPlayers + ], + [ + avoidClasses( + clWater, 1, + clForest, 0, + clPlayer, 0, + clHill, 1, + clFood, 20 + ), + stayClasses(clForest, 2) + ], + clFood); +RMS.SetProgress(81); + +createFood( + [ + [new SimpleObject(oFruitBush, 1, 2, 0, 4)] + ], + [ + 3 * numPlayers + ], + [stayClasses(clGrass, 1), avoidClasses(clWater, 1, clForest, 0, clPlayer, 0, clHill, 1, clFood, 10)], + clFood); +RMS.SetProgress(85); + +log("Creating straggler trees and bushes..."); +var types = [oTree, oTree2, oTree3, oTree4, oBush]; +var num = Math.floor(numStragglers / types.length); +for (let type of types) + createObjectGroups( + new SimpleGroup( + [new SimpleObject(type, 1, 1, 0, 3)], + true, + clForest), + 0, + [ + stayClasses(clGrass, 1), + avoidClasses( + clWater, 5, + clForest, 1, + clHill, 1, + clPlayer, 0, + clMetal, 1, + clRock, 1) + ], + num); + +RMS.SetProgress(90); + +log("Creating straggler bushes..."); +createObjectGroups( + new SimpleGroup( + [new SimpleObject(oBush, 1, 3, 0, 3)], + true, + clForest + ), + 0, + [ + stayClasses(clGrass, 3), + avoidClasses( + clWater, 1, + clForest, 1, + clPlayer, 0, + clMetal, 1, + clRock, 1) + ], + numStragglers); +RMS.SetProgress(95); + +setWaterType("lake"); +setWaterWaviness(2); +setWaterColor(0.035, 0.047, 0.05); +setWaterTint(0.058, 0.05, 0.035); +setWaterMurkiness(0.9); + +setPPEffect("hdr"); + +ExportMap(); Index: binaries/data/mods/public/maps/random/extinct_volcano.json =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/extinct_volcano.json @@ -0,0 +1,19 @@ +{ + "settings" : { + "Name" : "Extinct Volcano", + "Script" : "extinct_volcano.js", + "Description" : "[color=\"red\"]IMPORTANT NOTE: AI PLAYERS DO NOT WORK WITH THIS MAP[/color]\n\nA once fertile valley... desolated by the eruption of the long-dormant volcano in the heart of the region. Following years of empty, scorched deadness, signs of life started reappearing and spreading. Now the land is half-way to the full lushness of its former era. Alas, it is not to be: following a long stretch of drought, interminable rains have set in in the higher regions to the north. Water levels are rising at drastic levels, slowly forcing players to seek the high ground of the lesser, extinct volcanoes or the now again dormant great cone.", + "DisabledTemplates": [ + "structures/ptol_lighthouse" + ], + "BaseTerrain" : "ocean_rock_a", + "BaseHeight" : 1, + "Keywords": ["new", "trigger"], + "CircularMap" : true, + "Preview" : "extinctvolcano.png", + "TriggerScripts": [ + "scripts/TriggerHelper.js", + "random/extinct_volcano_triggers.js" + ] + } +} Index: binaries/data/mods/public/maps/random/extinct_volcano_triggers.js =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/extinct_volcano_triggers.js @@ -0,0 +1,200 @@ +/** + * Whether to log the water levels and which units became killed or transformed to visual actors. + */ +var debugLog = false; + +/** + * Whether to rise the water to the maximum level in a minute or two. + */ +var debugWaterRise = false; + +/** + * Time in minutes when the water level starts to rise. + * Allow players to build up the economy and military for some time. + */ +var waterRiseStartTime = [22, 26]; + +/** + * Duration in minutes for which the notification will be shown that states that the water will rise soon. + */ +var waterRiseNotificationDuration = 1; + +/** + * Time in minutes between increases of the water level. + * If the water rises too fast, the hills are of no strategic importance, + * building structures would be pointless. + * + * At height 27, most trees are not gatherable anymore and enemies not reachable. + * At height 37 most hills are barely usable. + * + * At min 30 stuff at the ground level should not be gatherable anymore. + * At min 45 CC should be destroyed. + * + * Notice regular and military docks will raise with the water! + */ +var waterIncreaseTime = [0.5, 1]; + +/** + * Number of meters the waterheight increases each step. + * Each time the water level is changed, the pathfinder grids have to be recomputed. + * Therefore raising the level should occur as rarely as possible, i.e. have the value + * as big as possible, but as small as needed to keep it visually authentic. + */ +var waterLevelIncreaseHeight = 1; + +/** + * At which height to stop increasing the water level. + * Since players can survive on ships, don't endlessly raise the water. + */ +var maxWaterLevel = 70; + +/** + * Let buildings, relics and siege engines become actors, but kill organic units. + */ +var drownClass = "Organic"; + +/** + * Maximum height that units and structures can be submerged before drowning or becoming destructed. + */ +var drownHeight = 1; + +/** + * One of these warnings is printed some minutes before the water level starts to rise. + */ +var waterWarningTexts = [ + markForTranslation("It keeps on raining, we will have to evacuate soon!"), + markForTranslation("The rivers are standing high, we need to find a save place!"), + markForTranslation("We have got to find dry ground, our lands will drawn soon!"), + markForTranslation("The lakes start swallowing the land, we have to find shelter!") +]; + +/** + * Units to be garrisoned in the wooden towers. + */ +var garrisonedUnits = "units/rome_legionnaire_marian"; + +Trigger.prototype.RaisingWaterNotification = function() +{ + Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).AddTimeNotification({ + "message": pickRandom(waterWarningTexts), + "translateMessage": true + }, waterRiseNotificationDuration * 60 * 1000); +}; + +Trigger.prototype.DebugLog = function(txt) +{ + if (!debugLog) + return; + + let time = Math.round(Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime() / 60 / 1000); + print("DEBUG [" + time + "] " + txt + "\n"); +}; + + +Trigger.prototype.GarrisonWoodenTowers = function() +{ + for (let gaiaEnt of Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEntitiesByPlayer(0)) + { + let cmpIdentity = Engine.QueryInterface(gaiaEnt, IID_Identity); + if (!cmpIdentity || !cmpIdentity.HasClass("DefenseTower")) + continue; + + let cmpGarrisonHolder = Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder); + if (!cmpGarrisonHolder) + continue; + + for (let newEnt of TriggerHelper.SpawnUnits(gaiaEnt, garrisonedUnits, cmpGarrisonHolder.GetCapacity(), 0)) + if (Engine.QueryInterface(gaiaEnt, IID_GarrisonHolder).Garrison(newEnt)) + Engine.QueryInterface(newEnt, IID_UnitAI).Autogarrison(gaiaEnt); + } +}; + +Trigger.prototype.RaiseWaterLevelStep = function() +{ + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + let time = cmpTimer.GetTime(); + let cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager); + let newLevel = cmpWaterManager.GetWaterLevel() + waterLevelIncreaseHeight; + cmpWaterManager.SetWaterLevel(newLevel); + this.DebugLog("Raising water level to " + Math.round(newLevel) + " took " + (cmpTimer.GetTime() - time)); + + if (newLevel < maxWaterLevel) + this.DoAfterDelay((debugWaterRise ? 10 : randFloat(...waterIncreaseTime) * 60) * 1000, "RaiseWaterLevelStep", {}); + else + this.DebugLog("Water reached final level"); + + let actorTemplates = {}; + let killedTemplates = {}; + + let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); + let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + + for (let ent of cmpRangeManager.GetGaiaAndNonGaiaEntities()) + { + let cmpPosition = Engine.QueryInterface(ent, IID_Position); + if (!cmpPosition || !cmpPosition.IsInWorld()) + continue; + + let pos = cmpPosition.GetPosition(); + if (pos.y + drownHeight >= newLevel) + continue; + + let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); + if (!cmpIdentity) + continue; + + let templateName = cmpTemplateManager.GetCurrentTemplateName(ent); + + // Animals and units drown + let cmpHealth = Engine.QueryInterface(ent, IID_Health); + if (cmpHealth && cmpIdentity.HasClass(drownClass)) + { + cmpHealth.Kill(); + + if (debugLog) + killedTemplates[templateName] = (killedTemplates[templateName] || 0) + 1; + + continue; + } + + // Resources and buildings become actors + // Do not use ChangeEntityTemplate for performance and + // because we don't need nor want the effects of MT_EntityRenamed + + let cmpVisualActor = Engine.QueryInterface(ent, IID_Visual); + if (!cmpVisualActor) + continue; + + let height = cmpPosition.GetHeightOffset(); + let rot = cmpPosition.GetRotation(); + + let actorTemplate = cmpTemplateManager.GetTemplate(templateName).VisualActor.Actor; + let seed = cmpVisualActor.GetActorSeed(); + Engine.DestroyEntity(ent); + + let newEnt = Engine.AddEntity("actor|" + actorTemplate); + Engine.QueryInterface(newEnt, IID_Visual).SetActorSeed(seed); + + let cmpNewPos = Engine.QueryInterface(newEnt, IID_Position); + cmpNewPos.JumpTo(pos.x, pos.z); + cmpNewPos.SetHeightOffset(height); + cmpNewPos.SetXZRotation(rot.x, rot.z); + cmpNewPos.SetYRotation(rot.y); + + if (debugLog) + actorTemplates[templateName] = (actorTemplates[templateName] || 0) + 1; + } + + this.DebugLog("Checking entities took " + (cmpTimer.GetTime() - time)); + this.DebugLog("Killed: " + uneval(killedTemplates)); + this.DebugLog("Converted to actors: " + uneval(actorTemplates)); +}; + + +{ + let waterRiseTime = debugWaterRise ? 0 : randFloat(...waterRiseStartTime); + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.GarrisonWoodenTowers(); + cmpTrigger.DoAfterDelay((waterRiseTime - waterRiseNotificationDuration) * 60 * 1000, "RaisingWaterNotification", {}); + cmpTrigger.DoAfterDelay(waterRiseTime * 60 * 1000, "RaiseWaterLevelStep", {}); +} Index: binaries/data/mods/public/maps/random/ratumacos.js =================================================================== --- binaries/data/mods/public/maps/random/ratumacos.js +++ binaries/data/mods/public/maps/random/ratumacos.js @@ -84,16 +84,17 @@ RMS.SetProgress(40); log("Placing players..."); + //Coordinate system of the heightmap var singleBases = [ - [45, 285], - [45, 175], - [145, 245], - [100, 115], - [220, 175], - [180, 85], - [280, 110], - [245, 35] + [100, 265], + [180, 260], + [245, 220], + [275, 145], + [40, 165], + [70, 95], + [130, 50], + [205, 45] ]; var strongholdBases = [ Index: binaries/data/mods/public/maps/random/ratumacos.json =================================================================== --- /dev/null +++ binaries/data/mods/public/maps/random/ratumacos.json @@ -0,0 +1,12 @@ +{ + "settings" : { + "Name" : "Ratumacos", + "Script" : "ratumacos.js", + "Description" : "Players start on the banks of the River Sequana (Seine) in Northern Gaul near the settlement of Ratumacos. Destined to become one of the largest and most prosperous cities of Medieval Europe and one of the Anglo-Norman dynasty capitals under the new name of Rouen, Ratumacos is still a peaceful land - but not for long.", + "BaseTerrain" : ["medit_sea_depths"], + "BaseHeight" : 1, + "Keywords": [], + "Preview" : "ratumacos.png", + "CircularMap" : true + } +} Index: binaries/data/mods/public/maps/random/rmgen/misc.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/misc.js +++ binaries/data/mods/public/maps/random/rmgen/misc.js @@ -802,3 +802,85 @@ } } } + +/** + * Generates a volcano mountain. Smoke and lava are optional. + * + * @param {number} fx - Horizontal coordinate of the center. + * @param {number} fz - Horizontal coordinate of the center. + * @param {number} tileClass - Painted onto every tile that is occupied by the volcano. + * @param {string} terrainTexture - The texture painted onto the volcano hill. + * @param {array} lavaTextures - Three different textures for the interior, from the outside to the inside. + * @param {boolean} smoke - Whether to place smoke particles. + * @param {number} elevationType - Elevation painter type, ELEVATION_SET = absolute or ELEVATION_MODIFY = relative. + */ +function createVolcano(fx, fz, tileClass, terrainTexture, lavaTextures, smoke, elevationType) +{ + log("Creating volcano"); + + let ix = Math.round(fractionToTiles(fx)); + let iz = Math.round(fractionToTiles(fz)); + + let baseSize = mapArea / scaleByMapSize(1, 8); + + let coherence = 0.7; + let smoothness = 0.05; + let failFraction = 100; + let steepness = 3; + + let clLava = createTileClass(); + + let layers = [ + { + "clumps": 0.067, + "elevation": 15, + "tileClass": tileClass + }, + { + "clumps": 0.05, + "elevation": 25, + "tileClass": createTileClass() + }, + { + "clumps": 0.02, + "elevation": 45, + "tileClass": createTileClass() + }, + { + "clumps": 0.011, + "elevation": 62, + "tileClass": createTileClass() + }, + { + "clumps": 0.003, + "elevation": 42, + "tileClass": clLava, + "painter": lavaTextures && new LayeredPainter([terrainTexture, ...lavaTextures], [1, 1, 1]), + "steepness": 1 + } + ]; + + for (let i = 0; i < layers.length; ++i) + createArea( + new ClumpPlacer(baseSize * layers[i].clumps, coherence, smoothness, failFraction, ix, iz), + [ + layers[i].painter || new LayeredPainter([terrainTexture, terrainTexture], [3]), + new SmoothElevationPainter(elevationType, layers[i].elevation, layers[i].steepness || steepness), + paintClass(layers[i].tileClass) + ], + i == 0 ? null : stayClasses(layers[i - 1].tileClass, 1)); + + if (smoke) + { + let num = Math.floor(baseSize * 0.002); + createObjectGroup( + new SimpleGroup( + [new SimpleObject("actor|particle/smoke.xml", num, num, 0, 7)], + false, + clLava, + ix, + iz), + 0, + stayClasses(tileClass, 1)); + } +} Index: binaries/data/mods/public/maps/random/rouen.json =================================================================== --- binaries/data/mods/public/maps/random/rouen.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "settings" : { - "Name" : "Rouen", - "Script" : "rouen.js", - "Description" : "Players start on the banks of the River Sequana (Seine) in the north of Gaul in the place of the modern-day city of Rouen. Destined to become one of the largest and most prosperous cities of medieval Europe and one of the Anglo-Norman dynasty capitals, Rouen is still a peaceful land - but not for long.", - "BaseTerrain" : ["medit_sea_depths"], - "BaseHeight" : 1, - "Keywords": [], - "Preview" : "rouen.png", - "CircularMap" : false - } -} Index: binaries/data/mods/public/maps/random/volcanic_lands.js =================================================================== --- binaries/data/mods/public/maps/random/volcanic_lands.js +++ binaries/data/mods/public/maps/random/volcanic_lands.js @@ -20,7 +20,6 @@ // decorative props var aRockLarge = "actor|geology/stone_granite_med.xml"; var aRockMedium = "actor|geology/stone_granite_med.xml"; -var aSmoke = "actor|particle/smoke.xml"; var pForestD = [tGrassC + TERRAIN_SEPARATOR + oTree, tGrassC]; var pForestP = [tGrassB + TERRAIN_SEPARATOR + oTree, tGrassB]; @@ -35,9 +34,6 @@ // create tile classes var clPlayer = createTileClass(); var clHill = createTileClass(); -var clHill2 = createTileClass(); -var clHill3 = createTileClass(); -var clHill4 = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); @@ -134,96 +130,19 @@ break; } } - RMS.SetProgress(15); -log("Creating volcano"); -var fx = fractionToTiles(0.5); -var fz = fractionToTiles(0.5); -var ix = round(fx); -var iz = round(fz); -var div = scaleByMapSize(1,8); -var placer = new ClumpPlacer(mapArea * 0.067 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 15, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill)], null); - -var placer = new ClumpPlacer(mapArea * 0.05 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 25, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill2)], stayClasses(clHill, 1)); - -var placer = new ClumpPlacer(mapArea * 0.02 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 45, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill3)], stayClasses(clHill2, 1)); - -var placer = new ClumpPlacer(mapArea * 0.011 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tCliff], // terrains - [3] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 62, // elevation - 3 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill4)], stayClasses(clHill3, 1)); - -var placer = new ClumpPlacer(mapArea * 0.003 / div, 0.7, 0.05, 100, ix, iz); -var terrainPainter = new LayeredPainter( - [tCliff, tLava1, tLava2, tLava3], // terrains - [1, 1, 1] // widths -); -var elevationPainter = new SmoothElevationPainter( - ELEVATION_SET, // type - 42, // elevation - 1 // blend radius -); -createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill4)], stayClasses(clHill4, 1)); - -var num = floor(mapArea * 0.03 / 15 / div); -var tX = round(fx); -var tZ = round(fz); -var group = new SimpleGroup( - [new SimpleObject(aSmoke, num, num, 0,7)], - false, clBaseResource, tX, tZ -); -createObjectGroup(group, 0, stayClasses(clHill4,1)); - +createVolcano(0.5, 0.5, clHill, tCliff, [tLava1, tLava2, tLava3], true, ELEVATION_SET); RMS.SetProgress(45); log("Creating hills..."); -placer = new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1); -terrainPainter = new LayeredPainter( - [tCliff, tGrass], // terrains - [2] // widths -); -elevationPainter = new SmoothElevationPainter(ELEVATION_SET, 18, 2); createAreas( - placer, - [terrainPainter, elevationPainter, paintClass(clHill)], + new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), + [ + new LayeredPainter([tCliff, tGrass], [2]), + new SmoothElevationPainter(ELEVATION_SET, 18, 2), + paintClass(clHill) + ], avoidClasses(clPlayer, 12, clHill, 15, clBaseResource, 2), scaleByMapSize(2, 8) * numPlayers ); Index: binaries/data/mods/public/maps/scripts/CaptureTheRelic.js =================================================================== --- binaries/data/mods/public/maps/scripts/CaptureTheRelic.js +++ binaries/data/mods/public/maps/scripts/CaptureTheRelic.js @@ -48,7 +48,8 @@ return; } - let numSpawnedRelics = Math.ceil(TriggerHelper.GetNumberOfPlayers() / 2); + let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); + let numSpawnedRelics = cmpEndGameManager.GetGameTypeSettings().relicCount; this.playerRelicsCount = new Array(TriggerHelper.GetNumberOfPlayers()).fill(0, 1); this.playerRelicsCount[0] = numSpawnedRelics; Index: binaries/data/mods/public/maps/scripts/Conquest.js =================================================================== --- binaries/data/mods/public/maps/scripts/Conquest.js +++ binaries/data/mods/public/maps/scripts/Conquest.js @@ -1,9 +1,11 @@ -var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); -cmpTrigger.conquestClassFilter = "ConquestCritical"; +{ + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.conquestClassFilter = "ConquestCritical"; -var data = {"enabled": true}; -cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data); -cmpTrigger.RegisterTrigger("OnStructureBuilt", "ConquestAddStructure", data); -cmpTrigger.RegisterTrigger("OnTrainingFinished", "ConquestTrainingFinished", data); + let data = { "enabled": true }; + cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data); + cmpTrigger.RegisterTrigger("OnStructureBuilt", "ConquestAddStructure", data); + cmpTrigger.RegisterTrigger("OnTrainingFinished", "ConquestTrainingFinished", data); -cmpTrigger.DoAfterDelay(0, "ConquestStartGameCount", null); + cmpTrigger.DoAfterDelay(0, "ConquestStartGameCount", null); +} Index: binaries/data/mods/public/maps/scripts/ConquestCommon.js =================================================================== --- binaries/data/mods/public/maps/scripts/ConquestCommon.js +++ binaries/data/mods/public/maps/scripts/ConquestCommon.js @@ -95,8 +95,9 @@ this.conquestDataInit = true; }; -var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); - -cmpTrigger.conquestEntitiesByPlayer = {}; -cmpTrigger.conquestDataInit = false; -cmpTrigger.conquestClassFilter = ""; +{ + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.conquestEntitiesByPlayer = {}; + cmpTrigger.conquestDataInit = false; + cmpTrigger.conquestClassFilter = ""; +} Index: binaries/data/mods/public/maps/scripts/ConquestStructures.js =================================================================== --- binaries/data/mods/public/maps/scripts/ConquestStructures.js +++ binaries/data/mods/public/maps/scripts/ConquestStructures.js @@ -1,8 +1,10 @@ -var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); -cmpTrigger.conquestClassFilter = "Structure"; +{ + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.conquestClassFilter = "Structure"; -var data = {"enabled": true}; -cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data); -cmpTrigger.RegisterTrigger("OnStructureBuilt", "ConquestAddStructure", data); + let data = { "enabled": true }; + cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data); + cmpTrigger.RegisterTrigger("OnStructureBuilt", "ConquestAddStructure", data); -cmpTrigger.DoAfterDelay(0, "ConquestStartGameCount", null); + cmpTrigger.DoAfterDelay(0, "ConquestStartGameCount", null); +} Index: binaries/data/mods/public/maps/scripts/ConquestUnits.js =================================================================== --- binaries/data/mods/public/maps/scripts/ConquestUnits.js +++ binaries/data/mods/public/maps/scripts/ConquestUnits.js @@ -1,8 +1,10 @@ -var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); -cmpTrigger.conquestClassFilter = "Unit"; +{ + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.conquestClassFilter = "Unit"; -var data = {"enabled": true}; -cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data); -cmpTrigger.RegisterTrigger("OnTrainingFinished", "ConquestTrainingFinished", data); + let data = { "enabled": true }; + cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data); + cmpTrigger.RegisterTrigger("OnTrainingFinished", "ConquestTrainingFinished", data); -cmpTrigger.DoAfterDelay(0, "ConquestStartGameCount", null); + cmpTrigger.DoAfterDelay(0, "ConquestStartGameCount", null); +} Index: binaries/data/mods/public/maps/scripts/Regicide.js =================================================================== --- binaries/data/mods/public/maps/scripts/Regicide.js +++ binaries/data/mods/public/maps/scripts/Regicide.js @@ -106,7 +106,9 @@ return undefined; }; -let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); -cmpTrigger.regicideHeroes = []; -cmpTrigger.DoAfterDelay(0, "InitRegicideGame", {}); -cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckRegicideDefeat", { "enabled": true }); +{ + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.regicideHeroes = []; + cmpTrigger.DoAfterDelay(0, "InitRegicideGame", {}); + cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckRegicideDefeat", { "enabled": true }); +} Index: binaries/data/mods/public/maps/scripts/WonderVictory.js =================================================================== --- binaries/data/mods/public/maps/scripts/WonderVictory.js +++ binaries/data/mods/public/maps/scripts/WonderVictory.js @@ -76,8 +76,10 @@ } }; -var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); -cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckWonderVictory", { "enabled": true }); -cmpTrigger.RegisterTrigger("OnPlayerWon", "DeleteWonderVictoryMessages", { "enabled": true }); -cmpTrigger.wonderVictoryTimers = {}; -cmpTrigger.wonderVictoryMessages = {}; +{ + let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); + cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckWonderVictory", { "enabled": true }); + cmpTrigger.RegisterTrigger("OnPlayerWon", "DeleteWonderVictoryMessages", { "enabled": true }); + cmpTrigger.wonderVictoryTimers = {}; + cmpTrigger.wonderVictoryMessages = {}; +} Index: binaries/data/mods/public/maps/skirmishes/Mediterranean Cove (2).xml =================================================================== --- binaries/data/mods/public/maps/skirmishes/Mediterranean Cove (2).xml +++ binaries/data/mods/public/maps/skirmishes/Mediterranean Cove (2).xml @@ -6933,7 +6933,7 @@ 0 - + Index: binaries/data/mods/public/simulation/ai/common-api/map-module.js =================================================================== --- binaries/data/mods/public/simulation/ai/common-api/map-module.js +++ binaries/data/mods/public/simulation/ai/common-api/map-module.js @@ -10,7 +10,7 @@ m.Map = function Map(sharedScript, type, originalMap, actualCopy) { // get the correct dimensions according to the map type - let map = (type === "territory" || type === "resource") ? sharedScript.territoryMap : sharedScript.passabilityMap; + let map = type === "territory" || type === "resource" ? sharedScript.territoryMap : sharedScript.passabilityMap; this.width = map.width; this.height = map.height; this.cellSize = map.cellSize; Index: binaries/data/mods/public/simulation/ai/petra/attackManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -216,7 +216,7 @@ if ((barracksNb >= 1 && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.townPhase()))) || !gameState.ai.HQ.baseManagers[1]) // if we have no base ... nothing else to do than attack { - let type = (this.attackNumber < 2 || this.startedAttacks.HugeAttack.length > 0) ? "Attack" : "HugeAttack"; + let type = this.attackNumber < 2 || this.startedAttacks.HugeAttack.length > 0 ? "Attack" : "HugeAttack"; let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, type); if (attackPlan.failed) this.attackPlansEncounteredWater = true; // hack Index: binaries/data/mods/public/simulation/ai/petra/attackPlan.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -1030,7 +1030,7 @@ if (blocker && blocker.hasClass("StoneWall")) { -/* if (this.hasSiegeUnits(gameState)) +/* if (this.hasSiegeUnits()) { */ this.isBlocked = true; return blocker; @@ -1216,18 +1216,18 @@ if (!attacker || !attacker.position() || !attacker.hasClass("Unit")) continue; let ourUnit = gameState.getEntityById(evt.target); - if (this.isSiegeUnit(gameState, ourUnit)) + if (m.isSiegeUnit(ourUnit)) { // if our siege units are attacked, we'll send some units to deal with enemies. let collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5); for (let ent of collec.values()) { - if (this.isSiegeUnit(gameState, ent)) // needed as mauryan elephants are not filtered out + if (m.isSiegeUnit(ent)) // needed as mauryan elephants are not filtered out continue; ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); } // And if this attacker is a non-ranged siege unit and our unit also, attack it - if (this.isSiegeUnit(gameState, attacker) && attacker.hasClass("Melee") && ourUnit.hasClass("Melee")) + if (m.isSiegeUnit(attacker) && attacker.hasClass("Melee") && ourUnit.hasClass("Melee")) { ourUnit.attack(attacker.id(), m.allowCapture(gameState, ourUnit, attacker)); ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); @@ -1241,7 +1241,7 @@ // TODO check that the attacker is from behind the wall continue; } - else if (this.isSiegeUnit(gameState, attacker)) + else if (m.isSiegeUnit(attacker)) { // if our unit is attacked by a siege unit, we'll send some melee units to help it. let collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5); for (let ent of collec.values()) @@ -1289,7 +1289,7 @@ continue; if (!(targetId in unitTargets)) { - if (this.isSiegeUnit(gameState, target) || target.hasClass("Hero")) + if (m.isSiegeUnit(target) || target.hasClass("Hero")) unitTargets[targetId] = -8; else if (target.hasClass("Champion") || target.hasClass("Ship")) unitTargets[targetId] = -5; @@ -1332,7 +1332,7 @@ this.unitCollUpdateArray = this.unitCollection.toIdArray(); // Let's check a few units each time we update (currently 10) except when attack starts - let lgth = (this.unitCollUpdateArray.length < 15 || this.startingAttack) ? this.unitCollUpdateArray.length : 10; + let lgth = this.unitCollUpdateArray.length < 15 || this.startingAttack ? this.unitCollUpdateArray.length : 10; for (let check = 0; check < lgth; check++) { let ent = gameState.getEntityById(this.unitCollUpdateArray[check]); @@ -1347,7 +1347,7 @@ // update the order if needed let needsUpdate = false; let maybeUpdate = false; - let siegeUnit = this.isSiegeUnit(gameState, ent); + let siegeUnit = m.isSiegeUnit(ent); if (ent.isIdle()) needsUpdate = true; else if (siegeUnit && targetId) @@ -1658,7 +1658,7 @@ } } // Are we arrived at destination ? - if (attackedNB > 1 && (attackedUnitNB || this.hasSiegeUnits(gameState))) + if (attackedNB > 1 && (attackedUnitNB || this.hasSiegeUnits())) { if (gameState.ai.HQ.territoryMap.getOwner(this.position) === this.targetPlayer || attackedNB > 3) { @@ -1918,10 +1918,10 @@ return false; }; -m.AttackPlan.prototype.hasSiegeUnits = function(gameState) +m.AttackPlan.prototype.hasSiegeUnits = function() { for (let ent of this.unitCollection.values()) - if (this.isSiegeUnit(gameState, ent)) + if (m.isSiegeUnit(ent)) return true; return false; }; @@ -1940,11 +1940,6 @@ return false; }; -m.AttackPlan.prototype.isSiegeUnit = function(gameState, ent) -{ - return ent.hasClass("Siege") || (ent.hasClass("Elephant") && ent.hasClass("Champion")); -}; - m.AttackPlan.prototype.debugAttack = function() { API3.warn("---------- attack " + this.name); Index: binaries/data/mods/public/simulation/ai/petra/defenseManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/defenseManager.js +++ binaries/data/mods/public/simulation/ai/petra/defenseManager.js @@ -476,7 +476,7 @@ { // If enemies are in range of one of our defensive structures, garrison it for arrow multiplier if (attacker.position() && attacker.isGarrisonHolder() && attacker.getArrowMultiplier()) - this.garrisonRangedUnitsInside(gameState, attacker, {"attacker": target}); + this.garrisonUnitsInside(gameState, attacker, {"attacker": target}); } if (!gameState.isEntityOwn(target)) @@ -557,11 +557,11 @@ continue; if (target.isGarrisonHolder() && target.getArrowMultiplier()) - this.garrisonRangedUnitsInside(gameState, target, {"attacker": attacker}); + this.garrisonUnitsInside(gameState, target, {"attacker": attacker}); } }; -m.DefenseManager.prototype.garrisonRangedUnitsInside = function(gameState, target, data) +m.DefenseManager.prototype.garrisonUnitsInside = function(gameState, target, data) { let minGarrison = data.min ? data.min : target.garrisonMax(); let typeGarrison = data.type ? data.type : "protection"; @@ -583,12 +583,23 @@ let garrisonManager = gameState.ai.HQ.garrisonManager; let garrisonArrowClasses = target.getGarrisonArrowClasses(); let units = gameState.getOwnUnits().filter(ent => MatchesClassList(ent.classes(), garrisonArrowClasses)).filterNearest(target.position()); + let allowMelee = gameState.ai.HQ.garrisonManager.allowMelee(target); + if (allowMelee === undefined) + { + // Should be kept in sync with garrisonManager to avoid garrisoning-ungarrisoning some units + if (data.attacker) + allowMelee = data.attacker.hasClass("Structure") ? data.attacker.attackRange("Ranged") : !m.isSiegeUnit(data.attacker); + else + allowMelee = true; + } for (let ent of units.values()) { if (garrisonManager.numberOfGarrisonedUnits(target) >= minGarrison) break; if (!ent.position()) continue; + if (typeGarrison !== "decay" && !allowMelee && ent.attackTypes().indexOf("Melee") !== -1) + continue; if (ent.getMetadata(PlayerID, "transport") !== undefined) continue; let army = ent.getMetadata(PlayerID, "PartOfArmy") ? this.getArmy(ent.getMetadata(PlayerID, "PartOfArmy")) : undefined; Index: binaries/data/mods/public/simulation/ai/petra/entityExtend.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/entityExtend.js +++ binaries/data/mods/public/simulation/ai/petra/entityExtend.js @@ -1,6 +1,12 @@ var PETRA = function(m) { +/** returns true if this unit should be considered as a siege unit */ +m.isSiegeUnit = function(ent) +{ + return ent.hasClass("Siege") || (ent.hasClass("Elephant") && ent.hasClass("Champion")); +}; + /** returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. */ m.getMaxStrength = function(ent, againstClass) { Index: binaries/data/mods/public/simulation/ai/petra/garrisonManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/garrisonManager.js +++ binaries/data/mods/public/simulation/ai/petra/garrisonManager.js @@ -24,9 +24,9 @@ { if (id !== evt.entity) continue; - let list = this.holders.get(id); + let data = this.holders.get(id); this.holders.delete(id); - this.holders.set(evt.newentity, list); + this.holders.set(evt.newentity, data); } for (let id of this.decayingStructures.keys()) { @@ -43,8 +43,9 @@ } } - for (let [id, list] of this.holders.entries()) + for (let [id, data] of this.holders.entries()) { + let list = data.list; let holder = gameState.getEntityById(id); if (!holder || !gameState.isPlayerAlly(holder.owner())) { @@ -108,7 +109,7 @@ if (gameState.ai.elapsedTime - holder.getMetadata(PlayerID, "holderTimeUpdate") > 3) { let range = holder.attackRange("Ranged") ? holder.attackRange("Ranged").max : 80; - let enemiesAround = false; + let around = { "defenseStructure": false, "inertStructure": false, "meleeSiege": false, "rangeSiege": false, "unit": false }; for (let ent of gameState.getEnemyEntities().values()) { if (!ent.position()) @@ -118,20 +119,39 @@ let dist = API3.SquareVectorDistance(ent.position(), holder.position()); if (dist > range*range) continue; - enemiesAround = true; - break; + if (ent.hasClass("Structure")) + { + if (ent.attackRange("Ranged")) // TODO units on wall are not taken into account + around.defenseStructure = true; + else + around.inertStructure = true; + } + else if (m.isSiegeUnit(ent)) + { + if (ent.attackTypes().indexOf("Melee") !== -1) + around.meleeSiege = true; + else + around.rangeSiege = true; + } + else + { + around.unit = true; + break; + } } + // Keep defenseManager.garrisonUnitsInside in sync to avoid garrisoning-ungarrisoning some units + data.allowMelee = around.defenseStructure || around.unit; for (let entId of holder.garrisoned()) { let ent = gameState.getEntityById(entId); - if (ent.owner() === PlayerID && !this.keepGarrisoned(ent, holder, enemiesAround)) + if (ent.owner() === PlayerID && !this.keepGarrisoned(ent, holder, around)) holder.unload(entId); } for (let j = 0; j < list.length; ++j) { let ent = gameState.getEntityById(list[j]); - if (this.keepGarrisoned(ent, holder, enemiesAround)) + if (this.keepGarrisoned(ent, holder, around)) continue; if (ent.getMetadata(PlayerID, "garrisonHolder") == id) { @@ -155,7 +175,7 @@ if (!ent || ent.owner() !== PlayerID) this.decayingStructures.delete(id); else if (this.numberOfGarrisonedUnits(ent) < gmin) - gameState.ai.HQ.defenseManager.garrisonRangedUnitsInside(gameState, ent, {"min": gmin, "type": "decay"}); + gameState.ai.HQ.defenseManager.garrisonUnitsInside(gameState, ent, {"min": gmin, "type": "decay"}); } }; @@ -165,7 +185,15 @@ if (!this.holders.has(holder.id())) return holder.garrisoned().length; - return holder.garrisoned().length + this.holders.get(holder.id()).length; + return holder.garrisoned().length + this.holders.get(holder.id()).list.length; +}; + +m.GarrisonManager.prototype.allowMelee = function(holder) +{ + if (!this.holders.has(holder.id())) + return undefined; + + return this.holders.get(holder.id()).allowMelee; }; /** This is just a pre-garrison state, while the entity walk to the garrison holder */ @@ -175,7 +203,7 @@ return; this.registerHolder(gameState, holder); - this.holders.get(holder.id()).push(ent.id()); + this.holders.get(holder.id()).list.push(ent.id()); if (gameState.ai.Config.debug > 2) { @@ -218,13 +246,13 @@ let holderId = ent.getMetadata(PlayerID, "garrisonHolder"); if (!holderId || !this.holders.has(holderId)) return; - let list = this.holders.get(holderId); + let list = this.holders.get(holderId).list; let index = list.indexOf(ent.id()); if (index !== -1) list.splice(index, 1); }; -m.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, enemiesAround) +m.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, around) { switch (ent.getMetadata(PlayerID, "garrisonType")) { @@ -233,14 +261,28 @@ case 'trade': // trader garrisoned in ship return true; case 'protection': // hurt unit for healing or infantry for defense - return ent.needsHeal() && holder.buffHeal() || - enemiesAround && (ent.hasClass("Support") || - MatchesClassList(ent.classes(), holder.getGarrisonArrowClasses()) || - MatchesClassList(ent.classes(), "Siege+!Melee")); + if (ent.needsHeal() && holder.buffHeal()) + return true; + if (MatchesClassList(ent.classes(), holder.getGarrisonArrowClasses())) + { + if (around.unit || around.defenseStructure) + return true; + else if (around.meleeSiege || around.rangeSiege) + return ent.attackTypes().indexOf("Melee") === -1; + else + return false; + } + if (ent.attackTypes() && ent.attackTypes().indexOf("Melee") !== -1) + return false; + if (around.unit) + return ent.hasClass("Support") || m.isSiegeUnit(ent); // only ranged siege here and below as melee siege already released above + if (m.isSiegeUnit(ent)) + return around.meleeSiege; + return false; case 'decay': return this.decayingStructures.has(holder.id()); case 'emergency': // f.e. hero in regicide mode - return enemiesAround; + return around.unit || around.defenseStructure || around.meleeSiege; default: if (ent.getMetadata(PlayerID, "onBoard") === "onBoard") // transport is not (yet ?) managed by garrisonManager return true; @@ -256,7 +298,7 @@ { if (this.holders.has(holder.id())) // already registered return; - this.holders.set(holder.id(), []); + this.holders.set(holder.id(), { "list": [], "allowMelee": true }); holder.setMetadata(PlayerID, "holderTimeUpdate", gameState.ai.elapsedTime); }; Index: binaries/data/mods/public/simulation/ai/petra/headquarters.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -774,7 +774,7 @@ let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)]; if (proximity) // this is our first cc, let's do it near our units - norm /= (1 + API3.SquareVectorDistance(proximity, pos) / scale); + norm /= 1 + API3.SquareVectorDistance(proximity, pos) / scale; else { let minDist = Math.min(); Index: binaries/data/mods/public/simulation/ai/petra/mapModule.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/mapModule.js +++ binaries/data/mods/public/simulation/ai/petra/mapModule.js @@ -44,7 +44,7 @@ for (let k = 0; k < territoryMap.data.length; ++k) { - let tilePlayer = (territoryMap.data[k] & m.TERRITORY_PLAYER_MASK); + let tilePlayer = territoryMap.data[k] & m.TERRITORY_PLAYER_MASK; let isConnected = (territoryMap.data[k] & m.TERRITORY_BLINKING_MASK) == 0; if (tilePlayer === PlayerID) { @@ -68,7 +68,7 @@ } let x = ratio * (k % territoryMap.width); - let y = ratio * (Math.floor(k / territoryMap.width)); + let y = ratio * Math.floor(k / territoryMap.width); for (let ix = 0; ix < ratio; ++ix) { for (let iy = 0; iy < ratio; ++iy) Index: binaries/data/mods/public/simulation/ai/petra/queueManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queueManager.js +++ binaries/data/mods/public/simulation/ai/petra/queueManager.js @@ -282,7 +282,7 @@ } else if (this.accounts[j][res] > queueCost[res]) { - availableRes[res] += (this.accounts[j][res] - queueCost[res]); + availableRes[res] += this.accounts[j][res] - queueCost[res]; this.accounts[j][res] = queueCost[res]; } } @@ -421,16 +421,16 @@ { let toBePaused = false; if (gameState.ai.HQ.numActiveBase() === 0) - toBePaused = (q !== "dock" && q !== "civilCentre"); + toBePaused = q !== "dock" && q !== "civilCentre"; else if (numWorkers < workersMin / 3) - toBePaused = (q !== "citizenSoldier" && q !== "villager" && q !== "emergency"); + toBePaused = q !== "citizenSoldier" && q !== "villager" && q !== "emergency"; else if (numWorkers < workersMin * 2 / 3) - toBePaused = (q === "civilCentre" || q === "economicBuilding" || + toBePaused = q === "civilCentre" || q === "economicBuilding" || q === "militaryBuilding" || q === "defenseBuilding" || q === "healer" || - q === "majorTech" || q === "minorTech" || q.indexOf("plan_") !== -1); + q === "majorTech" || q === "minorTech" || q.indexOf("plan_") !== -1; else if (numWorkers < workersMin) - toBePaused = (q === "civilCentre" || q === "defenseBuilding" || - q == "majorTech" || q.indexOf("_siege") != -1 || q.indexOf("_champ") != -1); + toBePaused = q === "civilCentre" || q === "defenseBuilding" || + q == "majorTech" || q.indexOf("_siege") != -1 || q.indexOf("_champ") != -1; if (toBePaused) { Index: binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js +++ binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js @@ -514,7 +514,7 @@ let waterPoints = []; for (let i = 0; i < numPoints; ++i) { - let angle = (i/numPoints)*2*Math.PI; + let angle = 2 * Math.PI * i / numPoints; pos = [x - (1+dist)*size*Math.sin(angle), z + (1+dist)*size*Math.cos(angle)]; pos = gameState.ai.accessibility.gamePosToMapPos(pos); if (pos[0] < 0 || pos[0] >= gameState.ai.accessibility.width || Index: binaries/data/mods/public/simulation/ai/petra/tradeManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/tradeManager.js +++ binaries/data/mods/public/simulation/ai/petra/tradeManager.js @@ -424,7 +424,7 @@ let access2 = m.getLandAccess(gameState, m2); let sea2 = m2.hasClass("NavalMarket") ? m.getSeaAccess(gameState, m2) : undefined; let land = access1 == access2 ? access1 : undefined; - let sea = (sea1 && sea1 == sea2) ? sea1 : undefined; + let sea = sea1 && sea1 == sea2 ? sea1 : undefined; if (!land && !sea) continue; let gainMultiplier; @@ -628,7 +628,7 @@ return route; let ret = {}; for (let key in route) - ret[key] = (key == "source" || key == "target") ? route[key].id() : route[key]; + ret[key] = key == "source" || key == "target" ? route[key].id() : route[key]; return ret; }; @@ -638,7 +638,7 @@ return route; let ret = {}; for (let key in route) - ret[key] = (key == "source" || key == "target") ? gameState.getEntityById(route[key]) : route[key]; + ret[key] = key == "source" || key == "target" ? gameState.getEntityById(route[key]) : route[key]; return ret; }; Index: binaries/data/mods/public/simulation/ai/petra/worker.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/worker.js +++ binaries/data/mods/public/simulation/ai/petra/worker.js @@ -39,15 +39,60 @@ this.ent = ent; let unitAIState = ent.unitAIState(); - if (unitAIState === "INDIVIDUAL.GATHER.GATHERING" || unitAIState === "INDIVIDUAL.GATHER.APPROACHING" || - unitAIState === "INDIVIDUAL.COMBAT.APPROACHING") + if ((subrole === "hunter" || subrole === "gatherer") && + (unitAIState === "INDIVIDUAL.GATHER.GATHERING" || unitAIState === "INDIVIDUAL.GATHER.APPROACHING" || + unitAIState === "INDIVIDUAL.COMBAT.APPROACHING")) { - if (this.isInaccessibleSupply(gameState) && ((subrole === "hunter" && !this.startHunting(gameState)) || - (subrole === "gatherer" && !this.startGathering(gameState)))) + if (this.isInaccessibleSupply(gameState) && !this.retryGathering(gameState, subrole)) ent.stopMoving(); + + // Check that we have not drifted too far + if (unitAIState === "INDIVIDUAL.COMBAT.APPROACHING" && ent.unitAIOrderData().length) + { + let orderData = ent.unitAIOrderData()[0]; + if (orderData && orderData.target) + { + let supply = gameState.getEntityById(orderData.target); + if (supply && supply.resourceSupplyType() && supply.resourceSupplyType().generic === "food") + { + let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(supply.position()); + if (gameState.isPlayerEnemy(territoryOwner) && !this.retryGathering(gameState, subrole)) + ent.stopMoving(); + else if (!gameState.isPlayerAlly(territoryOwner)) + { + let distanceSquare = ent.hasClass("Cavalry") ? 90000 : 30000; + let supplyAccess = gameState.ai.accessibility.getAccessValue(supply.position()); + let foodDropsites = gameState.playerData.hasSharedDropsites ? + gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food"); + let hasFoodDropsiteWithinDistance = false; + for (let dropsite of foodDropsites.values()) + { + if (!dropsite.position()) + continue; + let owner = dropsite.owner(); + // owner !== PlayerID can only happen when hasSharedDropsites === true, so no need to test it again + if (owner !== PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner))) + continue; + if (supplyAccess !== m.getLandAccess(gameState, dropsite)) + continue; + if (API3.SquareVectorDistance(supply.position(), dropsite.position()) < distanceSquare) + { + hasFoodDropsiteWithinDistance = true; + break; + } + } + if (!hasFoodDropsiteWithinDistance && !this.retryGathering(gameState, subrole)) + ent.stopMoving(); + } + } + } + } } else if (ent.getMetadata(PlayerID, "approachingTarget")) + { ent.setMetadata(PlayerID, "approachingTarget", undefined); + ent.setMetadata(PlayerID, "alreadyTried", undefined); + } let unitAIStateOrder = unitAIState.split(".")[1]; // If we're fighting or hunting, let's not start gathering @@ -261,6 +306,21 @@ } }; +m.Worker.prototype.retryGathering = function(gameState, subrole) +{ + switch (subrole) + { + case "gatherer": + return this.startGathering(gameState); + case "hunter": + return this.startHunting(gameState); + case "fisher": + return this.startFishing(gameState); + default: + return false; + } +}; + m.Worker.prototype.startGathering = function(gameState) { let access = gameState.ai.accessibility.getAccessValue(this.ent.position()); @@ -550,12 +610,12 @@ let isRanged = this.ent.hasClass("Ranged"); let entPosition = position ? position : this.ent.position(); let access = gameState.ai.accessibility.getAccessValue(entPosition); - let foodDropsites = (gameState.playerData.hasSharedDropsites ? gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food")).toEntityArray(); + let foodDropsites = gameState.playerData.hasSharedDropsites ? + gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food"); - let nearestDropsiteDist = function(supply) { - let distMin = 1000000; - let pos = supply.position(); - for (let dropsite of foodDropsites) + let hasFoodDropsiteWithinDistance = function(supplyPosition, supplyAccess, distSquare) + { + for (let dropsite of foodDropsites.values()) { if (!dropsite.position()) continue; @@ -563,11 +623,12 @@ // owner !== PlayerID can only happen when hasSharedDropsites === true, so no need to test it again if (owner !== PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner))) continue; - if (access !== m.getLandAccess(gameState, dropsite)) + if (supplyAccess !== m.getLandAccess(gameState, dropsite)) continue; - distMin = Math.min(distMin, API3.SquareVectorDistance(pos, dropsite.position())); + if (API3.SquareVectorDistance(supplyPosition, dropsite.position()) < distSquare) + return true; } - return distMin; + return false; }; resources.forEach(function(supply) @@ -612,11 +673,11 @@ if (territoryOwner !== 0 && territoryOwner !== PlayerID && supply.owner() === territoryOwner) return; - let dropsiteDist = nearestDropsiteDist(supply); - if (dropsiteDist > 35000) - return; // Only cavalry should hunt far from dropsite (specially for non domestic animals which flee) - if (!isCavalry && (dropsiteDist > 12000 || ((dropsiteDist > 7000 || territoryOwner === 0 ) && canFlee))) + if (!isCavalry && canFlee && territoryOwner === 0) + return; + let distanceSquare = isCavalry ? 35000 : ( canFlee ? 7000 : 12000); + if (!hasFoodDropsiteWithinDistance(supply.position(), supplyAccess, distanceSquare)) return; nearestSupplyDist = dist; @@ -857,22 +918,29 @@ if (!target) return true; + if (!target.resourceSupplyType()) + return false; + let approachingTarget = this.ent.getMetadata(PlayerID, "approachingTarget"); + let carriedAmount = this.ent.resourceCarrying().length ? this.ent.resourceCarrying()[0].amount : 0; if (!approachingTarget || approachingTarget !== targetId) { this.ent.setMetadata(PlayerID, "approachingTarget", targetId); this.ent.setMetadata(PlayerID, "approachingTime", undefined); this.ent.setMetadata(PlayerID, "approachingPos", undefined); - this.ent.setMetadata(PlayerID, "carriedAmount", undefined); + this.ent.setMetadata(PlayerID, "carriedBefore", carriedAmount); + let alreadyTried = this.ent.getMetadata(PlayerID, "alreadyTried"); + if (alreadyTried && alreadyTried !== targetId) + this.ent.setMetadata(PlayerID, "alreadyTried", undefined); } - let carriedAmount = this.ent.resourceCarrying().length ? this.ent.resourceCarrying()[0].amount : 0; - if (this.ent.getMetadata(PlayerID, "carriedAmount") === undefined || - this.ent.getMetadata(PlayerID, "carriedAmount") !== carriedAmount) + let carriedBefore = this.ent.getMetadata(PlayerID, "carriedBefore"); + if (carriedBefore !== carriedAmount) { - this.ent.setMetadata(PlayerID, "carriedAmount", carriedAmount); - this.ent.setMetadata(PlayerID, "approachingTime", undefined); - this.ent.setMetadata(PlayerID, "approachingPos", undefined); + this.ent.setMetadata(PlayerID, "approachingTarget", undefined); + this.ent.setMetadata(PlayerID, "alreadyTried", undefined); + if (target.getMetadata(PlayerID, "inaccessibleTime")) + target.setMetadata(PlayerID, "inaccessibleTime", 0); return false; } @@ -889,12 +957,20 @@ { this.ent.setMetadata(PlayerID, "approachingTime", gameState.ai.elapsedTime); this.ent.setMetadata(PlayerID, "approachingPos", presentPos); + return false; } - else if (gameState.ai.elapsedTime - approachingTime > 10) + if (gameState.ai.elapsedTime - approachingTime > 10) { - - target.setMetadata(PlayerID, "inaccessibleTime", gameState.ai.elapsedTime + 600); - return true; + if (this.ent.getMetadata(PlayerID, "alreadyTried")) + { + target.setMetadata(PlayerID, "inaccessibleTime", gameState.ai.elapsedTime + 600); + return true; + } + // let's try again to reach it + this.ent.setMetadata(PlayerID, "alreadyTried", targetId); + this.ent.setMetadata(PlayerID, "approachingTarget", undefined); + this.ent.gather(target); + return false; } } return false; Index: binaries/data/mods/public/simulation/components/Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/Attack.js +++ binaries/data/mods/public/simulation/components/Attack.js @@ -1,5 +1,7 @@ function Attack() {} +var g_AttackTypes = ["Melee", "Ranged", "Capture"]; + Attack.prototype.bonusesSchema = "" + "" + @@ -188,9 +190,15 @@ Attack.prototype.Serialize = null; // we have no dynamic state to save -Attack.prototype.GetAttackTypes = function() +Attack.prototype.GetAttackTypes = function(wantedTypes) { - return ["Melee", "Ranged", "Capture"].filter(type => !!this.template[type]); + let types = g_AttackTypes.filter(type => !!this.template[type]); + if (!wantedTypes) + return types; + + let wantedTypesReal = wantedTypes.filter(wtype => wtype.indexOf("!") != 0); + return types.filter(type => wantedTypes.indexOf("!" + type) == -1 && + (!wantedTypesReal || !wantedTypesReal.length || wantedTypesReal.indexOf(type) != -1)); }; Attack.prototype.GetPreferredClasses = function(type) @@ -211,7 +219,7 @@ return []; }; -Attack.prototype.CanAttack = function(target) +Attack.prototype.CanAttack = function(target, wantedTypes) { let cmpFormation = Engine.QueryInterface(target, IID_Formation); if (cmpFormation) @@ -222,20 +230,36 @@ if (!cmpThisPosition || !cmpTargetPosition || !cmpThisPosition.IsInWorld() || !cmpTargetPosition.IsInWorld()) return false; + let cmpIdentity = Engine.QueryInterface(target, IID_Identity); + if (!cmpIdentity) + return false; + + let targetClasses = cmpIdentity.GetClassesList(); + if (targetClasses.indexOf("Domestic") != -1 && this.template.Slaughter && + (!wantedTypes || !wantedTypes.filter(wType => wType.indexOf("!") != 0).length)) + return true; + + let cmpEntityPlayer = QueryOwnerInterface(this.entity); + let cmpTargetPlayer = QueryOwnerInterface(target); + if (!cmpTargetPlayer || !cmpEntityPlayer) + return false; + + let types = this.GetAttackTypes(wantedTypes); + let entityOwner = cmpEntityPlayer.GetPlayerID(); + let targetOwner = cmpTargetPlayer.GetPlayerID(); + let cmpCapturable = QueryMiragedInterface(target, IID_Capturable); + // Check if the relative height difference is larger than the attack range // If the relative height is bigger, it means they will never be able to // reach each other, no matter how close they come. let heightDiff = Math.abs(cmpThisPosition.GetHeightOffset() - cmpTargetPosition.GetHeightOffset()); - const cmpIdentity = Engine.QueryInterface(target, IID_Identity); - if (!cmpIdentity) - return undefined; - - const targetClasses = cmpIdentity.GetClassesList(); - - for (let type of this.GetAttackTypes()) + for (let type of types) { - if (type == "Capture" && !QueryMiragedInterface(target, IID_Capturable)) + if (type != "Capture" && !cmpEntityPlayer.IsEnemy(targetOwner)) + continue; + + if (type == "Capture" && (!cmpCapturable || !cmpCapturable.CanCapture(entityOwner))) continue; if (heightDiff > this.GetRange(type).max) @@ -301,7 +325,7 @@ { // TODO: Formation against formation needs review let types = this.GetAttackTypes(); - return ["Ranged", "Melee", "Capture"].find(attack => types.indexOf(attack) != -1); + return g_AttackTypes.find(attack => types.indexOf(attack) != -1); } let cmpIdentity = Engine.QueryInterface(target, IID_Identity); Index: binaries/data/mods/public/simulation/components/Auras.js =================================================================== --- binaries/data/mods/public/simulation/components/Auras.js +++ binaries/data/mods/public/simulation/components/Auras.js @@ -32,11 +32,14 @@ Auras.prototype.GetDescriptions = function() { var ret = {}; - for (let name of this.GetAuraNames()) + for (let auraID of this.GetAuraNames()) { - let aura = this.auras[name]; - if (aura.auraName) - ret[aura.auraName] = aura.auraDescription || null; + let aura = this.auras[auraID]; + ret[auraID] = { + "name": aura.auraName, + "description": aura.auraDescription || null, + "radius": this.GetRange(auraID) || null + }; } return ret; }; @@ -63,6 +66,29 @@ return undefined; }; +/** + * Return the names of any range auras - used to render their ranges. + */ +Auras.prototype.GetVisualAuraRangeNames = function() +{ + return this.GetAuraNames().filter(auraName => this.IsRangeAura(auraName)); +}; + +Auras.prototype.GetLineTexture = function(name) +{ + return this.auras[name].rangeOverlay ? this.auras[name].rangeOverlay.lineTexture : "outline_border.png"; +}; + +Auras.prototype.GetLineTextureMask = function(name) +{ + return this.auras[name].rangeOverlay ? this.auras[name].rangeOverlay.lineTextureMask : "outline_border_mask.png"; +}; + +Auras.prototype.GetLineThickness = function(name) +{ + return this.auras[name].rangeOverlay ? this.auras[name].rangeOverlay.lineThickness : 0.2; +}; + Auras.prototype.GetClasses = function(name) { return this.auras[name].affects; Index: binaries/data/mods/public/simulation/components/BuildRestrictions.js =================================================================== --- binaries/data/mods/public/simulation/components/BuildRestrictions.js +++ binaries/data/mods/public/simulation/components/BuildRestrictions.js @@ -32,7 +32,7 @@ "" + "" + "" + - "" + + "" + "" + "" + "" + Index: binaries/data/mods/public/simulation/components/BuildingAI.js =================================================================== --- binaries/data/mods/public/simulation/components/BuildingAI.js +++ binaries/data/mods/public/simulation/components/BuildingAI.js @@ -222,7 +222,7 @@ BuildingAI.prototype.GetDefaultArrowCount = function() { var arrowCount = +this.template.DefaultArrowCount; - return ApplyValueModificationsToEntity("BuildingAI/DefaultArrowCount", arrowCount, this.entity); + return Math.round(ApplyValueModificationsToEntity("BuildingAI/DefaultArrowCount", arrowCount, this.entity)); }; BuildingAI.prototype.GetMaxArrowCount = function() Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -33,6 +33,7 @@ this.timeNotifications = []; this.entsRallyPointsDisplayed = []; this.entsWithAuraAndStatusBars = new Set(); + this.enabledVisualRangeOverlayTypes = {}; }; /* @@ -194,7 +195,7 @@ let playerEnt = cmpPlayerManager.GetPlayerByID(i); let cmpPlayerStatisticsTracker = Engine.QueryInterface(playerEnt, IID_StatisticsTracker); if (cmpPlayerStatisticsTracker) - ret.players[i].statistics = cmpPlayerStatisticsTracker.GetStatistics(); + ret.players[i].sequences = cmpPlayerStatisticsTracker.GetSequences(); } return ret; @@ -449,6 +450,7 @@ let types = cmpAttack.GetAttackTypes(); if (types.length) ret.attack = {}; + for (let type of types) { ret.attack[type] = cmpAttack.GetAttackStrengths(type); @@ -644,18 +646,7 @@ let auraNames = template.Auras._string.split(/\s+/); let cmpDataTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_DataTemplateManager); for (let name of auraNames) - { - let auraTemplate = cmpDataTemplateManager.GetAuraTemplate(name); - if (!auraTemplate) - { - // The following warning is perhaps useless since it's yet done in DataTemplateManager - warn("Tried to get data for invalid aura: " + name); - continue; - } - aurasTemplate[name] = {}; - aurasTemplate[name].auraName = auraTemplate.auraName || null; - aurasTemplate[name].auraDescription = auraTemplate.auraDescription || null; - } + aurasTemplate[name] = cmpDataTemplateManager.GetAuraTemplate(name); return GetTemplateDataHelper(template, player, aurasTemplate, Resources); }; @@ -895,9 +886,20 @@ } cmpSelectable.SetSelectionHighlight({ "r": color.r, "g": color.g, "b": color.b, "a": cmd.alpha }, cmd.selected); + + let cmpRangeVisualization = Engine.QueryInterface(ent, IID_RangeVisualization); + if (!cmpRangeVisualization || player != owner && player != -1) + continue; + + cmpRangeVisualization.SetEnabled(cmd.selected, this.enabledVisualRangeOverlayTypes); } }; +GuiInterface.prototype.EnableVisualRangeOverlayType = function(player, data) +{ + this.enabledVisualRangeOverlayTypes[data.type] = data.enabled; +}; + GuiInterface.prototype.GetEntitiesWithStatusBars = function() { return [...this.entsWithAuraAndStatusBars]; @@ -938,6 +940,16 @@ } }; +GuiInterface.prototype.SetRangeOverlays = function(player, cmd) +{ + for (let ent of cmd.entities) + { + let cmpRangeVisualization = Engine.QueryInterface(ent, IID_RangeVisualization); + if (cmpRangeVisualization) + cmpRangeVisualization.SetEnabled(cmd.enabled, this.enabledVisualRangeOverlayTypes); + } +}; + GuiInterface.prototype.GetPlayerEntities = function(player) { return Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEntitiesByPlayer(player); @@ -1832,37 +1844,10 @@ return result; }; -GuiInterface.prototype.CanCapture = function(player, data) -{ - let cmpAttack = Engine.QueryInterface(data.entity, IID_Attack); - if (!cmpAttack) - return false; - - let owner = QueryOwnerInterface(data.entity).GetPlayerID(); - - let cmpCapturable = QueryMiragedInterface(data.target, IID_Capturable); - if (cmpCapturable && cmpCapturable.CanCapture(owner) && cmpAttack.GetAttackTypes().indexOf("Capture") != -1) - return cmpAttack.CanAttack(data.target); - - return false; -}; - GuiInterface.prototype.CanAttack = function(player, data) { let cmpAttack = Engine.QueryInterface(data.entity, IID_Attack); - if (!cmpAttack) - return false; - - let cmpEntityPlayer = QueryOwnerInterface(data.entity, IID_Player); - let cmpTargetPlayer = QueryOwnerInterface(data.target, IID_Player); - if (!cmpEntityPlayer || !cmpTargetPlayer) - return false; - - // if the owner is an enemy, it's up to the attack component to decide - if (cmpEntityPlayer.IsEnemy(cmpTargetPlayer.GetPlayerID())) - return cmpAttack.CanAttack(data.target); - - return false; + return cmpAttack && cmpAttack.CanAttack(data.target, data.types || undefined); }; /* @@ -2007,7 +1992,6 @@ "HasIdleUnits": 1, "GetTradingRouteGain": 1, "GetTradingDetails": 1, - "CanCapture": 1, "CanAttack": 1, "GetBatchTime": 1, @@ -2017,6 +2001,8 @@ "SetObstructionDebugOverlay": 1, "SetMotionDebugOverlay": 1, "SetRangeDebugOverlay": 1, + "EnableVisualRangeOverlayType": 1, + "SetRangeOverlays": 1, "GetTraderNumber": 1, "GetTradingGoods": 1, Index: binaries/data/mods/public/simulation/components/RangeVisualization.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/RangeVisualization.js @@ -0,0 +1,78 @@ +function RangeVisualization() {} + +RangeVisualization.prototype.Schema = ""; + +RangeVisualization.prototype.Init = function() +{ + this.enabled = false; + this.enabledRangeTypes = { + "Aura": false + }; + + this.rangeVisualizations = new Map(); + for (let type in this.enabledRangeTypes) + this["GetVisual" + type + "Ranges"](type); +}; + +// The GUI enables visualizations +RangeVisualization.prototype.Serialize = null; + +RangeVisualization.prototype.Deserialize = function(data) +{ + this.Init(); +}; + +RangeVisualization.prototype.GetVisualAuraRanges = function(type) +{ + let cmpAuras = Engine.QueryInterface(this.entity, IID_Auras); + if (!cmpAuras) + return; + + this.rangeVisualizations.set(type, []); + + for (let auraName of cmpAuras.GetVisualAuraRangeNames()) + this.rangeVisualizations.get(type).push({ + "radius": cmpAuras.GetRange(auraName), + "texture": cmpAuras.GetLineTexture(auraName), + "textureMask": cmpAuras.GetLineTextureMask(auraName), + "thickness": cmpAuras.GetLineThickness(auraName), + }); +}; + +RangeVisualization.prototype.SetEnabled = function(enabled, enabledRangeTypes) +{ + this.enabled = enabled; + this.enabledRangeTypes = enabledRangeTypes; + + this.RegenerateRangeVisualizations(); +}; + +RangeVisualization.prototype.RegenerateRangeVisualizations = function() +{ + let cmpSelectable = Engine.QueryInterface(this.entity, IID_Selectable); + if (!cmpSelectable) + return; + + cmpSelectable.ResetRangeOverlays(); + + if (!this.enabled) + return; + + // Only render individual range types that have been enabled + for (let rangeOverlayType of this.rangeVisualizations.keys()) + if (this.enabledRangeTypes[rangeOverlayType]) + for (let rangeOverlay of this.rangeVisualizations.get(rangeOverlayType)) + cmpSelectable.AddRangeOverlay( + rangeOverlay.radius, + rangeOverlay.texture, + rangeOverlay.textureMask, + rangeOverlay.thickness); +}; + +RangeVisualization.prototype.OnOwnershipChanged = function(msg) +{ + if (this.enabled && msg.to != -1) + this.RegenerateRangeVisualizations(); +}; + +Engine.RegisterComponentType(IID_RangeVisualization, "RangeVisualization", RangeVisualization); Index: binaries/data/mods/public/simulation/components/StatisticsTracker.js =================================================================== --- binaries/data/mods/public/simulation/components/StatisticsTracker.js +++ binaries/data/mods/public/simulation/components/StatisticsTracker.js @@ -1,5 +1,7 @@ function StatisticsTracker() {} +const g_UpdateSequenceInterval = 30 * 1000; + StatisticsTracker.prototype.Schema = ""; @@ -142,6 +144,10 @@ this.lootCollected = 0; this.peakPercentMapControlled = 0; this.teamPeakPercentMapControlled = 0; + + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + this.updateTimer = cmpTimer.SetInterval( + this.entity, IID_StatisticsTracker, "updateSequences", 0, g_UpdateSequenceInterval); }; /** @@ -195,11 +201,21 @@ }; }; +StatisticsTracker.prototype.GetSequences = function() +{ + let ret = clone(this.sequences); + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + + ret.time.push(cmpTimer.GetTime() / 1000); + this.PushValue(this.GetStatistics(), ret); + return ret; +} + /** * Increments counter associated with certain entity/counter and type of given entity. - * @param cmpIdentity The entity identity component - * @param counter The name of the counter to increment (e.g. "unitsTrained") - * @param type The type of the counter (e.g. "workers") + * @param cmpIdentity - the entity identity component. + * @param counter - the name of the counter to increment (e.g. "unitsTrained"). + * @param type - the type of the counter (e.g. "workers"). */ StatisticsTracker.prototype.CounterIncrement = function(cmpIdentity, counter, type) { @@ -356,9 +372,9 @@ }; /** - * @param type Generic type of resource (string) - * @param amount Amount of resource, whick should be added (integer) - * @param specificType Specific type of resource (string, optional) + * @param {string} type - generic type of resource. + * @param {number} amount - amount of resource, whick should be added. + * @param {string} specificType - specific type of resource. */ StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amount, specificType) { @@ -369,8 +385,8 @@ }; /** - * @param type Generic type of resource (string) - * @param amount Amount of resource, which should be added (integer) + * @param {string} type - generic type of resource. + * @param {number} amount - amount of resource, which should be added. */ StatisticsTracker.prototype.IncreaseResourceUsedCounter = function(type, amount) { @@ -493,4 +509,41 @@ this.teamPeakPercentMapControlled = newPercent; }; +/** + * Adds the values of fromData to the end of the arrays of toData. + * If toData misses the needed array, one will be created. + * + * @param fromData - an object of values or a value. + * @param toData - an object of arrays or an array. +**/ +StatisticsTracker.prototype.PushValue = function(fromData, toData) +{ + if (typeof fromData == "object") + for (let prop in fromData) + { + if (typeof toData[prop] != "object") + toData[prop] = [fromData[prop]]; + else + this.PushValue(fromData[prop], toData[prop]); + } + else + toData.push(fromData); +}; + +StatisticsTracker.prototype.updateSequences = function() +{ + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + + // Don't do this on Init, because GetStatistics doesn't work in this state of the game + // This is probably, because the simulation hasn't totally started/initialized and we query some simulation values + if (!this.sequences) + { + this.sequences = clone(this.GetStatistics()); + this.sequences.time = []; + } + + this.sequences.time.push(cmpTimer.GetTime() / 1000); + this.PushValue(this.GetStatistics(), this.sequences); +} + Engine.RegisterComponentType(IID_StatisticsTracker, "StatisticsTracker", StatisticsTracker); Index: binaries/data/mods/public/simulation/components/interfaces/RangeVisualization.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/interfaces/RangeVisualization.js @@ -0,0 +1 @@ +Engine.RegisterInterface("RangeVisualization"); Index: binaries/data/mods/public/simulation/components/tests/test_Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Attack.js +++ binaries/data/mods/public/simulation/components/tests/test_Attack.js @@ -10,7 +10,7 @@ let entityID = 903; -function attackComponentTest(defenderClass, test_function) +function attackComponentTest(defenderClass, isEnemy, test_function) { ResetState(); @@ -22,7 +22,8 @@ }); AddMock(playerEnt1, IID_Player, { - "GetPlayerID": () => 1 + "GetPlayerID": () => 1, + "IsEnemy": () => isEnemy }); } @@ -87,6 +88,10 @@ "HasClass": className => className == defenderClass }); + AddMock(defender, IID_Ownership, { + "GetOwner": () => 1 + }); + AddMock(defender, IID_Position, { "IsInWorld": () => true, "GetHeightOffset": () => 0 @@ -96,9 +101,19 @@ } // Validate template getter functions -attackComponentTest(undefined, (attacker, cmpAttack, defender) => { +attackComponentTest(undefined, true ,(attacker, cmpAttack, defender) => { TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(), ["Melee", "Ranged", "Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes([]), ["Melee", "Ranged", "Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["Melee", "Ranged", "Capture"]), ["Melee", "Ranged", "Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["Melee", "Ranged"]), ["Melee", "Ranged"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["Capture"]), ["Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["Melee", "!Melee"]), []); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["!Melee"]), ["Ranged", "Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["!Melee", "!Ranged"]), ["Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["Capture", "!Ranged"]), ["Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(["Capture", "Melee", "!Ranged"]), ["Melee", "Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetPreferredClasses("Melee"), ["FemaleCitizen"]); TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetRestrictedClasses("Melee"), ["Elephant", "Archer"]); TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetFullAttackRange(), { "min": 0, "max": 80 }); @@ -122,7 +137,7 @@ }); for (let className of ["Infantry", "Cavalry"]) - attackComponentTest(className, (attacker, cmpAttack, defender) => { + attackComponentTest(className, true, (attacker, cmpAttack, defender) => { TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackBonus("Melee", defender), className == "Cavalry" ? 2 : 1); TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackBonus("Ranged", defender), 1); TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackBonus("Capture", defender), 1); @@ -130,13 +145,13 @@ }); // CanAttack rejects elephant attack due to RestrictedClasses -attackComponentTest("Elephant", (attacker, cmpAttack, defender) => { +attackComponentTest("Elephant", true, (attacker, cmpAttack, defender) => { TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender), false); }); function testGetBestAttackAgainst(defenderClass, bestAttack, isBuilding = false) { - attackComponentTest(defenderClass, (attacker, cmpAttack, defender) => { + attackComponentTest(defenderClass, true, (attacker, cmpAttack, defender) => { if (isBuilding) AddMock(defender, IID_Capturable, { @@ -147,6 +162,14 @@ }); TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender), true); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, []), true); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Ranged"]), true); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["!Melee"]), true); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Capture"]), isBuilding); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Melee", "Capture"]), defenderClass != "Archer"); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Ranged", "Capture"]), true); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["!Ranged", "!Melee"]), isBuilding || defenderClass == "Domestic"); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Melee", "!Melee"]), false); let allowCapturing = [true]; if (!isBuilding) @@ -155,6 +178,40 @@ for (let ac of allowCapturing) TS_ASSERT_EQUALS(cmpAttack.GetBestAttackAgainst(defender, ac), bestAttack); }); + + attackComponentTest(defenderClass, false, (attacker, cmpAttack, defender) => { + + if (isBuilding) + AddMock(defender, IID_Capturable, { + "CanCapture": playerID => { + TS_ASSERT_EQUALS(playerID, 1); + return true; + } + }); + + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender), isBuilding || defenderClass == "Domestic"); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, []), isBuilding || defenderClass == "Domestic"); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Ranged"]), false); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["!Melee"]), isBuilding || defenderClass == "Domestic"); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Capture"]), isBuilding); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Melee", "Capture"]), isBuilding); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Ranged", "Capture"]), isBuilding); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["!Ranged", "!Melee"]), isBuilding || defenderClass == "Domestic"); + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender, ["Melee", "!Melee"]), false); + + let allowCapturing = [true]; + if (!isBuilding) + allowCapturing.push(false); + + let attack = undefined; + if (defenderClass == "Domestic") + attack = "Slaughter"; + else if (defenderClass == "Structure") + attack = "Capture"; + + for (let ac of allowCapturing) + TS_ASSERT_EQUALS(cmpAttack.GetBestAttackAgainst(defender, ac), bestAttack); + }); } testGetBestAttackAgainst("FemaleCitizen", "Melee"); Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -47,7 +47,7 @@ "aiAnalysisInfluenceGroup": resource == "food" ? "ignore" : resource == "wood" ? "abundant" : "sparse" - }), + }) }; var cmp = ConstructComponent(SYSTEM_ENTITY, "GuiInterface"); @@ -70,22 +70,22 @@ AddMock(SYSTEM_ENTITY, IID_PlayerManager, { GetNumPlayers: function() { return 2; }, - GetPlayerByID: function(id) { TS_ASSERT(id === 0 || id === 1); return 100+id; }, + GetPlayerByID: function(id) { TS_ASSERT(id === 0 || id === 1); return 100+id; } }); AddMock(SYSTEM_ENTITY, IID_RangeManager, { GetLosVisibility: function(ent, player) { return "visible"; }, - GetLosCircular: function() { return false; }, + GetLosCircular: function() { return false; } }); AddMock(SYSTEM_ENTITY, IID_TemplateManager, { GetCurrentTemplateName: function(ent) { return "example"; }, - GetTemplate: function(name) { return ""; }, + GetTemplate: function(name) { return ""; } }); AddMock(SYSTEM_ENTITY, IID_Timer, { GetTime: function() { return 0; }, - SetTimeout: function(ent, iid, funcname, time, data) { return 0; }, + SetTimeout: function(ent, iid, funcname, time, data) { return 0; } }); AddMock(100, IID_Player, { @@ -112,7 +112,7 @@ GetDisabledTechnologies: function() { return {}; }, GetSpyCostMultiplier: function() { return 1; }, HasSharedDropsites: function() { return false; }, - HasSharedLos: function() { return false; }, + HasSharedLos: function() { return false; } }); AddMock(100, IID_EntityLimits, { @@ -128,7 +128,7 @@ GetResearchedTechs: function() { return {}; }, GetClassCounts: function() { return {}; }, GetTypeCountsByClass: function() { return {}; }, - GetTechModifications: function() { return {}; }, + GetTechModifications: function() { return {}; } }); AddMock(100, IID_StatisticsTracker, { @@ -139,39 +139,39 @@ "wood": 0, "metal": 0, "stone": 0, - "vegetarianFood": 0, + "vegetarianFood": 0 }, "percentMapExplored": 10 }; }, - GetStatistics: function() { + GetSequences: function() { return { - "unitsTrained": 10, - "unitsLost": 9, - "buildingsConstructed": 5, - "buildingsCaptured": 7, - "buildingsLost": 4, - "civCentresBuilt": 1, + "unitsTrained": [0, 10], + "unitsLost": [0, 42], + "buildingsConstructed": [1, 3], + "buildingsCaptured": [3, 7], + "buildingsLost": [3, 10], + "civCentresBuilt": [4, 10], "resourcesGathered": { - "food": 100, - "wood": 0, - "metal": 0, - "stone": 0, - "vegetarianFood": 0, + "food": [5, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0] }, - "treasuresCollected": 0, - "lootCollected": 0, - "percentMapExplored": 10, - "teamPercentMapExplored": 10, - "percentMapControlled": 10, - "teamPercentMapControlled": 10, - "peakPercentOfMapControlled": 10, - "teamPeakPercentOfMapControlled": 10 + "treasuresCollected": [1, 20], + "lootCollected": [0, 2], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] }; }, IncreaseTrainedUnitsCounter: function() { return 1; }, IncreaseConstructedBuildingsCounter: function() { return 1; }, - IncreaseBuiltCivCentresCounter: function() { return 1; }, + IncreaseBuiltCivCentresCounter: function() { return 1; } }); AddMock(101, IID_Player, { @@ -198,7 +198,7 @@ GetDisabledTechnologies: function() { return {}; }, GetSpyCostMultiplier: function() { return 1; }, HasSharedDropsites: function() { return false; }, - HasSharedLos: function() { return false; }, + HasSharedLos: function() { return false; } }); AddMock(101, IID_EntityLimits, { @@ -214,7 +214,7 @@ GetResearchedTechs: function() { return {}; }, GetClassCounts: function() { return {}; }, GetTypeCountsByClass: function() { return {}; }, - GetTechModifications: function() { return {}; }, + GetTechModifications: function() { return {}; } }); AddMock(101, IID_StatisticsTracker, { @@ -225,39 +225,39 @@ "wood": 0, "metal": 0, "stone": 0, - "vegetarianFood": 0, + "vegetarianFood": 0 }, "percentMapExplored": 10 }; }, - GetStatistics: function() { + GetSequences: function() { return { - "unitsTrained": 10, - "unitsLost": 9, - "buildingsConstructed": 5, - "buildingsCaptured": 7, - "buildingsLost": 4, - "civCentresBuilt": 1, + "unitsTrained": [0, 10], + "unitsLost": [0, 9], + "buildingsConstructed": [0, 5], + "buildingsCaptured": [0, 7], + "buildingsLost": [0, 4], + "civCentresBuilt": [0, 1], "resourcesGathered": { - "food": 100, - "wood": 0, - "metal": 0, - "stone": 0, - "vegetarianFood": 0, + "food": [0, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0] }, - "treasuresCollected": 0, - "lootCollected": 0, - "percentMapExplored": 10, - "teamPercentMapExplored": 10, - "percentMapControlled": 10, - "teamPercentMapControlled": 10, - "peakPercentOfMapControlled": 10, - "teamPeakPercentOfMapControlled": 10 + "treasuresCollected": [0, 0], + "lootCollected": [0, 0], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] }; }, IncreaseTrainedUnitsCounter: function() { return 1; }, IncreaseConstructedBuildingsCounter: function() { return 1; }, - IncreaseBuiltCivCentresCounter: function() { return 1; }, + IncreaseBuiltCivCentresCounter: function() { return 1; } }); // Note: property order matters when using TS_ASSERT_UNEVAL_EQUALS, @@ -305,10 +305,10 @@ wood: 0, metal: 0, stone: 0, - vegetarianFood: 0, + vegetarianFood: 0 }, percentMapExplored: 10 - }, + } }, { name: "Player 2", @@ -350,10 +350,10 @@ wood: 0, metal: 0, stone: 0, - vegetarianFood: 0, + vegetarianFood: 0 }, percentMapExplored: 10 - }, + } } ], circularMap: false, @@ -370,140 +370,160 @@ "food": "Food", "metal": "Metal", "stone": "Stone", - "wood": "Wood", + "wood": "Wood" }, "aiInfluenceGroups": { "food": "ignore", "metal": "sparse", "stone": "sparse", - "wood": "abundant", + "wood": "abundant" } - }, + } }); TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), { - players: [ + "players": [ { - name: "Player 1", - civ: "gaia", - color: { r:1, g:1, b:1, a:1 }, - controlsAll: false, - popCount: 10, - popLimit: 20, - popMax: 200, - panelEntities: [], - resourceCounts: { food: 100 }, - trainingBlocked: false, - state: "active", - team: -1, - teamsLocked: false, - cheatsEnabled: false, - disabledTemplates: {}, - disabledTechnologies: {}, - hasSharedDropsites: false, - hasSharedLos: false, - spyCostMultiplier: 1, - phase: "village", - isAlly: [false, false], - isMutualAlly: [false, false], - isNeutral: [false, false], - isEnemy: [true, true], - entityLimits: {"Foo": 10}, - entityCounts: {"Foo": 5}, - entityLimitChangers: {"Foo": {}}, - researchQueued: {}, - researchStarted: {}, - researchedTechs: {}, - classCounts: {}, - typeCountsByClass: {}, - canBarter: false, - statistics: { - unitsTrained: 10, - unitsLost: 9, - buildingsConstructed: 5, - buildingsCaptured: 7, - buildingsLost: 4, - civCentresBuilt: 1, - resourcesGathered: { - food: 100, - wood: 0, - metal: 0, - stone: 0, - vegetarianFood: 0, + "name": "Player 1", + "civ": "gaia", + "color": { "r":1, "g":1, "b":1, "a":1 }, + "controlsAll": false, + "popCount": 10, + "popLimit": 20, + "popMax": 200, + "panelEntities": [], + "resourceCounts": { "food": 100 }, + "trainingBlocked": false, + "state": "active", + "team": -1, + "teamsLocked": false, + "cheatsEnabled": false, + "disabledTemplates": {}, + "disabledTechnologies": {}, + "hasSharedDropsites": false, + "hasSharedLos": false, + "spyCostMultiplier": 1, + "phase": "village", + "isAlly": [false, false], + "isMutualAlly": [false, false], + "isNeutral": [false, false], + "isEnemy": [true, true], + "entityLimits": {"Foo": 10}, + "entityCounts": {"Foo": 5}, + "entityLimitChangers": {"Foo": {}}, + "researchQueued": {}, + "researchStarted": {}, + "researchedTechs": {}, + "classCounts": {}, + "typeCountsByClass": {}, + "canBarter": false, + "statistics": { + "resourcesGathered": { + "food": 100, + "wood": 0, + "metal": 0, + "stone": 0, + "vegetarianFood": 0 }, - treasuresCollected: 0, - lootCollected: 0, - percentMapExplored: 10, - teamPercentMapExplored: 10, - percentMapControlled: 10, - teamPercentMapControlled: 10, - peakPercentOfMapControlled: 10, - teamPeakPercentOfMapControlled: 10 + "percentMapExplored": 10 }, + "sequences": { + "unitsTrained": [0, 10], + "unitsLost": [0, 42], + "buildingsConstructed": [1, 3], + "buildingsCaptured": [3, 7], + "buildingsLost": [3, 10], + "civCentresBuilt": [4, 10], + "resourcesGathered": { + "food": [5, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0] + }, + "treasuresCollected": [1, 20], + "lootCollected": [0, 2], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] + } }, { - name: "Player 2", - civ: "mace", - color: { r:1, g:0, b:0, a:1 }, - controlsAll: true, - popCount: 40, - popLimit: 30, - popMax: 300, - panelEntities: [], - resourceCounts: { food: 200 }, - trainingBlocked: false, - state: "active", - team: -1, - teamsLocked: false, - cheatsEnabled: false, - disabledTemplates: {}, - disabledTechnologies: {}, - hasSharedDropsites: false, - hasSharedLos: false, - spyCostMultiplier: 1, - phase: "village", - isAlly: [true, true], - isMutualAlly: [false, false], - isNeutral: [false, false], - isEnemy: [false, false], - entityLimits: {"Bar": 20}, - entityCounts: {"Bar": 0}, - entityLimitChangers: {"Bar": {}}, - researchQueued: {}, - researchStarted: {}, - researchedTechs: {}, - classCounts: {}, - typeCountsByClass: {}, - canBarter: false, - statistics: { - unitsTrained: 10, - unitsLost: 9, - buildingsConstructed: 5, - buildingsCaptured: 7, - buildingsLost: 4, - civCentresBuilt: 1, - resourcesGathered: { - food: 100, - wood: 0, - metal: 0, - stone: 0, - vegetarianFood: 0, + "name": "Player 2", + "civ": "mace", + "color": { "r":1, "g":0, "b":0, "a":1 }, + "controlsAll": true, + "popCount": 40, + "popLimit": 30, + "popMax": 300, + "panelEntities": [], + "resourceCounts": { "food": 200 }, + "trainingBlocked": false, + "state": "active", + "team": -1, + "teamsLocked": false, + "cheatsEnabled": false, + "disabledTemplates": {}, + "disabledTechnologies": {}, + "hasSharedDropsites": false, + "hasSharedLos": false, + "spyCostMultiplier": 1, + "phase": "village", + "isAlly": [true, true], + "isMutualAlly": [false, false], + "isNeutral": [false, false], + "isEnemy": [false, false], + "entityLimits": {"Bar": 20}, + "entityCounts": {"Bar": 0}, + "entityLimitChangers": {"Bar": {}}, + "researchQueued": {}, + "researchStarted": {}, + "researchedTechs": {}, + "classCounts": {}, + "typeCountsByClass": {}, + "canBarter": false, + "statistics": { + "resourcesGathered": { + "food": 100, + "wood": 0, + "metal": 0, + "stone": 0, + "vegetarianFood": 0 }, - treasuresCollected: 0, - lootCollected: 0, - percentMapExplored: 10, - teamPercentMapExplored: 10, - percentMapControlled: 10, - teamPercentMapControlled: 10, - peakPercentOfMapControlled: 10, - teamPeakPercentOfMapControlled: 10 + "percentMapExplored": 10 }, + "sequences": { + "unitsTrained": [0, 10], + "unitsLost": [0, 9], + "buildingsConstructed": [0, 5], + "buildingsCaptured": [0, 7], + "buildingsLost": [0, 4], + "civCentresBuilt": [0, 1], + "resourcesGathered": { + "food": [0, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0] + }, + "treasuresCollected": [0, 0], + "lootCollected": [0, 0], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] + } } ], - circularMap: false, - timeElapsed: 0, - gameType: "conquest", - alliedVictory: false, + "circularMap": false, + "timeElapsed": 0, + "gameType": "conquest", + "alliedVictory": false, "barterPrices": { "buy": { "food": 150 }, "sell": { "food": 25 } @@ -514,15 +534,15 @@ "food": "Food", "metal": "Metal", "stone": "Stone", - "wood": "Wood", + "wood": "Wood" }, "aiInfluenceGroups": { "food": "ignore", "metal": "sparse", "stone": "sparse", - "wood": "abundant", + "wood": "abundant" } - }, + } }); @@ -537,7 +557,7 @@ GetMaxHitpoints: function() { return 60; }, IsRepairable: function() { return false; }, IsUnhealable: function() { return false; }, - IsUndeletable: function() { return false; }, + IsUndeletable: function() { return false; } }); AddMock(10, IID_Identity, { @@ -545,7 +565,7 @@ GetVisibleClassesList: function() { return ["class3", "class4"]; }, GetRank: function() { return "foo"; }, GetSelectionGroupName: function() { return "Selection Group Name"; }, - HasClass: function() { return true; }, + HasClass: function() { return true; } }); AddMock(10, IID_Position, { @@ -558,7 +578,7 @@ }, IsInWorld: function() { return true; - }, + } }); AddMock(10, IID_ResourceTrickle, { @@ -583,7 +603,7 @@ rank: "foo", classes: ["class1", "class2"], visibleClasses: ["class3", "class4"], - selectionGroupName: "Selection Group Name", + selectionGroupName: "Selection Group Name" }, fogging: null, foundation: null, @@ -607,7 +627,7 @@ maxHitpoints: 60, needsRepair: false, needsHeal: true, - canDelete: true, + canDelete: true }); TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedEntityState(-1, 10), { @@ -634,5 +654,5 @@ "metal": 9 } }, - speed: null, + speed: null }); Index: binaries/data/mods/public/simulation/data/auras/structures/cart_super_dock_repair.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/cart_super_dock_repair.json +++ binaries/data/mods/public/simulation/data/auras/structures/cart_super_dock_repair.json @@ -4,7 +4,7 @@ "modifications": [ { "value": "Health/RegenRate", "add": 10 } ], - "auraName": "Repairing Ship aura", + "auraName": "Dockyard Repairs", "auraDescription": "Heals garrisoned ship at 10 HP per second.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/structures/epic_temple_heal.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/epic_temple_heal.json +++ binaries/data/mods/public/simulation/data/auras/structures/epic_temple_heal.json @@ -5,7 +5,7 @@ "modifications": [ { "value": "Health/RegenRate", "add": 3 } ], - "auraName": "Healing Aura", + "auraName": "Blessing of the Gods", "auraDescription": "Heals nearby units at 3 HP per second.", "overlayIcon": "art/textures/ui/session/auras/heal.png" } Index: binaries/data/mods/public/simulation/data/auras/structures/iber_monument.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/iber_monument.json +++ binaries/data/mods/public/simulation/data/auras/structures/iber_monument.json @@ -10,7 +10,7 @@ { "value": "Attack/Ranged/Pierce", "multiply": 1.20 }, { "value": "Attack/Ranged/Crush", "multiply": 1.20 } ], - "auraName": "Defensive Aura", + "auraName": "Religious Fervor", "auraDescription": "Gives all iberian soldiers in aura +20% attack boost. Effect Range: 50 meters", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/structures/library.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/library.json +++ binaries/data/mods/public/simulation/data/auras/structures/library.json @@ -9,6 +9,6 @@ { "value": "ProductionQueue/TechCostMultiplier/time", "multiply": 0.9 } ], "auraDescription": "Reduce the cost and research time of technologies by 10% per library owned.", - "auraName": "Library Aura", + "auraName": "Power of Knowledge", "stackable": true } Index: binaries/data/mods/public/simulation/data/auras/structures/loyalty_regen.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/loyalty_regen.json +++ binaries/data/mods/public/simulation/data/auras/structures/loyalty_regen.json @@ -5,7 +5,7 @@ "modifications": [ { "value": "Capturable/GarrisonRegenRate", "multiply": 1.5 } ], - "auraName": "Loyalty Aura", + "auraName": "Loyalty", "auraDescription": "All structures in range +50% garrisoned loyalty regeneration rate.", "overlayIcon": "art/textures/ui/session/auras/build_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/structures/temple_heal.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/temple_heal.json +++ binaries/data/mods/public/simulation/data/auras/structures/temple_heal.json @@ -5,7 +5,7 @@ "modifications": [ { "value": "Health/RegenRate", "add": 1 } ], - "auraName": "Healing Aura", + "auraName": "Medical Treatment", "auraDescription": "Heals nearby units at 1 HP per second.", "overlayIcon": "art/textures/ui/session/auras/heal.png" } Index: binaries/data/mods/public/simulation/data/auras/structures/theatron.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/theatron.json +++ binaries/data/mods/public/simulation/data/auras/structures/theatron.json @@ -5,6 +5,6 @@ { "value": "TerritoryInfluence/Radius", "multiply": 1.20 } ], "auraDescription": "Increase territory influence by 20% per theatron owned.", - "auraName": "Hellenization.", + "auraName": "Hellenization", "stackable": true } Index: binaries/data/mods/public/simulation/data/auras/structures/wonder_pop_1.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/wonder_pop_1.json +++ binaries/data/mods/public/simulation/data/auras/structures/wonder_pop_1.json @@ -4,7 +4,7 @@ "modifications": [ { "value": "Player/MaxPopulation", "add": 10 } ], - "auraName": "Wonder Aura", + "auraName": "Symbol of Greatness", "auraDescription": "Increase the population limit by 10 per wonder owned.", "stackable": true } Index: binaries/data/mods/public/simulation/data/auras/structures/wonder_pop_2.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/structures/wonder_pop_2.json +++ binaries/data/mods/public/simulation/data/auras/structures/wonder_pop_2.json @@ -4,7 +4,7 @@ "modifications": [ { "value": "Player/MaxPopulation", "add": 40 } ], - "auraName": "Glorious Expansion Aura", + "auraName": "Glorious Expansion", "auraDescription": "Further increase the population limit by 40 per wonder owned (requires \"Glorious Expansion\" tech).", "requiredTechnology": "pop_wonder", "stackable": true Index: binaries/data/mods/public/simulation/data/auras/units/catafalques/rome_catafalque_2.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/catafalques/rome_catafalque_2.json +++ binaries/data/mods/public/simulation/data/auras/units/catafalques/rome_catafalque_2.json @@ -6,6 +6,6 @@ { "value": "Armour/Hack", "add": 1 }, { "value": "Armour/Crush", "add": 1 } ], - "auraName": "Founder and Defender of the Republic Aura", + "auraName": "Founder and Defender of the Republic", "auraDescription": "Brutus was one of the key figures in the overthrow of the monarchy and the founding of the Roman Republic. Later, as consul he led a Roman army to victory against the Etruscan King Tarquinius who sought to retake the throne.\n+1 armor for all units and siege engines." } Index: binaries/data/mods/public/simulation/data/auras/units/catafalques/sele_catafalque_3.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/catafalques/sele_catafalque_3.json +++ binaries/data/mods/public/simulation/data/auras/units/catafalques/sele_catafalque_3.json @@ -7,6 +7,6 @@ { "value": "Cost/Resources/metal", "multiply": 0.9 }, { "value": "Cost/Resources/stone", "multiply": 0.9 } ], - "auraName": "Basileus Megas (Great King)", + "auraName": "Basileus Megas", "auraDescription": "Son of Selecus Nicator, Antiochus succeeded in the formidable task of keeping the empire together, meanwhile founding temples and defeating the invading Gauls with war elephants.\n-10% cost for War Elephants." } Index: binaries/data/mods/public/simulation/data/auras/units/female_inspiration.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/female_inspiration.json +++ binaries/data/mods/public/simulation/data/auras/units/female_inspiration.json @@ -6,7 +6,12 @@ { "value": "Builder/Rate", "multiply": 1.1 }, { "value": "ResourceGatherer/BaseSpeed", "multiply": 1.1 } ], - "auraName": "Inspiration Aura", + "auraName": "Inspiration", "auraDescription": "Nearby citizen soldiers work 10% faster.", - "overlayIcon": "art/textures/ui/session/auras/buildgather_bonus.png" + "overlayIcon": "art/textures/ui/session/auras/buildgather_bonus.png", + "rangeOverlay" : { + "lineTexture": "outline_border.png", + "lineTextureMask": "outline_border_mask.png", + "lineThickness": 0.075 + } } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_pericles_1.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_pericles_1.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_pericles_1.json @@ -5,7 +5,7 @@ "modifications": [ { "value": "Builder/Rate", "multiply": 1.15 } ], - "auraName": "Builder Aura", + "auraName": "Periclean Building Program", "auraDescription": "Buildings construct 15% faster within his aura.", "overlayIcon": "art/textures/ui/session/auras/build_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_pericles_2.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_pericles_2.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_pericles_2.json @@ -4,6 +4,6 @@ "modifications": [ { "value": "Cost/Resources/stone", "add": -50 } ], - "auraName": "Acropolis Aura", + "auraName": "Temple to Athena", "auraDescription": "Temples are 50 stone cheaper during his lifetime." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_themistocles_1.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_themistocles_1.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_themistocles_1.json @@ -6,6 +6,6 @@ { "value": "UnitMotion/WalkSpeed", "multiply": 1.5 }, { "value": "UnitMotion/Run/Speed", "multiply": 1.5 } ], - "auraName": "Naval Commander Aura", + "auraName": "Naval Commander", "auraDescription": "When garrisoned in a ship, his ship is +50% faster." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_themistocles_2.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_themistocles_2.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/athen_hero_themistocles_2.json @@ -4,6 +4,6 @@ "modifications": [ { "value": "Cost/BuildTime", "multiply": 0.8 } ], - "auraName": "Naval Architect Aura", + "auraName": "Naval Architect", "auraDescription": "-20% build time for ships during his lifespan." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/brit_hero_caratacos.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/brit_hero_caratacos.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/brit_hero_caratacos.json @@ -5,6 +5,6 @@ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }, { "value": "UnitMotion/Run/Speed", "multiply": 1.15 } ], - "auraName": "Hero Aura", + "auraName": "Guerrilla Chief", "auraDescription": "All soldiers and siege engines +15% speed." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/brit_hero_cunobelin.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/brit_hero_cunobelin.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/brit_hero_cunobelin.json @@ -5,7 +5,7 @@ "modifications": [ { "value": "Health/RegenRate", "add": 1 } ], - "auraName": "Hero Aura", + "auraName": "Britannorum Rex", "auraDescription": "+1 HP per second healing rate. Effect Range: 30 meters.", "overlayIcon": "art/textures/ui/session/auras/heal.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_hamilcar.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_hamilcar.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_hamilcar.json @@ -5,6 +5,6 @@ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }, { "value": "UnitMotion/Run/Speed", "multiply": 1.15 } ], - "auraName": "Lightning Aura", + "auraName": "Lightning General", "auraDescription": "All soldiers and siege engines +15% speed." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_hannibal.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_hannibal.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_hannibal.json @@ -12,7 +12,7 @@ { "value": "Attack/Ranged/Crush", "multiply": 1.20 }, { "value": "Attack/Capture/Value", "add": 1 } ], - "auraName": "Tactician Aura", + "auraName": "Tactician", "auraDescription": "All allied soldiers and siege engines +20% attack and +1 capture within his vision range.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_maharbal.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_maharbal.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/cart_hero_maharbal.json @@ -7,7 +7,7 @@ { "value": "Attack/Melee/Pierce", "multiply": 1.2 }, { "value": "Attack/Melee/Crush", "multiply": 1.2 } ], - "auraName": "Commander Aura", + "auraName": "Cavalry Commander", "auraDescription": "+20% cavalry melee attack within his aura.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_brennus.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_brennus.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_brennus.json @@ -4,6 +4,6 @@ "modifications": [ { "value": "Looter/Resource/metal", "add": 10 } ], - "auraName": "Hero Aura", + "auraName": "Sacker of Rome", "auraDescription": "+10 Metal loot for every enemy unit killed." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_britomartus.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_britomartus.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_britomartus.json @@ -4,6 +4,6 @@ "modifications": [ { "value": "ResourceGatherer/BaseSpeed", "multiply": 1.15 } ], - "auraName": "Hero Aura", + "auraName": "Preparation for War", "auraDescription": "Gathering rates increased with +15% during his lifetime." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_vercingetorix.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_vercingetorix.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/gaul_hero_vercingetorix.json @@ -11,7 +11,7 @@ { "value": "Attack/Ranged/Crush", "multiply": 1.20 }, { "value": "Attack/Capture/Value", "add": 1 } ], - "auraName": "Hero Aura", + "auraName": "Celtic Warlord", "auraDescription": "+20% attack and +1 capture for all soldiers and siege engines within his aura.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/hero_garrison.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/hero_garrison.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/hero_garrison.json @@ -4,6 +4,6 @@ "modifications": [ { "value": "Capturable/GarrisonRegenRate", "add": 2 } ], - "auraName": "Garrisoned Capture Aura", + "auraName": "Inspired Defense", "auraDescription": "When garrisoned in a structure or a siege engine, the hero gives it a bonus of +2 capture points recovery rate." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_caros_1.json =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_caros_1.json @@ -0,0 +1,9 @@ +{ + "type": "garrison", + "affects": ["Structure", "SiegeTower"], + "modifications": [ + { "value": "BuildingAI/GarrisonArrowMultiplier", "multiply": 1.75 }, + { "value": "BuildingAI/MaxArrowCount", "multiply": 1.75 }], + "auraDescription": "75% more arrows fired per garrisoned soldier for the building while he is garrisoned in it.", + "auraName": "Valiant Defender" +} Index: binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_caros_2.json =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_caros_2.json @@ -0,0 +1,13 @@ +{ + "type": "range", + "radius": 50, + "affects": ["Soldier"], + "modifications": [ + { "value": "Armour/Pierce", "add": 1 }, + { "value": "Armour/Hack", "add": 1 }, + { "value": "Armour/Crush", "add": 1 } + ], + "auraDescription": "+1 armor for soldiers.", + "auraName": "Battle Fervor", + "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" +} Index: binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_indibil.json =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_indibil.json @@ -0,0 +1,13 @@ +{ + "type": "global", + "affects": ["Soldier"], + "modifications": [ + { "value": "Cost/BuildTime", "multiply": 0.80 }, + { "value": "Cost/Resources/food", "multiply": 0.85 }, + { "value": "Cost/Resources/wood", "multiply": 0.85 }, + { "value": "Cost/Resources/metal", "multiply": 0.85 }, + { "value": "Cost/Resources/stone", "multiply": 0.85 } + ], + "auraName": "Mobilization", + "auraDescription": "-20% training time and -15% resources cost for soldiers." +} Index: binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_viriato_1.json =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_viriato_1.json @@ -0,0 +1,12 @@ +{ + "type": "range", + "radius": 60, + "affects": ["Soldier"], + "modifications": [ + { "value": "UnitMotion/WalkSpeed", "multiply": 1.2 }, + { "value": "UnitMotion/Run/Speed", "multiply": 1.2 } + ], + "auraDescription": "+20% movement speed for soldiers.", + "auraName": "Guerrilla Tactics", + "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" +} Index: binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_viriato_2.json =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/data/auras/units/heroes/iber_hero_viriato_2.json @@ -0,0 +1,13 @@ +{ + "type": "range", + "radius": 60, + "affects": ["Soldier", "Siege"], + "modifications": [ + { "value": "Looter/Resource/food", "multiply": 2.0 }, + { "value": "Looter/Resource/wood", "multiply": 2.0 }, + { "value": "Looter/Resource/stone", "multiply": 2.0 }, + { "value": "Looter/Resource/metal", "multiply": 2.0 } + ], + "auraName": "Swag", + "auraDescription": "+100% resources loot increase for every enemy unit killed or structure destroyed." +} Index: binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_alexander.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_alexander.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_alexander.json @@ -4,6 +4,6 @@ "modifications": [ { "value": "TerritoryInfluence/Radius", "multiply": 1.1 } ], - "auraName": "Imperialism Aura", + "auraName": "Imperialism", "auraDescription": "+10% territory effect for all buildings while he lives." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_demetrius.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_demetrius.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_demetrius.json @@ -7,7 +7,7 @@ { "value": "Attack/Ranged/MaxRange", "multiply": 1.15 }, { "value": "Vision/Range", "multiply": 1.15 } ], - "auraName": "Besieger Aura", + "auraName": "Besieger", "auraDescription": "+15% range and +10% crush attack for siege engines.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_philip.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_philip.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/mace_hero_philip.json @@ -11,7 +11,7 @@ { "value": "Attack/Ranged/Crush", "multiply": 1.2 }, { "value": "Attack/Capture/Value", "add": 2 } ], - "auraName": "Hero Aura", + "auraName": "Rise of Macedon", "auraDescription": "+20% attack and +2 capture for champion units.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/maur_hero_ashoka.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/maur_hero_ashoka.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/maur_hero_ashoka.json @@ -12,6 +12,6 @@ { "value": "Cost/Resources/wood", "multiply": 0.5 } ], "auraDescription": "Temples and temple technologies -50% cost, in resources and time.", - "auraName": "Buddhism Aura", + "auraName": "Buddhism", "overlayIcon": "art/textures/ui/session/auras/build_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/maur_hero_chanakya.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/maur_hero_chanakya.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/maur_hero_chanakya.json @@ -5,6 +5,6 @@ { "value": "ProductionQueue/TechCostMultiplier/time", "multiply": 0.5 } ], "auraDescription": "Empower a building with -50% research time while he is garrisoned in it.", - "auraName": "Teacher Aura", + "auraName": "Teacher", "overlayIcon": "art/textures/ui/session/auras/build_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_cyrus.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_cyrus.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_cyrus.json @@ -11,7 +11,7 @@ { "value": "Attack/Ranged/Crush", "multiply": 1.2 }, { "value": "Attack/Capture/Value", "add": 1 } ], - "auraName": "Lead from the Front Aura", + "auraName": "Lead from the Front", "auraDescription": "+20% attack and +1 capture for nearby cavalry units.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_darius.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_darius.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_darius.json @@ -5,6 +5,6 @@ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }, { "value": "UnitMotion/Run/Speed", "multiply": 1.15 } ], - "auraName": "Leadership Aura", + "auraName": "Leadership", "auraDescription": "+15% movement speed for all soldiers and siege engines." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_xerxes.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_xerxes.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/pers_hero_xerxes.json @@ -6,7 +6,7 @@ { "value": "Builder/Rate", "multiply": 1.15 }, { "value": "ResourceGatherer/BaseSpeed", "multiply": 1.15 } ], - "auraName": "Administrator Aura", + "auraName": "Administrator", "auraDescription": "+15% gather rate and build rate of nearby workers.", "overlayIcon": "art/textures/ui/session/auras/buildgather_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_cleopatra_1.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_cleopatra_1.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_cleopatra_1.json @@ -6,7 +6,7 @@ { "value": "Attack/Melee/RepeatTime", "multiply": 0.8 }, { "value": "Attack/Ranged/RepeatTime", "multiply": 0.8 } ], - "auraName": "Patriot Aura", + "auraName": "Patriot", "auraDescription": "-20% attack repeat time for Egyptian soldiers and siege engines in her aura.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_IV.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_IV.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_IV.json @@ -4,6 +4,6 @@ "modifications": [ { "value": "Health/Max", "multiply": 1.40 } ], - "auraName": "Raphia Aura", + "auraName": "Raphia", "auraDescription": "Egyptian pikemen have 40% greater health during his lifetime." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_1.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_1.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_1.json @@ -5,7 +5,7 @@ "modifications": [ { "value": "Builder/Rate", "multiply": 1.10 } ], - "auraName": "Construction Aura", + "auraName": "Patron of Construction", "auraDescription": "Buildings construct 10% faster within his aura.", "overlayIcon": "art/textures/ui/session/auras/build_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_2.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_2.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/ptol_hero_ptolemy_I_2.json @@ -7,6 +7,6 @@ { "value": "Cost/Resources/stone", "multiply": 0.5 }, { "value": "Cost/Resources/metal", "multiply": 0.5 } ], - "auraName": "Mercenary Patron Aura", + "auraName": "Mercenary Patron", "auraDescription": "Mercenaries cost -50% resources during his lifetime." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/rome_hero_marcellus.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/rome_hero_marcellus.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/rome_hero_marcellus.json @@ -11,7 +11,7 @@ { "value": "Attack/Ranged/Crush", "multiply": 1.2 }, { "value": "Attack/Capture/Value", "add": 2 } ], - "auraName": "Sword of Rome Aura", + "auraName": "Sword of Rome", "auraDescription": "+20% attack and +2 capture for Roman soldiers and siege engines within his range.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/rome_hero_maximus.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/rome_hero_maximus.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/rome_hero_maximus.json @@ -6,6 +6,6 @@ { "value": "Armour/Hack", "add": 1 }, { "value": "Armour/Crush", "add": 1 } ], - "auraName": "Shield of Rome Aura", + "auraName": "Shield of Rome", "auraDescription": "+1 armor for all units and structures." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_antiochus_great.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_antiochus_great.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_antiochus_great.json @@ -6,6 +6,6 @@ { "value": "Armour/Hack", "add": 2 }, { "value": "Armour/Crush", "add": 2 } ], - "auraName": "Ilarchès Aura", + "auraName": "Ilarchès", "auraDescription": "All cavalry gains +2 levels of all armor types." } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_antiochus_righteous.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_antiochus_righteous.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_antiochus_righteous.json @@ -6,7 +6,7 @@ "modifications": [ { "value": "Health/Max", "multiply": 0.8 } ], - "auraName": "Conquest Aura", + "auraName": "Renowned Conqueror", "auraDescription": "All nearby enemy buildings, siege engines, and ships have their health reduced by 20%.", "overlayIcon": "art/textures/ui/session/auras/health_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_seleucus_victor.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_seleucus_victor.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/sele_hero_seleucus_victor.json @@ -8,7 +8,7 @@ { "value": "Attack/Melee/Hack", "multiply": 1.2 }, { "value": "Attack/Melee/Crush", "multiply": 1.2 } ], - "auraName": "Zooiarchos Aura", + "auraName": "Zooiarchos", "auraDescription": "Boosts war elephant attack and speed +20% within his aura.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/data/auras/units/heroes/spart_hero_leonidas.json =================================================================== --- binaries/data/mods/public/simulation/data/auras/units/heroes/spart_hero_leonidas.json +++ binaries/data/mods/public/simulation/data/auras/units/heroes/spart_hero_leonidas.json @@ -8,7 +8,7 @@ { "value": "Attack/Melee/Crush", "multiply": 1.2 }, { "value": "Attack/Capture/Value", "add": 1 } ], - "auraName": "Last Stand Aura", + "auraName": "Last Stand", "auraDescription": "+20% attack and +1 capture for nearby Spearmen.", "overlayIcon": "art/textures/ui/session/auras/attack_bonus.png" } Index: binaries/data/mods/public/simulation/helpers/Setup.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Setup.js +++ binaries/data/mods/public/simulation/helpers/Setup.js @@ -45,6 +45,8 @@ let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); let gameTypeSettings = {}; + if (settings.RelicCount) + gameTypeSettings.relicCount = settings.RelicCount; if (settings.VictoryDuration) gameTypeSettings.victoryDuration = settings.VictoryDuration * 60 * 1000; if (settings.GameType) Index: binaries/data/mods/public/simulation/templates/special/dummy.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/dummy.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - 0 - upright - false - 6.0 - - Index: binaries/data/mods/public/simulation/templates/special/marker_object_sound.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/marker_object_sound.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - Special - - - - 5.0 - - - 200000 - - - gaia - Marker - Marker - gaia/special_blank.png - - - - - - - - - - - true - - - props/special/common/marker_object_sound.xml - - Index: binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml =================================================================== --- binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml +++ binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml @@ -3,7 +3,7 @@ -5.0 -5.0 - -2.0 + -1.0 1.0 5.0 @@ -15,7 +15,7 @@ 0.0 25.0 0.0 - 80.0 + 50.0 12.0 75.0 1200 @@ -29,14 +29,23 @@ 1 - own neutral enemy - Fortress + neutral enemy + ArmyCamp + + ArmyCamp + 80 + + + 1500 + 10.0 + 3.0 + 5 250 - 400 + 500 0 Index: binaries/data/mods/public/simulation/templates/template_structure.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_structure.xml +++ binaries/data/mods/public/simulation/templates/template_structure.xml @@ -101,6 +101,7 @@ round default + 2.0 Index: binaries/data/mods/public/simulation/templates/template_unit.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit.xml +++ binaries/data/mods/public/simulation/templates/template_unit.xml @@ -65,6 +65,7 @@ false + 2.0 1.0 Index: binaries/data/mods/public/simulation/templates/units/iber_hero_caros.xml =================================================================== --- binaries/data/mods/public/simulation/templates/units/iber_hero_caros.xml +++ binaries/data/mods/public/simulation/templates/units/iber_hero_caros.xml @@ -1,10 +1,14 @@ + + units/heroes/iber_hero_caros_1 + units/heroes/iber_hero_caros_2 + iber Caros - units/iber_hero_caros.png Caros was a chief of the Belli tribe located just east of the Celtiberi (Numantines at the center). Leading the confederated tribes of the meseta central (central upland plain) he concealed 20,000 foot and 5,000 mounted troops along a densely wooded track. Q. Fulvius Nobilior neglected proper reconnaissance and lead his army into the trap strung out in a long column. Some 10,000 of 15,000 Roman legionaries fell in the massive ambush that was sprung upon them. The date was 23 August of 153 BCE, the day when Rome celebrated the feast of Vulcan. By later Senatorial Decree it was ever thereafter known as dies ater, a 'sinister day', and Rome never again fought a battle on the 23rd of August. Caros was wounded in a small cavalry action the same evening and died soon thereafter, but he had carried off one of the most humiliating defeats that Rome ever suffered. + units/iber_hero_caros.png units/iberians/hero_caros.xml Index: binaries/data/mods/public/simulation/templates/units/iber_hero_indibil.xml =================================================================== --- binaries/data/mods/public/simulation/templates/units/iber_hero_indibil.xml +++ binaries/data/mods/public/simulation/templates/units/iber_hero_indibil.xml @@ -1,10 +1,13 @@ + + units/heroes/iber_hero_indibil + iber Indibil - units/iber_hero_indibil.png Indibil was king of the Ilegetes, a large federation ranged principally along the Ebro River in the northwest of the Iberian Peninsula. During the Barcid expansion, from 212 BCE he had initially been talked into allying himself with the Carthaginians who hade taken control of a lot of territory to the south and west, however after loss and his capture in a major battle he was convinced, some say tricked, to switch to the Roman side by Scipio Africanus. But that alliance didn't last long, as Roman promises were hollow and the Romans acted more like conquerors than allies. So, while the Romans and their allies had ended Carthaginian presence in 'Hispania' in 206 BCE, Indibil and another tribal prince by the name of Mandonio, who may have been his brother, rose up in rebellion against the Romans. They were defeated in battle, but rose up in a 2nd even larger rebellion that had unified all the Ilergetes again in 205 BCE. Outnumbered and outarmed they were again defeated, Indibil losing his life in the final battle and Mandonio being captured then later put to death. From that date onward the Ilergetes remained a pacified tribe under Roman rule. + units/iber_hero_indibil.png units/iberians/hero_indibil_horse.xml Index: binaries/data/mods/public/simulation/templates/units/iber_hero_viriato.xml =================================================================== --- binaries/data/mods/public/simulation/templates/units/iber_hero_viriato.xml +++ binaries/data/mods/public/simulation/templates/units/iber_hero_viriato.xml @@ -1,10 +1,14 @@ + + units/heroes/iber_hero_viriato_1 + units/heroes/iber_hero_viriato_2 + iber Viriato - units/iber_hero_viriato.png Viriato, like Vercingentorix amongst the Gauls, was the most famous of the Iberian tribal war leaders, having conducted at least 7 campaigns against the Romans in the southern half of the peninsula during the 'Lusitani Wars' from 147 to 139 BCE. He surfaced as a survivor of the treacherous massacre of 9,000 men and the selling into slavery of 21,000 elderly, women, and children of the Lusitani. They had signed a treaty of peace with the Romans, conducted by Servius Sulpicius Galba, governor of Hispania Ulterior, as the 'final solution' to the Lusitani problem. He emerged from humble beginnings in 151 BCE to become war chief of the Lusitani. He was intelligent and a superior tactician, never really defeated in any encounter (though suffered losses in some requiring retreat). He succumbed instead to another treachery arranged by a later Roman commander, Q. Servilius Caepio, to have him assassinated by three comrades that were close to him. + units/iber_hero_viriato.png units/iberians/hero_viriato.xml Index: build/premake/premake4.lua =================================================================== --- build/premake/premake4.lua +++ build/premake/premake4.lua @@ -1109,7 +1109,6 @@ "CustomControls/SnapSplitterWindow", "CustomControls/VirtualDirTreeCtrl", "CustomControls/Windows", - "ErrorReporter", "General", "General/VideoRecorder", "Misc", @@ -1121,7 +1120,6 @@ "ScenarioEditor/Sections/Object", "ScenarioEditor/Sections/Player", "ScenarioEditor/Sections/Terrain", - "ScenarioEditor/Sections/Trigger", "ScenarioEditor/Tools", "ScenarioEditor/Tools/Common", } Index: libraries/source/fcollada/src/FCollada/FCDocument/FCDAnimationCurveTools.cpp =================================================================== --- libraries/source/fcollada/src/FCollada/FCDocument/FCDAnimationCurveTools.cpp +++ libraries/source/fcollada/src/FCollada/FCDocument/FCDAnimationCurveTools.cpp @@ -254,7 +254,8 @@ float Average(float* values, uint32 count) { float v = 0.0f; - for (uint32 i = 0; i < count; ++i) v += values[i]; v /= float(count); - return v; + for (uint32 i = 0; i < count; ++i) + v += values[i]; + return v / count; } }; Index: source/graphics/Camera.h =================================================================== --- source/graphics/Camera.h +++ source/graphics/Camera.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -95,7 +95,7 @@ void LookAt(const CVector3D& camera, const CVector3D& orientation, const CVector3D& up); // Build an orientation matrix from camera position, camera orientation, and up-vector - void LookAlong(CVector3D camera, CVector3D focus, CVector3D up); + void LookAlong(const CVector3D& camera, CVector3D focus, CVector3D up); /** * Render: Renders the camera's frustum in world space. Index: source/graphics/Camera.cpp =================================================================== --- source/graphics/Camera.cpp +++ source/graphics/Camera.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -357,7 +357,7 @@ LookAlong(camera, delta, up); } -void CCamera::LookAlong(CVector3D camera, CVector3D orientation, CVector3D up) +void CCamera::LookAlong(const CVector3D& camera, CVector3D orientation, CVector3D up) { orientation.Normalize(); up.Normalize(); Index: source/graphics/CinemaManager.h =================================================================== --- source/graphics/CinemaManager.h +++ source/graphics/CinemaManager.h @@ -15,15 +15,14 @@ * along with 0 A.D. If not, see . */ - #ifndef INCLUDED_CINEMAMANAGER #define INCLUDED_CINEMAMANAGER #include "lib/input.h" // InReaction - can't forward-declare enum - -#include "simulation2/helpers/CinemaPath.h" #include "ps/CStr.h" #include "ps/Shapes.h" +#include "simulation2/helpers/CinemaPath.h" + /** * Class for in game playing of cinematics. Should only be instantiated in CGameView. Index: source/graphics/CinemaManager.cpp =================================================================== --- source/graphics/CinemaManager.cpp +++ source/graphics/CinemaManager.cpp @@ -73,8 +73,7 @@ { if (IsEnabled()) DrawBars(); - - if (m_DrawPaths) + else if (m_DrawPaths) DrawPaths(); } Index: source/gui/CDropDown.cpp =================================================================== --- source/gui/CDropDown.cpp +++ source/gui/CDropDown.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -347,7 +347,7 @@ int diff = 0; for (size_t j = 0; j < m_InputBuffer.length(); ++j) { - diff = abs(pList->m_Items[i].GetOriginalString().LowerCase()[j] - (int)m_InputBuffer[j]); + diff = std::abs(pList->m_Items[i].GetRawString().LowerCase()[j] - (int)m_InputBuffer[j]); if (diff == 0) indexOfDifference = j+1; else Index: source/gui/GUItext.h =================================================================== --- source/gui/GUItext.h +++ source/gui/GUItext.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -291,6 +291,11 @@ const CStrW& GetOriginalString() const { return m_OriginalString; } /** + * Get String, stripped of tags + */ + const CStrW& GetRawString() const { return m_RawString; } + + /** * Generate Text Call from specified range. The range * must span only within ONE TextChunk though. Otherwise * it can't be fit into a single Text Call Index: source/lib/sysdep/os/win/wdbg_sym.cpp =================================================================== --- source/lib/sysdep/os/win/wdbg_sym.cpp +++ source/lib/sysdep/os/win/wdbg_sym.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2013 Wildfire Games +/* Copyright (c) 2017 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -210,8 +210,10 @@ // get source file and/or line number (if requested) if(file || line) { - file[0] = '\0'; - *line = 0; + if (file) + file[0] = '\0'; + if (line) + *line = 0; IMAGEHLP_LINEW64 line_info = { sizeof(IMAGEHLP_LINEW64) }; DWORD displacement; // unused but required by pSymGetLineFromAddr64! Index: source/lib/utf8.cpp =================================================================== --- source/lib/utf8.cpp +++ source/lib/utf8.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2017 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -21,6 +21,7 @@ */ #include "precompiled.h" + #include "lib/utf8.h" static const StatusDefinition utf8StatusDefinitions[] = { @@ -81,7 +82,10 @@ *perr = err; } else - DEBUG_WARN_ERR(err); + { + wchar_t error[200]; + debug_printf("UTF8 error: %s\n", utf8_from_wstring(StatusDescription(err, error, ARRAY_SIZE(error))).c_str()); + } return 0xFFFDul; // replacement character } Index: source/lobby/XmppClient.cpp =================================================================== --- source/lobby/XmppClient.cpp +++ source/lobby/XmppClient.cpp @@ -751,12 +751,12 @@ */ void XmppClient::handleMUCParticipantPresence(glooxwrapper::MUCRoom*, const glooxwrapper::MUCRoomParticipant participant, const glooxwrapper::Presence& presence) { - //std::string jid = participant.jid->full(); std::string nick = participant.nick->resource().to_string(); gloox::Presence::PresenceType presenceType = presence.presence(); std::string presenceString, roleString; GetPresenceString(presenceType, presenceString); GetRoleString(participant.role, roleString); + if (presenceType == gloox::Presence::Unavailable) { if (!participant.newNick.empty() && (participant.flags & (gloox::UserNickChanged | gloox::UserSelf))) @@ -800,6 +800,8 @@ } else if (m_PlayerMap.find(nick) == m_PlayerMap.end()) CreateGUIMessage("chat", "join", nick); + else if (m_PlayerMap[nick][2] != roleString) + CreateGUIMessage("chat", "role", nick, m_PlayerMap[nick][2]); else CreateGUIMessage("chat", "presence", nick); Index: source/main.cpp =================================================================== --- source/main.cpp +++ source/main.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -31,6 +31,8 @@ #define MINIMAL_PCH 2 #include "lib/precompiled.h" +#include + #include "lib/debug.h" #include "lib/status.h" #include "lib/secure_crt.h" @@ -92,6 +94,8 @@ static int g_ResizedW; static int g_ResizedH; +static std::chrono::high_resolution_clock::time_point lastFrameTime; + // main app message handler static InReaction MainInputHandler(const SDL_Event_* ev) { @@ -178,6 +182,31 @@ g_TouchInput.Frame(); } +/** + * Optionally throttle the render frequency in order to + * prevent 100% workload of the currently used CPU core. + */ +inline static void LimitFPS() +{ + if (g_VSync) + return; + + double fpsLimit = 0.0; + CFG_GET_VAL(g_Game && g_Game->IsGameStarted() ? "adaptivefps.session" : "adaptivefps.menu", fpsLimit); + + // Keep in sync with options.json + if (fpsLimit < 20.0 || fpsLimit >= 100.0) + return; + + double wait = 1000.0 / fpsLimit - + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - lastFrameTime).count() / 1000.0; + + if (wait > 0.0) + SDL_Delay(wait); + + lastFrameTime = std::chrono::high_resolution_clock::now(); +} static int ProgressiveLoad() { @@ -275,7 +304,7 @@ // If we are not running a multiplayer game, disable updates when the game is // minimized or out of focus and relinquish the CPU a bit, in order to make // debugging easier. - if(g_PauseOnFocusLoss && !g_NetClient && !g_app_has_focus) + if (g_PauseOnFocusLoss && !g_NetClient && !g_app_has_focus) { PROFILE3("non-focus delay"); need_update = false; @@ -283,22 +312,6 @@ SDL_Delay(10); } - // Throttling: limit update and render frequency to the minimum to 50 FPS - // in the "inactive" state, so that other windows get enough CPU time, - // (and it's always nice for power+thermal management). - // TODO: when the game performance is high enough, implementing a limit for - // in-game framerate might be sensible. - const float maxFPSMenu = 50.0; - bool limit_fps = false; - CFG_GET_VAL("gui.menu.limitfps", limit_fps); - if (limit_fps && (!g_Game || !g_Game->IsGameStarted())) - { - float remainingFrameTime = (1000.0 / maxFPSMenu) - realTimeSinceLastFrame; - if (remainingFrameTime > 0) - SDL_Delay(remainingFrameTime); - } - - // this scans for changed files/directories and reloads them, thus // allowing hotloading (changes are immediately assimilated in-game). ReloadChangedFiles(); @@ -351,7 +364,7 @@ g_Console->Update(realTimeSinceLastFrame); ogl_WarnIfError(); - if(need_render) + if (need_render) { Render(); @@ -363,6 +376,8 @@ g_Profiler.Frame(); g_GameRestarted = false; + + LimitFPS(); } Index: source/renderer/WaterManager.cpp =================================================================== --- source/renderer/WaterManager.cpp +++ source/renderer/WaterManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -1030,11 +1030,10 @@ continue; // Calculate how dampened our waves should be. - float tendency = 0.0f; float oldHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[4][0],j+kernel[4][1])); float currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[3][0],j+kernel[3][1])); float avgheight = oldHeight + currentHeight; - tendency = currentHeight - oldHeight; + float tendency = currentHeight - oldHeight; oldHeight = currentHeight; currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[2][0],j+kernel[2][1])); avgheight += currentHeight; Index: source/simulation2/components/CCmpCinemaManager.cpp =================================================================== --- source/simulation2/components/CCmpCinemaManager.cpp +++ source/simulation2/components/CCmpCinemaManager.cpp @@ -20,20 +20,13 @@ #include "simulation2/system/Component.h" #include "ICmpCinemaManager.h" -#include "graphics/GameView.h" -#include "graphics/CinemaManager.h" -#include "gui/CGUI.h" -#include "gui/GUIManager.h" -#include "gui/IGUIObject.h" #include "ps/CLogger.h" -#include "ps/Game.h" #include "simulation2/components/ICmpOverlayRenderer.h" #include "simulation2/components/ICmpRangeManager.h" #include "simulation2/components/ICmpSelectable.h" #include "simulation2/components/ICmpTerritoryManager.h" #include "simulation2/MessageTypes.h" #include "simulation2/Simulation2.h" -#include "renderer/Renderer.h" class CCmpCinemaManager : public ICmpCinemaManager Index: source/simulation2/components/CCmpRangeManager.cpp =================================================================== --- source/simulation2/components/CCmpRangeManager.cpp +++ source/simulation2/components/CCmpRangeManager.cpp @@ -1053,6 +1053,11 @@ return GetEntitiesByMask(~3); // bit 0 for owner=-1 and bit 1 for gaia } + virtual std::vector GetGaiaAndNonGaiaEntities() const + { + return GetEntitiesByMask(~1); // bit 0 for owner=-1 + } + std::vector GetEntitiesByMask(u32 ownerMask) const { std::vector entities; Index: source/simulation2/components/CCmpSelectable.cpp =================================================================== --- source/simulation2/components/CCmpSelectable.cpp +++ source/simulation2/components/CCmpSelectable.cpp @@ -64,7 +64,7 @@ CCmpSelectable() : m_DebugBoundingBoxOverlay(NULL), m_DebugSelectionBoxOverlay(NULL), - m_BuildingOverlay(NULL), m_UnitOverlay(NULL), + m_BuildingOverlay(NULL), m_UnitOverlay(NULL), m_RangeOverlayData(), m_FadeBaselineAlpha(0.f), m_FadeDeltaAlpha(0.f), m_FadeProgress(0.f), m_Selected(false), m_Cached(false), m_Visible(false) { @@ -77,6 +77,8 @@ delete m_DebugSelectionBoxOverlay; delete m_BuildingOverlay; delete m_UnitOverlay; + for (RangeOverlayData& rangeOverlay : m_RangeOverlayData) + delete rangeOverlay.second; } static std::string GetSchema() @@ -126,23 +128,21 @@ const CParamNode& textureNode = paramNode.GetChild("Overlay").GetChild("Texture"); const CParamNode& outlineNode = paramNode.GetChild("Overlay").GetChild("Outline"); - const char* textureBasePath = "art/textures/selection/"; - // Save some memory by using interned file paths in these descriptors (almost all actors and // entities have this component, and many use the same textures). if (textureNode.IsOk()) { // textured quad mode (dynamic, for units) m_OverlayDescriptor.m_Type = ICmpSelectable::DYNAMIC_QUAD; - m_OverlayDescriptor.m_QuadTexture = CStrIntern(textureBasePath + textureNode.GetChild("MainTexture").ToUTF8()); - m_OverlayDescriptor.m_QuadTextureMask = CStrIntern(textureBasePath + textureNode.GetChild("MainTextureMask").ToUTF8()); + m_OverlayDescriptor.m_QuadTexture = CStrIntern(TEXTUREBASEPATH + textureNode.GetChild("MainTexture").ToUTF8()); + m_OverlayDescriptor.m_QuadTextureMask = CStrIntern(TEXTUREBASEPATH + textureNode.GetChild("MainTextureMask").ToUTF8()); } else if (outlineNode.IsOk()) { // textured outline mode (static, for buildings) m_OverlayDescriptor.m_Type = ICmpSelectable::STATIC_OUTLINE; - m_OverlayDescriptor.m_LineTexture = CStrIntern(textureBasePath + outlineNode.GetChild("LineTexture").ToUTF8()); - m_OverlayDescriptor.m_LineTextureMask = CStrIntern(textureBasePath + outlineNode.GetChild("LineTextureMask").ToUTF8()); + m_OverlayDescriptor.m_LineTexture = CStrIntern(TEXTUREBASEPATH + outlineNode.GetChild("LineTexture").ToUTF8()); + m_OverlayDescriptor.m_LineTextureMask = CStrIntern(TEXTUREBASEPATH + outlineNode.GetChild("LineTextureMask").ToUTF8()); m_OverlayDescriptor.m_LineThickness = outlineNode.GetChild("LineThickness").ToFloat(); } @@ -193,6 +193,22 @@ SetSelectionHighlightAlpha(color.a); } + virtual void AddRangeOverlay(float radius, const std::string& texture, const std::string& textureMask, float thickness) + { + if (!CRenderer::IsInitialised()) + return; + + SOverlayDescriptor rangeOverlayDescriptor; + SOverlayTexturedLine* rangeOverlay = nullptr; + + rangeOverlayDescriptor.m_Radius = radius; + rangeOverlayDescriptor.m_LineTexture = CStrIntern(TEXTUREBASEPATH + texture); + rangeOverlayDescriptor.m_LineTextureMask = CStrIntern(TEXTUREBASEPATH + textureMask); + rangeOverlayDescriptor.m_LineThickness = thickness; + + m_RangeOverlayData.push_back({rangeOverlayDescriptor, rangeOverlay}); + } + virtual void SetSelectionHighlightAlpha(float alpha) { alpha = std::max(m_AlphaMin, alpha); @@ -219,11 +235,9 @@ void RenderSubmit(SceneCollector& collector); /** - * Called from RenderSubmit if using a static outline; responsible for ensuring that the static overlay - * is up-to-date before it is rendered. Has no effect unless the static overlay is explicitly marked as - * invalid first (see InvalidateStaticOverlay). + * Draw a textured line overlay. The selection overlays for structures are based solely on footprint shape. */ - void UpdateStaticOverlay(); + void UpdateTexturedLineOverlay(const SOverlayDescriptor* overlayDescriptor, SOverlayTexturedLine& overlay, float frameOffset, bool buildingOverlay); /** * Called from the interpolation handler; responsible for ensuring the dynamic overlay (provided we're @@ -244,11 +258,20 @@ */ void UpdateMessageSubscriptions(); + /** + * Delete all range overlays. + */ + void ResetRangeOverlays(); + private: SOverlayDescriptor m_OverlayDescriptor; SOverlayTexturedLine* m_BuildingOverlay; SOverlayQuad* m_UnitOverlay; + // Holds the data for all range overlays + typedef std::pair RangeOverlayData; + std::vector m_RangeOverlayData; + SOverlayLine* m_DebugBoundingBoxOverlay; SOverlayLine* m_DebugSelectionBoxOverlay; @@ -279,9 +302,11 @@ /// Total duration of a single fade, in seconds. Assumed constant for now; feel free to change this into /// a member variable if you need to adjust it per component. static const double FADE_DURATION; + static const char* TEXTUREBASEPATH; }; const double CCmpSelectable::FADE_DURATION = 0.3; +const char* CCmpSelectable::TEXTUREBASEPATH = "art/textures/selection/"; void CCmpSelectable::HandleMessage(const CMessage& msg, bool UNUSED(global)) { @@ -314,8 +339,17 @@ // update dynamic overlay only when visible if (m_Color.a > 0) + { UpdateDynamicOverlay(msgData.offset); + for (RangeOverlayData& rangeOverlay : m_RangeOverlayData) + { + delete rangeOverlay.second; + rangeOverlay.second = new SOverlayTexturedLine; + UpdateTexturedLineOverlay(&rangeOverlay.first, *rangeOverlay.second, msgData.offset, false); + } + } + UpdateMessageSubscriptions(); break; @@ -373,6 +407,15 @@ } } +void CCmpSelectable::ResetRangeOverlays() +{ + for (RangeOverlayData& rangeOverlay : m_RangeOverlayData) + delete rangeOverlay.second; + m_RangeOverlayData.clear(); + + UpdateMessageSubscriptions(); +} + void CCmpSelectable::UpdateMessageSubscriptions() { bool needInterpolate = false; @@ -402,15 +445,8 @@ SAFE_DELETE(m_BuildingOverlay); } -void CCmpSelectable::UpdateStaticOverlay() +void CCmpSelectable::UpdateTexturedLineOverlay(const SOverlayDescriptor* overlayDescriptor, SOverlayTexturedLine& overlay, float frameOffset, bool buildingOverlay) { - // Static overlays are allocated once and not updated until they are explicitly deleted again - // (see InvalidateStaticOverlay). Since they are expected to change rarely (if ever) during - // normal gameplay, this saves us doing all the work below on each frame. - - if (m_BuildingOverlay || m_OverlayDescriptor.m_Type != STATIC_OUTLINE) - return; - if (!CRenderer::IsInitialised()) return; @@ -419,84 +455,69 @@ if (!cmpFootprint || !cmpPosition || !cmpPosition->IsInWorld()) return; - CmpPtr cmpTerrain(GetSystemEntity()); - if (!cmpTerrain) - return; // should never happen - - // grab position/footprint data - CFixedVector2D position = cmpPosition->GetPosition2D(); - CFixedVector3D rotation = cmpPosition->GetRotation(); - ICmpFootprint::EShape fpShape; entity_pos_t fpSize0_fixed, fpSize1_fixed, fpHeight_fixed; cmpFootprint->GetShape(fpShape, fpSize0_fixed, fpSize1_fixed, fpHeight_fixed); - CTextureProperties texturePropsBase(m_OverlayDescriptor.m_LineTexture.c_str()); + float rotY; + CVector2D origin; + cmpPosition->GetInterpolatedPosition2D(frameOffset, origin.X, origin.Y, rotY); + CFixedVector3D rotation = cmpPosition->GetRotation(); + + CTextureProperties texturePropsBase(overlayDescriptor->m_LineTexture.c_str()); texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); texturePropsBase.SetMaxAnisotropy(4.f); - CTextureProperties texturePropsMask(m_OverlayDescriptor.m_LineTextureMask.c_str()); + CTextureProperties texturePropsMask(overlayDescriptor->m_LineTextureMask.c_str()); texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); texturePropsMask.SetMaxAnisotropy(4.f); - // ------------------------------------------------------------------------------------- - - m_BuildingOverlay = new SOverlayTexturedLine; - m_BuildingOverlay->m_AlwaysVisible = false; - m_BuildingOverlay->m_Closed = true; - m_BuildingOverlay->m_SimContext = &GetSimContext(); - m_BuildingOverlay->m_Thickness = m_OverlayDescriptor.m_LineThickness; - m_BuildingOverlay->m_TextureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase); - m_BuildingOverlay->m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask); - - CVector2D origin(position.X.ToFloat(), position.Y.ToFloat()); + overlay.m_AlwaysVisible = false; + overlay.m_Closed = true; + overlay.m_SimContext = &GetSimContext(); + overlay.m_Thickness = overlayDescriptor->m_LineThickness; + overlay.m_TextureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase); + overlay.m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask); + overlay.m_Color = m_Color; - switch (fpShape) + if (buildingOverlay && fpShape == ICmpFootprint::SQUARE) { - case ICmpFootprint::SQUARE: - { - float s = sinf(-rotation.Y.ToFloat()); - float c = cosf(-rotation.Y.ToFloat()); - CVector2D unitX(c, s); - CVector2D unitZ(-s, c); - - // add half the line thickness to the radius so that we get an 'outside' stroke of the footprint shape - const float halfSizeX = fpSize0_fixed.ToFloat()/2.f + m_BuildingOverlay->m_Thickness/2.f; - const float halfSizeZ = fpSize1_fixed.ToFloat()/2.f + m_BuildingOverlay->m_Thickness/2.f; - - std::vector points; - points.push_back(CVector2D(origin + unitX * halfSizeX + unitZ *(-halfSizeZ))); - points.push_back(CVector2D(origin + unitX *(-halfSizeX) + unitZ *(-halfSizeZ))); - points.push_back(CVector2D(origin + unitX *(-halfSizeX) + unitZ * halfSizeZ)); - points.push_back(CVector2D(origin + unitX * halfSizeX + unitZ * halfSizeZ)); - - SimRender::SubdividePoints(points, TERRAIN_TILE_SIZE/3.f, m_BuildingOverlay->m_Closed); - m_BuildingOverlay->PushCoords(points); - } - break; - case ICmpFootprint::CIRCLE: - { - const float radius = fpSize0_fixed.ToFloat() + m_BuildingOverlay->m_Thickness/3.f; - if (radius > 0) // prevent catastrophic failure - { - float stepAngle; - unsigned numSteps; - SimRender::AngularStepFromChordLen(TERRAIN_TILE_SIZE/3.f, radius, stepAngle, numSteps); + float s = sinf(-rotation.Y.ToFloat()); + float c = cosf(-rotation.Y.ToFloat()); + CVector2D unitX(c, s); + CVector2D unitZ(-s, c); + + // Add half the line thickness to the radius so that we get an 'outside' stroke of the footprint shape + const float halfSizeX = fpSize0_fixed.ToFloat() / 2.f + overlay.m_Thickness / 2.f; + const float halfSizeZ = fpSize1_fixed.ToFloat() / 2.f + overlay.m_Thickness / 2.f; + + std::vector points; + points.push_back(CVector2D(origin + unitX * halfSizeX + unitZ * (-halfSizeZ))); + points.push_back(CVector2D(origin + unitX * (-halfSizeX) + unitZ * (-halfSizeZ))); + points.push_back(CVector2D(origin + unitX * (-halfSizeX) + unitZ * halfSizeZ)); + points.push_back(CVector2D(origin + unitX * halfSizeX + unitZ * halfSizeZ)); + + SimRender::SubdividePoints(points, TERRAIN_TILE_SIZE / 3.f, overlay.m_Closed); + overlay.PushCoords(points); + } + else + { + const float radius = (buildingOverlay ? fpSize0_fixed.ToFloat() : overlayDescriptor->m_Radius) + overlay.m_Thickness / 3.f; + float stepAngle; + unsigned numSteps; + SimRender::AngularStepFromChordLen(TERRAIN_TILE_SIZE / 3.f, radius, stepAngle, numSteps); - for (unsigned i = 0; i < numSteps; i++) // '<' is sufficient because the line is closed automatically - { - float angle = i * stepAngle; - float px = origin.X + radius * sinf(angle); - float pz = origin.Y + radius * cosf(angle); + for (unsigned i = 0; i < numSteps; ++i) + { + float angle = i * stepAngle; + float px = origin.X + radius * sinf(angle); + float pz = origin.Y + radius * cosf(angle); - m_BuildingOverlay->PushCoords(px, pz); - } - } + overlay.PushCoords(px, pz); } - break; } - ENSURE(m_BuildingOverlay); + ENSURE(overlay.m_TextureBase); } void CCmpSelectable::UpdateDynamicOverlay(float frameOffset) @@ -625,7 +646,14 @@ { case STATIC_OUTLINE: { - UpdateStaticOverlay(); + if (!m_BuildingOverlay) + { + // Static overlays are allocated once and not updated until they are explicitly deleted again + // (see InvalidateStaticOverlay). Since they are expected to change rarely (if ever) during + // normal gameplay, this saves us doing all the work below on each frame. + m_BuildingOverlay = new SOverlayTexturedLine; + UpdateTexturedLineOverlay(&m_OverlayDescriptor, *m_BuildingOverlay, 0, true); + } m_BuildingOverlay->m_Color = m_Color; // done separately so alpha changes don't require a full update call collector.Submit(m_BuildingOverlay); } @@ -639,6 +667,10 @@ default: break; } + + for (const RangeOverlayData& rangeOverlay : m_RangeOverlayData) + if (rangeOverlay.second) + collector.Submit(rangeOverlay.second); } // Render bounding box debug overlays if we have a positive target alpha value. This ensures Index: source/simulation2/components/ICmpCinemaManager.h =================================================================== --- source/simulation2/components/ICmpCinemaManager.h +++ source/simulation2/components/ICmpCinemaManager.h @@ -21,10 +21,10 @@ #include #include +#include "ps/CStr.h" #include "simulation2/helpers/CinemaPath.h" #include "simulation2/system/Interface.h" -#include "ps/CStr.h" /** * Component for CCinemaManager class Index: source/simulation2/components/ICmpRangeManager.h =================================================================== --- source/simulation2/components/ICmpRangeManager.h +++ source/simulation2/components/ICmpRangeManager.h @@ -199,6 +199,11 @@ virtual std::vector GetNonGaiaEntities() const = 0; /** + * Returns a list of all entities owned by players or gaia. + */ + virtual std::vector GetGaiaAndNonGaiaEntities() const = 0; + + /** * Toggle the rendering of debug info. */ virtual void SetDebugOverlay(bool enabled) = 0; Index: source/simulation2/components/ICmpRangeManager.cpp =================================================================== --- source/simulation2/components/ICmpRangeManager.cpp +++ source/simulation2/components/ICmpRangeManager.cpp @@ -47,6 +47,7 @@ DEFINE_INTERFACE_METHOD_CONST_1("GetEntityFlagMask", u8, ICmpRangeManager, GetEntityFlagMask, std::string) DEFINE_INTERFACE_METHOD_CONST_1("GetEntitiesByPlayer", std::vector, ICmpRangeManager, GetEntitiesByPlayer, player_id_t) DEFINE_INTERFACE_METHOD_CONST_0("GetNonGaiaEntities", std::vector, ICmpRangeManager, GetNonGaiaEntities) +DEFINE_INTERFACE_METHOD_CONST_0("GetGaiaAndNonGaiaEntities", std::vector, ICmpRangeManager, GetGaiaAndNonGaiaEntities) DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpRangeManager, SetDebugOverlay, bool) DEFINE_INTERFACE_METHOD_1("ExploreAllTiles", void, ICmpRangeManager, ExploreAllTiles, player_id_t) DEFINE_INTERFACE_METHOD_0("ExploreTerritories", void, ICmpRangeManager, ExploreTerritories) Index: source/simulation2/components/ICmpSelectable.h =================================================================== --- source/simulation2/components/ICmpSelectable.h +++ source/simulation2/components/ICmpSelectable.h @@ -43,6 +43,7 @@ CStrIntern m_LineTexture; CStrIntern m_LineTextureMask; float m_LineThickness; + int m_Radius; SOverlayDescriptor() : m_LineThickness(0) { } }; @@ -61,6 +62,16 @@ virtual void SetSelectionHighlight(const CColor& color, bool selected) = 0; /** + * Add a range overlay to this entity, for example for an aura or attack. + */ + virtual void AddRangeOverlay(float radius, const std::string& texture, const std::string& textureMask, float thickness) = 0; + + /** + * Delete all range overlays. + */ + virtual void ResetRangeOverlays() = 0; + + /** * Enables or disables rendering of an entity's selectable. * @param visible Whether the selectable should be visible. */ Index: source/simulation2/components/ICmpSelectable.cpp =================================================================== --- source/simulation2/components/ICmpSelectable.cpp +++ source/simulation2/components/ICmpSelectable.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2012 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,6 +25,8 @@ BEGIN_INTERFACE_WRAPPER(Selectable) DEFINE_INTERFACE_METHOD_2("SetSelectionHighlight", void, ICmpSelectable, SetSelectionHighlight, CColor, bool) +DEFINE_INTERFACE_METHOD_4("AddRangeOverlay", void, ICmpSelectable, AddRangeOverlay, float, std::string, std::string, float) +DEFINE_INTERFACE_METHOD_0("ResetRangeOverlays", void, ICmpSelectable, ResetRangeOverlays) END_INTERFACE_WRAPPER(Selectable) bool ICmpSelectable::ms_EnableDebugOverlays = false; Index: source/soundmanager/ISoundManager.h =================================================================== --- source/soundmanager/ISoundManager.h +++ source/soundmanager/ISoundManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -47,7 +47,7 @@ virtual void PlayAsUI(const VfsPath& itemPath, bool looping) = 0; virtual void PlayAsMusic(const VfsPath& itemPath, bool looping) = 0; virtual void PlayAsAmbient(const VfsPath& itemPath, bool looping) = 0; - virtual void PlayAsGroup(const VfsPath& groupPath, CVector3D sourcePos, entity_id_t source, bool ownedSound) = 0; + virtual void PlayAsGroup(const VfsPath& groupPath, const CVector3D& sourcePos, entity_id_t source, bool ownedSound) = 0; virtual bool InDistress() = 0; }; Index: source/soundmanager/SoundManager.h =================================================================== --- source/soundmanager/SoundManager.h +++ source/soundmanager/SoundManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -137,7 +137,7 @@ void PlayAsMusic(const VfsPath& itemPath, bool looping); void PlayAsAmbient(const VfsPath& itemPath, bool looping); void PlayAsUI(const VfsPath& itemPath, bool looping); - void PlayAsGroup(const VfsPath& groupPath, CVector3D sourcePos, entity_id_t source, bool ownedSound); + void PlayAsGroup(const VfsPath& groupPath, const CVector3D& sourcePos, entity_id_t source, bool ownedSound); void PlayGroupItem(ISoundItem* anItem, ALfloat groupGain); Index: source/soundmanager/SoundManager.cpp =================================================================== --- source/soundmanager/SoundManager.cpp +++ source/soundmanager/SoundManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -639,7 +639,7 @@ m_MusicEnabled = isEnabled; } -void CSoundManager::PlayAsGroup(const VfsPath& groupPath, CVector3D sourcePos, entity_id_t source, bool ownedSound) +void CSoundManager::PlayAsGroup(const VfsPath& groupPath, const CVector3D& sourcePos, entity_id_t source, bool ownedSound) { // Make sure the sound group is loaded CSoundGroup* group; Index: source/tools/atlas/AtlasUI/ErrorReporter/ErrorReporter.h =================================================================== --- source/tools/atlas/AtlasUI/ErrorReporter/ErrorReporter.h +++ /dev/null @@ -1 +0,0 @@ -void ReportError(); Index: source/tools/atlas/AtlasUI/ErrorReporter/ErrorReporter.cpp =================================================================== --- source/tools/atlas/AtlasUI/ErrorReporter/ErrorReporter.cpp +++ /dev/null @@ -1,706 +0,0 @@ -/** - * ========================================================================= - * File : ErrorReporter.cpp - * Project : 0 A.D. - * Description : preview and send crashlogs to server. - * - * @author jan@wildfiregames.com, joe@wildfiregames.com - * ========================================================================= - */ - -/* - * based on dbgrptg.cpp, - * Copyright (c) 2005 Vadim Zeitlin - * License: wxWindows license - */ - -#include "precompiled.h" - -#if 0 - -#include "wx/statline.h" -#include "wx/mimetype.h" - -static const wxChar* DIALOG_TITLE = _T("WildfireGames Reporting Utility"); -static const wxChar* PROBLEM_TITLE = _T("Problem Report for 0AD"); -static const wxChar* DIALOG_REGRET = _T("Much to our regret, we must report the program has encountered a problem.\n"); -static const wxChar* DIALOG_REPGEN = _T("A problem report has been generated in the directory\n"); -static const wxChar* DIALOG_SPACER = _T("\n"); -static const wxChar* DIALOG_FILES = _T("The report contains the files listed below.\nWe believe they hold information that will help us to improve the program,\nso please take a minute and send them!\n"); -static const wxChar* DIALOG_PRIVACY = _T("If any of these files contain private information,\nplease uncheck them and they will be removed from the report.\n"); -static const wxChar* DIALOG_RESPECT = _T("We respect your privacy so if you do not wish to send us this problem report\nwe understand and you can use the \"Cancel\" button.\n"); -static const wxChar* DIALOG_APOLOGY = _T("Thank you and we're sorry for the inconvenience!\n"); -static const wxChar* DIALOG_ADDL_INFO = _T("If you have any additional information pertaining to this problem report,\n please enter it here and it will be joined to it:"); -// This is displayed to indicate a successful upload of the report file. -static const wxChar* DIALOG_UPLOAD = _T("Report successfully uploaded."); -// Default location for the crash files to include in the report file. -static const wxChar* LOGS_LOCATION = _T("C:\\0AD\\BINARIES\\LOGS\\"); - - -// ---------------------------------------------------------------------------- -// wxDumpPreviewDlg: simple class for showing ASCII preview of dump files -// ---------------------------------------------------------------------------- - -class wxDumpPreviewDlg : public wxDialog -{ -public: - wxDumpPreviewDlg(wxWindow *parent, - const wxString& title, - const wxString& text); - -private: - // the text we show - wxTextCtrl *m_text; - - DECLARE_NO_COPY_CLASS(wxDumpPreviewDlg) -}; - -wxDumpPreviewDlg::wxDumpPreviewDlg(wxWindow *parent, const wxString& title, const wxString& text) -: wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) -{ - // create controls - // --------------- - - // use wxTE_RICH2 style to avoid 64kB limit under MSW and display big files - // faster than with wxTE_RICH - m_text = new wxTextCtrl(this, wxID_ANY, wxEmptyString, - wxPoint(0, 0), wxDefaultSize, - wxTE_MULTILINE | - wxTE_READONLY | - wxTE_NOHIDESEL | - wxTE_RICH2); - m_text->SetValue(text); - - // use fixed-width font - m_text->SetFont(wxFont(12, wxFONTFAMILY_TELETYPE, - wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); - - wxButton *btnClose = new wxButton(this, wxID_CANCEL, _("Close")); - - - // layout them - // ----------- - - wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL), - *sizerBtns = new wxBoxSizer(wxHORIZONTAL); - - sizerBtns->Add(btnClose, 0, 0, 1); - - sizerTop->Add(m_text, 1, wxEXPAND); - sizerTop->Add(sizerBtns, 0, wxALIGN_RIGHT | wxTOP | wxBOTTOM | wxRIGHT, 1); - - // set the sizer &c - // ---------------- - - // make the text window bigger to show more contents of the file - sizerTop->SetItemMinSize(m_text, 600, 300); - SetSizer(sizerTop); - - Layout(); - Fit(); - - m_text->SetFocus(); -} - -// ---------------------------------------------------------------------------- -// wxDumpOpenExternalDlg: choose a command for opening the given file -// ---------------------------------------------------------------------------- - -class wxDumpOpenExternalDlg : public wxDialog -{ -public: - wxDumpOpenExternalDlg(wxWindow *parent, const wxFileName& filename); - - // return the command chosed by user to open this file - const wxString& GetCommand() const { return m_command; } - - wxString m_command; - -private: - -#if wxUSE_FILEDLG - void OnBrowse(wxCommandEvent& event); -#endif // wxUSE_FILEDLG - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxDumpOpenExternalDlg) -}; - -BEGIN_EVENT_TABLE(wxDumpOpenExternalDlg, wxDialog) - -#if wxUSE_FILEDLG -EVT_BUTTON(wxID_MORE, wxDumpOpenExternalDlg::OnBrowse) -#endif - -END_EVENT_TABLE() - - -wxDumpOpenExternalDlg::wxDumpOpenExternalDlg(wxWindow *parent, const wxFileName& filename) -: wxDialog(parent, wxID_ANY, -wxString::Format -( - _("Open file \"%s\""), - filename.GetFullPath().c_str() -) -) -{ - // create controls - // --------------- - - wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL); - sizerTop->Add(new wxStaticText(this, wxID_ANY, - wxString::Format - ( - _("Enter command to open file \"%s\":"), - filename.GetFullName().c_str() - )), - wxSizerFlags().Border()); - - wxSizer *sizerH = new wxBoxSizer(wxHORIZONTAL); - - wxTextCtrl *command = new wxTextCtrl - ( - this, - wxID_ANY, - wxEmptyString, - wxDefaultPosition, - wxSize(250, wxDefaultCoord), - 0 -#if wxUSE_VALIDATORS - ,wxTextValidator(wxFILTER_NONE, &m_command) -#endif - ); - sizerH->Add(command, - wxSizerFlags(1).Align(wxALIGN_CENTER_VERTICAL)); - -#if wxUSE_FILEDLG - - wxButton *browse = new wxButton(this, wxID_MORE, wxT(">>"), - wxDefaultPosition, wxDefaultSize, - wxBU_EXACTFIT); - sizerH->Add(browse, - wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL). Border(wxLEFT)); - -#endif // wxUSE_FILEDLG - - sizerTop->Add(sizerH, wxSizerFlags(0).Expand().Border()); - - sizerTop->Add(new wxStaticLine(this), wxSizerFlags().Expand().Border()); - - sizerTop->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), - wxSizerFlags().Align(wxALIGN_RIGHT).Border()); - - // set the sizer &c - // ---------------- - - SetSizer(sizerTop); - - Layout(); - Fit(); - - command->SetFocus(); -} - -#if wxUSE_FILEDLG - -void wxDumpOpenExternalDlg::OnBrowse(wxCommandEvent& ) -{ - wxFileName fname(m_command); - wxFileDialog dlg(this, - wxFileSelectorPromptStr, - fname.GetPathWithSep(), - fname.GetFullName() -#ifdef __WXMSW__ - , _("Executable files (*.exe)|*.exe|All files (*.*)|*.*||") -#endif // __WXMSW__ - ); - if ( dlg.ShowModal() == wxID_OK ) - { - m_command = dlg.GetPath(); - TransferDataToWindow(); - } -} - -#endif // wxUSE_FILEDLG - -// ---------------------------------------------------------------------------- -// wxDebugReportDialog: class showing debug report to the user -// ---------------------------------------------------------------------------- - -class wxDebugReportDialog : public wxDialog -{ -public: - wxDebugReportDialog(wxDebugReport& dbgrpt); - - virtual bool TransferDataToWindow(); - virtual bool TransferDataFromWindow(); - -private: - void OnView(wxCommandEvent& ); - void OnViewUpdate(wxUpdateUIEvent& ); - void OnOpen(wxCommandEvent& ); - - - // small helper: add wxEXPAND and wxALL flags - static wxSizerFlags SizerFlags(int proportion) - { - return wxSizerFlags(proportion).Expand().Border(); - } - - - wxDebugReport& m_dbgrpt; - - wxCheckListBox *m_checklst; - wxTextCtrl *m_notes; - - wxArrayString m_files; - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxDebugReportDialog) -}; - -// ============================================================================ -// wxDebugReportDialog implementation -// ============================================================================ - -BEGIN_EVENT_TABLE(wxDebugReportDialog, wxDialog) -EVT_BUTTON(wxID_VIEW_DETAILS, wxDebugReportDialog::OnView) -EVT_UPDATE_UI(wxID_VIEW_DETAILS, wxDebugReportDialog::OnViewUpdate) -EVT_BUTTON(wxID_OPEN, wxDebugReportDialog::OnOpen) -EVT_UPDATE_UI(wxID_OPEN, wxDebugReportDialog::OnViewUpdate) -END_EVENT_TABLE() - - -// ---------------------------------------------------------------------------- -// construction -// ---------------------------------------------------------------------------- -/** - * wxDebugReportDialog: This contains modifications to implement changes to - * the content of the dialog. - * - * @param wxDebugReport & dbgrpt reference to compressed report file. - **/ -wxDebugReportDialog::wxDebugReportDialog(wxDebugReport& dbgrpt) -: wxDialog(NULL, wxID_ANY, - wxString(DIALOG_TITLE), - wxDefaultPosition, - wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), - m_dbgrpt(dbgrpt) -{ - // upper part of the dialog: explanatory message - wxString msg; - msg << DIALOG_REGRET - << DIALOG_REPGEN - << _T("\"") << dbgrpt.GetDirectory() << _T("\"\n") - << DIALOG_SPACER - << DIALOG_FILES - << DIALOG_PRIVACY - << DIALOG_RESPECT - << DIALOG_SPACER - << DIALOG_APOLOGY - ; - - const wxSizerFlags flagsFixed(SizerFlags(0)); - const wxSizerFlags flagsExpand(SizerFlags(1)); - const wxSizerFlags flagsExpand2(SizerFlags(2)); - - wxSizer *sizerPreview = new wxStaticBoxSizer(wxVERTICAL, this, PROBLEM_TITLE); - sizerPreview->Add(CreateTextSizer(msg), SizerFlags(0).Centre()); - - // ... and the list of files in this debug report with buttons to view them - wxSizer *sizerFileBtns = new wxBoxSizer(wxVERTICAL); - sizerFileBtns->AddStretchSpacer(1); - sizerFileBtns->Add(new wxButton(this, wxID_VIEW_DETAILS, _T("&View...")), - wxSizerFlags().Border(wxBOTTOM)); - sizerFileBtns->Add(new wxButton(this, wxID_OPEN, _T("&Open...")), - wxSizerFlags().Border(wxTOP)); - sizerFileBtns->AddStretchSpacer(1); - - m_checklst = new wxCheckListBox(this, wxID_ANY); - - wxSizer *sizerFiles = new wxBoxSizer(wxHORIZONTAL); - sizerFiles->Add(m_checklst, flagsExpand); - sizerFiles->Add(sizerFileBtns, flagsFixed); - - sizerPreview->Add(sizerFiles, flagsExpand2); - - - // lower part of the dialog: notes field - wxSizer *sizerNotes = new wxStaticBoxSizer(wxVERTICAL, this, _("&Notes:")); - - msg = DIALOG_ADDL_INFO; - - m_notes = new wxTextCtrl(this, wxID_ANY, wxEmptyString, - wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE); - - sizerNotes->Add(CreateTextSizer(msg), flagsFixed); - sizerNotes->Add(m_notes, flagsExpand); - - - wxSizer *sizerTop = new wxBoxSizer(wxVERTICAL); - sizerTop->Add(sizerPreview, flagsExpand2); - sizerTop->AddSpacer(5); - sizerTop->Add(sizerNotes, flagsExpand); - sizerTop->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), flagsFixed); - - SetSizerAndFit(sizerTop); - Layout(); - CentreOnScreen(); -} - -// ---------------------------------------------------------------------------- -// data exchange -// ---------------------------------------------------------------------------- - -bool wxDebugReportDialog::TransferDataToWindow() -{ - // all files are included in the report by default - const size_t count = m_dbgrpt.GetFilesCount(); - for ( size_t n = 0; n < count; n++ ) - { - wxString name, desc; - if ( m_dbgrpt.GetFile(n, &name, &desc) ) - { - m_checklst->Append(name + _T(" (") + desc + _T(')')); - m_checklst->Check(n); - - m_files.Add(name); - } - } - - return true; -} - -bool wxDebugReportDialog::TransferDataFromWindow() -{ - // any unchecked files should be removed from the report - const size_t count = m_checklst->GetCount(); - for ( size_t n = 0; n < count; n++ ) - { - if ( !m_checklst->IsChecked(n) ) - { - m_dbgrpt.RemoveFile(m_files[n]); - } - } - - // if the user entered any notes, add them to the report - const wxString notes = m_notes->GetValue(); - if ( !notes.empty() ) - { - // for now filename fixed, could make it configurable in the future... - m_dbgrpt.AddText(_T("notes.txt"), notes, _T("user notes")); - } - - return true; -} - -// ---------------------------------------------------------------------------- -// event handlers -// ---------------------------------------------------------------------------- - -void wxDebugReportDialog::OnView(wxCommandEvent& ) -{ - const int sel = m_checklst->GetSelection(); - wxCHECK_RET( sel != wxNOT_FOUND, _T("invalid selection in OnView()") ); - - wxFileName fn(m_dbgrpt.GetDirectory(), m_files[sel]); - wxString str; - - wxFFile file(fn.GetFullPath()); - if ( file.IsOpened() && file.ReadAll(&str) ) - { - wxDumpPreviewDlg dlg(this, m_files[sel], str); - dlg.ShowModal(); - } -} - -void wxDebugReportDialog::OnOpen(wxCommandEvent& ) -{ - const int sel = m_checklst->GetSelection(); - wxCHECK_RET( sel != wxNOT_FOUND, _T("invalid selection in OnOpen()") ); - - wxFileName fn(m_dbgrpt.GetDirectory(), m_files[sel]); - - // try to get the command to open this kind of files ourselves - wxString command; -#if wxUSE_MIMETYPE - wxFileType * - ft = wxTheMimeTypesManager->GetFileTypeFromExtension(fn.GetExt()); - if ( ft ) - { - command = ft->GetOpenCommand(fn.GetFullPath()); - delete ft; - } -#endif // wxUSE_MIMETYPE - - // if we couldn't, ask the user - if ( command.empty() ) - { - wxDumpOpenExternalDlg dlg(this, fn); - if ( dlg.ShowModal() == wxID_OK ) - { - // get the command chosen by the user and append file name to it - - // if we don't have place marker for file name in the command... - wxString cmd = dlg.GetCommand(); - if ( !cmd.empty() ) - { -#if wxUSE_MIMETYPE - if ( cmd.find(_T('%')) != wxString::npos ) - { - command = wxFileType::ExpandCommand(cmd, fn.GetFullPath()); - } - else // no %s nor %1 -#endif // wxUSE_MIMETYPE - { - // append the file name to the end - command << cmd << _T(" \"") << fn.GetFullPath() << _T('"'); - } - } - } - } - - if ( !command.empty() ) - ::wxExecute(command); -} - -void wxDebugReportDialog::OnViewUpdate(wxUpdateUIEvent& event) -{ - int sel = m_checklst->GetSelection(); - if (sel >= 0) - { - wxFileName fn(m_dbgrpt.GetDirectory(), m_files[sel]); - event.Enable(fn.FileExists()); - } - else - event.Enable(false); -} - - -// ============================================================================ -// wxDebugReportPreviewStd implementation -// ============================================================================ - -bool wxDebugReportPreviewStd::Show(wxDebugReport& dbgrpt) const -{ - if ( !dbgrpt.GetFilesCount() ) - return false; - - wxDebugReportDialog dlg(dbgrpt); - -#ifdef __WXMSW__ - // before entering the event loop (from ShowModal()), block the event - // handling for all other windows as this could result in more crashes - wxEventLoop::SetCriticalWindow(&dlg); -#endif // __WXMSW__ - - return dlg.ShowModal() == wxID_OK && dbgrpt.GetFilesCount() != 0; -} - - - - - - - - - - - - -// ---------------------------------------------------------------------------- -// custom debug reporting class -// ---------------------------------------------------------------------------- - -// this is your custom debug reporter: it will use curl program (which should -// be available) to upload the crash report to the given URL (which should be -// set up by you) -class MyDebugReport : public wxDebugReportUpload -{ -public: - MyDebugReport() - : wxDebugReportUpload(_T("http://your.url.here/"), _T("report:file"), _T("action")) - { - //This would be the place to put the compressed file in a custom directory - //but since the directory member is private, it would require pulling in yet - //another wxWidget module to modify and I decided against it and take the - //time and date based random location generator. (JAC - 4/16/07) - } - -protected: - // this is called with the contents of the server response: you will - // probably want to parse the XML document in OnServerReply() instead of - // just dumping it as I do - virtual bool OnServerReply(const wxArrayString& reply) - { - if ( reply.IsEmpty() ) - { - wxLogError(_T("Didn't receive the expected server reply.")); - return false; - } - - wxString s(_T("Server replied:\n")); - - const size_t count = reply.GetCount(); - for ( size_t n = 0; n < count; n++ ) - { - s << _T('\t') << reply[n] << _T('\n'); - } - - wxLogMessage(_T("%s"), s.c_str()); - - return true; - } - /** - * DoProcess: This is called by the wxWidgets Dialog when OK is clicked. - * Since it overrides a virtual method we must make sure - * that DoProcess() of the parent class is called. - * - * @return bool true if upload was successful, false otherwise. - **/ - virtual bool DoProcess() - { - //Call the DoProcess of the parent class and make sure it is successful. - if ( !wxDebugReportCompress::DoProcess() ) - return false; - - //Shell execute the curl command with the appropriate arguments - //for the custom application of this class: - // - //C:\curl-7.16.0\curl -v -F upfile=@"" --trace trace.txt - // - //The -v and the --trace trace.txt arguments are for debugging and are not required. - wxArrayString output, errors; - int rc = wxExecute(wxString::Format - ( - _T("%s -v -F %s=@\"%s\" --trace trace.txt %s"), - _T("C:\\curl-7.16.0\\curl"), - _T("upfile"), - GetCompressedFileName().c_str(), - _T("http://kaxa.findhere.org/0ad/upload.php") - ), - output, - errors); - //Check the result for errors and log them with wxWidgets. - if ( rc == -1 ) - { - wxLogError(_("Failed to execute curl, please install it in PATH.")); - } - else if ( rc != 0 ) - { - const size_t count = errors.GetCount(); - if ( count ) - { - for ( size_t n = 0; n < count; n++ ) - { - wxLogWarning(_T("%s"), errors[n].c_str()); - } - } - - wxLogError(_("Failed to upload the debug report (error code %d)."), rc); - } - else // rc == 0 - { - //this causes a crash and I have no incentive to debug it - //if ( OnServerReply(output) ) - return true; - } - - return false; - } -}; - -// another possibility would be to build email library from contrib and use -// this class, after uncommenting it: -#if 0 - -#include "wx/net/email.h" - -class MyDebugReport : public wxDebugReportCompress -{ -public: - virtual bool DoProcess() - { - if ( !wxDebugReportCompress::DoProcess() ) - return false; - wxMailMessage msg(GetReportName() + _T(" crash report"), - _T("vadim@wxwindows.org"), - wxEmptyString, // mail body - wxEmptyString, // from address - GetCompressedFileName(), - _T("crashreport.zip")); - - return wxEmail::Send(msg); - } -}; - -#endif // 0 - -// ---------------------------------------------------------------------------- -// application class -// ---------------------------------------------------------------------------- - -bool m_uploadReport = true; - -//m_generateReport = false; //Flag to report back to wxWidgets that indicates -//whether the DoProcess() method should be called. -//GenerateReport(false); //Create the compressed report file. -//The argument is not used in this version but -//is meant to indicate whether crash files already -//existed and the calling method did not create new ones. -//false means the crash files were recently generated... -//true means they already existed. - -// this is where we really generate the debug report -//void GenerateReport(bool CrashFilesExist); - - -void GenerateReport() -{ - wxDebugReportCompress *report = m_uploadReport ? new MyDebugReport - : new wxDebugReportCompress; - wxString fn1, fn2, fn3; - fn1 = _T(LOGS_LOCATION); - fn1 += _T("crashlog.dmp"); - fn2 = _T(LOGS_LOCATION); - fn2 += _T("crashlog.txt"); - - if(wxFileExists(fn1)) - report->AddFile(fn1, _T("memory dump")); - else - wxLogError(_T("crashlog.dmp not found!")); - if(wxFileExists(fn2)) - report->AddFile(fn2, _T("debug information")); - else - wxLogError(_T("crashlog.txt not found!")); - - //Then call the built in wxWidgets dialog which is modified to be - //customizable for each individual project and can be found in - // *****dbgrptg.cpp***** - bool m_generateReport = wxDebugReportPreviewStd().Show(*report); - if ( m_generateReport ) - { //User clicked OK - if ( report->Process() ) - { - if ( m_uploadReport ) - { - wxLogMessage(DIALOG_UPLOAD); - } - else - { - wxLogMessage(_T("Report generated in \"%s\"."), - report->GetCompressedFileName().c_str()); - report->Reset(); - } - } - // remove all crashlog files whether or not upload was successful!! - wxRemove(fn1); - wxRemove(fn2); - } - //delete the compressed report file always!! - delete report; -} - -#endif Index: source/tools/atlas/AtlasUI/Misc/DLLInterface.h =================================================================== --- source/tools/atlas/AtlasUI/Misc/DLLInterface.h +++ source/tools/atlas/AtlasUI/Misc/DLLInterface.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -33,6 +33,4 @@ ATLASDLLIMPEXP void Atlas_DisplayError(const wchar_t* text, size_t flags); -ATLASDLLIMPEXP void Atlas_ReportError(); - #endif // DLLINTERFACE_INCLUDED Index: source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp =================================================================== --- source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp +++ source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2012 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,7 +25,6 @@ #include "ActorEditor/ActorEditor.h" #include "ScenarioEditor/ScenarioEditor.h" -#include "ErrorReporter/ErrorReporter.h" #include "GameInterface/MessagePasser.h" @@ -177,12 +176,6 @@ // this function is called } - -ATLASDLLIMPEXP void Atlas_ReportError() -{ - ///ReportError(); // janwas: disabled until ErrorReporter.cpp compiles -} - class AtlasDLLApp : public wxApp { public: Index: source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Cinema/Cinema.cpp =================================================================== --- source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Cinema/Cinema.cpp +++ source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Cinema/Cinema.cpp @@ -95,7 +95,7 @@ if (m_NewPathName->GetValue().empty()) return; - POST_COMMAND(AddCinemaPath, (m_NewPathName->GetValue().ToStdWstring())); + POST_COMMAND(AddCinemaPath, ((std::wstring)m_NewPathName->GetValue().wc_str())); m_NewPathName->Clear(); ReloadPathList(); } @@ -110,7 +110,7 @@ if (pathName.empty()) return; - POST_COMMAND(DeleteCinemaPath, (pathName.ToStdWstring())); + POST_COMMAND(DeleteCinemaPath, ((std::wstring)pathName.wc_str())); ReloadPathList(); } Index: source/tools/atlas/GameInterface/GameLoop.cpp =================================================================== --- source/tools/atlas/GameInterface/GameLoop.cpp +++ source/tools/atlas/GameInterface/GameLoop.cpp @@ -57,7 +57,6 @@ void (*Atlas_GLSwapBuffers)(void* canvas); void (*Atlas_NotifyEndOfFrame)(); void (*Atlas_DisplayError)(const wchar_t* text, size_t flags); -void (*Atlas_ReportError)(); namespace AtlasMessage { void* (*ShareableMallocFptr)(size_t); @@ -285,7 +284,6 @@ dll.LoadSymbol("Atlas_GLSwapBuffers", Atlas_GLSwapBuffers); dll.LoadSymbol("Atlas_NotifyEndOfFrame", Atlas_NotifyEndOfFrame); dll.LoadSymbol("Atlas_DisplayError", Atlas_DisplayError); - dll.LoadSymbol("Atlas_ReportError", Atlas_ReportError); dll.LoadSymbol("ShareableMalloc", ShareableMallocFptr); dll.LoadSymbol("ShareableFree", ShareableFreeFptr); } Index: source/tools/atlas/GameInterface/Handlers/TriggerHandler.cpp =================================================================== --- source/tools/atlas/GameInterface/Handlers/TriggerHandler.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with 0 A.D. If not, see . - */ - -#include "precompiled.h" - -#include "MessageHandler.h" -#include "../CommandProc.h" -#include "../Shareable.h" - -#include "ps/Game.h" -#include "graphics/GameView.h" -#include "graphics/CinemaManager.h" -#include "simulation2/Simulation2.h" - -namespace AtlasMessage { - -/* -sTriggerSpec TriggerSpecToAtlas(const CTriggerSpec& spec) -{ - sTriggerSpec atlasSpec; - std::vector< sTriggerParameter > atlasParameters; - const std::set& parameters = spec.GetParameters(); - - for ( std::set::const_iterator it=parameters.begin(); it!=parameters.end(); ++it ) - { - sTriggerParameter atlasParam; - atlasParam.column = it->column; - atlasParam.inputType = it->inputType; - atlasParam.name = it->name; - atlasParam.parameterOrder = it->parameterOrder; - atlasParam.row = it->row; - - atlasParam.windowType = it->windowType; - atlasParam.xPos = it->xPos; - atlasParam.yPos = it->yPos; - atlasParam.xSize = it->xSize; - atlasParam.ySize = it->ySize; - atlasParameters.push_back(atlasParam); - } - atlasSpec.functionName = spec.functionName; - atlasSpec.displayName = spec.displayName; - atlasSpec.parameters = atlasParameters; - return atlasSpec; -} - -sTrigger TriggerToAtlas(const MapTrigger& trigger) -{ - sTrigger atlasTrigger; - atlasTrigger.active = trigger.active; - atlasTrigger.timeValue = trigger.timeValue; - atlasTrigger.maxRuns = trigger.maxRunCount; - atlasTrigger.group = trigger.groupName; - atlasTrigger.name = trigger.name; - - std::vector atlasNots; - std::vector atlasBlocks, atlasBlockEnds; - - for ( std::set::const_iterator it = trigger.logicBlocks.begin(); - it != trigger.logicBlocks.end(); ++it ) - { - atlasBlocks.push_back( (int)it->index ); - atlasNots.push_back( it->negated ); - } - for ( std::set::const_iterator it = trigger.logicBlockEnds.begin(); - it != trigger.logicBlockEnds.end(); ++it ) - { - atlasBlockEnds.push_back( (int)*it ); - } - atlasTrigger.logicNots = atlasNots; - atlasTrigger.logicBlocks = atlasBlocks; - atlasTrigger.logicBlockEnds = atlasBlockEnds; - - std::vector atlasConditions; - std::vector atlasEffects; - - for ( std::list::const_iterator it = trigger.conditions.begin(); - it != trigger.conditions.end(); ++it ) - { - sTriggerCondition atlasCondition; - atlasCondition.linkLogic = it->linkLogic; - atlasCondition.name = it->name; - atlasCondition.functionName = it->functionName; - atlasCondition.displayName = it->displayName; - atlasCondition.negated = it->negated; - std::vector parameters; - - for ( std::list::const_iterator it2=it->parameters.begin(); - it2 != it->parameters.end(); ++it2 ) - { - parameters.push_back( std::wstring(it2->c_str()) ); - } - - atlasCondition.parameters = parameters; - atlasConditions.push_back(atlasCondition); - } - - for ( std::list::const_iterator it = trigger.effects.begin(); - it != trigger.effects.end(); ++it ) - { - sTriggerEffect atlasEffect; - std::vector parameters; - atlasEffect.name = it->name; - atlasEffect.functionName = it->functionName; - atlasEffect.displayName = it->displayName; - - for ( std::list::const_iterator it2 = it->parameters.begin(); - it2 != it->parameters.end(); ++it2 ) - { - parameters.push_back( std::wstring(it2->c_str()) ); - } - atlasEffect.parameters = parameters; - atlasEffects.push_back( atlasEffect ); - } - - atlasTrigger.conditions = atlasConditions; - atlasTrigger.effects = atlasEffects; - return atlasTrigger; -} - -sTriggerGroup GroupToAtlas(const MapTriggerGroup& group) -{ - sTriggerGroup atlasGroup; - atlasGroup.parentName = group.parentName; - atlasGroup.name = group.name; - - std::vector atlasTriggers; - std::vector atlasChildren; - for ( std::list::const_iterator it = group.triggers.begin(); - it != group.triggers.end(); ++it ) - { - atlasTriggers.push_back( TriggerToAtlas(*it) ); - } - for ( std::list::const_iterator it = group.childGroups.begin(); - it != group.childGroups.end(); ++it ) - { - atlasChildren.push_back( std::wstring(it->c_str()) ); - } - - atlasGroup.triggers = atlasTriggers; - atlasGroup.children = atlasChildren; - return atlasGroup; -} - -MapTrigger AtlasToTrigger(const sTrigger& trigger) -{ - MapTrigger engineTrigger; - - engineTrigger.active = trigger.active; - engineTrigger.groupName = *trigger.group; - - std::vector blockEnds = *trigger.logicBlockEnds, blocks = *trigger.logicBlocks; - std::copy( blockEnds.begin(), blockEnds.end(), inserter(engineTrigger.logicBlockEnds, engineTrigger.logicBlockEnds.begin()) ); - std::copy( blocks.begin(), blocks.end(), inserter(engineTrigger.logicBlocks, engineTrigger.logicBlocks.begin()) ); - - engineTrigger.maxRunCount = trigger.maxRuns; - engineTrigger.name = *trigger.name; - engineTrigger.timeValue = trigger.timeValue; - - std::vector conditions = *trigger.conditions; - std::vector effects = *trigger.effects; - - for ( std::vector::const_iterator it = conditions.begin(); it != conditions.end(); ++it ) - { - engineTrigger.conditions.push_back( MapTriggerCondition() ); - MapTriggerCondition* cond = &engineTrigger.conditions.back(); - - cond->functionName = *it->functionName; - cond->displayName = *it->displayName; - cond->linkLogic = it->linkLogic; - cond->name = *it->name; - cond->negated = it->negated; - - std::vector parameters = *it->parameters; - for ( std::vector::const_iterator it2 = parameters.begin(); it2 != parameters.end(); ++it2 ) - { - cond->parameters.push_back( CStrW(*it2) ); - } - } - - for ( std::vector::const_iterator it = effects.begin(); it != effects.end(); ++it ) - { - engineTrigger.effects.push_back( MapTriggerEffect() ); - MapTriggerEffect* effect = &engineTrigger.effects.back(); - - effect->functionName = *it->functionName; - effect->displayName = *it->displayName; - effect->name = *it->name; - std::vector parameters = *it->parameters; - - for ( std::vector::const_iterator it2 = parameters.begin(); it2 != parameters.end(); ++it2 ) - { - effect->parameters.push_back( CStrW(*it2) ); - } - } - - return engineTrigger; -} - - -MapTriggerGroup AtlasToGroup(const sTriggerGroup& group) -{ - MapTriggerGroup engineGroup; - engineGroup.parentName = *group.parentName; - engineGroup.name = *group.name; - - std::list engineTriggers; - std::vector atlasChildren = *group.children; - std::vector atlasTriggers = *group.triggers; - - for ( std::vector::const_iterator it = atlasTriggers.begin(); - it != atlasTriggers.end(); ++it ) - { - engineTriggers.push_back( AtlasToTrigger(*it) ); - } - for ( std::vector::const_iterator it = atlasChildren.begin(); it != atlasChildren.end(); ++it ) - engineGroup.childGroups.push_back(*it); - - engineGroup.triggers = engineTriggers; - return engineGroup; -} - -std::vector GetCurrentTriggers() -{ - return std::vector(); - - const std::list& groups = g_TriggerManager.GetAllTriggerGroups(); - std::vector atlasGroups; - - for ( std::list::const_iterator it = groups.begin(); it != groups.end(); ++it ) - atlasGroups.push_back( GroupToAtlas(*it) ); - return atlasGroups; -} - -void SetCurrentTriggers(const std::vector& groups) -{ - std::list engineGroups; - for ( std::vector::const_iterator it = groups.begin(); it != groups.end(); ++it ) - engineGroups.push_back( AtlasToGroup(*it) ); - g_TriggerManager.SetAllGroups(engineGroups); -} -*/ - -QUERYHANDLER(GetTriggerData) -{ - UNUSED2(msg); -/* - const std::list& conditions = g_TriggerManager.GetAllConditions(); - const std::list& effects = g_TriggerManager.GetAllEffects(); - std::vector atlasConditions; - std::vector atlasEffects; - - for ( std::list::const_iterator it=conditions.begin(); it!=conditions.end(); ++it ) - atlasConditions.push_back( TriggerSpecToAtlas(*it) ); - for ( std::list::const_iterator it=effects.begin(); it!=effects.end(); ++it ) - atlasEffects.push_back( TriggerSpecToAtlas(*it) ); - - msg->conditions = atlasConditions; - msg->effects = atlasEffects; - msg->groups = GetCurrentTriggers(); -*/ -} - -QUERYHANDLER(GetTriggerChoices) -{ - UNUSED2(msg); -/* - CStrW selectedName(*msg->name); - std::vector choices = g_TriggerManager.GetTriggerChoices(selectedName); - std::vector translations = g_TriggerManager.GetTriggerTranslations(selectedName); - - if ( choices.empty() ) - return; - - //If a special list (i.e. uses engine data) - if ( choices.size() == 1 ) - { - - if ( choices[0] == std::wstring(L"ATLAS_CINEMA_LIST") ) - { - choices.clear(); - const std::map& paths = g_Game->GetView()->GetCinema()->GetAllPaths(); - for ( std::map::const_iterator it = paths.begin(); it != paths.end(); ++it ) - { - choices.push_back(it->first); - translations.push_back( L"\"" + it->first + L"\"" ); //Strings need quotes in JS - } - } - else if ( choices[0] == std::wstring(L"ATLAS_TRIGGER_LIST") ) - { - choices.clear(); - const std::list& groups = g_TriggerManager.GetAllTriggerGroups(); - for ( std::list::const_iterator it = groups.begin(); - it != groups.end(); ++it ) - { - for ( std::list::const_iterator it2 = it->triggers.begin(); - it2 != it->triggers.end(); ++it2 ) - { - choices.push_back(it2->name); - translations.push_back( L"\"" + it2->name + L"\"" ); - } - } - } - else if ( choices[0] == std::wstring(L"ATLAS_TRIG_GROUP_LIST") ) - { - choices.clear(); - const std::list& groups = g_TriggerManager.GetAllTriggerGroups(); - for ( std::list::const_iterator it = groups.begin(); - it != groups.end(); ++it ) - { - choices.push_back(it->name); - translations.push_back( L"\"" + it->name + L"\"" ); - } - } - else - debug_warn(L"Invalid special choice list for trigger specification parameter"); - } - msg->choices = choices; - msg->translations = translations; -*/ -} - -QUERYHANDLER(GetWorldPosition) -{ - Position pos; - pos.type1.x = msg->x; - pos.type1.y = msg->y; - CVector3D worldPos = pos.GetWorldSpace(); - Position ret(worldPos.X, worldPos.Y, worldPos.Z); - msg->position = ret; -} -BEGIN_COMMAND(SetAllTriggers) -{ -// std::vector m_oldGroups, m_newGroups; - - void Do() - { -// m_oldGroups = GetCurrentTriggers(); -// m_newGroups = *msg->groups; -// Redo(); - } - void Redo() - { -// SetCurrentTriggers(m_newGroups); - } - void Undo() - { -// SetCurrentTriggers(m_oldGroups); - } -}; -END_COMMAND(SetAllTriggers) - - -MESSAGEHANDLER(TriggerToggleSelector) -{ - //TODO: Draw stuff - UNUSED2(msg); -} - -} Index: source/tools/atlas/GameInterface/Messages.h =================================================================== --- source/tools/atlas/GameInterface/Messages.h +++ source/tools/atlas/GameInterface/Messages.h @@ -704,43 +704,6 @@ ////////////////////////////////////////////////////////////////////////// -enum eTriggerListType -{ - CINEMA_LIST, - TRIGGER_LIST, - TRIG_GROUP_LIST //list of trigger groups - // [Eventually include things like entities and areas as the editor progresses...] -}; - - -QUERY(GetTriggerData, - , //no inputs - ((std::vector, groups)) - ((std::vector, conditions)) - ((std::vector, effects)) - ); - -QUERY(GetTriggerChoices, - ((std::wstring, name)), - ((std::vector, choices)) - ((std::vector, translations)) - ); - -COMMAND(SetAllTriggers, NOMERGE, - ((std::vector, groups)) - ); - -QUERY(GetWorldPosition, - ((int, x)) - ((int, y)), - ((Position, position)) - ); - -MESSAGE(TriggerToggleSelector, - ((bool, enable)) - ((Position, position)) - ); - QUERY(GetSelectedObjectsTemplateNames, ((std::vector, ids)) , Index: source/tools/atlas/GameInterface/SharedTypes.h =================================================================== --- source/tools/atlas/GameInterface/SharedTypes.h +++ source/tools/atlas/GameInterface/SharedTypes.h @@ -144,120 +144,6 @@ }; SHAREABLE_STRUCT(sCameraInfo); - -//******Triggers***** - - -struct sTriggerParameter -{ - Shareable row, column, xPos, yPos, xSize, ySize, parameterOrder; - Shareable name, inputType, windowType; -}; -SHAREABLE_STRUCT(sTriggerParameter); - -struct sTriggerSpec -{ - Shareable > parameters; - Shareable displayName, functionName; - - bool operator== ( const std::wstring& name) const - { - return ( *displayName == name ); - } -}; -SHAREABLE_STRUCT(sTriggerSpec); - - -struct sTriggerCondition -{ - sTriggerCondition() : linkLogic(1), negated(false) {} - sTriggerCondition(const std::wstring& _name) : linkLogic(1), negated(false), name(_name) {} - - //displayName is used for selecting choice items in Atlas - Shareable name, functionName, displayName; - Shareable< std::vector > parameters; - - //0 = none, 1 = and, 2 = or - Shareable linkLogic; - Shareable negated; - - bool operator== ( const std::wstring& _name ) const - { - return (*name == _name); - } -}; - -SHAREABLE_STRUCT(sTriggerCondition); - -struct sTriggerEffect -{ - sTriggerEffect() {} - sTriggerEffect(const std::wstring& _name) : name(_name) {} - - Shareable name, functionName, displayName; - Shareable< std::vector > parameters; - //Shareable loop; - - bool operator== ( const std::wstring& _name ) - { - return (*name == _name); - } -}; -SHAREABLE_STRUCT(sTriggerEffect); - -struct sTrigger -{ - Shareable name, group; - Shareable< std::vector > conditions; - Shareable< std::vector > effects; - - //For beginnings, the index of the term it comes before. For ends, the index it comes after - Shareable< std::vector > logicBlocks, logicBlockEnds; - Shareable< std::vector > logicNots; //true if logicBlocks are not-ed - Shareable timeValue; - Shareable maxRuns; - Shareable active; - - sTrigger() : timeValue(0), maxRuns(-1), active(0) {} - sTrigger(const std::wstring& _name) : name(_name), timeValue(0), maxRuns(0), active(true) {} - bool operator== (const sTrigger& trigger) - { - return (*name == *trigger.name); - } - bool operator!= (const sTrigger& trigger) - { - return (*name != *trigger.name); - } -}; -SHAREABLE_STRUCT(sTrigger); - - -struct sTriggerGroup -{ - Shareable name, parentName; - Shareable< std::vector > children; - Shareable< std::vector > triggers; - - sTriggerGroup() { } - sTriggerGroup(const std::wstring& _name) : name(_name) { } - - bool operator== (const sTriggerGroup& group) const - { - return (*name == *group.name); - } - bool operator== (const std::wstring& _name) const - { - return (*name == _name); - } - /*bool operator!= (const sTriggerGroup& group) - { - return (*name != *group.name); - }*/ -}; - -SHAREABLE_STRUCT(sTriggerGroup); - - } #endif // INCLUDED_SHAREDTYPES Index: source/tools/atlas/GameInterface/View.cpp =================================================================== --- source/tools/atlas/GameInterface/View.cpp +++ source/tools/atlas/GameInterface/View.cpp @@ -213,8 +213,7 @@ // Cinematic motion should be independent of simulation update, so we can // preview the cinematics by themselves - if (g_Game->GetView()->GetCinema()->IsPlaying()) - g_Game->GetView()->GetCinema()->Update(realFrameLength); + g_Game->GetView()->GetCinema()->Update(realFrameLength); } void AtlasViewGame::Render() Index: source/tools/dist/0ad.nsi =================================================================== --- source/tools/dist/0ad.nsi +++ source/tools/dist/0ad.nsi @@ -13,6 +13,9 @@ ;-------------------------------- ;General + ;Properly display all languages (Installer will not work on Windows 95, 98 or ME!) + Unicode true + ;Name and file Name "0 A.D." OutFile "${PREFIX}-win32.exe" @@ -37,6 +40,15 @@ !define MUI_WELCOMEFINISHPAGE_BITMAP ${CHECKOUTPATH}\build\resources\installer.bmp !define MUI_ICON ${CHECKOUTPATH}\build\resources\ps.ico !define MUI_ABORTWARNING + !define MUI_LANGDLL_ALLLANGUAGES + +;-------------------------------- +;Language Selection Dialog Settings + + ;Remember the installer language + !define MUI_LANGDLL_REGISTRY_ROOT "HKCU" + !define MUI_LANGDLL_REGISTRY_KEY "Software\0 A.D." + !define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" ;-------------------------------- ;Pages @@ -65,8 +77,29 @@ ;-------------------------------- ;Languages +; Keep in sync with remove-incomplete-translations.sh - !insertmacro MUI_LANGUAGE "English" + !insertmacro MUI_LANGUAGE "English" ;first language is the default language + !insertmacro MUI_LANGUAGE "Bulgarian" + !insertmacro MUI_LANGUAGE "Catalan" + !insertmacro MUI_LANGUAGE "Czech" + !insertmacro MUI_LANGUAGE "Dutch" + !insertmacro MUI_LANGUAGE "French" + !insertmacro MUI_LANGUAGE "Galician" + !insertmacro MUI_LANGUAGE "German" + !insertmacro MUI_LANGUAGE "Hungarian" + !insertmacro MUI_LANGUAGE "Indonesian" + !insertmacro MUI_LANGUAGE "Italian" + !insertmacro MUI_LANGUAGE "Norwegian" + !insertmacro MUI_LANGUAGE "Polish" + !insertmacro MUI_LANGUAGE "Portuguese" + !insertmacro MUI_LANGUAGE "PortugueseBR" + !insertmacro MUI_LANGUAGE "Russian" + !insertmacro MUI_LANGUAGE "ScotsGaelic" + !insertmacro MUI_LANGUAGE "Slovak" + !insertmacro MUI_LANGUAGE "Spanish" + !insertmacro MUI_LANGUAGE "Swedish" + !insertmacro MUI_LANGUAGE "Turkish" ;-------------------------------- ;Installer Sections @@ -142,6 +175,8 @@ Function .onInit + !insertmacro MUI_LANGDLL_DISPLAY + ReadRegStr $R0 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\0 A.D." "UninstallString" StrCmp $R0 "" done @@ -208,3 +243,12 @@ DeleteRegKey /ifempty SHCTX "Software\0 A.D." SectionEnd + +;-------------------------------- +;Uninstaller Functions + +Function un.onInit + + !insertmacro MUI_UNGETLANGUAGE + +FunctionEnd Index: source/tools/dist/remove-incomplete-translations.sh =================================================================== --- source/tools/dist/remove-incomplete-translations.sh +++ source/tools/dist/remove-incomplete-translations.sh @@ -1,6 +1,7 @@ #!/bin/bash # Included languages +# Keep in sync with source/tools/i18n/creditTranslators.py and with the installer languages in 0ad.nsi LANGS=("bg" "ca" "cs" "de" "en_GB" "es" "fr" "gd" "gl" "hu" "id" "it" "nb" "nl" "pl" "pt_BR" "pt_PT" "ru" "sk" "sv" "tr") REGEX=$(printf "\|%s" "${LANGS[@]}")