Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml (revision 26015) @@ -1,73 +1,70 @@ 50 100 2.0 250 decay|rubble/rubble_field Field template_structure_resource_field Field Harvest grain for food. Each subsequent gatherer works less efficiently. structures/field.png 50 false false - - 15 40 5 false Infinity food.grain 5 0.90 interface/complete/building/complete_field.xml 8.0 - 0 structures/plot_field_found.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_rotarymill.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_rotarymill.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_rotarymill.xml (revision 26015) @@ -1,59 +1,56 @@ 100 200 100 6.0 2000 Rotary Mill template_structure_special_rotarymill RotaryMill structures/rotarymill.png 40 20 - - food true interface/complete/building/complete_ffactri.xml 8.0 false 32 40000 - 40 structures/fndn_6x6.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml (revision 26015) @@ -1,81 +1,82 @@ structures/xp_trickle 150 200 100 12.0 10 Infantry Cavalry 2000 decay|rubble/rubble_stone_4x4 Barracks template_structure_military_barracks Train Infantry and research Infantry technologies. Village Barracks structures/barracks.png phase_village 40 20 + barracks_batch_training infantry_cost_time unlock_champion_infantry pair_unlock_champions_sele interface/complete/building/complete_barracks.xml 0.8 units/{civ}/infantry_spearman_b units/{civ}/infantry_pikeman_b units/{civ}/infantry_maceman_b units/{civ}/infantry_axeman_b units/{civ}/infantry_swordsman_b units/{civ}/infantry_javelineer_b units/{civ}/infantry_slinger_b units/{civ}/infantry_archer_b units/{civ}/champion_infantry_spearman units/{civ}/champion_infantry_pikeman units/{civ}/champion_infantry_maceman units/{civ}/champion_infantry_axeman units/{civ}/champion_infantry_swordsman units/{civ}/champion_infantry_javelineer units/{civ}/champion_infantry_slinger units/{civ}/champion_infantry_archer 32 structures/fndn_6x6.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_elephant_stable.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_elephant_stable.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_elephant_stable.xml (revision 26015) @@ -1,63 +1,64 @@ structures/xp_trickle 180 200 200 8.0 5 Elephant 3000 decay|rubble/rubble_stone_6x6 Elephant Stable template_structure_military_elephant_stable Train Elephants and research Elephant technologies. City ElephantStable phase_city structures/stable_elephant.png 40 40 + interface/complete/building/complete_elephant_stable.xml 38 0.7 units/{civ}/support_elephant units/{civ}/elephant_archer_b units/{civ}/champion_elephant 40 structures/fndn_9x8.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_forge.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_forge.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_forge.xml (revision 26015) @@ -1,70 +1,71 @@ 120 200 12.0 1 Infantry Healer 2000 decay|rubble/rubble_stone_4x4 Forge template_structure_military_forge Research attack damage and damage resistance technologies. -ConquestCritical Town Forge structures/blacksmith.png phase_town 40 + soldier_attack_melee_01 soldier_attack_melee_02 soldier_attack_melee_03 soldier_attack_melee_03_variant soldier_attack_ranged_01 soldier_attack_ranged_02 soldier_attack_ranged_03 soldier_resistance_hack_01 soldier_resistance_hack_02 soldier_resistance_hack_03 soldier_resistance_pierce_01 soldier_resistance_pierce_02 soldier_resistance_pierce_03 archer_attack_spread interface/complete/building/complete_forge.xml 38 30000 32 structures/fndn_5x5.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_range.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_range.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_range.xml (revision 26015) @@ -1,55 +1,56 @@ 120 200 12.0 10 Infantry 2000 decay|rubble/rubble_stone_5x5 Practice Range template_structure_military_range Train Ranged Infantry and research technologies. Village Range structures/range.png phase_village 40 + interface/complete/building/complete_range.xml 0.8 units/{civ}/infantry_javelineer_b units/{civ}/infantry_slinger_b units/{civ}/infantry_archer_b 32 structures/fndn_7x7.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_corral.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_corral.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_corral.xml (revision 26015) @@ -1,69 +1,70 @@ 50 100 5.0 500 decay|rubble/rubble_stone_3x3 Corral template_structure_resource_corral Raise Domestic Animals for food. Economic Village Corral structures/corral.png phase_village 20 + gather_animals_stockbreeding 20 interface/complete/building/complete_corral.xml false 20 30000 0.7 gaia/fauna_goat_trainable gaia/fauna_sheep_trainable gaia/fauna_pig_trainable gaia/fauna_cattle_cow_trainable 20 structures/fndn_3x3.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_library.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_library.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_library.xml (revision 26015) @@ -1,57 +1,54 @@ structures/library Library 200 200 200 9.0 2000 decay|rubble/rubble_stone_4x6 Library template_structure_special_library Library structures/library_scroll.png 40 40 - - interface/complete/building/complete_library.xml false 50 40000 - 40 structures/fndn_7x9.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_wonder.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_wonder.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_wonder.xml (revision 26015) @@ -1,94 +1,95 @@ structures/wonder_population_cap Wonder 4 1000 1000 1500 1000 10.0 50 0.1 Unit Support Soldier 5 2 5000 decay|rubble/rubble_stone_6x6 Wonder template_structure_wonder Bring glory to your civilization and add large tracts of land to your empire. ConquestCritical City Wonder structures/wonder.png phase_city 200 300 200 + wonder_population_cap 15 25 3 1.0 1.0 1.0 1.0 2000 interface/complete/building/complete_wonder.xml true 100 65535 72 structures/fndn_9x9.xml Index: ps/trunk/binaries/data/mods/public/simulation/components/ProductionQueue.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/ProductionQueue.js (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/components/ProductionQueue.js (revision 26015) @@ -1,518 +1,528 @@ function ProductionQueue() {} ProductionQueue.prototype.Schema = "Helps the building to train new units and research technologies." + ""; ProductionQueue.prototype.ProgressInterval = 1000; ProductionQueue.prototype.MaxQueueSize = 16; /** * This object represents an item in the queue. */ ProductionQueue.prototype.Item = function() {}; /** * @param {number} producer - The entity ID of our producer. * @param {string} metadata - Optionally any metadata attached to us. */ ProductionQueue.prototype.Item.prototype.Init = function(producer, metadata) { this.producer = producer; this.metadata = metadata; +}; +/** + * @param {string} type - The type of queue to use. + * @param {string} templateName - The template to queue. + * @param {number} count - The amount of template to queue. Only applicable for type == "unit". + * + * @return {boolean} - Whether the item could be queued. + */ +ProductionQueue.prototype.Item.prototype.Queue = function(type, templateName, count) +{ + if (type == "unit") + return this.QueueEntity(templateName, count); + + if (type == "technology") + return this.QueueTechnology(templateName); + + warn("Tried to add invalid item of type \"" + type + "\" and template \"" + templateName + "\" to a production queue (entity: " + this.producer + ")."); + return false; }; +/** + * @param {string} templateName - The name of the entity to queue. + * @param {number} count - The number of entities that should be produced. + * @return {boolean} - Whether the batch was successfully created. + */ ProductionQueue.prototype.Item.prototype.QueueEntity = function(templateName, count) { const cmpTrainer = Engine.QueryInterface(this.producer, IID_Trainer); if (!cmpTrainer) return false; this.entity = cmpTrainer.QueueBatch(templateName, count, this.metadata); if (this.entity == -1) return false; this.originalItem = { "templateName": templateName, "count": count, "metadata": this.metadata }; return true; }; +/** + * @param {string} templateName - The name of the technology to queue. + * @return {boolean} - Whether the technology was successfully queued. + */ ProductionQueue.prototype.Item.prototype.QueueTechnology = function(templateName) { const cmpResearcher = Engine.QueryInterface(this.producer, IID_Researcher); if (!cmpResearcher) return false; this.technology = cmpResearcher.QueueTechnology(templateName, this.metadata); - if (this.technology == -1) - return false; - - return true; + return this.technology != -1; }; /** - * @param {number} id - The id of this item. + * @param {number} id - The id this item needs to get. */ ProductionQueue.prototype.Item.prototype.SetID = function(id) { this.id = id; }; ProductionQueue.prototype.Item.prototype.Stop = function() { - if (this.entity) - { - const cmpTrainer = Engine.QueryInterface(this.producer, IID_Trainer); - if (cmpTrainer) - cmpTrainer.StopBatch(this.entity); - } + if (this.entity > 0) + Engine.QueryInterface(this.producer, IID_Trainer)?.StopBatch(this.entity); - if (this.technology) - { - const cmpResearcher = Engine.QueryInterface(this.producer, IID_Researcher); - if (cmpResearcher) - cmpResearcher.StopResearching(this.technology); - } + if (this.technology > 0) + Engine.QueryInterface(this.producer, IID_Researcher)?.StopResearching(this.technology); }; /** * Called when the first work is performed. */ ProductionQueue.prototype.Item.prototype.Start = function() { this.started = true; }; +/** + * @return {boolean} - Whether there is work done on the item. + */ ProductionQueue.prototype.Item.prototype.IsStarted = function() { return !!this.started; }; /** * @return {boolean} - Whether this item is finished. */ ProductionQueue.prototype.Item.prototype.IsFinished = function() { return !!this.finished; }; /** * @param {number} allocatedTime - The time allocated to this item. * @return {number} - The time used for this item. */ ProductionQueue.prototype.Item.prototype.Progress = function(allocatedTime) { if (this.entity) { const cmpTrainer = Engine.QueryInterface(this.producer, IID_Trainer); allocatedTime -= cmpTrainer.Progress(this.entity, allocatedTime); if (!cmpTrainer.HasBatch(this.entity)) delete this.entity; } if (this.technology) { const cmpResearcher = Engine.QueryInterface(this.producer, IID_Researcher); allocatedTime -= cmpResearcher.Progress(this.technology, allocatedTime); if (!cmpResearcher.HasItem(this.technology)) delete this.technology; } if (!this.entity && !this.technology) this.finished = true; return allocatedTime; }; ProductionQueue.prototype.Item.prototype.Pause = function() { this.paused = true; if (this.entity) Engine.QueryInterface(this.producer, IID_Trainer).PauseBatch(this.entity); if (this.technology) Engine.QueryInterface(this.producer, IID_Researcher).PauseTechnology(this.technology); }; ProductionQueue.prototype.Item.prototype.Unpause = function() { delete this.paused; if (this.entity) Engine.QueryInterface(this.producer, IID_Trainer).UnpauseBatch(this.entity); if (this.technology) Engine.QueryInterface(this.producer, IID_Researcher).UnpauseTechnology(this.technology); }; +/** + * @return {boolean} - Whether the item is currently paused. + */ ProductionQueue.prototype.Item.prototype.IsPaused = function() { return !!this.paused; }; /** * @return {Object} - Some basic information of this item. */ ProductionQueue.prototype.Item.prototype.GetBasicInfo = function() { let result; if (this.technology) result = Engine.QueryInterface(this.producer, IID_Researcher).GetResearchingTechnology(this.technology); else if (this.entity) result = Engine.QueryInterface(this.producer, IID_Trainer).GetBatch(this.entity); result.id = this.id; result.paused = this.paused; return result; }; /** * @return {Object} - The originally queued item. */ ProductionQueue.prototype.Item.prototype.OriginalItem = function() { return this.originalItem; }; ProductionQueue.prototype.Item.prototype.Serialize = function() { return { "id": this.id, "metadata": this.metadata, "paused": this.paused, "producer": this.producer, "entity": this.entity, "technology": this.technology, "started": this.started, "originalItem": this.originalItem }; }; ProductionQueue.prototype.Item.prototype.Deserialize = function(data) { this.Init(data.producer, data.metadata); this.id = data.id; this.paused = data.paused; this.entity = data.entity; this.technology = data.technology; this.started = data.started; this.originalItem = data.originalItem; }; ProductionQueue.prototype.Init = function() { this.nextID = 1; - this.queue = []; }; ProductionQueue.prototype.Serialize = function() { const queue = []; for (const item of this.queue) queue.push(item.Serialize()); return { "autoqueuing": this.autoqueuing, "nextID": this.nextID, "paused": this.paused, "timer": this.timer, "queue": queue }; }; ProductionQueue.prototype.Deserialize = function(data) { this.Init(); this.autoqueuing = data.autoqueuing; this.nextID = data.nextID; this.paused = data.paused; this.timer = data.timer; for (const item of data.queue) { const newItem = new this.Item(); newItem.Deserialize(item); this.queue.push(newItem); } }; /** * @return {boolean} - Whether we are automatically queuing items. */ ProductionQueue.prototype.IsAutoQueueing = function() { return !!this.autoqueuing; }; /** * Turn on Auto-Queue. */ ProductionQueue.prototype.EnableAutoQueue = function() { this.autoqueuing = true; }; /** * Turn off Auto-Queue. */ ProductionQueue.prototype.DisableAutoQueue = function() { delete this.autoqueuing; }; /* * Adds a new batch of identical units to train or a technology to research to the production queue. * @param {string} templateName - The template to start production on. * @param {string} type - The type of production (i.e. "unit" or "technology"). * @param {number} count - The amount of units to be produced. Ignored for a tech. * @param {any} metadata - Optionally any metadata to be attached to the item. * @param {boolean} pushFront - Whether to push the item to the front of the queue and pause any item(s) currently in progress. * * @return {boolean} - Whether the addition of the item has succeeded. */ ProductionQueue.prototype.AddItem = function(templateName, type, count, metadata, pushFront = false) { // TODO: there should be a way for the GUI to determine whether it's going // to be possible to add a batch (based on resource costs and length limits). if (!this.queue.length) { const cmpPlayer = QueryOwnerInterface(this.entity); if (!cmpPlayer) return false; const player = cmpPlayer.GetPlayerID(); const cmpUpgrade = Engine.QueryInterface(this.entity, IID_Upgrade); if (cmpUpgrade && cmpUpgrade.IsUpgrading()) { let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); cmpGUIInterface.PushNotification({ "players": [player], "message": markForTranslation("Entity is being upgraded. Cannot start production."), "translateMessage": true }); return false; } } else if (this.queue.length >= this.MaxQueueSize) { const cmpPlayer = QueryOwnerInterface(this.entity); if (!cmpPlayer) return false; const player = cmpPlayer.GetPlayerID(); const cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); cmpGUIInterface.PushNotification({ "players": [player], "message": markForTranslation("The production queue is full."), "translateMessage": true, }); return false; } const item = new this.Item(); item.Init(this.entity, metadata); - if (type == "unit") - { - if (!item.QueueEntity(templateName, count)) - return false; - } - else if (type == "technology") - { - if (!item.QueueTechnology(templateName)) - return false; - } - else - { - warn("Tried to add invalid item of type \"" + type + "\" and template \"" + templateName + "\" to a production queue"); + if (!item.Queue(type, templateName, count)) return false; - } item.SetID(this.nextID++); if (pushFront) { this.queue[0]?.Pause(); this.queue.unshift(item); } else this.queue.push(item); Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); if (!this.timer) this.StartTimer(); return true; }; /* - * Removes an item from the queue. + * @param {number} - The ID of the item to remove from the queue. */ ProductionQueue.prototype.RemoveItem = function(id) { let itemIndex = this.queue.findIndex(item => item.id == id); if (itemIndex == -1) return; this.queue.splice(itemIndex, 1)[0].Stop(); Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); if (!this.queue.length) this.StopTimer(); }; ProductionQueue.prototype.SetAnimation = function(name) { let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); if (cmpVisual) cmpVisual.SelectAnimation(name, false, 1); }; /* * Returns basic data from all batches in the production queue. */ ProductionQueue.prototype.GetQueue = function() { return this.queue.map(item => item.GetBasicInfo()); }; /* * Removes all existing batches from the queue. */ ProductionQueue.prototype.ResetQueue = function() { while (this.queue.length) this.RemoveItem(this.queue[0].id); this.DisableAutoQueue(); }; /* * Increments progress on the first item in the production queue. * @param {Object} data - Unused in this case. * @param {number} lateness - The time passed since the expected time to fire the function. */ ProductionQueue.prototype.ProgressTimeout = function(data, lateness) { if (this.paused) return; // Allocate available time to as many queue items as it takes // until we've used up all the time (so that we work accurately // with items that take fractions of a second). let time = this.ProgressInterval + lateness; while (this.queue.length) { let item = this.queue[0]; if (item.IsPaused()) item.Unpause(); if (!item.IsStarted()) { if (item.entity) this.SetAnimation("training"); if (item.technology) this.SetAnimation("researching"); item.Start(); } time -= item.Progress(time); if (!item.IsFinished()) { Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); return; } this.queue.shift(); Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); // If autoqueuing, push a new unit on the queue immediately, // but don't start right away. This 'wastes' some time, making // autoqueue slightly worse than regular queuing, and also ensures // that autoqueue doesn't train more than one item per turn, // if the units would take fewer than ProgressInterval ms to train. if (this.autoqueuing) { const autoqueueData = item.OriginalItem(); if (!autoqueueData) continue; if (!this.AddItem(autoqueueData.templateName, "unit", autoqueueData.count, autoqueueData.metadata)) { this.DisableAutoQueue(); const cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); cmpGUIInterface.PushNotification({ "players": [QueryOwnerInterface(this.entity).GetPlayerID()], "message": markForTranslation("Could not auto-queue unit, de-activating."), "translateMessage": true }); } break; } } if (!this.queue.length) this.StopTimer(); }; ProductionQueue.prototype.PauseProduction = function() { this.StopTimer(); this.paused = true; this.queue[0]?.Pause(); - this.StopTimer(); }; ProductionQueue.prototype.UnpauseProduction = function() { this.queue[0]?.Unpause(); delete this.paused; this.StartTimer(); }; ProductionQueue.prototype.StartTimer = function() { if (this.timer) return; this.timer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).SetInterval( this.entity, IID_ProductionQueue, "ProgressTimeout", this.ProgressInterval, this.ProgressInterval, null ); }; ProductionQueue.prototype.StopTimer = function() { if (!this.timer) return; this.SetAnimation("idle"); Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).CancelTimer(this.timer); delete this.timer; }; +/** + * @return {boolean} - Whether this entity is currently producing. + */ ProductionQueue.prototype.HasQueuedProduction = function() { return this.queue.length > 0; }; ProductionQueue.prototype.OnOwnershipChanged = function(msg) { // Reset the production queue whenever the owner changes. // (This should prevent players getting surprised when they capture // an enemy building, and then loads of the enemy's civ's soldiers get // created from it. Also it means we don't have to worry about // updating the reserved pop slots.) this.ResetQueue(); }; ProductionQueue.prototype.OnGarrisonedStateChanged = function(msg) { if (msg.holderID != INVALID_ENTITY) this.PauseProduction(); else this.UnpauseProduction(); }; Engine.RegisterComponentType(IID_ProductionQueue, "ProductionQueue", ProductionQueue); Index: ps/trunk/binaries/data/mods/public/simulation/components/Researcher.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Researcher.js (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/components/Researcher.js (revision 26015) @@ -1,447 +1,446 @@ function Researcher() {} Researcher.prototype.Schema = "Allows the entity to research technologies." + "" + "" + "0.5" + "0.1" + "0" + "2" + "" + "" + "" + "\n phase_town_{civ}\n phase_metropolis_ptol\n unlock_shared_los\n wonder_population_cap\n " + "" + "" + "" + "" + "" + "tokens" + "" + "" + "" + "" + "" + "" + Resources.BuildSchema("nonNegativeDecimal", ["time"]) + "" + ""; /** * This object represents a technology being researched. */ Researcher.prototype.Item = function() {}; /** * @param {string} templateName - The name of the template we ought to research. * @param {number} researcher - The entity ID of our researcher. * @param {string} metadata - Optionally any metadata to attach to us. */ Researcher.prototype.Item.prototype.Init = function(templateName, researcher, metadata) { this.templateName = templateName; this.researcher = researcher; this.metadata = metadata; }; /** * Prepare for the queue. * @param {Object} techCostMultiplier - The multipliers to use when calculating costs. * @return {boolean} - Whether the item was successfully initiated. */ Researcher.prototype.Item.prototype.Queue = function(techCostMultiplier) { const template = TechnologyTemplates.Get(this.templateName); if (!template) return false; this.resources = {}; if (template.cost) for (const res in template.cost) this.resources[res] = Math.floor((techCostMultiplier[res] === undefined ? 1 : techCostMultiplier[res]) * template.cost[res]); const cmpPlayer = QueryOwnerInterface(this.researcher); // TrySubtractResources should report error to player (they ran out of resources). if (!cmpPlayer?.TrySubtractResources(this.resources)) return false; this.player = cmpPlayer.GetPlayerID(); const time = (techCostMultiplier.time || 1) * (template.researchTime || 0) * 1000; this.timeRemaining = time; this.timeTotal = time; // Tell the technology manager that we have queued researching this // such that players can't research the same thing twice. const cmpTechnologyManager = QueryPlayerIDInterface(this.player, IID_TechnologyManager); cmpTechnologyManager.QueuedResearch(this.templateName, this.researcher); return true; }; Researcher.prototype.Item.prototype.Stop = function() { const cmpTechnologyManager = QueryPlayerIDInterface(this.player, IID_TechnologyManager); if (cmpTechnologyManager) cmpTechnologyManager.StoppedResearch(this.templateName, true); QueryPlayerIDInterface(this.player)?.RefundResources(this.resources); delete this.resources; }; /** * Called when the first work is performed. */ Researcher.prototype.Item.prototype.Start = function() { const cmpTechnologyManager = QueryPlayerIDInterface(this.player, IID_TechnologyManager); cmpTechnologyManager.StartedResearch(this.templateName, true); this.started = true; }; Researcher.prototype.Item.prototype.Finish = function() { const cmpTechnologyManager = QueryPlayerIDInterface(this.player, IID_TechnologyManager); cmpTechnologyManager.ResearchTechnology(this.templateName); const template = TechnologyTemplates.Get(this.templateName); if (template?.soundComplete) Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager)?.PlaySoundGroup(template.soundComplete, this.researcher); this.finished = true; }; /** * @param {number} allocatedTime - The time allocated to this item. * @return {number} - The time used for this item. */ Researcher.prototype.Item.prototype.Progress = function(allocatedTime) { if (!this.started) this.Start(); if (this.timeRemaining > allocatedTime) { this.timeRemaining -= allocatedTime; return allocatedTime; } this.Finish(); return this.timeRemaining; }; Researcher.prototype.Item.prototype.Pause = function() { this.paused = true; }; Researcher.prototype.Item.prototype.Unpause = function() { delete this.paused; }; /** * @return {Object} - Some basic information of this item. */ Researcher.prototype.Item.prototype.GetBasicInfo = function() { return { "technologyTemplate": this.templateName, "progress": 1 - (this.timeRemaining / this.timeTotal), "timeRemaining": this.timeRemaining, "paused": this.paused, "metadata": this.metadata }; }; Researcher.prototype.Item.prototype.Serialize = function(id) { return { "id": id, "metadata": this.metadata, "paused": this.paused, "player": this.player, "researcher": this.researcher, "resource": this.resources, "started": this.started, "templateName": this.templateName, "timeRemaining": this.timeRemaining, "timeTotal": this.timeTotal, }; }; Researcher.prototype.Item.prototype.Deserialize = function(data) { this.Init(data.templateName, data.researcher, data.metadata); this.paused = data.paused; this.player = data.player; this.researcher = data.researcher; this.resources = data.resources; this.started = data.started; this.timeRemaining = data.timeRemaining; this.timeTotal = data.timeTotal; }; Researcher.prototype.Init = function() { this.nextID = 1; this.queue = new Map(); }; Researcher.prototype.Serialize = function() { const queue = []; for (const [id, item] of this.queue) queue.push(item.Serialize(id)); return { "nextID": this.nextID, "queue": queue }; }; Researcher.prototype.Deserialize = function(data) { this.Init(); this.nextID = data.nextID; for (const item of data.queue) { const newItem = new this.Item(); newItem.Deserialize(item); this.queue.set(item.id, newItem); } }; /* * Returns list of technologies that can be researched by this entity. */ Researcher.prototype.GetTechnologiesList = function() { if (!this.template.Technologies) return []; - let string = this.template.Technologies._string; - string = ApplyValueModificationsToEntity("Researcher/Technologies/_string", string, this.entity); + const string = ApplyValueModificationsToEntity("Researcher/Technologies/_string", this.template.Technologies._string, this.entity); if (!string) return []; const cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); if (!cmpTechnologyManager) return []; const cmpPlayer = QueryOwnerInterface(this.entity); if (!cmpPlayer) return []; let techs = string.split(/\s+/); // Replace the civ specific technologies. for (let i = 0; i < techs.length; ++i) { const tech = techs[i]; if (tech.indexOf("{civ}") == -1) continue; const civTech = tech.replace("{civ}", cmpPlayer.GetCiv()); techs[i] = TechnologyTemplates.Has(civTech) ? civTech : tech.replace("{civ}", "generic"); } // Remove any technologies that can't be researched by this civ. techs = techs.filter(tech => cmpTechnologyManager.CheckTechnologyRequirements( DeriveTechnologyRequirements(TechnologyTemplates.Get(tech), cmpPlayer.GetCiv()), true)); const techList = []; const superseded = {}; const disabledTechnologies = cmpPlayer.GetDisabledTechnologies(); // Add any top level technologies to an array which corresponds to the displayed icons. // Also store what technology is superseded in the superseded object { "tech1":"techWhichSupercedesTech1", ... }. for (const tech of techs) { if (disabledTechnologies && disabledTechnologies[tech]) continue; const template = TechnologyTemplates.Get(tech); if (!template.supersedes || techs.indexOf(template.supersedes) === -1) techList.push(tech); else superseded[template.supersedes] = tech; } // Now make researched/in progress techs invisible. for (const i in techList) { let tech = techList[i]; while (this.IsTechnologyResearchedOrInProgress(tech)) tech = superseded[tech]; techList[i] = tech; } const ret = []; // This inserts the techs into the correct positions to line up the technology pairs. for (let i = 0; i < techList.length; ++i) { const tech = techList[i]; if (!tech) { ret[i] = undefined; continue; } const template = TechnologyTemplates.Get(tech); if (template.top) ret[i] = { "pair": true, "top": template.top, "bottom": template.bottom }; else ret[i] = tech; } return ret; }; /** * @return {Object} - The multipliers to change the costs of any research with. */ Researcher.prototype.GetTechCostMultiplier = function() { const techCostMultiplier = {}; for (const res in this.template.TechCostMultiplier) techCostMultiplier[res] = ApplyValueModificationsToEntity( "Researcher/TechCostMultiplier/" + res, +this.template.TechCostMultiplier[res], this.entity); return techCostMultiplier; }; /** * Checks whether we can research the given technology, minding paired techs. */ Researcher.prototype.IsTechnologyResearchedOrInProgress = function(tech) { if (!tech) return false; const cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); if (!cmpTechnologyManager) return false; const template = TechnologyTemplates.Get(tech); if (template.top) return cmpTechnologyManager.IsTechnologyResearched(template.top) || cmpTechnologyManager.IsInProgress(template.top) || cmpTechnologyManager.IsTechnologyResearched(template.bottom) || cmpTechnologyManager.IsInProgress(template.bottom); return cmpTechnologyManager.IsTechnologyResearched(tech) || cmpTechnologyManager.IsInProgress(tech); }; /** * @param {string} templateName - The technology to queue. * @param {string} metadata - Any metadata attached to the item. * @return {number} - The ID of the item. -1 if the item could not be researched. */ Researcher.prototype.QueueTechnology = function(templateName, metadata) { if (!this.GetTechnologiesList().some(tech => tech && (tech == templateName || tech.pair && (tech.top == templateName || tech.bottom == templateName)))) { error("This entity cannot research " + templateName + "."); return -1; } const item = new this.Item(); item.Init(templateName, this.entity, metadata); const techCostMultiplier = this.GetTechCostMultiplier(); if (!item.Queue(techCostMultiplier)) return -1; const id = this.nextID++; this.queue.set(id, item); return id; }; /** * @param {number} id - The id of the technology researched here we need to stop. */ Researcher.prototype.StopResearching = function(id) { this.queue.get(id).Stop(); this.queue.delete(id); }; /** * @param {number} id - The id of the technology. */ Researcher.prototype.PauseTechnology = function(id) { this.queue.get(id).Pause(); }; /** * @param {number} id - The id of the technology. */ Researcher.prototype.UnpauseTechnology = function(id) { this.queue.get(id).Unpause(); }; /** * @param {number} id - The ID of the item to check. * @return {boolean} - Whether we are currently training the item. */ Researcher.prototype.HasItem = function(id) { return this.queue.has(id); }; /** * @parameter {number} id - The id of the research. * @return {Object} - Some basic information about the research. */ Researcher.prototype.GetResearchingTechnology = function(id) { return this.queue.get(id).GetBasicInfo(); }; /** * @parameter {string} technologyName - The name of the research. * @return {Object} - Some basic information about the research. */ Researcher.prototype.GetResearchingTechnologyByName = function(technologyName) { let techID; for (const [id, value] of this.queue) if (value.templateName === technologyName) { techID = id; break; } if (!techID) return undefined; return this.GetResearchingTechnology(techID); }; /** * @param {number} id - The ID of the item we spent time on. * @param {number} allocatedTime - The time we spent on the given item. * @return {number} - The time we've actually used. */ Researcher.prototype.Progress = function(id, allocatedTime) { const item = this.queue.get(id); const usedTime = item.Progress(allocatedTime); if (item.finished) this.queue.delete(id); return usedTime; }; Engine.RegisterComponentType(IID_Researcher, "Researcher", Researcher); Index: ps/trunk/binaries/data/mods/public/simulation/components/Trainer.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Trainer.js (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/components/Trainer.js (revision 26015) @@ -1,727 +1,729 @@ function Trainer() {} Trainer.prototype.Schema = "Allows the entity to train new units." + "" + "0.7" + "" + "\n units/{civ}/support_female_citizen\n units/{native}/support_trader\n units/athen/infantry_spearman_b\n " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "tokens" + "" + "" + "" + ""; /** * This object represents a batch of entities being trained. */ Trainer.prototype.Item = function() {}; /** * @param {string} templateName - The name of the template we ought to train. * @param {number} count - The size of the batch to train. * @param {number} trainer - The entity ID of our trainer. * @param {string} metadata - Optionally any metadata to attach to us. */ Trainer.prototype.Item.prototype.Init = function(templateName, count, trainer, metadata) { this.count = count; this.templateName = templateName; this.trainer = trainer; this.metadata = metadata; }; /** * Prepare for the queue. * @param {Object} trainCostMultiplier - The multipliers to use when calculating costs. * @param {number} batchTimeMultiplier - The factor to use when training this batches. * * @return {boolean} - Whether the item was successfully initiated. */ Trainer.prototype.Item.prototype.Queue = function(trainCostMultiplier, batchTimeMultiplier) { if (!Number.isInteger(this.count) || this.count <= 0) { error("Invalid batch count " + this.count + "."); return false; } const cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); const template = cmpTemplateManager.GetTemplate(this.templateName); if (!template) return false; const cmpPlayer = QueryOwnerInterface(this.trainer); if (!cmpPlayer) return false; this.player = cmpPlayer.GetPlayerID(); this.resources = {}; const totalResources = {}; for (const res in template.Cost.Resources) { this.resources[res] = (trainCostMultiplier[res] === undefined ? 1 : trainCostMultiplier[res]) * ApplyValueModificationsToTemplate( "Cost/Resources/" + res, +template.Cost.Resources[res], this.player, template); totalResources[res] = Math.floor(this.count * this.resources[res]); } // TrySubtractResources should report error to player (they ran out of resources). if (!cmpPlayer.TrySubtractResources(totalResources)) return false; this.population = ApplyValueModificationsToTemplate("Cost/Population", +template.Cost.Population, this.player, template); if (template.TrainingRestrictions) { const unitCategory = template.TrainingRestrictions.Category; const cmpPlayerEntityLimits = QueryPlayerIDInterface(this.player, IID_EntityLimits); if (cmpPlayerEntityLimits) { if (!cmpPlayerEntityLimits.AllowedToTrain(unitCategory, this.count, this.templateName, template.TrainingRestrictions.MatchLimit)) // Already warned, return. { cmpPlayer.RefundResources(totalResources); return false; } // ToDo: Should warn here v and return? cmpPlayerEntityLimits.ChangeCount(unitCategory, this.count); if (template.TrainingRestrictions.MatchLimit) cmpPlayerEntityLimits.ChangeMatchCount(this.templateName, this.count); } } const buildTime = ApplyValueModificationsToTemplate("Cost/BuildTime", +template.Cost.BuildTime, this.player, template); const time = batchTimeMultiplier * (trainCostMultiplier.time || 1) * buildTime * 1000; this.timeRemaining = time; this.timeTotal = time; const cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); cmpTrigger.CallEvent("OnTrainingQueued", { "playerid": this.player, "unitTemplate": this.templateName, "count": this.count, "metadata": this.metadata, "trainerEntity": this.trainer }); return true; }; /** * Destroy cached entities, refund resources and free (population) limits. */ Trainer.prototype.Item.prototype.Stop = function() { // Destroy any cached entities (those which didn't spawn for some reason). if (this.entities?.length) { for (const ent of this.entities) Engine.DestroyEntity(ent); delete this.entities; } const cmpPlayer = QueryPlayerIDInterface(this.player); const cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); const template = cmpTemplateManager.GetTemplate(this.templateName); if (template.TrainingRestrictions) { const cmpPlayerEntityLimits = QueryPlayerIDInterface(this.player, IID_EntityLimits); if (cmpPlayerEntityLimits) cmpPlayerEntityLimits.ChangeCount(template.TrainingRestrictions.Category, -this.count); if (template.TrainingRestrictions.MatchLimit) cmpPlayerEntityLimits.ChangeMatchCount(this.templateName, -this.count); } const cmpStatisticsTracker = QueryPlayerIDInterface(this.player, IID_StatisticsTracker); const totalCosts = {}; for (const resource in this.resources) { totalCosts[resource] = Math.floor(this.count * this.resources[resource]); if (cmpStatisticsTracker) cmpStatisticsTracker.IncreaseResourceUsedCounter(resource, -totalCosts[resource]); } if (cmpPlayer) { if (this.started) cmpPlayer.UnReservePopulationSlots(this.population * this.count); cmpPlayer.RefundResources(totalCosts); cmpPlayer.UnBlockTraining(); } delete this.resources; }; /** * This starts the item, reserving population. * @return {boolean} - Whether the item was started successfully. */ Trainer.prototype.Item.prototype.Start = function() { const cmpPlayer = QueryPlayerIDInterface(this.player); if (!cmpPlayer) return false; const template = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).GetTemplate(this.templateName); this.population = ApplyValueModificationsToTemplate( "Cost/Population", +template.Cost.Population, this.player, template); this.missingPopSpace = cmpPlayer.TryReservePopulationSlots(this.population * this.count); if (this.missingPopSpace) { cmpPlayer.BlockTraining(); return false; } cmpPlayer.UnBlockTraining(); Engine.PostMessage(this.trainer, MT_TrainingStarted, { "entity": this.trainer }); this.started = true; return true; }; Trainer.prototype.Item.prototype.Finish = function() { this.Spawn(); if (!this.count) this.finished = true; }; /* * This function creates the entities and places them in world if possible * (some of these entities may be garrisoned directly if autogarrison, the others are spawned). */ Trainer.prototype.Item.prototype.Spawn = function() { const createdEnts = []; const spawnedEnts = []; // We need entities to test spawning, but we don't want to waste resources, // so only create them once and use as needed. if (!this.entities) { this.entities = []; for (let i = 0; i < this.count; ++i) this.entities.push(Engine.AddEntity(this.templateName)); } let autoGarrison; const cmpRallyPoint = Engine.QueryInterface(this.trainer, IID_RallyPoint); if (cmpRallyPoint) { const data = cmpRallyPoint.GetData()[0]; if (data?.target && data.target == this.trainer && data.command == "garrison") autoGarrison = true; } const cmpFootprint = Engine.QueryInterface(this.trainer, IID_Footprint); const cmpPosition = Engine.QueryInterface(this.trainer, IID_Position); const positionTrainer = cmpPosition && cmpPosition.GetPosition(); const cmpPlayerEntityLimits = QueryPlayerIDInterface(this.player, IID_EntityLimits); const cmpPlayerStatisticsTracker = QueryPlayerIDInterface(this.player, IID_StatisticsTracker); while (this.entities.length) { const ent = this.entities[0]; const cmpNewOwnership = Engine.QueryInterface(ent, IID_Ownership); let garrisoned = false; if (autoGarrison) { const cmpGarrisonable = Engine.QueryInterface(ent, IID_Garrisonable); if (cmpGarrisonable) { // Temporary owner affectation needed for GarrisonHolder checks. cmpNewOwnership.SetOwnerQuiet(this.player); garrisoned = cmpGarrisonable.Garrison(this.trainer); cmpNewOwnership.SetOwnerQuiet(INVALID_PLAYER); } } if (!garrisoned) { const pos = cmpFootprint.PickSpawnPoint(ent); if (pos.y < 0) break; const cmpNewPosition = Engine.QueryInterface(ent, IID_Position); cmpNewPosition.JumpTo(pos.x, pos.z); if (positionTrainer) cmpNewPosition.SetYRotation(positionTrainer.horizAngleTo(pos)); spawnedEnts.push(ent); } // Decrement entity count in the EntityLimits component // since it will be increased by EntityLimits.OnGlobalOwnershipChanged, // i.e. we replace a 'trained' entity by 'alive' one. // Must be done after spawn check so EntityLimits decrements only if unit spawns. if (cmpPlayerEntityLimits) { const cmpTrainingRestrictions = Engine.QueryInterface(ent, IID_TrainingRestrictions); if (cmpTrainingRestrictions) cmpPlayerEntityLimits.ChangeCount(cmpTrainingRestrictions.GetCategory(), -1); } cmpNewOwnership.SetOwner(this.player); if (cmpPlayerStatisticsTracker) cmpPlayerStatisticsTracker.IncreaseTrainedUnitsCounter(ent); this.count--; this.entities.shift(); createdEnts.push(ent); } if (spawnedEnts.length && !autoGarrison && cmpRallyPoint) for (const com of GetRallyPointCommands(cmpRallyPoint, spawnedEnts)) ProcessCommand(this.player, com); const cmpPlayer = QueryOwnerInterface(this.trainer); if (createdEnts.length) { if (this.population) cmpPlayer.UnReservePopulationSlots(this.population * createdEnts.length); // Play a sound, but only for the first in the batch (to avoid nasty phasing effects). PlaySound("trained", createdEnts[0]); Engine.PostMessage(this.trainer, MT_TrainingFinished, { "entities": createdEnts, "owner": this.player, "metadata": this.metadata }); } if (this.count) { cmpPlayer.BlockTraining(); if (!this.spawnNotified) { Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification({ "players": [cmpPlayer.GetPlayerID()], "message": markForTranslation("Can't find free space to spawn trained units."), "translateMessage": true }); this.spawnNotified = true; } } else { cmpPlayer.UnBlockTraining(); delete this.spawnNotified; } }; /** * @param {number} allocatedTime - The time allocated to this item. * @return {number} - The time used for this item. */ Trainer.prototype.Item.prototype.Progress = function(allocatedTime) { // We couldn't start this timeout, try again later. if (!this.started && !this.Start()) return allocatedTime; if (this.timeRemaining > allocatedTime) { this.timeRemaining -= allocatedTime; return allocatedTime; } this.Finish(); return this.timeRemaining; }; Trainer.prototype.Item.prototype.Pause = function() { this.paused = true; }; Trainer.prototype.Item.prototype.Unpause = function() { delete this.paused; }; /** * @return {Object} - Some basic information of this batch. */ Trainer.prototype.Item.prototype.GetBasicInfo = function() { return { "unitTemplate": this.templateName, "count": this.count, "neededSlots": this.missingPopSpace, "progress": 1 - (this.timeRemaining / this.timeTotal), "timeRemaining": this.timeRemaining, "paused": this.paused, "metadata": this.metadata }; }; Trainer.prototype.Item.prototype.Serialize = function(id) { return { "id": id, "count": this.count, "entities": this.entities, "metadata": this.metadata, "missingPopSpace": this.missingPopSpace, "paused": this.paused, "player": this.player, + "population": this.population, "trainer": this.trainer, "resource": this.resources, "started": this.started, "templateName": this.templateName, "timeRemaining": this.timeRemaining, "timeTotal": this.timeTotal, }; }; Trainer.prototype.Item.prototype.Deserialize = function(data) { this.Init(data.templateName, data.count, data.trainer, data.metadata); this.entities = data.entities; this.missingPopSpace = data.missingPopSpace; this.paused = data.paused; this.player = data.player; + this.population = data.population; this.trainer = data.trainer; this.resources = data.resources; this.started = data.started; this.timeRemaining = data.timeRemaining; this.timeTotal = data.timeTotal; }; Trainer.prototype.Init = function() { this.nextID = 1; this.queue = new Map(); }; Trainer.prototype.Serialize = function() { const queue = []; for (const [id, item] of this.queue) queue.push(item.Serialize(id)); return { "entitiesMap": this.entitiesMap, "nextID": this.nextID, "queue": queue }; }; Trainer.prototype.Deserialize = function(data) { this.Init(); this.entitiesMap = data.entitiesMap; this.nextID = data.nextID; for (const item of data.queue) { const newItem = new this.Item(); newItem.Deserialize(item); this.queue.set(item.id, newItem); } }; /* * Returns list of entities that can be trained by this entity. */ Trainer.prototype.GetEntitiesList = function() { return Array.from(this.entitiesMap.values()); }; /** * Calculate the new list of producible entities * and update any entities currently being produced. */ Trainer.prototype.CalculateEntitiesMap = function() { // Don't reset the map, it's used below to update entities. if (!this.entitiesMap) this.entitiesMap = new Map(); if (!this.template.Entities) return; const string = this.template.Entities._string; // Tokens can be added -> process an empty list to get them. let addedTokens = ApplyValueModificationsToEntity("Trainer/Entities/_string", "", this.entity); if (!addedTokens && !string) return; addedTokens = addedTokens == "" ? [] : addedTokens.split(/\s+/); const cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); const cmpPlayer = QueryOwnerInterface(this.entity); const disabledEntities = cmpPlayer ? cmpPlayer.GetDisabledTemplates() : {}; /** * Process tokens: * - process token modifiers (this is a bit tricky). * - replace the "{civ}" and "{native}" codes with the owner's civ ID and entity's civ ID * - remove disabled entities * - upgrade templates where necessary * This also updates currently queued production (it's more convenient to do it here). */ const removeAllQueuedTemplate = (token) => { const queue = clone(this.queue); const template = this.entitiesMap.get(token); for (const [id, item] of queue) if (item.templateName == template) this.StopBatch(id); }; // ToDo: Notice this doesn't account for entity limits changing due to the template change. const updateAllQueuedTemplate = (token, updateTo) => { const template = this.entitiesMap.get(token); for (const [id, item] of this.queue) if (item.templateName === template) item.templateName = updateTo; }; const toks = string.split(/\s+/); for (const tok of addedTokens) toks.push(tok); const nativeCiv = Engine.QueryInterface(this.entity, IID_Identity)?.GetCiv(); const playerCiv = cmpPlayer?.GetCiv(); const addedDict = addedTokens.reduce((out, token) => { out[token] = true; return out; }, {}); this.entitiesMap = toks.reduce((entMap, token) => { const rawToken = token; if (!(token in addedDict)) { // This is a bit wasteful but I can't think of a simpler/better way. // The list of token is unlikely to be a performance bottleneck anyways. token = ApplyValueModificationsToEntity("Trainer/Entities/_string", token, this.entity); token = token.split(/\s+/); if (token.every(tok => addedTokens.indexOf(tok) !== -1)) { removeAllQueuedTemplate(rawToken); return entMap; } token = token[0]; } // Replace the "{civ}" and "{native}" codes with the owner's civ ID and entity's civ ID. if (nativeCiv) token = token.replace(/\{native\}/g, nativeCiv); if (playerCiv) token = token.replace(/\{civ\}/g, playerCiv); // Filter out disabled and invalid entities. if (disabledEntities[token] || !cmpTemplateManager.TemplateExists(token)) { removeAllQueuedTemplate(rawToken); return entMap; } token = this.GetUpgradedTemplate(token); entMap.set(rawToken, token); updateAllQueuedTemplate(rawToken, token); return entMap; }, new Map()); }; /* * Returns the upgraded template name if necessary. */ Trainer.prototype.GetUpgradedTemplate = function(templateName) { const cmpPlayer = QueryOwnerInterface(this.entity); if (!cmpPlayer) return templateName; const cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); let template = cmpTemplateManager.GetTemplate(templateName); while (template && template.Promotion !== undefined) { const requiredXp = ApplyValueModificationsToTemplate( "Promotion/RequiredXp", +template.Promotion.RequiredXp, cmpPlayer.GetPlayerID(), template); if (requiredXp > 0) break; templateName = template.Promotion.Entity; template = cmpTemplateManager.GetTemplate(templateName); } return templateName; }; /** * @return {Object} - The multipliers to change the costs of any training activity with. */ Trainer.prototype.GetTrainCostMultiplier = function() { const trainCostMultiplier = {}; for (const res in this.template.TrainCostMultiplier) trainCostMultiplier[res] = ApplyValueModificationsToEntity( "Trainer/TrainCostMultiplier/" + res, +this.template.TrainCostMultiplier[res], this.entity); return trainCostMultiplier; }; /* * Returns batch build time. */ Trainer.prototype.GetBatchTime = function(batchSize) { // TODO: work out what equation we should use here. return Math.pow(batchSize, ApplyValueModificationsToEntity( "Trainer/BatchTimeModifier", +(this.template.BatchTimeModifier || 1), this.entity)); }; /** * @param {string} templateName - The template name to check. * @return {boolean} - Whether we can train this template. */ Trainer.prototype.CanTrain = function(templateName) { return this.GetEntitiesList().includes(templateName); }; /** * @param {string} templateName - The entity to queue. * @param {number} count - The batch size. * @param {string} metadata - Any metadata attached to the item. * * @return {number} - The ID of the item. -1 if the item could not be queued. */ Trainer.prototype.QueueBatch = function(templateName, count, metadata) { const item = new this.Item(); item.Init(templateName, count, this.entity, metadata); const trainCostMultiplier = this.GetTrainCostMultiplier(); const batchTimeMultiplier = this.GetBatchTime(count); if (!item.Queue(trainCostMultiplier, batchTimeMultiplier)) return -1; const id = this.nextID++; this.queue.set(id, item); return id; }; /** * @param {number} id - The ID of the batch being trained here we need to stop. */ Trainer.prototype.StopBatch = function(id) { this.queue.get(id).Stop(); this.queue.delete(id); }; /** * @param {number} id - The ID of the training. */ Trainer.prototype.PauseBatch = function(id) { this.queue.get(id).Pause(); }; /** * @param {number} id - The ID of the training. */ Trainer.prototype.UnpauseBatch = function(id) { this.queue.get(id).Unpause(); }; /** * @param {number} id - The ID of the batch to check. * @return {boolean} - Whether we are currently training the batch. */ Trainer.prototype.HasBatch = function(id) { return this.queue.has(id); }; /** * @parameter {number} id - The id of the training. * @return {Object} - Some basic information about the training. */ Trainer.prototype.GetBatch = function(id) { const item = this.queue.get(id); return item?.GetBasicInfo(); }; /** * @param {number} id - The ID of the item we spent time on. * @param {number} allocatedTime - The time we spent on the given item. * @return {number} - The time we've actually used. */ Trainer.prototype.Progress = function(id, allocatedTime) { const item = this.queue.get(id); const usedTime = item.Progress(allocatedTime); if (item.finished) this.queue.delete(id); return usedTime; }; Trainer.prototype.OnCivChanged = function() { this.CalculateEntitiesMap(); }; Trainer.prototype.OnOwnershipChanged = function(msg) { if (msg.to != INVALID_PLAYER) this.CalculateEntitiesMap(); }; Trainer.prototype.OnValueModification = function(msg) { // If the promotion requirements of units is changed, // update the entities list so that automatically promoted units are shown // appropriately in the list. if (msg.component != "Promotion" && (msg.component != "Trainer" || !msg.valueNames.some(val => val.startsWith("Trainer/Entities/")))) return; if (msg.entities.indexOf(this.entity) === -1) return; // This also updates the queued production if necessary. this.CalculateEntitiesMap(); // Inform the GUI that it'll need to recompute the selection panel. // TODO: it would be better to only send the message if something actually changing // for the current training queue. const cmpPlayer = QueryOwnerInterface(this.entity); if (cmpPlayer) Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).SetSelectionDirty(cmpPlayer.GetPlayerID()); }; Trainer.prototype.OnDisabledTemplatesChanged = function(msg) { this.CalculateEntitiesMap(); }; Engine.RegisterComponentType(IID_Trainer, "Trainer", Trainer); Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen/gymnasium.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen/gymnasium.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen/gymnasium.xml (revision 26015) @@ -1,56 +1,57 @@ 200 200 200 8.0 10 2000 athen Gymnasium Gymnasion Train Champions. ConquestCritical CivSpecific Gymnasium structures/gymnasium.png 40 40 + interface/complete/building/complete_gymnasium.xml 0.7 units/{civ}/champion_infantry units/{civ}/champion_ranged 40 structures/athenians/gymnasium.xml structures/fndn_8x9.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen/prytaneion.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen/prytaneion.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen/prytaneion.xml (revision 26015) @@ -1,67 +1,68 @@ Council 200 100 200 8.0 2000 athen Council Chamber Prytaneion Train Heroes and research technologies. ConquestCritical CivSpecific Council structures/tholos.png 20 40 + long_walls iphicratean_reforms interface/complete/building/complete_tholos.xml false 38 40000 0.7 units/{civ}/hero_themistocles units/{civ}/hero_pericles units/{civ}/hero_iphicrates 40 structures/athenians/prytaneion.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/gaul/assembly.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/structures/gaul/assembly.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/gaul/assembly.xml (revision 26015) @@ -1,77 +1,78 @@ 200 400 10.0 20 0.1 Unit Support Infantry Cavalry 0 2 2000 decay|rubble/rubble_stone_6x6 gaul Assembly of Princes Remogantion Train Champion Trumpeters and Heroes. ConquestCritical CivSpecific City Council structures/tholos.png phase_city 80 + 20 30 3 interface/complete/building/complete_iber_monument.xml false 40 40000 0.7 units/{civ}/champion_infantry_trumpeter units/{civ}/hero_brennus units/{civ}/hero_viridomarus units/{civ}/hero_vercingetorix 40 structures/gauls/theater.xml structures/fndn_6x6.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/pers/apadana.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/structures/pers/apadana.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/pers/apadana.xml (revision 26015) @@ -1,81 +1,82 @@ Palace 300 300 200 8.0 10 3000 decay|rubble/rubble_stone_6x6 pers Throne Hall Apadāna Train Champions and Heroes. ConquestCritical CivSpecific Palace structures/palace.png 60 40 + immortals 1.0 1.0 0.75 0.75 2000 interface/complete/building/complete_broch.xml true 48 40000 0.8 units/{civ}/champion_infantry units/{civ}/hero_cyrus_ii units/{civ}/hero_darius_i units/{civ}/hero_xerxes_i 40 structures/persians/apadana.xml structures/fndn_8x9.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome/army_camp.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome/army_camp.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome/army_camp.xml (revision 26015) @@ -1,127 +1,128 @@ Bow 10 60 1200 2000 100 1.5 50 false Human outline_border.png outline_border_mask.png 0.175 neutral enemy ArmyCamp ArmyCamp 80 3 15 1 Soldier 3 10.0 3.0 250 500 100 100 12.0 20 0.1 Unit Support Infantry Cavalry Siege 0 6 2250 decay|rubble/rubble_rome_sb rome Army Camp Castra Build in neutral or enemy territory. Train Advanced Melee Infantry. Construct Rams. Garrison Soldiers for additional arrows. ConquestCritical CivSpecific City ArmyCamp structures/roman_camp.png phase_city 100 + 15 35 3 interface/complete/building/complete_broch.xml attack/weapon/bow_attack.xml attack/impact/arrow_impact.xml 2 0.7 units/{civ}/infantry_axeman_a units/{civ}/infantry_swordsman_a units/{civ}/infantry_spearman_a units/{civ}/infantry_pikeman_a units/{civ}/siege_ram 90 structures/romans/camp.xml structures/fndn_8x8.xml 29.5 8 Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart/gerousia.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart/gerousia.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart/gerousia.xml (revision 26015) @@ -1,59 +1,60 @@ Council 200 100 200 8.0 2000 spart Spartan Senate Gerousia Train Heroes. ConquestCritical Council structures/tholos.png 20 40 + interface/complete/building/complete_tholos.xml false 38 40000 0.7 units/{civ}/hero_leonidas 40 structures/spartans/gerousia.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart/syssiton.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart/syssiton.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart/syssiton.xml (revision 26015) @@ -1,69 +1,70 @@ 200 200 200 8.0 10 2000 decay|rubble/rubble_stone_4x6 spart Military Mess Hall Syssition Train Champions and Heroes. ConquestCritical CivSpecific Syssiton structures/syssition.png 40 40 + agoge interface/complete/building/complete_gymnasium.xml false 38 40000 0.7 units/{civ}/champion_infantry_spear units/{civ}/hero_leonidas units/{civ}/hero_brasidas units/{civ}/hero_agis 40 structures/spartans/syssiton.xml structures/fndn_5x8.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure.xml (revision 26015) @@ -1,185 +1,172 @@ land own Structure 500 0.5 5.0 0 10 0 0 0 0 false false 0.0 3.0 9.8 0.85 0.65 0.35 corpse 0 0 true gaia Structure Structure false 0 0 0 0 0 structure true true true true false false false false 0 upright false 0 6 - special/rallypoint art/textures/misc/rallypoint_line.png art/textures/misc/rallypoint_line_mask.png 0.25 square round default 2.0 - - - 1.0 - 1.0 - 1.0 - 1.0 - - - 0.0 1 1 10 1 0.0 1 1 10 1 outline_border.png outline_border_mask.png 0.4 interface/complete/building/complete_universal.xml attack/destruction/building_collapse_large.xml interface/alarm/alarm_attackplayer.xml interface/alarm/alarm_attacked_gaia.xml interface/alarm/alarm_attackplayer.xml interface/alarm/alarm_attacked_gaia.xml 6.0 0.6 12.0 20 neutral enemy - - 1.0 - true false false false 4 false false true false Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml (revision 26015) @@ -1,145 +1,146 @@ FemaleCitizen 120 180 100 Bow 10 60 1200 2000 100 1.5 50 false Human outline_border.png outline_border_mask.png 0.175 own neutral CivilCentre CivilCentre 200 3 1 Soldier 2500 5.0 500 500 500 500 8.0 20 0.1 Unit Support Infantry Cavalry 1 1 3000 decay|rubble/rubble_stone_6x6 Civic Center template_structure_civic_civil_centre Build in own or neutral territory. Acquire large tracts of territory. Territory root. Train Citizens and research technologies. Garrison Soldiers for additional arrows. CivCentre Defensive CivilCentre structures/civic_centre.png 100 100 100 20 + phase_town_{civ} phase_city_{civ} unlock_shared_los unlock_shared_dropsites unlock_spies spy_counter archery_tradition hoplite_tradition hellenistic_metropolis 5 5 food wood stone metal true interface/complete/building/complete_civ_center.xml interface/alarm/alarm_alert_0.xml interface/alarm/alarm_alert_1.xml attack/weapon/bow_attack.xml attack/impact/arrow_impact.xml true 140 10000 0.8 units/{civ}/support_female_citizen 90 structures/fndn_8x8.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml (revision 26015) @@ -1,74 +1,75 @@ 30 75 5.0 3 0 0.1 Unit Support+!Elephant 1 800 decay|rubble/rubble_stone_2x2 House template_structure_civic_house Village House structures/house.png phase_village 15 5 + health_females_01 pop_house_01 pop_house_02 unlock_females_house interface/complete/building/complete_house.xml 8.0 false 16 65535 units/{civ}/support_female_citizen_house 20 structures/fndn_3x3.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml (revision 26015) @@ -1,75 +1,76 @@ structures/temple_heal 200 300 12.0 20 0.1 Unit Support Infantry Cavalry 3 2 2000 decay|rubble/rubble_stone_4x6 Temple template_structure_civic_temple Train Healers and research healing technologies. Town Temple structures/temple.png phase_town 60 + heal_range heal_range_2 heal_rate heal_rate_2 garrison_heal health_regen_units interface/complete/building/complete_temple.xml false 40 30000 0.8 units/{civ}/support_healer_b 40 structures/fndn_4x6.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_outpost.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_outpost.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_outpost.xml (revision 26015) @@ -1,81 +1,82 @@ structures/wall_garrisoned own neutral Outpost 50 30 60 13.0 400 decay|rubble/rubble_stone_2x2 Outpost template_structure_defensive_outpost Build in own or neutral territory. Outpost structures/outpost.png 12 + outpost_vision 10 20 1 interface/complete/building/complete_tower.xml 14.0 enemy 0 8 0 90 structures/fndn_2x2.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_palisade.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_palisade.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_palisade.xml (revision 26015) @@ -1,38 +1,35 @@ land-shore own neutral Wall 1000 decay|rubble/rubble_stone_2x2 Palisade template_structure_defensive_palisade Wall off an area. Build in own or neutral territory. Palisade gaia/special_palisade.png - - 4 25 2 interface/complete/building/complete_wall.xml - Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_artillery.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_artillery.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_artillery.xml (revision 26015) @@ -1,79 +1,80 @@ Stone 90 0 80 40 15 4500 5000 40 6 9.81 false props/units/weapons/tower_artillery_projectile.xml props/units/weapons/tower_artillery_projectile_impact.xml 0.3 -Human !Organic 1 0 200 200 200 15.0 5 1400 Artillery Tower template_structure_defensive_tower_artillery ArtilleryTower structures/tower_artillery.png phase_city 40 40 + tower_health attack/impact/siegeprojectilehit.xml attack/siege/ballist_attack.xml false 32 30000 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_bolt.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_bolt.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_bolt.xml (revision 26015) @@ -1,76 +1,77 @@ Bolt 100 90 30 15 500 4000 150 1 9.81 false props/units/weapons/tower_artillery_projectile_impact.xml 0.1 1 0 200 200 100 15.0 5 1400 Bolt Tower template_structure_defensive_tower_bolt BoltTower structures/tower_bolt.png phase_city 40 20 + tower_health attack/weapon/arrowfly.xml attack/impact/arrow_metal.xml false 32 30000 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_sentry.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_sentry.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_sentry.xml (revision 26015) @@ -1,64 +1,65 @@ 9 3 40 100 9.0 3 400 Sentry Tower template_structure_defensive_tower_sentry Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot. SentryTower structures/sentry_tower.png phase_village 20 + tower_watch false 16 30000 structures/{civ}/defense_tower Reinforce with stone and upgrade to a defense tower. phase_town 50 100 upgrading Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_stone.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_stone.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_tower_stone.xml (revision 26015) @@ -1,54 +1,55 @@ 15 150 100 100 15.0 5 1000 Stone Tower template_structure_defensive_tower_stone Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot. StoneTower structures/defense_tower.png phase_town 20 20 + tower_watch tower_crenellations tower_range tower_murderholes tower_health false 32 30000 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_wall.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_wall.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defensive_wall.xml (revision 26015) @@ -1,39 +1,36 @@ land-shore Wall 8.0 Wall template_structure_defensive_wall Wall off your town for a stout defense. Wall structures/wall.png phase_town - 4.5 - interface/complete/building/complete_wall.xml false 20 65535 - Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml (revision 26015) @@ -1,70 +1,71 @@ FemaleCitizen 50 100 100 300 45 100 8.0 900 decay|rubble/rubble_stone_4x4 Farmstead template_structure_economic_farmstead Research food gathering technologies. DropsiteFood Village Farmstead structures/farmstead.png phase_village 20 + gather_wicker_baskets gather_farming_plows gather_farming_training gather_farming_fertilizer gather_farming_harvester food true interface/complete/building/complete_farmstead.xml interface/alarm/alarm_alert_0.xml interface/alarm/alarm_alert_1.xml false 20 30000 20 structures/fndn_5x5.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml (revision 26015) @@ -1,75 +1,76 @@ Trader+!Ship -1 -1 100 150 300 8.0 1500 decay|rubble/rubble_stone_5x5 Market template_structure_economic_market Barter resources. Establish trade routes. Train Traders and research trade and barter technologies. Barter Trade Town Market structures/market.png phase_town 60 land 0.2 + trader_health trade_gain_01 trade_gain_02 trade_commercial_treaty interface/complete/building/complete_market.xml interface/alarm/alarm_alert_0.xml interface/alarm/alarm_alert_1.xml false 40 30000 0.7 units/{civ}/support_trader 32 structures/fndn_8x8.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml (revision 26015) @@ -1,78 +1,79 @@ FemaleCitizen 50 100 100 40 100 8.0 800 decay|rubble/rubble_stone_3x3 Storehouse template_structure_economic_storehouse Research gathering technologies. DropsiteWood DropsiteMetal DropsiteStone Village Storehouse structures/storehouse.png phase_village 20 + gather_lumbering_ironaxes gather_lumbering_strongeraxes gather_lumbering_sharpaxes gather_mining_servants gather_mining_serfs gather_mining_slaves gather_mining_wedgemallet gather_mining_shaftmining gather_mining_silvermining gather_capacity_basket gather_capacity_wheelbarrow gather_capacity_carts wood stone metal true interface/complete/building/complete_storehouse.xml interface/alarm/alarm_alert_0.xml interface/alarm/alarm_alert_1.xml false 20 30000 20 structures/fndn_3x3.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_arsenal.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_arsenal.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_arsenal.xml (revision 26015) @@ -1,75 +1,76 @@ structures/arsenal_repair 180 300 12.0 2 Siege 2000 decay|rubble/rubble_stone_5x5 Arsenal template_structure_military_arsenal Train Champion Infantry Crossbowmen, construct Siege Engines, and research Siege Engine technologies. City Arsenal structures/siege_workshop.png phase_city 60 + siege_attack siege_cost_time siege_health siege_pack_unpack siege_bolt_accuracy interface/complete/building/complete_barracks.xml 38 0.7 units/{civ}/champion_infantry_crossbowman units/{civ}/siege_scorpio_packed units/{civ}/siege_polybolos_packed units/{civ}/siege_oxybeles_packed units/{civ}/siege_lithobolos_packed units/{civ}/siege_ballista_packed units/{civ}/siege_ram units/{civ}/siege_tower 40 structures/fndn_8x8.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml (revision 26015) @@ -1,86 +1,87 @@ own ally neutral shore Dock 150 200 8.0 2500 decay|rubble/rubble_stone_4x4 Dock template_structure_military_dock Build upon a shoreline in own, neutral, or allied territory. Establish trade routes. Construct Ships and research Ship technologies. Economic Naval Trade Village Dock structures/dock.png 40 land naval 0.2 true 0.0 + ship fishing_boat_gather_rate fishing_boat_gather_capacity ship_cost_time ship_health ship_movement_speed equine_transports food wood stone metal true interface/complete/building/complete_dock.xml 0.8 units/{civ}/ship_fishing units/{civ}/ship_merchant units/{civ}/ship_bireme units/{civ}/ship_trireme units/{civ}/ship_quinquereme units/{civ}/ship_fire 40 structures/fndn_4x4_dock.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_embassy.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_embassy.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_embassy.xml (revision 26015) @@ -1,55 +1,56 @@ Embassy 150 12.0 6 Support Infantry Cavalry 2000 decay|rubble/rubble_stone_3x3 Embassy template_structure_military_embassy Town Embassy phase_town + 30 interface/complete/building/complete_gymnasium.xml 25 0.8 24 structures/fndn_4x4.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml (revision 26015) @@ -1,108 +1,109 @@ Bow 10 60 1200 2000 100 1.5 50 false Human outline_border.png outline_border_mask.png 0.175 Fortress Fortress 80 4 1 Soldier 8 10.0 450 300 600 8.0 20 0.075 Support Infantry Cavalry Siege 6 5200 decay|rubble/rubble_stone_6x6 Fortress template_structure_military_fortress Garrison Soldiers for additional arrows. GarrisonFortress Defensive Fortress structures/fortress.png phase_city 60 120 + attack_soldiers_will interface/complete/building/complete_fortress.xml attack/weapon/bow_attack.xml attack/impact/arrow_impact.xml 2 80 0.8 90 structures/fndn_8x8.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_stable.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_stable.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_stable.xml (revision 26015) @@ -1,73 +1,74 @@ structures/xp_trickle 120 200 50 12.0 10 Cavalry 2000 decay|rubble/rubble_stone_5x5 Stable template_structure_military_stable Train Cavalry and research Cavalry technologies. Village Stable structures/stable.png phase_village 40 10 + stable_batch_training cavalry_cost_time cavalry_movement_speed cavalry_health nisean_horses unlock_champion_cavalry unlock_champion_chariots interface/complete/building/complete_stable.xml 0.8 units/{civ}/cavalry_axeman_b units/{civ}/cavalry_swordsman_b units/{civ}/cavalry_spearman_b units/{civ}/cavalry_javelineer_b units/{civ}/cavalry_archer_b units/{civ}/champion_cavalry units/{civ}/champion_chariot units/{civ}/war_dog 32 Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_amphitheater.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_amphitheater.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_amphitheater.xml (revision 26015) @@ -1,54 +1,55 @@ 500 500 500 20.0 2000 Amphitheater template_structure_special_amphitheater Amphitheater structures/theater.png 100 100 + interface/complete/building/complete_tholos.xml false 100 40000 0.8 units/{civ}/champion_infantry_spear_gladiator units/{civ}/champion_infantry_sword_gladiator 40 structures/fndn_6x6.xml Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_theater.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_theater.xml (revision 26014) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special_theater.xml (revision 26015) @@ -1,55 +1,52 @@ structures/theater Theater 500 200 600 200 10.0 3000 Theater template_structure_special_theater Theater structures/theater.png 40 120 40 - - interface/complete/building/complete_greek_theater.xml false 100 40000 - 40