Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks_workshop.xml (nonexistent)
@@ -1,32 +0,0 @@
-
-
-
- 175
-
- 450
-
-
-
- Siege -Infantry -Cavalry
-
-
- Siege Workshop
- Train siege weapons. Research training improvements.
- structures/barracks.png
-
-
- 0.8
-
- -units/{civ}_infantry_spearman_b
- -units/{civ}_infantry_pikeman_b
- -units/{civ}_infantry_swordsman_b
- units/{civ}_mechanical_siege_ballista_packed
- units/{civ}_mechanical_siege_scorpio_packed
- units/{civ}_mechanical_siege_oxybeles_packed
- units/{civ}_mechanical_siege_lithobolos_packed
- units/{civ}_mechanical_siege_polybolos_packed
- units/{civ}_mechanical_siege_ram
- units/{civ}_mechanical_siege_tower
-
-
-
Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks_workshop.xml
___________________________________________________________________
Deleted: svn:eol-style
## -1 +0,0 ##
-native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_siege_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_siege_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_siege_workshop.xml (nonexistent)
@@ -1,49 +0,0 @@
-
-
-
- 200
-
- 300
- 0
- 0
-
-
-
- 2000
- decay|rubble/rubble_stone_4x4
-
-
- mace
- Synergeîon Poliorkētṓn
- Siege Workshop
- Build siege engines. Research siege technologies.
- structures/siege_workshop.png
- phase_city
- Workshop
-
-
- 75
-
-
- 0.8
-
- units/{civ}_mechanical_siege_ballista_packed
- units/{civ}_mechanical_siege_scorpio_packed
- units/{civ}_mechanical_siege_oxybeles_packed
- units/{civ}_mechanical_siege_lithobolos_packed
- units/{civ}_mechanical_siege_polybolos_packed
- units/{civ}_mechanical_siege_ram
- units/{civ}_mechanical_siege_tower
-
-
- siege_attack
- siege_armor
- siege_cost_metal
- siege_cost_wood
- siege_bolt_accuracy
-
-
-
- structures/hellenes/blacksmith.xml
-
-
Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_siege_workshop.xml
___________________________________________________________________
Deleted: svn:eol-style
## -1 +0,0 ##
-native
\ No newline at end of property
Deleted: svn:mime-type
## -1 +0,0 ##
-text/xml
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/maps/scenarios/Sandbox - Macedonians.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenarios/Sandbox - Macedonians.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/maps/scenarios/Sandbox - Macedonians.xml (revision 20457)
@@ -1,10217 +1,10217 @@
sunset
0.00237305
0.251465
ocean
26.859
4.0
0.597656
0
1.07813
1.09375
0.126953
hdr
actor|geology/stone_scandiv_formation.xml
gaia/geology_metal_mediterranean_slabs
0
gaia/geology_metal_mediterranean_slabs
0
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
actor|props/flora/bush_medit_sm_dry.xml
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_aleppo_pine
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_poplar_lombardy
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/flora_tree_carob
0
gaia/geology_metal_mediterranean_slabs
0
gaia/geology_metal_mediterranean_slabs
0
gaia/geology_metal_mediterranean_slabs
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_metal_mediterranean_slabs
0
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
actor|props/flora/bush_medit_underbrush.xml
structures/mace_civil_centre
1
structures/mace_fortress
1
structures/mace_fortress
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_house
1
structures/mace_library
1
structures/mace_temple
1
structures/mace_theatron
1
structures/mace_market
1
structures/mace_fortress
1
structures/mace_defense_tower
1
structures/mace_barracks
1
structures/mace_barracks
1
structures/mace_barracks
1
structures/mace_blacksmith
1
structures/mace_dock
1
structures/mace_dock
1
structures/mace_dock
1
structures/mace_dock
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_slinger_b
1
units/mace_infantry_slinger_b
1
units/mace_infantry_slinger_b
1
units/mace_infantry_slinger_a
1
units/mace_infantry_slinger_a
1
units/mace_infantry_slinger_a
1
units/mace_infantry_slinger_e
1
units/mace_infantry_slinger_e
1
units/mace_infantry_slinger_e
1
units/mace_infantry_javelinist_b
1
units/mace_infantry_javelinist_b
1
units/mace_infantry_javelinist_b
1
units/mace_infantry_javelinist_a
1
units/mace_infantry_javelinist_a
1
units/mace_infantry_javelinist_a
1
units/mace_infantry_javelinist_e
1
units/mace_infantry_javelinist_e
1
units/mace_infantry_javelinist_e
1
units/mace_infantry_archer_b
1
units/mace_infantry_archer_b
1
units/mace_infantry_archer_b
1
units/mace_infantry_archer_a
1
units/mace_infantry_archer_a
1
units/mace_infantry_archer_a
1
units/mace_infantry_archer_e
1
units/mace_infantry_archer_e
1
units/mace_infantry_archer_e
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_e
1
units/mace_champion_infantry_e
1
units/mace_champion_infantry_e
1
units/mace_champion_infantry_e
1
units/mace_champion_infantry_e
1
units/mace_champion_cavalry
1
units/mace_champion_cavalry
1
units/mace_champion_cavalry
1
units/mace_champion_cavalry
1
units/mace_champion_cavalry
1
units/mace_champion_cavalry
1
units/mace_champion_cavalry
1
units/mace_champion_cavalry
1
units/mace_cavalry_spearman_e
1
units/mace_cavalry_spearman_e
1
units/mace_cavalry_spearman_e
1
units/mace_cavalry_spearman_a
1
units/mace_cavalry_spearman_a
1
units/mace_cavalry_spearman_a
1
units/mace_cavalry_spearman_b
1
units/mace_cavalry_spearman_b
1
units/mace_cavalry_spearman_b
1
units/mace_hero_alexander
1
units/mace_hero_craterus
1
units/mace_hero_demetrius
1
units/mace_hero_philip
1
units/mace_mechanical_siege_tower
1
units/mace_mechanical_siege_oxybeles_packed
1
units/mace_mechanical_siege_lithobolos_packed
1
units/mace_mechanical_siege_ram
1
units/mace_ship_bireme
1
units/mace_ship_trireme
1
units/mace_ship_merchant
1
units/mace_ship_merchant
1
units/mace_ship_merchant
1
units/mace_ship_fishing
1
units/mace_ship_fishing
1
units/mace_ship_fishing
1
units/mace_ship_fishing
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
units/mace_support_female_citizen
1
structures/mace_royal_stoa
1
other/hellenic_stoa
1
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/fauna_fish_tilapia
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_patch
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_tall
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
gaia/flora_tree_cretan_date_palm_short
0
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
gaia/special_ruins_stone_statues_roman
0
gaia/special_ruins_stone_statues_roman
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_stonemine_medit_quarry
0
gaia/geology_metal_mediterranean_slabs
0
gaia/geology_metal_mediterranean_slabs
0
gaia/geology_metal_mediterranean_slabs
0
gaia/geology_metal_mediterranean_slabs
0
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
gaia/special_treasure_food_barrels_buried
0
gaia/special_treasure_food_barrel
0
other/special_treasure_shipwreck_debris
0
other/special_treasure_shipwreck_debris
0
other/special_treasure_shipwreck_debris
0
other/special_treasure_shipwreck_debris
0
other/special_treasure_shipwreck_debris
0
other/special_treasure_shipwreck
0
other/special_treasure_shipwreck
0
other/special_treasure_shipwreck
0
other/special_treasure_shipwreck
0
other/special_treasure_shipwreck
0
other/special_treasure_shipwreck
0
gaia/special_treasure_stone
0
gaia/special_treasure_stone
0
gaia/special_treasure_golden_fleece
0
structures/spart_dock
2
- structures/mace_siege_workshop
+ structures/mace_workshop
1
structures/mace_wonder
1
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_cypress
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
gaia/flora_tree_medit_fan_palm
0
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
actor|geology/stone_medit_med.xml
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_b
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_e
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_infantry_pikeman_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
units/mace_champion_infantry_a
1
Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/headquarters.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/ai/petra/headquarters.js (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/headquarters.js (revision 20457)
@@ -1,2583 +1,2583 @@
var PETRA = function(m)
{
/**
* Headquarters
* Deal with high level logic for the AI. Most of the interesting stuff gets done here.
* Some tasks:
* -defining RESS needs
* -BO decisions.
* > training workers
* > building stuff (though we'll send that to bases)
* -picking strategy (specific manager?)
* -diplomacy -> diplomacyManager
* -planning attacks -> attackManager
* -picking new CC locations.
*/
m.HQ = function(Config)
{
this.Config = Config;
this.phasing = 0; // existing values: 0 means no, i > 0 means phasing towards phase i
// Cache the rates.
this.turnCache = {};
// Some resources objects (will be filled in init)
this.wantedRates = {};
this.currentRates = {};
this.lastFailedGather = {};
// workers configuration
this.targetNumWorkers = this.Config.Economy.targetNumWorkers;
this.supportRatio = this.Config.Economy.supportRatio;
this.fortStartTime = 180; // sentry defense towers, will start at fortStartTime + towerLapseTime
this.towerStartTime = 0; // stone defense towers, will start as soon as available
this.towerLapseTime = this.Config.Military.towerLapseTime;
this.fortressStartTime = 0; // will start as soon as available
this.fortressLapseTime = this.Config.Military.fortressLapseTime;
this.extraTowers = Math.round(Math.min(this.Config.difficulty, 3) * this.Config.personality.defensive);
this.extraFortresses = Math.round(Math.max(Math.min(this.Config.difficulty - 1, 2), 0) * this.Config.personality.defensive);
this.baseManagers = [];
this.attackManager = new m.AttackManager(this.Config);
this.buildManager = new m.BuildManager();
this.defenseManager = new m.DefenseManager(this.Config);
this.tradeManager = new m.TradeManager(this.Config);
this.navalManager = new m.NavalManager(this.Config);
this.researchManager = new m.ResearchManager(this.Config);
this.diplomacyManager = new m.DiplomacyManager(this.Config);
this.garrisonManager = new m.GarrisonManager(this.Config);
this.gameTypeManager = new m.GameTypeManager(this.Config);
this.capturableTargets = new Map();
this.capturableTargetsTime = 0;
};
/** More initialisation for stuff that needs the gameState */
m.HQ.prototype.init = function(gameState, queues)
{
this.territoryMap = m.createTerritoryMap(gameState);
// initialize base map. Each pixel is a base ID, or 0 if not or not accessible
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
// create borderMap: flag cells on the border of the map
// then this map will be completed with our frontier in updateTerritories
this.borderMap = m.createBorderMap(gameState);
// list of allowed regions
this.landRegions = {};
// try to determine if we have a water map
this.navalMap = false;
this.navalRegions = {};
for (let res of gameState.sharedScript.resourceInfo.codes)
{
this.wantedRates[res] = 0;
this.currentRates[res] = 0;
}
this.treasures = gameState.getEntities().filter(function (ent) {
let type = ent.resourceSupplyType();
return type && type.generic === "treasure";
});
this.treasures.registerUpdates();
this.currentPhase = gameState.currentPhase();
this.decayingStructures = new Set();
};
/**
* initialization needed after deserialization (only called when deserialization)
*/
m.HQ.prototype.postinit = function(gameState)
{
// Rebuild the base maps from the territory indices of each base
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
for (let base of this.baseManagers)
for (let j of base.territoryIndices)
this.basesMap.map[j] = base.ID;
for (let ent of gameState.getOwnEntities().values())
{
if (!ent.resourceDropsiteTypes() || ent.hasClass("Elephant"))
continue;
// Entities which have been built or have changed ownership after the last AI turn have no base.
// they will be dealt with in the next checkEvents
let baseID = ent.getMetadata(PlayerID, "base");
if (baseID === undefined)
continue;
let base = this.getBaseByID(baseID);
base.assignResourceToDropsite(gameState, ent);
}
this.updateTerritories(gameState);
};
/**
* returns the sea index linking regions 1 and region 2 (supposed to be different land region)
* otherwise return undefined
* for the moment, only the case land-sea-land is supported
*/
m.HQ.prototype.getSeaBetweenIndices = function (gameState, index1, index2)
{
let path = gameState.ai.accessibility.getTrajectToIndex(index1, index2);
if (path && path.length == 3 && gameState.ai.accessibility.regionType[path[1]] === "water")
return path[1];
if (this.Config.debug > 1)
{
API3.warn("bad path from " + index1 + " to " + index2 + " ??? " + uneval(path));
API3.warn(" regionLinks start " + uneval(gameState.ai.accessibility.regionLinks[index1]));
API3.warn(" regionLinks end " + uneval(gameState.ai.accessibility.regionLinks[index2]));
}
return undefined;
};
m.HQ.prototype.checkEvents = function (gameState, events, queues)
{
this.buildManager.checkEvents(gameState, events);
if (events.TerritoriesChanged.length || events.DiplomacyChanged.length)
this.updateTerritories(gameState);
for (let evt of events.DiplomacyChanged)
{
if (evt.player !== PlayerID && evt.otherPlayer !== PlayerID)
continue;
// Reset the entities collections which depend on diplomacy
gameState.resetOnDiplomacyChanged();
break;
}
for (let evt of events.Create)
{
// Let's check if we have a valuable foundation needing builders quickly
// (normal foundations are taken care in baseManager.assignToFoundations)
let ent = gameState.getEntityById(evt.entity);
if (!ent || !ent.isOwn(PlayerID) || ent.foundationProgress() === undefined)
continue;
if (ent.getMetadata(PlayerID, "base") == -1)
{
// Okay so let's try to create a new base around this.
let newbase = new m.BaseManager(gameState, this.Config);
newbase.init(gameState, "unconstructed");
newbase.setAnchor(gameState, ent);
this.baseManagers.push(newbase);
// Let's get a few units from other bases there to build this.
let builders = this.bulkPickWorkers(gameState, newbase, 10);
if (builders !== false)
{
builders.forEach(function (worker) {
worker.setMetadata(PlayerID, "base", newbase.ID);
worker.setMetadata(PlayerID, "subrole", "builder");
worker.setMetadata(PlayerID, "target-foundation", ent.id());
});
}
}
}
for (let evt of events.ConstructionFinished)
{
// Let's check if we have a building set to create a new base.
// TODO: move to the base manager.
if (evt.newentity)
{
if (evt.newentity === evt.entity) // repaired building
continue;
let ent = gameState.getEntityById(evt.newentity);
if (!ent || !ent.isOwn(PlayerID))
continue;
if (ent.getMetadata(PlayerID, "baseAnchor") === true)
{
let base = this.getBaseByID(ent.getMetadata(PlayerID, "base"));
if (base.constructing)
base.constructing = false;
base.anchor = ent;
base.anchorId = evt.newentity;
base.buildings.updateEnt(ent);
if (base.ID === this.baseManagers[1].ID)
{
// this is our first base, let us configure our starting resources
this.configFirstBase(gameState);
}
else
{
// let us hope this new base will fix our possible resource shortage
this.saveResources = undefined;
this.saveSpace = undefined;
}
}
}
}
for (let evt of events.OwnershipChanged) // capture events
{
if (evt.to !== PlayerID)
continue;
let ent = gameState.getEntityById(evt.entity);
if (!ent)
continue;
if (ent.position())
ent.setMetadata(PlayerID, "access", gameState.ai.accessibility.getAccessValue(ent.position()));
if (ent.hasClass("Unit"))
{
m.getBestBase(gameState, ent).assignEntity(gameState, ent);
ent.setMetadata(PlayerID, "role", undefined);
ent.setMetadata(PlayerID, "subrole", undefined);
ent.setMetadata(PlayerID, "plan", undefined);
ent.setMetadata(PlayerID, "PartOfArmy", undefined);
if (ent.hasClass("Trader"))
{
ent.setMetadata(PlayerID, "role", "trader");
ent.setMetadata(PlayerID, "route", undefined);
}
if (ent.hasClass("Worker"))
{
ent.setMetadata(PlayerID, "role", "worker");
ent.setMetadata(PlayerID, "subrole", "idle");
}
if (ent.hasClass("Ship"))
ent.setMetadata(PlayerID, "sea", gameState.ai.accessibility.getAccessValue(ent.position(), true));
if (!ent.hasClass("Support") && !ent.hasClass("Ship") && ent.attackTypes() !== undefined)
ent.setMetadata(PlayerID, "plan", -1);
continue;
}
if (ent.hasClass("CivCentre")) // build a new base around it
{
let newbase = new m.BaseManager(gameState, this.Config);
if (ent.foundationProgress() !== undefined)
newbase.init(gameState, "unconstructed");
else
newbase.init(gameState, "captured");
newbase.setAnchor(gameState, ent);
this.baseManagers.push(newbase);
newbase.assignEntity(gameState, ent);
}
else
{
// TODO should be reassigned later if a better base is captured
m.getBestBase(gameState, ent).assignEntity(gameState, ent);
if (ent.decaying())
{
if (ent.isGarrisonHolder() && this.garrisonManager.addDecayingStructure(gameState, evt.entity, true))
continue;
if (!this.decayingStructures.has(evt.entity))
this.decayingStructures.add(evt.entity);
}
}
}
// deal with the different rally points of training units: the rally point is set when the training starts
// for the time being, only autogarrison is used
for (let evt of events.TrainingStarted)
{
let ent = gameState.getEntityById(evt.entity);
if (!ent || !ent.isOwn(PlayerID))
continue;
if (!ent._entity.trainingQueue || !ent._entity.trainingQueue.length)
continue;
let metadata = ent._entity.trainingQueue[0].metadata;
if (metadata && metadata.garrisonType)
ent.setRallyPoint(ent, "garrison"); // trained units will autogarrison
else
ent.unsetRallyPoint();
}
for (let evt of events.TrainingFinished)
{
for (let entId of evt.entities)
{
let ent = gameState.getEntityById(entId);
if (!ent || !ent.isOwn(PlayerID))
continue;
if (!ent.position())
{
// we are autogarrisoned, check that the holder is registered in the garrisonManager
let holderId = ent.unitAIOrderData()[0].target;
let holder = gameState.getEntityById(holderId);
if (holder)
this.garrisonManager.registerHolder(gameState, holder);
}
else if (ent.getMetadata(PlayerID, "garrisonType"))
{
// we were supposed to be autogarrisoned, but this has failed (may-be full)
ent.setMetadata(PlayerID, "garrisonType", undefined);
}
// Check if this unit is no more needed in its attack plan
// (happen when the training ends after the attack is started or aborted)
let plan = ent.getMetadata(PlayerID, "plan");
if (plan !== undefined && plan >= 0)
{
let attack = this.attackManager.getPlan(plan);
if (!attack || attack.state !== "unexecuted")
ent.setMetadata(PlayerID, "plan", -1);
}
// Assign it immediately to something useful to do
if (ent.getMetadata(PlayerID, "role") === "worker")
{
let base;
if (ent.getMetadata(PlayerID, "base") === undefined)
{
base = m.getBestBase(gameState, ent);
base.assignEntity(gameState, ent);
}
else
base = this.getBaseByID(ent.getMetadata(PlayerID, "base"));
base.reassignIdleWorkers(gameState, [ent]);
base.workerObject.update(gameState, ent);
}
else if (ent.resourceSupplyType() && ent.position())
{
let type = ent.resourceSupplyType();
if (!type.generic)
continue;
let dropsites = gameState.getOwnDropsites(type.generic);
let pos = ent.position();
let access = gameState.ai.accessibility.getAccessValue(pos);
let distmin = Math.min();
let goal;
for (let dropsite of dropsites.values())
{
if (!dropsite.position() || dropsite.getMetadata(PlayerID, "access") !== access)
continue;
let dist = API3.SquareVectorDistance(pos, dropsite.position());
if (dist > distmin)
continue;
distmin = dist;
goal = dropsite.position();
}
if (goal)
ent.moveToRange(goal[0], goal[1]);
}
}
}
for (let evt of events.TerritoryDecayChanged)
{
let ent = gameState.getEntityById(evt.entity);
if (!ent || !ent.isOwn(PlayerID) || ent.foundationProgress() !== undefined)
continue;
if (evt.to)
{
if (ent.isGarrisonHolder() && this.garrisonManager.addDecayingStructure(gameState, evt.entity))
continue;
if (!this.decayingStructures.has(evt.entity))
this.decayingStructures.add(evt.entity);
}
else if (ent.isGarrisonHolder())
this.garrisonManager.removeDecayingStructure(evt.entity);
}
// Then deals with decaying structures: destroy them if being lost to enemy (except in easier difficulties)
if (this.Config.difficulty < 2)
return;
for (let entId of this.decayingStructures)
{
let ent = gameState.getEntityById(entId);
if (ent && ent.decaying() && ent.isOwn(PlayerID))
{
let capture = ent.capturePoints();
if (!capture)
continue;
let captureRatio = capture[PlayerID] / capture.reduce((a, b) => a + b);
if (captureRatio < 0.50)
continue;
let decayToGaia = true;
for (let i = 1; i < capture.length; ++i)
{
if (gameState.isPlayerAlly(i) || !capture[i])
continue;
decayToGaia = false;
break;
}
if (decayToGaia)
continue;
let ratioMax = 0.70 + randFloat(0., 0.1);
for (let evt of events.Attacked)
{
if (ent.id() != evt.target)
continue;
ratioMax = 0.85 + randFloat(0., 0.1);
break;
}
if (captureRatio > ratioMax)
continue;
ent.destroy();
}
this.decayingStructures.delete(entId);
}
};
/** Ensure that all requirements are met when phasing up*/
m.HQ.prototype.checkPhaseRequirements = function(gameState, queues)
{
if (gameState.getNumberOfPhases() == this.currentPhase)
return;
let requirements = gameState.getPhaseEntityRequirements(this.currentPhase + 1);
let plan;
let queue;
for (let entityReq of requirements)
{
// Village requirements are met elsewhere by constructing more houses
if (entityReq.class === "Village" || entityReq.class === "NotField")
continue;
if (gameState.getOwnEntitiesByClass(entityReq.class, true).length >= entityReq.count)
continue;
switch (entityReq.class)
{
case "Town":
if (!queues.economicBuilding.hasQueuedUnits() &&
!queues.militaryBuilding.hasQueuedUnits() &&
!queues.defenseBuilding.hasQueuedUnits())
{
if (!gameState.getOwnEntitiesByClass("BarterMarket", true).hasEntities() &&
this.canBuild(gameState, "structures/{civ}_market"))
{
plan = new m.ConstructionPlan(gameState, "structures/{civ}_market");
queue = "economicBuilding";
break;
}
if (!gameState.getOwnEntitiesByClass("Temple", true).hasEntities() &&
this.canBuild(gameState, "structures/{civ}_temple"))
{
plan = new m.ConstructionPlan(gameState, "structures/{civ}_temple");
queue = "economicBuilding";
break;
}
if (!gameState.getOwnEntitiesByClass("Blacksmith", true).hasEntities() &&
this.canBuild(gameState, "structures/{civ}_blacksmith"))
{
plan = new m.ConstructionPlan(gameState, "structures/{civ}_blacksmith");
queue = "militaryBuilding";
break;
}
if (this.canBuild(gameState, "structures/{civ}_defense_tower"))
{
plan = new m.ConstructionPlan(gameState, "structures/{civ}_defense_tower");
queue = "defenseBuilding";
break;
}
}
break;
default:
// All classes not dealt with inside vanilla game.
// We put them for the time being on the economic queue, except if wonder
queue = entityReq.class === "Wonder" ? "wonder" : "economicBuilding";
if (!queues[queue].hasQueuedUnits())
{
let structure = this.buildManager.findStructureWithClass(gameState, [entityReq.class]);
if (structure && this.canBuild(gameState, structure))
plan = new m.ConstructionPlan(gameState, structure);
}
}
if (plan)
{
if (queue == "wonder")
{
gameState.ai.queueManager.changePriority("majorTech", 400);
plan.queueToReset = "majorTech";
}
else
{
gameState.ai.queueManager.changePriority(queue, 1000);
plan.queueToReset = queue;
}
queues[queue].addPlan(plan);
return;
}
}
};
/** Called by any "phase" research plan once it's started */
m.HQ.prototype.OnPhaseUp = function(gameState, phase)
{
};
/** This code trains citizen workers, trying to keep close to a ratio of worker/soldiers */
m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
{
// default template
let requirementsDef = [ ["costsResource", 1, "food"] ];
let classesDef = ["Support", "Worker"];
let templateDef = this.findBestTrainableUnit(gameState, classesDef, requirementsDef);
// counting the workers that aren't part of a plan
let numberOfWorkers = 0; // all workers
let numberOfSupports = 0; // only support workers (i.e. non fighting)
gameState.getOwnUnits().forEach (function (ent) {
if (ent.getMetadata(PlayerID, "role") === "worker" && ent.getMetadata(PlayerID, "plan") === undefined)
{
++numberOfWorkers;
if (ent.hasClass("Support"))
++numberOfSupports;
}
});
let numberInTraining = 0;
gameState.getOwnTrainingFacilities().forEach(function(ent) {
for (let item of ent.trainingQueue())
{
numberInTraining += item.count;
if (item.metadata && item.metadata.role && item.metadata.role === "worker" && item.metadata.plan === undefined)
{
numberOfWorkers += item.count;
if (item.metadata.support)
numberOfSupports += item.count;
}
}
});
// Anticipate the optimal batch size when this queue will start
// and adapt the batch size of the first and second queued workers to the present population
// to ease a possible recovery if our population was drastically reduced by an attack
// (need to go up to second queued as it is accounted in queueManager)
let size = numberOfWorkers < 12 ? 1 : Math.min(5, Math.ceil(numberOfWorkers / 10));
if (queues.villager.plans[0])
{
queues.villager.plans[0].number = Math.min(queues.villager.plans[0].number, size);
if (queues.villager.plans[1])
queues.villager.plans[1].number = Math.min(queues.villager.plans[1].number, size);
}
if (queues.citizenSoldier.plans[0])
{
queues.citizenSoldier.plans[0].number = Math.min(queues.citizenSoldier.plans[0].number, size);
if (queues.citizenSoldier.plans[1])
queues.citizenSoldier.plans[1].number = Math.min(queues.citizenSoldier.plans[1].number, size);
}
let numberOfQueuedSupports = queues.villager.countQueuedUnits();
let numberOfQueuedSoldiers = queues.citizenSoldier.countQueuedUnits();
let numberQueued = numberOfQueuedSupports + numberOfQueuedSoldiers;
let numberTotal = numberOfWorkers + numberQueued;
if (this.saveResources && numberTotal > this.Config.Economy.popPhase2 + 10)
return;
if (numberTotal > this.targetNumWorkers || (numberTotal >= this.Config.Economy.popPhase2 &&
this.currentPhase == 1 && !gameState.isResearching(gameState.getPhaseName(2))))
return;
if (numberQueued > 50 || (numberOfQueuedSupports > 20 && numberOfQueuedSoldiers > 20) || numberInTraining > 15)
return;
// Choose whether we want soldiers or support units: when full pop, we aim at targetNumWorkers workers
// with supportRatio fraction of support units. But we want to have more support (less cost) at startup.
// So we take: supportRatio*targetNumWorkers*(1 - exp(-alfa*currentWorkers/supportRatio/targetNumWorkers))
// This gives back supportRatio*targetNumWorkers when currentWorkers >> supportRatio*targetNumWorkers
// and gives a ratio alfa at startup.
let supportRatio = this.supportRatio;
let alpha = 0.85;
if (!gameState.isTemplateAvailable(gameState.applyCiv("structures/{civ}_field")))
supportRatio = Math.min(this.supportRatio, 0.1);
if (this.attackManager.rushNumber < this.attackManager.maxRushes || this.attackManager.upcomingAttacks.Rush.length)
alpha = 0.7;
if (gameState.isCeasefireActive())
alpha += (1 - alpha) * Math.min(Math.max(gameState.ceasefireTimeRemaining - 120, 0), 180) / 180;
let supportMax = supportRatio * this.targetNumWorkers;
let supportNum = supportMax * (1 - Math.exp(-alpha*numberTotal/supportMax));
let template;
if (numberOfSupports + numberOfQueuedSupports > supportNum)
{
let requirements;
if (numberTotal < 45)
requirements = [ ["speed", 0.5], ["costsResource", 0.5, "stone"], ["costsResource", 0.5, "metal"] ];
else
requirements = [ ["strength", 1] ];
let classes = ["CitizenSoldier", "Infantry"];
// We want at least 33% ranged and 33% melee
classes.push(pickRandom(["Ranged", "Melee", "Infantry"]));
template = this.findBestTrainableUnit(gameState, classes, requirements);
}
// If the template variable is empty, the default unit (Support unit) will be used
// base "0" means automatic choice of base
if (!template && templateDef)
queues.villager.addPlan(new m.TrainingPlan(gameState, templateDef, { "role": "worker", "base": 0, "support": true }, size, size));
else if (template)
queues.citizenSoldier.addPlan(new m.TrainingPlan(gameState, template, { "role": "worker", "base": 0 }, size, size));
};
/** picks the best template based on parameters and classes */
m.HQ.prototype.findBestTrainableUnit = function(gameState, classes, requirements)
{
let units;
if (classes.indexOf("Hero") !== -1)
units = gameState.findTrainableUnits(classes, []);
else if (classes.indexOf("Siege") !== -1) // We do not want siege tower as AI does not know how to use it
units = gameState.findTrainableUnits(classes, ["SiegeTower"]);
else // We do not want hero when not explicitely specified
units = gameState.findTrainableUnits(classes, ["Hero"]);
if (units.length === 0)
return undefined;
let parameters = requirements.slice();
let remainingResources = this.getTotalResourceLevel(gameState); // resources (estimation) still gatherable in our territory
let availableResources = gameState.ai.queueManager.getAvailableResources(gameState); // available (gathered) resources
for (let type in remainingResources)
{
if (availableResources[type] > 800)
continue;
if (remainingResources[type] > 800)
continue;
let costsResource = remainingResources[type] > 400 ? 0.6 : 0.2;
let toAdd = true;
for (let param of parameters)
{
if (param[0] !== "costsResource" || param[2] !== type)
continue;
param[1] = Math.min( param[1], costsResource );
toAdd = false;
break;
}
if (toAdd)
parameters.push( [ "costsResource", costsResource, type ] );
}
units.sort(function(a, b) {
let aCost = 1 + a[1].costSum();
let bCost = 1 + b[1].costSum();
let aValue = 0.1;
let bValue = 0.1;
for (let param of parameters)
{
if (param[0] == "strength")
{
aValue += m.getMaxStrength(a[1]) * param[1];
bValue += m.getMaxStrength(b[1]) * param[1];
}
else if (param[0] == "siegeStrength")
{
aValue += m.getMaxStrength(a[1], "Structure") * param[1];
bValue += m.getMaxStrength(b[1], "Structure") * param[1];
}
else if (param[0] == "speed")
{
aValue += a[1].walkSpeed() * param[1];
bValue += b[1].walkSpeed() * param[1];
}
else if (param[0] == "costsResource")
{
// requires a third parameter which is the resource
if (a[1].cost()[param[2]])
aValue *= param[1];
if (b[1].cost()[param[2]])
bValue *= param[1];
}
else if (param[0] == "canGather")
{
// checking against wood, could be anything else really.
if (a[1].resourceGatherRates() && a[1].resourceGatherRates()["wood.tree"])
aValue *= param[1];
if (b[1].resourceGatherRates() && b[1].resourceGatherRates()["wood.tree"])
bValue *= param[1];
}
else
API3.warn(" trainMoreUnits avec non prevu " + uneval(param));
}
return -aValue/aCost + bValue/bCost;
});
return units[0][0];
};
/**
* returns an entity collection of workers through BaseManager.pickBuilders
* TODO: when same accessIndex, sort by distance
*/
m.HQ.prototype.bulkPickWorkers = function(gameState, baseRef, number)
{
let accessIndex = baseRef.accessIndex;
if (!accessIndex)
return false;
// sorting bases by whether they are on the same accessindex or not.
let baseBest = this.baseManagers.slice().sort(function (a,b) {
if (a.accessIndex == accessIndex && b.accessIndex != accessIndex)
return -1;
else if (b.accessIndex == accessIndex && a.accessIndex != accessIndex)
return 1;
return 0;
});
let needed = number;
let workers = new API3.EntityCollection(gameState.sharedScript);
for (let base of baseBest)
{
if (base.ID === baseRef.ID)
continue;
base.pickBuilders(gameState, workers, needed);
if (workers.length < number)
needed = number - workers.length;
else
break;
}
if (!workers.length)
return false;
return workers;
};
m.HQ.prototype.getTotalResourceLevel = function(gameState)
{
let total = {};
for (let res of gameState.sharedScript.resourceInfo.codes)
total[res] = 0;
for (let base of this.baseManagers)
for (let res in total)
total[res] += base.getResourceLevel(gameState, res);
return total;
};
/**
* returns the current gather rate
* This is not per-se exact, it performs a few adjustments ad-hoc to account for travel distance, stuffs like that.
*/
m.HQ.prototype.GetCurrentGatherRates = function(gameState)
{
if (!this.turnCache.gatherRates)
{
for (let res in this.currentRates)
this.currentRates[res] = 0.5 * this.GetTCResGatherer(res);
for (let base of this.baseManagers)
base.getGatherRates(gameState, this.currentRates);
for (let res in this.currentRates)
{
if (this.currentRates[res] < 0)
{
if (this.Config.debug > 0)
API3.warn("Petra: current rate for " + res + " < 0 with " + this.GetTCResGatherer(res) + " moved gatherers");
this.currentRates[res] = 0;
}
}
this.turnCache.gatherRates = true;
}
return this.currentRates;
};
/**
* Pick the resource which most needs another worker
* How this works:
* We get the rates we would want to have to be able to deal with our plans
* We get our current rates
* We compare; we pick the one where the discrepancy is highest.
* Need to balance long-term needs and possible short-term needs.
*/
m.HQ.prototype.pickMostNeededResources = function(gameState)
{
this.wantedRates = gameState.ai.queueManager.wantedGatherRates(gameState);
let currentRates = this.GetCurrentGatherRates(gameState);
let needed = [];
for (let res in this.wantedRates)
needed.push({ "type": res, "wanted": this.wantedRates[res], "current": currentRates[res] });
needed.sort((a, b) => {
let va = Math.max(0, a.wanted - a.current) / (a.current + 1);
let vb = Math.max(0, b.wanted - b.current) / (b.current + 1);
// If they happen to be equal (generally this means "0" aka no need), make it fair.
if (va === vb)
return a.current - b.current;
return vb - va;
});
return needed;
};
/**
* Returns the best position to build a new Civil Centre
* Whose primary function would be to reach new resources of type "resource".
*/
m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource, proximity, fromStrategic)
{
// This builds a map. The procedure is fairly simple. It adds the resource maps
// (which are dynamically updated and are made so that they will facilitate DP placement)
// Then look for a good spot.
Engine.ProfileStart("findEconomicCCLocation");
// obstruction map
let obstructions = m.createObstructionMap(gameState, 0, template);
let halfSize = 0;
if (template.get("Footprint/Square"))
halfSize = Math.max(+template.get("Footprint/Square/@depth"), +template.get("Footprint/Square/@width")) / 2;
else if (template.get("Footprint/Circle"))
halfSize = +template.get("Footprint/Circle/@radius");
let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre"));
let dpEnts = gameState.getOwnDropsites().filter(API3.Filters.not(API3.Filters.byClassesOr(["CivCentre", "Elephant"])));
let ccList = [];
for (let cc of ccEnts.values())
ccList.push({"pos": cc.position(), "ally": gameState.isPlayerAlly(cc.owner())});
let dpList = [];
for (let dp of dpEnts.values())
dpList.push({"pos": dp.position()});
let bestIdx;
let bestVal;
let radius = Math.ceil(template.obstructionRadius().max / obstructions.cellSize);
let scale = 250 * 250;
let proxyAccess;
let nbShips = this.navalManager.transportShips.length;
if (proximity) // this is our first base
{
// if our first base, ensure room around
radius = Math.ceil((template.obstructionRadius().max + 8) / obstructions.cellSize);
// scale is the typical scale at which we want to find a location for our first base
// look for bigger scale if we start from a ship (access < 2) or from a small island
let cellArea = gameState.getPassabilityMap().cellSize * gameState.getPassabilityMap().cellSize;
proxyAccess = gameState.ai.accessibility.getAccessValue(proximity);
if (proxyAccess < 2 || cellArea*gameState.ai.accessibility.regionSize[proxyAccess] < 24000)
scale = 400 * 400;
}
let width = this.territoryMap.width;
let cellSize = this.territoryMap.cellSize;
for (let j = 0; j < this.territoryMap.length; ++j)
{
if (this.territoryMap.getOwnerIndex(j) !== 0)
continue;
// with enough room around to build the cc
let i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
if (i < 0)
continue;
// we require that it is accessible
let index = gameState.ai.accessibility.landPassMap[i];
if (!this.landRegions[index])
continue;
if (proxyAccess && nbShips === 0 && proxyAccess !== index)
continue;
let norm = 0.5; // TODO adjust it, knowing that we will sum 5 maps
// checking distance to other cc
let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
if (proximity) // this is our first cc, let's do it near our units
norm /= 1 + API3.SquareVectorDistance(proximity, pos) / scale;
else
{
let minDist = Math.min();
for (let cc of ccList)
{
let dist = API3.SquareVectorDistance(cc.pos, pos);
if (dist < 14000) // Reject if too near from any cc
{
norm = 0;
break;
}
if (!cc.ally)
continue;
if (dist < 40000) // Reject if too near from an allied cc
{
norm = 0;
break;
}
if (dist < 62000) // Disfavor if quite near an allied cc
norm *= 0.5;
if (dist < minDist)
minDist = dist;
}
if (norm === 0)
continue;
if (minDist > 170000 && !this.navalMap) // Reject if too far from any allied cc (not connected)
continue;
if (minDist > 130000) // Disfavor if quite far from any allied cc
{
if (this.navalMap)
{
if (minDist > 250000)
norm *= 0.5;
else
norm *= 0.8;
}
else
norm *= 0.5;
}
for (let dp of dpList)
{
let dist = API3.SquareVectorDistance(dp.pos, pos);
if (dist < 3600)
{
norm = 0;
break;
}
else if (dist < 6400)
norm *= 0.5;
}
if (norm === 0)
continue;
}
if (this.borderMap.map[j] & m.fullBorder_Mask) // disfavor the borders of the map
norm *= 0.5;
let val = 2*gameState.sharedScript.ccResourceMaps[resource].map[j];
for (let res in gameState.sharedScript.resourceMaps)
if (res !== "food")
val += gameState.sharedScript.ccResourceMaps[res].map[j];
val *= norm;
if (bestVal !== undefined && val < bestVal)
continue;
if (this.isDangerousLocation(gameState, pos, halfSize))
continue;
bestVal = val;
bestIdx = i;
}
Engine.ProfileStop();
if (bestVal === undefined)
return false;
let cut = 60;
if (fromStrategic || proximity) // be less restrictive
cut = 30;
if (this.Config.debug > 1)
API3.warn("we have found a base for " + resource + " with best (cut=" + cut + ") = " + bestVal);
// not good enough.
if (bestVal < cut)
return false;
let x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
let z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
// Define a minimal number of wanted ships in the seas reaching this new base
let indexIdx = gameState.ai.accessibility.landPassMap[bestIdx];
for (let base of this.baseManagers)
{
if (!base.anchor || base.accessIndex === indexIdx)
continue;
let sea = this.getSeaBetweenIndices(gameState, base.accessIndex, indexIdx);
if (sea !== undefined)
this.navalManager.setMinimalTransportShips(gameState, sea, 1);
}
return [x, z];
};
/**
* Returns the best position to build a new Civil Centre
* Whose primary function would be to assure territorial continuity with our allies
*/
m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
{
// This builds a map. The procedure is fairly simple.
// We minimize the Sum((dist-300)**2) where the sum is on the three nearest allied CC
// with the constraints that all CC have dist > 200 and at least one have dist < 400
// This needs at least 2 CC. Otherwise, go back to economic CC.
let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre"));
let ccList = [];
let numAllyCC = 0;
for (let cc of ccEnts.values())
{
let ally = gameState.isPlayerAlly(cc.owner());
ccList.push({"pos": cc.position(), "ally": ally});
if (ally)
++numAllyCC;
}
if (numAllyCC < 2)
return this.findEconomicCCLocation(gameState, template, "wood", undefined, true);
Engine.ProfileStart("findStrategicCCLocation");
// obstruction map
let obstructions = m.createObstructionMap(gameState, 0, template);
let halfSize = 0;
if (template.get("Footprint/Square"))
halfSize = Math.max(+template.get("Footprint/Square/@depth"), +template.get("Footprint/Square/@width")) / 2;
else if (template.get("Footprint/Circle"))
halfSize = +template.get("Footprint/Circle/@radius");
let bestIdx;
let bestVal;
let radius = Math.ceil(template.obstructionRadius().max / obstructions.cellSize);
let width = this.territoryMap.width;
let cellSize = this.territoryMap.cellSize;
let currentVal, delta;
let distcc0, distcc1, distcc2;
for (let j = 0; j < this.territoryMap.length; ++j)
{
if (this.territoryMap.getOwnerIndex(j) !== 0)
continue;
// with enough room around to build the cc
let i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
if (i < 0)
continue;
// we require that it is accessible
let index = gameState.ai.accessibility.landPassMap[i];
if (!this.landRegions[index])
continue;
// checking distances to other cc
let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
let minDist = Math.min();
distcc0 = undefined;
for (let cc of ccList)
{
let dist = API3.SquareVectorDistance(cc.pos, pos);
if (dist < 14000) // Reject if too near from any cc
{
minDist = 0;
break;
}
if (!cc.ally)
continue;
if (dist < 62000) // Reject if quite near from ally cc
{
minDist = 0;
break;
}
if (dist < minDist)
minDist = dist;
if (!distcc0 || dist < distcc0)
{
distcc2 = distcc1;
distcc1 = distcc0;
distcc0 = dist;
}
else if (!distcc1 || dist < distcc1)
{
distcc2 = distcc1;
distcc1 = dist;
}
else if (!distcc2 || dist < distcc2)
distcc2 = dist;
}
if (minDist < 1 || minDist > 170000 && !this.navalMap)
continue;
delta = Math.sqrt(distcc0) - 300; // favor a distance of 300
currentVal = delta*delta;
delta = Math.sqrt(distcc1) - 300;
currentVal += delta*delta;
if (distcc2)
{
delta = Math.sqrt(distcc2) - 300;
currentVal += delta*delta;
}
// disfavor border of the map
if (this.borderMap.map[j] & m.fullBorder_Mask)
currentVal += 10000;
if (bestVal !== undefined && currentVal > bestVal)
continue;
if (this.isDangerousLocation(gameState, pos, halfSize))
continue;
bestVal = currentVal;
bestIdx = i;
}
if (this.Config.debug > 1)
API3.warn("We've found a strategic base with bestVal = " + bestVal);
Engine.ProfileStop();
if (bestVal === undefined)
return undefined;
let x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
let z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
// Define a minimal number of wanted ships in the seas reaching this new base
let indexIdx = gameState.ai.accessibility.landPassMap[bestIdx];
for (let base of this.baseManagers)
{
if (!base.anchor || base.accessIndex === indexIdx)
continue;
let sea = this.getSeaBetweenIndices(gameState, base.accessIndex, indexIdx);
if (sea !== undefined)
this.navalManager.setMinimalTransportShips(gameState, sea, 1);
}
return [x, z];
};
/**
* Returns the best position to build a new market: if the allies already have a market, build it as far as possible
* from it, although not in our border to be able to defend it easily. If no allied market, our second market will
* follow the same logic.
* To do so, we suppose that the gain/distance is an increasing function of distance and look for the max distance
* for performance reasons.
*/
m.HQ.prototype.findMarketLocation = function(gameState, template)
{
let markets = gameState.updatingCollection("diplo-ExclusiveAllyMarkets", API3.Filters.byClass("Market"), gameState.getExclusiveAllyEntities()).toEntityArray();
if (!markets.length)
markets = gameState.updatingCollection("OwnMarkets", API3.Filters.byClass("Market"), gameState.getOwnStructures()).toEntityArray();
if (!markets.length) // this is the first market. For the time being, place it arbitrarily by the ConstructionPlan
return [-1, -1, -1, 0];
// obstruction map
let obstructions = m.createObstructionMap(gameState, 0, template);
let halfSize = 0;
if (template.get("Footprint/Square"))
halfSize = Math.max(+template.get("Footprint/Square/@depth"), +template.get("Footprint/Square/@width")) / 2;
else if (template.get("Footprint/Circle"))
halfSize = +template.get("Footprint/Circle/@radius");
let bestIdx;
let bestJdx;
let bestVal;
let bestDistSq;
let bestGainMult;
let radius = Math.ceil(template.obstructionRadius().max / obstructions.cellSize);
let isNavalMarket = template.hasClass("NavalMarket");
let width = this.territoryMap.width;
let cellSize = this.territoryMap.cellSize;
let traderTemplatesGains = gameState.getTraderTemplatesGains();
for (let j = 0; j < this.territoryMap.length; ++j)
{
// do not try on the narrow border of our territory
if (this.borderMap.map[j] & m.narrowFrontier_Mask)
continue;
if (this.basesMap.map[j] === 0) // only in our territory
continue;
// with enough room around to build the market
let i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
if (i < 0)
continue;
let index = gameState.ai.accessibility.landPassMap[i];
if (!this.landRegions[index])
continue;
let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
// checking distances to other markets
let maxVal = 0;
let maxDistSq;
let maxGainMult;
let gainMultiplier;
for (let market of markets)
{
if (isNavalMarket && market.hasClass("NavalMarket"))
{
if (m.getSeaAccess(gameState, market) !== gameState.ai.accessibility.getAccessValue(pos, true))
continue;
gainMultiplier = traderTemplatesGains.navalGainMultiplier;
}
else if (m.getLandAccess(gameState, market) === index &&
!m.isLineInsideEnemyTerritory(gameState, market.position(), pos))
gainMultiplier = traderTemplatesGains.landGainMultiplier;
else
continue;
if (!gainMultiplier)
continue;
let distSq = API3.SquareVectorDistance(market.position(), pos);
if (gainMultiplier * distSq > maxVal)
{
maxVal = gainMultiplier * distSq;
maxDistSq = distSq;
maxGainMult = gainMultiplier;
}
}
if (maxVal === 0)
continue;
if (bestVal !== undefined && maxVal < bestVal)
continue;
if (this.isDangerousLocation(gameState, pos, halfSize))
continue;
bestVal = maxVal;
bestDistSq = maxDistSq;
bestGainMult = maxGainMult;
bestIdx = i;
bestJdx = j;
}
if (this.Config.debug > 1)
API3.warn("We found a market position with bestVal = " + bestVal);
if (bestVal === undefined) // no constraints. For the time being, place it arbitrarily by the ConstructionPlan
return [-1, -1, -1, 0];
let expectedGain = Math.round(bestGainMult * TradeGain(bestDistSq, gameState.sharedScript.mapSize));
if (this.Config.debug > 1)
API3.warn("this would give a trading gain of " + expectedGain);
// do not keep it if gain is too small, except if this is our first BarterMarket
if (expectedGain < this.tradeManager.minimalGain ||
expectedGain < 8 && (!template.hasClass("BarterMarket") || gameState.getOwnEntitiesByClass("BarterMarket", true).hasEntities()))
return false;
let x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
let z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
return [x, z, this.basesMap.map[bestJdx], expectedGain];
};
/**
* Returns the best position to build defensive buildings (fortress and towers)
* Whose primary function is to defend our borders
*/
m.HQ.prototype.findDefensiveLocation = function(gameState, template)
{
// We take the point in our territory which is the nearest to any enemy cc
// but requiring a minimal distance with our other defensive structures
// and not in range of any enemy defensive structure to avoid building under fire.
let ownStructures = gameState.getOwnStructures().filter(API3.Filters.byClassesOr(["Fortress", "Tower"])).toEntityArray();
let enemyStructures = gameState.getEnemyStructures().filter(API3.Filters.not(API3.Filters.byOwner(0))).
filter(API3.Filters.byClassesOr(["CivCentre", "Fortress", "Tower"]));
if (!enemyStructures.hasEntities()) // we may be in cease fire mode, build defense against neutrals
{
enemyStructures = gameState.getNeutralStructures().filter(API3.Filters.not(API3.Filters.byOwner(0))).
filter(API3.Filters.byClassesOr(["CivCentre", "Fortress", "Tower"]));
if (!enemyStructures.hasEntities() && !gameState.getAlliedVictory())
enemyStructures = gameState.getAllyStructures().filter(API3.Filters.not(API3.Filters.byOwner(PlayerID))).
filter(API3.Filters.byClassesOr(["CivCentre", "Fortress", "Tower"]));
if (!enemyStructures.hasEntities())
return undefined;
}
enemyStructures = enemyStructures.toEntityArray();
let wonderMode = gameState.getGameType() === "wonder";
let wonderDistmin;
let wonders;
if (wonderMode)
{
wonders = gameState.getOwnStructures().filter(API3.Filters.byClass("Wonder")).toEntityArray();
wonderMode = wonders.length !== 0;
if (wonderMode)
wonderDistmin = (50 + wonders[0].footprintRadius()) * (50 + wonders[0].footprintRadius());
}
// obstruction map
let obstructions = m.createObstructionMap(gameState, 0, template);
let halfSize = 0;
if (template.get("Footprint/Square"))
halfSize = Math.max(+template.get("Footprint/Square/@depth"), +template.get("Footprint/Square/@width")) / 2;
else if (template.get("Footprint/Circle"))
halfSize = +template.get("Footprint/Circle/@radius");
let bestIdx;
let bestJdx;
let bestVal;
let width = this.territoryMap.width;
let cellSize = this.territoryMap.cellSize;
let isTower = template.hasClass("Tower");
let isFortress = template.hasClass("Fortress");
let radius;
if (isFortress)
radius = Math.floor((template.obstructionRadius().max + 8) / obstructions.cellSize);
else
radius = Math.ceil(template.obstructionRadius().max / obstructions.cellSize);
for (let j = 0; j < this.territoryMap.length; ++j)
{
if (!wonderMode)
{
// do not try if well inside or outside territory
if (!(this.borderMap.map[j] & m.fullFrontier_Mask))
continue;
if (this.borderMap.map[j] & m.largeFrontier_Mask && isTower)
continue;
}
if (this.basesMap.map[j] === 0) // inaccessible cell
continue;
// with enough room around to build the cc
let i = this.territoryMap.getNonObstructedTile(j, radius, obstructions);
if (i < 0)
continue;
let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
// checking distances to other structures
let minDist = Math.min();
let dista = 0;
if (wonderMode)
{
dista = API3.SquareVectorDistance(wonders[0].position(), pos);
if (dista < wonderDistmin)
continue;
dista *= 200; // empirical factor (TODO should depend on map size) to stay near the wonder
}
for (let str of enemyStructures)
{
if (str.foundationProgress() !== undefined)
continue;
let strPos = str.position();
if (!strPos)
continue;
let dist = API3.SquareVectorDistance(strPos, pos);
if (dist < 6400) // TODO check on true attack range instead of this 80*80
{
minDist = -1;
break;
}
if (str.hasClass("CivCentre") && dist + dista < minDist)
minDist = dist + dista;
}
if (minDist < 0)
continue;
let cutDist = 900; // 30*30 TODO maybe increase it
for (let str of ownStructures)
{
let strPos = str.position();
if (!strPos)
continue;
if (API3.SquareVectorDistance(strPos, pos) < cutDist)
{
minDist = -1;
break;
}
}
if (minDist < 0 || minDist === Math.min())
continue;
if (bestVal !== undefined && minDist > bestVal)
continue;
if (this.isDangerousLocation(gameState, pos, halfSize))
continue;
bestVal = minDist;
bestIdx = i;
bestJdx = j;
}
if (bestVal === undefined)
return undefined;
let x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
let z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
return [x, z, this.basesMap.map[bestJdx]];
};
m.HQ.prototype.buildTemple = function(gameState, queues)
{
// at least one market (which have the same queue) should be build before any temple
if (queues.economicBuilding.hasQueuedUnits() ||
gameState.getOwnEntitiesByClass("Temple", true).hasEntities() ||
!gameState.getOwnEntitiesByClass("BarterMarket", true).hasEntities())
return;
// Try to build a temple earlier if in regicide to recruit healer guards
if (this.currentPhase < 3 && gameState.getGameType() !== "regicide")
return;
if (!this.canBuild(gameState, "structures/{civ}_temple"))
return;
queues.economicBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_temple"));
};
m.HQ.prototype.buildMarket = function(gameState, queues)
{
if (gameState.getOwnEntitiesByClass("BarterMarket", true).hasEntities() ||
!this.canBuild(gameState, "structures/{civ}_market"))
return;
if (queues.economicBuilding.hasQueuedUnitsWithClass("BarterMarket"))
{
if (!this.navalMap && !queues.economicBuilding.paused)
{
// Put available resources in this market when not a naval map
let queueManager = gameState.ai.queueManager;
let cost = queues.economicBuilding.plans[0].getCost();
queueManager.setAccounts(gameState, cost, "economicBuilding");
if (!queueManager.canAfford("economicBuilding", cost))
{
for (let q in queueManager.queues)
{
if (q === "economicBuilding")
continue;
queueManager.transferAccounts(cost, q, "economicBuilding");
if (queueManager.canAfford("economicBuilding", cost))
break;
}
}
}
return;
}
if (gameState.getPopulation() < this.Config.Economy.popForMarket)
return;
gameState.ai.queueManager.changePriority("economicBuilding", 3*this.Config.priorities.economicBuilding);
let plan = new m.ConstructionPlan(gameState, "structures/{civ}_market");
plan.queueToReset = "economicBuilding";
queues.economicBuilding.addPlan(plan);
};
/** Build a farmstead */
m.HQ.prototype.buildFarmstead = function(gameState, queues)
{
// Only build one farmstead for the time being ("DropsiteFood" does not refer to CCs)
if (gameState.getOwnEntitiesByClass("Farmstead", true).hasEntities())
return;
// Wait to have at least one dropsite and house before the farmstead
if (!gameState.getOwnEntitiesByClass("Storehouse", true).hasEntities())
return;
if (!gameState.getOwnEntitiesByClass("House", true).hasEntities())
return;
if (queues.economicBuilding.hasQueuedUnitsWithClass("DropsiteFood"))
return;
if (!this.canBuild(gameState, "structures/{civ}_farmstead"))
return;
queues.economicBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_farmstead"));
};
/**
* Try to build a wonder when required
* force = true when called from the gameTypeManager in case of Wonder mode
*/
m.HQ.prototype.buildWonder = function(gameState, queues, force = false)
{
if (queues.wonder && queues.wonder.hasQueuedUnits() ||
gameState.getOwnEntitiesByClass("Wonder", true).hasEntities() ||
!this.canBuild(gameState, "structures/{civ}_wonder"))
return;
if (!force)
{
let templateName = gameState.applyCiv("structures/{civ}_wonder");
if (gameState.isTemplateDisabled(templateName))
return;
let template = gameState.getTemplate(templateName);
if (!template)
return;
// Check that we have enough resources to start thinking to build a wonder
let cost = template.cost();
let resources = gameState.getResources();
let highLevel = 0;
let lowLevel = 0;
for (let res in cost)
{
if (resources[res] && resources[res] > 0.7 * cost[res])
++highLevel;
else if (!resources[res] || resources[res] < 0.3 * cost[res])
++lowLevel;
}
if (highLevel == 0 || lowLevel > 1)
return;
}
queues.wonder.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_wonder"));
};
/** Build a corral, and train animals there */
m.HQ.prototype.manageCorral = function(gameState, queues)
{
if (queues.corral.hasQueuedUnits())
return;
let nCorral = gameState.getOwnEntitiesByClass("Corral", true).length;
if (!nCorral || !gameState.isTemplateAvailable(gameState.applyCiv("structures/{civ}_field")) &&
nCorral < this.currentPhase && gameState.getPopulation() > 30*nCorral)
{
if (this.canBuild(gameState, "structures/{civ}_corral"))
{
queues.corral.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_corral"));
return;
}
if (!nCorral)
return;
}
// And train some animals
for (let corral of gameState.getOwnEntitiesByClass("Corral", true).values())
{
if (corral.foundationProgress() !== undefined)
continue;
let trainables = corral.trainableEntities();
for (let trainable of trainables)
{
if (gameState.isTemplateDisabled(trainable))
continue;
let template = gameState.getTemplate(trainable);
if (!template || !template.isHuntable())
continue;
let count = gameState.countEntitiesByType(trainable, true);
for (let item of corral.trainingQueue())
count += item.count;
if (count > nCorral)
continue;
queues.corral.addPlan(new m.TrainingPlan(gameState, trainable, { "trainer": corral.id() }));
return;
}
}
};
/**
* build more houses if needed.
* kinda ugly, lots of special cases to both build enough houses but not tooo many…
*/
m.HQ.prototype.buildMoreHouses = function(gameState, queues)
{
if (!gameState.isTemplateAvailable(gameState.applyCiv("structures/{civ}_house")) ||
gameState.getPopulationMax() <= gameState.getPopulationLimit())
return;
let numPlanned = queues.house.length();
if (numPlanned < 3 || numPlanned < 5 && gameState.getPopulation() > 80)
{
let plan = new m.ConstructionPlan(gameState, "structures/{civ}_house");
// change the starting condition according to the situation.
plan.goRequirement = "houseNeeded";
queues.house.addPlan(plan);
}
if (numPlanned > 0 && this.phasing && gameState.getPhaseEntityRequirements(this.phasing).length)
{
let houseTemplateName = gameState.applyCiv("structures/{civ}_house");
let houseTemplate = gameState.getTemplate(houseTemplateName);
let needed = 0;
for (let entityReq of gameState.getPhaseEntityRequirements(this.phasing))
{
if (!houseTemplate.hasClass(entityReq.class))
continue;
let count = gameState.getOwnStructures().filter(API3.Filters.byClass(entityReq.class)).length;
if (count < entityReq.count && this.buildManager.isUnbuildable(gameState, houseTemplateName))
{
if (this.Config.debug > 1)
API3.warn("no room to place a house ... try to be less restrictive");
this.buildManager.setBuildable(houseTemplateName);
this.requireHouses = true;
}
needed = Math.max(needed, entityReq.count - count);
}
let houseQueue = queues.house.plans;
for (let i = 0; i < numPlanned; ++i)
if (houseQueue[i].isGo(gameState))
--needed;
else if (needed > 0)
{
houseQueue[i].goRequirement = undefined;
--needed;
}
}
if (this.requireHouses)
{
let houseTemplate = gameState.getTemplate(gameState.applyCiv("structures/{civ}_house"));
if (!this.phasing || gameState.getPhaseEntityRequirements(this.phasing).every(req =>
!houseTemplate.hasClass(req.class) || gameState.getOwnStructures().filter(API3.Filters.byClass(req.class)).length >= req.count))
this.requireHouses = undefined;
}
// When population limit too tight
// - if no room to build, try to improve with technology
// - otherwise increase temporarily the priority of houses
let house = gameState.applyCiv("structures/{civ}_house");
let HouseNb = gameState.getOwnFoundations().filter(API3.Filters.byClass("House")).length;
let popBonus = gameState.getTemplate(house).getPopulationBonus();
let freeSlots = gameState.getPopulationLimit() + HouseNb*popBonus - gameState.getPopulation();
let priority;
if (freeSlots < 5)
{
if (this.buildManager.isUnbuildable(gameState, house))
{
if (this.Config.debug > 1)
API3.warn("no room to place a house ... try to improve with technology");
this.researchManager.researchPopulationBonus(gameState, queues);
}
else
priority = 2*this.Config.priorities.house;
}
else
priority = this.Config.priorities.house;
if (priority && priority != gameState.ai.queueManager.getPriority("house"))
gameState.ai.queueManager.changePriority("house", priority);
};
/** Checks the status of the territory expansion. If no new economic bases created, build some strategic ones. */
m.HQ.prototype.checkBaseExpansion = function(gameState, queues)
{
if (queues.civilCentre.hasQueuedUnits())
return;
// First build one cc if all have been destroyed
let activeBases = this.numActiveBase();
if (activeBases === 0)
{
this.buildFirstBase(gameState);
return;
}
// Then expand if we have not enough room available for buildings
if (this.buildManager.numberMissingRoom(gameState) > 1)
{
if (this.Config.debug > 2)
API3.warn("try to build a new base because not enough room to build ");
this.buildNewBase(gameState, queues);
return;
}
// If we've already planned to phase up, wait a bit before trying to expand
if (this.phasing)
return;
// Finally expand if we have lots of units (threshold depending on the aggressivity value)
let numUnits = gameState.getOwnUnits().length;
let numvar = 10 * (1 - this.Config.personality.aggressive);
if (numUnits > activeBases * (65 + numvar + (10 + numvar)*(activeBases-1)) || this.saveResources && numUnits > 50)
{
if (this.Config.debug > 2)
API3.warn("try to build a new base because of population " + numUnits + " for " + activeBases + " CCs");
this.buildNewBase(gameState, queues);
}
};
m.HQ.prototype.buildNewBase = function(gameState, queues, resource)
{
if (this.numActiveBase() > 0 && this.currentPhase == 1 && !gameState.isResearching(gameState.getPhaseName(2)))
return false;
if (gameState.getOwnFoundations().filter(API3.Filters.byClass("CivCentre")).hasEntities() || queues.civilCentre.hasQueuedUnits())
return false;
let template;
// We require at least one of this civ civCentre as they may allow specific units or techs
let hasOwnCC = false;
for (let ent of gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")).values())
{
if (ent.owner() !== PlayerID || ent.templateName() !== gameState.applyCiv("structures/{civ}_civil_centre"))
continue;
hasOwnCC = true;
break;
}
if (hasOwnCC && this.canBuild(gameState, "structures/{civ}_military_colony"))
template = "structures/{civ}_military_colony";
else if (this.canBuild(gameState, "structures/{civ}_civil_centre"))
template = "structures/{civ}_civil_centre";
else if (!hasOwnCC && this.canBuild(gameState, "structures/{civ}_military_colony"))
template = "structures/{civ}_military_colony";
else
return false;
// base "-1" means new base.
if (this.Config.debug > 1)
API3.warn("new base " + gameState.applyCiv(template) + " planned with resource " + resource);
queues.civilCentre.addPlan(new m.ConstructionPlan(gameState, template, { "base": -1, "resource": resource }));
return true;
};
/** Deals with building fortresses and towers along our border with enemies. */
m.HQ.prototype.buildDefenses = function(gameState, queues)
{
if (this.saveResources && !this.canBarter || queues.defenseBuilding.hasQueuedUnits())
return;
if (!this.saveResources && (this.currentPhase > 2 || gameState.isResearching(gameState.getPhaseName(3))))
{
// try to build fortresses
if (this.canBuild(gameState, "structures/{civ}_fortress"))
{
let numFortresses = gameState.getOwnEntitiesByClass("Fortress", true).length;
if ((!numFortresses || gameState.ai.elapsedTime > (1 + 0.10*numFortresses)*this.fortressLapseTime + this.fortressStartTime) &&
numFortresses < this.numActiveBase() + 1 + this.extraFortresses &&
numFortresses < Math.floor(gameState.getPopulation() / 25) &&
gameState.getOwnFoundationsByClass("Fortress").length < 2)
{
this.fortressStartTime = gameState.ai.elapsedTime;
if (!numFortresses)
gameState.ai.queueManager.changePriority("defenseBuilding", 2*this.Config.priorities.defenseBuilding);
let plan = new m.ConstructionPlan(gameState, "structures/{civ}_fortress");
plan.queueToReset = "defenseBuilding";
queues.defenseBuilding.addPlan(plan);
return;
}
}
}
if (this.Config.Military.numSentryTowers && this.currentPhase < 2 && this.canBuild(gameState, "structures/{civ}_sentry_tower"))
{
let numTowers = gameState.getOwnEntitiesByClass("Tower", true).length; // we count all towers, including wall towers
let towerLapseTime = this.saveResource ? (1 + 0.5*numTowers) * this.towerLapseTime : this.towerLapseTime;
if (numTowers < this.Config.Military.numSentryTowers && gameState.ai.elapsedTime > towerLapseTime + this.fortStartTime)
{
this.fortStartTime = gameState.ai.elapsedTime;
queues.defenseBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_sentry_tower"));
}
return;
}
if (this.currentPhase < 2 || !this.canBuild(gameState, "structures/{civ}_defense_tower"))
return;
let numTowers = gameState.getOwnEntitiesByClass("DefenseTower", true).filter(API3.Filters.not(API3.Filters.byClass("SentryTower"))).length;
let towerLapseTime = this.saveResource ? (1 + numTowers) * this.towerLapseTime : this.towerLapseTime;
if ((!numTowers || gameState.ai.elapsedTime > (1 + 0.1*numTowers)*towerLapseTime + this.towerStartTime) &&
numTowers < 2 * this.numActiveBase() + 3 + this.extraTowers &&
numTowers < Math.floor(gameState.getPopulation() / 8) &&
gameState.getOwnFoundationsByClass("DefenseTower").length < 3)
{
this.towerStartTime = gameState.ai.elapsedTime;
if (numTowers > 2 * this.numActiveBase() + 3)
gameState.ai.queueManager.changePriority("defenseBuilding", Math.round(0.7*this.Config.priorities.defenseBuilding));
let plan = new m.ConstructionPlan(gameState, "structures/{civ}_defense_tower");
plan.queueToReset = "defenseBuilding";
queues.defenseBuilding.addPlan(plan);
}
};
m.HQ.prototype.buildBlacksmith = function(gameState, queues)
{
if (gameState.getPopulation() < this.Config.Military.popForBlacksmith ||
queues.militaryBuilding.hasQueuedUnits() || gameState.getOwnEntitiesByClass("Blacksmith", true).length)
return;
// build a market before the blacksmith
if (!gameState.getOwnEntitiesByClass("BarterMarket", true).hasEntities())
return;
if (this.canBuild(gameState, "structures/{civ}_blacksmith"))
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_blacksmith"));
};
/**
* Deals with constructing military buildings (barracks, stables…)
* They are mostly defined by Config.js. This is unreliable since changes could be done easily.
*/
m.HQ.prototype.constructTrainingBuildings = function(gameState, queues)
{
if (this.saveResources && !this.canBarter || queues.militaryBuilding.hasQueuedUnits())
return;
let numBarracks = this.canBuild(gameState, "structures/{civ}_barracks") ? gameState.getOwnEntitiesByClass("Barracks", true).length : -1;
let numStables = this.canBuild(gameState, "structures/{civ}_stables") ? gameState.getOwnEntitiesByClass("Stables", true).length : -1;
if (this.saveResources && numBarracks != 0)
return;
if (gameState.getPopulation() > this.Config.Military.popForBarracks1 ||
this.phasing == 2 && gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length < 5)
{
// first barracks and stables.
if (numBarracks == 0)
{
gameState.ai.queueManager.changePriority("militaryBuilding", 2*this.Config.priorities.militaryBuilding);
let plan = new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "militaryBase": true });
plan.queueToReset = "militaryBuilding";
queues.militaryBuilding.addPlan(plan);
return;
}
if (numStables == 0)
{
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", { "militaryBase": true }));
return;
}
// Second barracks and stables
if (numBarracks == 1 && gameState.getPopulation() > this.Config.Military.popForBarracks2)
{
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "militaryBase": true }));
return;
}
if (numStables == 1 && gameState.getPopulation() > this.Config.Military.popForBarracks2)
{
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", { "militaryBase": true }));
return;
}
// Then 3rd barracks/stables if needed
if (numBarracks == 2 && numStables == -1 && gameState.getPopulation() > this.Config.Military.popForBarracks2 + 30)
{
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_barracks", { "militaryBase": true }));
return;
}
if (numBarracks == -1 && numStables == 2 && gameState.getPopulation() > this.Config.Military.popForBarracks2 + 30)
{
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_stables", { "militaryBase": true }));
return;
}
}
if (this.saveResources)
return;
if (this.currentPhase < 3)
return;
if (this.canBuild(gameState, "structures/{civ}_elephant_stables") && !gameState.getOwnEntitiesByClass("ElephantStables", true).hasEntities())
{
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_elephant_stables", { "militaryBase": true }));
return;
}
- if (this.canBuild(gameState, "structures/{civ}_siege_workshop") && !gameState.getOwnEntitiesByClass("Workshop", true).hasEntities())
+ if (this.canBuild(gameState, "structures/{civ}_workshop") && !gameState.getOwnEntitiesByClass("Workshop", true).hasEntities())
{
- queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_siege_workshop", { "militaryBase": true }));
+ queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_workshop", { "militaryBase": true }));
return;
}
if (gameState.getPopulation() < 80 || !this.bAdvanced.length)
return;
//build advanced military buildings
let nAdvanced = 0;
for (let advanced of this.bAdvanced)
nAdvanced += gameState.countEntitiesAndQueuedByType(advanced, true);
if (!nAdvanced || nAdvanced < this.bAdvanced.length && gameState.getPopulation() > 110)
{
for (let advanced of this.bAdvanced)
{
if (gameState.countEntitiesAndQueuedByType(advanced, true) > 0 || !this.canBuild(gameState, advanced))
continue;
let template = gameState.getTemplate(advanced);
if (!template)
continue;
if (template.hasDefensiveFire() || template.trainableEntities())
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, advanced, { "militaryBase": true }));
else // not a military building, but still use this queue
queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, advanced));
return;
}
}
};
/**
* Find base nearest to ennemies for military buildings.
*/
m.HQ.prototype.findBestBaseForMilitary = function(gameState)
{
let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")).toEntityArray();
let bestBase;
let enemyFound = false;
let distMin = Math.min();
for (let cce of ccEnts)
{
if (gameState.isPlayerAlly(cce.owner()))
continue;
if (enemyFound && !gameState.isPlayerEnemy(cce.owner()))
continue;
let access = m.getLandAccess(gameState, cce);
let isEnemy = gameState.isPlayerEnemy(cce.owner());
for (let cc of ccEnts)
{
if (cc.owner() != PlayerID)
continue;
if (m.getLandAccess(gameState, cc) != access)
continue;
let dist = API3.SquareVectorDistance(cc.position(), cce.position());
if (!enemyFound && isEnemy)
enemyFound = true;
else if (dist > distMin)
continue;
bestBase = cc.getMetadata(PlayerID, "base");
distMin = dist;
}
}
return bestBase;
};
/**
* train with highest priority ranged infantry in the nearest civil centre from a given set of positions
* and garrison them there for defense
*/
m.HQ.prototype.trainEmergencyUnits = function(gameState, positions)
{
if (gameState.ai.queues.emergency.hasQueuedUnits())
return false;
let civ = gameState.getPlayerCiv();
// find nearest base anchor
let distcut = 20000;
let nearestAnchor;
let distmin;
for (let pos of positions)
{
let access = gameState.ai.accessibility.getAccessValue(pos);
// check nearest base anchor
for (let base of this.baseManagers)
{
if (!base.anchor || !base.anchor.position())
continue;
if (base.anchor.getMetadata(PlayerID, "access") !== access)
continue;
if (!base.anchor.trainableEntities(civ)) // base still in construction
continue;
let queue = base.anchor._entity.trainingQueue;
if (queue)
{
let time = 0;
for (let item of queue)
if (item.progress > 0 || item.metadata && item.metadata.garrisonType)
time += item.timeRemaining;
if (time/1000 > 5)
continue;
}
let dist = API3.SquareVectorDistance(base.anchor.position(), pos);
if (nearestAnchor && dist > distmin)
continue;
distmin = dist;
nearestAnchor = base.anchor;
}
}
if (!nearestAnchor || distmin > distcut)
return false;
// We will choose randomly ranged and melee units, except when garrisonHolder is full
// in which case we prefer melee units
let numGarrisoned = this.garrisonManager.numberOfGarrisonedUnits(nearestAnchor);
if (nearestAnchor._entity.trainingQueue)
{
for (let item of nearestAnchor._entity.trainingQueue)
{
if (item.metadata && item.metadata.garrisonType)
numGarrisoned += item.count;
else if (!item.progress && (!item.metadata || !item.metadata.trainer))
nearestAnchor.stopProduction(item.id);
}
}
let autogarrison = numGarrisoned < nearestAnchor.garrisonMax() &&
nearestAnchor.hitpoints() > nearestAnchor.garrisonEjectHealth() * nearestAnchor.maxHitpoints();
let rangedWanted = randBool() && autogarrison;
let total = gameState.getResources();
let templateFound;
let trainables = nearestAnchor.trainableEntities(civ);
let garrisonArrowClasses = nearestAnchor.getGarrisonArrowClasses();
for (let trainable of trainables)
{
if (gameState.isTemplateDisabled(trainable))
continue;
let template = gameState.getTemplate(trainable);
if (!template || !template.hasClass("Infantry") || !template.hasClass("CitizenSoldier"))
continue;
if (autogarrison && !MatchesClassList(template.classes(), garrisonArrowClasses))
continue;
if (!total.canAfford(new API3.Resources(template.cost())))
continue;
templateFound = [trainable, template];
if (template.hasClass("Ranged") === rangedWanted)
break;
}
if (!templateFound)
return false;
// Check first if we can afford it without touching the other accounts
// and if not, take some of other accounted resources
// TODO sort the queues to be substracted
let queueManager = gameState.ai.queueManager;
let cost = new API3.Resources(templateFound[1].cost());
queueManager.setAccounts(gameState, cost, "emergency");
if (!queueManager.canAfford("emergency", cost))
{
for (let q in queueManager.queues)
{
if (q === "emergency")
continue;
queueManager.transferAccounts(cost, q, "emergency");
if (queueManager.canAfford("emergency", cost))
break;
}
}
let metadata = { "role": "worker", "base": nearestAnchor.getMetadata(PlayerID, "base"), "plan": -1, "trainer": nearestAnchor.id() };
if (autogarrison)
metadata.garrisonType = "protection";
gameState.ai.queues.emergency.addPlan(new m.TrainingPlan(gameState, templateFound[0], metadata, 1, 1));
return true;
};
m.HQ.prototype.canBuild = function(gameState, structure, debug = false)
{
let type = gameState.applyCiv(structure);
if (this.buildManager.isUnbuildable(gameState, type))
return false;
if (gameState.isTemplateDisabled(type))
{
this.buildManager.setUnbuildable(gameState, type, Infinity, "disabled");
return false;
}
let template = gameState.getTemplate(type);
if (!template)
{
this.buildManager.setUnbuildable(gameState, type, Infinity, "notemplate");
return false;
}
if (!template.available(gameState))
{
this.buildManager.setUnbuildable(gameState, type, 30, "tech");
return false;
}
if (!this.buildManager.hasBuilder(type))
{
this.buildManager.setUnbuildable(gameState, type, 120, "nobuilder");
return false;
}
if (this.numActiveBase() < 1)
{
// if no base, check that we can build outside our territory
let buildTerritories = template.buildTerritories();
if (buildTerritories && (!buildTerritories.length || buildTerritories.length === 1 && buildTerritories[0] === "own"))
{
this.buildManager.setUnbuildable(gameState, type, 180, "room");
return false;
}
}
// build limits
let limits = gameState.getEntityLimits();
let category = template.buildCategory();
if (category && limits[category] !== undefined && gameState.getEntityCounts()[category] >= limits[category])
{
this.buildManager.setUnbuildable(gameState, type, 90, "limit");
return false;
}
return true;
};
m.HQ.prototype.updateTerritories = function(gameState)
{
const around = [ [-0.7,0.7], [0,1], [0.7,0.7], [1,0], [0.7,-0.7], [0,-1], [-0.7,-0.7], [-1,0] ];
let alliedVictory = gameState.getAlliedVictory();
let passabilityMap = gameState.getPassabilityMap();
let width = this.territoryMap.width;
let cellSize = this.territoryMap.cellSize;
let insideSmall = Math.round(45 / cellSize);
let insideLarge = Math.round(80 / cellSize); // should be about the range of towers
let expansion = 0;
for (let j = 0; j < this.territoryMap.length; ++j)
{
if (this.borderMap.map[j] & m.outside_Mask)
continue;
if (this.borderMap.map[j] & m.fullFrontier_Mask)
this.borderMap.map[j] &= ~m.fullFrontier_Mask; // reset the frontier
if (this.territoryMap.getOwnerIndex(j) != PlayerID)
{
// If this tile was already accounted, remove it
if (this.basesMap.map[j] === 0)
continue;
let base = this.getBaseByID(this.basesMap.map[j]);
let index = base.territoryIndices.indexOf(j);
if (index == -1)
{
API3.warn(" problem in headquarters::updateTerritories for base " + this.basesMap.map[j]);
continue;
}
base.territoryIndices.splice(index, 1);
this.basesMap.map[j] = 0;
}
else
{
// Update the frontier
let ix = j%width;
let iz = Math.floor(j/width);
let onFrontier = false;
for (let a of around)
{
let jx = ix + Math.round(insideSmall*a[0]);
if (jx < 0 || jx >= width)
continue;
let jz = iz + Math.round(insideSmall*a[1]);
if (jz < 0 || jz >= width)
continue;
if (this.borderMap.map[jx+width*jz] & m.outside_Mask)
continue;
let territoryOwner = this.territoryMap.getOwnerIndex(jx+width*jz);
if (territoryOwner !== PlayerID && !(alliedVictory && gameState.isPlayerAlly(territoryOwner)))
{
this.borderMap.map[j] |= m.narrowFrontier_Mask;
break;
}
jx = ix + Math.round(insideLarge*a[0]);
if (jx < 0 || jx >= width)
continue;
jz = iz + Math.round(insideLarge*a[1]);
if (jz < 0 || jz >= width)
continue;
if (this.borderMap.map[jx+width*jz] & m.outside_Mask)
continue;
territoryOwner = this.territoryMap.getOwnerIndex(jx+width*jz);
if (territoryOwner !== PlayerID && !(alliedVictory && gameState.isPlayerAlly(territoryOwner)))
onFrontier = true;
}
if (onFrontier && !(this.borderMap.map[j] & m.narrowFrontier_Mask))
this.borderMap.map[j] |= m.largeFrontier_Mask;
// If this tile was not already accounted, add it
if (this.basesMap.map[j] !== 0)
continue;
let landPassable = false;
let ind = API3.getMapIndices(j, this.territoryMap, passabilityMap);
let access;
for (let k of ind)
{
if (!this.landRegions[gameState.ai.accessibility.landPassMap[k]])
continue;
landPassable = true;
access = gameState.ai.accessibility.landPassMap[k];
break;
}
if (!landPassable)
continue;
let distmin = Math.min();
let baseID;
let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
for (let base of this.baseManagers)
{
if (!base.anchor || !base.anchor.position())
continue;
if (base.accessIndex != access)
continue;
let dist = API3.SquareVectorDistance(base.anchor.position(), pos);
if (dist >= distmin)
continue;
distmin = dist;
baseID = base.ID;
}
if (!baseID)
continue;
this.getBaseByID(baseID).territoryIndices.push(j);
this.basesMap.map[j] = baseID;
expansion++;
}
}
if (!expansion)
return;
// We've increased our territory, so we may have some new room to build
this.buildManager.resetMissingRoom(gameState);
// And if sufficient expansion, check if building a new market would improve our present trade routes
let cellArea = this.territoryMap.cellSize * this.territoryMap.cellSize;
if (expansion * cellArea > 960)
this.tradeManager.routeProspection = true;
};
/**
* returns the base corresponding to baseID
*/
m.HQ.prototype.getBaseByID = function(baseID)
{
for (let base of this.baseManagers)
if (base.ID === baseID)
return base;
API3.warn("Petra error: no base found with ID " + baseID);
return undefined;
};
/**
* returns the number of active (i.e. with one cc) bases
*/
m.HQ.prototype.numActiveBase = function()
{
if (!this.turnCache.activeBase)
{
let num = 0;
for (let base of this.baseManagers)
if (base.anchor)
++num;
this.turnCache.activeBase = num;
}
return this.turnCache.activeBase;
};
m.HQ.prototype.resetActiveBase = function()
{
this.turnCache.activeBase = undefined;
};
/**
* Count gatherers returning resources in the number of gatherers of resourceSupplies
* to prevent the AI always reaffecting idle workers to these resourceSupplies (specially in naval maps).
*/
m.HQ.prototype.assignGatherers = function()
{
for (let base of this.baseManagers)
{
for (let worker of base.workers.values())
{
if (worker.unitAIState().split(".")[1] !== "RETURNRESOURCE")
continue;
let orders = worker.unitAIOrderData();
if (orders.length < 2 || !orders[1].target || orders[1].target !== worker.getMetadata(PlayerID, "supply"))
continue;
this.AddTCGatherer(orders[1].target);
}
}
};
m.HQ.prototype.isDangerousLocation = function(gameState, pos, radius)
{
return this.isNearInvadingArmy(pos) || this.isUnderEnemyFire(gameState, pos, radius);
};
/** Check that the chosen position is not too near from an invading army */
m.HQ.prototype.isNearInvadingArmy = function(pos)
{
for (let army of this.defenseManager.armies)
if (army.foePosition && API3.SquareVectorDistance(army.foePosition, pos) < 12000)
return true;
return false;
};
m.HQ.prototype.isUnderEnemyFire = function(gameState, pos, radius = 0)
{
if (!this.turnCache.firingStructures)
this.turnCache.firingStructures = gameState.updatingCollection("diplo-FiringStructures", API3.Filters.hasDefensiveFire(), gameState.getEnemyStructures());
for (let ent of this.turnCache.firingStructures.values())
{
let range = radius + ent.attackRange("Ranged").max;
if (API3.SquareVectorDistance(ent.position(), pos) < range*range)
return true;
}
return false;
};
/** Compute the capture strength of all units attacking a capturable target */
m.HQ.prototype.updateCaptureStrength = function(gameState)
{
this.capturableTargets.clear();
for (let ent of gameState.getOwnUnits().values())
{
if (!ent.canCapture())
continue;
let state = ent.unitAIState();
if (!state || !state.split(".")[1] || state.split(".")[1] !== "COMBAT")
continue;
let orderData = ent.unitAIOrderData();
if (!orderData || !orderData.length || !orderData[0].target)
continue;
let targetId = orderData[0].target;
let target = gameState.getEntityById(targetId);
if (!target || !target.isCapturable() || !ent.canCapture(target))
continue;
if (!this.capturableTargets.has(targetId))
this.capturableTargets.set(targetId, {
"strength": ent.captureStrength() * m.getAttackBonus(ent, target, "Capture"),
"ents": new Set([ent.id()])
});
else
{
let capturableTarget = this.capturableTargets.get(target.id());
capturableTarget.strength += ent.captureStrength() * m.getAttackBonus(ent, target, "Capture");
capturableTarget.ents.add(ent.id());
}
}
for (let [targetId, capturableTarget] of this.capturableTargets)
{
let target = gameState.getEntityById(targetId);
let allowCapture;
for (let entId of capturableTarget.ents)
{
let ent = gameState.getEntityById(entId);
if (allowCapture === undefined)
allowCapture = m.allowCapture(gameState, ent, target);
let orderData = ent.unitAIOrderData();
if (!orderData || !orderData.length || !orderData[0].attackType)
continue;
if ((orderData[0].attackType === "Capture") !== allowCapture)
ent.attack(targetId, allowCapture);
}
}
this.capturableTargetsTime = gameState.ai.elapsedTime;
};
/** Some functions that register that we assigned a gatherer to a resource this turn */
/** add a gatherer to the turn cache for this supply. */
m.HQ.prototype.AddTCGatherer = function(supplyID)
{
if (this.turnCache.resourceGatherer && this.turnCache.resourceGatherer[supplyID] !== undefined)
++this.turnCache.resourceGatherer[supplyID];
else
{
if (!this.turnCache.resourceGatherer)
this.turnCache.resourceGatherer = {};
this.turnCache.resourceGatherer[supplyID] = 1;
}
};
/** remove a gatherer to the turn cache for this supply. */
m.HQ.prototype.RemoveTCGatherer = function(supplyID)
{
if (this.turnCache.resourceGatherer && this.turnCache.resourceGatherer[supplyID])
--this.turnCache.resourceGatherer[supplyID];
else
{
if (!this.turnCache.resourceGatherer)
this.turnCache.resourceGatherer = {};
this.turnCache.resourceGatherer[supplyID] = -1;
}
};
m.HQ.prototype.GetTCGatherer = function(supplyID)
{
if (this.turnCache.resourceGatherer && this.turnCache.resourceGatherer[supplyID])
return this.turnCache.resourceGatherer[supplyID];
return 0;
};
/** The next two are to register that we assigned a gatherer to a resource this turn. */
m.HQ.prototype.AddTCResGatherer = function(resource)
{
if (this.turnCache["resourceGatherer-" + resource])
++this.turnCache["resourceGatherer-" + resource];
else
this.turnCache["resourceGatherer-" + resource] = 1;
this.turnCache.gatherRates = false;
};
m.HQ.prototype.GetTCResGatherer = function(resource)
{
if (this.turnCache["resourceGatherer-" + resource])
return this.turnCache["resourceGatherer-" + resource];
return 0;
};
/**
* Check if a structure in blinking territory should/can be defended (currently if it has some attacking armies around)
*/
m.HQ.prototype.isDefendable = function(ent)
{
if (!this.turnCache.numAround)
this.turnCache.numAround = {};
if (this.turnCache.numAround[ent.id()] === undefined)
this.turnCache.numAround[ent.id()] = this.attackManager.numAttackingUnitsAround(ent.position(), 130);
return +this.turnCache.numAround[ent.id()] > 8;
};
/**
* Some functions are run every turn
* Others once in a while
*/
m.HQ.prototype.update = function(gameState, queues, events)
{
Engine.ProfileStart("Headquarters update");
this.turnCache = {};
this.territoryMap = m.createTerritoryMap(gameState);
this.canBarter = gameState.getOwnEntitiesByClass("BarterMarket", true).filter(API3.Filters.isBuilt()).hasEntities();
// TODO find a better way to update
if (this.currentPhase != gameState.currentPhase())
{
if (this.Config.debug > 0)
API3.warn(" civ " + gameState.getPlayerCiv() + " has phasedUp from " + this.currentPhase +
" to " + gameState.currentPhase() + " at time " + gameState.ai.elapsedTime +
" phasing " + this.phasing);
this.currentPhase = gameState.currentPhase();
// In principle, this.phasing should be already reset to 0 when starting the research
// but this does not work in case of an autoResearch tech
if (this.phasing)
this.phasing = 0;
}
/* if (this.Config.debug > 1)
{
gameState.getOwnUnits().forEach (function (ent) {
if (!ent.position())
return;
m.dumpEntity(ent);
});
} */
this.checkEvents(gameState, events, queues);
if (this.phasing)
this.checkPhaseRequirements(gameState, queues);
else
this.researchManager.checkPhase(gameState, queues);
if (this.numActiveBase() > 0)
{
if (gameState.ai.playedTurn % 4 == 0)
this.trainMoreWorkers(gameState, queues);
if (gameState.ai.playedTurn % 4 == 1)
this.buildMoreHouses(gameState,queues);
if ((!this.saveResources || this.canBarter) && gameState.ai.playedTurn % 4 == 2)
this.buildFarmstead(gameState, queues);
if (this.needCorral && gameState.ai.playedTurn % 4 == 3)
this.manageCorral(gameState, queues);
if (!queues.minorTech.hasQueuedUnits() && gameState.ai.playedTurn % 5 == 1)
this.researchManager.update(gameState, queues);
}
if (this.numActiveBase() < 1 ||
this.canExpand && gameState.ai.playedTurn % 10 == 7 && this.currentPhase > 1)
this.checkBaseExpansion(gameState, queues);
if (this.currentPhase > 1)
{
if (!this.canBarter)
this.buildMarket(gameState, queues);
if (!this.saveResources)
{
this.buildBlacksmith(gameState, queues);
this.buildTemple(gameState, queues);
}
if (gameState.ai.playedTurn % 30 === 0 &&
gameState.getPopulation() > 0.9 * gameState.getPopulationMax())
this.buildWonder(gameState, queues, false);
}
this.tradeManager.update(gameState, events, queues);
this.garrisonManager.update(gameState, events);
this.defenseManager.update(gameState, events);
if (gameState.ai.playedTurn % 3 == 0)
this.constructTrainingBuildings(gameState, queues);
if (this.Config.difficulty > 0)
this.buildDefenses(gameState, queues);
this.assignGatherers();
for (let i = 0; i < this.baseManagers.length; ++i)
{
this.baseManagers[i].checkEvents(gameState, events, queues);
if ((i + gameState.ai.playedTurn)%this.baseManagers.length === 0)
this.baseManagers[i].update(gameState, queues, events);
}
this.navalManager.update(gameState, queues, events);
if (this.Config.difficulty > 0 && (this.numActiveBase() > 0 || !this.canBuildUnits))
this.attackManager.update(gameState, queues, events);
this.diplomacyManager.update(gameState, events);
this.gameTypeManager.update(gameState, events, queues);
// We update the capture strength at the end as it can change attack orders
if (gameState.ai.elapsedTime - this.capturableTargetsTime > 3)
this.updateCaptureStrength(gameState);
Engine.ProfileStop();
};
m.HQ.prototype.Serialize = function()
{
let properties = {
"phasing": this.phasing,
"wantedRates": this.wantedRates,
"currentRates": this.currentRates,
"lastFailedGather": this.lastFailedGather,
"supportRatio": this.supportRatio,
"targetNumWorkers": this.targetNumWorkers,
"fortStartTime": this.fortStartTime,
"towerStartTime": this.towerStartTime,
"fortressStartTime": this.fortressStartTime,
"bAdvanced": this.bAdvanced,
"saveResources": this.saveResources,
"saveSpace": this.saveSpace,
"needCorral": this.needCorral,
"needFarm": this.needFarm,
"needFish": this.needFish,
"canExpand": this.canExpand,
"canBuildUnits": this.canBuildUnits,
"navalMap": this.navalMap,
"landRegions": this.landRegions,
"navalRegions": this.navalRegions,
"decayingStructures": this.decayingStructures,
"capturableTargets": this.capturableTargets,
"capturableTargetsTime": this.capturableTargetsTime
};
let baseManagers = [];
for (let base of this.baseManagers)
baseManagers.push(base.Serialize());
if (this.Config.debug == -100)
{
API3.warn(" HQ serialization ---------------------");
API3.warn(" properties " + uneval(properties));
API3.warn(" baseManagers " + uneval(baseManagers));
API3.warn(" attackManager " + uneval(this.attackManager.Serialize()));
API3.warn(" buildManager " + uneval(this.buildManager.Serialize()));
API3.warn(" defenseManager " + uneval(this.defenseManager.Serialize()));
API3.warn(" tradeManager " + uneval(this.tradeManager.Serialize()));
API3.warn(" navalManager " + uneval(this.navalManager.Serialize()));
API3.warn(" researchManager " + uneval(this.researchManager.Serialize()));
API3.warn(" diplomacyManager " + uneval(this.diplomacyManager.Serialize()));
API3.warn(" garrisonManager " + uneval(this.garrisonManager.Serialize()));
API3.warn(" gameTypeManager " + uneval(this.gameTypeManager.Serialize()));
}
return {
"properties": properties,
"baseManagers": baseManagers,
"attackManager": this.attackManager.Serialize(),
"buildManager": this.buildManager.Serialize(),
"defenseManager": this.defenseManager.Serialize(),
"tradeManager": this.tradeManager.Serialize(),
"navalManager": this.navalManager.Serialize(),
"researchManager": this.researchManager.Serialize(),
"diplomacyManager": this.diplomacyManager.Serialize(),
"garrisonManager": this.garrisonManager.Serialize(),
"gameTypeManager": this.gameTypeManager.Serialize(),
};
};
m.HQ.prototype.Deserialize = function(gameState, data)
{
for (let key in data.properties)
this[key] = data.properties[key];
this.baseManagers = [];
for (let base of data.baseManagers)
{
// the first call to deserialize set the ID base needed by entitycollections
let newbase = new m.BaseManager(gameState, this.Config);
newbase.Deserialize(gameState, base);
newbase.init(gameState);
newbase.Deserialize(gameState, base);
this.baseManagers.push(newbase);
}
this.navalManager = new m.NavalManager(this.Config);
this.navalManager.init(gameState, true);
this.navalManager.Deserialize(gameState, data.navalManager);
this.attackManager = new m.AttackManager(this.Config);
this.attackManager.Deserialize(gameState, data.attackManager);
this.attackManager.init(gameState);
this.attackManager.Deserialize(gameState, data.attackManager);
this.buildManager = new m.BuildManager();
this.buildManager.Deserialize(data.buildManager);
this.defenseManager = new m.DefenseManager(this.Config);
this.defenseManager.Deserialize(gameState, data.defenseManager);
this.tradeManager = new m.TradeManager(this.Config);
this.tradeManager.init(gameState);
this.tradeManager.Deserialize(gameState, data.tradeManager);
this.researchManager = new m.ResearchManager(this.Config);
this.researchManager.Deserialize(data.researchManager);
this.diplomacyManager = new m.DiplomacyManager(this.Config);
this.diplomacyManager.Deserialize(data.diplomacyManager);
this.garrisonManager = new m.GarrisonManager(this.Config);
this.garrisonManager.Deserialize(data.garrisonManager);
this.gameTypeManager = new m.GameTypeManager(this.Config);
this.gameTypeManager.Deserialize(data.gameTypeManager);
};
return m;
}(PETRA);
Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/queueplanBuilding.js (revision 20457)
@@ -1,812 +1,812 @@
var PETRA = function(m)
{
/**
* Defines a construction plan, ie a building.
* We'll try to fing a good position if non has been provided
*/
m.ConstructionPlan = function(gameState, type, metadata, position)
{
if (!m.QueuePlan.call(this, gameState, type, metadata))
return false;
this.position = position ? position : 0;
this.category = "building";
return true;
};
m.ConstructionPlan.prototype = Object.create(m.QueuePlan.prototype);
m.ConstructionPlan.prototype.canStart = function(gameState)
{
if (gameState.ai.HQ.turnCache.buildingBuilt) // do not start another building if already one this turn
return false;
if (!this.isGo(gameState))
return false;
if (this.template.requiredTech() && !gameState.isResearched(this.template.requiredTech()))
return false;
return gameState.ai.HQ.buildManager.hasBuilder(this.type);
};
m.ConstructionPlan.prototype.start = function(gameState)
{
Engine.ProfileStart("Building construction start");
// We don't care which builder we assign, since they won't actually
// do the building themselves - all we care about is that there is
// at least one unit that can start the foundation
let builder = gameState.findBuilder(this.type);
let pos = this.findGoodPosition(gameState);
if (!pos)
{
gameState.ai.HQ.buildManager.setUnbuildable(gameState, this.type);
Engine.ProfileStop();
return;
}
if (this.metadata && this.metadata.expectedGain)
{
// Check if this market is still worth building (others may have been built making it useless)
let tradeManager = gameState.ai.HQ.tradeManager;
tradeManager.checkRoutes(gameState);
if (!tradeManager.isNewMarketWorth(this.metadata.expectedGain))
{
Engine.ProfileStop();
return;
}
}
gameState.ai.HQ.turnCache.buildingBuilt = true;
if (this.metadata === undefined)
this.metadata = { "base": pos.base };
else if (this.metadata.base === undefined)
this.metadata.base = pos.base;
if (pos.access)
this.metadata.access = pos.access; // needed for Docks whose position is on water
else
this.metadata.access = gameState.ai.accessibility.getAccessValue([pos.x, pos.z]);
if (this.template.buildPlacementType() === "shore")
{
// adjust a bit the position if needed
let cosa = Math.cos(pos.angle);
let sina = Math.sin(pos.angle);
let shiftMax = gameState.ai.HQ.territoryMap.cellSize;
for (let shift = 0; shift <= shiftMax; shift += 2)
{
builder.construct(this.type, pos.x-shift*sina, pos.z-shift*cosa, pos.angle, this.metadata);
if (shift > 0)
builder.construct(this.type, pos.x+shift*sina, pos.z+shift*cosa, pos.angle, this.metadata);
}
}
else if (pos.xx === undefined || pos.x == pos.xx && pos.z == pos.zz)
builder.construct(this.type, pos.x, pos.z, pos.angle, this.metadata);
else // try with the lowest, move towards us unless we're same
{
for (let step = 0; step <= 1; step += 0.2)
builder.construct(this.type, step*pos.x + (1-step)*pos.xx, step*pos.z + (1-step)*pos.zz,
pos.angle, this.metadata);
}
this.onStart(gameState);
Engine.ProfileStop();
if (this.metadata && this.metadata.proximity)
gameState.ai.HQ.navalManager.createTransportIfNeeded(gameState, this.metadata.proximity, [pos.x, pos.z], this.metadata.access);
};
m.ConstructionPlan.prototype.findGoodPosition = function(gameState)
{
let template = this.template;
if (template.buildPlacementType() === "shore")
return this.findDockPosition(gameState);
let HQ = gameState.ai.HQ;
if (template.hasClass("Storehouse") && this.metadata.base)
{
// recompute the best dropsite location in case some conditions have changed
let base = HQ.getBaseByID(this.metadata.base);
let type = this.metadata.type ? this.metadata.type : "wood";
let newpos = base.findBestDropsiteLocation(gameState, type);
if (newpos && newpos.quality > 0)
{
let pos = newpos.pos;
return { "x": pos[0], "z": pos[1], "angle": 3*Math.PI/4, "base": this.metadata.base };
}
}
if (!this.position)
{
if (template.hasClass("CivCentre"))
{
let pos;
if (this.metadata && this.metadata.resource)
{
let proximity = this.metadata.proximity ? this.metadata.proximity : undefined;
pos = HQ.findEconomicCCLocation(gameState, template, this.metadata.resource, proximity);
}
else
pos = HQ.findStrategicCCLocation(gameState, template);
if (pos)
return { "x": pos[0], "z": pos[1], "angle": 3*Math.PI/4, "base": 0 };
return false;
}
else if (template.hasClass("DefenseTower") || template.hasClass("Fortress") || template.hasClass("ArmyCamp"))
{
let pos = HQ.findDefensiveLocation(gameState, template);
if (pos)
return { "x": pos[0], "z": pos[1], "angle": 3*Math.PI/4, "base": pos[2] };
// if this fortress is our first one, just try the standard placement
if (!template.hasClass("Fortress") || gameState.getOwnEntitiesByClass("Fortress", true).hasEntities())
return false;
}
else if (template.hasClass("Market")) // Docks (i.e. NavalMarket) are done before
{
let pos = HQ.findMarketLocation(gameState, template);
if (pos && pos[2] > 0)
{
if (!this.metadata)
this.metadata = {};
this.metadata.expectedGain = pos[3];
return { "x": pos[0], "z": pos[1], "angle": 3*Math.PI/4, "base": pos[2] };
}
else if (!pos)
return false;
}
}
// Compute each tile's closeness to friendly structures:
let placement = new API3.Map(gameState.sharedScript, "territory");
let cellSize = placement.cellSize; // size of each tile
let alreadyHasHouses = false;
if (this.position) // If a position was specified then place the building as close to it as possible
{
let x = Math.floor(this.position[0] / cellSize);
let z = Math.floor(this.position[1] / cellSize);
placement.addInfluence(x, z, 255);
}
else // No position was specified so try and find a sensible place to build
{
// give a small > 0 level as the result of addInfluence is constrained to be > 0
// if we really need houses (i.e. Phasing without enough village building), do not apply these constraints
if (this.metadata && this.metadata.base !== undefined)
{
let base = this.metadata.base;
for (let j = 0; j < placement.map.length; ++j)
if (HQ.basesMap.map[j] == base)
placement.map[j] = 45;
}
else
{
for (let j = 0; j < placement.map.length; ++j)
if (HQ.basesMap.map[j] != 0)
placement.map[j] = 45;
}
if (!HQ.requireHouses || !template.hasClass("House"))
{
gameState.getOwnStructures().forEach(function(ent) {
let pos = ent.position();
let x = Math.round(pos[0] / cellSize);
let z = Math.round(pos[1] / cellSize);
let struct = ent.foundationProgress() === undefined ? ent : gameState.getBuiltTemplate(ent.templateName());
if (struct.resourceDropsiteTypes() && struct.resourceDropsiteTypes().indexOf("food") !== -1)
{
if (template.hasClass("Field") || template.hasClass("Corral"))
placement.addInfluence(x, z, 80/cellSize, 50);
else // If this is not a field add a negative influence because we want to leave this area for fields
placement.addInfluence(x, z, 80/cellSize, -20);
}
else if (template.hasClass("House"))
{
if (ent.hasClass("House"))
{
placement.addInfluence(x, z, 60/cellSize, 40); // houses are close to other houses
alreadyHasHouses = true;
}
else if (!ent.hasClass("StoneWall") || ent.hasClass("Gates"))
placement.addInfluence(x, z, 60/cellSize, -40); // and further away from other stuffs
}
else if (template.hasClass("Farmstead") && (!ent.hasClass("Field") && !ent.hasClass("Corral") &&
(!ent.hasClass("StoneWall") || ent.hasClass("Gates"))))
placement.addInfluence(x, z, 100/cellSize, -25); // move farmsteads away to make room (StoneWall test needed for iber)
else if (template.hasClass("GarrisonFortress") && ent.hasClass("House"))
placement.addInfluence(x, z, 120/cellSize, -50);
else if (template.hasClass("Military"))
placement.addInfluence(x, z, 40/cellSize, -40);
else if (template.genericName() === "Rotary Mill" && ent.hasClass("Field"))
placement.addInfluence(x, z, 60/cellSize, 40);
});
}
if (template.hasClass("Farmstead"))
{
for (let j = 0; j < placement.map.length; ++j)
{
let value = placement.map[j] - gameState.sharedScript.resourceMaps.wood.map[j]/3;
placement.map[j] = value >= 0 ? value : 0;
if (HQ.borderMap.map[j] & m.fullBorder_Mask)
placement.map[j] /= 2; // we need space around farmstead, so disfavor map border
}
}
}
// Requires to be inside our territory, and inside our base territory if required
// and if our first market, put it on border if possible to maximize distance with next market
let favorBorder = template.hasClass("BarterMarket");
let disfavorBorder = gameState.currentPhase() > 1 && !template.hasDefensiveFire();
let militaryBase = this.metadata && this.metadata.militaryBase ? HQ.findBestBaseForMilitary(gameState) : undefined;
if (this.metadata && this.metadata.base !== undefined)
{
let base = this.metadata.base;
for (let j = 0; j < placement.map.length; ++j)
{
if (HQ.basesMap.map[j] != base)
placement.map[j] = 0;
else if (placement.map[j] > 0)
{
if (favorBorder && HQ.borderMap.map[j] & m.border_Mask)
placement.map[j] += 50;
else if (disfavorBorder && !(HQ.borderMap.map[j] & m.fullBorder_Mask))
placement.map[j] += 10;
let x = (j % placement.width + 0.5) * cellSize;
let z = (Math.floor(j / placement.width) + 0.5) * cellSize;
if (HQ.isNearInvadingArmy([x, z]))
placement.map[j] = 0;
}
}
}
else
{
for (let j = 0; j < placement.map.length; ++j)
{
if (HQ.basesMap.map[j] == 0)
placement.map[j] = 0;
else if (placement.map[j] > 0)
{
if (favorBorder && HQ.borderMap.map[j] & m.border_Mask)
placement.map[j] += 50;
else if (disfavorBorder && !(HQ.borderMap.map[j] & m.fullBorder_Mask))
placement.map[j] += 10;
let x = (j % placement.width + 0.5) * cellSize;
let z = (Math.floor(j / placement.width) + 0.5) * cellSize;
if (HQ.isNearInvadingArmy([x, z]))
placement.map[j] = 0;
else if (militaryBase && HQ.basesMap.map[j] == militaryBase)
placement.map[j] += 200;
}
}
}
// Find the best non-obstructed:
// Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close,
// this allows room for units to walk between buildings.
// note: not for houses and dropsites who ought to be closer to either each other or a resource.
// also not for fields who can be stacked quite a bit
let obstructions = m.createObstructionMap(gameState, 0, template);
//obstructions.dumpIm(template.buildPlacementType() + "_obstructions.png");
let radius = 0;
- if (template.hasClass("Fortress") || this.type === gameState.applyCiv("structures/{civ}_siege_workshop") ||
+ if (template.hasClass("Fortress") || this.type === gameState.applyCiv("structures/{civ}_workshop") ||
this.type === gameState.applyCiv("structures/{civ}_elephant_stables"))
radius = Math.floor((template.obstructionRadius().max + 12) / obstructions.cellSize);
else if (template.resourceDropsiteTypes() === undefined && !template.hasClass("House") && !template.hasClass("Field"))
radius = Math.ceil((template.obstructionRadius().max + 4) / obstructions.cellSize);
else
radius = Math.ceil((template.obstructionRadius().max + 0.5) / obstructions.cellSize);
let bestTile;
if (template.hasClass("House") && !alreadyHasHouses)
{
// try to get some space to place several houses first
bestTile = placement.findBestTile(3*radius, obstructions);
if (!bestTile.val)
bestTile = undefined;
}
if (!bestTile)
bestTile = placement.findBestTile(radius, obstructions);
if (!bestTile.val)
return false;
let bestIdx = bestTile.idx;
let x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
let z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
let territorypos = placement.gamePosToMapPos([x, z]);
let territoryIndex = territorypos[0] + territorypos[1]*placement.width;
// default angle = 3*Math.PI/4;
return { "x": x, "z": z, "angle": 3*Math.PI/4, "base": HQ.basesMap.map[territoryIndex] };
};
/**
* Placement of buildings with Dock build category
* metadata.proximity is defined when first dock without any territory
*/
m.ConstructionPlan.prototype.findDockPosition = function(gameState)
{
let template = this.template;
let territoryMap = gameState.ai.HQ.territoryMap;
let obstructions = m.createObstructionMap(gameState, 0, template);
//obstructions.dumpIm(template.buildPlacementType() + "_obstructions.png");
let bestIdx;
let bestJdx;
let bestAngle;
let bestLand;
let bestWater;
let bestVal = -1;
let navalPassMap = gameState.ai.accessibility.navalPassMap;
let width = gameState.ai.HQ.territoryMap.width;
let cellSize = gameState.ai.HQ.territoryMap.cellSize;
let nbShips = gameState.ai.HQ.navalManager.transportShips.length;
let wantedLand = this.metadata && this.metadata.land ? this.metadata.land : null;
let wantedSea = this.metadata && this.metadata.sea ? this.metadata.sea : null;
let proxyAccess = this.metadata && this.metadata.proximity ? gameState.ai.accessibility.getAccessValue(this.metadata.proximity) : null;
if (nbShips === 0 && proxyAccess && proxyAccess > 1)
{
wantedLand = {};
wantedLand[proxyAccess] = true;
}
let dropsiteTypes = template.resourceDropsiteTypes();
let radius = Math.ceil(template.obstructionRadius().max / obstructions.cellSize);
let halfSize = 0; // used for dock angle
let halfDepth = 0; // used by checkPlacement
let halfWidth = 0; // used by checkPlacement
if (template.get("Footprint/Square"))
{
halfSize = Math.max(+template.get("Footprint/Square/@depth"), +template.get("Footprint/Square/@width")) / 2;
halfDepth = +template.get("Footprint/Square/@depth") / 2;
halfWidth = +template.get("Footprint/Square/@width") / 2;
}
else if (template.get("Footprint/Circle"))
{
halfSize = +template.get("Footprint/Circle/@radius");
halfDepth = halfSize;
halfWidth = halfSize;
}
// res is a measure of the amount of resources around, and maxRes is the max value taken into account
// water is a measure of the water space around, and maxWater is the max value that can be returned by checkDockPlacement
const maxRes = 10;
const maxWater = 16;
for (let j = 0; j < territoryMap.length; ++j)
{
if (!this.isDockLocation(gameState, j, halfDepth, wantedLand, wantedSea))
continue;
let dist;
if (!proxyAccess)
{
// if not in our (or allied) territory, we do not want it too far to be able to defend it
dist = this.getFrontierProximity(gameState, j);
if (dist > 4)
continue;
}
let i = territoryMap.getNonObstructedTile(j, radius, obstructions);
if (i < 0)
continue;
if (wantedSea && navalPassMap[i] !== wantedSea)
continue;
let res = dropsiteTypes ? Math.min(maxRes, this.getResourcesAround(gameState, dropsiteTypes, j, 80)) : maxRes;
let pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
if (proxyAccess)
{
// if proximity is given, we look for the nearest point
dist = API3.SquareVectorDistance(this.metadata.proximity, pos);
dist = Math.sqrt(dist) + 20 * (maxRes - res);
}
else
dist += 0.6 * (maxRes - res);
// Add a penalty if on the map border as ship movement will be difficult
if (gameState.ai.HQ.borderMap.map[j] & m.fullBorder_Mask)
dist += 2;
// do a pre-selection, supposing we will have the best possible water
if (bestIdx !== undefined && dist > bestVal + maxWater)
continue;
let x = (i % obstructions.width + 0.5) * obstructions.cellSize;
let z = (Math.floor(i / obstructions.width) + 0.5) * obstructions.cellSize;
let angle = this.getDockAngle(gameState, x, z, halfSize);
if (angle === false)
continue;
let ret = this.checkDockPlacement(gameState, x, z, halfDepth, halfWidth, angle);
if (!ret || !gameState.ai.HQ.landRegions[ret.land])
continue;
// final selection now that the checkDockPlacement water is known
if (bestIdx !== undefined && dist + maxWater - ret.water > bestVal)
continue;
if (this.metadata.proximity && gameState.ai.accessibility.regionSize[ret.land] < 4000)
continue;
if (gameState.ai.HQ.isDangerousLocation(gameState, pos, halfSize))
continue;
bestVal = dist + maxWater - ret.water;
bestIdx = i;
bestJdx = j;
bestAngle = angle;
bestLand = ret.land;
bestWater = ret.water;
}
if (bestVal < 0)
return false;
// if no good place with enough water around and still in first phase, wait for expansion at the next phase
if (!this.metadata.proximity && bestWater < 10 && gameState.currentPhase() == 1)
return false;
let x = (bestIdx % obstructions.width + 0.5) * obstructions.cellSize;
let z = (Math.floor(bestIdx / obstructions.width) + 0.5) * obstructions.cellSize;
// Assign this dock to a base
let baseIndex = gameState.ai.HQ.basesMap.map[bestJdx];
if (!baseIndex)
{
for (let base of gameState.ai.HQ.baseManagers)
{
if (!base.anchor || !base.anchor.position())
continue;
if (base.accessIndex !== bestLand)
continue;
baseIndex = base.ID;
break;
}
if (!baseIndex)
{
if (gameState.ai.HQ.numActiveBase() > 0)
API3.warn("Petra: dock constructed without base index " + baseIndex);
else
baseIndex = gameState.ai.HQ.baseManagers[0].ID;
}
}
return { "x": x, "z": z, "angle": bestAngle, "base": baseIndex, "access": bestLand };
};
/** Algorithm taken from the function GetDockAngle in simulation/helpers/Commands.js */
m.ConstructionPlan.prototype.getDockAngle = function(gameState, x, z, size)
{
let pos = gameState.ai.accessibility.gamePosToMapPos([x, z]);
let k = pos[0] + pos[1]*gameState.ai.accessibility.width;
let seaRef = gameState.ai.accessibility.navalPassMap[k];
if (seaRef < 2)
return false;
const numPoints = 16;
for (let dist = 0; dist < 4; ++dist)
{
let waterPoints = [];
for (let i = 0; i < numPoints; ++i)
{
let angle = 2 * Math.PI * i / numPoints;
pos = [x - (1+dist)*size*Math.sin(angle), z + (1+dist)*size*Math.cos(angle)];
pos = gameState.ai.accessibility.gamePosToMapPos(pos);
if (pos[0] < 0 || pos[0] >= gameState.ai.accessibility.width ||
pos[1] < 0 || pos[1] >= gameState.ai.accessibility.height)
continue;
let j = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (gameState.ai.accessibility.navalPassMap[j] === seaRef)
waterPoints.push(i);
}
let length = waterPoints.length;
if (!length)
continue;
let consec = [];
for (let i = 0; i < length; ++i)
{
let count = 0;
for (let j = 0; j < length-1; ++j)
{
if ((waterPoints[(i + j) % length]+1) % numPoints == waterPoints[(i + j + 1) % length])
++count;
else
break;
}
consec[i] = count;
}
let start = 0;
let count = 0;
for (let c in consec)
{
if (consec[c] > count)
{
start = c;
count = consec[c];
}
}
// If we've found a shoreline, stop searching
if (count != numPoints-1)
return -((waterPoints[start] + consec[start]/2) % numPoints)/numPoints*2*Math.PI;
}
return false;
};
/**
* Algorithm taken from checkPlacement in simulation/components/BuildRestriction.js
* to determine the special dock requirements
* returns {"land": land index for this dock, "water": amount of water around this spot}
*/
m.ConstructionPlan.prototype.checkDockPlacement = function(gameState, x, z, halfDepth, halfWidth, angle)
{
let sz = halfDepth * Math.sin(angle);
let cz = halfDepth * Math.cos(angle);
// center back position
let pos = gameState.ai.accessibility.gamePosToMapPos([x - sz, z - cz]);
let j = pos[0] + pos[1]*gameState.ai.accessibility.width;
let land = gameState.ai.accessibility.landPassMap[j];
if (land < 2)
return null;
// center front position
pos = gameState.ai.accessibility.gamePosToMapPos([x + sz, z + cz]);
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (gameState.ai.accessibility.landPassMap[j] > 1 || gameState.ai.accessibility.navalPassMap[j] < 2)
return null;
// additional constraints compared to BuildRestriction.js to assure we have enough place to build
let sw = halfWidth * Math.cos(angle) * 3 / 4;
let cw = halfWidth * Math.sin(angle) * 3 / 4;
pos = gameState.ai.accessibility.gamePosToMapPos([x - sz + sw, z - cz - cw]);
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (gameState.ai.accessibility.landPassMap[j] != land)
return null;
pos = gameState.ai.accessibility.gamePosToMapPos([x - sz - sw, z - cz + cw]);
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (gameState.ai.accessibility.landPassMap[j] != land)
return null;
let water = 0;
let sp = 15 * Math.sin(angle);
let cp = 15 * Math.cos(angle);
for (let i = 1; i < 5; ++i)
{
pos = gameState.ai.accessibility.gamePosToMapPos([x + sz + i*(sp+sw), z + cz + i*(cp-cw)]);
if (pos[0] < 0 || pos[0] >= gameState.ai.accessibility.width ||
pos[1] < 0 || pos[1] >= gameState.ai.accessibility.height)
break;
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (gameState.ai.accessibility.landPassMap[j] > 1 || gameState.ai.accessibility.navalPassMap[j] < 2)
break;
pos = gameState.ai.accessibility.gamePosToMapPos([x + sz + i*sp, z + cz + i*cp]);
if (pos[0] < 0 || pos[0] >= gameState.ai.accessibility.width ||
pos[1] < 0 || pos[1] >= gameState.ai.accessibility.height)
break;
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (gameState.ai.accessibility.landPassMap[j] > 1 || gameState.ai.accessibility.navalPassMap[j] < 2)
break;
pos = gameState.ai.accessibility.gamePosToMapPos([x + sz + i*(sp-sw), z + cz + i*(cp+cw)]);
if (pos[0] < 0 || pos[0] >= gameState.ai.accessibility.width ||
pos[1] < 0 || pos[1] >= gameState.ai.accessibility.height)
break;
j = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (gameState.ai.accessibility.landPassMap[j] > 1 || gameState.ai.accessibility.navalPassMap[j] < 2)
break;
water += 4;
}
return {"land": land, "water": water};
};
/**
* fast check if we can build a dock: returns false if nearest land is farther than the dock dimension
* if the (object) wantedLand is given, this nearest land should have one of these accessibility
* if wantedSea is given, this tile should be inside this sea
*/
const around = [ [ 1.0, 0.0], [ 0.87, 0.50], [ 0.50, 0.87], [ 0.0, 1.0], [-0.50, 0.87], [-0.87, 0.50],
[-1.0, 0.0], [-0.87,-0.50], [-0.50,-0.87], [ 0.0,-1.0], [ 0.50,-0.87], [ 0.87,-0.50] ];
m.ConstructionPlan.prototype.isDockLocation = function(gameState, j, dimension, wantedLand, wantedSea)
{
let width = gameState.ai.HQ.territoryMap.width;
let cellSize = gameState.ai.HQ.territoryMap.cellSize;
let dist = dimension + 2*cellSize;
let x = (j%width + 0.5) * cellSize;
let z = (Math.floor(j/width) + 0.5) * cellSize;
for (let a of around)
{
let pos = gameState.ai.accessibility.gamePosToMapPos([x + dist*a[0], z + dist*a[1]]);
if (pos[0] < 0 || pos[0] >= gameState.ai.accessibility.width)
continue;
if (pos[1] < 0 || pos[1] >= gameState.ai.accessibility.height)
continue;
let k = pos[0] + pos[1]*gameState.ai.accessibility.width;
let landPass = gameState.ai.accessibility.landPassMap[k];
if (landPass < 2 || wantedLand && !wantedLand[landPass])
continue;
pos = gameState.ai.accessibility.gamePosToMapPos([x - dist*a[0], z - dist*a[1]]);
if (pos[0] < 0 || pos[0] >= gameState.ai.accessibility.width)
continue;
if (pos[1] < 0 || pos[1] >= gameState.ai.accessibility.height)
continue;
k = pos[0] + pos[1]*gameState.ai.accessibility.width;
if (wantedSea && gameState.ai.accessibility.navalPassMap[k] !== wantedSea)
continue;
else if (!wantedSea && gameState.ai.accessibility.navalPassMap[k] < 2)
continue;
return true;
}
return false;
};
/**
* return a measure of the proximity to our frontier (including our allies)
* 0=inside, 1=less than 24m, 2= less than 48m, 3= less than 72m, 4=less than 96m, 5=above 96m
*/
m.ConstructionPlan.prototype.getFrontierProximity = function(gameState, j)
{
let alliedVictory = gameState.getAlliedVictory();
let territoryMap = gameState.ai.HQ.territoryMap;
let territoryOwner = territoryMap.getOwnerIndex(j);
if (territoryOwner === PlayerID || alliedVictory && gameState.isPlayerAlly(territoryOwner))
return 0;
let borderMap = gameState.ai.HQ.borderMap;
let width = territoryMap.width;
let step = Math.round(24 / territoryMap.cellSize);
let ix = j % width;
let iz = Math.floor(j / width);
let best = 5;
for (let a of around)
{
for (let i = 1; i < 5; ++i)
{
let jx = ix + Math.round(i*step*a[0]);
if (jx < 0 || jx >= width)
continue;
let jz = iz + Math.round(i*step*a[1]);
if (jz < 0 || jz >= width)
continue;
if (borderMap.map[jx+width*jz] & m.outside_Mask)
continue;
territoryOwner = territoryMap.getOwnerIndex(jx+width*jz);
if (alliedVictory && gameState.isPlayerAlly(territoryOwner) || territoryOwner === PlayerID)
{
best = Math.min(best, i);
break;
}
}
if (best === 1)
break;
}
return best;
};
/**
* get the sum of the resources (except food) around, inside a given radius
* resources have a weight (1 if dist=0 and 0 if dist=size) doubled for wood
*/
m.ConstructionPlan.prototype.getResourcesAround = function(gameState, types, i, radius)
{
let resourceMaps = gameState.sharedScript.resourceMaps;
let w = resourceMaps.wood.width;
let cellSize = resourceMaps.wood.cellSize;
let size = Math.floor(radius / cellSize);
let ix = i % w;
let iy = Math.floor(i / w);
let total = 0;
let nbcell = 0;
for (let k of types)
{
if (k === "food" || !resourceMaps[k])
continue;
let weigh0 = k === "wood" ? 2 : 1;
for (let dy = 0; dy <= size; ++dy)
{
let dxmax = size - dy;
let ky = iy + dy;
if (ky >= 0 && ky < w)
{
for (let dx = -dxmax; dx <= dxmax; ++dx)
{
let kx = ix + dx;
if (kx < 0 || kx >= w)
continue;
let ddx = dx > 0 ? dx : -dx;
let weight = weigh0 * (dxmax - ddx) / size;
total += weight * resourceMaps[k].map[kx + w * ky];
nbcell += weight;
}
}
if (dy === 0)
continue;
ky = iy - dy;
if (ky >= 0 && ky < w)
{
for (let dx = -dxmax; dx <= dxmax; ++dx)
{
let kx = ix + dx;
if (kx < 0 || kx >= w)
continue;
let ddx = dx > 0 ? dx : -dx;
let weight = weigh0 * (dxmax - ddx) / size;
total += weight * resourceMaps[k].map[kx + w * ky];
nbcell += weight;
}
}
}
}
return nbcell ? total / nbcell : 0;
};
m.ConstructionPlan.prototype.isGo = function(gameState)
{
if (this.goRequirement && this.goRequirement === "houseNeeded")
{
if (!gameState.ai.HQ.canBuild(gameState, "structures/{civ}_house"))
return false;
if (gameState.getPopulationMax() <= gameState.getPopulationLimit())
return false;
let freeSlots = gameState.getPopulationLimit() - gameState.getPopulation();
for (let ent of gameState.getOwnFoundations().values())
{
let template = gameState.getBuiltTemplate(ent.templateName());
if (template)
freeSlots += template.getPopulationBonus();
}
if (gameState.ai.HQ.saveResources)
return freeSlots <= 10;
if (gameState.getPopulation() > 55)
return freeSlots <= 21;
if (gameState.getPopulation() > 30)
return freeSlots <= 15;
return freeSlots <= 10;
}
return true;
};
m.ConstructionPlan.prototype.onStart = function(gameState)
{
if (this.queueToReset)
gameState.ai.queueManager.changePriority(this.queueToReset, gameState.ai.Config.priorities[this.queueToReset]);
};
m.ConstructionPlan.prototype.Serialize = function()
{
return {
"category": this.category,
"type": this.type,
"ID": this.ID,
"metadata": this.metadata,
"cost": this.cost.Serialize(),
"number": this.number,
"position": this.position,
"goRequirement": this.goRequirement || undefined,
"queueToReset": this.queueToReset || undefined
};
};
m.ConstructionPlan.prototype.Deserialize = function(gameState, data)
{
for (let key in data)
this[key] = data[key];
let cost = new API3.Resources();
cost.Deserialize(data.cost);
this.cost = cost;
};
return m;
}(PETRA);
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/brit_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/brit_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/brit_workshop.xml (revision 20457)
@@ -1,20 +1,15 @@
-
+
- 12.0
-
- decay|rubble/rubble_stone_5x5
-
brit
structures/britons/workshop.xml
- structures/fndn_5x5.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/cart_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/cart_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/cart_workshop.xml (revision 20457)
@@ -1,21 +1,9 @@
-
-
-
-
- 12.0
-
-
- decay|rubble/rubble_stone_5x5
-
-
- cart
- ?
-
-
-
-
-
- structures/carthaginians/workshop.xml
- structures/fndn_5x5.xml
-
-
+
+
+
+ cart
+
+
+ structures/carthaginians/workshop.xml
+
+
Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/structures/cart_workshop.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/iber_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/iber_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/iber_workshop.xml (revision 20457)
@@ -1,20 +1,9 @@
-
-
-
- 12.0
-
-
- decay|rubble/rubble_stone_5x5
-
+
iber
-
-
-
structures/iberians/workshop.xml
- structures/fndn_5x5.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_workshop.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_workshop.xml (revision 20457)
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ decay|rubble/rubble_stone_4x4
+
+
+ mace
+ Synergeîon Poliorkētṓn
+
+
+
+
+
+ structures/hellenes/blacksmith.xml
+ structures/fndn_4x4.xml
+
+
Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_workshop.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/pers_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/pers_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/pers_workshop.xml (revision 20457)
@@ -1,21 +1,9 @@
-
-
-
- 12.0
-
-
- decay|rubble/rubble_stone_5x5
-
+
pers
- ?
-
-
-
structures/persians/workshop.xml
- structures/fndn_5x5.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/sele_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/sele_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/sele_workshop.xml (revision 20457)
@@ -1,21 +1,9 @@
-
-
-
- 12.0
-
-
- decay|rubble/rubble_stone_5x5
-
+
sele
- ?
-
-
-
structures/seleucids/workshop.xml
- structures/fndn_5x5.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart_workshop.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart_workshop.xml (revision 20457)
@@ -1,21 +1,10 @@
-
-
-
- 12.0
-
-
- decay|rubble/rubble_stone_5x5
-
+
spart
Stratēgeîon
-
-
-
structures/spartans/workshop.xml
- structures/fndn_5x5.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_workshop.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_workshop.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_workshop.xml (revision 20457)
@@ -0,0 +1,75 @@
+
+
+
+ Barracks
+
+
+ 200
+
+ 300
+
+
+
+
+ 12.0
+
+
+ 2
+ 0.1
+ Unit
+ Siege
+ 0
+ 2
+
+
+ 2000
+ decay|rubble/rubble_stone_5x5
+
+
+ Siege Workshop
+ structures/siege_workshop.png
+ phase_city
+ Build siege engines to destroy your opponents buildings. Research siege technologies to improve the effectiveness of these weapons.
+ City Workshop
+
+
+ 75
+
+
+
+
+
+ 0.7
+
+ units/{civ}_mechanical_siege_ballista_packed
+ units/{civ}_mechanical_siege_scorpio_packed
+ units/{civ}_mechanical_siege_oxybeles_packed
+ units/{civ}_mechanical_siege_lithobolos_packed
+ units/{civ}_mechanical_siege_polybolos_packed
+ units/{civ}_mechanical_siege_ram
+ units/{civ}_mechanical_siege_tower
+
+
+ siege_attack
+ siege_armor
+ siege_cost_metal
+ siege_cost_wood
+ siege_bolt_accuracy
+
+
+
+
+ interface/complete/building/complete_barracks.xml
+ attack/destruction/building_collapse_large.xml
+
+
+
+
+ false
+ 38
+ 40000
+
+
+ structures/fndn_5x5.xml
+
+
Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_workshop.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_archer_b.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_archer_b.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_archer_b.xml (revision 20457)
@@ -1,32 +1,32 @@
- structures/mace_siege_workshop
+ structures/mace_workshop
structures/mace_theatron
structures/mace_library
-25
25
mace
greek
units/mace_infantry_archer_b
Cretan Mercenary Archer
Toxótēs Krētikós
Mercenary
units/hele_infantry_archer.png
phase_town
units/mace_infantry_archer_a
units/macedonians/infantry_archer_b.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_javelinist_b.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_javelinist_b.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_javelinist_b.xml (revision 20457)
@@ -1,24 +1,24 @@
- structures/mace_siege_workshop
+ structures/mace_workshop
structures/mace_theatron
structures/mace_library
mace
greek
units/mace_infantry_javelinist_b
Agrianian Peltast
Peltastḗs Agrías
units/mace_infantry_javelinist.png
units/mace_infantry_javelinist_a
units/macedonians/infantry_javelinist_b.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_pikeman_b.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_pikeman_b.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_pikeman_b.xml (revision 20457)
@@ -1,24 +1,24 @@
- structures/mace_siege_workshop
+ structures/mace_workshop
structures/mace_theatron
structures/mace_library
mace
greek
units/mace_infantry_pikeman_b
Foot Companion
Pezétairos
units/mace_infantry_pikeman.png
units/mace_infantry_pikeman_a
units/macedonians/infantry_spearman_b.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_slinger_b.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_slinger_b.xml (revision 20456)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/units/mace_infantry_slinger_b.xml (revision 20457)
@@ -1,32 +1,32 @@
- structures/mace_siege_workshop
+ structures/mace_workshop
structures/mace_theatron
structures/mace_library
-25
25
mace
greek
units/mace_infantry_slinger_b
Rhodian Slinger
Sphendonḗtēs Rhódios
Mercenary
units/hele_infantry_slinger.png
phase_town
units/mace_infantry_slinger_a
units/macedonians/infantry_slinger_b.xml