Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_skittish.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_skittish.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_skittish.xml (revision 4185)
@@ -1,20 +1,20 @@
Skittish
-
+ Skittish
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/foundation_5x5.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/foundation_5x5.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/foundation_5x5.xml (revision 4185)
@@ -1,28 +1,28 @@
Foundation 5x5Foundation24.024.08.0
- 5
+ 5structures/fndn_5x5.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit.xml (revision 4185)
@@ -1,110 +1,110 @@
template_corpseUnitPortraitSheetUnit0.54.0MeleeLoose1005.0205.05.3205.0Unit
- 6
+ 6true5.01.60.257.00.012.050.00.00.10.2true5.0
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/art/textures/ui/session/icons/sheets/tab.txt
===================================================================
--- ps/trunk/binaries/data/mods/official/art/textures/ui/session/icons/sheets/tab.txt (revision 4184)
+++ ps/trunk/binaries/data/mods/official/art/textures/ui/session/icons/sheets/tab.txt (revision 4185)
@@ -1,9 +1,9 @@
-structciv,Construct Civic Buildings,
-structmil,Construct Military Buildings,
+structCiv,Construct Civic Buildings,
+structMil,Construct Military Buildings,
train,Train,
research,Research,
barter,Barter,
allegiance,Allegiance,
selection,Selection,
garrison,Garrison,
command,Command,
Index: ps/trunk/binaries/data/mods/official/formations/BoxForm2.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/formations/BoxForm2.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/formations/BoxForm2.xml (revision 4185)
@@ -1,56 +1,56 @@
-
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_defensive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_defensive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_defensive.xml (revision 4185)
@@ -1,20 +1,20 @@
Defensive
-
+ Defensive
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_aggressive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_aggressive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_aggressive.xml (revision 4185)
@@ -1,20 +1,20 @@
Aggressive
-
+ Aggressive
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_structure_resource.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_structure_resource.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_structure_resource.xml (revision 4185)
@@ -1,25 +1,25 @@
Resource StructureResource
- 0
+ 0
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_violent.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_violent.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_violent.xml (revision 4185)
@@ -1,20 +1,20 @@
Violent
-
+ Violent
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/structures/hele_civil_centre.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/structures/hele_civil_centre.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/structures/hele_civil_centre.xml (revision 4185)
@@ -1,34 +1,34 @@
ApoikiaHellenesDuring the Great Colonisation, the Hellenes built numerous colonies all over the shores of the Mediterranean and Black Seas, which became miniature versions of Greece on foreign soil.structures/hellenes/civil_centre.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/structures/celt_barracks.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/structures/celt_barracks.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/structures/celt_barracks.xml (revision 4185)
@@ -1,22 +1,22 @@
GwersylltyCeltsI could not find any evidence of a central military structure, but I?m giving them one for gameplay reasons. Their training would have been performed in an open area, and on the battle field. They would have been housed in their home. The blacksmith would probably have been a part of the blacksmith?s home. The men were responsible for making or scavengering their own weapons.structures/celts/barracks.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_super.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_super.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_super.xml (revision 4185)
@@ -1,61 +1,61 @@
Super UnitMilitary, Super0.2
- 50
+ 50111115.00.00.50.55.0150030.00.00.50.55.0150010.00.00.50.524.01500
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_structure.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_structure.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_structure.xml (revision 4185)
@@ -1,52 +1,52 @@
StructurePortraitSheetStructure01005012.0
- 9
+ 9true12.05.00.3
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_structure_economic_farmstead.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_structure_economic_farmstead.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_structure_economic_farmstead.xml (revision 4185)
@@ -1,46 +1,46 @@
-
+Farmstead49VillageThe Farmstead is the centre for food gathering operations. Fields and Corrals must be built at lots adjacent to it.foundation_3x312.012.08.055050
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_passive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_passive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_passive.xml (revision 4185)
@@ -1,20 +1,20 @@
Passive
-
+ Passive
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/foundation_2x2.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/foundation_2x2.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/foundation_2x2.xml (revision 4185)
@@ -1,28 +1,28 @@
Foundation 2x2Foundation9.09.08.0
- 2
+ 2structures/fndn_2x2.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/units/pers_infantry_archer_b.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/units/pers_infantry_archer_b.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/units/pers_infantry_archer_b.xml (revision 4185)
@@ -1,22 +1,22 @@
Kamandar KappadokiPersiansArchers were the core of the Persian infantry, often going into battle with spearmen equipped with spara, whom they heavily outnumbered. Although powerful, the Persian recurved bow was cursed with light arrows that had trouble piercing the armor of heavily armed opponents. But the huge numbers loosed in each volley were more than enough to alarm even the most armored enemy.units/persians/infantry_archer_b.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/foundation_4x4.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/foundation_4x4.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/foundation_4x4.xml (revision 4185)
@@ -1,28 +1,28 @@
Foundation 4x4Foundation18.018.08.0
- 4
+ 4structures/fndn_4x4.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_breed_passive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_breed_passive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_breed_passive.xml (revision 4185)
@@ -1,20 +1,20 @@
Passive
-
+ Passive
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_herd_passive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_herd_passive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_herd_passive.xml (revision 4185)
@@ -1,20 +1,20 @@
Passive
-
+ Passive
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_violent.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_violent.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_violent.xml (revision 4185)
@@ -1,20 +1,20 @@
Violent
-
+ Violent
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_aggressive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_aggressive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_wild_aggressive.xml (revision 4185)
@@ -1,20 +1,20 @@
Violent
-
+ Violent
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_infantry.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_infantry.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_infantry.xml (revision 4185)
@@ -1,104 +1,104 @@
InfantryMilitary, Foot, Organic, CitizenSoldier, Worker40.00.750.250.2
- 800
+ 80011111600202020201010102.03000
-
+ 1.010001.01000
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_passive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_passive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_passive.xml (revision 4185)
@@ -1,20 +1,20 @@
Passive
-
+ Passive
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna.xml (revision 4185)
@@ -1,39 +1,39 @@
FaunaGaiaPortraitSheetAnimal0Gaia, Fauna1.02.5
- 10
+ 10Food20511516
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_hero.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_hero.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_hero.xml (revision 4185)
@@ -1,62 +1,62 @@
HeroHero, Military, Organic00.30.30.30.2
- 1000
+ 10001111Hero20.00.00.50.55.0150040.00.00.50.55.01500
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_cavalry.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_cavalry.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_cavalry.xml (revision 4185)
@@ -1,84 +1,84 @@
CavalryCavalry, Military, Mounted, Organic, CitizenSoldier, Worker40.00.750.251.57.50.230.0
- 200
+ 2001111290022.03000
-
+ 7.01.022.0500.010.0
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/foundation_1x1.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/foundation_1x1.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/foundation_1x1.xml (revision 4185)
@@ -1,28 +1,28 @@
Foundation 1x1Foundation6.06.08.0
- 2
+ 2structures/fndn_1x1.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/foundation_3x3.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/foundation_3x3.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/foundation_3x3.xml (revision 4185)
@@ -1,28 +1,28 @@
Foundation 3x3Foundation12.012.08.0
- 3
+ 3structures/fndn_3x3.xml
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_support.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_support.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_support.xml (revision 4185)
@@ -1,38 +1,38 @@
SupportSupport, Organic0.2
- 10
+ 101111Support1
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_defensive.xml
===================================================================
--- ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_defensive.xml (revision 4184)
+++ ps/trunk/binaries/data/mods/official/entities/template_unit_fauna_hunt_defensive.xml (revision 4185)
@@ -1,20 +1,20 @@
Defensive
-
+ Defensive
-
+
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/official/gui/test/functions_page_session_status_commands.js
===================================================================
--- ps/trunk/binaries/data/mods/official/gui/test/functions_page_session_status_commands.js (revision 4184)
+++ ps/trunk/binaries/data/mods/official/gui/test/functions_page_session_status_commands.js (revision 4185)
@@ -1,1133 +1,1133 @@
/*
DESCRIPTION : Functions for the Command Buttons orbitting the Status Pane.
NOTES :
*/
// ====================================================================
function defineCommandButtons(command)
{
snStatusPaneCommand = new Array();
snStatusPaneCommand.tab = new Object();
snStatusPaneCommand.list = new Object();
snStatusPaneCommand.button = new Object();
// Maximum number of buttons (either single or lists).
snStatusPaneCommand.tab.max = command.substring (command.lastIndexOf ("d")+1, command.lastIndexOf ("_")); // 8
// Maximum number of entries in a list.
snStatusPaneCommand.list.max = command.substring (command.lastIndexOf ("_")+1, command.length); // 12
// When we reach this button, split the rows (remainder are vertical, not horizontal).
snStatusPaneCommand.split = 5;
// Spacing between lists.
snStatusPaneCommand.span = 1;
// Get the coordinates of the Status Pane background (referenced to determine command button locations).
var currCrd = getCrd ("snStatusPaneBkg");
// Update each tab.
for (var tabLoop = 1; tabLoop <= snStatusPaneCommand.tab.max; tabLoop++)
{
var tempGroupObject = getGUIObjectByName("snStatusPaneCommand" +
"Group" + tabLoop);
// Update each list under each tab.
for (var listLoop = 1; listLoop <= snStatusPaneCommand.list.max; listLoop++)
{
var tempListObject = getGUIObjectByName("snStatusPaneCommand" +
tabLoop + "_" + listLoop);
// Set default portrait.
setPortrait (tempListObject.name, "IconPortrait");
// Width and height of buttons is always the same.
var buttonWidth = snConst.Portrait.Sml.Width;
var buttonHeight = snConst.Portrait.Sml.Height;
// If we're doing the arc of commands.
if (tabLoop >= snStatusPaneCommand.split)
{
if (listLoop == 1)
{
if (tabLoop > snStatusPaneCommand.split)
{
// Get the first tab.
var firstTab = getCrd ("snStatusPaneCommand" + (snStatusPaneCommand.split) + "_" + listLoop);
// Get the previous tab.
var lastTab = getCrd ("snStatusPaneCommand" + (tabLoop-1) + "_" + listLoop);
}
// Set position of tab (it curves, so we need to specifically set each button position).
switch (tabLoop)
{
case (snStatusPaneCommand.split):
var buttonX = currCrd.coord[rb].x + currCrd.coord[rb].width - buttonWidth + 2;
var buttonY = currCrd.coord[rb].y + currCrd.coord[rb].height - buttonHeight - 3.3;
break;
case (snStatusPaneCommand.split+1):
var buttonX = lastTab.coord[rb].x + (lastTab.coord[rb].width/1.7);
var buttonY = lastTab.coord[rb].y - (lastTab.coord[rb].height/1.3);
break;
case (snStatusPaneCommand.split+2):
var buttonX = firstTab.coord[rb].x - (buttonWidth / 1.5);
var buttonY = firstTab.coord[rb].y + (buttonHeight / 1.5);
break;
default:
var buttonX = getCrd ("snStatusPaneCommand" + (snStatusPaneCommand.split+1) + "_" + listLoop).coord[rb].x;
var buttonY = currCrd.coord[rb].y + 3;
break;
}
// Set default portrait.
setPortrait (tempListObject.name, "IconCommand");
}
else
{
var parentTab = getCrd ("snStatusPaneCommand" + (tabLoop) + "_" + (listLoop - 1));
// Set position of buttons under tab (parallel row to the right of it).
var buttonX = parentTab.coord[rb].x+parentTab.coord[rb].width
+snStatusPaneCommand.span;
var buttonY = parentTab.coord[rb].y;
}
var barX = buttonX;
var barY = 0;
}
else // If we're doing the row of tabs,
{
// Set position of tab.
if (listLoop == 1)
{
if (tabLoop == 1 && listLoop == 1)
var buttonX = currCrd.coord[rb].x;
else
var buttonX = Crd[Crd.last].coord[rb].x+Crd[Crd.last].coord[rb].width
+snStatusPaneCommand.span;
var buttonY = currCrd.coord[rb].y+currCrd.coord[rb].height-7;
}
else // Set position of buttons under tab.
{
var buttonX = Crd[Crd.last].coord[rb].x;
var buttonY = Crd[Crd.last].coord[rb].y+Crd[Crd.last].coord[rb].height
+snStatusPaneCommand.span;
}
}
// Define dimensions of list buttons.
addCrds ("snStatusPaneCommand" + tabLoop + "_" + listLoop, 0, 100, buttonX, buttonY,
buttonWidth, buttonHeight);
// If we're defining the last button in the list, and it's not a tab,
if (tabLoop == snStatusPaneCommand.tab.max && listLoop != 1)
{
// It has health bars (for the current selection). Set them up too.
addCrds ("snStatusPaneCommand" + tabLoop + "_" + listLoop + "Bar", 0, 100, barX, barY,
buttonWidth, 4);
getGUIObjectByName ("snStatusPaneCommand" + tabLoop + "_" + listLoop + "Bar").hidden = false;
getGUIObjectByName ("snStatusPaneCommand" + tabLoop + "_" + listLoop + "Bar").caption = 100;
}
/*
// Store indexes to the button for easy future reference.
Crd[getCrd ("snStatusPaneCommand" + tabLoop + "_" + listLoop, true)].tab = tabLoop;
Crd[getCrd ("snStatusPaneCommand" + tabLoop + "_" + listLoop, true)].list = listLoop;
// Create quantity and entity for create lists for future use.
Crd[getCrd ("snStatusPaneCommand" + tabLoop + "_" + listLoop, true)].entity = "";
Crd[getCrd ("snStatusPaneCommand" + tabLoop + "_" + listLoop, true)].quantity = 0;
*/
}
}
}
// ====================================================================
function updateTab (tab, type, cellSheet, attribute, attribute2, arrayCells)
{
// Refresh the next tab (and any buttons in its list, if appropriate), according to tabCounter.
// tab: The name of the tab (eg research).
// type: The type of button:
// * production (Tab button, persistent list of buttons, click them to do things.)
// * pick (Tab button holds current selection, click a button from the list to select a new option and close the tab.)
// * command (Click "tab" button to do something; no list.)
// cellSheet: * The cell sheet (icon group) containing this item's tab icon.
// * production: Tends to be "Tab" (since they need a special tab icon), so defaults to this.
// * pick: Usually a group that's the same as the tab name, so use this.
// * command: Tends to be "Command" (though not always), so defaults to this.
// * If no value is specified, it will use the above default for the type.
// attribute: * For production & pick: the attribute containing the list of items to display (eg selection[0].actions.create.list.research), which this entity must
// have in order to update the tab with this list.
// * For command: the attribute that the entity must have in order to have this command.
// * If no value is specified, all entities have this attribute.
// attribute2: * For pick: The variable used to store the current item in the pick that has been selected -- placed in the tab (eg selection[0].actions.formation.curr)
// arrayCells: * Optional attribute. If true, assume that items cannot all be found in the same cellSheet; check in attribute.sheet for cell sheet name for each item.
// Store string form of attribute for future reference.
attributeString = attribute;
attribute2String = attribute2;
// If either attribute is not a valid property, return false.
if ( (validProperty (attributeString)) && (validProperty (attribute2String)) )
{
// Use the value of the attribute from this point forwards.
attribute = eval (attribute);
attribute2 = eval (attribute2);
// String properties taken from entities can be a little confused about their variable type, so make sure they're considered strings.
attribute2 = String (attribute2);
//console.write ("1st: " + tabCounter + " " + tab + " " + type + " " + cellSheet + " " + attribute + " " + attribute2);
// Set default values.
if (cellSheet == "")
{
switch (type)
{
case "command":
cellSheet = "Command";
break;
case "pick":
cellSheet = toTitleCase(tab);
tab = attribute2.toLowerCase();
// avoid, pick, Stance, .traits.ai.stance.list
break;
default:
cellSheet = "Tab";
break;
}
}
//console.write ("2nd: " + tabCounter + " " + tab + " " + type + " " + cellSheet + " " + attribute + " " + attribute2);
// Get tab.
var tabObject = getGUIObjectByName ("snStatusPaneCommand" + tabCounter + "_1");
// Enable tab.
guiUnHide (tabObject.name);
// Set tab portrait.
switch (tab)
{
case "selection":
case "garrison":
// Temporarily (until we have the tab textures) force a particular cell ID for selection and garrison so we can see the icon.
cellGroup[cellSheet][tab].id = 2;
// Use the horizontal tab. (Extends right.)
setPortrait ("snStatusPaneCommand" + tabCounter + "_1", "IconSheet", cellSheet + "Button_H", cellGroup[cellSheet][tab].id);
break;
default:
if (type == "pick" && cellSheet != "Tab") // Force the tab background for pick lists,
setPortrait ("snStatusPaneCommand" + tabCounter + "_1", "IconSheet", cellSheet + "TabButton", cellGroup[cellSheet][tab].id);
- else // Use the vertical tab. (Extends up).
+ else
setPortrait ("snStatusPaneCommand" + tabCounter + "_1", "IconSheet", cellSheet + "Button", cellGroup[cellSheet][tab].id);
break;
}
//console.write ("3rd: " + "snStatusPaneCommand" + tabCounter + "_1" + "|" + "IconSheet" + "|" + cellSheet + "Button" + "|" + cellGroup[cellSheet][tab].id);
switch (type)
{
case "command":
// Set tab tooltip.
var tooltip = cellGroup[cellSheet][tab].name;
tooltip += " " + cellSheet;
break;
case "pick":
// Set tab tooltip.
var tooltip = cellSheet;
tooltip += " List\nCurrent " + cellSheet + ": " + cellGroup[cellSheet][tab].name;
break;
default:
// Set tab tooltip.
var tooltip = cellGroup[cellSheet][tab].name;
tooltip += " " + cellSheet;
break;
}
tabObject.tooltip = tooltip;
switch (type)
{
case "command":
// An array of actions for the command "tabs" can be triggered by clicking the individual commands. We'll set them here.
switch(tab)
{
case "rally":
tabObject.onPress = function (event)
{
setCursor("cursor-rally");
}
break;
default:
tabObject.onPress = function (event)
{
}
break;
}
break;
default:
// Set tab function when user moves mouse over tab.
tabObject.onMouseEnter = function (event)
{
var currTab = this.name.substring (this.name.lastIndexOf ("d")+1, this.name.lastIndexOf ("_"));
// Seek through all list tabs. (Only the one being hovered should stay open.)
for (var i = 1; i < snStatusPaneCommand.split; i++)
{
// If we've found the current tab,
if (i == currTab)
{
// Click the tab button to toggle visibility of its list.
guiToggle ( "snStatusPaneCommandGroup" + currTab );
}
else // If it's another list tab,
{
// Ensure this tab is hidden.
guiHide ("snStatusPaneCommandGroup" + i);
}
}
}
// Set tab function when user clicks tab.
tabObject.onPress = function (event)
{
// Click the tab button to toggle visibility of its list.
guiToggle ( "snStatusPaneCommandGroup" + this.name.substring (this.name.lastIndexOf ("d")+1, this.name.lastIndexOf ("_")) );
}
break;
}
// Get group.
var groupObject = getGUIObjectByName ("snStatusPaneCommand" + "Group" + tabCounter);
// If the list hasn't been hidden (tab is open), and it should have list items (it's not merely a command),
if ( type != "command" && attribute != undefined )
{
// Reset array.
var listArray = [];
// Insert blank array element at location 0 (to buffer array so items are in 1..n range).
listArray[0] = "";
// Extract item list into an array.
if (!attribute.length)
{ // If it's a list where each element is a value, (entity list)
for ( var i in attribute )
{
listArray[listArray.length] = i;
// Store any current quantity in the queue for this object.
// if (attribute[i].quantity)
// listArray[listArray.length].quantity = attribute[i].quantity;
}
}
else
{ // If it's a list where each element is part of a numbered array, (array list)
for ( var i = 0; i < attribute.length; i++ )
{
listArray[listArray.length] = attribute[i];
// If cell sheet for each item is stored in attribute.sheet, transfer that across too.
if (arrayCells == true)
{
console.write (attribute[i].sheet);
listArray[listArray.length].sheet = new Object(attribute[i].sheet);
console.write (listArray[listArray.length].sheet);
}
// Store any current quantity in the queue for this object.
// if (attribute[i].quantity)
// listArray[listArray.length].quantity = attribute[i].quantity;
}
}
// Populate the buttons in this tab's list.
for (var createLoop = 1; createLoop < snStatusPaneCommand.list.max; createLoop++)
{
// (Skip research list for the moment, since we don't have any portraits for techs. But we should remove the research condition later.)
if (createLoop < listArray.length && type != "command")
{
// Get name of current button.
var listObject = getGUIObjectByName ("snStatusPaneCommand" + tabCounter + "_" + (createLoop+1));
switch (type)
{
case "pick":
// Set tooltip.
if (arrayCells == true) // Check a .sheet property for each item in the list to know its cellsheet.
listObject.tooltip = cellGroup[listArray[createLoop].sheet][listArray[createLoop]].name;
else // Assume each item uses the same cell sheet as the tab.
listObject.tooltip = cellGroup[cellSheet][listArray[createLoop]].name;
// Set portrait.
setPortrait (listObject.name,
"IconSheet", cellSheet + "Button", cellGroup[cellSheet][listArray[createLoop]].id);
// Store properties which we will need when doing the press event as a comma-delimited string.
// * 0 Name of variable that contains the ".curr" value.
// * 1 String name of current button's content.
// * 2 Array list of tab items.
// * 3 Name of tab (eg stance).
// * 4 Tab number.
// (Storing the properties in the hotkey is an evil hack, and we'll eventually need the hotkey, but since new properties cannot be
// attached to controls, there doesn't seem to be much choice ... Maybe add a .container property for situations such as this?)
listObject.hotkey = attribute2String + "," + listArray[createLoop] + "," + attributeString + "," + cellSheet.toLowerCase() + "," + tabCounter + ",";
// console.write (listObject.caption);
// Set item function.
listObject.onPress = function (event)
{
// Extract the properties from the comma-delimited string.
var tempArray = parseDelimiterString (this.hotkey, ",");
// Set .curr property to refer to this item.
// console.write (tempArray[0]);
// console.write (tempArray[1]);
tempStr = tempArray[0] + " = '" + tempArray[1] + "'";
eval(tempStr);
// console.write (tempArray[0]);
// console.write (tempArray[1]);
// console.write (selection[0].traits.ai.stance.curr);
// Refresh tab so it displays the new .curr sprite.
tabCounter = tempArray[4];
updateTab (tempArray[3], "pick", "", tempArray[2], tempArray[0]);
// Do case-specific stuff depending on name of tab.
switch (tempArray[3])
{
case "formation":
// Remove the selected units from any existing formation.
removeFromFormation (selection);
// Set the selected entities to the chosen formation name.
createEntityFormation (selection, tempArray[0]);
// If the player is choosing to "disband" the formation (disable "batallion mode"),
if ( tempArray[0] == "Loose" && isFormationLocked (selection) )
{
// Unlock the formation so each entity can be individually selected.
lockEntityFormation (false);
}
else
{
// Lock the formation so the selection can be controlled as one unit.
lockEntityFormation (true);
}
break;
default:
break;
}
}
break;
default:
switch (tab)
{
case "selection":
case "garrison":
case "research":
// Get name of item to display in list.
// (These already know the full name of the entity tag, so we don't prefix it.)
var itemName = listArray[createLoop];
break;
default:
// Get name of item to display in list.
var itemName = selection[0].traits.id.civ_code + "_" + listArray[createLoop];
break;
}
//console.write ("4th: " + "snStatusPaneCommand" + tabCounter + "_1" + "|" + "IconSheet" + "|" + cellSheet + "Button" + "|" + createLoop + "|" + listArray[createLoop]);
switch (tab)
{
case "research":
// Store name of tech to display in list in this button's coordinate.
Crd[getCrd (listObject.name, true)].entity = getTechnology(itemName, selection[0].player);
// Set tooltip.
listObject.tooltip = Crd[getCrd (listObject.name, true)].entity.generic + " (" + Crd[getCrd (listObject.name, true)].entity.specific + ")";
// Set portrait.
setPortrait (listObject.name,
Crd[getCrd (listObject.name, true)].entity.icon,
"Button",
Crd[getCrd (listObject.name, true)].entity.icon_cell);
break;
default:
// Store name of entity to display in list in this button's coordinate.
Crd[getCrd (listObject.name, true)].entity = new Object(itemName);
// Set tooltip.
listObject.tooltip = getEntityTemplate(itemName).traits.id.civ + " " + getEntityTemplate(itemName).traits.id.generic;
// Set portrait.
setPortrait (listObject.name,
getEntityTemplate(itemName).traits.id.icon,
"Button" + toTitleCase(selection[0].traits.id.civ_code),
getEntityTemplate(itemName).traits.id.icon_cell);
break;
}
// Set item function.
listObject.onPress = function (event)
{
switch (tab)
{
case "train":
// TODO: Remove this item from the production queue if right-clicked.
- issueCommand(selection, NMT_Produce, PRODUCTION_TRAIN, ""+(Crd[getCrd (this.name, true)].entity));
+ issueCommand(selection, NMT_Produce, PRODUCTION_TRAiN, ""+(Crd[getCrd (this.name, true)].entity));
case "research":
// TODO: Remove this item from the production queue if right-clicked.
issueCommand(selection, NMT_Produce, PRODUCTION_RESEARCH, ""+(Crd[getCrd (this.name, true)].entity.name));
break;
case "barter":
// Buy a quantity of this resource if left-clicked.
// Sell a quantity of this resource if right-clicked.
break;
- case "structciv":
- case "structmil":
+ case "structCiv":
+ case "structMil":
// Select building placement cursor.
tryConstruction( Crd[getCrd (this.name, true)].entity );
break;
case "garrison":
// Remove this item from the entity's garrison inventory.
break;
case "selection":
// Change the selection to this unit.
break;
default:
break;
}
}
break;
}
// Create quantity container in entity's create list if necessary.
// if (!attribute[listArray[createLoop]].quantity)
// attribute[listArray[createLoop]].quantity = new Object(0);
// Set caption to counter.
// if (attribute[listArray[createLoop]].quantity > 1)
// listObject.caption = attribute[listArray[createLoop]].quantity-1;
// Store pointer to quantity in coordinate.
// Crd[getCrd (listObject.name, true)].quantity = new Object(attribute[listArray[createLoop]].quantity);
// Reveal portrait.
guiUnHide (listObject.name);
//
// // Set function that occurs when the button is pressed (left-clicked).
// // (Somehow, we also need to do one for right-clicking -- decrement counter and remove item from queue.)
// listObject.onPress = function (event)
// {
// switch (tab)
// {
// case "StructCiv":
// case "StructMil":
// // Create building placement cursor.
// startPlacing (Crd[getCrd (this.name, true)].entity);
// break;
// default:
// // Attempt to add the entry to the queue.
// if (attemptAddToBuildQueue (selection[0], Crd[getCrd (this.name, true)].entity, Crd[getCrd (this.name, true)].tab, Crd[getCrd (this.name, true)].list))
// if (attemptAddToBuildQueue (selection[0], itemName, tab, list))
// {
//// // Create quantity container in entity's create list if necessary.
// // if (!attribute[Crd[getCrd (this.name, true)].list].quantity)
// // attribute[Crd[getCrd (this.name, true)].list].quantity = new Object(0);
// // Increment counter.
// attribute[Crd[getCrd (this.name, true)].list].quantity++;
// // Set caption to counter.
// if (attribute[Crd[getCrd (this.name, true)].list].quantity > 1)
// this.caption = attribute[Crd[getCrd (this.name, true)].list].quantity-1;
//
// console.write (this.caption);
// }
// break;
// }
// }
//
}
else
{
// Conceal this button.
guiHide ("snStatusPaneCommand" + tabCounter + "_" + parseInt(createLoop+1));
// Ensure it doesn't have a stored entity to display in list.
// Crd[getCrd ("snStatusPaneCommand" + tabCounter + "_" + parseInt(createLoop+1), true)].entity = "";
// Ensure it doesn't have a stored quantity of queued items.
// Crd[getCrd ("snStatusPaneCommand" + tabCounter + "_" + parseInt(createLoop+1), true)].quantity = 0;
}
}
}
// Default the list to closed.
groupObject.hidden = true;
tabCounter++;
// Completed tab update successfully.
return true;
}
// Invalid property ... Cancel tab update.
return false;
}
// ====================================================================
function tryConstruction( name )
{
// Start the building placement cursor for the given template, if we have enough resources
var result = checkEntityReqs( localPlayer, getEntityTemplate( name ) );
if (result == true) // If the entry meets requirements to be built (i.e. resources)
{
// Show the building placement cursor
startPlacing( name );
}
else
{
// If not, output the error message.
console.write( result );
}
}
// ====================================================================
function refreshCommandButtons()
{
// Reset button counter.
tabCounter = 1;
if ( selectionChanged )
{
// Update production lists (both types of Construction, Train). (Tab button, persistent buttons, click them to do things.)
if (validProperty ("selection[0].actions.create.list"))
{
listRoot = selection[0].actions.create.list;
for (listTab in listRoot)
{
if (listTab != "research") // Do research later.
updateTab (listTab, "production", "Tab", "listRoot[listTab]", "");
}
}
// Update Barter. (Tab button, persistent buttons, click them to do things.)
updateTab ("barter", "production", "Tab", "selection[0].actions.barter.list");
// Update pick lists (formation, stance, trade). (Tab button holds current selection, click a button to select a new option and close the tab.)
updateTab ("formation", "pick", "", "selection[0].traits.formation.list", "selection[0].traits.formation.curr");
updateTab ("stance", "pick", "", "selection[0].traits.ai.stance.list", "selection[0].traits.ai.stance.curr");
updateTab ("trade", "pick", "", "selection[0].actions.trade.list", "selection[0].actions.trade.curr");
updateTab ("gate", "pick", "", "selection[0].actions.gate.list", "selection[0].actions.gate.curr");
updateTab ("weapon", "pick", "", "selection[0].actions.attack", "selection[0].actions.attack.curr");
/*
// Update Allegiance. (Tab button, persistent buttons (though unit will be lost as soon as option selected), click them to do things.)
tempArray = new Array ("kill", "food");
tempArray.sheet = new Object();
tempArray.sheet = new Array ("Command", "Resource");
console.write(tempArray[1].sheet);
updateTab ("allegiance", "pick", "Tab", "", "", true);
*/
// Update research. (Tab button, persistent buttons, click them to do things.)
updateTab ("research", "production", "", "selection[0].actions.create.list.research", "");
// End of production and pick lists. Store end position.
var listCounter = tabCounter;
// Commands begin from this point.
tabCounter = snStatusPaneCommand.split;
// Update commands. (Click "tab" button to do something; no list.)
updateTab ("patrol", "command", "", "selection[0].actions.patrol", "");
- updateTab ("townbell", "command", "", "selection[0].actions.townbell", "");
+ updateTab ("townbell", "command", "", "selection[0].actions.townBell", "");
updateTab ("rally", "command", "", "selection[0].actions.create.rally", "");
updateTab ("explore", "command", "", "selection[0].actions.explore", "");
updateTab ("retreat", "command", "", "selection[0].actions.retreat", "");
updateTab ("stop", "command", "", "selection[0].actions.stop", "");
// End of commands. Store end position.
var commandCounter = tabCounter;
}
else
{
// Ensure tabs should be cleanable.
listCounter = 1;
commandCounter = snStatusPaneCommand.split-1;
}
if ( selectionChanged )
{
// Clear remaining buttons between the lists and commands.
for (var commandClearLoop = listCounter; commandClearLoop <= snStatusPaneCommand.split-1; commandClearLoop++)
{
guiHide ("snStatusPaneCommand" + commandClearLoop + "_1");
// If this slot could possibly contain a list, hide that too.
guiHide ("snStatusPaneCommand" + "Group" + commandClearLoop);
}
for (var commandClearLoop = commandCounter; commandClearLoop <= snStatusPaneCommand.tab.max; commandClearLoop++)
{
guiHide ("snStatusPaneCommand" + commandClearLoop + "_1");
// If this slot could possibly contain a list, hide that too.
guiHide ("snStatusPaneCommand" + "Group" + commandClearLoop);
}
}
if ( selectionChanged )
{
// Do the selection/garrison list.
tabCounter = snStatusPaneCommand.tab.max;
// If there are entities garrisoned in the current entity,
if (selection[0].traits.garrison && selection[0].traits.garrison.curr > 0)
{
updateTab ("garrison", "production", "Tab", "selection", "");
}
else
{
// If more than one entity is selected, list them.
if (selection.length > 1)
{
tempArray = [];
for ( var i = 0; i < selection.length; i++ )
tempArray[i] = selection[i].tag;
tempArray.length = i;
updateTab ("selection", "production", "Tab", "tempArray", "");
}
}
}
}
// ====================================================================
// ====================================================================
/*
function updateList (listTab, listGroup)
{
// If any items in the list (construction, train, research, etc, need to be updated, update that list.)
if ( shouldUpdateStat (listGroup) )
{
// Get tab.
tabObject = getGUIObjectByName ("snStatusPaneCommand" + listCounter + "_1");
groupObject = getGUIObjectByName ("snStatusPaneCommand" + "Group" + listCounter);
// Enable tab.
guiUnHide (tabObject.name);
// Set tab portrait.
setPortrait ("snStatusPaneCommand" + listCounter + "_1", "IconSheet", "TabButton", cellGroup["Tab"][listTab].id);
tooltip = cellGroup["Tab"][listTab].name;
tooltip += " Tab";
tabObject.tooltip = tooltip;
// Set tab function.
tabObject.onPress = function (event)
{
// Click the tab button to toggle visibility of its list.
guiToggle ( "snStatusPaneCommandGroup" + this.name.substring (this.name.lastIndexOf ("d")+1, this.name.lastIndexOf ("_")) );
}
// If the list hasn't been hidden (tab is open),
if ( groupObject.hidden == false )
{
// Extract entity list into an array.
listArray = [];
for ( i in listGroup )
{
listArray[listArray.length] = i;
// Store any current quantity in the queue for this object.
// if (listGroup[i].quantity)
// listArray[listArray.length].quantity = listGroup[i].quantity;
}
// Populate the buttons in this tab's list.
for (createLoop = 1; createLoop < snStatusPaneCommand.list.max; createLoop++)
{
if (createLoop < listArray.length)
{
// Get name of current button.
listObject = getGUIObjectByName ("snStatusPaneCommand" + listCounter + "_" + (createLoop+1));
// Get name of entity to display in list.
updateListEntityName = selection[0].traits.id.civ_code + "_" + listArray[createLoop];
// Store name of entity to display in list in this button's coordinate.
// Crd[getCrd (listObject.name, true)].entity = new Object(updateListEntityName);
// Set tooltip.
listObject.tooltip = getEntityTemplate(updateListEntityName).traits.id.civ + " " + getEntityTemplate(updateListEntityName).traits.id.generic;
// Create quantity container in entity's create list if necessary.
// if (!listGroup[listArray[createLoop]].quantity)
// listGroup[listArray[createLoop]].quantity = new Object(0);
// Set caption to counter.
// if (listGroup[listArray[createLoop]].quantity > 1)
// listObject.caption = listGroup[listArray[createLoop]].quantity-1;
// Store pointer to quantity in coordinate.
// Crd[getCrd (listObject.name, true)].quantity = new Object(listGroup[listArray[createLoop]].quantity);
// Set portrait.
switch (listTab)
{
case "research":
// Skip research list for the moment, since we don't have any portraits for techs.
break;
default:
setPortrait (listObject.name,
getEntityTemplate(updateListEntityName).traits.id.icon,
toTitleCase(selection[0].traits.id.civ_code),
getEntityTemplate(updateListEntityName).traits.id.icon_cell);
break;
}
// Reveal portrait.
guiUnHide (listObject.name);
*/
/*
// Set function that occurs when the button is pressed (left-clicked).
// (Somehow, we also need to do one for right-clicking -- decrement counter and remove item from queue.)
listObject.onPress = function (event)
{
switch (listTab)
{
case "StructCiv":
case "StructMil":
// Create building placement cursor.
startPlacing (Crd[getCrd (this.name, true)].entity);
break;
default:
// Attempt to add the entry to the queue.
if (attemptAddToBuildQueue (selection[0], Crd[getCrd (this.name, true)].entity, Crd[getCrd (this.name, true)].tab, Crd[getCrd (this.name, true)].list))
// if (attemptAddToBuildQueue (selection[0], updateListEntityName, tab, list))
{
// // Create quantity container in entity's create list if necessary.
// if (!listGroup[Crd[getCrd (this.name, true)].list].quantity)
// listGroup[Crd[getCrd (this.name, true)].list].quantity = new Object(0);
// Increment counter.
listGroup[Crd[getCrd (this.name, true)].list].quantity++;
// Set caption to counter.
if (listGroup[Crd[getCrd (this.name, true)].list].quantity > 1)
this.caption = listGroup[Crd[getCrd (this.name, true)].list].quantity-1;
console.write (this.caption);
}
break;
}
}
*/
/*
}
else
{
// Conceal this button.
guiHide ("snStatusPaneCommand" + listCounter + "_" + parseInt(createLoop+1));
// Ensure it doesn't have a stored entity to display in list.
// Crd[getCrd ("snStatusPaneCommand" + listCounter + "_" + parseInt(createLoop+1), true)].entity = "";
// Ensure it doesn't have a stored quantity of queued items.
// Crd[getCrd ("snStatusPaneCommand" + listCounter + "_" + parseInt(createLoop+1), true)].quantity = 0;
}
}
}
listCounter++;
}
}
*/
// ====================================================================
/*
function refreshCommandButtons()
{
// Set start of tabs.
listCounter = 1;
if ( selection[0].actions && shouldUpdateStat( "actions" ) )
{
// Update production lists (Construction, Train, Barter). (Tab button, persistent buttons, click them to do things.)
if ( shouldUpdateStat( "actions.create" ) && shouldUpdateStat( "actions.create.list" ) )
{
// Everything in this block is tied to properties in
// actions.create.list, the above check should limit the
// number of times this update is needlessly made.
// Get train/research/build lists by seeking through entity's creation list.
listRoot = selection[0].actions.create.list;
for (listTab in listRoot)
{
// Note: This check indicates the production lists are updated twice on some occasions. I don't know why (error from shouldUpdate()?).
console.write (listTab + " " + listRoot[listTab]);
if (listTab != "research") // Do research later.
updateList (listTab, listRoot[listTab]);
}
}
// Update selection lists (formation, stance, trade). (Tab button holds current selection, click a button to select a new option and close the tab.)
// Update research production list (which should always go last after all the other lists).
if ( shouldUpdateStat( "actions.create" ) && shouldUpdateStat( "actions.create.list" )
&& shouldUpdateStat( "actions.create.list.research" ) && selection[0].actions.create.list.research )
{
updateList ("research", selection[0].actions.create.list.research);
}
// Update commands. (Click "tab" button to do something; no list).
*/
// This whole section needs to be rewritten (now list of XML attributes instead of semicolon-delimited string).
/*
unitArray = UpdateList(action_tab_train, listCounter); if (unitArray != 0) listCounter++;
structcivArray = UpdateList(action_tab_buildciv, listCounter); if (structcivArray != 0) listCounter++;
structmilArray = UpdateList(action_tab_buildmil, listCounter); if (structmilArray != 0) listCounter++;
techArray = UpdateList(action_tab_research, listCounter); if (techArray != 0) listCounter++;
formationArray = UpdateList(action_tab_formation, listCounter); if (formationArray != 0) listCounter++;
stanceArray = UpdateList(action_tab_stance, listCounter); if (stanceArray != 0) listCounter++;
*/
/*
if ( shouldUpdateStat( "actions" ) )
{
// Update commands.
commandCounter = snStatusPaneCommand.tab.max;
*/
/*
commandCounter = UpdateCommand(cellGroup["Command"]["attack"].id, commandCounter);
commandCounter = UpdateCommand(cellGroup["Command"]["patrol"].id, commandCounter);
commandCounter = UpdateCommand(cellGroup["Command"]["repair"].id, commandCounter);
commandCounter = UpdateCommand(cellGroup["Gather"]["food"].id, commandCounter);
commandCounter = UpdateCommand(cellGroup["Gather"]["wood"].id, commandCounter);
commandCounter = UpdateCommand(cellGroup["Gather"]["stone"].id, commandCounter);
commandCounter = UpdateCommand(cellGroup["Gather"]["ore"].id, commandCounter);
*/
/*
}
}
if (listCounter > 0 && commandCounter > 0)
{
// Clear remaining buttons between them.
for (commandClearLoop = listCounter; commandClearLoop <= commandCounter; commandClearLoop++)
{
guiHide ("snStatusPaneCommand" + commandClearLoop + "_1");
// If this slot could possibly contain a list, hide that too.
guiHide ("snStatusPaneCommand" + "Group" + commandClearLoop);
}
}
*/
/*
// Update production queue.
GUIObject = getGUIObjectByName("snStatusPaneCommandProgress");
// If the entity has a production item underway,
if ( selection[0].actions.create && selection[0].actions.create.progress
&& selection[0].actions.create.progress.valueOf()
&& selection[0].actions.create.progress.valueOf().current
&& selection[0].actions.create.queue.valueOf()
&& selection[0].actions.create.queue.valueOf()[0].traits.creation.time )
{
// Set the value of the production progress bar.
GUIObject.caption = ((Math.round(Math.round(selection[0].actions.create.progress.valueOf().current)) * 100 ) / Math.round(selection[0].actions.create.queue.valueOf()[0].traits.creation.time));
// Set position of progress bar.
GUIObject.size = getGUIObjectByName("snStatusPaneCommand" + selection[0].actions.create.queue.valueOf()[0].tab + "_" + selection[0].actions.create.queue.valueOf()[0].list).size;
// Set progress bar tooltip.
// GUIObject.tooltip = "Training " + selection[0].actions.create.queue.valueOf()[0].traits.id.generic + " ... " + (Math.round(selection[0].actions.create.queue.valueOf()[0].traits.creation.time-Math.round(selection[0].actions.create.progress.valueOf().current)) + " seconds remaining.";
// Reveal progressbar.
GUIObject.hidden = false;
// Seek through queue.
for( queueEntry = 0; queueEntry < selection[0].actions.create.queue.valueOf().length; queueEntry++)
{
// Update list buttons so that they match the number of entries of that type in the queue.
getGUIObjectByName("snStatusPaneCommand" + selection[0].actions.create.queue.valueOf()[queueEntry].tab + "_" + selection[0].actions.create.queue.valueOf()[queueEntry].list).caption++;
}
}
else
{
// Hide the progress bar.
GUIObject.hidden = true;
GUIObject.tooltip = "";
}
*/
//}
// ====================================================================
/*
function UpdateCommand(listIcon, listCounter)
{
// Similar to UpdateList, but without the list.
// Updates a particular command button with a particular action.
if ( selection[0].actions && (
(listIcon == action_attack && selection[0].actions.attack)
|| (listIcon == action_patrol && selection[0].actions.patrol)
|| (listIcon == action_repair && selection[0].actions.repair)
|| (listIcon == action_gather_food && selection[0].actions.gather
&& selection[0].actions.gather.resource && selection[0].actions.gather.resource.food)
|| (listIcon == action_gather_wood && selection[0].actions.gather
&& selection[0].actions.gather.resource && selection[0].actions.gather.resource.wood)
|| (listIcon == action_gather_stone && selection[0].actions.gather
&& selection[0].actions.gather.resource && selection[0].actions.gather.resource.stone)
|| (listIcon == action_gather_ore && selection[0].actions.gather
&& selection[0].actions.gather.resource && selection[0].actions.gather.resource.ore)
)
)
{
// Set appearance of tab.
setPortrait("snStatusPaneCommand" + listCounter + "_1", "IconSheetCommand", "Button", listIcon);
guiUnHide("snStatusPaneCommand" + listCounter + "_1");
// Hide its list.
guiHide("snStatusPaneCommand" + "Group" + listCounter);
// Store content info in tab button for future reference.
snStatusPaneCommand[listCounter][1].type = "command";
snStatusPaneCommand[listCounter][1].last = 0;
snStatusPaneCommand[listCounter][1].name = listIcon;
return (listCounter-1);
}
else
return (listCounter);
}
*/
// ====================================================================
/*
function pressCommandButton(commandButton)
{
// Determine current object, tab, and list from command button name.
tab = commandButton.name.substring (commandButton.name.lastIndexOf ("d")+1, commandButton.name.lastIndexOf ("_"));
list = commandButton.name.substring (commandButton.name.lastIndexOf ("_")+1, commandButton.name.length);
console.write ("Button pressed. " + commandButton + " " + tab + " " + list);
console.write (snStatusPaneCommand[tab][list].type);
switch (list)
{
case 1:
commandButton.caption = "";
if (snStatusPaneCommand[tab][list].type == "list")
{
// Click the tab button to toggle visibility of its list (if it's of a list type).
guiToggle ("snStatusPaneCommand" + "Group" + tab);
console.write ("Toggled " + "snStatusPaneCommand" + "Group" + tab);
}
else
{
console.write ("Some weird action.");
// Perform appropriate actions for different command buttons.
switch (snStatusPaneCommand[tab][list].name)
{
case action_patrol:
setCursor ("action-patrol");
selectLocation(
function (x, y) {
issueCommand (selection, NMT_Patrol, x, y);
}
);
break;
case action_attack:
setCursor ("action-attack");
selectEntity(
function (target) {
issueCommand (selection, NMT_AttackMelee, target);
}
);
break;
}
}
break;
default:
// Left-clicked list button.
console.write("Clicked [" + tab + "," + list + "]: list of type " + snStatusPaneCommand[tab][list].type + "; " + snStatusPaneCommand[tab][list].name + " " + snStatusPaneCommand[tab][list].object);
switch (snStatusPaneCommand[tab][list].name)
{
case action_tab_buildciv:
case action_tab_buildmil:
// Create building placement cursor.
startPlacing(selection[0].traits.id.civ_code + "_" + snStatusPaneCommand[tab][list].object);
break;
default:
// Attempt to add the entry to the queue.
attemptAddToBuildQueue( selection[0], selection[0].traits.id.civ_code + "_" + snStatusPaneCommand[tab][list].object, tab, list);
break;
}
break;
}
}
*/
// ====================================================================
/*
function UpdateListold(listIcon, listCounter)
{
// Populates a given column of command icons with appropriate build portraits for the selected object.
// Returns an array of this selection.
// Build unit list.
if ( selection[0].traits.id.civ_code
&& selection[0].actions
&& selection[0].actions.create
&& selection[0].actions.create.list )
{
list = null;
switch (listIcon)
{
case action_tab_train:
if ( selection[0].actions.create.list.unit && shouldUpdateStat( "actions.create.list.unit" ) )
list = selection[0].actions.create.list.unit;
break;
case action_tab_buildciv:
if ( selection[0].actions.create.list.structciv && shouldUpdateStat( "actions.create.list.structciv" ) )
list = selection[0].actions.create.list.structciv;
break;
case action_tab_buildmil:
if ( selection[0].actions.create.list.structmil && shouldUpdateStat( "actions.create.list.structmil" ) )
list = selection[0].actions.create.list.structmil;
break;
case action_tab_research:
if ( selection[0].actions.create.list.tech && shouldUpdateStat( "actions.create.list.tech" ) )
list = selection[0].actions.create.list.tech;
break;
default:
return 0;
break;
}
if ( list )
{
// Enable tab portrait.
guiUnHide("snStatusPaneCommand" + "Group" + listCounter);
guiUnHide("snStatusPaneCommand" + listCounter + "_1");
setPortrait("snStatusPaneCommand" + listCounter + "_1", "sheet_action", "", listIcon);
// Reset list length.
snStatusPaneCommand[listCounter][1].last = 0;
// Store content info in tab button for future reference.
snStatusPaneCommand[listCounter][1].type = "list";
// Extract entity list into an array.
listArray = [];
for( i in list )
{
listArray[listArray.length] = i.toString();
}
// Populate appropriate command buttons.
for (createLoop = 1; createLoop < snStatusPaneCommand.list.max; createLoop++)
{
if (createLoop < listArray.length)
{
// Get name of entity to display in list.
UpdateListEntityName = selection[0].traits.id.civ_code + "_" + listArray[createLoop];
getGUIObjectByName ("snStatusPaneCommand" + listCounter + "_" + (createLoop+1)).caption = "";
guiUnHide("snStatusPaneCommand" + listCounter + "_" + (createLoop+1));
setPortrait("snStatusPaneCommand" + listCounter + "_" + (createLoop+1),
getEntityTemplate(UpdateListEntityName).traits.id.icon,
selection[0].traits.id.civ_code,
getEntityTemplate(UpdateListEntityName).traits.id.icon_cell);
// Store content info in tab button for future reference.
snStatusPaneCommand[listCounter][createLoop+1].name = listIcon;
snStatusPaneCommand[listCounter][createLoop+1].object = listArray[createLoop];
snStatusPaneCommand[listCounter][createLoop+1].type = "list";
snStatusPaneCommand[listCounter][createLoop+1].last++;
}
else
guiHide("snStatusPaneCommand" + listCounter + "_" + parseInt(createLoop+1));
}
}
return listArray;
}
}
return 0;
}
*/
Index: ps/trunk/binaries/data/mods/official/scripts/entity_functions.js
===================================================================
--- ps/trunk/binaries/data/mods/official/scripts/entity_functions.js (revision 4184)
+++ ps/trunk/binaries/data/mods/official/scripts/entity_functions.js (revision 4185)
@@ -1,2084 +1,2084 @@
/*
DESCRIPTION : Functions that define the scripted behaviour of properties, particularly the effects of entity events and their properties when initialised.
NOTES :
*/
// ====================================================================
// To add a new generic order, do the following all within template_entity_script.js:
// * Pick a number to be its ID (add this to the "const"s directly below).
// * Add code in the entityInit() function below that will call setActionParams to set the action's range, speed and animation if the entity supports this action. For example this.setActionParams( ACTION_GATHER, 0.0, a.range, a.speed, "gather" ) tells the entity that the action with ID of ACTION_GATHER has min range 0, max range a.range, speed a.speed, and should play the animation called "gather" while active.
// * Add code in entityEventTargetChanged() to tell the GUI whether the entity should use this action depending on what target the mouse is hovering over. This is also where you can set a cursor for the action.
// * Add code in entityEventGeneric() to deal with new generic order events of your type. Note that if you want to have your action handler in a separate function (is preferable), you need to also add this function to the entity object in entityInit() (its initialize event), e.g. this.processGather = entityEventGather.
const ACTION_NONE = 0;
const ACTION_ATTACK = 1;
const ACTION_GATHER = 2;
const ACTION_HEAL = 3;
const ACTION_ATTACK_RANGED = 4;
const ACTION_BUILD = 5;
const ACTION_REPAIR = 6;
const PRODUCTION_TRAIN = 1;
const PRODUCTION_RESEARCH = 2;
// ====================================================================
function entityInit()
{
// Initialise an entity when it is first spawned (generate starting hitpoints, etc).
// This function is called for all "full" entities - those inheriting from template_entity_full; there is a simpler version below
// called entityInitQuasi for quasi-entities (rocks, trees, etc) for which most of the things dealt with here don't apply.
startXTimer(1);
// If the entity is a foundation, we must deduct resource costs here
if( this.building )
{
var template = getEntityTemplate( this.building, this.player );
var result = checkEntityReqs( this.player, template );
if (result == true) // If the entry meets requirements to be added to the queue (eg sufficient resources)
{
console.write("There");
// Cycle through all costs of this entry.
var pool = template.traits.creation.resource;
for ( resource in pool )
{
switch ( resource.toString() )
{
case "population":
case "housing":
break;
default:
// Deduct the given quantity of resources.
this.player.resources[resource.toString()] -= parseInt(pool[resource]);
console.write ("Spent " + pool[resource] + " " + resource + " to build " +
template.traits.id.generic);
break;
}
}
}
else
{
// Might happen if the player clicks to place 2 buildings really fast
console.write( "Could not begin construction: " + result );
evt.preventDefault();
return;
}
}
stopXTimer(1);
startXTimer(2);
// Attach our functions to ourselves
this.performAttack = performAttack;
this.performAttackRanged = performAttackRanged;
this.performGather = performGather;
this.performHeal = performHeal;
this.performBuild = performBuild;
this.performRepair = performRepair;
this.damage = damage;
this.entityComplete = entityComplete;
this.GotoInRange = GotoInRange;
this.attachAuras = attachAuras;
this.setupRank = setupRank;
// Some temp variables to speed up property access
var id = this.traits.id;
var supply = this.traits.supply;
var health = this.traits.health;
var stamina = this.traits.stamina;
// If this is a foundation, initialize traits from the building we're converting into
if( this.building && this.building != "" )
{
var building = getEntityTemplate( this.building, this.player );
id.generic = building.traits.id.generic;
id.specific = building.traits.id.specific;
id.civ = building.traits.id.civ;
id.icon_cell = building.traits.id.icon_cell;
this.traits.health.max = building.traits.health.max;
this.build_points = new Object();
this.build_points.curr = 0.0;
this.build_points.max = parseFloat( building.traits.creation.time );
}
// Generate civ code (1st four characters of civ name, in lower case eg "Carthaginians" => "cart").
if( !id.civ_code && id.civ )
{
var civ_code = id.civ.toString().substring (0, 4);
civ_code = civ_code.toString().toLowerCase();
// Exception to make the Romans into rome and Hellenes into hele.
if (civ_code == "roma") civ_code = "rome";
else if (civ_code == "hell") civ_code = "hele";
id.civ_code = civ_code;
}
// If entity can contain garrisoned units, empty it.
if ( this.traits.garrison && this.traits.garrison.max )
this.traits.garrison.curr = 0;
// If entity has health, set current to same, unless it's a foundation, in which case we set it to 0.
if ( health && health.max )
health.curr = ( this.building!="" ? 0.0 : health.max );
// If entity has stamina, set current to same.
if ( stamina && stamina.max )
stamina.curr = stamina.max
stopXTimer(2);
startXTimer(3);
if( supply )
{
// If entity has supply, set current to same.
supply.curr = supply.max;
- // If entity has type of supply and no subtype, set subtype to same
- // (so we don't have to say type="wood", subtype="wood"
- if (supply.type && !supply.subtype)
- supply.subtype = supply.type;
+ // If entity has type of supply and no subType, set subType to same
+ // (so we don't have to say type="wood", subType="wood"
+ if (supply.type && !supply.subType)
+ supply.subType = supply.type;
// The "dropsite_count" array holds the number of units with gather aura in range of the object;
// this is important so that if you have two mills near something and one of them is destroyed,
// you can still gather from the thing. Initialize it to 0 (ungatherable) for every player unless
// the entity is forageable (e.g. for huntable animals).
var dropsite_count = new Array();
- initialCount = supply.subtype.meat ? 1 : 0;
+ initialCount = supply.subType.meat ? 1 : 0;
for( i=0; i<=8; i++ )
{
dropsite_count[i] = initialCount;
}
supply.dropsite_count = dropsite_count;
}
if (!this.traits.promotion)
this.traits.promotion = new Object();
// If entity becomes another entity after it gains enough experience points, set up secondary attributes.
if (this.traits.promotion.req)
{
this.setupRank();
// Give the entity an initial value of 0 earned XP at startup if a default value is not specified.
if (!this.traits.promotion.curr)
this.traits.promotion.curr = 0;
}
else
{
this.traits.promotion.rank = "0";
}
stopXTimer(3);
startXTimer(4);
// Register our actions with the generic order system
if( this.actions )
{
if ( this.actions.move && this.actions.move.speed )
this.actions.move.speed_curr = this.actions.move.speed;
if( this.actions.attack && this.actions.attack.melee )
{
a = this.actions.attack.melee;
minRange = ( a.minRange ? a.minRange : 0.0 );
this.setActionParams( ACTION_ATTACK, minRange, a.range, a.speed, "melee" );
}
if( this.actions.gather )
{
a = this.actions.gather;
this.setActionParams( ACTION_GATHER, 0.0, a.range, a.speed, "gather" );
}
if( this.actions.heal )
{
a = this.actions.heal;
this.setActionParams( ACTION_HEAL, 0.0, a.range, a.speed, "heal" );
}
if( this.actions.attack && this.actions.attack.ranged )
{
a = this.actions.attack.ranged;
minRange = ( a.minRange ? a.minRange : 0.0 );
// this animation should actually be "ranged" except the current actors still have it called "melee"
this.setActionParams( ACTION_ATTACK_RANGED, minRange, a.range, a.speed, "melee" );
}
if( this.actions.build )
{
this.setActionParams( ACTION_BUILD, 0.0, 2.0, this.actions.build.speed, "build" );
}
if( this.actions.repair )
{
this.setActionParams( ACTION_REPAIR, 0.0, 2.0, this.actions.repair.speed, "build" );
}
}
stopXTimer(4);
startXTimer(5);
this.attachAuras();
// If the entity either costs population or adds to it,
if (this.traits.population)
{
// If the entity increases the population limit (provides Housing),
if (this.traits.population.add)
this.player.resources.housing += parseInt(this.traits.population.add);
// If the entity takes up Housing,
if (this.traits.population.rem)
this.player.resources.population += parseInt(this.traits.population.rem);
}
- // Build Unit AI Stance list, and set default stance. ---> Can eventually be done in C++, since stances will likely be implemented in C++.
+ // Build Unit Ai Stance list, and set default stance. ---> Can eventually be done in C++, since stances will likely be implemented in C++.
if (this.actions && this.actions.move)
{
if ( !this.traits.ai )
this.traits.ai = new Object();
if ( !this.traits.ai.stance )
this.traits.ai.stance = new Object();
if ( !this.traits.ai.stance.list )
this.traits.ai.stance.list = new Object();
// Create standard stances that all units have.
this.traits.ai.stance.list.avoid = new Object();
this.traits.ai.stance.list.hold = new Object();
if ( this.actions && this.actions.attack )
{
// Create stances that units only have if they can attack.
this.traits.ai.stance.list.aggress = new Object();
this.traits.ai.stance.list.defend = new Object();
this.traits.ai.stance.list.stand = new Object();
// Set default stance for combat units.
this.traits.ai.stance.curr = "Defend";
}
else
{
// Set default stance for non-combat units.
this.traits.ai.stance.curr = "Avoid";
}
}
stopXTimer(5);
/*
// Generate entity's personal name (if it needs one).
if (this.traits.id.personal)
{
// todo
// Look in the appropriate array for a random string.
switch (this.traits.id.classes[])
{
case "Male":
this.traits.id.personal.table1 = "Male"
break;
case "Female":
this.traits.id.personal.table1 = "Female"
break;
default:
this.traits.id.personal.table1 = "Gen"
break;
}
this.traits.id.personal.name = getRandom (this.traits.id.civ_code + this.traits.id.personal.table1 + "1")
this.traits.id.personal.name = this.traits.id.personal.name + " " + getRandom (this.traits.id.civ_code + this.traits.id.personal.table1 + "2")
}
else
this.traits.id.personal.name = "";
*/
// Log creation of entity to console.
//if (this.traits.id.personal && this.traits.id.personal.name)
// console.write ("A new " + this.traits.id.specific + " (" + this.traits.id.generic + ") called " + this.traits.id.personal.name + " has entered your dungeon.")
//else
// console.write ("A new " + this.traits.id.specific + " (" + this.traits.id.generic + ") has entered your dungeon.")
}
// ====================================================================
function entityInitQuasi()
{
// Initialization function for quasi-entities like trees, rocks, etc; only sets up resources
startXTimer(6);
var supply = this.traits.supply;
if( supply )
{
// If entity has supply, set current to same.
supply.curr = supply.max;
- // If entity has type of supply and no subtype, set subtype to same
- // (so we don't have to say type="wood", subtype="wood"
- if (supply.type && !supply.subtype)
- supply.subtype = supply.type;
+ // If entity has type of supply and no subType, set subType to same
+ // (so we don't have to say type="wood", subType="wood"
+ if (supply.type && !supply.subType)
+ supply.subType = supply.type;
// The "dropsite_count" array holds the number of units with gather aura in range of the object;
// this is important so that if you have two mills near something and one of them is destroyed,
// you can still gather from the thing. Initialize it to 0 (ungatherable) for every player unless
// the entity is forageable (e.g. for huntable animals).
var dropsite_count = new Array();
- initialCount = supply.subtype.meat ? 1 : 0;
+ initialCount = supply.subType.meat ? 1 : 0;
for( i=0; i<=8; i++ )
{
dropsite_count[i] = initialCount;
}
supply.dropsite_count = dropsite_count;
}
stopXTimer(6);
startXTimer(7);
// Generate civ code (1st four characters of civ name, in lower case eg "Carthaginians" => "cart").
var id = this.traits.id;
if( !id.civ_code && id.civ )
{
var civ_code = id.civ.toString().substring (0, 4);
civ_code = civ_code.toString().toLowerCase();
// Exception to make the Romans into rome and Hellenes into hele.
if (civ_code == "roma") civ_code = "rome";
else if (civ_code == "hell") civ_code = "hele";
id.civ_code = civ_code;
}
/*
// Original unoptimized version of the above code (for reference)
if( this.traits.id && this.traits.id.civ )
{
var id = this.traits.id;
id.civ_code = id.civ.toString().substring (0, 4);
id.civ_code = id.civ_code.toString().toLowerCase();
// Exception to make the Romans into rome.
if (id.civ_code == "roma") id.civ_code = "rome";
// Exception to make the Hellenes into hele.
if (id.civ_code == "hell") id.civ_code = "hele";
}
*/
stopXTimer(7);
/*
// Profiling code for comparing performance of pure JavaScript objects vs. CJSComplex.
// This should be removed when we're done optimizing entity init.
var ob = new Object();
ob.traits = new Object();
for(var i=0; i<50; i++) {
ob["temp"+i] = "blah";
}
for(var i=0; i<50; i++) {
ob.traits["temp"+i] = "blah";
}
ob.traits.id = new Object();
ob.traits.id.civ = "hellenes";
startXTimer(9);
// ~100 MS with ob = new Object() as above, ~400 MS with ob = this
if( ob.traits.id && ob.traits.id.civ )
{
var id = ob.traits.id;
id.civ_code = id.civ.toString().substring (0, 4);
id.civ_code = id.civ_code.toString().toLowerCase();
// Exception to make the Romans into rome.
if (id.civ_code == "roma") id.civ_code = "rome";
// Exception to make the Hellenes into hele.
if (id.civ_code == "hell") id.civ_code = "hele";
}
stopXTimer(9);
*/
}
// ====================================================================
// Setup entity's next rank
function setupRank()
{
// Get the name of the entity.
entityName = this.tag.toString();
// For accessing the this.traits.promotion object quicker
var promotion = this.traits.promotion;
// Determine whether current is basic, advanced or elite, and set rank to suit.
switch (entityName.substring (entityName.length-2, entityName.length))
{
case "_b":
// Basic. Upgrades to Advanced.
promotion.rank = "1";
nextSuffix = "_a";
// Set rank image to put over entity's head.
this.traits.rank.name = "";
break;
case "_a":
// Advanced. Upgrades to Elite.
promotion.rank = "2";
nextSuffix = "_e";
// Set rank image to put over entity's head.
this.traits.rank.name = "advanced.dds";
break;
case "_e":
// Elite. Maximum rank.
promotion.rank = "3";
nextSuffix = "";
// Set rank image to put over entity's head.
this.traits.rank.name = "elite.dds";
break;
default:
// Does not gain promotions.
promotion.rank = "0"
nextSuffix = ""
break;
}
// If entity is an additional rank and the correct actor has not been specified
// (it's just inherited the Basic), point it to the correct suffix. (Saves us specifying it each time.)
actorStr = this.actor.toString();
if (promotion.rank > "1"
&& actorStr.substring (actorStr.length-5, actorStr.length) != nextSuffix + ".xml")
this.actor = actorStr.substring (1,actorStr.length-5) + nextSuffix + ".xml";
// The entity it should become (unless specified otherwise) is the base entity plus promotion suffix.
if (!promotion.newentity && nextSuffix != "" && promotion.rank != "0")
promotion.newentity = entityName.substring (0, entityName.length-2) + nextSuffix;
}
// ====================================================================
// Attach any auras the entity is entitled to. This was moved to a separate function so that buildings can have their auras
// attached to them only when they finish construction.
function attachAuras()
{
if( this.traits.auras )
{
if( this.traits.auras.courage )
{
a = this.traits.auras.courage;
this.addAura ( "courage", a.radius, 0, new DamageModifyAura( this, true, a.bonus ) );
}
if( this.traits.auras.fear )
{
a = this.traits.auras.fear;
this.addAura ( "fear", a.radius, 0, new DamageModifyAura( this, false, -a.bonus ) );
}
if( this.traits.auras.infidelity )
{
a = this.traits.auras.infidelity;
this.addAura ( "infidelity", a.radius, 0, new InfidelityAura( this, a.time ) );
}
if( this.traits.auras.dropsite )
{
a = this.traits.auras.dropsite;
this.addAura ( "dropsite", a.radius, 0, new DropsiteAura( this, a.types ) );
}
if( this.traits.auras.heal )
{
a = this.traits.auras.heal;
this.addAura ( "heal", a.radius, a.speed, new HealAura( this ) );
}
if( this.traits.auras.trample )
{
a = this.traits.auras.trample;
this.addAura ( "trample", a.radius, a.speed, new TrampleAura( this ) );
}
}
if( this.hasClass("Settlement") )
{
this.addAura ( "settlement", 1.0, 0, new SettlementAura( this ) );
}
}
// ====================================================================
function entityDestroyed( evt )
{
// If the entity either costs population or adds to it,
if (this.traits.population)
{
// If the entity increases the population limit (provides Housing),
if (this.traits.population.add)
this.player.resources.housing -= parseInt(this.traits.population.add);
// If the entity occupies population slots (occupies Housing),
if (this.traits.population.rem)
this.player.resources.population -= parseInt(this.traits.population.rem);
}
}
// ====================================================================
function foundationDestroyed( evt )
{
if( this.building != "" ) // Check that we're *really* a foundation since the event handler is kept when we change templates (probably a bug)
{
//console.write( "Hari Seldon made a small calculation error." );
var bp = this.build_points;
var fractionToReturn = (bp.max - bp.curr) / bp.max;
var resources = getEntityTemplate( this.building, this.player ).traits.creation.resource;
for( r in resources )
{
amount = parseInt( fractionToReturn * parseInt(resources[r]) );
this.player.resources[r.toString()] += amount;
}
}
}
// ====================================================================
function performAttack( evt )
{
this.last_combat_time = getGameTime();
var curr_hit = getGUIGlobal().newRandomSound("voice", "hit", this.traits.audio.path);
curr_hit.play();
// Attack logic.
var dmg = new DamageType();
if ( this.getRunState() )
{
console.write("" + this + " doing a charge attack!");
var a = this.actions.attack.charge;
dmg.crush = parseInt(a.damage * a.crush);
dmg.hack = parseInt(a.damage * a.hack);
dmg.pierce = parseInt(a.damage * a.pierce);
this.setRun( false );
}
else
{
var a = this.actions.attack.melee;
dmg.crush = parseInt(a.damage * a.crush);
dmg.hack = parseInt(a.damage * a.hack);
dmg.pierce = parseInt(a.damage * a.pierce);
}
// Add flank penalty
if(evt.target.traits.flank_penalty)
{
var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value;
dmg.crush += dmg.crush * flank;
dmg.hack += dmg.hack * flank;
dmg.pierce += dmg.pierce * flank;
}
evt.target.damage( dmg, this );
}
// ====================================================================
function performAttackRanged( evt )
{
this.last_combat_time = getGameTime();
// Create a projectile from us, to the target, that will do some damage when it hits them.
dmg = new DamageType();
var a = this.actions.attack.ranged;
dmg.crush = parseInt(a.damage * a.crush);
dmg.hack = parseInt(a.damage * a.hack);
dmg.pierce = parseInt(a.damage * a.pierce);
// Add flank penalty and elevation bonus
var elevationBonus = (this.getHeight() - evt.target.getHeight())/this.traits.elevation.rate * this.traits.elevation.value;
dmg.crush += dmg.crush * elevationBonus;
dmg.hack += dmg.hack * elevationBonus;
dmg.pierce += dmg.pierce * elevationBonus;
if(evt.target.traits.flank_penalty)
{
var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value;
dmg.crush += dmg.crush * flank;
dmg.hack += dmg.hack * flank;
dmg.pierce += dmg.pierce * flank;
}
// The parameters for Projectile are:
// 1 - The actor to use as the projectile. There are two ways of specifying this:
// the first is by giving an entity. The projectile's actor is found by looking
// in the actor of that entity. This way is usual, and preferred - visual
// information, like the projectile model, should be stored in the actor files.
// The second way is to give a actor/file name string (e.g. "props/weapon/weap_
// arrow_front.xml"). This is only meant to be used for 'Acts of Gaia' where
// there is no originating entity. Right now, this entity is the one doing the
// firing, so pass this.
// 2 - Where the projectile is coming from. This can be an entity or a Vector3D.
// For now, that's also us.
// 3 - Where the projectile is going to. Again, either a vector (attack ground?)
// or an entity. Let's shoot at our target, lest we get people terribly confused.
// 4 - How fast the projectile should go. To keep things clear, we'll set it to
// just a bit faster than the average cavalry.
// 5 - Who fired it? Erm... yep, us again.
// 6 - The function you'd like to call when it hits an entity.
// There's also a seventh parameter, for a function to call when it misses (more
// accurately, when it hits the floor). At the moment, however, the default
// action (do nothing) is what we want.
// Parameters 5, 6, and 7 are all optional.
projectile = new Projectile( this, this, evt.target, 12.0, this, projectileEventImpact )
// We'll attach the damage information to the projectile, just to show you can
// do that like you can with most other objects. Could also do this by making
// the function we pass a closure.
projectile.damage = dmg;
// Finally, tell the engine not to send this event to anywhere else -
// in particular, this shouldn't get to the melee event handler, above.
evt.stopPropagation();
//console.write( "Fire!" );
}
// ====================================================================
function projectileEventImpact( evt )
{
//console.write( "Hit!" );
evt.impacted.damage( this.damage, evt.originator );
// Just so you know - there's no guarantee that evt.impacted is the thing you were
// aiming at. This function gets called when the projectile hits *anything*.
// For example:
if( evt.impacted.player == evt.originator.player )
console.write( "Friendly fire!" );
// The three properties of the ProjectileImpact event are:
// - impacted, the thing it hit
// - originator, the thing that fired it (the fifth parameter of Projectile's
// constructor) - may be null
// - position, the position the arrow was in the world when it hit.
// The properties of the ProjectileMiss event (the one that gets sent to the
// handler that was the seventh parameter of the constructor) are similar,
// but it doesn't have 'impacted' - for obvious reasons.
}
// ====================================================================
function performGather( evt )
{
g = this.actions.gather;
s = evt.target.traits.supply;
if( !s.dropsite_count[this.player.id] )
{
// Entity has become ungatherable for us, probably meaning our mill near it was killed; cancel order
evt.preventDefault();
return;
}
- if( g.resource[s.type][s.subtype])
- gather_amt = parseFloat( g.resource[s.type][s.subtype] );
+ if( g.resource[s.type][s.subType])
+ gather_amt = parseFloat( g.resource[s.type][s.subType] );
else
gather_amt = parseFloat( g.resource[s.type] );
if( s.max > 0 )
{
if( s.curr <= gather_amt )
{
gather_amt = s.curr.valueOf();
}
// Remove amount from target.
s.curr -= gather_amt;
// Add extracted resources to player's resource pool.
this.player.resources[s.type.toString()] += gather_amt;
// Kill the target if it's now out of resources
if( s.curr == 0 )
{
evt.target.kill();
}
}
}
// ====================================================================
function performHeal( evt )
{
if ( evt.target.player != this.player )
{
console.write( "You have a traitor!" );
return;
}
// Cycle through all resources.
pool = this.player.resources;
for( resource in pool )
{
switch( resource.toString() )
{
case "population" || "housing":
break;
default:
// Make sure we have enough resources.
if ( pool[resource] - evt.target.actions.heal.cost * evt.target.traits.creation.cost[resource] < 0 )
{
console.write( "Not enough " + resource.toString() + " for healing." );
return;
}
break;
}
}
evt.target.traits.health.curr += this.actions.heal.speed;
console.write( this.traits.id.specific + " has performed a miracle!" );
if (evt.target.traits.health.curr >= evt.target.traits.health.max)
{
evt.target.traits.health.curr = evt.target.traits.health.max;
}
// Cycle through all resources.
pool = this.player.resources;
for( resource in pool )
{
switch( resource.toString() )
{
case "population":
case "housing":
break;
default:
// Deduct resources to pay for healing.
this.player.resources[resource.toString()] -= parseInt(evt.target.actions.heal.cost * evt.target.traits.creation.cost[resource]);
break;
}
}
}
// ====================================================================
function performBuild( evt )
{
if( !canBuild( this, evt.target ) )
{
evt.preventDefault();
return;
}
var t = evt.target;
var b = this.actions.build;
var bp = t.build_points;
var hp = t.traits.health;
var points = parseFloat( b.rate ) * parseFloat( b.speed ) / 1000.0;
bp.curr += points;
hp.curr = Math.min( hp.max, hp.curr + (points/bp.max)*hp.max );
if( bp.curr >= bp.max )
{
// We've finished building this object; convert the foundation to a building
if( t.building != "" ) // Might be false if another unit finished building the thing during our last anim cycle
{
t.template = getEntityTemplate( t.building, t.player );
t.building = "";
t.attachAuras();
if (t.traits.population && t.traits.population.add)
this.player.resources.housing += parseInt(t.traits.population.add);
}
evt.preventDefault(); // Stop performing this action
}
}
// ====================================================================
function performRepair( evt )
{
if( !canRepair( this, evt.target ) )
{
evt.preventDefault();
return;
}
var t = evt.target;
var b = this.actions.build;
var hp = t.traits.health;
// Find the fraction of max health to repair by; this should be one build tick (i.e. longer for buildings with
// longer creation time) but also not so much that it causes the unit to have more than max HP
var fraction = Math.min(
parseFloat( b.rate ) / t.traits.creation.time,
( hp.max - hp.curr ) / hp.max
);
console.write("Repair fraction is " + fraction);
// Check if we can afford to repair
var resources = t.traits.creation.resource;
for( r in resources )
{
amount = parseInt( fraction * parseInt(resources[r]) );
this.player.resources[r.toString()] -= parseInt(amount);
}
// Heal the building
hp.curr = Math.min( hp.max, hp.curr + fraction * hp.max );
// Deduct the resources
var resources = t.traits.creation.resource;
for( r in resources )
{
amount = parseInt( fraction * parseInt(resources[r]) );
this.player.resources[r.toString()] -= parseInt(amount);
}
}
// ====================================================================
function damage( dmg, inflictor )
{
if(!this.traits.armour) return; // corpses have no armour, everything else should
this.last_combat_time = getGameTime();
// Apply armour and work out how much damage we actually take
crushDamage = parseInt(dmg.crush - this.traits.armour.value * this.traits.armour.crush);
if ( crushDamage < 0 ) crushDamage = 0;
pierceDamage = parseInt(dmg.pierce - this.traits.armour.value * this.traits.armour.pierce);
if ( pierceDamage < 0 ) pierceDamage = 0;
hackDamage = parseInt(dmg.hack - this.traits.armour.value * this.traits.armour.hack);
if ( hackDamage < 0 ) hackDamage = 0;
totalDamage = parseInt(dmg.typeless + crushDamage + pierceDamage + hackDamage);
// Minimum of 1 damage
if( totalDamage < 1 ) totalDamage = 1;
this.traits.health.curr -= totalDamage;
if( this.traits.health.curr <= 0 )
{
// If the fallen is worth any loot and the inflictor is capable of looting
if (this.traits.loot && inflictor.actions && inflictor.actions.loot)
{
// Cycle through all loot on this entry.
for( loot in this.traits.loot )
{
switch( loot.toString().toUpperCase() )
{
- case "XP":
+ case "XP":
// If the inflictor gains promotions, and he's capable of earning more ranks,
if (inflictor.traits.promotion && inflictor.traits.promotion.curr && inflictor.traits.promotion.req
&& inflictor.traits.promotion.newentity && inflictor.traits.promotion.newentity != ""
&& this.traits.loot && this.traits.loot.xp && inflictor.actions.loot.xp)
{
// Give him the fallen's upgrade points (if he has any).
if (this.traits.loot.xp)
inflictor.traits.promotion.curr = parseInt(inflictor.traits.promotion.curr) + parseInt(this.traits.loot.xp);
// Notify player.
if (inflictor.traits.id.specific)
console.write(inflictor.traits.id.specific + " has earned " + this.traits.loot.xp + " upgrade points!");
else
console.write("One of your units has earned " + this.traits.loot.xp + " upgrade points!");
// If he now has maximum upgrade points for his rank,
if (inflictor.traits.promotion.curr >= inflictor.traits.promotion.req)
{
// Notify the player.
if (inflictor.traits.id.specific)
console.write(inflictor.traits.id.specific + " has gained a promotion!");
else
console.write("One of your units has gained a promotion!");
// Reset his upgrade points.
inflictor.traits.promotion.curr = 0;
// Upgrade his portrait to the next level.
inflictor.traits.id.icon_cell++;
// Transmogrify him into his next rank.
inflictor.template = getEntityTemplate( inflictor.traits.promotion.newentity, inflictor.player );
inflictor.traits.promotion.newentity = null; // So that setupRank() can set it properly
inflictor.setupRank();
console.write("New promotion values: " + inflictor.traits.promotion.curr + " / " + inflictor.traits.promotion.req);
}
}
break;
default:
if ( inflictor.actions.loot.resources )
{
// Notify player.
console.write ("Spoils of war! " + this.traits.loot[loot] + " " + loot.toString() + "!");
// Give the inflictor his resources.
this.player.resources[loot.toString()] -= parseInt(this.traits.loot[loot]);
}
break;
}
}
}
// Notify player.
if ( inflictor )
console.write( this.traits.id.generic + " got the point of " + inflictor.traits.id.generic + "'s weapon." );
else
console.write( this.traits.id.generic + " died in mysterious circumstances." );
// Make him cry out in pain.
if (this.traits.audio && this.traits.audio.path)
{
curr_pain = getGUIGlobal().newRandomSound("voice", "pain", this.traits.audio.path);
if (curr_pain) curr_pain.play();
}
else
{
console.write ("Sorry, no death sound for this unit; you'll just have to imagine it ...");
}
// We've taken what we need. Kill the swine.
console.write("Kill!!");
this.kill();
}
else if( inflictor && this.actions && this.actions.attack )
{
// If we're not already doing something else, take a measured response - hit 'em back.
- // You know, I think this is quite possibly the first AI code the AI divlead has written
+ // You know, I think this is quite possibly the first Ai code the Ai divlead has written
// for 0 A.D....
//When the entity changes order, we can readjust flank penalty. We must destroy the notifiers ourselves later,however.
this.requestNotification( inflictor, NOTIFY_ORDER_CHANGE, false, true );
this.registerDamage( inflictor );
if( this.isIdle() )
this.order( ORDER_GENERIC, inflictor, getAttackAction( this, inflictor ) );
}
}
// ====================================================================
function entityEventGeneric( evt )
{
switch( evt.action )
{
case ACTION_ATTACK:
this.performAttack( evt ); break;
case ACTION_GATHER:
evt.notifyType = NOTIFY_GATHER;
this.performGather( evt );
break;
case ACTION_HEAL:
this.performHeal( evt ); break;
case ACTION_ATTACK_RANGED:
this.performAttackRanged( evt ); break;
case ACTION_BUILD:
this.performBuild( evt ); break;
case ACTION_REPAIR:
this.performRepair( evt ); break;
default:
console.write( "Unknown generic action: " + evt.action );
}
}
//======================================================================
function entityEventNotification( evt )
{
//This is used to adjust the flank penalty (we're no longer being attacked).
if ( this.getCurrentRequest() == NOTIFY_ORDER_CHANGE )
{
this.registerOrderChange( evt.target );
destroyNotifier( evt.target );
return;
}
//Add "true" to the end of order() to indicate that this is a notification order.
switch( evt.notifyType )
{
case NOTIFY_GOTO:
this.GotoInRange( evt.location.x, evt.location.z, false);
break;
case NOTIFY_RUN:
this.GotoInRange( evt.location.x, evt.location.z, true );
break;
case NOTIFY_ATTACK:
case NOTIFY_DAMAGE:
this.order( ORDER_GENERIC, evt.target, ACTION_ATTACK, true );
break;
case NOTIFY_HEAL:
this.order( ORDER_GENERIC, evt.target, ACTION_HEAL, true );
break;
case NOTIFY_GATHER:
this.order( ORDER_GENERIC, evt.target, ACTION_GATHER, true );
break;
case NOTIFY_IDLE:
//target is the unit that has become idle. Eventually...do something here.
break;
default:
console.write( "Unknown notification request " + evt.notifyType );
return;
}
}
// ====================================================================
function getAttackAction( source, target )
{
if (!source.actions.attack)
return ACTION_NONE;
attack = source.actions.attack;
if ( attack.melee )
return ACTION_ATTACK;
else if ( attack.ranged )
return ACTION_ATTACK_RANGED;
else
return ACTION_NONE;
}
// ====================================================================
// TODO: Change this to an event so that it gets passed to our parent too, like other events
function entityComplete()
{
console.write( this + " is finished building." );
}
//=====================================================================
function entityEventIdle( evt )
{
//Use our own data for target; we aren't affecting anyone, so listeners want to know about us
this.forceCheckListeners( NOTIFY_IDLE, this );
}
//=====================================================================
function entityEventMovement( evt )
{
var divs = this.traits.pitch.sectors;
var sector = this.findSector( divs, evt.slope, 3.141592, true );
if ( divs % 2 )
{
sector += ((divs+1)/2 - sector)*2;
sector -= (divs+1)/2;
this.actions.move.speed_curr = this.actions.move.speed;
this.actions.move.speed_curr += this.actions.move.speed*this.traits.pitch.value*sector;
}
else
{
sector += ((divs)/2 - sector)*2;
sector -= (divs)/2;
this.actions.move.speed_curr = this.actions.move.speed;
this.actions.move.speed_curr += this.actions.move.speed*this.traits.pitch.value*sector;
}
}
// ====================================================================
function entityEventTargetChanged( evt )
{
// This event lets us know when the user moves his/her cursor to a different unit (provided this
// unit is selected) - use it to tell the engine what context cursor should be displayed, given
// the target.
// If we can gather, and the target supplies, gather. If it's our enemy, and we're armed, attack.
// If all else fails, move (or run on a right-click).
if ( getCursorName() == "cursor-rally" )
{
evt.defaultCursor = "cursor-rally";
evt.defaultOrder = -1;
return;
}
evt.defaultOrder = NMT_Goto;
evt.defaultCursor = "arrow-default";
evt.defaultAction = ACTION_NONE;
evt.secondaryAction = ACTION_NONE;
evt.secondaryCursor = "arrow-default";
if ( this.actions && this.actions.run && this.actions.run.speed > 0 )
{
evt.secondaryOrder = NMT_Run;
}
if( evt.target && this.actions )
{
if( this.actions.attack &&
evt.target.player != this.player &&
evt.target.traits.health &&
evt.target.traits.health.max != 0 )
{
evt.defaultOrder = NMT_Generic;
evt.defaultAction = getAttackAction( this, evt.target );
evt.defaultCursor = "action-attack";
evt.secondaryOrder = NMT_Generic;
evt.secondaryAction = getAttackAction( this, evt.target );
evt.secondaryCursor = "action-attack";
}
if ( this.actions.escort &&
this != evt.target &&
evt.target.player == this.player &&
evt.target.actions )
{
if (evt.target.actions.move)
{
//Send an empty order
evt.defaultOrder = NMT_NotifyRequest;
evt.secondaryOrder = NMT_NotifyRequest;
evt.defaultAction = NOTIFY_ESCORT;
evt.secondaryAction = NOTIFY_ESCORT;
}
}
if ( canGather( this, evt.target ) )
{
evt.defaultOrder = NMT_Generic;
evt.defaultAction = ACTION_GATHER;
// Set cursor (eg "action-gather-fruit").
- evt.defaultCursor = "action-gather-" + evt.target.traits.supply.subtype;
+ evt.defaultCursor = "action-gather-" + evt.target.traits.supply.subType;
evt.secondaryOrder = NMT_Generic;
evt.secondaryAction = ACTION_GATHER;
// Set cursor (eg "action-gather-fruit").
- evt.secondaryCursor = "action-gather-" + evt.target.traits.supply.subtype;
+ evt.secondaryCursor = "action-gather-" + evt.target.traits.supply.subType;
}
if ( canBuild( this, evt.target ) )
{
evt.defaultOrder = NMT_Generic;
evt.defaultAction = ACTION_BUILD;
evt.defaultCursor = "action-build";
evt.secondaryOrder = NMT_Generic;
evt.secondaryAction = ACTION_BUILD;
evt.secondaryCursor = "action-build";
}
if ( canRepair( this, evt.target ) )
{
evt.defaultOrder = NMT_Generic;
evt.defaultAction = ACTION_REPAIR;
evt.defaultCursor = "action-build";
evt.secondaryOrder = NMT_Generic;
evt.secondaryAction = ACTION_REPAIR;
evt.secondaryCursor = "action-build";
}
}
//Rally point
else if ( this.building )
{
evt.defaultOrder = -1;
evt.defaultCursor = "cursor-rally";
}
}
// ====================================================================
function entityEventPrepareOrder( evt )
{
// This event gives us a chance to veto any order we're given before we execute it.
// Not sure whether this really belongs here like this: the alternative is to override it in
- // subtypes - then you wouldn't need to check tags, you could hardcode results.
+ // subTypes - then you wouldn't need to check tags, you could hardcode results.
if ( !this.actions )
{
evt.preventDefault();
return;
}
//evt.notifySource is the entity order data will be obtained from, so if we're attacking and we
//want our listeners to copy us, then we will use our own order as the source.
//registerOrderChange() is used to adjust the flank penalty
switch( evt.orderType )
{
case ORDER_GOTO:
if ( !this.actions.move )
{
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_GOTO;
evt.notifySource = this;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break;
case ORDER_RUN:
if ( !this.actions.move.run )
{
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_RUN;
evt.notifySource = this;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break;
case ORDER_PATROL:
if ( !this.actions.patrol )
{
evt.preventDefault();
return;
}
this.registerOrderChange();
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break;
case ORDER_GENERIC:
evt.notifySource = this;
switch ( evt.action )
{
case ACTION_ATTACK:
case ACTION_ATTACK_RANGED:
evt.action = getAttackAction( this, evt.target );
if ( evt.action == ACTION_NONE )
{
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_ATTACK;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break;
case ACTION_GATHER:
if ( !this.actions.gather )
{
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_GATHER;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
// Change our gather animation based on the type of target
var a = this.actions.gather;
this.setActionParams( ACTION_GATHER, 0.0, a.range, a.speed,
- "gather_" + evt.target.traits.supply.subtype );
+ "gather_" + evt.target.traits.supply.subType );
break;
case ACTION_HEAL:
if ( !this.actions.heal )
{
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_HEAL;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break;
case ACTION_BUILD:
if ( !this.actions.build || !evt.target.building || evt.target.building=="" )
{
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_NONE;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break;
case ACTION_REPAIR:
if ( !this.actions.repair )
{
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_NONE;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break;
}
break;
case ORDER_PRODUCE:
case ORDER_START_CONSTRUCTION:
evt.notifyType = NOTIFY_NONE;
break;
default:
console.write("Unknown order type " + evt.orderType + "; ignoring.");
evt.preventDefault();
break;
}
}
// ====================================================================
function entityStartConstruction( evt )
{
this.order( ORDER_GENERIC, evt.target, ACTION_BUILD );
}
// ====================================================================
const TECH_RESOURCES = new Array("food", "wood", "stone", "ore");
function entityStartProduction( evt )
{
console.write("StartProduction: " + evt.productionType + " " + evt.name);
if( evt.productionType == PRODUCTION_TRAIN )
{
var template = getEntityTemplate( evt.name, this.player );
var result = checkEntityReqs( this.player, template );
if (result == true) // If the entry meets requirements to be added to the queue (eg sufficient resources)
{
// Cycle through all costs of this entry.
var pool = template.traits.creation.resource;
for ( resource in pool )
{
switch ( resource.toString() )
{
case "population":
case "housing":
break;
default:
// Deduct the given quantity of resources.
this.player.resources[resource.toString()] -= parseInt(pool[resource]);
console.write ("Spent " + pool[resource] + " " + resource + " to purchase " +
template.traits.id.generic);
break;
}
}
// Reserve population space for the unit
if( template.traits.population && template.traits.population.rem )
{
this.player.resources.population += parseInt(template.traits.population.rem);
}
// Set the amount of time it will take to complete production of the production object.
evt.time = getEntityTemplate( evt.name, this.player ).traits.creation.time;
}
else
{
// If not, output the error message.
console.write(result);
evt.preventDefault();
return;
}
}
else if( evt.productionType == PRODUCTION_RESEARCH )
{
var tech = getTechnology( evt.name, this.player );
if( !tech )
{
console.write( "No such tech: " + evt.name );
evt.preventDefault();
return;
}
// Check tech requirements other than resources
if( !tech.isValid() )
{
console.write( "Tech " + evt.name + " is currently unavailable" );
evt.preventDefault();
return;
}
// Check for sufficient resources
for( i in TECH_RESOURCES )
{
var res = TECH_RESOURCES[i];
if( this.player.resources[res] < tech[res] )
{
console.write( "Cannot afford " + evt.name + ": need " + tech[res] + " " + res );
evt.preventDefault();
return;
}
}
// Subtract resources
for( i in TECH_RESOURCES )
{
var res = TECH_RESOURCES[i];
this.player.resources[res] -= tech[res];
}
// Mark it as in progress
tech.in_progress = true;
// Set the amount of time it will take to complete production of the tech.
evt.time = tech.time;
}
else
{
evt.preventDefault();
}
}
function entityCancelProduction( evt )
{
console.write("CancelProduction: " + evt.productionType + " " + evt.name);
if( evt.productionType == PRODUCTION_TRAIN )
{
// Give back all the resources spent on this entry.
var template = getEntityTemplate( evt.name, this.player );
var pool = template.traits.creation.resource;
for ( resource in pool )
{
switch ( resource.toString() )
{
case "population":
case "housing":
break;
default:
// Refund the given quantity of resources.
this.player.resources[resource.toString()] += parseInt(pool[resource]);
console.write ("Got back " + pool[resource] + " " + resource + " from cancelling " +
template.traits.id.generic);
break;
}
}
// Give back the reserved population space
if( template.traits.population && template.traits.population.rem )
{
this.player.resources.population -= parseInt(template.traits.population.rem);
}
}
else if( evt.productionType == PRODUCTION_RESEARCH )
{
var tech = getTechnology( evt.name, this.player );
// Give back the player's resources
for( i in TECH_RESOURCES )
{
var res = TECH_RESOURCES[i];
this.player.resources[res] += tech[res];
}
// Unmark tech as in progress
tech.in_progress = false;
}
}
function entityFinishProduction( evt )
{
console.write("FinishProduction: " + evt.productionType + " " + evt.name);
if( evt.productionType == PRODUCTION_TRAIN )
{
var template = getEntityTemplate( evt.name, this.player );
// Give back reserved population space (the unit will take it up again in its initialize event, if we find space to place it)
if( template.traits.population && template.traits.population.rem )
{
this.player.resources.population -= parseInt(template.traits.population.rem);
}
// Code to find a free space around an object is tedious and slow, so
// I wrote it in C. Takes the template object so it can determine how
// much space it needs to leave.
var position = this.getSpawnPoint( template );
// The above function returns null if it couldn't find a large enough space.
if( !position )
{
console.write( "Couldn't train unit - not enough space" );
// Oh well. The player's just lost all the resources and time they put into
// construction - serves them right for not paying attention to the land
// around their barracks, doesn't it?
}
else
{
var created = new Entity( template, position, 0, this.player );
// Above shouldn't ever fail, but just in case...
if( created )
{
console.write( "Created: ", template.tag );
var rally = this.getRallyPoint();
created.order( ORDER_GOTO, rally.x, rally.z );
}
}
}
else if( evt.productionType == PRODUCTION_RESEARCH )
{
// Apply the tech's effects
var tech = getTechnology( evt.name, this.player );
tech.applyEffects();
}
}
// Old training queue system
// ====================================================================
/*
function entityAddCreateQueue( template, tab, list )
{
// Make sure we have a queue to put things in...
if( !this.actions.create.queue )
this.actions.create.queue = new Array();
// Construct template object.
comboTemplate = template;
comboTemplate.list = list;
comboTemplate.tab = tab;
// Append to the end of this queue
this.actions.create.queue.push( template );
// If we're not already building something...
if( !this.actions.create.progress )
{
console.write( "Starting work on (unqueued) ", template.tag );
// Start the progress timer.
// - First parameter is target value (in this case, base build time in seconds)
// - Second parameter is increment per millisecond (use build rate modifier and correct for milliseconds)
// - Third parameter is the function to call when the timer finishes.
// - Fourth parameter is the scope under which to run that function (what the 'this' parameter should be)
this.actions.create.progress = new ProgressTimer( template.traits.creation.time,
this.actions.create.speed / 1000, entityCreateComplete, this )
}
}
// ====================================================================
// This is the syntax to add a function (or a property) to absolutely every entity.
Entity.prototype.add_create_queue = entityAddCreateQueue;
// ====================================================================
function entityCreateComplete()
{
// Get the unit that was at the head of our queue, and remove it.
// (Oh, for information about all these nifty properties and functions
// of the Array object that I use, see the ECMA-262 documentation
// at http://www.mozilla.org/js/language/E262-3.pdf. Bit technical but
// the sections on 'Native ECMAScript Objects' are quite useful)
var template = this.actions.create.queue.shift();
// Code to find a free space around an object is tedious and slow, so
// I wrote it in C. Takes the template object so it can determine how
// much space it needs to leave.
position = this.getSpawnPoint( template );
// The above function returns null if it couldn't find a large enough space.
if( !position )
{
console.write( "Couldn't train unit - not enough space" );
// Oh well. The player's just lost all the resources and time they put into
// construction - serves them right for not paying attention to the land
// around their barracks, doesn't it?
}
else
{
created = new Entity( template, position );
// Above shouldn't ever fail, but just in case...
if( created )
{
console.write( "Created: ", template.tag );
// Entities start under Gaia control - make the controller
// the same as our controller
created.player = this.player;
}
}
// If there's something else in the build queue...
if( this.actions.create.queue.length > 0 )
{
// Start on the next item.
template = this.actions.create.queue[0];
console.write( "Starting work on (queued) ", template.tag );
this.actions.create.progress = new ProgressTimer( template.traits.creation.time,
this.actions.create.speed / 1000, entityCreateComplete, this )
}
else
{
// Otherwise, delete the timer.
this.actions.create.progress = null;
}
}
// ====================================================================
function attemptAddToBuildQueue( entity, create_tag, tab, list )
{
template = getEntityTemplate( create_tag, entity.player );
result = checkEntityReqs( entity, template );
if (result == true) // If the entry meets requirements to be added to the queue (eg sufficient resources)
{
// Cycle through all costs of this entry.
pool = template.traits.creation.resource;
for ( resource in pool )
{
switch ( resource.toString() )
{
case "population":
case "housing":
break;
default:
// Deduct the given quantity of resources.
this.player.resources[resource.toString()] -= parseInt(pool[resource]);
console.write ("Spent " + pool[resource] + " " + resource + " to purchase " +
template.traits.id.generic);
break;
}
}
// Add entity to queue.
console.write( "Adding ", create_tag, " to build queue..." );
entity.addCreateQueue( template, tab, list );
return true;
}
else
{ // If not, output the error message.
console.write(result);
return false;
}
}
*/
// ====================================================================
function checkEntityReqs( player, template )
{
// Determines if the given entity meets requirements for production by the player, and returns an appropriate
// error string.
// A return value of 0 equals success -- entry meets requirements for production.
// Cycle through all resources that this item costs, and check the player can afford the cost.
var resources = template.traits.creation.resource;
for( resource in resources )
{
switch( resource.toString() )
{
case "population":
case "housing": // Ignore housing. It's handled in combination with population.
break
default:
// If the item costs more of this resource type than we have,
var cur = player.resources[resource];
var req = parseInt(resources[resource]);
if (req > cur)
{
// Return an error.
return ("Insufficient " + resource + "; " + (req-cur) + " more required.");
}
//else
// console.write("Player has at least " + req + " " + resource + ".");
break;
}
}
// Check if we have enough population space for the entity
if(template.traits.population && template.traits.population.rem)
{
var req = parseInt(template.traits.population.rem);
var space = player.resources.housing - player.resources.population;
// If the item costs more of this resource type than we have,
if (req > space)
{
// Return an error.
return ("Insufficient Housing; " + (req - space) + " more required.");
}
}
// Check if another entity must first exist.
// Check if another tech must first be researched.
// Check if the limit for this type of entity has been reached.
// If we passed all checks, return success. Entity can be queued.
return true;
}
// ====================================================================
function canGather( source, target )
{
- // Checks whether we're allowed to gather from a target entity (this involves looking at both the type and subtype).
+ // Checks whether we're allowed to gather from a target entity (this involves looking at both the type and subType).
if( !source.actions )
return false;
g = source.actions.gather;
s = target.traits.supply;
return ( g && s && g.resource && g.resource[s.type] &&
- ( s.subtype==s.type || g.resource[s.type][s.subtype] ) &&
+ ( s.subType==s.type || g.resource[s.type][s.subType] ) &&
( s.curr > 0 || s.max == 0 ) &&
s.dropsite_count[source.player.id] );
}
// ====================================================================
function canBuild( source, target )
{
// Checks whether we're allowed to build a target entity
if( !source.actions )
return false;
b = source.actions.build;
return (b && target.building != "" && target.player.id == source.player.id );
}
// ====================================================================
function canRepair( source, target )
{
- // Checks whether we're allowed to gather from a target entity (this involves looking at both the type and subtype).
+ // Checks whether we're allowed to gather from a target entity (this involves looking at both the type and subType).
if( !source.actions )
return false;
r = source.actions.repair;
return( r && target.traits.health.repairable && target.player.id == source.player.id
&& target.traits.health.curr < target.traits.health.max
&& target.building == "" );
}
// ====================================================================
function DamageType()
{
this.typeless = 0.0;
this.crush = 0.0;
this.pierce = 0.0;
this.hack = 0.0;
}
// ====================================================================
function DamageModifyAura( source, ally, bonus )
{
// Defines the effects of the DamageModify Aura. (Adjacent units have modified attack bonus.)
// The Courage Aura uses this function to give attack bonus to allies.
// The Fear Aura uses this function to give attack penalties to enemies.
this.source = source;
this.bonus = bonus;
this.ally = ally;
this.affects = function( e )
{
if( this.ally )
{
return ( e.player.id == this.source.player.id && e.actions && e.actions.attack );
}
else
{
return ( e.player.id != this.source.player.id && e.actions && e.actions.attack );
}
}
this.onEnter = function( e )
{
if( this.affects( e ) )
{
//console.write( "DamageModify aura: giving " + this.bonus + " damage to " + e );
e.actions.attack.damage += this.bonus;
}
};
this.onExit = function( e )
{
if( this.affects( e ) )
{
//console.write( "DamageModify aura: taking away " + this.bonus + " damage from " + e );
e.actions.attack.damage -= this.bonus;
}
};
}
// ====================================================================
function DropsiteAura( source, types )
{
// Defines the effects of the Gather aura. Enables resource gathering on entities
// near the source for it's owner.
this.source = source;
this.types = types;
this.affects = function( e )
{
return( e.traits.supply && this.types[e.traits.supply.type] );
}
this.onEnter = function( e )
{
if( this.affects( e ) )
{
//console.write( "Dropsite aura: adding +1 for " + this.source.player.id + " on " + e );
e.traits.supply.dropsite_count[this.source.player.id]++;
}
};
this.onExit = function( e )
{
if( this.affects( e ) )
{
//console.write( "Dropsite aura: adding -1 for " + this.source.player.id + " on " + e );
e.traits.supply.dropsite_count[this.source.player.id]--;
}
};
}
// ====================================================================
function InfidelityAura( source, time )
{
// Defines the effects of the Infidelity Aura. Changes ownership of entity when only one player's units surrounds them.
this.source = source;
this.time = time;
this.count = new Array( 9 );
for( i = 0; i <= 8; i++ )
{
this.count[i] = 0;
}
this.convertTimer = 0;
this.affects = function( e )
{
return ( e.player.id != 0 && ( !e.traits.auras || !e.traits.auras.infidelity ) );
}
this.onEnter = function( e )
{
if( this.affects( e ) )
{
//console.write( "Infidelity aura: adding +1 count to " + e.player.id );
this.count[e.player.id]++;
this.changePlayerIfNeeded();
}
};
this.onExit = function( e )
{
if( this.affects( e ) )
{
//console.write( "Infidelity aura: adding -1 count to " + e.player.id );
this.count[e.player.id]--;
this.changePlayerIfNeeded();
}
};
this.changePlayerIfNeeded = function()
{
if( this.count[this.source.player.id] == 0 )
{
// If our owner has nothing near us but someone else does, start a time to convert over if we haven't done so already
if( !this.convertTimer )
{
for( i = 1; i <= 8; i++ )
{
if( this.count[i] > 0 )
{
console.write( "Starting convert timer" );
this.convertTimer = setTimeout( this.convert, parseInt( this.time * 1000 ), this );
return;
}
}
}
}
// If we had started a convert timer before, cancel it (either we have units from our owner in range, or there are no units from anyone in range)
if( this.convertTimer )
{
console.write( "Cancelling convert timer" );
cancelTimer( this.convertTimer );
this.convertTimer = 0;
}
}
this.convert = function()
{
console.write( "Conversion time!" );
// Switch ownership to whichever non-gaia player has the most units near us
bestPlayer = 0;
bestCount = 0;
for( i = 1; i <= 8; i++ )
{
if( this.count[i] > bestCount )
{
bestCount = this.count[i];
bestPlayer = i;
}
}
if( bestCount > 0 )
{
//console.write( "Infidelity aura: changing ownership to " + bestPlayer );
this.source.player = players[bestPlayer];
}
this.convertTimer = 0;
}
}
// ====================================================================
function HealAura( source )
{
// Defines the effects of the Heal aura. Slowly heals nearby allies over time.
this.source = source;
this.affects = function( e )
{
return ( e.player.id == this.source.player.id && e.traits.health
&& e.traits.health.healable
&& e.traits.health.curr < e.traits.health.max );
}
this.onTick = function( e )
{
if( this.affects( e ) )
{
var hp = e.traits.health;
var rate = this.source.traits.auras.heal.rate;
hp.curr = Math.min( hp.max, hp.curr + rate );
}
};
}
// ====================================================================
function TrampleAura( source )
{
// Defines the effects of the Trample aura. Damages nearby enemies over time if the unit is charging or has recently charged.
this.source = source;
this.affects = function( e )
{
// Check if the target is an enemy foot unit with health and if we were running in the last 3 seconds
var a = this.source.traits.auras.trample;
return ( e.player.id != this.source.player.id && e.traits.health && e.hasClass("Foot")
&& (getGameTime() - this.source.last_run_time < a.duration) );
}
this.onTick = function( e )
{
if( this.affects( e ) )
{
// Set up the damage object
var dmg = new DamageType();
var a = this.source.traits.auras.trample;
dmg.crush = parseInt(a.damage * a.crush);
dmg.hack = parseInt(a.damage * a.hack);
dmg.pierce = parseInt(a.damage * a.pierce);
// Add flank bonus
if(e.traits.flank_penalty)
{
var flank = (e.getAttackDirections()-1)*e.traits.flank_penalty.value;
dmg.crush += dmg.crush * flank;
dmg.hack += dmg.hack * flank;
dmg.pierce += dmg.pierce * flank;
}
// Perform the damage
e.damage( dmg, this.source );
}
};
}
// ====================================================================
function SettlementAura( source )
{
// Defines the effects of the Settlement Aura. Changes ownership of entity when a civil center is on it.
this.source = source;
this.affects = function( e )
{
return ( e.hasClass("CivilCentre") );
}
this.onEnter = function( e )
{
if( this.affects( e ) )
{
// If a new Civ Centre has entered our radius, it must mean it's on us; switch player and become invisible
source.player = e.player;
source.visible = false;
}
};
this.onExit = function( e )
{
if( this.affects( e ) )
{
// If a Civ Centre has entered our radius, it must mean the one on us died; become visible again
source.visible = true;
}
}
}
// ====================================================================
function GotoInRange( x, y, run )
{
if ( !this.actions || !this.actions.move )
return;
//Add "true" at the end to indicate that this is a notification order.
if (run && this.actions.move.run)
this.order( ORDER_RUN, x, y - this.actions.escort.distance, true);
else
this.order( ORDER_GOTO, x, y - this.actions.escort.distance, true);
}
//=====================================================================
function entityEventFormation( evt )
{
if ( evt.formationEvent == FORMATION_ENTER )
{
if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) )
{
eval( this + this.getFormationBonus() ) += eval( this + this.getFormationBonusBase() ) *
this.getFormationBonusVal();
}
if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) )
{
eval( this + this.getFormationPenalty() ) -= eval( this + this.getFormationPenaltyBase() ) *
this.getFormationPenaltyVal();
}
}
//Reverse the bonuses
else if ( evt.formationEvent == FORMATION_LEAVE )
{
if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) )
{
eval( this + this.getFormationPenalty() ) += eval( this + this.getFormationPenaltyBase() ) *
this.getFormationPenaltyVal();
}
if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) )
{
eval( this + this.getFormationBonus() ) -= eval( this + this.getFormationBonusBase() ) * this.getFormationBonusVal();
}
}
}
\ No newline at end of file