Technologies. Refs #3. Full unlocking technology implementation. Only unit gathering rates can be modified currently because the patch was big enough already.
This was SVN commit r11584.
This commit is contained in:
parent
be0109331c
commit
755e407aeb
BIN
binaries/data/mods/public/art/textures/ui/session/portraits/technologies/city_phase.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/portraits/technologies/city_phase.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/portraits/technologies/plough.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/portraits/technologies/plough.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/portraits/technologies/town_phase.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/portraits/technologies/town_phase.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -764,7 +764,7 @@ function handleInputBeforeGui(ev, hoveredObject)
|
||||
case "hotkeyup":
|
||||
if (ev.hotkey == "session.batchtrain")
|
||||
{
|
||||
flushTrainingQueueBatch();
|
||||
flushTrainingBatch();
|
||||
inputState = INPUT_NORMAL;
|
||||
}
|
||||
break;
|
||||
@ -1202,13 +1202,13 @@ var batchTrainingType;
|
||||
var batchTrainingCount;
|
||||
const batchIncrementSize = 5;
|
||||
|
||||
function flushTrainingQueueBatch()
|
||||
function flushTrainingBatch()
|
||||
{
|
||||
Engine.PostNetworkCommand({"type": "train", "entity": batchTrainingEntity, "template": batchTrainingType, "count": batchTrainingCount});
|
||||
}
|
||||
|
||||
// Called by GUI when user clicks training button
|
||||
function addToTrainingQueue(entity, trainEntType)
|
||||
function addTrainingToQueue(entity, trainEntType)
|
||||
{
|
||||
if (Engine.HotkeyIsPressed("session.batchtrain"))
|
||||
{
|
||||
@ -1223,7 +1223,7 @@ function addToTrainingQueue(entity, trainEntType)
|
||||
// Otherwise start a new one
|
||||
else
|
||||
{
|
||||
flushTrainingQueueBatch();
|
||||
flushTrainingBatch();
|
||||
// fall through to create the new batch
|
||||
}
|
||||
}
|
||||
@ -1239,9 +1239,15 @@ function addToTrainingQueue(entity, trainEntType)
|
||||
}
|
||||
}
|
||||
|
||||
// Called by GUI when user clicks research button
|
||||
function addResearchToQueue(entity, researchType)
|
||||
{
|
||||
Engine.PostNetworkCommand({"type": "research", "entity": entity, "template": researchType});
|
||||
}
|
||||
|
||||
// Returns the number of units that will be present in a batch if the user clicks
|
||||
// the training button with shift down
|
||||
function getTrainingQueueBatchStatus(entity, trainEntType)
|
||||
function getTrainingBatchStatus(entity, trainEntType)
|
||||
{
|
||||
if (inputState == INPUT_BATCHTRAINING && batchTrainingEntity == entity && batchTrainingType == trainEntType)
|
||||
return [batchTrainingCount, batchIncrementSize];
|
||||
@ -1250,9 +1256,9 @@ function getTrainingQueueBatchStatus(entity, trainEntType)
|
||||
}
|
||||
|
||||
// Called by GUI when user clicks production queue item
|
||||
function removeFromTrainingQueue(entity, id)
|
||||
function removeFromProductionQueue(entity, id)
|
||||
{
|
||||
Engine.PostNetworkCommand({"type": "stop-train", "entity": entity, "id": id});
|
||||
Engine.PostNetworkCommand({"type": "stop-production", "entity": entity, "id": id});
|
||||
}
|
||||
|
||||
// Called by unit selection buttons
|
||||
|
@ -15,7 +15,7 @@ var g_DevSettings = {
|
||||
|
||||
// Indicate when one of the current player's training queues is blocked
|
||||
// (this is used to support population counter blinking)
|
||||
var g_IsTrainingQueueBlocked = false;
|
||||
var g_IsTrainingBlocked = false;
|
||||
|
||||
// Cache EntityStates
|
||||
var g_EntityStates = {}; // {id:entState}
|
||||
@ -53,6 +53,20 @@ function GetTemplateData(templateName)
|
||||
return g_TemplateData[templateName];
|
||||
}
|
||||
|
||||
// Cache TechnologyData
|
||||
var g_TechnologyData = {}; // {id:template}
|
||||
|
||||
function GetTechnologyData(technologyName)
|
||||
{
|
||||
if (!(technologyName in g_TechnologyData))
|
||||
{
|
||||
var template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName);
|
||||
g_TechnologyData[technologyName] = template;
|
||||
}
|
||||
|
||||
return g_TechnologyData[technologyName];
|
||||
}
|
||||
|
||||
// Init
|
||||
function init(initData, hotloadData)
|
||||
{
|
||||
@ -210,7 +224,7 @@ function onTick()
|
||||
global.music.updateTimer();
|
||||
|
||||
// When training is blocked, flash population (alternates colour every 500msec)
|
||||
if (g_IsTrainingQueueBlocked && (Date.now() % 1000) < 500)
|
||||
if (g_IsTrainingBlocked && (Date.now() % 1000) < 500)
|
||||
getGUIObjectByName("resourcePop").textcolor = POPULATION_ALERT_COLOR;
|
||||
else
|
||||
getGUIObjectByName("resourcePop").textcolor = DEFAULT_POPULATION_COLOR;
|
||||
@ -283,6 +297,7 @@ function onSimulationUpdate()
|
||||
g_Selection.dirty = false;
|
||||
g_EntityStates = {};
|
||||
g_TemplateData = {};
|
||||
g_TechnologyData = {};
|
||||
|
||||
var simState = Engine.GuiInterfaceCall("GetSimulationState");
|
||||
|
||||
@ -371,7 +386,7 @@ function updatePlayerDisplay(simState)
|
||||
getGUIObjectByName("resourceMetal").caption = playerState.resourceCounts.metal;
|
||||
getGUIObjectByName("resourcePop").caption = playerState.popCount + "/" + playerState.popLimit;
|
||||
|
||||
g_IsTrainingQueueBlocked = playerState.trainingQueueBlocked;
|
||||
g_IsTrainingBlocked = playerState.trainingBlocked;
|
||||
}
|
||||
|
||||
function updateTimeElapsedCounter(simState)
|
||||
|
@ -727,11 +727,15 @@
|
||||
</object>
|
||||
|
||||
<object name="unitResearchPanel"
|
||||
style="TranslucentPanelThinBorder"
|
||||
size="0 100%-56 100% 100%"
|
||||
type="text"
|
||||
size="14 100%-56 100% 100%"
|
||||
>
|
||||
|
||||
<object size="0 0 100% 100%">
|
||||
<repeat count="8">
|
||||
<object name="unitResearchButton[n]" hidden="true" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom">
|
||||
<object name="unitResearchIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
|
||||
</object>
|
||||
</repeat>
|
||||
</object>
|
||||
[research commands]
|
||||
</object>
|
||||
|
||||
|
@ -4,6 +4,7 @@ const QUEUE = "Queue";
|
||||
const GARRISON = "Garrison";
|
||||
const FORMATION = "Formation";
|
||||
const TRAINING = "Training";
|
||||
const RESEARCH = "Research";
|
||||
const CONSTRUCTION = "Construction";
|
||||
const COMMAND = "Command";
|
||||
const STANCE = "Stance";
|
||||
@ -23,7 +24,7 @@ const BARTER_RESOURCES = ["food", "wood", "stone", "metal"];
|
||||
const BARTER_ACTIONS = ["Sell", "Buy"];
|
||||
|
||||
// The number of currently visible buttons (used to optimise showing/hiding)
|
||||
var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Barter": 0, "Trading": 0, "Construction": 0, "Command": 0, "Stance": 0};
|
||||
var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Barter": 0, "Trading": 0, "Construction": 0, "Command": 0, "Stance": 0};
|
||||
|
||||
// Unit panels are panels with row(s) of buttons
|
||||
var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Barter", "Trading", "Construction", "Research", "Stance", "Command"];
|
||||
@ -164,6 +165,11 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
numberOfItems = 24;
|
||||
break;
|
||||
|
||||
case RESEARCH:
|
||||
if (numberOfItems > 8)
|
||||
numberOfItems = 8;
|
||||
break;
|
||||
|
||||
case CONSTRUCTION:
|
||||
if (numberOfItems > 24)
|
||||
numberOfItems = 24;
|
||||
@ -183,13 +189,54 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
for (i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var entType = ((guiName == "Queue")? item.template : item);
|
||||
var template;
|
||||
if (guiName != "Formation" && guiName != "Command" && guiName != "Stance")
|
||||
|
||||
// If a tech has been researched it leaves an empty slot
|
||||
if (guiName == RESEARCH && !item)
|
||||
{
|
||||
getGUIObjectByName("unit"+guiName+"Button["+i+"]").hidden = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the entity type and load the template for that type if necessary
|
||||
var entType;
|
||||
var template;
|
||||
switch (guiName)
|
||||
{
|
||||
case QUEUE:
|
||||
// The queue can hold both technologies and units so we need to use the correct code for
|
||||
// loading the templates
|
||||
if (item.unitTemplate)
|
||||
{
|
||||
entType = item.unitTemplate;
|
||||
template = GetTemplateData(entType);
|
||||
}
|
||||
else if (item.technologyTemplate)
|
||||
{
|
||||
entType = item.technologyTemplate;
|
||||
template = GetTechnologyData(entType);
|
||||
}
|
||||
|
||||
if (!template)
|
||||
continue; // ignore attempts to use invalid templates (an error should have been
|
||||
// reported already)
|
||||
break;
|
||||
case RESEARCH:
|
||||
entType = item;
|
||||
template = GetTechnologyData(entType);
|
||||
if (!template)
|
||||
continue; // ignore attempts to use invalid templates (an error should have been
|
||||
// reported already)
|
||||
break;
|
||||
case SELECTION:
|
||||
case GARRISON:
|
||||
case TRAINING:
|
||||
case CONSTRUCTION:
|
||||
entType = item;
|
||||
template = GetTemplateData(entType);
|
||||
if (!template)
|
||||
continue; // ignore attempts to use invalid templates (an error should have been reported already)
|
||||
continue; // ignore attempts to use invalid templates (an error should have been
|
||||
// reported already)
|
||||
break;
|
||||
}
|
||||
|
||||
switch (guiName)
|
||||
@ -233,7 +280,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
if (template.tooltip)
|
||||
tooltip += "\n[font=\"serif-13\"]" + template.tooltip + "[/font]";
|
||||
|
||||
var [batchSize, batchIncrement] = getTrainingQueueBatchStatus(unitEntState.id, entType);
|
||||
var [batchSize, batchIncrement] = getTrainingBatchStatus(unitEntState.id, entType);
|
||||
var trainNum = batchSize ? batchSize+batchIncrement : batchIncrement;
|
||||
|
||||
tooltip += "\n" + getEntityCost(template);
|
||||
@ -251,6 +298,15 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
|
||||
break;
|
||||
|
||||
case RESEARCH:
|
||||
var tooltip = getEntityNameWithGenericType(template);
|
||||
|
||||
if (template.tooltip)
|
||||
tooltip += "\n[font=\"serif-13\"]" + template.tooltip + "[/font]";
|
||||
|
||||
tooltip += "\n" + getEntityCost(template);
|
||||
break;
|
||||
|
||||
case CONSTRUCTION:
|
||||
var tooltip = getEntityNameWithGenericType(template);
|
||||
if (template.tooltip)
|
||||
@ -352,7 +408,25 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
}
|
||||
else if (template.icon)
|
||||
{
|
||||
icon.sprite = "stretched:session/portraits/" + template.icon;
|
||||
var grayscale = "";
|
||||
button.enabled = true;
|
||||
|
||||
if (template.requiredTechnology && !Engine.GuiInterfaceCall("IsTechnologyResearched", template.requiredTechnology))
|
||||
{
|
||||
button.enabled = false;
|
||||
var techName = getEntityName(GetTechnologyData(template.requiredTechnology));
|
||||
button.tooltip += "\nRequires " + techName;
|
||||
grayscale = "grayscale:";
|
||||
}
|
||||
|
||||
if (guiName == RESEARCH && !Engine.GuiInterfaceCall("CheckTechnologyRequirements", entType))
|
||||
{
|
||||
button.enabled = false;
|
||||
button.tooltip += "\n" + GetTechnologyData(entType).requirementsTooltip;
|
||||
grayscale = "grayscale:";
|
||||
}
|
||||
|
||||
icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -538,15 +612,21 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
|
||||
setupUnitPanel("Construction", usedPanels, entState, entState.buildEntities, startBuildingPlacement);
|
||||
}
|
||||
|
||||
if (entState.training && entState.training.entities.length)
|
||||
if (entState.production && entState.production.entities.length)
|
||||
{
|
||||
setupUnitPanel("Training", usedPanels, entState, entState.training.entities,
|
||||
function (trainEntType) { addToTrainingQueue(entState.id, trainEntType); } );
|
||||
setupUnitPanel("Training", usedPanels, entState, entState.production.entities,
|
||||
function (trainEntType) { addTrainingToQueue(entState.id, trainEntType); } );
|
||||
}
|
||||
|
||||
if (entState.training && entState.training.queue.length)
|
||||
setupUnitPanel("Queue", usedPanels, entState, entState.training.queue,
|
||||
function (item) { removeFromTrainingQueue(entState.id, item.id); } );
|
||||
if (entState.production && entState.production.technologies.length)
|
||||
{
|
||||
setupUnitPanel("Research", usedPanels, entState, entState.production.technologies,
|
||||
function (researchType) { addResearchToQueue(entState.id, researchType); } );
|
||||
}
|
||||
|
||||
if (entState.production && entState.production.queue.length)
|
||||
setupUnitPanel("Queue", usedPanels, entState, entState.production.queue,
|
||||
function (item) { removeFromProductionQueue(entState.id, item.id); } );
|
||||
|
||||
if (entState.trader)
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ BaseAI.prototype.Deserialize = function(data)
|
||||
// CCmpTemplateManager::CopyFoundationSubset and only includes components
|
||||
// that our EntityTemplate class currently uses.)
|
||||
var g_FoundationForbiddenComponents = {
|
||||
"TrainingQueue": 1,
|
||||
"ProductionQueue": 1,
|
||||
"ResourceSupply": 1,
|
||||
"ResourceDropsite": 1,
|
||||
"GarrisonHolder": 1,
|
||||
|
@ -127,10 +127,10 @@ var EntityTemplate = Class({
|
||||
},
|
||||
|
||||
trainableEntities: function() {
|
||||
if (!this._template.TrainingQueue)
|
||||
if (!this._template.ProductionQueue)
|
||||
return undefined;
|
||||
var civ = this.civ();
|
||||
var templates = this._template.TrainingQueue.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/);
|
||||
var templates = this._template.ProductionQueue.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/);
|
||||
return templates;
|
||||
},
|
||||
|
||||
|
@ -43,7 +43,7 @@ BaseAI.prototype.Deserialize = function(data)
|
||||
// CCmpTemplateManager::CopyFoundationSubset and only includes components
|
||||
// that our EntityTemplate class currently uses.)
|
||||
var g_FoundationForbiddenComponents = {
|
||||
"TrainingQueue": 1,
|
||||
"ProductionQueue": 1,
|
||||
"ResourceSupply": 1,
|
||||
"ResourceDropsite": 1,
|
||||
"GarrisonHolder": 1,
|
||||
|
@ -126,10 +126,10 @@ var EntityTemplate = Class({
|
||||
},
|
||||
|
||||
trainableEntities: function() {
|
||||
if (!this._template.TrainingQueue)
|
||||
if (!this._template.ProductionQueue)
|
||||
return undefined;
|
||||
var civ = this.civ();
|
||||
var templates = this._template.TrainingQueue.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/);
|
||||
var templates = this._template.ProductionQueue.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/);
|
||||
return templates;
|
||||
},
|
||||
|
||||
|
@ -119,12 +119,12 @@ AIProxy.prototype.OnUnitAIOrderDataChanged = function(msg)
|
||||
this.changes.unitAIOrderData = msg.to;
|
||||
};
|
||||
|
||||
AIProxy.prototype.OnTrainingQueueChanged = function(msg)
|
||||
AIProxy.prototype.OnProductionQueueChanged = function(msg)
|
||||
{
|
||||
this.NotifyChange();
|
||||
|
||||
var cmpTrainingQueue = Engine.QueryInterface(this.entity, IID_TrainingQueue);
|
||||
this.changes.trainingQueue = cmpTrainingQueue.GetQueue();
|
||||
var cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
|
||||
this.changes.trainingQueue = cmpProductionQueue.GetQueue();
|
||||
};
|
||||
|
||||
AIProxy.prototype.OnGarrisonedUnitsChanged = function(msg)
|
||||
@ -206,11 +206,11 @@ AIProxy.prototype.GetFullRepresentation = function()
|
||||
ret.unitAIOrderData = cmpUnitAI.GetOrderData();
|
||||
}
|
||||
|
||||
var cmpTrainingQueue = Engine.QueryInterface(this.entity, IID_TrainingQueue);
|
||||
if (cmpTrainingQueue)
|
||||
var cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
|
||||
if (cmpProductionQueue)
|
||||
{
|
||||
// Updated by OnTrainingQueueChanged
|
||||
ret.trainingQueue = cmpTrainingQueue.GetQueue();
|
||||
// Updated by OnProductionQueueChanged
|
||||
ret.trainingQueue = cmpProductionQueue.GetQueue();
|
||||
}
|
||||
|
||||
var cmpFoundation = Engine.QueryInterface(this.entity, IID_Foundation);
|
||||
|
@ -50,6 +50,16 @@ GuiInterface.prototype.GetSimulationState = function(player)
|
||||
var cmpPlayerBuildLimits = Engine.QueryInterface(playerEnt, IID_BuildLimits);
|
||||
var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
|
||||
|
||||
// Work out what phase we are in
|
||||
var cmpTechMan = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
|
||||
var phase = "";
|
||||
if (cmpTechMan.IsTechnologyResearched("city"))
|
||||
phase = "city";
|
||||
else if (cmpTechMan.IsTechnologyResearched("town"))
|
||||
phase = "town";
|
||||
else if (cmpTechMan.IsTechnologyResearched("village"))
|
||||
phase = "village";
|
||||
|
||||
// store player ally/enemy data as arrays
|
||||
var allies = [];
|
||||
var enemies = [];
|
||||
@ -66,10 +76,10 @@ GuiInterface.prototype.GetSimulationState = function(player)
|
||||
"popLimit": cmpPlayer.GetPopulationLimit(),
|
||||
"popMax": cmpPlayer.GetMaxPopulation(),
|
||||
"resourceCounts": cmpPlayer.GetResourceCounts(),
|
||||
"trainingQueueBlocked": cmpPlayer.IsTrainingQueueBlocked(),
|
||||
"trainingBlocked": cmpPlayer.IsTrainingBlocked(),
|
||||
"state": cmpPlayer.GetState(),
|
||||
"team": cmpPlayer.GetTeam(),
|
||||
"phase": cmpPlayer.GetPhase(),
|
||||
"phase": phase,
|
||||
"isAlly": allies,
|
||||
"isEnemy": enemies,
|
||||
"buildLimits": cmpPlayerBuildLimits.GetLimits(),
|
||||
@ -177,12 +187,13 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
ret.buildEntities = cmpBuilder.GetEntitiesList();
|
||||
}
|
||||
|
||||
var cmpTrainingQueue = Engine.QueryInterface(ent, IID_TrainingQueue);
|
||||
if (cmpTrainingQueue)
|
||||
var cmpProductionQueue = Engine.QueryInterface(ent, IID_ProductionQueue);
|
||||
if (cmpProductionQueue)
|
||||
{
|
||||
ret.training = {
|
||||
"entities": cmpTrainingQueue.GetEntitiesList(),
|
||||
"queue": cmpTrainingQueue.GetQueue(),
|
||||
ret.production = {
|
||||
"entities": cmpProductionQueue.GetEntitiesList(),
|
||||
"technologies": cmpProductionQueue.GetTechnologiesList(),
|
||||
"queue": cmpProductionQueue.GetQueue(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -346,6 +357,7 @@ GuiInterface.prototype.GetTemplateData = function(player, name)
|
||||
};
|
||||
ret.icon = template.Identity.Icon;
|
||||
ret.tooltip = template.Identity.Tooltip;
|
||||
ret.requiredTechnology = template.Identity.RequiredTechnology;
|
||||
}
|
||||
|
||||
if (template.UnitMotion)
|
||||
@ -359,6 +371,74 @@ GuiInterface.prototype.GetTemplateData = function(player, name)
|
||||
return ret;
|
||||
};
|
||||
|
||||
GuiInterface.prototype.GetTechnologyData = function(player, name)
|
||||
{
|
||||
var cmpTechTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TechnologyTemplateManager);
|
||||
var template = cmpTechTempMan.GetTemplate(name);
|
||||
|
||||
if (!template)
|
||||
{
|
||||
warn("Tried to get data for invalid technology: " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
var ret = {};
|
||||
|
||||
// Get specific name for this civ or else the generic specific name
|
||||
var cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
|
||||
var specific = undefined;
|
||||
if (template.specificName)
|
||||
{
|
||||
if (template.specificName[cmpPlayer.GetCiv()])
|
||||
specific = template.specificName[cmpPlayer.GetCiv()];
|
||||
else
|
||||
specific = template.specificName['generic'];
|
||||
}
|
||||
|
||||
ret.name = {
|
||||
"specific": specific,
|
||||
"generic": template.genericName,
|
||||
};
|
||||
ret.icon = "technologies/" + template.icon;
|
||||
ret.cost = {
|
||||
"food": +template.cost.food,
|
||||
"wood": +template.cost.wood,
|
||||
"metal": +template.cost.metal,
|
||||
"stone": +template.cost.stone,
|
||||
}
|
||||
ret.tooltip = template.tooltip;
|
||||
|
||||
if (template.requirementsTooltip)
|
||||
ret.requirementsTooltip = template.requirementsTooltip;
|
||||
else
|
||||
ret.requirementsTooltip = "";
|
||||
|
||||
ret.description = template.description;
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
GuiInterface.prototype.IsTechnologyResearched = function(player, tech)
|
||||
{
|
||||
var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
|
||||
if (!cmpTechMan)
|
||||
return false;
|
||||
|
||||
return cmpTechMan.IsTechnologyResearched(tech);
|
||||
};
|
||||
|
||||
// Checks whether the requirements for this technology have been met
|
||||
GuiInterface.prototype.CheckTechnologyRequirements = function(player, tech)
|
||||
{
|
||||
var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
|
||||
if (!cmpTechMan)
|
||||
return false;
|
||||
|
||||
return cmpTechMan.CanResearch(tech);
|
||||
};
|
||||
|
||||
GuiInterface.prototype.PushNotification = function(notification)
|
||||
{
|
||||
this.notifications.push(notification);
|
||||
@ -812,6 +892,9 @@ var exposedFunctions = {
|
||||
"ClearRenamedEntities": 1,
|
||||
"GetEntityState": 1,
|
||||
"GetTemplateData": 1,
|
||||
"GetTechnologyData": 1,
|
||||
"IsTechnologyResearched": 1,
|
||||
"CheckTechnologyRequirements": 1,
|
||||
"GetNextNotification": 1,
|
||||
|
||||
"GetFormationRequirements": 1,
|
||||
|
@ -68,6 +68,11 @@ Identity.prototype.Schema =
|
||||
"<element name='Icon'>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<optional>" +
|
||||
"<element name='RequiredTechnology' a:help='Optional name of a technology which must be researched before the entity can be produced'>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"</optional>";
|
||||
|
||||
|
||||
@ -126,6 +131,6 @@ Identity.prototype.CanUseFormation = function(name)
|
||||
Identity.prototype.GetSelectionGroupName = function()
|
||||
{
|
||||
return (this.template.SelectionGroupName || "");
|
||||
}
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Identity, "Identity", Identity);
|
||||
|
@ -12,7 +12,7 @@ Player.prototype.Init = function()
|
||||
this.popUsed = 0; // population of units owned or trained by this player
|
||||
this.popBonuses = 0; // sum of population bonuses of player's entities
|
||||
this.maxPop = 300; // maximum population
|
||||
this.trainingQueueBlocked = false; // indicates whether any training queue is currently blocked
|
||||
this.trainingBlocked = false; // indicates whether any training queue is currently blocked
|
||||
this.resourceCount = {
|
||||
"food": 1000,
|
||||
"wood": 1000,
|
||||
@ -104,19 +104,19 @@ Player.prototype.GetMaxPopulation = function()
|
||||
return this.maxPop;
|
||||
};
|
||||
|
||||
Player.prototype.IsTrainingQueueBlocked = function()
|
||||
Player.prototype.IsTrainingBlocked = function()
|
||||
{
|
||||
return this.trainingQueueBlocked;
|
||||
return this.trainingBlocked;
|
||||
};
|
||||
|
||||
Player.prototype.BlockTrainingQueue = function()
|
||||
Player.prototype.BlockTraining = function()
|
||||
{
|
||||
this.trainingQueueBlocked = true;
|
||||
this.trainingBlocked = true;
|
||||
};
|
||||
|
||||
Player.prototype.UnBlockTrainingQueue = function()
|
||||
Player.prototype.UnBlockTraining = function()
|
||||
{
|
||||
this.trainingQueueBlocked = false;
|
||||
this.trainingBlocked = false;
|
||||
};
|
||||
|
||||
Player.prototype.SetResourceCounts = function(resources)
|
||||
@ -223,16 +223,6 @@ Player.prototype.SetDiplomacy = function(dipl)
|
||||
this.diplomacy = dipl;
|
||||
};
|
||||
|
||||
Player.prototype.GetPhase = function()
|
||||
{
|
||||
return this.phase;
|
||||
};
|
||||
|
||||
Player.prototype.SetPhase = function(p)
|
||||
{
|
||||
this.phase = p;
|
||||
};
|
||||
|
||||
Player.prototype.GetStartingCameraPos = function()
|
||||
{
|
||||
return this.startCam.position;
|
||||
|
@ -0,0 +1,538 @@
|
||||
var g_ProgressInterval = 1000;
|
||||
const MAX_QUEUE_SIZE = 16;
|
||||
|
||||
function ProductionQueue() {}
|
||||
|
||||
ProductionQueue.prototype.Schema =
|
||||
"<a:help>Allows the building to train new units and research technologies</a:help>" +
|
||||
"<a:example>" +
|
||||
"<Entities datatype='tokens'>" +
|
||||
"\n units/{civ}_support_female_citizen\n units/{civ}_support_trader\n units/celt_infantry_spearman_b\n " +
|
||||
"</Entities>" +
|
||||
"</a:example>" +
|
||||
"<optional>" +
|
||||
"<element name='Entities' a:help='Space-separated list of entity template names that this building can train. The special string \"{civ}\" will be automatically replaced by the building's four-character civ code'>" +
|
||||
"<attribute name='datatype'>" +
|
||||
"<value>tokens</value>" +
|
||||
"</attribute>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<optional>" +
|
||||
"<element name='Technologies' a:help='Space-separated list of technology names that this building can research.'>" +
|
||||
"<attribute name='datatype'>" +
|
||||
"<value>tokens</value>" +
|
||||
"</attribute>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"</optional>";
|
||||
|
||||
ProductionQueue.prototype.Init = function()
|
||||
{
|
||||
this.nextID = 1;
|
||||
|
||||
this.queue = [];
|
||||
// Queue items are:
|
||||
// {
|
||||
// "id": 1,
|
||||
// "player": 1, // who paid for this batch; we need this to cope with refunds cleanly
|
||||
// "unitTemplate": "units/example",
|
||||
// "count": 10,
|
||||
// "resources": { "wood": 100, ... }, // resources per unit, multiply by count to get total
|
||||
// "population": 1, // population per unit, multiply by count to get total
|
||||
// "productionStarted": false, // true iff we have reserved population
|
||||
// "timeTotal": 15000, // msecs
|
||||
// "timeRemaining": 10000, // msecs
|
||||
// }
|
||||
//
|
||||
// {
|
||||
// "id": 1,
|
||||
// "player": 1, // who paid for this research; we need this to cope with refunds cleanly
|
||||
// "technologyTemplate": "example_tech",
|
||||
// "resources": { "wood": 100, ... }, // resources needed for research
|
||||
// "productionStarted": false, // true iff production has started
|
||||
// "timeTotal": 15000, // msecs
|
||||
// "timeRemaining": 10000, // msecs
|
||||
// }
|
||||
|
||||
this.timer = undefined; // g_ProgressInterval msec timer, active while the queue is non-empty
|
||||
|
||||
this.entityCache = [];
|
||||
this.spawnNotified = false;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns list of entities that can be trained by this building.
|
||||
*/
|
||||
ProductionQueue.prototype.GetEntitiesList = function()
|
||||
{
|
||||
if (!this.template.Entities)
|
||||
return [];
|
||||
|
||||
var string = this.template.Entities._string;
|
||||
|
||||
// Replace the "{civ}" codes with this entity's civ ID
|
||||
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
||||
if (cmpIdentity)
|
||||
string = string.replace(/\{civ\}/g, cmpIdentity.GetCiv());
|
||||
|
||||
return string.split(/\s+/);
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns list of technologies that can be researched by this building.
|
||||
*/
|
||||
ProductionQueue.prototype.GetTechnologiesList = function()
|
||||
{
|
||||
if (!this.template.Technologies)
|
||||
return [];
|
||||
|
||||
var string = this.template.Technologies._string;
|
||||
|
||||
var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager);
|
||||
if (!cmpTechMan)
|
||||
return [];
|
||||
|
||||
var techs = string.split(/\s+/);
|
||||
var ret = [];
|
||||
var superseded = {}; // Stores the tech which supersedes the key
|
||||
|
||||
// Add any top level technologies to an array which corresponds to the displayed icons
|
||||
// Also store what a technology is superceded by in the superceded object {"tech1":"techWhichSupercedesTech1", ...}
|
||||
for (var i in techs)
|
||||
{
|
||||
var tech = techs[i];
|
||||
var template = cmpTechMan.GetTechnologyTemplate(tech);
|
||||
if (!template.supersedes || techs.indexOf(template.supersedes) === -1)
|
||||
ret.push(tech);
|
||||
else
|
||||
superseded[template.supersedes] = tech;
|
||||
}
|
||||
|
||||
// Now make researched/in progress techs invisible
|
||||
for (var i in ret)
|
||||
{
|
||||
var tech = ret[i];
|
||||
while (cmpTechMan.IsTechnologyResearched(tech) || cmpTechMan.IsInProgress(tech))
|
||||
{
|
||||
tech = superseded[tech];
|
||||
}
|
||||
|
||||
|
||||
ret[i] = tech;
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/*
|
||||
* Adds a new batch of identical units to train or a technology to research to the production queue.
|
||||
*/
|
||||
ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadata)
|
||||
{
|
||||
// TODO: there should probably be a limit on the number of queued batches
|
||||
// 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)
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
|
||||
if (this.queue.length < MAX_QUEUE_SIZE)
|
||||
{
|
||||
|
||||
if (type == "unit")
|
||||
{
|
||||
// Find the template data so we can determine the build costs
|
||||
var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var template = cmpTempMan.GetTemplate(templateName);
|
||||
if (!template)
|
||||
return;
|
||||
|
||||
// Apply a time discount to larger batches.
|
||||
// TODO: work out what equation we should use here.
|
||||
var timeMult = Math.pow(count, 0.7);
|
||||
|
||||
var time = timeMult * template.Cost.BuildTime;
|
||||
|
||||
var totalCosts = {};
|
||||
for each (var r in ["food", "wood", "stone", "metal"])
|
||||
totalCosts[r] = Math.floor(count * template.Cost.Resources[r]);
|
||||
|
||||
var population = template.Cost.Population;
|
||||
|
||||
// TrySubtractResources should report error to player (they ran out of resources)
|
||||
if (!cmpPlayer.TrySubtractResources(totalCosts))
|
||||
return;
|
||||
|
||||
this.queue.push({
|
||||
"id": this.nextID++,
|
||||
"player": cmpPlayer.GetPlayerID(),
|
||||
"unitTemplate": templateName,
|
||||
"count": count,
|
||||
"metadata": metadata,
|
||||
"resources": deepcopy(template.Cost.Resources), // need to copy to avoid serialization problems
|
||||
"population": population,
|
||||
"productionStarted": false,
|
||||
"timeTotal": time*1000,
|
||||
"timeRemaining": time*1000,
|
||||
});
|
||||
}
|
||||
else if (type == "technology")
|
||||
{
|
||||
// Load the technology template
|
||||
var cmpTechTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TechnologyTemplateManager);
|
||||
var template = cmpTechTempMan.GetTemplate(templateName);
|
||||
if (!template)
|
||||
return;
|
||||
|
||||
var time = template.researchTime;
|
||||
|
||||
var cost = {};
|
||||
for each (var r in ["food", "wood", "stone", "metal"])
|
||||
cost[r] = Math.floor(template.cost[r]);
|
||||
|
||||
// TrySubtractResources should report error to player (they ran out of resources)
|
||||
if (!cmpPlayer.TrySubtractResources(cost))
|
||||
return;
|
||||
|
||||
// Tell the technology manager that we have started researching this so that people can't research the same
|
||||
// thing twice.
|
||||
var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager);
|
||||
cmpTechMan.StartedResearch(templateName);
|
||||
|
||||
this.queue.push({
|
||||
"id": this.nextID++,
|
||||
"player": cmpPlayer.GetPlayerID(),
|
||||
"count": 1,
|
||||
"technologyTemplate": templateName,
|
||||
"resources": deepcopy(template.cost), // need to copy to avoid serialization problems
|
||||
"productionStarted": false,
|
||||
"timeTotal": time*1000,
|
||||
"timeRemaining": time*1000,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Tried to add invalid item of type \"" + type + "\" and template \"" + templateName + "\" to a production queue");
|
||||
return;
|
||||
}
|
||||
|
||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, { });
|
||||
|
||||
// If this is the first item in the queue, start the timer
|
||||
if (!this.timer)
|
||||
{
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
this.timer = cmpTimer.SetTimeout(this.entity, IID_ProductionQueue, "ProgressTimeout", g_ProgressInterval, {});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "The production queue is full."};
|
||||
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGUIInterface.PushNotification(notification);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Removes an existing batch of units from the production queue.
|
||||
* Refunds resource costs and population reservations.
|
||||
*/
|
||||
ProductionQueue.prototype.RemoveBatch = function(id)
|
||||
{
|
||||
// Destroy any cached entities (those which didn't spawn for some reason)
|
||||
for (var i = 0; i < this.entityCache.length; ++i)
|
||||
{
|
||||
Engine.DestroyEntity(this.entityCache[i]);
|
||||
}
|
||||
this.entityCache = [];
|
||||
|
||||
for (var i = 0; i < this.queue.length; ++i)
|
||||
{
|
||||
var item = this.queue[i];
|
||||
if (item.id != id)
|
||||
continue;
|
||||
|
||||
// Now we've found the item to remove
|
||||
|
||||
var cmpPlayer = QueryPlayerIDInterface(item.player, IID_Player);
|
||||
|
||||
// Refund the resource cost for this batch
|
||||
var totalCosts = {};
|
||||
for each (var r in ["food", "wood", "stone", "metal"])
|
||||
totalCosts[r] = Math.floor(item.count * item.resources[r]);
|
||||
|
||||
cmpPlayer.AddResources(totalCosts);
|
||||
|
||||
// Remove reserved population slots if necessary
|
||||
if (item.productionStarted && item.unitTemplate)
|
||||
cmpPlayer.UnReservePopulationSlots(item.population * item.count);
|
||||
|
||||
// Mark the research as stopped if we cancel it
|
||||
if (item.technologyTemplate)
|
||||
{
|
||||
var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager);
|
||||
cmpTechMan.StoppedResearch(item.technologyTemplate);
|
||||
}
|
||||
|
||||
// Remove from the queue
|
||||
// (We don't need to remove the timer - it'll expire if it discovers the queue is empty)
|
||||
this.queue.splice(i, 1);
|
||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, { });
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns basic data from all batches in the production queue.
|
||||
*/
|
||||
ProductionQueue.prototype.GetQueue = function()
|
||||
{
|
||||
var out = [];
|
||||
for each (var item in this.queue)
|
||||
{
|
||||
out.push({
|
||||
"id": item.id,
|
||||
"unitTemplate": item.unitTemplate,
|
||||
"technologyTemplate": item.technologyTemplate,
|
||||
"count": item.count,
|
||||
"progress": 1-(item.timeRemaining/item.timeTotal),
|
||||
"metadata": item.metadata,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
/*
|
||||
* Removes all existing batches from the queue.
|
||||
*/
|
||||
ProductionQueue.prototype.ResetQueue = function()
|
||||
{
|
||||
// Empty the production queue and refund all the resource costs
|
||||
// to the player. (This is to avoid players having to micromanage their
|
||||
// buildings' queues when they're about to be destroyed or captured.)
|
||||
|
||||
while (this.queue.length)
|
||||
this.RemoveBatch(this.queue[0].id);
|
||||
};
|
||||
|
||||
ProductionQueue.prototype.OnOwnershipChanged = function(msg)
|
||||
{
|
||||
if (msg.from != -1)
|
||||
{
|
||||
// Unset flag that previous owner's training may be blocked
|
||||
var cmpPlayer = QueryPlayerIDInterface(msg.from, IID_Player);
|
||||
if (cmpPlayer && this.queue.length > 0)
|
||||
cmpPlayer.UnBlockTraining();
|
||||
}
|
||||
|
||||
// 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.OnDestroy = function()
|
||||
{
|
||||
// Reset the queue to refund any resources
|
||||
this.ResetQueue();
|
||||
|
||||
if (this.timer)
|
||||
{
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
cmpTimer.CancelTimer(this.timer);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This function creates the entities and places them in world if possible.
|
||||
* returns the number of successfully spawned entities.
|
||||
*/
|
||||
ProductionQueue.prototype.SpawnUnits = function(templateName, count, metadata)
|
||||
{
|
||||
var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
|
||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
var cmpRallyPoint = Engine.QueryInterface(this.entity, IID_RallyPoint);
|
||||
|
||||
var spawnedEnts = [];
|
||||
|
||||
if (this.entityCache.length == 0)
|
||||
{
|
||||
// We need entities to test spawning, but we don't want to waste resources,
|
||||
// so only create them once and use as needed
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
this.entityCache.push(Engine.AddEntity(templateName));
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var ent = this.entityCache[0];
|
||||
var pos = cmpFootprint.PickSpawnPoint(ent);
|
||||
if (pos.y < 0)
|
||||
{
|
||||
// Fail: there wasn't any space to spawn the unit
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Successfully spawned
|
||||
var cmpNewPosition = Engine.QueryInterface(ent, IID_Position);
|
||||
cmpNewPosition.JumpTo(pos.x, pos.z);
|
||||
// TODO: what direction should they face in?
|
||||
|
||||
var cmpNewOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
||||
cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
|
||||
|
||||
var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
|
||||
cmpPlayerStatisticsTracker.IncreaseTrainedUnitsCounter();
|
||||
|
||||
// Play a sound, but only for the first in the batch (to avoid nasty phasing effects)
|
||||
if (spawnedEnts.length == 0)
|
||||
PlaySound("trained", ent);
|
||||
|
||||
this.entityCache.shift();
|
||||
spawnedEnts.push(ent);
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnedEnts.length > 0)
|
||||
{
|
||||
// If a rally point is set, walk towards it (in formation) using a suitable command based on where the
|
||||
// rally point is placed.
|
||||
if (cmpRallyPoint)
|
||||
{
|
||||
var rallyPos = cmpRallyPoint.GetPosition();
|
||||
if (rallyPos)
|
||||
{
|
||||
ProcessCommand(cmpOwnership.GetOwner(), GetRallyPointCommand(cmpRallyPoint, spawnedEnts));
|
||||
}
|
||||
}
|
||||
|
||||
Engine.PostMessage(this.entity, MT_TrainingFinished, {
|
||||
"entities": spawnedEnts,
|
||||
"owner": cmpOwnership.GetOwner(),
|
||||
"metadata": metadata,
|
||||
});
|
||||
}
|
||||
|
||||
return spawnedEnts.length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Increments progress on the first batch in the production queue, and blocks the
|
||||
* queue if population limit is reached or some units failed to spawn.
|
||||
*/
|
||||
ProductionQueue.prototype.ProgressTimeout = function(data)
|
||||
{
|
||||
// Allocate the 1000msecs 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)
|
||||
var time = g_ProgressInterval;
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
|
||||
while (time > 0 && this.queue.length)
|
||||
{
|
||||
var item = this.queue[0];
|
||||
if (!item.productionStarted)
|
||||
{
|
||||
// If the item is a unit then do population checks
|
||||
if (item.unitTemplate)
|
||||
{
|
||||
// Batch's training hasn't started yet.
|
||||
// Try to reserve the necessary population slots
|
||||
if (item.unitTemplate && !cmpPlayer.TryReservePopulationSlots(item.population * item.count))
|
||||
{
|
||||
// No slots available - don't train this batch now
|
||||
// (we'll try again on the next timeout)
|
||||
|
||||
// Set flag that training is blocked
|
||||
cmpPlayer.BlockTraining();
|
||||
break;
|
||||
}
|
||||
|
||||
// Unset flag that training is blocked
|
||||
cmpPlayer.UnBlockTraining();
|
||||
}
|
||||
|
||||
item.productionStarted = true;
|
||||
}
|
||||
|
||||
// If we won't finish the batch now, just update its timer
|
||||
if (item.timeRemaining > time)
|
||||
{
|
||||
item.timeRemaining -= time;
|
||||
break;
|
||||
}
|
||||
|
||||
if (item.unitTemplate)
|
||||
{
|
||||
var numSpawned = this.SpawnUnits(item.unitTemplate, item.count, item.metadata);
|
||||
if (numSpawned == item.count)
|
||||
{
|
||||
// All entities spawned, this batch finished
|
||||
cmpPlayer.UnReservePopulationSlots(item.population * numSpawned);
|
||||
time -= item.timeRemaining;
|
||||
this.queue.shift();
|
||||
// Unset flag that training is blocked
|
||||
cmpPlayer.UnBlockTraining();
|
||||
this.spawnNotified = false;
|
||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, { });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (numSpawned > 0)
|
||||
{
|
||||
// Only partially finished
|
||||
cmpPlayer.UnReservePopulationSlots(item.population * numSpawned);
|
||||
item.count -= numSpawned;
|
||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, { });
|
||||
}
|
||||
|
||||
// Some entities failed to spawn
|
||||
// Set flag that training is blocked
|
||||
cmpPlayer.BlockTraining();
|
||||
|
||||
if (!this.spawnNotified)
|
||||
{
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Can't find free space to spawn trained units" };
|
||||
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGUIInterface.PushNotification(notification);
|
||||
this.spawnNotified = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (item.technologyTemplate)
|
||||
{
|
||||
var cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager);
|
||||
cmpTechnologyManager.ResearchTechnology(item.technologyTemplate);
|
||||
|
||||
time -= item.timeRemaining;
|
||||
|
||||
this.queue.shift();
|
||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, { });
|
||||
}
|
||||
}
|
||||
|
||||
// If the queue's empty, delete the timer, else repeat it
|
||||
if (this.queue.length == 0)
|
||||
{
|
||||
this.timer = undefined;
|
||||
|
||||
// Unset flag that training is blocked
|
||||
// (This might happen when the player unqueues all batches)
|
||||
cmpPlayer.UnBlockTraining();
|
||||
}
|
||||
else
|
||||
{
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
this.timer = cmpTimer.SetTimeout(this.entity, IID_ProductionQueue, "ProgressTimeout", g_ProgressInterval, data);
|
||||
}
|
||||
}
|
||||
|
||||
Engine.RegisterComponentType(IID_ProductionQueue, "ProductionQueue", ProductionQueue);
|
@ -125,19 +125,47 @@ ResourceGatherer.prototype.GetLastCarriedType = function()
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Remove any cached template data which is based on technology data
|
||||
ResourceGatherer.prototype.OnTechnologyModificationChange = function(msg)
|
||||
{
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (!cmpOwnership)
|
||||
return;
|
||||
|
||||
var player = cmpOwnership.GetOwner();
|
||||
|
||||
if (msg.component === "ResourceGatherer" && msg.player === player)
|
||||
delete this.gatherRatesCache;
|
||||
};
|
||||
|
||||
// Remove any cached template data which is based on technology data
|
||||
ResourceGatherer.prototype.OnOwnershipChanged = function(msg)
|
||||
{
|
||||
delete this.gatherRatesCache;
|
||||
};
|
||||
|
||||
ResourceGatherer.prototype.GetGatherRates = function()
|
||||
{
|
||||
var ret = {};
|
||||
if (!this.gatherRatesCache)
|
||||
{
|
||||
this.gatherRatesCache = {};
|
||||
var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager);
|
||||
var baseSpeed = cmpTechMan.ApplyModifications("ResourceGatherer/BaseSpeed", this.template.BaseSpeed, this.entity);
|
||||
for (var r in this.template.Rates)
|
||||
ret[r] = this.template.Rates[r] * this.template.BaseSpeed;
|
||||
return ret;
|
||||
{
|
||||
var rate = cmpTechMan.ApplyModifications("ResourceGatherer/Rates/" + r, this.template.Rates[r], this.entity);
|
||||
this.gatherRatesCache[r] = rate * baseSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
return this.gatherRatesCache;
|
||||
};
|
||||
|
||||
ResourceGatherer.prototype.GetRange = function()
|
||||
{
|
||||
return { "max": +this.template.MaxDistance, "min": 0 };
|
||||
// maybe this should depend on the unit or target or something?
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to gather treasure
|
||||
@ -223,13 +251,13 @@ ResourceGatherer.prototype.GetTargetGatherRate = function(target)
|
||||
var type = cmpResourceSupply.GetType();
|
||||
|
||||
var rate;
|
||||
if (type.specific && this.template.Rates[type.generic+"."+type.specific])
|
||||
rate = this.template.Rates[type.generic+"."+type.specific];
|
||||
if (type.specific && this.GetGatherRates()[type.generic+"."+type.specific])
|
||||
rate = this.GetGatherRates()[type.generic+"."+type.specific];
|
||||
else
|
||||
rate = this.template.Rates[type.generic];
|
||||
rate = this.GetGatherRates()[type.generic];
|
||||
|
||||
return (rate || 0) * this.template.BaseSpeed;
|
||||
}
|
||||
return (rate || 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether this unit can carry more of the given type of resource.
|
||||
|
@ -0,0 +1,320 @@
|
||||
function TechnologyManager() {}
|
||||
|
||||
TechnologyManager.prototype.Schema =
|
||||
"<a:component type='system'/><empty/>";
|
||||
|
||||
TechnologyManager.prototype.Init = function ()
|
||||
{
|
||||
var cmpTechTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TechnologyTemplateManager);
|
||||
this.allTechs = cmpTechTempMan.GetAllTechs();
|
||||
this.researchedTechs = {}; // technologies which have been researched
|
||||
this.inProgressTechs = {}; // technologies which are being researched currently
|
||||
|
||||
// This stores the modifications to unit stats from researched technologies
|
||||
// Example data: {"ResourceGatherer/Rates/food.grain": [
|
||||
// {"multiplier": 1.15, "affects": ["Female", "Infantry Swordsman"]},
|
||||
// {"add": 2}
|
||||
// ]}
|
||||
this.modifications = {};
|
||||
|
||||
this.typeCounts = {}; // stores the number of entities of each type
|
||||
this.classCounts = {}; // stores the number of entities of each Class
|
||||
this.typeCountsByClass = {}; // stores the number of entities of each type for each class i.e.
|
||||
// {"someClass": {"unit/spearman": 2, "unit/cav": 5} "someOtherClass":...}
|
||||
|
||||
// Some technologies are automatically researched when their conditions are met. They have no cost and are
|
||||
// researched instantly. This allows civ bonuses and more complicated technologies.
|
||||
this.autoResearchTech = {};
|
||||
for (var key in this.allTechs)
|
||||
{
|
||||
if (this.allTechs[key].autoResearch)
|
||||
this.autoResearchTech[key] = this.allTechs[key];
|
||||
}
|
||||
|
||||
this.UpdateAutoResearch();
|
||||
};
|
||||
|
||||
// This function checks if the requirements of any autoresearch techs are met and if they are it researches them
|
||||
TechnologyManager.prototype.UpdateAutoResearch = function ()
|
||||
{
|
||||
for (var key in this.autoResearchTech)
|
||||
{
|
||||
if (this.CanResearch(key))
|
||||
{
|
||||
delete this.autoResearchTech[key];
|
||||
this.ResearchTechnology(key);
|
||||
return; // We will have recursively handled any knock-on effects so can just return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TechnologyManager.prototype.GetTechnologyTemplate = function (tech)
|
||||
{
|
||||
return this.allTechs[tech];
|
||||
};
|
||||
|
||||
// Checks an entity template to see if its technology requirements have been met
|
||||
TechnologyManager.prototype.CanProduce = function (templateName)
|
||||
{
|
||||
var cmpTempManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var template = cmpTempManager.GetTemplate(templateName);
|
||||
|
||||
if (template.Identity && template.Identity.RequiredTechnology)
|
||||
return this.IsTechnologyResearched(template.Identity.RequiredTechnology);
|
||||
else
|
||||
return true; // If there is no required technology then this entity can be produced
|
||||
};
|
||||
|
||||
TechnologyManager.prototype.IsTechnologyResearched = function (tech)
|
||||
{
|
||||
return (this.researchedTechs[tech] !== undefined);
|
||||
};
|
||||
|
||||
// Checks the requirements for a technology to see if it can be researched at the current time
|
||||
TechnologyManager.prototype.CanResearch = function (tech)
|
||||
{
|
||||
var template = this.GetTechnologyTemplate(tech);
|
||||
if (!template)
|
||||
{
|
||||
warn("Technology \"" + tech + "\" does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The technology which this technology supersedes is required
|
||||
if (template.supersedes && !this.IsTechnologyResearched(template.supersedes))
|
||||
return false;
|
||||
|
||||
return this.CheckTechnologyRequirements(template.requirements);
|
||||
};
|
||||
|
||||
TechnologyManager.prototype.CheckTechnologyRequirements = function (reqs)
|
||||
{
|
||||
// If there are no requirements then all requirements are met
|
||||
if (!reqs)
|
||||
return true;
|
||||
|
||||
if (reqs.tech)
|
||||
{
|
||||
return this.IsTechnologyResearched(reqs.tech);
|
||||
}
|
||||
else if (reqs.all)
|
||||
{
|
||||
for (var i = 0; i < reqs.all.length; i++)
|
||||
{
|
||||
if (!this.CheckTechnologyRequirements(reqs.all[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (reqs.any)
|
||||
{
|
||||
for (var i = 0; i < reqs.any.length; i++)
|
||||
{
|
||||
if (this.CheckTechnologyRequirements(reqs.any[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (reqs.class)
|
||||
{
|
||||
return (reqs.numberOfTypes <= Object.keys(this.typeCountsByClass[reqs.class]).length);
|
||||
}
|
||||
|
||||
// The technologies requirements are not a recognised format
|
||||
error("Bad requirements " + uneval(reqs));
|
||||
return false;
|
||||
};
|
||||
|
||||
TechnologyManager.prototype.OnGlobalOwnershipChanged = function (msg)
|
||||
{
|
||||
// This automatically updates typeCounts, classCounts and typeCountsByClass
|
||||
var playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID();
|
||||
if (msg.to == playerID)
|
||||
{
|
||||
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var template = cmpTemplateManager.GetCurrentTemplateName(msg.entity);
|
||||
|
||||
this.typeCounts[template] = this.typeCounts[template] || 0;
|
||||
this.typeCounts[template] += 1;
|
||||
|
||||
// don't use foundations for the class counts
|
||||
if (Engine.QueryInterface(msg.entity, IID_Foundation))
|
||||
return;
|
||||
|
||||
var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
|
||||
if (cmpIdentity)
|
||||
{
|
||||
var classes = cmpIdentity.GetClassesList();
|
||||
for (var i in classes)
|
||||
{
|
||||
this.classCounts[classes[i]] = this.classCounts[classes[i]] || 0;
|
||||
this.classCounts[classes[i]] += 1;
|
||||
|
||||
this.typeCountsByClass[classes[i]] = this.typeCountsByClass[classes[i]] || {};
|
||||
this.typeCountsByClass[classes[i]][template] = this.typeCountsByClass[classes[i]][template] || 0;
|
||||
this.typeCountsByClass[classes[i]][template] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (msg.from == playerID)
|
||||
{
|
||||
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var template = cmpTemplateManager.GetCurrentTemplateName(msg.entity);
|
||||
|
||||
this.typeCounts[template] -= 1;
|
||||
if (this.typeCounts[template] <= 0)
|
||||
delete this.typeCounts[template];
|
||||
|
||||
// don't use foundations for the class counts
|
||||
if (Engine.QueryInterface(msg.entity, IID_Foundation))
|
||||
return;
|
||||
|
||||
var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
|
||||
if (cmpIdentity)
|
||||
{
|
||||
var classes = cmpIdentity.GetClassesList();
|
||||
for (var i in classes)
|
||||
{
|
||||
this.classCounts[classes[i]] -= 1;
|
||||
if (this.classCounts[classes[i]] <= 0)
|
||||
delete this.classCounts[classes[i]];
|
||||
|
||||
this.typeCountsByClass[classes[i]][template] -= 1;
|
||||
if (this.typeCountsByClass[classes[i]][template] <= 0)
|
||||
delete this.typeCountsByClass[classes[i]][template];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Marks a technology as researched. Note that this does not verify that the requirements are met.
|
||||
TechnologyManager.prototype.ResearchTechnology = function (tech)
|
||||
{
|
||||
this.StoppedResearch(tech); // The tech is no longer being currently researched
|
||||
|
||||
var template = this.GetTechnologyTemplate(tech);
|
||||
|
||||
if (!template)
|
||||
{
|
||||
error("Tried to research invalid techonology: " + uneval(tech));
|
||||
return;
|
||||
}
|
||||
|
||||
var modifiedComponents = {};
|
||||
this.researchedTechs[tech] = template;
|
||||
// store the modifications in an easy to access structure
|
||||
if (template.modifications)
|
||||
{
|
||||
var affects = [];
|
||||
if (template.affects && template.affects.length > 0)
|
||||
{
|
||||
for (var i in template.affects)
|
||||
{
|
||||
// Put the list of classes into an array for convenient access
|
||||
affects.push(template.affects[i].split(/\s+/));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
affects.push([]);
|
||||
}
|
||||
|
||||
// We add an item to this.modifications for every modification in the template.modifications array
|
||||
for (var i in template.modifications)
|
||||
{
|
||||
var modification = template.modifications[i];
|
||||
if (!this.modifications[modification.value])
|
||||
this.modifications[modification.value] = [];
|
||||
|
||||
var mod = {"affects": affects};
|
||||
// copy the modification data into our new data structure
|
||||
for (var j in modification)
|
||||
if (j !== "value")
|
||||
mod[j] = modification[j];
|
||||
|
||||
this.modifications[modification.value].push(mod);
|
||||
modifiedComponents[modification.value.split("/")[0]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
|
||||
var player = cmpPlayer.GetPlayerID();
|
||||
|
||||
for (var component in modifiedComponents)
|
||||
Engine.BroadcastMessage(MT_TechnologyModificationChange, { "component": component, "player": player });
|
||||
|
||||
this.UpdateAutoResearch();
|
||||
};
|
||||
|
||||
TechnologyManager.prototype.ApplyModifications = function(valueName, curValue, ent)
|
||||
{
|
||||
// Get all modifications to this value
|
||||
var modifications = this.modifications[valueName];
|
||||
if (!modifications) // no modifications so return the original value
|
||||
return curValue;
|
||||
|
||||
// Get the classes which this entity belongs to
|
||||
var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
var classes = cmpIdentity.GetClassesList();
|
||||
|
||||
var retValue = +curValue;
|
||||
|
||||
for (var i in modifications)
|
||||
{
|
||||
var modification = modifications[i];
|
||||
var applies = false;
|
||||
// See if any of the lists of classes matches this entity
|
||||
for (var j in modification.affects)
|
||||
{
|
||||
var hasAllClasses = true;
|
||||
// Check each class in affects is present for the entity
|
||||
for (var k in modification.affects[j])
|
||||
hasAllClasses = hasAllClasses && (classes.indexOf(modification.affects[j][k]) !== -1);
|
||||
|
||||
if (hasAllClasses)
|
||||
{
|
||||
applies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We found a match, apply the modification
|
||||
if (applies)
|
||||
{
|
||||
// Nothing is cumulative so that ordering doesn't matter as much as possible
|
||||
if (modification.multiplier)
|
||||
retValue += (modification.multiplier - 1) * +curValue;
|
||||
else if (modification.add)
|
||||
retValue += modification.add;
|
||||
else if (modification.replace) // This will depend on ordering because there is no choice
|
||||
retValue = modification.replace;
|
||||
else
|
||||
warn("modification format not recognised (modifying " + valueName + "): " + uneval(modification));
|
||||
}
|
||||
}
|
||||
|
||||
return retValue;
|
||||
};
|
||||
|
||||
// Marks a technology as being currently researched
|
||||
TechnologyManager.prototype.StartedResearch = function (tech)
|
||||
{
|
||||
this.inProgressTechs[tech] = true;
|
||||
};
|
||||
|
||||
// Marks a technology as not being currently researched
|
||||
TechnologyManager.prototype.StoppedResearch = function (tech)
|
||||
{
|
||||
delete this.inProgressTechs[tech];
|
||||
};
|
||||
|
||||
// Checks whether a technology is being currently researched
|
||||
TechnologyManager.prototype.IsInProgress = function(tech)
|
||||
{
|
||||
if (this.inProgressTechs[tech])
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_TechnologyManager, "TechnologyManager", TechnologyManager);
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* System component which loads the technology data files
|
||||
*/
|
||||
function TechnologyTemplateManager() {}
|
||||
|
||||
TechnologyTemplateManager.prototype.Schema =
|
||||
"<a:component type='system'/><empty/>";
|
||||
|
||||
TechnologyTemplateManager.prototype.Init = function()
|
||||
{
|
||||
this.allTechs = {};
|
||||
var techNames = this.ListAllTechs();
|
||||
for (i in techNames)
|
||||
this.GetTemplate(techNames[i]);
|
||||
};
|
||||
|
||||
TechnologyTemplateManager.prototype.GetTemplate = function(template)
|
||||
{
|
||||
if (!this.allTechs[template])
|
||||
{
|
||||
this.allTechs[template] = Engine.ReadJSONFile("technologies/" + template + ".json");
|
||||
if (! this.allTechs[template])
|
||||
error("Failed to load technology \"" + template + "\"");
|
||||
}
|
||||
|
||||
return this.allTechs[template];
|
||||
};
|
||||
|
||||
TechnologyTemplateManager.prototype.ListAllTechs = function()
|
||||
{
|
||||
return Engine.FindJSONFiles("technologies");
|
||||
}
|
||||
|
||||
TechnologyTemplateManager.prototype.GetAllTechs = function()
|
||||
{
|
||||
return this.allTechs;
|
||||
}
|
||||
|
||||
Engine.RegisterComponentType(IID_TechnologyTemplateManager, "TechnologyTemplateManager", TechnologyTemplateManager);
|
@ -1,400 +0,0 @@
|
||||
var g_ProgressInterval = 1000;
|
||||
const MAX_QUEUE_SIZE = 16;
|
||||
|
||||
function TrainingQueue() {}
|
||||
|
||||
TrainingQueue.prototype.Schema =
|
||||
"<a:help>Allows the building to train new units.</a:help>" +
|
||||
"<a:example>" +
|
||||
"<Entities datatype='tokens'>" +
|
||||
"\n units/{civ}_support_female_citizen\n units/{civ}_support_trader\n units/celt_infantry_spearman_b\n " +
|
||||
"</Entities>" +
|
||||
"</a:example>" +
|
||||
"<element name='Entities' a:help='Space-separated list of entity template names that this building can train. The special string \"{civ}\" will be automatically replaced by the building's four-character civ code'>" +
|
||||
"<attribute name='datatype'>" +
|
||||
"<value>tokens</value>" +
|
||||
"</attribute>" +
|
||||
"<text/>" +
|
||||
"</element>";
|
||||
|
||||
TrainingQueue.prototype.Init = function()
|
||||
{
|
||||
this.nextID = 1;
|
||||
|
||||
this.queue = [];
|
||||
// Queue items are:
|
||||
// {
|
||||
// "id": 1,
|
||||
// "player": 1, // who paid for this batch; we need this to cope with refunds cleanly
|
||||
// "template": "units/example",
|
||||
// "count": 10,
|
||||
// "resources": { "wood": 100, ... }, // resources per unit, multiply by count to get total
|
||||
// "population": 1, // population per unit, multiply by count to get total
|
||||
// "trainingStarted": false, // true iff we have reserved population
|
||||
// "timeTotal": 15000, // msecs
|
||||
// "timeRemaining": 10000, // msecs
|
||||
// }
|
||||
|
||||
this.timer = undefined; // g_ProgressInterval msec timer, active while the queue is non-empty
|
||||
|
||||
this.entityCache = [];
|
||||
this.spawnNotified = false;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns list of entities that can be trained by this building.
|
||||
*/
|
||||
TrainingQueue.prototype.GetEntitiesList = function()
|
||||
{
|
||||
var string = this.template.Entities._string;
|
||||
|
||||
// Replace the "{civ}" codes with this entity's civ ID
|
||||
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
||||
if (cmpIdentity)
|
||||
string = string.replace(/\{civ\}/g, cmpIdentity.GetCiv());
|
||||
|
||||
return string.split(/\s+/);
|
||||
};
|
||||
|
||||
/*
|
||||
* Adds a new batch of identical units to the training queue.
|
||||
*/
|
||||
TrainingQueue.prototype.AddBatch = function(templateName, count, metadata)
|
||||
{
|
||||
// TODO: there should probably be a limit on the number of queued batches
|
||||
// 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)
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
|
||||
if (this.queue.length < MAX_QUEUE_SIZE)
|
||||
{
|
||||
// Find the template data so we can determine the build costs
|
||||
var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
|
||||
var template = cmpTempMan.GetTemplate(templateName);
|
||||
if (!template)
|
||||
return;
|
||||
|
||||
// Apply a time discount to larger batches.
|
||||
// TODO: work out what equation we should use here.
|
||||
var timeMult = Math.pow(count, 0.7);
|
||||
|
||||
var time = timeMult * template.Cost.BuildTime;
|
||||
|
||||
var totalCosts = {};
|
||||
for each (var r in ["food", "wood", "stone", "metal"])
|
||||
totalCosts[r] = Math.floor(count * template.Cost.Resources[r]);
|
||||
|
||||
var population = template.Cost.Population;
|
||||
|
||||
// TrySubtractResources should report error to player (they ran out of resources)
|
||||
if (!cmpPlayer.TrySubtractResources(totalCosts))
|
||||
return;
|
||||
|
||||
this.queue.push({
|
||||
"id": this.nextID++,
|
||||
"player": cmpPlayer.GetPlayerID(),
|
||||
"template": templateName,
|
||||
"count": count,
|
||||
"metadata": metadata,
|
||||
"resources": deepcopy(template.Cost.Resources), // need to copy to avoid serialization problems
|
||||
"population": population,
|
||||
"trainingStarted": false,
|
||||
"timeTotal": time*1000,
|
||||
"timeRemaining": time*1000,
|
||||
});
|
||||
Engine.PostMessage(this.entity, MT_TrainingQueueChanged, { });
|
||||
|
||||
// If this is the first item in the queue, start the timer
|
||||
if (!this.timer)
|
||||
{
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
this.timer = cmpTimer.SetTimeout(this.entity, IID_TrainingQueue, "ProgressTimeout", g_ProgressInterval, {});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "The training queue is full."};
|
||||
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGUIInterface.PushNotification(notification);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Removes an existing batch of units from the training queue.
|
||||
* Refunds resource costs and population reservations.
|
||||
*/
|
||||
TrainingQueue.prototype.RemoveBatch = function(id)
|
||||
{
|
||||
// Destroy any cached entities (those which didn't spawn for some reason)
|
||||
for (var i = 0; i < this.entityCache.length; ++i)
|
||||
{
|
||||
Engine.DestroyEntity(this.entityCache[i]);
|
||||
}
|
||||
this.entityCache = [];
|
||||
|
||||
for (var i = 0; i < this.queue.length; ++i)
|
||||
{
|
||||
var item = this.queue[i];
|
||||
if (item.id != id)
|
||||
continue;
|
||||
|
||||
// Now we've found the item to remove
|
||||
|
||||
var cmpPlayer = QueryPlayerIDInterface(item.player, IID_Player);
|
||||
|
||||
// Refund the resource cost for this batch
|
||||
var totalCosts = {};
|
||||
for each (var r in ["food", "wood", "stone", "metal"])
|
||||
totalCosts[r] = Math.floor(item.count * item.resources[r]);
|
||||
|
||||
cmpPlayer.AddResources(totalCosts);
|
||||
|
||||
// Remove reserved population slots if necessary
|
||||
if (item.trainingStarted)
|
||||
cmpPlayer.UnReservePopulationSlots(item.population * item.count);
|
||||
|
||||
// Remove from the queue
|
||||
// (We don't need to remove the timer - it'll expire if it discovers the queue is empty)
|
||||
this.queue.splice(i, 1);
|
||||
Engine.PostMessage(this.entity, MT_TrainingQueueChanged, { });
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns basic data from all batches in the training queue.
|
||||
*/
|
||||
TrainingQueue.prototype.GetQueue = function()
|
||||
{
|
||||
var out = [];
|
||||
for each (var item in this.queue)
|
||||
{
|
||||
out.push({
|
||||
"id": item.id,
|
||||
"template": item.template,
|
||||
"count": item.count,
|
||||
"progress": 1-(item.timeRemaining/item.timeTotal),
|
||||
"metadata": item.metadata,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
/*
|
||||
* Removes all existing batches from the queue.
|
||||
*/
|
||||
TrainingQueue.prototype.ResetQueue = function()
|
||||
{
|
||||
// Empty the training queue and refund all the resource costs
|
||||
// to the player. (This is to avoid players having to micromanage their
|
||||
// buildings' queues when they're about to be destroyed or captured.)
|
||||
|
||||
while (this.queue.length)
|
||||
this.RemoveBatch(this.queue[0].id);
|
||||
};
|
||||
|
||||
TrainingQueue.prototype.OnOwnershipChanged = function(msg)
|
||||
{
|
||||
if (msg.from != -1)
|
||||
{
|
||||
// Unset flag that previous owner's training queue may be blocked
|
||||
var cmpPlayer = QueryPlayerIDInterface(msg.from, IID_Player);
|
||||
if (cmpPlayer && this.queue.length > 0)
|
||||
cmpPlayer.UnBlockTrainingQueue();
|
||||
}
|
||||
|
||||
// Reset the training 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();
|
||||
};
|
||||
|
||||
TrainingQueue.prototype.OnDestroy = function()
|
||||
{
|
||||
// Reset the queue to refund any resources
|
||||
this.ResetQueue();
|
||||
|
||||
if (this.timer)
|
||||
{
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
cmpTimer.CancelTimer(this.timer);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This function creates the entities and places them in world if possible.
|
||||
* returns the number of successfully spawned entities.
|
||||
*/
|
||||
TrainingQueue.prototype.SpawnUnits = function(templateName, count, metadata)
|
||||
{
|
||||
var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
|
||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
var cmpRallyPoint = Engine.QueryInterface(this.entity, IID_RallyPoint);
|
||||
|
||||
var spawnedEnts = [];
|
||||
|
||||
if (this.entityCache.length == 0)
|
||||
{
|
||||
// We need entities to test spawning, but we don't want to waste resources,
|
||||
// so only create them once and use as needed
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
this.entityCache.push(Engine.AddEntity(templateName));
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var ent = this.entityCache[0];
|
||||
var pos = cmpFootprint.PickSpawnPoint(ent);
|
||||
if (pos.y < 0)
|
||||
{
|
||||
// Fail: there wasn't any space to spawn the unit
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Successfully spawned
|
||||
var cmpNewPosition = Engine.QueryInterface(ent, IID_Position);
|
||||
cmpNewPosition.JumpTo(pos.x, pos.z);
|
||||
// TODO: what direction should they face in?
|
||||
|
||||
var cmpNewOwnership = Engine.QueryInterface(ent, IID_Ownership);
|
||||
cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
|
||||
|
||||
var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
|
||||
cmpPlayerStatisticsTracker.IncreaseTrainedUnitsCounter();
|
||||
|
||||
// Play a sound, but only for the first in the batch (to avoid nasty phasing effects)
|
||||
if (spawnedEnts.length == 0)
|
||||
PlaySound("trained", ent);
|
||||
|
||||
this.entityCache.shift();
|
||||
spawnedEnts.push(ent);
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnedEnts.length > 0)
|
||||
{
|
||||
// If a rally point is set, walk towards it (in formation) using a suitable command based on where the
|
||||
// rally point is placed.
|
||||
if (cmpRallyPoint)
|
||||
{
|
||||
var rallyPos = cmpRallyPoint.GetPosition();
|
||||
if (rallyPos)
|
||||
{
|
||||
ProcessCommand(cmpOwnership.GetOwner(), GetRallyPointCommand(cmpRallyPoint, spawnedEnts));
|
||||
}
|
||||
}
|
||||
|
||||
Engine.PostMessage(this.entity, MT_TrainingFinished, {
|
||||
"entities": spawnedEnts,
|
||||
"owner": cmpOwnership.GetOwner(),
|
||||
"metadata": metadata,
|
||||
});
|
||||
}
|
||||
|
||||
return spawnedEnts.length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Increments progress on the first batch in the training queue, and blocks the
|
||||
* queue if population limit is reached or some units failed to spawn.
|
||||
*/
|
||||
TrainingQueue.prototype.ProgressTimeout = function(data)
|
||||
{
|
||||
// Allocate the 1000msecs 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)
|
||||
var time = g_ProgressInterval;
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
|
||||
while (time > 0 && this.queue.length)
|
||||
{
|
||||
var item = this.queue[0];
|
||||
if (!item.trainingStarted)
|
||||
{
|
||||
// Batch's training hasn't started yet.
|
||||
// Try to reserve the necessary population slots
|
||||
if (!cmpPlayer.TryReservePopulationSlots(item.population * item.count))
|
||||
{
|
||||
// No slots available - don't train this batch now
|
||||
// (we'll try again on the next timeout)
|
||||
|
||||
// Set flag that training queue is blocked
|
||||
cmpPlayer.BlockTrainingQueue();
|
||||
break;
|
||||
}
|
||||
|
||||
// Unset flag that training queue is blocked
|
||||
cmpPlayer.UnBlockTrainingQueue();
|
||||
|
||||
item.trainingStarted = true;
|
||||
}
|
||||
|
||||
// If we won't finish the batch now, just update its timer
|
||||
if (item.timeRemaining > time)
|
||||
{
|
||||
item.timeRemaining -= time;
|
||||
break;
|
||||
}
|
||||
|
||||
var numSpawned = this.SpawnUnits(item.template, item.count, item.metadata);
|
||||
if (numSpawned == item.count)
|
||||
{
|
||||
// All entities spawned, this batch finished
|
||||
cmpPlayer.UnReservePopulationSlots(item.population * numSpawned);
|
||||
time -= item.timeRemaining;
|
||||
this.queue.shift();
|
||||
// Unset flag that training queue is blocked
|
||||
cmpPlayer.UnBlockTrainingQueue();
|
||||
this.spawnNotified = false;
|
||||
Engine.PostMessage(this.entity, MT_TrainingQueueChanged, { });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (numSpawned > 0)
|
||||
{
|
||||
// Only partially finished
|
||||
cmpPlayer.UnReservePopulationSlots(item.population * numSpawned);
|
||||
item.count -= numSpawned;
|
||||
Engine.PostMessage(this.entity, MT_TrainingQueueChanged, { });
|
||||
}
|
||||
|
||||
// Some entities failed to spawn
|
||||
// Set flag that training queue is blocked
|
||||
cmpPlayer.BlockTrainingQueue();
|
||||
|
||||
if (!this.spawnNotified)
|
||||
{
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
||||
var notification = {"player": cmpPlayer.GetPlayerID(), "message": "Can't find free space to spawn trained units" };
|
||||
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGUIInterface.PushNotification(notification);
|
||||
this.spawnNotified = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the queue's empty, delete the timer, else repeat it
|
||||
if (this.queue.length == 0)
|
||||
{
|
||||
this.timer = undefined;
|
||||
|
||||
// Unset flag that training queue is blocked
|
||||
// (This might happen when the player unqueues all batches)
|
||||
cmpPlayer.UnBlockTrainingQueue();
|
||||
}
|
||||
else
|
||||
{
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
this.timer = cmpTimer.SetTimeout(this.entity, IID_TrainingQueue, "ProgressTimeout", g_ProgressInterval, data);
|
||||
}
|
||||
}
|
||||
|
||||
Engine.RegisterComponentType(IID_TrainingQueue, "TrainingQueue", TrainingQueue);
|
@ -1,8 +1,8 @@
|
||||
Engine.RegisterInterface("TrainingQueue");
|
||||
Engine.RegisterInterface("ProductionQueue");
|
||||
|
||||
// Message of the form { } (use GetQueue if you want the current details),
|
||||
// sent to the current entity whenever the training queue changes.
|
||||
Engine.RegisterMessageType("TrainingQueueChanged");
|
||||
Engine.RegisterMessageType("ProductionQueueChanged");
|
||||
|
||||
// Message of the form { entities: [id, ...], metadata: ... }
|
||||
// sent to the current entity whenever a unit has been trained.
|
@ -0,0 +1,5 @@
|
||||
Engine.RegisterInterface("TechnologyManager");
|
||||
|
||||
// Message of the form { "component": "Attack", "player": 3 }
|
||||
// Sent when a new technology is researched which modifies a component
|
||||
Engine.RegisterMessageType("TechnologyModificationChange");
|
@ -0,0 +1 @@
|
||||
Engine.RegisterInterface("TechnologyTemplateManager");
|
@ -7,12 +7,13 @@ Engine.LoadComponentScript("interfaces/Foundation.js");
|
||||
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
|
||||
Engine.LoadComponentScript("interfaces/Heal.js");
|
||||
Engine.LoadComponentScript("interfaces/Health.js");
|
||||
Engine.LoadComponentScript("interfaces/ProductionQueue.js");
|
||||
Engine.LoadComponentScript("interfaces/Promotion.js");
|
||||
Engine.LoadComponentScript("interfaces/RallyPoint.js");
|
||||
Engine.LoadComponentScript("interfaces/ResourceDropsite.js");
|
||||
Engine.LoadComponentScript("interfaces/ResourceGatherer.js");
|
||||
Engine.LoadComponentScript("interfaces/ResourceSupply.js");
|
||||
Engine.LoadComponentScript("interfaces/TrainingQueue.js");
|
||||
Engine.LoadComponentScript("interfaces/TechnologyManager.js")
|
||||
Engine.LoadComponentScript("interfaces/Trader.js")
|
||||
Engine.LoadComponentScript("interfaces/Timer.js");
|
||||
Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
|
||||
@ -49,7 +50,6 @@ AddMock(SYSTEM_ENTITY, IID_Timer, {
|
||||
SetTimeout: function(ent, iid, funcname, time, data) { return 0; },
|
||||
});
|
||||
|
||||
|
||||
AddMock(100, IID_Player, {
|
||||
GetName: function() { return "Player 1"; },
|
||||
GetCiv: function() { return "gaia"; },
|
||||
@ -58,11 +58,10 @@ AddMock(100, IID_Player, {
|
||||
GetPopulationLimit: function() { return 20; },
|
||||
GetMaxPopulation: function() { return 200; },
|
||||
GetResourceCounts: function() { return { food: 100 }; },
|
||||
IsTrainingQueueBlocked: function() { return false; },
|
||||
IsTrainingBlocked: function() { return false; },
|
||||
GetState: function() { return "active"; },
|
||||
GetTeam: function() { return -1; },
|
||||
GetDiplomacy: function() { return [-1, 1]; },
|
||||
GetPhase: function() { return ""; },
|
||||
GetConquestCriticalEntitiesCount: function() { return 1; },
|
||||
IsAlly: function() { return false; },
|
||||
IsEnemy: function() { return true; },
|
||||
@ -73,6 +72,10 @@ AddMock(100, IID_BuildLimits, {
|
||||
GetCounts: function() { return {"Foo": 5}; },
|
||||
});
|
||||
|
||||
AddMock(100, IID_TechnologyManager, {
|
||||
IsTechnologyResearched: function(tech) { return false; },
|
||||
});
|
||||
|
||||
AddMock(100, IID_StatisticsTracker, {
|
||||
GetStatistics: function() {
|
||||
return {
|
||||
@ -105,11 +108,10 @@ AddMock(101, IID_Player, {
|
||||
GetPopulationLimit: function() { return 30; },
|
||||
GetMaxPopulation: function() { return 300; },
|
||||
GetResourceCounts: function() { return { food: 200 }; },
|
||||
IsTrainingQueueBlocked: function() { return false; },
|
||||
IsTrainingBlocked: function() { return false; },
|
||||
GetState: function() { return "active"; },
|
||||
GetTeam: function() { return -1; },
|
||||
GetDiplomacy: function() { return [-1, 1]; },
|
||||
GetPhase: function() { return "village"; },
|
||||
GetConquestCriticalEntitiesCount: function() { return 1; },
|
||||
IsAlly: function() { return true; },
|
||||
IsEnemy: function() { return false; },
|
||||
@ -120,6 +122,10 @@ AddMock(101, IID_BuildLimits, {
|
||||
GetCounts: function() { return {"Bar": 0}; },
|
||||
});
|
||||
|
||||
AddMock(101, IID_TechnologyManager, {
|
||||
IsTechnologyResearched: function(tech) { if (tech == "village") return true; else return false; },
|
||||
});
|
||||
|
||||
AddMock(101, IID_StatisticsTracker, {
|
||||
GetStatistics: function() {
|
||||
return {
|
||||
@ -157,7 +163,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
|
||||
popLimit: 20,
|
||||
popMax: 200,
|
||||
resourceCounts: { food: 100 },
|
||||
trainingQueueBlocked: false,
|
||||
trainingBlocked: false,
|
||||
state: "active",
|
||||
team: -1,
|
||||
phase: "",
|
||||
@ -174,7 +180,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
|
||||
popLimit: 30,
|
||||
popMax: 300,
|
||||
resourceCounts: { food: 200 },
|
||||
trainingQueueBlocked: false,
|
||||
trainingBlocked: false,
|
||||
state: "active",
|
||||
team: -1,
|
||||
phase: "village",
|
||||
@ -198,7 +204,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
|
||||
popLimit: 20,
|
||||
popMax: 200,
|
||||
resourceCounts: { food: 100 },
|
||||
trainingQueueBlocked: false,
|
||||
trainingBlocked: false,
|
||||
state: "active",
|
||||
team: -1,
|
||||
phase: "",
|
||||
@ -231,7 +237,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
|
||||
popLimit: 30,
|
||||
popMax: 300,
|
||||
resourceCounts: { food: 200 },
|
||||
trainingQueueBlocked: false,
|
||||
trainingBlocked: false,
|
||||
state: "active",
|
||||
team: -1,
|
||||
phase: "village",
|
||||
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
"genericName": "City Phase",
|
||||
"specificName": {
|
||||
"generic": "Generic Specific City Name",
|
||||
"hele": "Greek City"
|
||||
},
|
||||
"description": "Advances from a bustling town to a veritable metropolis, full of the wonders of modern technology.",
|
||||
"cost": {"food": 0, "wood": 0, "stone": 50, "metal": 0},
|
||||
"supersedes": "town_phase",
|
||||
"icon": "city_phase.png",
|
||||
"researchTime": 10,
|
||||
"tooltip": "Advance to City Phase"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"genericName": "Plough",
|
||||
"description": "A horse drawn instrument to turn the sod.",
|
||||
"cost": {"food": 0, "wood": 0, "stone": 0, "metal": 0},
|
||||
"requirements": {"tech": "town_phase"},
|
||||
"requirementsTooltip": "Requires Town Phase",
|
||||
"icon": "plough.png",
|
||||
"researchTime": 10,
|
||||
"tooltip": "Discover the Plough",
|
||||
"modifications": [{"value": "ResourceGatherer/Rates/food.grain", "multiplier": 15}],
|
||||
"affects": ["Female"]
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"genericName": "Town Phase",
|
||||
"description": "Advances from a small village to a bustling town, ready to expand rapidly.",
|
||||
"cost": { "food": 100, "wood": 100, "stone": 0, "metal": 50 },
|
||||
"requirements": { "class": "Village", "numberOfTypes": 2 },
|
||||
"requirementsTooltip": "Requires two village structures",
|
||||
"supersedes": "village_phase",
|
||||
"icon": "town_phase.png",
|
||||
"researchTime": 10,
|
||||
"tooltip": "Advance to Town Phase"
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
"genericName": "Village Phase",
|
||||
"autoResearch": true
|
||||
}
|
@ -135,9 +135,18 @@ function ProcessCommand(player, cmd)
|
||||
// Verify that the building can be controlled by the player
|
||||
if (CanControlUnit(cmd.entity, player, controlAllUnits))
|
||||
{
|
||||
var queue = Engine.QueryInterface(cmd.entity, IID_TrainingQueue);
|
||||
var cmpTechMan = QueryOwnerInterface(cmd.entity, IID_TechnologyManager);
|
||||
// TODO: Enable this check once the AI gets technology support
|
||||
if (cmpTechMan.CanProduce(cmd.template) || true)
|
||||
{
|
||||
var queue = Engine.QueryInterface(cmd.entity, IID_ProductionQueue);
|
||||
if (queue)
|
||||
queue.AddBatch(cmd.template, +cmd.count, cmd.metadata);
|
||||
queue.AddBatch(cmd.template, "unit", +cmd.count, cmd.metadata);
|
||||
}
|
||||
else if (g_DebugCommands)
|
||||
{
|
||||
warn("Invalid command: training requires unresearched technology: " + uneval(cmd));
|
||||
}
|
||||
}
|
||||
else if (g_DebugCommands)
|
||||
{
|
||||
@ -145,17 +154,40 @@ function ProcessCommand(player, cmd)
|
||||
}
|
||||
break;
|
||||
|
||||
case "stop-train":
|
||||
case "research":
|
||||
// Verify that the building can be controlled by the player
|
||||
if (CanControlUnit(cmd.entity, player, controlAllUnits))
|
||||
{
|
||||
var queue = Engine.QueryInterface(cmd.entity, IID_TrainingQueue);
|
||||
var cmpTechMan = QueryOwnerInterface(cmd.entity, IID_TechnologyManager);
|
||||
// TODO: Enable this check once the AI gets technology support
|
||||
if (cmpTechMan.CanResearch(cmd.template) || true)
|
||||
{
|
||||
var queue = Engine.QueryInterface(cmd.entity, IID_ProductionQueue);
|
||||
if (queue)
|
||||
queue.AddBatch(cmd.template, "technology");
|
||||
}
|
||||
else if (g_DebugCommands)
|
||||
{
|
||||
warn("Invalid command: Requirements to research technology are not met: " + uneval(cmd));
|
||||
}
|
||||
}
|
||||
else if (g_DebugCommands)
|
||||
{
|
||||
warn("Invalid command: research building cannot be controlled by player "+player+": "+uneval(cmd));
|
||||
}
|
||||
break;
|
||||
|
||||
case "stop-production":
|
||||
// Verify that the building can be controlled by the player
|
||||
if (CanControlUnit(cmd.entity, player, controlAllUnits))
|
||||
{
|
||||
var queue = Engine.QueryInterface(cmd.entity, IID_ProductionQueue);
|
||||
if (queue)
|
||||
queue.RemoveBatch(cmd.id);
|
||||
}
|
||||
else if (g_DebugCommands)
|
||||
{
|
||||
warn("Invalid command: training building cannot be controlled by player "+player+": "+uneval(cmd));
|
||||
warn("Invalid command: production building cannot be controlled by player "+player+": "+uneval(cmd));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -234,6 +266,22 @@ function ProcessCommand(player, cmd)
|
||||
break;
|
||||
}
|
||||
|
||||
var cmpTechMan = QueryPlayerIDInterface(player, IID_TechnologyManager);
|
||||
// TODO: Enable this check once the AI gets technology support
|
||||
if (!cmpTechMan.CanProduce(cmd.template) && false)
|
||||
{
|
||||
if (g_DebugCommands)
|
||||
{
|
||||
warn("Invalid command: required technology check failed for player "+player+": "+uneval(cmd));
|
||||
}
|
||||
|
||||
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGuiInterface.PushNotification({ "player": player, "message": "Building's technology requirements are not met." });
|
||||
|
||||
// Remove the foundation because the construction was aborted
|
||||
Engine.DestroyEntity(ent);
|
||||
}
|
||||
|
||||
// TODO: AI has no visibility info
|
||||
if (!cmpPlayer.IsAI())
|
||||
{
|
||||
|
@ -35,13 +35,13 @@
|
||||
<Radius>150</Radius>
|
||||
<Weight>35000</Weight>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
-units/{civ}_support_female_citizen
|
||||
campaigns/army_mace_hero_alexander
|
||||
campaigns/army_mace_standard
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>campaigns/structures/hellenes/settlement_curtainwall.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -35,14 +35,14 @@
|
||||
<Radius>300</Radius>
|
||||
<Weight>35000</Weight>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
-units/{civ}_support_female_citizen
|
||||
campaigns/army_mace_hero_alexander
|
||||
campaigns/army_mace_standard
|
||||
units/{civ}_support_trader
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>campaigns/structures/hellenes/settlement_curtainwall.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<Radius>100</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue disable=""/>
|
||||
<ProductionQueue disable=""/>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/temple_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -34,11 +34,11 @@
|
||||
<Static width="22.0" depth="24.0"/>
|
||||
</Obstruction>
|
||||
<TerritoryDecay disable=""/>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_sacred_band_cavalry
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<Vision>
|
||||
<Range>40</Range>
|
||||
</Vision>
|
||||
|
@ -35,13 +35,13 @@
|
||||
<ResourceDropsite>
|
||||
<Types>food wood stone metal</Types>
|
||||
</ResourceDropsite>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_cavalry_javelinist_b
|
||||
units/celt_infantry_javelinist_b
|
||||
units/celt_infantry_spearman_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<TerritoryInfluence>
|
||||
<Radius>100</Radius>
|
||||
<Weight>65536</Weight>
|
||||
|
@ -39,11 +39,11 @@
|
||||
<Radius>32</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_fanatic
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/celts/tavern.xml</Actor>
|
||||
<FoundationActor>structures/fndn_4x4.xml</FoundationActor>
|
||||
|
@ -48,13 +48,13 @@
|
||||
<Radius>40</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/thrace_black_cloak
|
||||
units/mace_thorakites
|
||||
units/mace_thureophoros
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<Vision>
|
||||
<Range>40</Range>
|
||||
<RetainInFog>true</RetainInFog>
|
||||
|
@ -21,11 +21,11 @@
|
||||
<TerritoryInfluence>
|
||||
<Radius>25</Radius>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/{civ}_support_female_citizen
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>props/structures/persians/alt_building_03.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -21,11 +21,11 @@
|
||||
<TerritoryInfluence>
|
||||
<Radius>26</Radius>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/{civ}_support_female_citizen
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>props/structures/persians/alt_building_04.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -10,4 +10,5 @@
|
||||
</BuildLimits>
|
||||
<Player/>
|
||||
<StatisticsTracker/>
|
||||
<TechnologyManager/>
|
||||
</Entity>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<SpecificName>Stratēgeîon</SpecificName>
|
||||
<History>The Stratigeion was the main military headquarters, where important decisions were taken and plans for battles discussed by the Hellene Generals, or "Strategoi".</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/athen_infantry_spearman_b
|
||||
units/athen_infantry_javelinist_b
|
||||
@ -23,7 +23,7 @@
|
||||
units/athen_cavalry_swordsman_b
|
||||
units/athen_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/athenians/barracks.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -11,13 +11,13 @@
|
||||
<SpecificName>Agorā́</SpecificName>
|
||||
<History>The most important place in most Classical Greek poleis, the Agora served many purposes; it was a place for public speeches and was the stage for civic life and commercial interests.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/athen_infantry_spearman_b
|
||||
units/athen_infantry_slinger_b
|
||||
units/athen_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/athenians/civic_centre_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -18,11 +18,11 @@
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/athenians/corral.xml</Actor>
|
||||
<FoundationActor>structures/fndn_3x3.xml</FoundationActor>
|
||||
|
@ -11,12 +11,12 @@
|
||||
<SpecificName>Limḗn</SpecificName>
|
||||
<History>Greece is a sea country, which is why some of the greatest Hellenic and Hellenistic cities like Ephesus, Corinth, Alexandria and Antioch were built by the sea. It should also be noted that all colonies during the Great Colonisation were thriving port centres, which traded with the local population.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/athen_ship_bireme
|
||||
units/athen_ship_trireme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/athenians/dock.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -19,12 +19,12 @@
|
||||
<Obstruction>
|
||||
<Static width="24.0" depth="26.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/athen_mechanical_siege_oxybeles
|
||||
units/athen_mechanical_siege_lithobolos
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/athenians/fortress.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -33,12 +33,12 @@
|
||||
<death>attack/destruction/building_collapse_large.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/athen_champion_infantry
|
||||
units/athen_champion_ranged
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/athenians/gymnasion.xml</Actor>
|
||||
<FoundationActor>structures/fndn_6x6.xml</FoundationActor>
|
||||
|
@ -34,13 +34,13 @@
|
||||
<death>attack/destruction/building_collapse_large.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/athen_hero_themistocles
|
||||
units/athen_hero_pericles
|
||||
units/athen_hero_xenophon
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/athenians/prytaneion.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -19,13 +19,13 @@
|
||||
<Obstruction>
|
||||
<Static width="22.0" depth="23.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_infantry_spearman_b
|
||||
units/cart_infantry_archer_b
|
||||
units/cart_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/barracks.xml</Actor>
|
||||
<FoundationActor>structures/fndn_5x5.xml</FoundationActor>
|
||||
|
@ -5,13 +5,13 @@
|
||||
<SpecificName>Merkāz</SpecificName>
|
||||
<History>Carthiginian's History</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_infantry_spearman_b
|
||||
units/cart_infantry_archer_b
|
||||
units/cart_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/civil_centre.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -12,11 +12,11 @@
|
||||
<Obstruction>
|
||||
<Static width="16.0" depth="14.5"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/corral.xml</Actor>
|
||||
<FoundationActor>structures/fndn_3x3.xml</FoundationActor>
|
||||
|
@ -21,7 +21,7 @@
|
||||
<Obstruction>
|
||||
<Static width="28.0" depth="28.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_infantry_swordsman_b
|
||||
units/cart_infantry_javelinist_b
|
||||
@ -29,7 +29,7 @@
|
||||
units/cart_cavalry_swordsman_b
|
||||
units/cart_cavalry_spearman_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/embassy.xml</Actor>
|
||||
<FoundationActor>structures/fndn_5x5.xml</FoundationActor>
|
||||
|
@ -24,12 +24,12 @@
|
||||
<Obstruction>
|
||||
<Static width="15.0" depth="12.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_infantry_swordsman_b
|
||||
units/cart_cavalry_swordsman_2_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/embassy_celtic.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -17,13 +17,13 @@
|
||||
<Obstruction>
|
||||
<Static width="16.0" depth="16.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_infantry_javelinist_b
|
||||
units/cart_infantry_slinger_b
|
||||
units/cart_cavalry_swordsman_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/embassy_iberian.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -20,12 +20,12 @@
|
||||
<Obstruction>
|
||||
<Static width="11.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_infantry_swordsman_2_b
|
||||
units/cart_cavalry_spearman_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/embassy_italiote.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -13,7 +13,7 @@
|
||||
<Obstruction>
|
||||
<Static width="26.0" depth="28.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_hero_hamilcar
|
||||
units/cart_hero_hannibal
|
||||
@ -22,7 +22,7 @@
|
||||
units/cart_mechanical_siege_ballista
|
||||
units/cart_mechanical_siege_oxybeles
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/fortress.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -46,13 +46,13 @@
|
||||
</Sound>
|
||||
<TerritoryDecay disable=""/>
|
||||
<TerritoryInfluence disable=""/>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_ship_bireme
|
||||
units/cart_ship_trireme
|
||||
units/cart_ship_quinquereme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<Vision>
|
||||
<Range>100</Range>
|
||||
</Vision>
|
||||
|
@ -18,11 +18,11 @@
|
||||
<Obstruction>
|
||||
<Static width="17.0" depth="30.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/cart_champion_infantry
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/carthaginians/temple_big.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<Obstruction>
|
||||
<Static width="20.0" depth="20.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_infantry_spearman_b
|
||||
units/celt_infantry_javelinist_b
|
||||
@ -22,7 +22,7 @@
|
||||
units/celt_cavalry_swordsman_b
|
||||
units/celt_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/celts/barracks_new.xml</Actor>
|
||||
<FoundationActor>structures/fndn_5x5.xml</FoundationActor>
|
||||
|
@ -12,13 +12,13 @@
|
||||
<Obstruction>
|
||||
<Static width="25.0" depth="25.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_infantry_spearman_b
|
||||
units/celt_infantry_javelinist_b
|
||||
units/celt_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/celts/civic_centre.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -12,11 +12,11 @@
|
||||
<Obstruction>
|
||||
<Static width="10.0" depth="20.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_sheep
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/celts/plot_corral.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -15,11 +15,11 @@
|
||||
<Obstruction>
|
||||
<Static width="10.0" depth="22.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_ship_trireme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/celts/dock_new.xml</Actor>
|
||||
<FoundationActor>structures/fndn_celt_dock.xml</FoundationActor>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<death>attack/destruction/building_collapse_large.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_hero_boudicca
|
||||
units/celt_hero_caratacos
|
||||
@ -38,7 +38,7 @@
|
||||
units/celt_champion_infantry_brit
|
||||
units/celt_mechanical_siege_ram
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<Vision>
|
||||
<Range>100</Range>
|
||||
</Vision>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<death>attack/destruction/building_collapse_large.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_hero_brennus
|
||||
units/celt_hero_britomartus
|
||||
@ -25,7 +25,7 @@
|
||||
units/celt_champion_infantry_gaul
|
||||
units/celt_mechanical_siege_ram
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/celts/fortress_gallic.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -40,11 +40,11 @@
|
||||
<Radius>20</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/celt_war_dog_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/celts/kennel.xml</Actor>
|
||||
<FoundationActor>structures/fndn_2x2.xml</FoundationActor>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<SpecificName>Stratēgeîon</SpecificName>
|
||||
<History>The Stratigeion was the main military headquarters, where important decisions were taken and plans for battles discussed by the Hellene Generals, or "Strategoi".</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_infantry_spearman_b
|
||||
units/hele_infantry_javelinist_b
|
||||
@ -24,7 +24,7 @@
|
||||
units/hele_cavalry_swordsman_b
|
||||
units/hele_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/barracks.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -11,13 +11,13 @@
|
||||
<SpecificName>Agorā́</SpecificName>
|
||||
<History>The most important place in most Classical Greek poleis, the Agora served many purposes; it was a place for public speeches and was the stage for civic life and commercial interests.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_infantry_spearman_b
|
||||
units/hele_infantry_javelinist_b
|
||||
units/hele_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/civic_centre_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -18,11 +18,11 @@
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/corral.xml</Actor>
|
||||
<FoundationActor>structures/fndn_3x3.xml</FoundationActor>
|
||||
|
@ -11,12 +11,12 @@
|
||||
<SpecificName>Limḗn</SpecificName>
|
||||
<History>Greece is a sea country, which is why some of the greatest Hellenic and Hellenistic cities like Ephesus, Corinth, Alexandria and Antioch were built by the sea. It should also be noted that all colonies during the Great Colonisation were thriving port centres, which traded with the local population.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_ship_bireme
|
||||
units/hele_ship_trireme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/dock.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -19,13 +19,13 @@
|
||||
<Obstruction>
|
||||
<Static width="24.0" depth="26.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_mechanical_siege_oxybeles
|
||||
units/hele_mechanical_siege_lithobolos
|
||||
units/hele_mechanical_siege_tower
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/fortress_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -33,14 +33,14 @@
|
||||
<death>attack/destruction/building_collapse_large.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_champion_cavalry_mace
|
||||
units/hele_champion_infantry_mace
|
||||
units/hele_champion_infantry_polis
|
||||
units/hele_champion_swordsman_polis
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/gymnasion.xml</Actor>
|
||||
<FoundationActor>structures/fndn_6x6.xml</FoundationActor>
|
||||
|
@ -34,7 +34,7 @@
|
||||
<death>attack/destruction/building_collapse_large.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_hero_alexander
|
||||
units/hele_hero_demetrius
|
||||
@ -43,7 +43,7 @@
|
||||
units/hele_hero_themistocles
|
||||
units/hele_hero_xenophon
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/tholos.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -11,7 +11,7 @@
|
||||
<SpecificName>Cuartel</SpecificName>
|
||||
<History>To the best of our knowledge, the Iberians did not have standing armies in the sense that we know of them elsewhere or of today, it is doubtful that they had specific structures designated as military centres; however as a game construct we show a modest structure wherein military related activities take place.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/iber_infantry_spearman_b
|
||||
units/iber_infantry_swordsman_b
|
||||
@ -20,7 +20,7 @@
|
||||
units/iber_cavalry_spearman_b
|
||||
units/iber_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/iberians/barracks.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -5,13 +5,13 @@
|
||||
<SpecificName>Oppidum</SpecificName>
|
||||
<History>The Oppidum, plural Oppida (oh-PEE-dah), has a long history in the Iberian Peninsula. They were walled towns, dating back to even before the time period of the game and expanding greatly during it. They were usually built upon heights for better defensive purposes but sometimes right out on the plains, especially in the east where there may not have been heights at desirable locations near meandering rivers. This concept drawing is derived from an actual archeological site that has been excavated in the northeast of Spain having belonged to the Ilergete (ee-layer-HAY-tay) tribe as shown in the figure below and from the virtual reconstruction of the site at the museum located adjacent to it.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/iber_infantry_swordsman_b
|
||||
units/iber_infantry_javelinist_b
|
||||
units/iber_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/iberians/civil_centre.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -12,11 +12,11 @@
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/iberians/corral.xml</Actor>
|
||||
<FoundationActor>structures/fndn_4x4.xml</FoundationActor>
|
||||
|
@ -12,11 +12,11 @@
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="24.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/iber_ship_fire
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/iberians/dock.xml</Actor>
|
||||
<FoundationActor>structures/fndn_dock_iber.xml</FoundationActor>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<Obstruction>
|
||||
<Static width="27.0" depth="27.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/iber_hero_caros
|
||||
units/iber_hero_indibil
|
||||
@ -28,7 +28,7 @@
|
||||
units/iber_champion_cavalry
|
||||
units/iber_mechanical_siege_ram
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/iberians/fortress.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<SpecificName>Stratēgeîon</SpecificName>
|
||||
<History>The Stratigeion was the main military headquarters, where important decisions were taken and plans for battles discussed by the Hellene Generals, or "Strategoi".</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/mace_infantry_spearman_b
|
||||
units/mace_infantry_javelinist_b
|
||||
@ -24,7 +24,7 @@
|
||||
units/mace_cavalry_spearman_b
|
||||
units/mace_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/barracks.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -11,13 +11,13 @@
|
||||
<SpecificName>Agorā́</SpecificName>
|
||||
<History>The most important place in most Classical Greek poleis, the Agora served many purposes; it was a place for public speeches and was the stage for civic life and commercial interests.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/mace_infantry_spearman_b
|
||||
units/mace_infantry_javelinist_b
|
||||
units/mace_cavalry_spearman_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/civic_centre_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -18,11 +18,11 @@
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/corral.xml</Actor>
|
||||
<FoundationActor>structures/fndn_3x3.xml</FoundationActor>
|
||||
|
@ -11,12 +11,12 @@
|
||||
<SpecificName>Limḗn</SpecificName>
|
||||
<History>Greece is a sea country, which is why some of the greatest Hellenic and Hellenistic cities like Ephesus, Corinth, Alexandria and Antioch were built by the sea. It should also be noted that all colonies during the Great Colonisation were thriving port centres, which traded with the local population.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_ship_bireme
|
||||
units/hele_ship_trireme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/dock.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<Obstruction>
|
||||
<Static width="24.0" depth="26.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/mace_hero_philip
|
||||
units/mace_hero_alexander
|
||||
@ -30,7 +30,7 @@
|
||||
units/mace_mechanical_siege_lithobolos
|
||||
units/mace_mechanical_siege_tower
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/fortress_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -32,14 +32,14 @@
|
||||
<Root>true</Root>
|
||||
<Radius>38</Radius>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/pers_hero_cyrus
|
||||
units/pers_hero_darius
|
||||
units/pers_hero_xerxes
|
||||
units/pers_champion_infantry
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/sb1_new.xml</Actor>
|
||||
<FoundationActor>structures/fndn_6x6.xml</FoundationActor>
|
||||
|
@ -17,13 +17,13 @@
|
||||
<Tooltip>Levy citizen-infantry units.</Tooltip>
|
||||
<Icon>structures/pers_barracks.png</Icon>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/pers_infantry_spearman_b
|
||||
units/pers_infantry_javelinist_b
|
||||
units/pers_infantry_archer_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/barracks.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -12,13 +12,13 @@
|
||||
<SpecificName>Xsacapava</SpecificName>
|
||||
<History>Possibly of Median origin, the word 'satrapy' means province. Soon after coming to the throne, Darius the Great carried out a vast administrative reform, dividing the huge empire into 20 satrapies governed by satraps.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/pers_infantry_spearman_b
|
||||
units/pers_infantry_archer_b
|
||||
units/pers_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/civil_centre.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -18,11 +18,11 @@
|
||||
<Obstruction>
|
||||
<Static width="17.0" depth="11.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/corral.xml</Actor>
|
||||
<FoundationActor>structures/fndn_4x2.xml</FoundationActor>
|
||||
|
@ -18,12 +18,12 @@
|
||||
<Obstruction>
|
||||
<Static width="23.0" depth="15.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/pers_ship_bireme
|
||||
units/pers_ship_trireme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/dock.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -12,12 +12,12 @@
|
||||
<History>The Susa Chateau was a fortress in the administrative capital of Susa, which was reconstructed by a French archaeologist in 1890 with the use of original building material.</History>
|
||||
<Tooltip>Train Champion Units and Construct Siege Rams.</Tooltip>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/pers_champion_cavalry
|
||||
units/pers_mechanical_siege_ram
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/fortress.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -40,13 +40,13 @@ Train War Elephants and Kardakes mercenaries.</Tooltip>
|
||||
<Root>false</Root>
|
||||
<Radius>38</Radius>
|
||||
</TerritoryInfluence>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/pers_kardakes_hoplite
|
||||
units/pers_kardakes_skirmisher
|
||||
units/pers_war_elephant
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/sb2.xml</Actor>
|
||||
<FoundationActor>structures/fndn_6x6.xml</FoundationActor>
|
||||
|
@ -25,14 +25,14 @@
|
||||
<Obstruction>
|
||||
<Static width="18.0" depth="16.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/pers_cavalry_spearman_b
|
||||
units/pers_cavalry_swordsman_b
|
||||
units/pers_cavalry_javelinist_b
|
||||
units/pers_cavalry_archer_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/persians/stables.xml</Actor>
|
||||
<FoundationActor>structures/fndn_4x4.xml</FoundationActor>
|
||||
|
@ -68,7 +68,7 @@
|
||||
<HealthDecayRate>5</HealthDecayRate>
|
||||
</TerritoryDecay>
|
||||
<TerritoryInfluence disable=""/>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/rome_infantry_swordsman_b
|
||||
units/rome_infantry_spearman_a
|
||||
@ -78,7 +78,7 @@
|
||||
units/rome_mechanical_siege_scorpio
|
||||
units/rome_mechanical_siege_ram
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<Vision>
|
||||
<Range>60</Range>
|
||||
</Vision>
|
||||
|
@ -18,14 +18,14 @@
|
||||
<Obstruction>
|
||||
<Static width="24.0" depth="24.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/rome_infantry_swordsman_b
|
||||
units/rome_infantry_spearman_a
|
||||
units/rome_infantry_javelinist_b
|
||||
units/rome_cavalry_spearman_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_5x5.xml</FoundationActor>
|
||||
<Actor>structures/romans/barracks.xml</Actor>
|
||||
|
@ -12,13 +12,13 @@
|
||||
<Obstruction>
|
||||
<Static width="37.0" depth="37.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/rome_infantry_swordsman_b
|
||||
units/rome_infantry_javelinist_b
|
||||
units/rome_cavalry_spearman_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_8x8.xml</FoundationActor>
|
||||
<Actor>structures/romans/civic_centre.xml</Actor>
|
||||
|
@ -12,12 +12,12 @@
|
||||
<Obstruction>
|
||||
<Static width="14" depth="20"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
gaia/fauna_sheep
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_2x4.xml</FoundationActor>
|
||||
<Actor>structures/romans/corral.xml</Actor>
|
||||
|
@ -12,13 +12,13 @@
|
||||
<Obstruction>
|
||||
<Static width="20.0" depth="24.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/rome_ship_bireme
|
||||
units/rome_ship_trireme
|
||||
units/rome_ship_quinquereme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/romans/dock.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<SpecificName>Castellum</SpecificName>
|
||||
<History>Fortified auxillary camp.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/rome_hero_marcellus
|
||||
units/rome_hero_maximus
|
||||
@ -16,7 +16,7 @@
|
||||
units/rome_mechanical_siege_scorpio
|
||||
units/rome_mechanical_siege_ram
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/romans/fortress.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -15,14 +15,14 @@
|
||||
<SpecificName>Stratēgeîon</SpecificName>
|
||||
<History>The Stratigeion was the main military headquarters, where important decisions were taken and plans for battles discussed by the Hellene Generals, or "Strategoi".</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/spart_infantry_spearman_b
|
||||
units/spart_champion_infantry_sword
|
||||
units/spart_infantry_javelinist_b
|
||||
units/spart_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/barracks.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -11,13 +11,13 @@
|
||||
<SpecificName>Agorā́</SpecificName>
|
||||
<History>The most important place in most Classical Greek poleis, the Agora served many purposes; it was a place for public speeches and was the stage for civic life and commercial interests.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/spart_infantry_spearman_b
|
||||
units/spart_infantry_javelinist_b
|
||||
units/spart_cavalry_javelinist_b
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/civic_centre_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -18,11 +18,11 @@
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
gaia/fauna_goat
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/corral.xml</Actor>
|
||||
<FoundationActor>structures/fndn_3x3.xml</FoundationActor>
|
||||
|
@ -11,12 +11,12 @@
|
||||
<SpecificName>Limḗn</SpecificName>
|
||||
<History>Greece is a sea country, which is why some of the greatest Hellenic and Hellenistic cities like Ephesus, Corinth, Alexandria and Antioch were built by the sea. It should also be noted that all colonies during the Great Colonisation were thriving port centres, which traded with the local population.</History>
|
||||
</Identity>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/spart_ship_bireme
|
||||
units/spart_ship_trireme
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/dock.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -19,11 +19,11 @@
|
||||
<Obstruction>
|
||||
<Static width="24.0" depth="26.0"/>
|
||||
</Obstruction>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/spart_mechanical_siege_ram
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/fortress_new.xml</Actor>
|
||||
</VisualActor>
|
||||
|
@ -34,11 +34,11 @@
|
||||
<death>attack/destruction/building_collapse_large.xml</death>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TrainingQueue>
|
||||
<ProductionQueue>
|
||||
<Entities datatype="tokens">
|
||||
units/hele_hero_leonidas
|
||||
</Entities>
|
||||
</TrainingQueue>
|
||||
</ProductionQueue>
|
||||
<VisualActor>
|
||||
<Actor>structures/hellenes/tholos.xml</Actor>
|
||||
</VisualActor>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user