diff --git a/.gitignore b/.gitignore
index 1937d52f4..af76202e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -388,3 +388,4 @@ freeciv-web/valhallabuild.sh
freeciv-web/vdebug-remake.sh
publite2/supercows.txt
publite2/supercows.txt
+publite2/init-freeciv-web.sh
diff --git a/README.md b/README.md
index 461d66b0a..1431f3426 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ under the GNU Affero General Public License. See [License](LICENSE.txt) for the
Currently known servers based on Freeciv-web:
- [Freecivweb.org](https://www.freecivweb.org) - Full Freeciv-web
+- [Freeciv TnT](https://www.tacticsandtriumph.com) - Freeciv-web Games & Mods (no PBEM)
- [moving borders](https://fcw.movingborders.es) (Everything except longturn and real-Earth)
![Freeciv-web](https://raw.githubusercontent.com/Lexxie9952/fcw.org-server/master/fcw-2021.png "Freeciv-web screenshot")
@@ -193,11 +194,15 @@ Developers interested in Freeciv-web
If you want to contibute to Freeciv-web, see the [issues](https://github.com/freeciv/freeciv-web/issues) on GibHub and the [TODO file](TODO.md) for
some tasks you can work on. Pull requests on Github is welcome!
+
+Freeciv WebGL
+-------------
+Freeciv WebGL is the 3D version, which uses the Three.js 3D engine. More info about the WebGL 3D version can be found for [developers](https://github.com/freeciv/freeciv-web/tree/develop/freeciv-web/src/main/webapp/javascript/webgl) and [3D artists](https://github.com/freeciv/freeciv-web/wiki/Contributing-Blender-models-for-Freeciv-WebGL).
Contributors to Freeciv-web
---------------------------
-Lexxie9952 [@lexxie9952](https://discordapp.com/users/Lexxie9952)
+Lexxie9952 [@lexxie9952](https://discordapp.com/users/Lexxie9952)
Andreas Røsdal [@andreasrosdal](https://github.com/andreasrosdal)
Marko Lindqvist [@cazfi](https://github.com/cazfi)
Sveinung Kvilhaugsvik [@kvilhaugsvik](https://github.com/kvilhaugsvik)
@@ -205,8 +210,3 @@ Máximo Castañeda [@lonemadmax](https://github.com/lonemadmax)
Gerik Bonaert [@adaxi](https://github.com/adaxi)
and the [Freeciv.org project](http://freeciv.wikia.com/wiki/People)!
-About FCW and this repository
------------------------------
-HELP! We have a dispute against this repository!
-Licensing restrictions not contained in the (A)GPL have been legally threatened against this repository. They seek to restrict our repository- and user- policies from having content allowed by our licenses. Claiming additional restrictions not contained within a license, misrepresenting them as being within that license, and threatening legal enforcement of said restrictions, legally qualifies as a breach of the license by the parties making the claim. Please be patient. FCW has suspended its Repository Policy pending resolution of the licensing infractions against us.
-
diff --git a/blender/CREDITS b/blender/CREDITS
new file mode 100644
index 000000000..2e90e7af2
--- /dev/null
+++ b/blender/CREDITS
@@ -0,0 +1,8 @@
+Some of the models were borrowed from www.blendswap.com and then modified. Original model were in public domain.
+I want to thank the following people who made their work available under Public Domain licence:
+
+1. Joni Vvaris for low poly ak-47
+2. Edimar Dos Reis Silva aka Mutte for his Lot Angel rigged character -- huge help.
+3. Anonymous for hs Black Hawk helicopter model
+4. Praydude for C-5B airplane model
+5. Jeff2207 for a nice model of an elephant.
diff --git a/blender/README.md b/blender/README.md
new file mode 100644
index 000000000..684729fe5
--- /dev/null
+++ b/blender/README.md
@@ -0,0 +1,39 @@
+Blender 3d models created for Freeciv-web.
+==========================================
+
+![Freeciv-web](https://raw.githubusercontent.com/freeciv/freeciv-web/develop/freeciv-web/src/main/webapp/javascript/webgl/freeciv-webgl.png "Freeciv-web WebGL screenshot")
+
+These are the original Blender files for all 3D models in the WebGL version.
+
+3D artists are welcome to improve these models!
+
+Export from Blender to glTF 2.0 .glb binary format
+============================================================
+
+Follow these steps to prepare the 3D models for usage in Freeciv-web:
+
+1. Install and activate the Blender glTF 2.0 exporter
+https://github.com/KhronosGroup/glTF-Blender-Exporter
+
+2. Export the blender file from Blender: File -> Export -> glTF 2.0 (.glb)
+ - Don't export normals.
+ - Export to the .glb file to freeciv-web/src/main/webapp/gltf/
+ - Filename must match unit name from Freeciv ruleset.
+
+3. Rebuild Freeciv-web using freeciv-web/build.sh script.
+
+When creating new additional 3D-models they must also be added to preload.js in Freeciv-web so that they are preloaded correctly.
+
+Be sure to limit the file size and number of verticies, since the result
+will be rendered in a web browser. Check the file size of the generated .glb file!
+
+Don't export any lights or cameras.
+
+3D Models with textures
+=======================
+
+See the citywalls.blend for an example of a 3D model with textures which works in Freeciv-web 3D.
+
+
+The unit models come from here: http://opengameart.org/content/blender-models-for-freeciv-units
+
diff --git a/blender/blender_models_version_1/AEGIS Cruiser.blend b/blender/blender_models_version_1/AEGIS Cruiser.blend
new file mode 100644
index 000000000..da94460df
Binary files /dev/null and b/blender/blender_models_version_1/AEGIS Cruiser.blend differ
diff --git a/blender/blender_models_version_1/AWACS.blend b/blender/blender_models_version_1/AWACS.blend
new file mode 100644
index 000000000..6226d9c33
Binary files /dev/null and b/blender/blender_models_version_1/AWACS.blend differ
diff --git a/blender/blender_models_version_1/Airbase.blend b/blender/blender_models_version_1/Airbase.blend
new file mode 100644
index 000000000..a76e93b84
Binary files /dev/null and b/blender/blender_models_version_1/Airbase.blend differ
diff --git a/blender/blender_models_version_1/Alpine Troops.blend b/blender/blender_models_version_1/Alpine Troops.blend
new file mode 100644
index 000000000..639725851
Binary files /dev/null and b/blender/blender_models_version_1/Alpine Troops.blend differ
diff --git a/blender/blender_models_version_1/Archers.blend b/blender/blender_models_version_1/Archers.blend
new file mode 100644
index 000000000..2ba2f9b14
Binary files /dev/null and b/blender/blender_models_version_1/Archers.blend differ
diff --git a/blender/blender_models_version_1/Armor.blend b/blender/blender_models_version_1/Armor.blend
new file mode 100644
index 000000000..30b567a02
Binary files /dev/null and b/blender/blender_models_version_1/Armor.blend differ
diff --git a/blender/blender_models_version_1/Artillery.blend b/blender/blender_models_version_1/Artillery.blend
new file mode 100644
index 000000000..32b5a7e2d
Binary files /dev/null and b/blender/blender_models_version_1/Artillery.blend differ
diff --git a/blender/blender_models_version_1/Barbarian Leader.blend b/blender/blender_models_version_1/Barbarian Leader.blend
new file mode 100644
index 000000000..f007dd0f9
Binary files /dev/null and b/blender/blender_models_version_1/Barbarian Leader.blend differ
diff --git a/blender/blender_models_version_1/Battleship.blend b/blender/blender_models_version_1/Battleship.blend
new file mode 100644
index 000000000..b30d96b5a
Binary files /dev/null and b/blender/blender_models_version_1/Battleship.blend differ
diff --git a/blender/blender_models_version_1/Bomber.blend b/blender/blender_models_version_1/Bomber.blend
new file mode 100644
index 000000000..2cfdf0086
Binary files /dev/null and b/blender/blender_models_version_1/Bomber.blend differ
diff --git a/blender/blender_models_version_1/Cannon.blend b/blender/blender_models_version_1/Cannon.blend
new file mode 100644
index 000000000..c3da868af
Binary files /dev/null and b/blender/blender_models_version_1/Cannon.blend differ
diff --git a/blender/blender_models_version_1/Caravan.blend b/blender/blender_models_version_1/Caravan.blend
new file mode 100644
index 000000000..d6d82c273
Binary files /dev/null and b/blender/blender_models_version_1/Caravan.blend differ
diff --git a/blender/blender_models_version_1/Caravel.blend b/blender/blender_models_version_1/Caravel.blend
new file mode 100644
index 000000000..da2282061
Binary files /dev/null and b/blender/blender_models_version_1/Caravel.blend differ
diff --git a/blender/blender_models_version_1/Carrier.blend b/blender/blender_models_version_1/Carrier.blend
new file mode 100644
index 000000000..8ee12640b
Binary files /dev/null and b/blender/blender_models_version_1/Carrier.blend differ
diff --git a/blender/blender_models_version_1/Catapult.blend b/blender/blender_models_version_1/Catapult.blend
new file mode 100644
index 000000000..0f67f5c17
Binary files /dev/null and b/blender/blender_models_version_1/Catapult.blend differ
diff --git a/blender/blender_models_version_1/Cavalry.blend b/blender/blender_models_version_1/Cavalry.blend
new file mode 100644
index 000000000..5a2772805
Binary files /dev/null and b/blender/blender_models_version_1/Cavalry.blend differ
diff --git a/blender/blender_models_version_1/Chariot.blend b/blender/blender_models_version_1/Chariot.blend
new file mode 100644
index 000000000..45af515ef
Binary files /dev/null and b/blender/blender_models_version_1/Chariot.blend differ
diff --git a/blender/blender_models_version_1/Cruise Missile.blend b/blender/blender_models_version_1/Cruise Missile.blend
new file mode 100644
index 000000000..1457d5f0c
Binary files /dev/null and b/blender/blender_models_version_1/Cruise Missile.blend differ
diff --git a/blender/blender_models_version_1/Cruiser.blend b/blender/blender_models_version_1/Cruiser.blend
new file mode 100644
index 000000000..a7a76fff2
Binary files /dev/null and b/blender/blender_models_version_1/Cruiser.blend differ
diff --git a/blender/blender_models_version_1/Destroyer.blend b/blender/blender_models_version_1/Destroyer.blend
new file mode 100644
index 000000000..37100e114
Binary files /dev/null and b/blender/blender_models_version_1/Destroyer.blend differ
diff --git a/blender/blender_models_version_1/Diplomat.blend b/blender/blender_models_version_1/Diplomat.blend
new file mode 100644
index 000000000..aa1aa933b
Binary files /dev/null and b/blender/blender_models_version_1/Diplomat.blend differ
diff --git a/blender/blender_models_version_1/Dragoons.blend b/blender/blender_models_version_1/Dragoons.blend
new file mode 100644
index 000000000..abc3f5714
Binary files /dev/null and b/blender/blender_models_version_1/Dragoons.blend differ
diff --git a/blender/blender_models_version_1/Engineers.blend b/blender/blender_models_version_1/Engineers.blend
new file mode 100644
index 000000000..4aeebf5a6
Binary files /dev/null and b/blender/blender_models_version_1/Engineers.blend differ
diff --git a/blender/blender_models_version_1/Explorer.blend b/blender/blender_models_version_1/Explorer.blend
new file mode 100644
index 000000000..25853ea87
Binary files /dev/null and b/blender/blender_models_version_1/Explorer.blend differ
diff --git a/blender/blender_models_version_1/Fighter.blend b/blender/blender_models_version_1/Fighter.blend
new file mode 100644
index 000000000..cc9e94296
Binary files /dev/null and b/blender/blender_models_version_1/Fighter.blend differ
diff --git a/blender/blender_models_version_1/Fish.blend b/blender/blender_models_version_1/Fish.blend
new file mode 100644
index 000000000..24cc6473b
Binary files /dev/null and b/blender/blender_models_version_1/Fish.blend differ
diff --git a/blender/blender_models_version_1/Fortress.blend b/blender/blender_models_version_1/Fortress.blend
new file mode 100644
index 000000000..4f3836c07
Binary files /dev/null and b/blender/blender_models_version_1/Fortress.blend differ
diff --git a/blender/blender_models_version_1/Freight.blend b/blender/blender_models_version_1/Freight.blend
new file mode 100644
index 000000000..5431034cf
Binary files /dev/null and b/blender/blender_models_version_1/Freight.blend differ
diff --git a/blender/blender_models_version_1/Frigate.blend b/blender/blender_models_version_1/Frigate.blend
new file mode 100644
index 000000000..c7e9b3f2a
Binary files /dev/null and b/blender/blender_models_version_1/Frigate.blend differ
diff --git a/blender/blender_models_version_1/Galleon.blend b/blender/blender_models_version_1/Galleon.blend
new file mode 100644
index 000000000..cbfa55b34
Binary files /dev/null and b/blender/blender_models_version_1/Galleon.blend differ
diff --git a/blender/blender_models_version_1/Helicopter.blend b/blender/blender_models_version_1/Helicopter.blend
new file mode 100644
index 000000000..3e536222d
Binary files /dev/null and b/blender/blender_models_version_1/Helicopter.blend differ
diff --git a/blender/blender_models_version_1/Horsemen.blend b/blender/blender_models_version_1/Horsemen.blend
new file mode 100644
index 000000000..58fe3bce7
Binary files /dev/null and b/blender/blender_models_version_1/Horsemen.blend differ
diff --git a/blender/blender_models_version_1/Howitzer.blend b/blender/blender_models_version_1/Howitzer.blend
new file mode 100644
index 000000000..b38d42d35
Binary files /dev/null and b/blender/blender_models_version_1/Howitzer.blend differ
diff --git a/blender/blender_models_version_1/Hut.blend b/blender/blender_models_version_1/Hut.blend
new file mode 100644
index 000000000..394002e2d
Binary files /dev/null and b/blender/blender_models_version_1/Hut.blend differ
diff --git a/blender/blender_models_version_1/Ironclad.blend b/blender/blender_models_version_1/Ironclad.blend
new file mode 100644
index 000000000..5bef6d353
Binary files /dev/null and b/blender/blender_models_version_1/Ironclad.blend differ
diff --git a/blender/blender_models_version_1/Knights.blend b/blender/blender_models_version_1/Knights.blend
new file mode 100644
index 000000000..4df949987
Binary files /dev/null and b/blender/blender_models_version_1/Knights.blend differ
diff --git a/blender/blender_models_version_1/Legion.blend b/blender/blender_models_version_1/Legion.blend
new file mode 100644
index 000000000..e76141bfc
Binary files /dev/null and b/blender/blender_models_version_1/Legion.blend differ
diff --git a/blender/blender_models_version_1/Marines.blend b/blender/blender_models_version_1/Marines.blend
new file mode 100644
index 000000000..a77d37244
Binary files /dev/null and b/blender/blender_models_version_1/Marines.blend differ
diff --git a/blender/blender_models_version_1/Mech. Inf..blend b/blender/blender_models_version_1/Mech. Inf..blend
new file mode 100644
index 000000000..629710779
Binary files /dev/null and b/blender/blender_models_version_1/Mech. Inf..blend differ
diff --git a/blender/blender_models_version_1/Migrants.blend b/blender/blender_models_version_1/Migrants.blend
new file mode 100644
index 000000000..e9922555a
Binary files /dev/null and b/blender/blender_models_version_1/Migrants.blend differ
diff --git a/blender/blender_models_version_1/Mine.blend b/blender/blender_models_version_1/Mine.blend
new file mode 100644
index 000000000..90cd46a89
Binary files /dev/null and b/blender/blender_models_version_1/Mine.blend differ
diff --git a/blender/blender_models_version_1/Musketeers.blend b/blender/blender_models_version_1/Musketeers.blend
new file mode 100644
index 000000000..d108ca684
Binary files /dev/null and b/blender/blender_models_version_1/Musketeers.blend differ
diff --git a/blender/blender_models_version_1/Nuclear.blend b/blender/blender_models_version_1/Nuclear.blend
new file mode 100644
index 000000000..2263f09dc
Binary files /dev/null and b/blender/blender_models_version_1/Nuclear.blend differ
diff --git a/blender/blender_models_version_1/Paratroopers.blend b/blender/blender_models_version_1/Paratroopers.blend
new file mode 100644
index 000000000..6528a044c
Binary files /dev/null and b/blender/blender_models_version_1/Paratroopers.blend differ
diff --git a/blender/blender_models_version_1/Partisan.blend b/blender/blender_models_version_1/Partisan.blend
new file mode 100644
index 000000000..6a20f525f
Binary files /dev/null and b/blender/blender_models_version_1/Partisan.blend differ
diff --git a/blender/blender_models_version_1/Phalanx.blend b/blender/blender_models_version_1/Phalanx.blend
new file mode 100644
index 000000000..eb6637c17
Binary files /dev/null and b/blender/blender_models_version_1/Phalanx.blend differ
diff --git a/blender/blender_models_version_1/Pikemen.blend b/blender/blender_models_version_1/Pikemen.blend
new file mode 100644
index 000000000..ed74d1f2f
Binary files /dev/null and b/blender/blender_models_version_1/Pikemen.blend differ
diff --git a/blender/blender_models_version_1/Riflemen.blend b/blender/blender_models_version_1/Riflemen.blend
new file mode 100644
index 000000000..628239745
Binary files /dev/null and b/blender/blender_models_version_1/Riflemen.blend differ
diff --git a/blender/blender_models_version_1/Ruins.blend b/blender/blender_models_version_1/Ruins.blend
new file mode 100644
index 000000000..c02c87308
Binary files /dev/null and b/blender/blender_models_version_1/Ruins.blend differ
diff --git a/blender/blender_models_version_1/Settlers.blend b/blender/blender_models_version_1/Settlers.blend
new file mode 100644
index 000000000..8d3b21c3f
Binary files /dev/null and b/blender/blender_models_version_1/Settlers.blend differ
diff --git a/blender/blender_models_version_1/Spy.blend b/blender/blender_models_version_1/Spy.blend
new file mode 100644
index 000000000..e87167f46
Binary files /dev/null and b/blender/blender_models_version_1/Spy.blend differ
diff --git a/blender/blender_models_version_1/Stealth Bomber.blend b/blender/blender_models_version_1/Stealth Bomber.blend
new file mode 100644
index 000000000..089c265a8
Binary files /dev/null and b/blender/blender_models_version_1/Stealth Bomber.blend differ
diff --git a/blender/blender_models_version_1/Stealth Fighter.blend b/blender/blender_models_version_1/Stealth Fighter.blend
new file mode 100644
index 000000000..32a6492bd
Binary files /dev/null and b/blender/blender_models_version_1/Stealth Fighter.blend differ
diff --git a/blender/blender_models_version_1/Submarine.blend b/blender/blender_models_version_1/Submarine.blend
new file mode 100644
index 000000000..ee41eaafc
Binary files /dev/null and b/blender/blender_models_version_1/Submarine.blend differ
diff --git a/blender/blender_models_version_1/Transport.blend b/blender/blender_models_version_1/Transport.blend
new file mode 100644
index 000000000..df7bab361
Binary files /dev/null and b/blender/blender_models_version_1/Transport.blend differ
diff --git a/blender/blender_models_version_1/Trireme.blend b/blender/blender_models_version_1/Trireme.blend
new file mode 100644
index 000000000..baae5de2b
Binary files /dev/null and b/blender/blender_models_version_1/Trireme.blend differ
diff --git a/blender/blender_models_version_1/Warriors.blend b/blender/blender_models_version_1/Warriors.blend
new file mode 100644
index 000000000..842ea46a3
Binary files /dev/null and b/blender/blender_models_version_1/Warriors.blend differ
diff --git a/blender/blender_models_version_1/Whales.blend b/blender/blender_models_version_1/Whales.blend
new file mode 100644
index 000000000..b5db4e514
Binary files /dev/null and b/blender/blender_models_version_1/Whales.blend differ
diff --git a/blender/blender_models_version_1/Workers.blend b/blender/blender_models_version_1/Workers.blend
new file mode 100644
index 000000000..4331b4bc4
Binary files /dev/null and b/blender/blender_models_version_1/Workers.blend differ
diff --git a/blender/blender_models_version_1/blender-settlers-example.png b/blender/blender_models_version_1/blender-settlers-example.png
new file mode 100644
index 000000000..507b6c4c6
Binary files /dev/null and b/blender/blender_models_version_1/blender-settlers-example.png differ
diff --git a/blender/blender_models_version_1/brick1.png b/blender/blender_models_version_1/brick1.png
new file mode 100644
index 000000000..7debbf780
Binary files /dev/null and b/blender/blender_models_version_1/brick1.png differ
diff --git a/blender/blender_models_version_1/city_classical_0.blend b/blender/blender_models_version_1/city_classical_0.blend
new file mode 100644
index 000000000..f356f29c5
Binary files /dev/null and b/blender/blender_models_version_1/city_classical_0.blend differ
diff --git a/blender/blender_models_version_1/city_classical_1.blend b/blender/blender_models_version_1/city_classical_1.blend
new file mode 100644
index 000000000..aae17263f
Binary files /dev/null and b/blender/blender_models_version_1/city_classical_1.blend differ
diff --git a/blender/blender_models_version_1/city_classical_2.blend b/blender/blender_models_version_1/city_classical_2.blend
new file mode 100644
index 000000000..ad4f31647
Binary files /dev/null and b/blender/blender_models_version_1/city_classical_2.blend differ
diff --git a/blender/blender_models_version_1/city_european_0.blend b/blender/blender_models_version_1/city_european_0.blend
new file mode 100644
index 000000000..f356f29c5
Binary files /dev/null and b/blender/blender_models_version_1/city_european_0.blend differ
diff --git a/blender/blender_models_version_1/city_european_1.blend b/blender/blender_models_version_1/city_european_1.blend
new file mode 100644
index 000000000..aae17263f
Binary files /dev/null and b/blender/blender_models_version_1/city_european_1.blend differ
diff --git a/blender/blender_models_version_1/city_european_2.blend b/blender/blender_models_version_1/city_european_2.blend
new file mode 100644
index 000000000..ad4f31647
Binary files /dev/null and b/blender/blender_models_version_1/city_european_2.blend differ
diff --git a/blender/blender_models_version_1/citywalls.blend b/blender/blender_models_version_1/citywalls.blend
new file mode 100644
index 000000000..7a994af8c
Binary files /dev/null and b/blender/blender_models_version_1/citywalls.blend differ
diff --git a/blender/blender_models_version_1/farmland.png b/blender/blender_models_version_1/farmland.png
new file mode 100644
index 000000000..c488d4663
Binary files /dev/null and b/blender/blender_models_version_1/farmland.png differ
diff --git a/blender/blender_models_version_2/BaseModel.blend b/blender/blender_models_version_2/BaseModel.blend
new file mode 100644
index 000000000..b31ca5443
Binary files /dev/null and b/blender/blender_models_version_2/BaseModel.blend differ
diff --git a/blender/blender_models_version_2/Buildings/EuropeanCity.blend b/blender/blender_models_version_2/Buildings/EuropeanCity.blend
new file mode 100644
index 000000000..c5f7b348b
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/EuropeanCity.blend differ
diff --git a/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier1.glb b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier1.glb
new file mode 100644
index 000000000..ec9579621
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier1.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier2.glb b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier2.glb
new file mode 100644
index 000000000..805ee5cdb
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier2.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier3.glb b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier3.glb
new file mode 100644
index 000000000..989c8498b
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier3.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier4.glb b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier4.glb
new file mode 100644
index 000000000..d5a8227df
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier4.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier5.glb b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier5.glb
new file mode 100644
index 000000000..f1a85c0e8
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/EuropeanCity_Tier5.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier1.glb b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier1.glb
new file mode 100644
index 000000000..971ee043a
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier1.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier2.glb b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier2.glb
new file mode 100644
index 000000000..f6dec0dce
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier2.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier3.glb b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier3.glb
new file mode 100644
index 000000000..b38cd9dd4
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier3.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier4.glb b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier4.glb
new file mode 100644
index 000000000..7059bbc8e
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier4.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier5.glb b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier5.glb
new file mode 100644
index 000000000..cb384c18e
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier5.glb differ
diff --git a/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier5_pbr.glb b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier5_pbr.glb
new file mode 100644
index 000000000..af1ee9a63
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Export/ModernCity_Tier5_pbr.glb differ
diff --git a/blender/blender_models_version_2/Buildings/ModernCity.blend b/blender/blender_models_version_2/Buildings/ModernCity.blend
new file mode 100644
index 000000000..2f9b26d6a
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/ModernCity.blend differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/CityPlates.psd b/blender/blender_models_version_2/Buildings/Renders/CityPlates.psd
new file mode 100644
index 000000000..10f5890e3
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/CityPlates.psd differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/EuropeanCity.jpg b/blender/blender_models_version_2/Buildings/Renders/EuropeanCity.jpg
new file mode 100644
index 000000000..2100c83ab
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/EuropeanCity.jpg differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/EuropeanCity.png b/blender/blender_models_version_2/Buildings/Renders/EuropeanCity.png
new file mode 100644
index 000000000..01eca0c1a
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/EuropeanCity.png differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/EuropeanCity1.png b/blender/blender_models_version_2/Buildings/Renders/EuropeanCity1.png
new file mode 100644
index 000000000..c7d0d4397
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/EuropeanCity1.png differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/ModernCity.jpg b/blender/blender_models_version_2/Buildings/Renders/ModernCity.jpg
new file mode 100644
index 000000000..95e570149
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/ModernCity.jpg differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/ModernCity.png b/blender/blender_models_version_2/Buildings/Renders/ModernCity.png
new file mode 100644
index 000000000..0f1977a79
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/ModernCity.png differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/ModernCity1.png b/blender/blender_models_version_2/Buildings/Renders/ModernCity1.png
new file mode 100644
index 000000000..c9091e03c
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/ModernCity1.png differ
diff --git a/blender/blender_models_version_2/Buildings/Renders/ModernCity2.png b/blender/blender_models_version_2/Buildings/Renders/ModernCity2.png
new file mode 100644
index 000000000..2511b225b
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Renders/ModernCity2.png differ
diff --git a/blender/blender_models_version_2/Buildings/Textures/CityTextures.psd b/blender/blender_models_version_2/Buildings/Textures/CityTextures.psd
new file mode 100644
index 000000000..f04e23670
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Textures/CityTextures.psd differ
diff --git a/blender/blender_models_version_2/Buildings/Textures/EuropeanCity.png b/blender/blender_models_version_2/Buildings/Textures/EuropeanCity.png
new file mode 100644
index 000000000..2feea08f9
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Textures/EuropeanCity.png differ
diff --git a/blender/blender_models_version_2/Buildings/Textures/ModernCity.png b/blender/blender_models_version_2/Buildings/Textures/ModernCity.png
new file mode 100644
index 000000000..cb626ab64
Binary files /dev/null and b/blender/blender_models_version_2/Buildings/Textures/ModernCity.png differ
diff --git a/blender/blender_models_version_2/Characters/Explorer.blend b/blender/blender_models_version_2/Characters/Explorer.blend
new file mode 100644
index 000000000..48970e280
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Explorer.blend differ
diff --git a/blender/blender_models_version_2/Characters/Export/Explorer.glb b/blender/blender_models_version_2/Characters/Export/Explorer.glb
new file mode 100644
index 000000000..57ea517ea
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Export/Explorer.glb differ
diff --git a/blender/blender_models_version_2/Characters/Export/Settlers.glb b/blender/blender_models_version_2/Characters/Export/Settlers.glb
new file mode 100644
index 000000000..1cc5019e9
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Export/Settlers.glb differ
diff --git a/blender/blender_models_version_2/Characters/Renders/Explorer.png b/blender/blender_models_version_2/Characters/Renders/Explorer.png
new file mode 100644
index 000000000..7808a8e93
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Renders/Explorer.png differ
diff --git a/blender/blender_models_version_2/Characters/Renders/Settler.png b/blender/blender_models_version_2/Characters/Renders/Settler.png
new file mode 100644
index 000000000..027386d39
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Renders/Settler.png differ
diff --git a/blender/blender_models_version_2/Characters/Settler.blend b/blender/blender_models_version_2/Characters/Settler.blend
new file mode 100644
index 000000000..16904762d
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Settler.blend differ
diff --git a/blender/blender_models_version_2/Characters/Textures/Base.psd b/blender/blender_models_version_2/Characters/Textures/Base.psd
new file mode 100644
index 000000000..2f5ace68f
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Textures/Base.psd differ
diff --git a/blender/blender_models_version_2/Characters/Textures/BaseTexture.png b/blender/blender_models_version_2/Characters/Textures/BaseTexture.png
new file mode 100644
index 000000000..e43f7c826
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Textures/BaseTexture.png differ
diff --git a/blender/blender_models_version_2/Characters/Textures/Explorer.png b/blender/blender_models_version_2/Characters/Textures/Explorer.png
new file mode 100644
index 000000000..92be637a3
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Textures/Explorer.png differ
diff --git a/blender/blender_models_version_2/Characters/Textures/HorseBase.psd b/blender/blender_models_version_2/Characters/Textures/HorseBase.psd
new file mode 100644
index 000000000..da6c985da
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Textures/HorseBase.psd differ
diff --git a/blender/blender_models_version_2/Characters/Textures/Settler.png b/blender/blender_models_version_2/Characters/Textures/Settler.png
new file mode 100644
index 000000000..0478a9691
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Textures/Settler.png differ
diff --git a/blender/blender_models_version_2/Characters/Textures/SettlerCart.png b/blender/blender_models_version_2/Characters/Textures/SettlerCart.png
new file mode 100644
index 000000000..d41e47a0e
Binary files /dev/null and b/blender/blender_models_version_2/Characters/Textures/SettlerCart.png differ
diff --git a/blender/blender_models_version_2/CityWallBig.glb b/blender/blender_models_version_2/CityWallBig.glb
new file mode 100644
index 000000000..9609ef114
Binary files /dev/null and b/blender/blender_models_version_2/CityWallBig.glb differ
diff --git a/blender/blender_models_version_2/CityWallSmall.glb b/blender/blender_models_version_2/CityWallSmall.glb
new file mode 100644
index 000000000..0df539113
Binary files /dev/null and b/blender/blender_models_version_2/CityWallSmall.glb differ
diff --git a/blender/blender_models_version_2/EuropeanCity_Tier1.glb b/blender/blender_models_version_2/EuropeanCity_Tier1.glb
new file mode 100644
index 000000000..55a5deb5e
Binary files /dev/null and b/blender/blender_models_version_2/EuropeanCity_Tier1.glb differ
diff --git a/blender/blender_models_version_2/EuropeanCity_Tier2.glb b/blender/blender_models_version_2/EuropeanCity_Tier2.glb
new file mode 100644
index 000000000..288afc673
Binary files /dev/null and b/blender/blender_models_version_2/EuropeanCity_Tier2.glb differ
diff --git a/blender/blender_models_version_2/EuropeanCity_Tier3.glb b/blender/blender_models_version_2/EuropeanCity_Tier3.glb
new file mode 100644
index 000000000..3a900677f
Binary files /dev/null and b/blender/blender_models_version_2/EuropeanCity_Tier3.glb differ
diff --git a/blender/blender_models_version_2/EuropeanCity_Tier4.glb b/blender/blender_models_version_2/EuropeanCity_Tier4.glb
new file mode 100644
index 000000000..56eb5871d
Binary files /dev/null and b/blender/blender_models_version_2/EuropeanCity_Tier4.glb differ
diff --git a/blender/blender_models_version_2/EuropeanCity_Tier5.glb b/blender/blender_models_version_2/EuropeanCity_Tier5.glb
new file mode 100644
index 000000000..50a1e0b56
Binary files /dev/null and b/blender/blender_models_version_2/EuropeanCity_Tier5.glb differ
diff --git a/blender/blender_models_version_2/Explorer.glb b/blender/blender_models_version_2/Explorer.glb
new file mode 100644
index 000000000..57ea517ea
Binary files /dev/null and b/blender/blender_models_version_2/Explorer.glb differ
diff --git a/blender/blender_models_version_2/Hut.glb b/blender/blender_models_version_2/Hut.glb
new file mode 100644
index 000000000..4d3bf1dee
Binary files /dev/null and b/blender/blender_models_version_2/Hut.glb differ
diff --git a/blender/blender_models_version_2/ModernCity_Tier1.glb b/blender/blender_models_version_2/ModernCity_Tier1.glb
new file mode 100644
index 000000000..971ee043a
Binary files /dev/null and b/blender/blender_models_version_2/ModernCity_Tier1.glb differ
diff --git a/blender/blender_models_version_2/ModernCity_Tier2.glb b/blender/blender_models_version_2/ModernCity_Tier2.glb
new file mode 100644
index 000000000..f6dec0dce
Binary files /dev/null and b/blender/blender_models_version_2/ModernCity_Tier2.glb differ
diff --git a/blender/blender_models_version_2/ModernCity_Tier3.glb b/blender/blender_models_version_2/ModernCity_Tier3.glb
new file mode 100644
index 000000000..b38cd9dd4
Binary files /dev/null and b/blender/blender_models_version_2/ModernCity_Tier3.glb differ
diff --git a/blender/blender_models_version_2/ModernCity_Tier4.glb b/blender/blender_models_version_2/ModernCity_Tier4.glb
new file mode 100644
index 000000000..7059bbc8e
Binary files /dev/null and b/blender/blender_models_version_2/ModernCity_Tier4.glb differ
diff --git a/blender/blender_models_version_2/ModernCity_Tier5.glb b/blender/blender_models_version_2/ModernCity_Tier5.glb
new file mode 100644
index 000000000..cb384c18e
Binary files /dev/null and b/blender/blender_models_version_2/ModernCity_Tier5.glb differ
diff --git a/blender/blender_models_version_2/ModernCity_Tier5_pbr.glb b/blender/blender_models_version_2/ModernCity_Tier5_pbr.glb
new file mode 100644
index 000000000..af1ee9a63
Binary files /dev/null and b/blender/blender_models_version_2/ModernCity_Tier5_pbr.glb differ
diff --git a/blender/blender_models_version_2/Settler.glb b/blender/blender_models_version_2/Settler.glb
new file mode 100644
index 000000000..40b8b828a
Binary files /dev/null and b/blender/blender_models_version_2/Settler.glb differ
diff --git a/blender/blender_models_version_2/Worker.glb b/blender/blender_models_version_2/Worker.glb
new file mode 100644
index 000000000..1513af6e4
Binary files /dev/null and b/blender/blender_models_version_2/Worker.glb differ
diff --git a/blender/blender_models_version_2/Worrior.glb b/blender/blender_models_version_2/Worrior.glb
new file mode 100644
index 000000000..ab48c4ee9
Binary files /dev/null and b/blender/blender_models_version_2/Worrior.glb differ
diff --git a/fcw-2021.png b/fcw-2021.png
deleted file mode 100644
index 65df9c6f3..000000000
Binary files a/fcw-2021.png and /dev/null differ
diff --git a/freeciv-proxy/civcom.py b/freeciv-proxy/civcom.py
index bf5600236..d7f5452df 100755
--- a/freeciv-proxy/civcom.py
+++ b/freeciv-proxy/civcom.py
@@ -59,6 +59,7 @@ def run(self):
self.send_error_to_client(
"Proxy unable to connect to civserver. Error: %s" %
(reason))
+ self.close_connection()
return
# send initial login packet to civserver
@@ -126,8 +127,13 @@ def close_connection(self):
"Server connection closed. Removing civcom thread for " +
self.username)
- if (hasattr(self.civwebserver, "civcoms") and self.key in list(self.civwebserver.civcoms.keys())):
- del self.civwebserver.civcoms[self.key]
+ # Flush buffers
+ self.send_packets_to_client()
+ self.send_packets_to_civserver()
+
+ if self.civwebserver is not None:
+ conn = self.civwebserver
+ conn.io_loop.add_callback(lambda: conn.close())
if (self.socket is not None):
self.socket.close()
@@ -154,7 +160,8 @@ def send_packets_to_client(self):
packet = self.get_client_result_string()
if (packet is not None and self.civwebserver is not None):
# Calls the write_message callback on the next Tornado I/O loop iteration (thread safely).
- self.civwebserver.io_loop.add_callback(lambda: self.civwebserver.write_message(packet))
+ conn = self.civwebserver
+ conn.io_loop.add_callback(lambda: conn.write_message(packet))
def get_client_result_string(self):
result = ""
diff --git a/freeciv-proxy/freeciv-proxy.py b/freeciv-proxy/freeciv-proxy.py
index a815f6b58..847d9e9b9 100755
--- a/freeciv-proxy/freeciv-proxy.py
+++ b/freeciv-proxy/freeciv-proxy.py
@@ -159,17 +159,38 @@ def get_game_auth_method(self, cursor):
return "password"
def check_user_password(self, cursor, username, password):
- query = ("select secure_hashed_password, activated from auth where lower(username)=lower(%(usr)s)")
- cursor.execute(query, {'usr': username, 'pwd': password})
+ # Encryption method transition period code. Clear out first query and
+ # compat_encrypt use after the transition period.
+ query = ("select digest_pw from auth where lower(username) = lower(%(usr)s)")
+ cursor.execute(query, {'usr': username})
+ result = cursor.fetchall()
+ if len(result) == 0:
+ return True
+ compat_encrypt = not result[0][0]
+ if compat_encrypt:
+ query = ("select secure_hashed_password, CAST(ENCRYPT(%(pwd)s, secure_hashed_password) AS CHAR), activated, id from auth where lower(username)=lower(%(usr)s)")
+ cursor.execute(query, {'usr': username, 'pwd': password})
+ else:
+ query = ("select secure_hashed_password, activated from auth where lower(username)=lower(%(usr)s)")
+ cursor.execute(query, {'usr': username})
result = cursor.fetchall()
if len(result) == 0:
# Unreserved user, no password needed
return True
- for secure_shashed_password, active in result:
- if (active == 0): return False
- if secure_shashed_password == hashlib.sha256(password.encode('utf-8')).hexdigest(): return True
+ if compat_encrypt:
+ for db_pass, encrypted_pass, active, uid in result:
+ if (active == 0): return False
+ if db_pass == encrypted_pass:
+ new_hash = hashlib.sha256(password.encode('utf-8')).hexdigest()
+ query = ("update auth set secure_hashed_password = %(pwd)s, digest_pw = TRUE where id = %(uid)s;")
+ cursor.execute(query, {'pwd': new_hash, 'uid': uid})
+ return True
+ else:
+ for secure_shashed_password, active in result:
+ if (active == 0): return False
+ if secure_shashed_password == hashlib.sha256(password.encode('utf-8')).hexdigest(): return True
return False
diff --git a/freeciv-web/build-js.sh b/freeciv-web/build-js.sh
index fd73a354a..43e7d53ef 100755
--- a/freeciv-web/build-js.sh
+++ b/freeciv-web/build-js.sh
@@ -6,12 +6,14 @@ FCW_DEST=/var/lib/tomcat8/webapps/freeciv-web
mvn compile && \
echo "Copying target/javascript/webclient.* to ${FCW_DEST}/javascript" && \
cp target/freeciv-web/javascript/webclient.* "${FCW_DEST}"/javascript/ && \
-echo target/freeciv-web/javascript/webgl/libs/webgl-client* "${FCW_DEST}"/javascript/webgl/libs && \
- cp target/freeciv-web/javascript/webgl/libs/webgl-client* "${FCW_DEST}"/javascript/webgl/libs/
+echo target/freeciv-web/javascript/webgl-client* "${FCW_DEST}"/javascript/ && \
+ cp target/freeciv-web/javascript/webgl-client* "${FCW_DEST}"/javascript/
# update timestamp to clear browser cache.
sed -i.bak -e "s/ts=\"/ts=\"1/" -e "s/\?ts=/\?ts=1/" "${FCW_DEST}"/webclient/index.jsp
+# 3d webgl shaders
+cp src/main/webapp/javascript/webgl/shaders/*.* "${FCW_DEST}"/javascript/webgl/shaders/
# let user know when it's finished
echo $'\a'
diff --git a/freeciv-web/build-rules.sh b/freeciv-web/build-rules.sh
index c5715688b..90202b390 100755
--- a/freeciv-web/build-rules.sh
+++ b/freeciv-web/build-rules.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# builds Freeciv-web, copies the war file to Tomcat and builds the selected rulesets.
-RULESETS=(civ2civ3 classic multiplayer mpplus mp2 ag mp2-brava experimental mp2-caravel mp2-dragoon)
+RULESETS=(mp2-dragoon mp2-caravel mp2-brava mp2-ag mpplus classic civ2civ3 multiplayer civ1 civ2 mp2 sandbox webperimental experimental alien)
TOPDIR="$( cd ../"$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
printf "\n**********************************************************************\n"
diff --git a/freeciv-web/clean-rules.sh b/freeciv-web/clean-rules.sh
index d57d6240e..b7100adfa 100644
--- a/freeciv-web/clean-rules.sh
+++ b/freeciv-web/clean-rules.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# builds Freeciv-web, copies the war file to Tomcat and builds the selected rulesets.
-RULESETS=(classic civ2civ3 maptest multiplayer mpplus mp2sandbox mp2 ag ag2 mp2-brava mp2-caravel mp2-dragoon)
+RULESETS=(classic civ2civ3 maptest multiplayer mpplus mp2sandbox mp2 ag mp2-ag mp2-brava mp2-caravel mp2-dragoon)
TOPDIR="$( cd ../"$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
printf "\n./clean-rules.sh: DEPLOYED SERVER VERSION. Use ./vclean-rules.sh for vagrant installations.\n"
@@ -16,6 +16,7 @@ printf "************************************************************************
printf "\nOverwriting auto-generated manual with release-version: MP2-Avant Garde"
cp ~/freeciv-web/freeciv-web/src/derived/webapp/man/ag7.bak.html ~/freeciv-web/freeciv-web/src/derived/webapp/man/ag7.html
+ cp ~/freeciv-web/freeciv-web/src/derived/webapp/man/ag7.bak.html ~/freeciv-web/freeciv-web/src/derived/webapp/man/mp2-ag7.html
printf "\nOverwriting auto-generated manual with release-version: MP2-Brava"
cp ~/freeciv-web/freeciv-web/src/derived/webapp/man/mp2-brava7.bak.html ~/freeciv-web/freeciv-web/src/derived/webapp/man/mp2-brava7.html
printf "\nOverwriting auto-generated manual with release-version: MP2-Caravel"
diff --git a/freeciv-web/coredumpon.sh b/freeciv-web/coredumpon.sh
new file mode 100644
index 000000000..9d2bd08fe
--- /dev/null
+++ b/freeciv-web/coredumpon.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+# builds javascript files Freeciv-web and copies the resulting file to tomcat.
+
+ulimit -c unlimited
diff --git a/freeciv-web/pom.xml b/freeciv-web/pom.xml
index d4bd6c582..88111c518 100644
--- a/freeciv-web/pom.xml
+++ b/freeciv-web/pom.xml
@@ -374,6 +374,7 @@ Rerun the sync-js-hand.js script.
libs/*.js2dcanvas/*.js2dcanvas/tilespec-constants.js
+ webgl/*.jswebclient.js
@@ -387,6 +388,24 @@ Rerun the sync-js-hand.js script.
minify
+
+ webgl-minify
+
+ ${project.build.directory}/${project.build.finalName}
+ javascript/webgl/libs
+ javascript
+ webgl-client.js
+
+ *.js
+
+
+ three.min.js
+
+
+
+ minify
+
+ index-minify
diff --git a/freeciv-web/remake-nodebug.sh b/freeciv-web/remake-nodebug.sh
new file mode 100644
index 000000000..b8eeeac17
--- /dev/null
+++ b/freeciv-web/remake-nodebug.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+# Does EVERY step to re-build C-server and restart freeciv-web.
+
+printf "\n**********************************************************************\n"
+printf "./remake.sh: DEPLOYED SERVER version. Re-builds server executables.\n"
+printf "Important: Use ./vremake.sh for vagrant installations.\n"
+printf "**********************************************************************\n"
+
+cd ~/freeciv-web/freeciv && ./prepare_freeciv.sh && cd build && make install && cd ../../scripts && ./stop-freeciv-web.sh && ./start-freeciv-web.sh
+
+# let user know when it's finished
+echo $'\a'
diff --git a/freeciv-web/remake.sh b/freeciv-web/remake.sh
index b8eeeac17..80fceee65 100644
--- a/freeciv-web/remake.sh
+++ b/freeciv-web/remake.sh
@@ -6,6 +6,8 @@ printf "./remake.sh: DEPLOYED SERVER version. Re-builds server executables.\n"
printf "Important: Use ./vremake.sh for vagrant installations.\n"
printf "**********************************************************************\n"
+CFLAGS="-g"
+
cd ~/freeciv-web/freeciv && ./prepare_freeciv.sh && cd build && make install && cd ../../scripts && ./stop-freeciv-web.sh && ./start-freeciv-web.sh
# let user know when it's finished
diff --git a/freeciv-web/scenario-build.sh b/freeciv-web/scenario-build.sh
index 0128f271a..a47348d17 100644
--- a/freeciv-web/scenario-build.sh
+++ b/freeciv-web/scenario-build.sh
@@ -21,7 +21,7 @@ printf "\n**********************************************************************
printf "This refreshes only the SCENARIOS marked for rebuild inside this script.\n"
printf "****************************************************************************\n"
-SCENARIOS=(europe.sav europe-new-positions.sav tutorial.sav)
+SCENARIOS=(europe.sav europe-new-positions.sav tutorial.sav riverland.sav)
TOPDIR="$( cd ../"$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
printf "\nUpdating scenarios...\n"
diff --git a/freeciv-web/src/derived/webapp/images/flags/abkhazia-web.png b/freeciv-web/src/derived/webapp/images/flags/abkhazia-web.png
index 29d360334..c93a68850 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/abkhazia-web.png and b/freeciv-web/src/derived/webapp/images/flags/abkhazia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/aborigines-web.png b/freeciv-web/src/derived/webapp/images/flags/aborigines-web.png
index d97fda348..905f8cd91 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/aborigines-web.png and b/freeciv-web/src/derived/webapp/images/flags/aborigines-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/africa-web.png b/freeciv-web/src/derived/webapp/images/flags/africa-web.png
index 301ef7cd4..81a3644b4 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/africa-web.png and b/freeciv-web/src/derived/webapp/images/flags/africa-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/anishinaabe-web.png b/freeciv-web/src/derived/webapp/images/flags/anishinaabe-web.png
index d4fd1912f..169786259 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/anishinaabe-web.png and b/freeciv-web/src/derived/webapp/images/flags/anishinaabe-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/antarctica-web.png b/freeciv-web/src/derived/webapp/images/flags/antarctica-web.png
index 731efa061..665f2fc3d 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/antarctica-web.png and b/freeciv-web/src/derived/webapp/images/flags/antarctica-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/argentina-web.png b/freeciv-web/src/derived/webapp/images/flags/argentina-web.png
index 302827c34..ac2ab7f10 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/argentina-web.png and b/freeciv-web/src/derived/webapp/images/flags/argentina-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/armenia-web.png b/freeciv-web/src/derived/webapp/images/flags/armenia-web.png
index fa5fefd64..54be8035d 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/armenia-web.png and b/freeciv-web/src/derived/webapp/images/flags/armenia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/assam-web.png b/freeciv-web/src/derived/webapp/images/flags/assam-web.png
index 0c399c3e3..ba1b70e08 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/assam-web.png and b/freeciv-web/src/derived/webapp/images/flags/assam-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/assyria-web.png b/freeciv-web/src/derived/webapp/images/flags/assyria-web.png
index 305dcbccb..fc89d947d 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/assyria-web.png and b/freeciv-web/src/derived/webapp/images/flags/assyria-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/australia-web.png b/freeciv-web/src/derived/webapp/images/flags/australia-web.png
index 87e9dea64..5ff9d474c 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/australia-web.png and b/freeciv-web/src/derived/webapp/images/flags/australia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/azerbaijan-web.png b/freeciv-web/src/derived/webapp/images/flags/azerbaijan-web.png
index cfc747b35..03177b158 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/azerbaijan-web.png and b/freeciv-web/src/derived/webapp/images/flags/azerbaijan-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/babylon-web.png b/freeciv-web/src/derived/webapp/images/flags/babylon-web.png
index 8740b921c..026f6156d 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/babylon-web.png and b/freeciv-web/src/derived/webapp/images/flags/babylon-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/bahamas-web.png b/freeciv-web/src/derived/webapp/images/flags/bahamas-web.png
index 0d80055ef..95805a4d0 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/bahamas-web.png and b/freeciv-web/src/derived/webapp/images/flags/bahamas-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/bangladesh-web.png b/freeciv-web/src/derived/webapp/images/flags/bangladesh-web.png
index 6a474b210..91b60bba9 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/bangladesh-web.png and b/freeciv-web/src/derived/webapp/images/flags/bangladesh-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/barbados-web.png b/freeciv-web/src/derived/webapp/images/flags/barbados-web.png
index 5ecb86575..eb4333949 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/barbados-web.png and b/freeciv-web/src/derived/webapp/images/flags/barbados-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/belarus-web.png b/freeciv-web/src/derived/webapp/images/flags/belarus-web.png
index f96707d34..971ce1551 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/belarus-web.png and b/freeciv-web/src/derived/webapp/images/flags/belarus-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/boii-web.png b/freeciv-web/src/derived/webapp/images/flags/boii-web.png
index 299ee94bb..d97432f84 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/boii-web.png and b/freeciv-web/src/derived/webapp/images/flags/boii-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/bosnia-web.png b/freeciv-web/src/derived/webapp/images/flags/bosnia-web.png
index 65a8a7f6c..a2c2c8821 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/bosnia-web.png and b/freeciv-web/src/derived/webapp/images/flags/bosnia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/brasil-web.png b/freeciv-web/src/derived/webapp/images/flags/brasil-web.png
index 9e7733894..0728dbb3b 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/brasil-web.png and b/freeciv-web/src/derived/webapp/images/flags/brasil-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/britannia-web.png b/freeciv-web/src/derived/webapp/images/flags/britannia-web.png
index e970fc4dd..96021f8bc 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/britannia-web.png and b/freeciv-web/src/derived/webapp/images/flags/britannia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/brunei-web.png b/freeciv-web/src/derived/webapp/images/flags/brunei-web.png
index bc1342d85..94f2adc4b 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/brunei-web.png and b/freeciv-web/src/derived/webapp/images/flags/brunei-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/canada-web.png b/freeciv-web/src/derived/webapp/images/flags/canada-web.png
index 6da64c2f0..7c0383873 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/canada-web.png and b/freeciv-web/src/derived/webapp/images/flags/canada-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/carantanian-web.png b/freeciv-web/src/derived/webapp/images/flags/carantanian-web.png
index cf3a51897..fa6609871 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/carantanian-web.png and b/freeciv-web/src/derived/webapp/images/flags/carantanian-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/corsica-web.png b/freeciv-web/src/derived/webapp/images/flags/corsica-web.png
index 8a891d307..d39ca8986 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/corsica-web.png and b/freeciv-web/src/derived/webapp/images/flags/corsica-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/crimean_tatar-web.png b/freeciv-web/src/derived/webapp/images/flags/crimean_tatar-web.png
index a43b4a825..03ccda73c 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/crimean_tatar-web.png and b/freeciv-web/src/derived/webapp/images/flags/crimean_tatar-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/croatia-web.png b/freeciv-web/src/derived/webapp/images/flags/croatia-web.png
index c3e07bd4a..9dd43b2ba 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/croatia-web.png and b/freeciv-web/src/derived/webapp/images/flags/croatia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/cuba-web.png b/freeciv-web/src/derived/webapp/images/flags/cuba-web.png
index 1446f357d..e48f2ff2e 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/cuba-web.png and b/freeciv-web/src/derived/webapp/images/flags/cuba-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/czech-web.png b/freeciv-web/src/derived/webapp/images/flags/czech-web.png
index 236fe6f65..5843cfdd9 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/czech-web.png and b/freeciv-web/src/derived/webapp/images/flags/czech-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/czechoslovakia-web.png b/freeciv-web/src/derived/webapp/images/flags/czechoslovakia-web.png
index 339fef3ec..c68aab905 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/czechoslovakia-web.png and b/freeciv-web/src/derived/webapp/images/flags/czechoslovakia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/dacian-web.png b/freeciv-web/src/derived/webapp/images/flags/dacian-web.png
index 6b5412d8e..d8112afd8 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/dacian-web.png and b/freeciv-web/src/derived/webapp/images/flags/dacian-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/dominica-web.png b/freeciv-web/src/derived/webapp/images/flags/dominica-web.png
index 08c6f19c9..8e2bb22db 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/dominica-web.png and b/freeciv-web/src/derived/webapp/images/flags/dominica-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/dryad-web.png b/freeciv-web/src/derived/webapp/images/flags/dryad-web.png
index 830525119..d9d8d86fd 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/dryad-web.png and b/freeciv-web/src/derived/webapp/images/flags/dryad-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/egypt_ancient-web.png b/freeciv-web/src/derived/webapp/images/flags/egypt_ancient-web.png
index e1a62a62d..074ef2db6 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/egypt_ancient-web.png and b/freeciv-web/src/derived/webapp/images/flags/egypt_ancient-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/eritrea-web.png b/freeciv-web/src/derived/webapp/images/flags/eritrea-web.png
index 3bc68b71a..9f524c0e0 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/eritrea-web.png and b/freeciv-web/src/derived/webapp/images/flags/eritrea-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/ethiopia-web.png b/freeciv-web/src/derived/webapp/images/flags/ethiopia-web.png
index a7001cd32..086514be8 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/ethiopia-web.png and b/freeciv-web/src/derived/webapp/images/flags/ethiopia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/etruscan-web.png b/freeciv-web/src/derived/webapp/images/flags/etruscan-web.png
index 29cb25872..e306cdb39 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/etruscan-web.png and b/freeciv-web/src/derived/webapp/images/flags/etruscan-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/europe-web.png b/freeciv-web/src/derived/webapp/images/flags/europe-web.png
index 4b3391e40..9043e0741 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/europe-web.png and b/freeciv-web/src/derived/webapp/images/flags/europe-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/fiji-web.png b/freeciv-web/src/derived/webapp/images/flags/fiji-web.png
index 3df2b5049..7377ede05 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/fiji-web.png and b/freeciv-web/src/derived/webapp/images/flags/fiji-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/finland-web.png b/freeciv-web/src/derived/webapp/images/flags/finland-web.png
index 1a0abff80..3a17c425e 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/finland-web.png and b/freeciv-web/src/derived/webapp/images/flags/finland-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/florence-web.png b/freeciv-web/src/derived/webapp/images/flags/florence-web.png
index 35ff0c2d3..0179ba695 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/florence-web.png and b/freeciv-web/src/derived/webapp/images/flags/florence-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/france-web.png b/freeciv-web/src/derived/webapp/images/flags/france-web.png
index b42b43f92..41e19c2e9 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/france-web.png and b/freeciv-web/src/derived/webapp/images/flags/france-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/gaul-web.png b/freeciv-web/src/derived/webapp/images/flags/gaul-web.png
index 2daf3f9e3..35ae8f62a 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/gaul-web.png and b/freeciv-web/src/derived/webapp/images/flags/gaul-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/germanic-web.png b/freeciv-web/src/derived/webapp/images/flags/germanic-web.png
index 8fabcdf03..5d2bad500 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/germanic-web.png and b/freeciv-web/src/derived/webapp/images/flags/germanic-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/germany-web.png b/freeciv-web/src/derived/webapp/images/flags/germany-web.png
index e0b777025..f8572d828 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/germany-web.png and b/freeciv-web/src/derived/webapp/images/flags/germany-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/ghaznavid-web.png b/freeciv-web/src/derived/webapp/images/flags/ghaznavid-web.png
index 8f4e57aa4..d2344a595 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/ghaznavid-web.png and b/freeciv-web/src/derived/webapp/images/flags/ghaznavid-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/gokturk-web.png b/freeciv-web/src/derived/webapp/images/flags/gokturk-web.png
index 3a258994c..9582b2565 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/gokturk-web.png and b/freeciv-web/src/derived/webapp/images/flags/gokturk-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/golden_horde-web.png b/freeciv-web/src/derived/webapp/images/flags/golden_horde-web.png
index e3c992ee0..4d4998a33 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/golden_horde-web.png and b/freeciv-web/src/derived/webapp/images/flags/golden_horde-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/guatemala-web.png b/freeciv-web/src/derived/webapp/images/flags/guatemala-web.png
index 1a5e81b32..d29b15c8a 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/guatemala-web.png and b/freeciv-web/src/derived/webapp/images/flags/guatemala-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/guinea-bissau-web.png b/freeciv-web/src/derived/webapp/images/flags/guinea-bissau-web.png
index 48474aa0f..7d96bb49f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/guinea-bissau-web.png and b/freeciv-web/src/derived/webapp/images/flags/guinea-bissau-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/hawaii-web.png b/freeciv-web/src/derived/webapp/images/flags/hawaii-web.png
index f52209132..e07082e90 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/hawaii-web.png and b/freeciv-web/src/derived/webapp/images/flags/hawaii-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/honduras-web.png b/freeciv-web/src/derived/webapp/images/flags/honduras-web.png
index d4558cee2..3a1ca001b 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/honduras-web.png and b/freeciv-web/src/derived/webapp/images/flags/honduras-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/hungary-web.png b/freeciv-web/src/derived/webapp/images/flags/hungary-web.png
index a379b7f95..31df9b512 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/hungary-web.png and b/freeciv-web/src/derived/webapp/images/flags/hungary-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/iceland-web.png b/freeciv-web/src/derived/webapp/images/flags/iceland-web.png
index ecc940f4e..cf682050d 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/iceland-web.png and b/freeciv-web/src/derived/webapp/images/flags/iceland-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/illyria-web.png b/freeciv-web/src/derived/webapp/images/flags/illyria-web.png
index e04410dc0..4337c32bb 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/illyria-web.png and b/freeciv-web/src/derived/webapp/images/flags/illyria-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/indoeuropean-web.png b/freeciv-web/src/derived/webapp/images/flags/indoeuropean-web.png
index 3227d78f4..2a9658bc8 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/indoeuropean-web.png and b/freeciv-web/src/derived/webapp/images/flags/indoeuropean-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/ireland-web.png b/freeciv-web/src/derived/webapp/images/flags/ireland-web.png
index 499c6b0f5..f550fe109 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/ireland-web.png and b/freeciv-web/src/derived/webapp/images/flags/ireland-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/israel-web.png b/freeciv-web/src/derived/webapp/images/flags/israel-web.png
index 643d032e6..ab773e87f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/israel-web.png and b/freeciv-web/src/derived/webapp/images/flags/israel-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/israel_ancient-web.png b/freeciv-web/src/derived/webapp/images/flags/israel_ancient-web.png
index e35bedf13..f25bcc87f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/israel_ancient-web.png and b/freeciv-web/src/derived/webapp/images/flags/israel_ancient-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/jamaica-web.png b/freeciv-web/src/derived/webapp/images/flags/jamaica-web.png
index 94aef3268..b994aef6d 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/jamaica-web.png and b/freeciv-web/src/derived/webapp/images/flags/jamaica-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/japan-web.png b/freeciv-web/src/derived/webapp/images/flags/japan-web.png
index 781462b5d..99b7ad28f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/japan-web.png and b/freeciv-web/src/derived/webapp/images/flags/japan-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/jordan-web.png b/freeciv-web/src/derived/webapp/images/flags/jordan-web.png
index 9ab41270f..123263e00 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/jordan-web.png and b/freeciv-web/src/derived/webapp/images/flags/jordan-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/kazakhstan-web.png b/freeciv-web/src/derived/webapp/images/flags/kazakhstan-web.png
index 02f387087..f06b0f07a 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/kazakhstan-web.png and b/freeciv-web/src/derived/webapp/images/flags/kazakhstan-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/khmer-web.png b/freeciv-web/src/derived/webapp/images/flags/khmer-web.png
index 18608c977..7e634e7d5 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/khmer-web.png and b/freeciv-web/src/derived/webapp/images/flags/khmer-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/kiribati-web.png b/freeciv-web/src/derived/webapp/images/flags/kiribati-web.png
index 88f0d3b3f..1b9575d9e 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/kiribati-web.png and b/freeciv-web/src/derived/webapp/images/flags/kiribati-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/kurd-web.png b/freeciv-web/src/derived/webapp/images/flags/kurd-web.png
index 4a4fd16d9..32d5ff74f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/kurd-web.png and b/freeciv-web/src/derived/webapp/images/flags/kurd-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/kuwait-web.png b/freeciv-web/src/derived/webapp/images/flags/kuwait-web.png
index d1313f66b..b0479ffe7 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/kuwait-web.png and b/freeciv-web/src/derived/webapp/images/flags/kuwait-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/latvia-web.png b/freeciv-web/src/derived/webapp/images/flags/latvia-web.png
index d4d96879d..3f51c37c4 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/latvia-web.png and b/freeciv-web/src/derived/webapp/images/flags/latvia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/lebanon-web.png b/freeciv-web/src/derived/webapp/images/flags/lebanon-web.png
index 4c3a3ab97..a777989f3 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/lebanon-web.png and b/freeciv-web/src/derived/webapp/images/flags/lebanon-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/libya-web.png b/freeciv-web/src/derived/webapp/images/flags/libya-web.png
index 9cb4d7731..d538a3de7 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/libya-web.png and b/freeciv-web/src/derived/webapp/images/flags/libya-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/libya_old-web.png b/freeciv-web/src/derived/webapp/images/flags/libya_old-web.png
index b5a1ebab6..29aefb0b1 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/libya_old-web.png and b/freeciv-web/src/derived/webapp/images/flags/libya_old-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/macedonia-web.png b/freeciv-web/src/derived/webapp/images/flags/macedonia-web.png
index e644da5bc..bfc0b6c16 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/macedonia-web.png and b/freeciv-web/src/derived/webapp/images/flags/macedonia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/malaysia-web.png b/freeciv-web/src/derived/webapp/images/flags/malaysia-web.png
index 603ed14bc..7f3e624ea 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/malaysia-web.png and b/freeciv-web/src/derived/webapp/images/flags/malaysia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/man-web.png b/freeciv-web/src/derived/webapp/images/flags/man-web.png
index 8cf22089b..ff9e3f78e 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/man-web.png and b/freeciv-web/src/derived/webapp/images/flags/man-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/messapian-web.png b/freeciv-web/src/derived/webapp/images/flags/messapian-web.png
index fb53648da..03fee3b2d 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/messapian-web.png and b/freeciv-web/src/derived/webapp/images/flags/messapian-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/moldova-web.png b/freeciv-web/src/derived/webapp/images/flags/moldova-web.png
index 5aa495871..cfbc3ae8c 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/moldova-web.png and b/freeciv-web/src/derived/webapp/images/flags/moldova-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/moluccas-web.png b/freeciv-web/src/derived/webapp/images/flags/moluccas-web.png
index 6f39b76dc..867601f62 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/moluccas-web.png and b/freeciv-web/src/derived/webapp/images/flags/moluccas-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/monaco_alternative-web.png b/freeciv-web/src/derived/webapp/images/flags/monaco_alternative-web.png
index 54b08e0c6..b380f25e1 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/monaco_alternative-web.png and b/freeciv-web/src/derived/webapp/images/flags/monaco_alternative-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/mongolia-web.png b/freeciv-web/src/derived/webapp/images/flags/mongolia-web.png
index b84416628..31f64821f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/mongolia-web.png and b/freeciv-web/src/derived/webapp/images/flags/mongolia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/montenegro-web.png b/freeciv-web/src/derived/webapp/images/flags/montenegro-web.png
index 9c72761fe..8971445a5 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/montenegro-web.png and b/freeciv-web/src/derived/webapp/images/flags/montenegro-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/nauru-web.png b/freeciv-web/src/derived/webapp/images/flags/nauru-web.png
index 360e7f0f1..36ac3d41b 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/nauru-web.png and b/freeciv-web/src/derived/webapp/images/flags/nauru-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/nenetsia-web.png b/freeciv-web/src/derived/webapp/images/flags/nenetsia-web.png
index 6e184b3bc..3b8f81ba5 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/nenetsia-web.png and b/freeciv-web/src/derived/webapp/images/flags/nenetsia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/newzealand-web.png b/freeciv-web/src/derived/webapp/images/flags/newzealand-web.png
index 133e3025e..e90ab85e0 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/newzealand-web.png and b/freeciv-web/src/derived/webapp/images/flags/newzealand-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/nigeria-web.png b/freeciv-web/src/derived/webapp/images/flags/nigeria-web.png
index 726947d36..205229922 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/nigeria-web.png and b/freeciv-web/src/derived/webapp/images/flags/nigeria-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/north_korea-web.png b/freeciv-web/src/derived/webapp/images/flags/north_korea-web.png
index a5597b5d4..cb0b8106f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/north_korea-web.png and b/freeciv-web/src/derived/webapp/images/flags/north_korea-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/norway-web.png b/freeciv-web/src/derived/webapp/images/flags/norway-web.png
index 042f37b55..3628327a4 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/norway-web.png and b/freeciv-web/src/derived/webapp/images/flags/norway-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/numidia-web.png b/freeciv-web/src/derived/webapp/images/flags/numidia-web.png
index 6be5ac2db..9cdb5cf3f 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/numidia-web.png and b/freeciv-web/src/derived/webapp/images/flags/numidia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/ohlone-web.png b/freeciv-web/src/derived/webapp/images/flags/ohlone-web.png
index 7173f9c2d..e1cdca56e 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/ohlone-web.png and b/freeciv-web/src/derived/webapp/images/flags/ohlone-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/oldenburg-web.png b/freeciv-web/src/derived/webapp/images/flags/oldenburg-web.png
index 0576a7552..aafb1e1f8 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/oldenburg-web.png and b/freeciv-web/src/derived/webapp/images/flags/oldenburg-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/palmyra-web.png b/freeciv-web/src/derived/webapp/images/flags/palmyra-web.png
index b99d02a0f..521f90c75 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/palmyra-web.png and b/freeciv-web/src/derived/webapp/images/flags/palmyra-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/philippines-web.png b/freeciv-web/src/derived/webapp/images/flags/philippines-web.png
index 915e1574a..592354086 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/philippines-web.png and b/freeciv-web/src/derived/webapp/images/flags/philippines-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/poland-web.png b/freeciv-web/src/derived/webapp/images/flags/poland-web.png
index 0355098e3..b7a84cdf8 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/poland-web.png and b/freeciv-web/src/derived/webapp/images/flags/poland-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/russia-web.png b/freeciv-web/src/derived/webapp/images/flags/russia-web.png
index 10b11e169..30a2d0fcb 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/russia-web.png and b/freeciv-web/src/derived/webapp/images/flags/russia-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/scotland-web.png b/freeciv-web/src/derived/webapp/images/flags/scotland-web.png
index 972277622..f36160d4b 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/scotland-web.png and b/freeciv-web/src/derived/webapp/images/flags/scotland-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/scottishgaelic-web.png b/freeciv-web/src/derived/webapp/images/flags/scottishgaelic-web.png
index d6ebf7c6a..42eb31b60 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/scottishgaelic-web.png and b/freeciv-web/src/derived/webapp/images/flags/scottishgaelic-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/seleucid-web.png b/freeciv-web/src/derived/webapp/images/flags/seleucid-web.png
index 0d4f7f463..c12b8dcea 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/seleucid-web.png and b/freeciv-web/src/derived/webapp/images/flags/seleucid-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/spartan-web.png b/freeciv-web/src/derived/webapp/images/flags/spartan-web.png
index 99820de48..26dc618dc 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/spartan-web.png and b/freeciv-web/src/derived/webapp/images/flags/spartan-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/sweden-web.png b/freeciv-web/src/derived/webapp/images/flags/sweden-web.png
index 3dedfdc78..70cb574c5 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/sweden-web.png and b/freeciv-web/src/derived/webapp/images/flags/sweden-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/switzerland-web.png b/freeciv-web/src/derived/webapp/images/flags/switzerland-web.png
index 578cfba95..289c45139 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/switzerland-web.png and b/freeciv-web/src/derived/webapp/images/flags/switzerland-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/templar-web.png b/freeciv-web/src/derived/webapp/images/flags/templar-web.png
index ce9a026e8..f69d9397b 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/templar-web.png and b/freeciv-web/src/derived/webapp/images/flags/templar-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/tuareg-web.png b/freeciv-web/src/derived/webapp/images/flags/tuareg-web.png
index e307e7dbe..9d5778e01 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/tuareg-web.png and b/freeciv-web/src/derived/webapp/images/flags/tuareg-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/vandal-web.png b/freeciv-web/src/derived/webapp/images/flags/vandal-web.png
index 3e31bc178..44021995b 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/vandal-web.png and b/freeciv-web/src/derived/webapp/images/flags/vandal-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/visigoth-web.png b/freeciv-web/src/derived/webapp/images/flags/visigoth-web.png
index 88233d24c..f57f6cb86 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/visigoth-web.png and b/freeciv-web/src/derived/webapp/images/flags/visigoth-web.png differ
diff --git a/freeciv-web/src/derived/webapp/images/flags/xiongnu-web.png b/freeciv-web/src/derived/webapp/images/flags/xiongnu-web.png
index 3031cdf84..11d2ca909 100644
Binary files a/freeciv-web/src/derived/webapp/images/flags/xiongnu-web.png and b/freeciv-web/src/derived/webapp/images/flags/xiongnu-web.png differ
diff --git a/freeciv-web/src/derived/webapp/man/mp2-caravel6.bak.html b/freeciv-web/src/derived/webapp/man/mp2-caravel6.bak.html
index 28706379d..8b23ab2c2 100644
--- a/freeciv-web/src/derived/webapp/man/mp2-caravel6.bak.html
+++ b/freeciv-web/src/derived/webapp/man/mp2-caravel6.bak.html
@@ -1,4 +1,8 @@
-
+
+
+
+
+
@@ -11,11 +15,47 @@
Anarchy
Anarchy offers slightly less corruption than Despotism, but slightly more unhappiness.
-
+Celebrating cities cannot rapture.
No citizens are made unhappy by each aggressive unit.
25% Base corruption in cities
2% Extra corruption per each tile in distance from capital
+
+City Happiness vs. Empire Size
+
+
Cities:
+
0-9
+
10-15
+
16-21
+
22-27
+
28-33
+
34-39
+
40-45
+
46-51
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
Features:
• Trade production will suffer some losses.
@@ -39,10 +79,48 @@
Despotism
Despotism suffers the highest level of corruption of all forms of government.
+Celebrating cities cannot rapture.
No citizens are made unhappy by each aggressive unit.
37% Base corruption in cities
4% Extra corruption per each tile in distance from capital
-Gulag:You can starve cities without disorder, with 2 martial law units.
+Gulag:You can starve cities without disorder, with 2 martial law units or a Police Station.
+
+
+City Happiness vs. Empire Size
+
+
Cities:
+
0-10
+
11-20
+
21-30
+
31-40
+
41-50
+
51-60
+
61-70
+
71-80
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
Features:
• Trade production will suffer massive losses.
@@ -65,13 +143,49 @@
Monarchy
Monarchy suffers the same small amount of corruption that the Republic does.
-
+Celebrating cities cannot rapture.
No citizens are made unhappy by each aggressive unit.
15% Base corruption in cities
2% Extra corruption per each tile in distance from capital
Requires knowledge of the technology Monarchy.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-11
+
12-23
+
24-35
+
36-47
+
48-59
+
60-71
+
72-83
+
84-95
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
Features:
• Trade production will suffer some losses.
• Trade losses will increase with distance from capital.
@@ -96,6 +210,42 @@
+City Happiness vs. Empire Size
+
Cities:
+
0-23
+
24-35
+
36-47
+
48-59
+
60-71
+
72-83
+
84-95
+
96-107
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
Features:
• Trade production will suffer some losses.
• Trade losses will increase with distance from capital.
@@ -131,6 +281,42 @@
Republic
Requires knowledge of the technology The Republic.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-13
+
14-27
+
28-41
+
42-55
+
56-69
+
70-83
+
84-97
+
98-111
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
Features:
• Trade production will suffer some losses.
• Trade losses will increase with distance from capital.
@@ -161,6 +347,42 @@
Democracy
Requires knowledge of the technology Democracy.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-14
+
15-30
+
31-46
+
47-62
+
63-78
+
79-94
+
95-110
+
111-126
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
Features:
• Trade production will suffer a small amount of losses.
• Trade losses will increase slowly with distance from capital.
@@ -186,8 +408,9 @@
Theocracy
The people are devoted and often willing to die for their faith. Cities very near a capital have very low corruption. Cost for enemies to incite cities or bribe units is 2x, but Zealots cannot be bribed. Enemies cannot establish embassies without first making Ceasefire or Peace.
-Improvements that appease unhappy citizens produce 1 gold in tithes for each citizen appeased. Tithes also boost gold income by +20%, but science output suffers -20%. Palace gives +50% to gold income in its city. In cities, two military units may impose martial law, affecting one citizen each. Pilgrims can be made to migrate and grow cities.
+Improvements that appease unhappy citizens add +1 base gold in tithes for each citizen appeased. Tithes also boost gold income by +20%, but science output suffers -20%. Palace gives +50% to gold income in its city. In cities, two military units may impose martial law, affecting one citizen each. Pilgrims can be made to migrate and grow cities.
+Having an Ecclesiastical Palace allows instant and orderly transition to this government.
No citizens are made unhappy by each aggressive unit.
2% Base corruption in cities
@@ -195,6 +418,42 @@
Theocracy
Requires knowledge of the technology Theocracy.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-14
+
15-29
+
30-44
+
45-59
+
60-74
+
75-89
+
90-104
+
105-119
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
Features:
• Trade production will suffer a small amount of losses.
• Trade losses will increase with distance from capital.
@@ -236,6 +495,42 @@
Communism
Requires knowledge of the technology Communism.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-12
+
13-∞
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
First Unhappy:
+
5
+
4
+
4
+
4
+
4
+
4
+
4
+
4
+
+
+
Extra Unhappy:
+
0
+
+1
+
+1
+
+1
+
+1
+
+1
+
+1
+
+1
+
+
+
Features:
• Trade production will suffer some losses.
• Each of your cities will avoid paying 4 Shield upkeep for your units.
@@ -244,7 +539,7 @@
Communism
• The maximum rate you can set for science, gold, or luxuries is 80%.
• Your units may impose martial law. Each military unit inside a city will force 2 unhappy citizens to become content.
• A maximum of 3 units in each city can enforce martial law.
-• New Diplomat units will be veteran.
+• New Diplomat and Spy units will be veteran.
• Each worked tile with Trade will yield +1 more Trade while its city is celebrating. (Cities below size 3 will not celebrate.)
• Cathedral and Michelangelo effects diminished by -1.
• Disqualified from all bonuses to Coinage.
@@ -256,7 +551,7 @@
Communism
Nationalism
-
Nationalism glorifies national supremacy over other peoples. Zealous citizens and corporatations align under a dictator who rules over a unified populace, military, and isolationist economy.
+
Nationalism glorifies national supremacy over other peoples. Citizens and corporatations align under a dictator who rules over a unified populace, military, and isolationist economy.
Content cities get a +1 trade bonus on Land tiles only. Celebrating cities also get Ocean tiles. Cities may rapture if founded under the original national hegemony. Luxury suffers a 15% penalty. Government mandated Science gets +15% in original cities.
@@ -274,6 +569,42 @@
Nationalism
Requires knowledge of the technology Nationalism.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-20
+
21-32
+
33-44
+
45-56
+
57-68
+
69-80
+
81-92
+
93-104
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
Features:
• Trade production will suffer a small amount of losses.
• Trade losses will increase with distance from capital.
@@ -288,7 +619,7 @@
Nationalism
* New Riflemen units will have the rank of veteran.
* Fighter and Dive Bomber units are promoted 50% more frequently.
• Luxury production is decreased 15%.
-• Science production is increased 15% in original cities.
+• Science production is increased 20% in original cities.
• Cities originally founded by you can grow by means of rapture. (Cities below size 3 cannot grow in this way.)
• +1 trade on each Land tile that produces trade.
• Each worked Sea tile yields +1 more Trade while its city is celebrating. (Cities below size 3 will not celebrate.)
diff --git a/freeciv-web/src/derived/webapp/man/mp2-caravel7.bak.html b/freeciv-web/src/derived/webapp/man/mp2-caravel7.bak.html
index 081ffed34..6b6c4cb14 100644
--- a/freeciv-web/src/derived/webapp/man/mp2-caravel7.bak.html
+++ b/freeciv-web/src/derived/webapp/man/mp2-caravel7.bak.html
@@ -16,9 +16,9 @@
Founders
Firepower: 1
Hitpoints: 25
Obsolete by: None
-
At game start, Founders are a more populous portion of your nomadic tribe.
+
At game start, Founders are a more populous portion of your nomadic tribe.
-Founders are Settlers who found a city of size 2—usually the first city and capital of a tribe. They are a start unit that cannot be built. In all other respects they are exactly like Settlers, except they cannot be bribed.
+Founders are Settlers who found a city of size 2—usually the first city and capital of a tribe. They are a start unit that cannot be built. In all other respects they are exactly like Settlers, except they cannot be bribed.➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
@@ -29,16 +29,16 @@
Founders
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, and Naval Base on tiles.
➤ Can clean Pollution from tiles.
➤ Can clean Fallout from tiles.
➤ Can Build City
- • uses up the Settlers.
- • initial population: 1.
+ • uses up the Founders.
+ • initial population: 2.
➤ Can Add to City
- • uses up the Settlers.
+ • uses up the Founders.
• max target size: 39.
• adds 1 population.
➤ Can Pillage
@@ -70,11 +70,11 @@
Settlers
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Population Cost: 1. Adds Population: 1.
+
Population Cost: 1. Adds Population: 1.
Settlers are your only means of founding new cities.
-Settlers can perform most of the terrain alterations as Workers (but cannot build Airbases or Buoys).
-Upkeep for Settlers costs food as well as production. A Settler can die if its supporting city runs out of food. Settlers in a Republic, Democracy, or Theocratic nation require twice as much food per turn.
+Settlers can perform most of the terrain alterations as Workers (but cannot build Airbases or Buoys).
+Upkeep for Settlers costs food as well as production. A Settler can die if its supporting city runs out of food. Settlers in a Republic, Democracy, or Theocratic nation require twice as much food per turn.➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
@@ -85,7 +85,7 @@
Settlers
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, and Naval Base on tiles.
➤ Can clean Pollution from tiles.
@@ -124,7 +124,7 @@
Tribesmen
Firepower: 1
Hitpoints: 8
Obsolete by: None
-
Tribesmen came before cities and specialized division of labor. During ancient times, they get primitive versatility bonuses:
+
Tribesmen came before cities and specialized division of labor. During ancient times, they get primitive versatility bonuses:
Tribesmen can explore, moving over terrain with no penalty. They don’t obey or exert ZOC. They can build roads, irrigate, or mine at half the rate of Workers.
Tribesmen can make basic diplomatic contact and freely Investigate cities. They can carry Goods and enter nations with whom you are at Peace. They can do primitive combat—they can’t conquer cities and only attack for 10 combat rounds.
Tribesmen help build nations by recycling into city production with a 2× bonus, donating their life-work of 20 shields into any output EXCEPT units. ** Changing production targets loses their work! **
@@ -139,7 +139,7 @@
Tribesmen
➤ Can build infrastructure until T20 (2000BC).
• Can Road, 2 turns. (4-6 turns for slower roaded terrain.)
• Can Mine, 10 turns.
- • Can Irrigate, 6 turns.
+ • Can Irrigate, 5 turns.
➤ Ignores terrain effects (moving costs at most ⅓ MP per tile).
➤ Never imposes a zone of control.
➤ Not subject to zones of control imposed by other units.
@@ -151,7 +151,7 @@
Tribesmen
• Can't conquer cities.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Investigate City (does not spend the unit)
➤ Can Recycle
• Must be done in a domestic city making a Building, Wonder, or Coinage.
@@ -186,7 +186,7 @@
Well-Digger
Firepower: 1
Hitpoints: 8
Obsolete by: Workers
-
This unit can fix unlucky starts, but has very high upkeep.
+
This unit can fix unlucky starts, but has very high upkeep.
If you have no water, make a Well-Digger in a city that can support it, create a water source, then disband the unit.
This unit can dig wells to create irrigation sources. It will cost its home city -2 Food -2 Prod. This unit WILL NOT WORK: • Outside your borders, • After Alphabet or Pottery, • After any player discovers Writing, • After it reaches an age of 10 turns. >> Don't fool around: high upkeep will permanently hinder you. Only make this unit if you lack water when the game starts!➤ Belongs to Land unit class.
@@ -198,7 +198,7 @@
Well-Digger
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Dig Well or Irrigate tiles with no water.
➤ Can clean Pollution from tiles.
➤ Can clean Fallout from tiles.
@@ -232,11 +232,11 @@
Proletarians
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Population Cost: 1. Adds Population: 2.
+
Population Cost: 1. Adds Population: 2.
Proletarians can only be controlled by Communist governments.
-Communist States can assign Proletarians from one city to another, which transfers population from one to the other. They can also be ordered to the same tasks as Workers. Proletarians are extremely valuable as Workers for a third of the price. The disadvantage is that they cost one population to make. However, this is compensated by the fact that when they join a city, they add two to the population!
-Large numbers of Proletarians can be used to rapidly complete important State projects, which you may organize into Five-turn Plans.
+Communist States can assign Proletarians from one city to another, which transfers population from one to the other. They can also be ordered to the same tasks as Workers. Proletarians are extremely valuable as Workers for a third of the price. The disadvantage is that they cost one population to make. However, this is compensated by the fact that when they join a city, they add two to the population!
+Large numbers of Proletarians can be used to rapidly complete important State projects, which you may organize into Five-turn Plans.➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
@@ -250,7 +250,7 @@
Proletarians
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, Naval Base, Airbase, Radar, and Buoy on tiles.
➤ Can clean Pollution from tiles.
@@ -285,7 +285,7 @@
Pilgrims
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Population Cost: 0. Adds Population: 1.
+
Population Cost: 0. Adds Population: 1.
Pilgrims are wayfaring migrants from Theocratic nations. Like Zealots, they have no upkeep if under a Theocratic government. Heeding the call of ecclesiastic authority, Pilgrims can be guided from one city to another in order to transfer population from city to city. Pilgrims have no population cost, so can be thought of as rapture for the price of 10 shields.➤ Belongs to Land unit class.
@@ -300,7 +300,7 @@
Pilgrims
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Add to City
• is done to domestic individual cities.
• uses up the Pilgrims.
@@ -343,7 +343,7 @@
Migrants
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Add to City
• is done to domestic individual cities.
• uses up the Migrants.
@@ -371,10 +371,10 @@
Workers
Firepower: 1
Hitpoints: 8
Obsolete by: Engineers
-
Workers can improve terrain tiles. See the manual on Terrain for details.
-Masonry lets Workers build Forts. Construction lets them build Fortresses and Oil Wells. Engineering lets them build Canals and Naval Bases.
+
Workers can improve terrain tiles. See the manual on Terrain for details.
+Masonry lets Workers build Forts. Construction lets them build Fortresses and Oil Wells. Engineering lets them build Canals and Naval Bases.
Feudalism with Construction lets them build Castles. Steel lets them build Bunkers and Sea Bridges. Radio lets them build Airbases and Buoys, which Settlers cannot. Workers must be on a ship to build Buoys.
-Communism tech allows Communist governments to conscript Workers into Riflemen via the Convert order. And vice versa.
+Communism tech allows Communist governments to conscript Workers into Riflemen via the Convert order. And vice versa.➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
@@ -386,7 +386,7 @@
Workers
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, Naval Base, Airbase, Radar, and Buoy on tiles.
➤ Can clean Pollution from tiles.
@@ -427,7 +427,7 @@
Engineers
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Engineers can do everything Workers can do, at twice the speed. Unlike Workers, Engineers can Transform, such as converting Desert to Plains. Converting Ocean to Swamp requires being on a ship on a tile bordering 2 land tiles.
+
Engineers can do everything Workers can do, at twice the speed. Unlike Workers, Engineers can Transform, such as converting Desert to Plains. Converting Ocean to Swamp requires being on a ship on a tile bordering 2 land tiles. Constructing a Sea Bridge requires being on a ship on a tile cardinally adjacent to land.➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
@@ -438,7 +438,7 @@
Engineers
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, Naval Base, Airbase, Radar, and Buoy on tiles.
➤ Can clean Pollution from tiles.
@@ -465,10 +465,10 @@
Warriors
Firepower: 1
Hitpoints: 10
Obsolete by: Pikemen
-
The Warrior is the weakest military unit, but can also be very cost effective.
-
+
The Warrior is the weakest military unit, but can also be very cost effective.
+
With the discovery of Conscription, Warriors can convert to Musketeers in any domestic city.
-
+
The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
@@ -478,6 +478,7 @@
Warriors
• Can't attack as Cargo. Must first unload.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can Upgrade
@@ -522,11 +523,11 @@
Phalanx
Firepower: 1
Hitpoints: 10
Obsolete by: Pikemen
-
The Phalanx is armored infantry, excellent for city defense and holding tactical positions. Phalanxes can do standard attacks and Rumble attacks while remaining fortified.
-
-If a Phalanx hasn’t moved, it can Rumble Attack adjacent enemies. Rumbles are disciplined skirmish—not mortal combat. The formation gives the Phalanx little or no vulnerability. The trade-off is that the Phalanx can only thrust at a single target for 3 combat rounds. Rumble attacks are used to unsettle enemies: they can discourage fortifying into tactical positions, or weaken them prior to attacks from other units. The action uses ⁵⁄₉ of a move point and doesn’t alter the fortified status of the Phalanx.
+
The Phalanx is armored infantry, excellent for city defense and holding tactical positions. Phalanxes can do standard attacks and Rumble attacks while remaining fortified.
+
+If a Phalanx hasn’t moved, it can Rumble Attack adjacent enemies. Rumbles are disciplined skirmish—not mortal combat. The formation gives the Phalanx little or no vulnerability. The trade-off is that the Phalanx can only thrust at a single target for 3 combat rounds. Rumble attacks are used to unsettle enemies: they can discourage fortifying into tactical positions, or weaken them prior to attacks from other units. The action uses ⁵⁄₉ of a move point and doesn’t alter the fortified status of the Phalanx.
-With the discovery of Conscription, Phalanxes can convert to Musketeers in any domestic city. The discovery of Banking changes upkeep from shields to gold.
+With the discovery of Conscription, Phalanxes can convert to Musketeers in any domestic city. The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -546,6 +547,7 @@
Phalanx
- Using the D command to click the target will preserve fortified status of Phalanx.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can Expel
@@ -593,12 +595,11 @@
Archers
Firepower: 1
Hitpoints: 10
Obsolete by: Musketeers
-
Archers fight with bows and arrows. They have good offense and decent defense. Rather than fight to the death, Archers can also do a ranged Volley Attack which avoids hand-to-hand retaliation: two volleys of arrows are fired on up to 7 enemy units on the tile, causing 1-2hp of damage to any units who are hit. This includes Oceanic tiles if not transported. This is useful for softening enemies prior to battle, or for deterring an approach to a strategic location. (Volley Attack is not possible on Cities or Fortresses.) Archers can do a retaliatory Volley attack against any unit which does a Special Unit Attack on them.
+
Archers fight with bows and arrows. They have good offense and decent defense. Rather than fight to the death, Archers can also do a ranged Volley Attack which avoids hand-to-hand retaliation: two volleys of arrows are fired on up to 3 enemy units on the tile, causing 1-2hp of damage to any units who are hit. This includes Oceanic tiles if not transported. This is useful for softening enemies prior to battle, or for deterring an approach to a strategic location. (Volley Attack is not possible on Cities or Fortresses.) Archers can do a retaliatory Volley attack against any unit which does a Special Unit Attack on them.
With the discovery of Conscription, Archers can convert to Musketeers in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -616,6 +617,7 @@
Archers
- Using the D command to click the target will preserve fortified status of Archers.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 2 free rounds of combat against up to 7 units in an enemy stack.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
@@ -667,14 +669,12 @@
Legion
Firepower: 1
Hitpoints: 10
Obsolete by: Musketeers
-
Legions are heavily armed well disciplined soldiers with excellent offensive strength. They are famous for their engineering abilities: with the required technology they can build Forts and Fortresses. They can build Roads outside domestic national territory and inside Forts and Fortresses.
+
Legions are heavily armed well disciplined soldiers with excellent offensive strength. They are famous for their engineering abilities: with the required technology they can build Forts and Fortresses. They can build Roads outside domestic national territory and inside Forts and Fortresses.
Legions are feared for how they start battle: a Pilum Assault targets the shields of the enemy. A pilum stuck in a shield renders it useless, leaving the front line vulnerable to a full-on charge. The Pilum Assault does 1 round of combat at 2× attack bonus on up to 2 units on the tile.
-
-With the discovery of Conscription, Legions can convert to Musketeers in any domestic city.
+With the discovery of Conscription, Legions can convert to Musketeers in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -692,6 +692,7 @@
Legion
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, and Naval Base on tiles.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can Capture Unit
@@ -745,13 +746,11 @@
Pikemen
Firepower: 1
Hitpoints: 10
Obsolete by: Musketeers
-
Long sharp pikes give Pikemen a counter to the height and mobility of mounted units. They enjoy a 2× defense bonus against mounted units.
+
Long sharp pikes give Pikemen a counter to the height and mobility of mounted units. They enjoy a 2× defense bonus against mounted units.
-
-With the discovery of Conscription, Pikemen can convert to Musketeers in any domestic city.
+With the discovery of Conscription, Pikemen can convert to Musketeers in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -761,6 +760,7 @@
Pikemen
➤ 2× defense bonus if attacked by Horsemen, Chariot, Elephants, Crusaders, Knights, or Dragoons.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can Expel
@@ -808,10 +808,9 @@
Musketeers
Firepower: 1
Hitpoints: 20
Obsolete by: Riflemen
-
Musketeers are infantry equipped with early firearms. They replace Pikemen as the preferred city defender, and replace Archers and Legions for offensive foot soldiers. The discovery of Labor Union allows upgrading Musketeers to Riflemen for free in any domestic city.
+
Musketeers are infantry equipped with early firearms. They replace Pikemen as the preferred city defender, and replace Archers and Legions for offensive foot soldiers. The discovery of Labor Union allows converting Musketeers to Riflemen for free in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -821,6 +820,7 @@
Musketeers
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can Capture Unit
@@ -883,6 +883,7 @@
Falconeers
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
@@ -936,13 +937,11 @@
Zealots
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Zealots are warriors devoted to a higher cause. Their faith is strong and they cannot be bribed.
-Theocratic nations can maintain Zealots without paying their steep upkeep. (Zealots are unhappy if not under Theocracy, requiring high upkeep to stay content.) Zealots produced in a city with Ecclesiastical Palace are inspired by fervorous faith to +1 higher veteran level.
-Zealots zealously defend their homeland, and can do surprise skirmish assaults to snipe and injure foreign occupants: Up to 4 invaders on the tile will endure three combat rounds without defense. Skirmish assaults can also be done to foreign occupied cities if they have a remaining Foreign National population.
+
Zealots are warriors devoted to a higher cause. Their faith is strong and they cannot be bribed.
+Theocratic nations can maintain Zealots without paying their steep upkeep. (Zealots are unhappy if not under Theocracy, requiring high upkeep to stay content.) Zealots produced in a city with Ecclesiastical Palace are inspired by fervorous faith to +1 higher veteran level.
+Zealots fervently defend their homeland, and can do surprise skirmish assaults to snipe and injure foreign occupants: Up to 4 invaders on the tile will endure three combat rounds without defense. Skirmish assaults can also be done to foreign occupied cities if they have a remaining Foreign National population.
-
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -960,6 +959,7 @@
Zealots
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
@@ -999,7 +999,7 @@
Zealots
Partisan
-
Cost: 50 shields
+
Cost: 45 shields
Upkeep: 1 Unhappy
Moves: 2
Vision: 2.00 tiles
@@ -1008,8 +1008,8 @@
Partisan
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Partisans are guerilla fighters who can use the terrain to their advantage. Like the Explorer that they upgrade, they can slip through ZOC and live off the land with no upkeep.
-Proportional to city size, up to 8 Partisans appear when an enemy conquers your city. They randomly fortify on any tile inside the circle defined by its workable radius. Partisans prefer defensive tiles, and ignore tile nationality. They spawn only when:
+
Partisans are guerilla fighters who can use the terrain to their advantage. Like the Explorer that they upgrade, they can slip through ZOC and live off the land with no upkeep.
+Proportional to city size, up to 8 Partisans appear when an enemy conquers your city. They randomly fortify on any tile inside the circle defined by its workable radius. Partisans prefer defensive tiles, and ignore tile nationality. They spawn only when:
➣ Guerilla Warfare is known by any player.
➣ The city was originally built by you.
➣ You must be Theocratic, OR,
@@ -1024,6 +1024,7 @@
Partisan
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ Ignores terrain effects (moving costs at most 1/3 MP per tile).
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Not subject to zones of control imposed by other units.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
@@ -1073,12 +1074,11 @@
Riflemen
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Riflemen are World War-era infantry. They are good at defending cities and strategic Fortresses. They also have good attack capability.
+
Riflemen are World War-era infantry. They are good at defending cities and strategic Fortresses. They also have good attack capability.
-Under Communism this unit costs 5 less. Also, Communism tech allows Communist governments to de-commission Riflemen into Workers via the Convert order. And vice versa.
+Under Communism this unit costs 5 less. Also, Communism tech allows Communist governments to de-commission Riflemen into Workers via the Convert order. And vice versa.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1088,6 +1088,7 @@
Riflemen
➤ Under Communism, may be obtained by conversion of Workers.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
@@ -1138,10 +1139,9 @@
Alpine Troops
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Alpine Troops are highly mobile units and excellent defenders. Similar to an Explorer, they treat every land tile like a road were on it.
+
Alpine Troops are highly mobile units and excellent defenders. Similar to an Explorer, they treat every land tile like a road were on it.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1152,6 +1152,7 @@
Alpine Troops
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ Ignores terrain effects (moving costs at most ⅓ MP per tile).
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
@@ -1191,7 +1192,7 @@
Alpine Troops
Marines
-
Cost: 60 shields
+
Cost: 55 shields
Upkeep: 1 Shield, 1 Unhappy
Moves: 2
Vision: 2.83 tiles
@@ -1200,14 +1201,13 @@
Marines
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Marines are experts at all-terrain warfare. They are the strongest foot unit in the game, and are armed with diverse weaponry.
+
Marines are experts at all-terrain warfare. They are the strongest foot unit in the game, and are armed with diverse weaponry.
Unlike other units, Marines can attack Sea units (at a 50% penalty). Air units do not stop them from attacking reachable targets. They can attack while fortified and maintain fortified status. They can attack from a Transport or Helicopter, and can offload without losing a turn of movement. Marines can make Forts and Airbases. Marines get an extra 2/9 movement points for each veteran level they possess. Starting at v2, they gain the ability to do long-range bazooka attacks for 3 combat rounds, hitting up to 2 units on the tile and killing a maximum of 1.
Bazooka attacks can be done to Ocean tiles if the Marines aren't transported.
Marines built in a city with an Airport, Port Facility, and Barracks III receive an extra veteran level.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to LandAirSea unit class.
• Can attack from Ships and Helicopters.
• Can occupy empty enemy cities.
@@ -1231,6 +1231,7 @@
Marines
➤ May load and unload from Trains and Helicopters even when underway.
➤ Won't lose all movement when moving from non-native terrain to native terrain, or unloading from transport.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
@@ -1270,7 +1271,7 @@
Marines
Paratroopers
-
Cost: 60 shields
+
Cost: 55 shields
Upkeep: 1 Shield, 1 Unhappy
Moves: 2
Vision: 2.00 tiles
@@ -1279,8 +1280,8 @@
Paratroopers
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Paratroopers are experts at airborne attacks. From a friendly city or airbase, Paratroopers who have moves left and have not already paradropped in the current turn, can paradrop directly to any tile in range, and be immediately ready to act there. Beware dropping into unseen territory, as Paratroopers landing on a tile occupied by enemy units will be lost. Also note: Paradropping is assumed to be done from aircraft. Therefore, Fighters on Vigil may auto-attack the tile you land on, if adjacent.
-
+
Paratroopers are experts at airborne attacks. From a friendly city or airbase, Paratroopers who have moves left and have not already paradropped in the current turn, can paradrop directly to any tile in range, and be immediately ready to act there. Beware dropping into unseen territory, as Paratroopers landing on a tile occupied by enemy units will be lost. Also note: Paradropping is assumed to be done from aircraft. Therefore, Fighters on Vigil may auto-attack the tile you land on, if adjacent.
+
The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
@@ -1292,6 +1293,7 @@
Paratroopers
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
@@ -1345,10 +1347,9 @@
Mechanized Infantry
Firepower: 1
Hitpoints: 30
Obsolete by: None
-
Mechanized Infantry have the strongest general defense strength of any land unit. They have decent attack strength in open field engagements, and excellent mobility.
+
Mechanized Infantry have the strongest general defense strength of any land unit. They have decent attack strength in open field engagements, and excellent mobility.
-
-Upkeep for Mechanized infantry is paid in gold instead of shields.
+Upkeep for Mechanized infantry is paid in gold instead of shields.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1358,6 +1359,7 @@
Mechanized Infantry
➤ Unable to attack air units.
➤ May load onto Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
@@ -1402,7 +1404,7 @@
Horsemen
Firepower: 1
Hitpoints: 10
Obsolete by: Knights
-
Horsemen are mounted warriors and an early shock-troop that can penetrate deep into enemy territory.
+
Horsemen are mounted warriors and an early shock-troop that can penetrate deep into enemy territory.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1413,6 +1415,7 @@
Horsemen
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Expel
• target unit must be alone and not on Mountains.
• uses one movement point
@@ -1456,7 +1459,7 @@
Chariot
Firepower: 1
Hitpoints: 10
Obsolete by: Knights
-
Chariots are horse-pulled war wagons, stronger but more expensive than horsemen.
+
Chariots are horse-pulled war wagons, stronger but more expensive than horsemen.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1467,6 +1470,7 @@
Chariot
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
➤ Can Expel
@@ -1512,7 +1516,7 @@
Elephants
Firepower: 1
Hitpoints: 10
Obsolete by: Crusaders
-
Elephants are towering animals trained for war. Their mobility and formidable attack strength make them excellent for offensive engagements, but they defend poorly against most units.
+
Elephants are towering animals trained for war. Their mobility and formidable attack strength make them excellent for offensive engagements, but they defend poorly against most units.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1523,6 +1527,7 @@
Elephants
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
➤ Can Expel
@@ -1568,7 +1573,7 @@
Crusaders
Firepower: 1
Hitpoints: 10
Obsolete by: Dragoons
-
Crusaders are mounted warriors driven by a higher cause. They have superior attack to Knights, but are poor at defending. They are ideally suited for leading the charge in offensive campaigns.
+
Crusaders are mounted warriors driven by a higher cause. They have superior attack to Knights, but are poor at defending. They are ideally suited for leading the charge in offensive campaigns.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1579,6 +1584,7 @@
Crusaders
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
➤ Can Expel
@@ -1624,7 +1630,7 @@
Knights
Firepower: 1
Hitpoints: 10
Obsolete by: Dragoons
-
Knights are heavily armored mounted warriors. They defend at D:3 against mounted units. They defend at D:2 against foot units. They defend at D:1 against everything else. Their noble status allows them to attack cities without population reduction.
+
Knights are heavily armored mounted warriors. They defend at D:3 against mounted units. They defend at D:2 against foot units. They defend at D:1 against everything else. Their noble status allows them to attack cities without population reduction, and conquer a size 1 city without destroying it.➤ Belongs to LandNoKill unit class.
• Does not reduce population when attacking city.
• Can occupy empty enemy cities.
@@ -1638,6 +1644,7 @@
Knights
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
➤ Can Expel
@@ -1683,7 +1690,7 @@
Dragoons
Firepower: 1
Hitpoints: 20
Obsolete by: Cavalry
-
Dragoons are mounted warriors carrying early firearms. Being the first highly mobile gunpowder unit, they are formidable for offensive campaigns.
+
Dragoons are mounted warriors carrying early firearms. Being the first highly mobile gunpowder unit, they are formidable for offensive campaigns.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1694,6 +1701,7 @@
Dragoons
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
➤ Can Expel
@@ -1739,7 +1747,7 @@
Cavalry
Firepower: 1
Hitpoints: 20
Obsolete by: Armor
-
The combination of mobility and superior attack strength make Cavalry the most feared offensive unit in their era.
+
The combination of mobility and superior attack strength make Cavalry the most feared offensive unit in their era.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1749,6 +1757,7 @@
Cavalry
➤ -50% attack penalty when attacking Helicopter.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
@@ -1795,7 +1804,8 @@
Armor
Firepower: 1
Hitpoints: 30
Obsolete by: Armor II
-
Armor is the mechanized equivalent of a Cavalry unit. They have high attack strength, superb mobility, and the highest defense of any offensive unit. They are massive all-terrain vehicles, well-suited for penetrating defensive fortifications: Units inside Forts get no defense bonus against Armor. Units inside Fortresses get a reduced bonus of 1.67× instead of 2×.
+
Armor is the mechanized equivalent of a Cavalry unit. They have high attack strength, superb mobility, and the highest defense of any offensive unit. They are massive all-terrain vehicles, well-suited for penetrating defensive fortifications: Units inside Forts get no defense bonus against Armor. Units inside Fortresses get a reduced bonus of 1.67× instead of 2×.
+Communists pay 10 less for this unit.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1810,6 +1820,7 @@
Armor
➤ Defending Forts get no bonus.+33% attack bonus vs. Fortress (reduces it to 1.67× defense).
➤ May load onto Helicopter transports even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Expel
• target unit must be alone and not on Mountains.
@@ -1854,7 +1865,7 @@
Armor II
Firepower: 1
Hitpoints: 30
Obsolete by: None
-
Armor II is the ultra-modern upgrade to Armor. It features high tech composite armor and electronic countermeasures (ECM). Forts and Fortresses gain no bonus against its ability to blast and break through fortifications. ECM and composite armor give a 2× defense bonus against Missiles. Unlike most land units, Armor II can attack reachable units regardless of whether unreachable units protect the tile.
+
Armor II is the ultra-modern upgrade to Armor. It features high tech composite armor and electronic countermeasures (ECM). Forts and Fortresses gain no bonus against its ability to blast and break through fortifications. ECM and composite armor give a 2× defense bonus against Missiles. Unlike most land units, Armor II can attack reachable units regardless of whether unreachable units protect the tile.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1872,6 +1883,7 @@
Armor II
➤ Defending Fortresses get no bonus.
➤ May load onto Helicopter transports even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Expel
• target unit must be alone and not on Mountains.
@@ -1914,10 +1926,9 @@
Catapult
Firepower: 1
Hitpoints: 10
Obsolete by: Cannon
-
Catapults are large rock-throwing war machines. The ballistic delivery of massive stones diminishes the defense bonus vs land attacks by Fortifications and City Walls by -25%, yielding a 1.25× and 2.75× bonus, respectively.
+
Catapults are large rock-throwing war machines. The ballistic delivery of massive stones diminishes the defense bonus vs land attacks by Fortifications and City Walls by -25%, yielding a 1.25× and 2.75× bonus, respectively.
-
-Catapults are weak defenders, and will need an escort to be effective. Even so, it is better to rush attack them than do special unit attacks from range:when attacked from range or rammed in a Fortress, Catapults can retaliate for 4 rounds of combat.
+Catapults are weak defenders, and will need an escort to be effective. Even so, it is better to rush attack them than do special unit attacks from range:when attacked from range or rammed in a Fortress, Catapults can retaliate for 4 rounds of combat.➤ Belongs to Land unit class, Ballistic sub-class.
• 25% is subtracted from the bonus of City defense improvements against Land attacks:
• Fortifications are reduced from 1.5× to 1.25×
@@ -1929,6 +1940,7 @@
Catapult
• Can't attack as Cargo. Must first unload.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 4 free rounds of combat against up to 1 unit in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -1975,7 +1987,7 @@
Siege Ram
Firepower: 1
Hitpoints: 10
Obsolete by: Cannon
-
Siege Rams launch a tremendous thrusting force through iron-capped timber shafts. They can break through City Walls if they are not killed first. City Walls have counter defenses, giving Siege Rams even odds of success. Wall defense in capitals is twice as stiff—cutting odds to 25%.
+
Siege Rams launch a tremendous thrusting force through iron-capped timber shafts. They can break through City Walls if they are not killed first. City Walls have counter defenses, giving Siege Rams even odds of success. Wall defense in capitals is twice as stiff—cutting odds to 25%.
Siege Rams may also ram Fortresses, doing up to 4hp of damage on each occupant. This emulates damage to the Fortress defense bonus, which is partly or fully repaired/recovered next turn.
@@ -2002,6 +2014,7 @@
Siege Ram
• Cannot be done to any tile except a Fortress or Naval Base.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Cannon or, when possible, to the unit type it upgrades to.
➤ Can Pillage
@@ -2023,10 +2036,9 @@
Cannon
Firepower: 1
Hitpoints: 20
Obsolete by: Artillery
-
Cannons are huge guns that use gunpowder to fire massive metallic projectiles much faster and more accurately than earlier ballistic weapons. The aerial delivery of high speed massive metal gives an edge against Fortifications and City Walls. The defense bonuses vs. land attacks from those are reduced by -50%. This leaves Fortifications only with their terrain bonus, and reduces City walls to 2.5x.
+
Cannons are huge guns that use gunpowder to fire massive metallic projectiles much faster and more accurately than earlier ballistic weapons. The aerial delivery of high speed massive metal gives an edge against Fortifications and City Walls. The defense bonuses vs. land attacks from those are reduced by -50%. This leaves Fortifications only with their terrain bonus, and reduces City walls to 2.5x.
-
-While Cannons are strong attackers, they are also weak defenders. Yet they are not without defensive purpose. They can retaliate 6 rounds of bombardment against special unit attacks from other units.
+While Cannons are strong attackers, they are also weak defenders. Yet they are not without defensive purpose. They can retaliate 6 rounds of bombardment against special unit attacks from other units.➤ Belongs to Land unit class, Ballistic sub-class.
• 50% is subtracted from the bonus of City defense improvements against Land attacks:
• Fortifications are reduced to terrain bonus only.
@@ -2039,6 +2051,7 @@
Cannon
➤ Unable to attack air units.
➤ May load onto and disembark from Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 5 free rounds of combat against up to 2 units in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -2085,10 +2098,9 @@
Artillery
Firepower: 2
Hitpoints: 20
Obsolete by: Howitzer
-
Artillery are a major upgrade to Cannons, with doubled firepower. Accuracy and projectile speed go to a whole new level. Thus, they have an even greater bonus than their predecessors against Fortifications and City Walls. Such bonuses are reduced -75%. This leaves Fortifications only with their terrain bonus, and reduces the bonus from Walls down to 2.25x.
+
Artillery are a major upgrade to Cannons, with doubled firepower. Accuracy and projectile speed go to a whole new level. Thus, they have an even greater bonus than their predecessors against Fortifications and City Walls. Such bonuses are reduced -75%. This leaves Fortifications only with their terrain bonus, and reduces the bonus from Walls down to 2.25x.
-
-Like their predecessors, Artillery are poor at defense, and need an escort to be effective. However, they can retaliate 6 rounds of bombardment against special unit attacks from other units.
+Like their predecessors, Artillery are poor at defense, and need an escort to be effective. However, they can retaliate 6 rounds of bombardment against special unit attacks from other units.➤ Belongs to Land unit class, Ballistic sub-class.
• 75% is subtracted from the bonus of City defense improvements against Land attacks:
• Fortifications are reduced to terrain bonus only.
@@ -2101,6 +2113,7 @@
Artillery
➤ Unable to attack air units.
➤ May load onto and disembark from Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 6 free rounds of combat against up to 3 units in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -2147,10 +2160,9 @@
Anti-Aircraft Artillery
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Anti-Aircraft Artillery (AAA) can attack almost anything from anywhere. AAA get a 2× bonus against Air units. AAA qualify as weaponry for Marines and have the same abilities: they can be transported on any unit that carries Marines, can attack from or to non-native tiles, and do not lose a turn when unloading. Transported AAA defend their tile on both Land and Sea. (AAA cannot attack Submarines, Missiles, or Jet Bombers.)
+
Anti-Aircraft Artillery (AAA) can attack almost anything from anywhere. AAA get a 2× bonus against Air units. AAA qualify as weaponry for Marines and have the same abilities: they can be transported on any unit that carries Marines, can attack from or to non-native tiles, and do not lose a turn when unloading. Transported AAA defend their tile on both Land and Sea. (AAA cannot attack Submarines, Missiles, or Jet Bombers.)
-
-After Space Flight and 10 turns of service, AAA can be retrofitted to Mobile SAM. The Convert order must be done in your capital, and takes 4 turns.
+After Space Flight and 10 turns of service, AAA can be retrofitted to Mobile SAM. The Convert order must be done in a capital, and takes 4 turns.➤ Belongs to LandAirSea unit class.
• Can attack from Ships and Helicopters.
• Can occupy empty enemy cities.
@@ -2167,6 +2179,7 @@
Anti-Aircraft Artillery
➤ Anti-Air bonus of this unit is less effective against Stealth, which has a 25% bonus against this unit.
➤ May load and unload from Trains and Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can attack against Air units, which are usually not reachable.
➤ Can attack against AirProtect units, which are usually not reachable.
@@ -2215,10 +2228,9 @@
Mobile SAM
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
The Mobile SAM is the strongest Anti-Air unit on the ground. It can attack nearby aircraft. It has a 2× bonus against all Air units. The Mobile SAM can also carry one Missile of any type. Unlike the AAA, it cannot attack non-native tiles or attack while transported.
+
The Mobile SAM is the strongest Anti-Air unit on the ground. It can attack nearby aircraft. It has a 2× bonus against all Air units. The Mobile SAM can also carry one Missile of any type. Unlike the AAA, it cannot attack non-native tiles or attack while transported.
-
-AAA can upgrade to Mobile SAM for free on its 10th turn of service. The Convert order must be done in the capital city and takes 4 turns.
+AAA can upgrade to Mobile SAM for free on its 10th turn of service. The Convert order must be done in the capital city and takes 4 turns.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -2232,9 +2244,10 @@
Mobile SAM
➤ Anti-Air bonus of this unit is less effective against Stealth, which has a 25% bonus against this unit.
➤ Can carry and refuel 1 Missile unit.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can attack against Air units, which are usually not reachable.
-➤ Can attack against AirPillage units, which are usually not reachable.
+➤ Can attack against Air_High_Altitude units, which are usually not reachable.
➤ Can attack against AirProtect units, which are usually not reachable.
➤ Can Expel
• target unit must be alone and not on Mountains.
@@ -2261,10 +2274,9 @@
Howitzer
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
Howitzers are upgraded Artillery with a terrifying increase to mobility and attack strength. Fortifications get no bonus except their terrain bonus, and City Walls are completely ineffective against Howitzers.
+
Howitzers are upgraded Artillery with a terrifying increase to mobility and attack strength. Fortifications get no bonus except their terrain bonus, and City Walls are completely ineffective against Howitzers.
-
-While Howitzers are vulnerable when not escorted, they can retaliate 7 rounds of bombardment against special unit attacks from other units. This is a strong deterrent.
+While Howitzers are vulnerable when not escorted, they can retaliate 7 rounds of bombardment against special unit attacks from other units. This is a strong deterrent.➤ Belongs to Land unit class, Ballistic sub-class.
• Fortifications are reduced to terrain bonus only.
• City Walls get no bonus against this unit.
@@ -2275,6 +2287,7 @@
Howitzer
• Can't attack as Cargo. Must first unload.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 7 free rounds of combat against up to 4 units in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -2319,8 +2332,8 @@
Balloon
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Balloons gather intel on potential battle areas. They have great vision and are unreachable by units prior to Riflemen and Ironclads. They can stay in the air for one Turn Change before landing in a City, Quay, Base, or transport unit with cargo capacity of 4+. Balloons do not block units under them from being attacked, and cannot fly over mountains.
-
+
Balloons gather intel on potential battle areas. They have great vision and are unreachable by units prior to Riflemen and Ironclads. They can stay in the air for one Turn Change before landing in a City, Quay, Base, or transport unit with cargo capacity of 4+. Balloons give sentry reports of nearby unit movements even when not on sentry. Balloons do not block units under them from being attacked, and cannot fly over mountains.
+
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Balloon unit class.
• Speed is not affected by terrain.
@@ -2333,8 +2346,9 @@
Balloon
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ Unit must be in a City, Fort, Fortress, Naval Base, Airbase; or on a Galleon, Cargo Ship, Transport, or Carrier, after 2 turns.
➤ Can't be bribed.
➤ Can't be sabotaged.
@@ -2353,9 +2367,9 @@
Zeppelin
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
As new technology inspires the upgrade of Balloons, the Zeppelin appears. In theory, it can be used for tactical advantage in combat. However, vulnerability and cost make it better for intel. It can do limited attack with bombs, or 20 combat rounds with medium caliber ordnance. It can stay out for two Turn Changes before landing in a City, Quay, Fortress, Naval Base, Airbase, or transport with 6+ capacity.
-Zeppelins have great vision and are unreachable by units prior to Marines and Destroyers. Zeppelins do not block units under them from being attacked. Unlike Balloons, they can fly over Mountains.
-
+
As new technology inspires the upgrade of Balloons, the Zeppelin appears. In theory, it can be used for tactical advantage in combat. However, vulnerability and cost make it better for intel. It can do limited attack with bombs, or 20 combat rounds with medium caliber ordnance. It can stay out for two Turn Changes before landing in a City, Quay, Fortress, Naval Base, Airbase, or transport with 6+ capacity.
+Zeppelins have great vision and are unreachable by units prior to Marines and Destroyers. Zeppelins do not block units under them from being attacked. Zeppelins give sentry reports of nearby unit movements even when not on sentry. Unlike Balloons, they can fly over Mountains.
+
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Zeppelin unit class.
• Speed is not affected by terrain.
@@ -2369,6 +2383,7 @@
Zeppelin
• A maximum of 1 unit can be killed.
• Can be done to Cities, Forts, Fortresses, Naval Bases, Land Tiles, and Oceanic Tiles.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Unit has to be in a City, Fortress, Naval Base, Airbase; or on a Carrier, Transport, or Train, after 3 turns.
➤ Can Attack
@@ -2404,8 +2419,8 @@
Airplane
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Airplanes can carry a single diplomatic unit. They can land in any domestic or allied city. For missions to non-allied nations, an empty Airbase must be available for landing. Airplanes have two turns of fuel. Each new aviation tech upgrades range by +2 moves per turn.
-
+
Airplanes can carry a diplomatic unit or a Freight unit. They can land in any domestic or allied city. For missions to non-allied nations, an empty Airbase must be available for landing. Airplanes have two turns of fuel. Each new aviation tech upgrades range by +2 moves per turn.
+
Airplanes can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -2413,7 +2428,7 @@
Airplane
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Never imposes a zone of control.
@@ -2440,10 +2455,10 @@
Fighter
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Fighter
-
Fighters are the first offensive Air units. Their superior mobility and firepower alter the very nature of warfare. They can move anywhere and attack any unit (except Submarines and Jet Bombers.)
-
+
Fighters are the first offensive Air units. Their superior mobility and firepower alter the very nature of warfare. They can move anywhere and attack any unit (except Submarines and Jet Bombers.)
+
INTERCEPTOR: the Vigil order lets Fighters auto-attack adjacent Air units if they have better attack odds. A Fighter can Vigil if it uses 2 move points or less.
-
+
Fighters can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to AirProtect unit class.
• INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
@@ -2452,7 +2467,7 @@
Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Anti-Air units have bonuses against this unit.
@@ -2482,6 +2497,11 @@
Fighter
crack
210%
10%
+ 1
ace
220%
10%
+ 1
top gun
230%
-
+ 1
+
+Fighters types on Vigil will engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
@@ -2496,9 +2516,9 @@
Escort Fighter
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Fighter
-
Escort Fighters are large fighters with high fuel capacity. Higher mass and stronger construction allow them to absorb more damage. They do not attack as well as standard Fighters, but can return home on the next turn. They are good for long-range missions, defensive air support, and escorting bombers.
-
-INTERCEPTOR: the Vigil order lets Escort Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it uses 3 move points or less.
+
Escort Fighters are large fighters with high fuel capacity. Higher mass and stronger construction allow them to absorb more damage. They do not attack as well as standard Fighters, but can return home on the next turn. They are good for long-range missions, defensive air support, and escorting bombers.
+
+INTERCEPTOR: the Vigil order lets Escort Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it uses 3 move points or less.
This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to AirProtect unit class.
• INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
@@ -2507,7 +2527,7 @@
Escort Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Anti-Air units have bonuses against this unit.
@@ -2536,7 +2556,12 @@
Escort Fighter
elite
200%
15%
-
crack
210%
15%
+ 1
ace
220%
15%
+ 1
-
top gun
230%
-
+ 1
+
top gun
230%
-
+ 1
+
+Fighters types on Vigil will engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
Dive Bomber
@@ -2561,7 +2586,7 @@
Dive Bomber
• A3 instead of A4 in Air-to-Air
• Can't block its tile
• Can't intercept
-
+
Communists pay 10 less for this unit. GOTO prevents unit loss from lack of fuel. Override this by ordering adjacent moves.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -2569,7 +2594,7 @@
Dive Bomber
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Ground Strike against tile infrastructure.
• 50% odds of success. +5% for each veteran level.
@@ -2619,11 +2644,11 @@
Medium Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Bomber
-
The Medium Bomber is well suited for moderately strong targets or multiple weak targets. It may not attack other Air units. It is a Soft Field Unit and can't carry Bomb units, causing less discontent than other types of Bombers.
+
The Medium Bomber is well suited for moderately strong targets or multiple weak targets. It may not attack other Air units. It is a Soft Field Unit and can't carry Bomb units, causing less discontent than other types of Bombers.
➤ Unhappy effect for Soft Field units:
Republic:0 Democracy:1 (non-aggressive)
Republic:1 Democracy:2 (aggressive)
-
+
This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -2631,7 +2656,7 @@
Medium Bomber
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Anti-Air units have bonuses against this unit.
@@ -2672,10 +2697,10 @@
Heavy Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Bomber
-
Heavy Bombers are large and sturdy Bombers with larger payload and numerous gunner defense stations. They are excellent for hitting strong well-defended ground targets. They cannot attack Air units. They can carry one Bomb. Like most Bombers, the Heavy Bomber is a Field Unit. Field Units cause the same unhappiness no matter if aggressively or peacefully deployed.
+
Heavy Bombers are large and sturdy Bombers with larger payload and numerous gunner defense stations. They are excellent for hitting strong well-defended ground targets. They cannot attack Air units. They can carry one Bomb. Like most Bombers, the Heavy Bomber is a Field Unit. Field Units cause the same unhappiness no matter if aggressively or peacefully deployed.
➤ Unhappy effect for Field units:
Republic:1 Democracy:2
-
+
This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -2683,7 +2708,7 @@
Heavy Bomber
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Anti-Air units have bonuses against this unit.
@@ -2726,8 +2751,8 @@
Strategic Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Bomber
-
The Strategic Bomber offers a small improvement in attack. Higher altitude offers a good upgrade in defense. It has considerably longer range, with one more turn of fuel. Upgraded payload enables instant-Pillage bombing. May carry two Bombs. May carry two Bombs.
-
+
The Strategic Bomber offers a small improvement in attack. Higher altitude offers a good upgrade in defense. It has considerably longer range, with one more turn of fuel. Upgraded payload enables instant-Pillage bombing. May carry two Bombs. May carry two Bombs.
+
This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -2735,7 +2760,7 @@
Strategic Bomber
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Bomb against tile infrastructure.
• 60% odds of success. +5% for each veteran level.
@@ -2782,8 +2807,8 @@
AWACS
Firepower: 1
Hitpoints: 20
Obsolete by: Spy Plane
-
The AWACS has great fuel capacity for long range flights, and advanced radar that can determine the location of enemy units over a wide area. The AWACS has 2 turns of fuel.
-
+
The AWACS has great fuel capacity for long range flights, and advanced radar that can determine the location of enemy units over a wide area. The AWACS has 2 turns of fuel.
+
AWACS can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -2791,7 +2816,7 @@
AWACS
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Never imposes a zone of control.
@@ -2816,8 +2841,8 @@
Spy Plane
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
The Spy Plane is an ultrasonic high altitude aircraft with long range. It gathers intel over a wide area. Stealth technology makes it invisible, except to enemies on adjacent tiles. Spy Planes have 2 turns of fuel. They are unreachable to primitive propeller based aircraft.
-
+
The Spy Plane is an ultrasonic high altitude aircraft with long range. It gathers intel over a wide area. Stealth technology makes it invisible, except to enemies on adjacent tiles. Spy Planes have 2 turns of fuel. They are unreachable to primitive propeller based aircraft.
+
Spy Planes can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air_High_Altitude unit class.
• Invisible except when next to an enemy unit or city.
@@ -2826,7 +2851,7 @@
Spy Plane
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Is invisible except when next to an enemy unit or city.
@@ -2851,8 +2876,8 @@
Satellite
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Satellites orbit the Earth above the atmosphere. They are unreachable to anything but missiles. Their orbital velocity allows them to circumnavigate the planet in a couple turns. Telescopic cameras allow them to Investigate City, even if the city has a Police Station. Satellites cannot be seen by others unless adjacent. Satellites require Laser tech, and can only be built in the city with the Apollo Program.
-
+
Satellites orbit the Earth above the atmosphere. They are unreachable to anything but missiles. Their orbital velocity allows them to circumnavigate the planet in a couple turns. Telescopic cameras allow them to Investigate City, even if the city has a Police Station. Satellites cannot be seen by others unless adjacent. Satellites require Laser tech, and can only be built in the city with the Apollo Program.
+
Satellites can Stack-Escape:a 100% chance to escape a killed stack if they have more remaining move points than the attacker.
NOTE: Due to game mechanics, Satellites block terrestrial movement over a tile. This is solved by killing it with any Missile. To avoid this, end your turn far away from developed or trafficked areas.➤ Belongs to Space unit class.
@@ -2861,7 +2886,7 @@
Satellite
• Not subject to zones of control.
• Unreachable. Only missile types can attack this unit.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ STACK ESCAPE: 100% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Is invisible except when next to an enemy unit or city.
➤ Never imposes a zone of control.
@@ -2883,12 +2908,12 @@
Transport Helicopter
Firepower: 2
Hitpoints: 21
Obsolete by: None
-
Transport Helicopters can carry three land units, which they can pick up from anywhere
- Foot and artillery types can unload by stepping off to any adjacent tile, but will lose all moves (except Marine types). Other types need a City, Airbase, or Naval Base to unload.
+
Transport Helicopters can carry three land units, which they can pick up from anywhere
+ Foot and artillery types can unload by stepping off to any adjacent tile, but will lose all moves (except Marine types). Other types need a City, Airbase, or Naval Base to unload.
- Helicopters lose 1 hp for each turn not ended in a City, Base, or Carrier. +1 move is awarded if starting a turn in a City or Airbase.
+ Helicopters lose 1 hp for each turn not ended in a City, Base, or Carrier. +1 move is awarded if starting a turn in a City or Airbase.
- All Helicopters can be attacked by land from Riflemen onward; by sea from Ironclad onward; get +50% defence vs. foot units; and are unreachable to artillery types. Unlike attack Helicopters, Transport Helicopters are intercepted by Fighters on vigil.
+ All Helicopters can be attacked by land from Riflemen onward; by sea from Ironclad onward; get +50% defence vs. foot units; and are unreachable to artillery types. Unlike attack Helicopters, Transport Helicopters are intercepted by Fighters on vigil.➤ Belongs to Helicopter unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -2933,14 +2958,14 @@
Helicopter
Firepower: 2
Hitpoints: 21
Obsolete by: None
-
Helicopters are the Air Cavalry of modern armies. They can attack multiple targets and conquer cities. Unaffected by terrain, they are good for Partisan suppression. They can transport Marines and AAA and are thus well-suited for commando ops.
+
Helicopters are the Air Cavalry of modern armies. They can attack multiple targets and conquer cities. Unaffected by terrain, they are good for Partisan suppression. They can transport Marines and AAA and are thus well-suited for commando ops.
-For each turn not ended in a City, Base, or Carrier, 1hp is lost. +1 move is awarded if starting a turn in a City or Airbase.
+For each turn not ended in a City, Base, or Carrier, 1hp is lost. +1 move is awarded if starting a turn in a City or Airbase.
-Helicopters fly low to avoid Interception. This invites attacks by ground units from Riflemen onward. They are unreachable to Artillery types and get +50% defense vs. foot units. Helicopters can be attacked by Sea from Ironclad onward.
+Helicopters fly low to avoid Interception. This invites attacks by ground units from Riflemen onward. They are unreachable to Artillery types and get +50% defense vs. foot units. Helicopters can be attacked by Sea from Ironclad onward.
Helicopters can retaliate 3 rounds against Special Unit Attacks.
-Helicopters can transport one Marines or AAA unit.
+Helicopters can transport one Marines or AAA unit.➤ Belongs to Helicopter unit class.
• Can occupy empty enemy cities.
• Speed is not affected by terrain.
@@ -2955,6 +2980,7 @@
Helicopter
➤ Gets +1 move point if starting its turn in a City or Airbase.
➤ Can carry and refuel 1 Marines or Anti-Aircraft Artillery unit.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 3 free rounds of combat against up to 3 units in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -2991,11 +3017,11 @@
Jet Fighter
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Jet Fighters upgrade the Fighter with improved attack, defense, and range.
-
+
Jet Fighters upgrade the Fighter with improved attack, defense, and range.
+
INTERCEPTOR: the Vigil order lets Jet Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it uses 3 move points or less.
-
-Helicopters can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+
+Jet Fighters can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to AirProtect unit class.
• INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
• AIR COVER: Most units who cannot attack this unit also cannot attack other units on the tile.
@@ -3003,7 +3029,7 @@
Jet Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Anti-Air units have bonuses against this unit.
@@ -3012,7 +3038,7 @@
Jet Fighter
➤ Can attack against Missile units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can attack against Air units, which are usually not reachable.
-➤ Can attack against AirPillage units, which are usually not reachable.
+➤ Can attack against Air_High_Altitude units, which are usually not reachable.
➤ Can attack against AirProtect units, which are usually not reachable.
➤ Unit must be in a City, Airbase, or on a Carrier after 1 turn.
➤ Can Expel Airplane and AWACS.
@@ -3032,6 +3058,11 @@
Jet Fighter
crack
210%
10%
+ 1
ace
220%
10%
+ 1
top gun
230%
-
+ 1
+
+Fighters types on Vigil will engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
@@ -3046,7 +3077,7 @@
Ground Strike Fighter
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Ground Strike Fighters upgrade the Dive Bomber. They cannot block attacks on other units on their tile, nor engage against Fighters. However, like the Dive Bomber, they can interdict ZoC over land tiles. High fuel capacity allows two turns in the air. No unit can stop this unit from attacking surface units on a tile. This unit can also do pinpointed surgical strikes to pillage tiles. This aircraft is a specialized niche unit. It is not an interceptor.
+
Ground Strike Fighters upgrade the Dive Bomber. They cannot block attacks on other units on their tile, nor engage against Fighters. However, like the Dive Bomber, they can interdict ZoC over land tiles. High fuel capacity allows two turns in the air. No unit can stop this unit from attacking surface units on a tile. This unit can also do pinpointed surgical strikes to pillage tiles. This aircraft is a specialized niche unit. It is not an interceptor.
This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO prevents unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -3054,7 +3085,7 @@
Ground Strike Fighter
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Ground Strike against tile infrastructure.
• 75% odds of success. +5% for each veteran level.
@@ -3100,8 +3131,8 @@
Jet Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Jet Bombers are stratospheric long-range bombers with 3 turns of fuel: they can be airborne for two turn-changes. Their range makes them useful even after Stealth Bombers are available. Stratospheric altitude is unreachable to propeller-based Fighters and AAA. Jet Bombers can carpet-bomb, pillaging tiles from the air. May carry three Bombs.
-
+
Jet Bombers are stratospheric long-range bombers with 3 turns of fuel: they can be airborne for two turn-changes. Their range makes them useful even after Stealth Bombers are available. Stratospheric altitude is unreachable to propeller-based Fighters and AAA. Jet Bombers can carpet-bomb, pillaging tiles from the air. May carry three Bombs.
+
Jet Bombers can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air_High_Altitude unit class.
• Speed is not affected by terrain.
@@ -3109,7 +3140,7 @@
Jet Bomber
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one, including AAA and propeller aircraft.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Carpet Bomb against tile infrastructure.
• 75% odds of success. +5% for each veteran level.
@@ -3155,10 +3186,10 @@
Stealth Fighter
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
The most advanced Fighter, with improved attack and longer range. Stealth Fighters cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to a smaller 25% bonus.
-
+
The most advanced Fighter, with improved attack and longer range. Stealth Fighters cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to a smaller 25% bonus.
+
INTERCEPTOR: the Vigil order lets Stealth Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it uses 4 move points or less.
-
+
Stealth Fighters can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining moves than the attacker.
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to AirProtect unit class.
@@ -3168,7 +3199,7 @@
Stealth Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ +25% attack bonus when attacking Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
➤ +25% defense bonus if attacked by Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
@@ -3181,7 +3212,7 @@
Stealth Fighter
➤ Can attack against Missile units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can attack against Air units, which are usually not reachable.
-➤ Can attack against AirPillage units, which are usually not reachable.
+➤ Can attack against Air_High_Altitude units, which are usually not reachable.
➤ Can attack against AirProtect units, which are usually not reachable.
➤ Unit must be in a City, Airbase, or on a Carrier after 1 turn.
➤ Can Expel Airplane and AWACS.
@@ -3201,6 +3232,11 @@
Stealth Fighter
crack
210%
10%
+ 1
ace
220%
10%
+ 1
top gun
230%
-
+ 1
+
+Fighters types on Vigil will engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
@@ -3215,8 +3251,8 @@
Stealth Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
The deadliest Bomber, with improved attack and speed. Stealth Bombers cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to only a 25% bonus. May carry two Bombs.
-
+
The deadliest Bomber, with improved attack and speed. Stealth Bombers cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to only a 25% bonus. May carry two Bombs.
+
Stealth Bombers can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining moves than the attacker.
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
@@ -3225,7 +3261,7 @@
Stealth Bomber
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ +25% attack bonus when attacking Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
➤ +25% defense bonus if attacked by Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
@@ -3269,7 +3305,7 @@
Boat
Firepower: 1
Hitpoints: 10
Obsolete by: Trireme
-
Boats were used pre-historically for exploration, transport, and interaction with other tribes. They can travel rivers and oceans, but must always stay near shore. Boats can carry one unit and do ancient commerce: building Wonders or setting up Trade Routes. Early bartering may help you advance faster or create relations with ancient neighbors.
+
Boats were used pre-historically for exploration, transport, and interaction with other tribes. They can travel rivers and oceans, but must always stay near shore. Boats can carry one unit and do ancient commerce: building Wonders or setting up Trade Routes. Early bartering may help you advance faster or create relations with ancient neighbors.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3284,14 +3320,11 @@
Boat
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
➤ Never imposes a zone of control.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Boat.
@@ -3326,13 +3359,11 @@
Trireme
Firepower: 1
Hitpoints: 10
Obsolete by: Galley
-
Triremes are used for exploration, transport, and Commerce. The entry on Caravan explains what Commerce units may do.
+
Triremes are used for exploration, transport, and Commerce. The entry on Caravan explains what Commerce units may do.
-
-Triremes can enter Deep Ocean, but there is risk: they must end every second turn on river, coastline, or a city—or else be lost at sea. They can attack and travel on rivers, but cannot attack the shore.
+Triremes can enter Deep Ocean, but there is risk: they must end every second turn on river, coastline, or a city—or else be lost at sea. They can attack and travel on rivers, but cannot attack the shore.
-
-Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in a victor. Triremes do not cause unhappiness.
+Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in a victor. Triremes do not cause unhappiness.➤ Belongs to Trireme unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3343,14 +3374,12 @@
Trireme
➤ Can carry and refuel up to 2 Land, LandNoKill, or LandAirSea units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Unit must be next to safe coast, in a city or a base after 2 turns.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Trireme.
@@ -3386,10 +3415,9 @@
Longboat
Firepower: 1
Hitpoints: 10
Obsolete by: Caravel
-
The Longboat is a warship. It can attack at sea, attack the shore, and carry one land unit. It is useful for ancient sea campaigns. Longboats can travel on rivers.
+
The Longboat is a warship. It can attack at sea, attack the shore, and carry one land unit. It is useful for ancient sea campaigns. Longboats can travel on rivers.
-
-Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3401,13 +3429,14 @@
Longboat
➤ Can carry and refuel 1 Land, LandNoKill, or LandAirSea unit.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Caravel or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Zeppelins:
+Veteran Levels for Longboat:
Veteran level
Power factor
Promotion Odds
Move bonus
@@ -3432,10 +3461,9 @@
Galley
Firepower: 1
Hitpoints: 10
Obsolete by: Caravel
-
The Galley is an all-purpose sea unit and upgrades the Trireme. It has decent combat strength, can transport, and is a Commerce unit. The entry on Caravan explains what Commerce units may do. Galleys can attack and travel on rivers, but cannot attack the shore. Galleys do not cause unhappiness.
+
The Galley is an all-purpose sea unit and upgrades the Trireme. It has decent combat strength, can transport, and is a Commerce unit. The entry on Caravan explains what Commerce units may do. Galleys can attack and travel on rivers, but cannot attack the shore. Galleys do not cause unhappiness.
-
-Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3447,13 +3475,11 @@
Galley
➤ Can only attack units on native tiles.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Galley.
@@ -3489,10 +3515,9 @@
War Galley
Firepower: 1
Hitpoints: 10
Obsolete by: Caravel
-
The War Galley has improved offense, defense, and cargo capacity, but lacks commerce ability. War Galleys can attack and travel on rivers, and do shore attacks from the sea.
+
The War Galley has improved offense, defense, and cargo capacity, but lacks commerce ability. War Galleys can attack and travel on rivers, and do shore attacks from the sea.
-
-Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
+Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3504,6 +3529,7 @@
War Galley
➤ Can carry and refuel up to 3 Land, LandNoKill, or LandAirSea units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Caravel or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3535,7 +3561,7 @@
Ram Ship
Firepower: 1
Hitpoints: 10
Obsolete by: Caravel
-
The Ram Ship has one purpose:to destroy other ships. It has poor vision, and no transport or commerce ability. Unlike other early ships, there will always be a winner and a loser when Ram Ships attack. They are good for first-strike, and are good counter units for nations not invested into sea tech. Ram Ships cannot do shore attacks.
+
The Ram Ship has one purpose:to destroy other ships. It has poor vision, and no transport or commerce ability. Unlike other early ships, there will always be a winner and a loser when Ram Ships attack. They are good for first-strike, and are good counter units for nations not invested into sea tech. Ram Ships cannot do shore attacks.➤ Belongs to Sea unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3545,6 +3571,7 @@
Ram Ship
➤ Can only attack units on native tiles.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Caravel or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3577,10 +3604,9 @@
Caravel
Firepower: 1
Hitpoints: 10
Obsolete by: Galleon
-
Caravels upgrade older sea units, and are a big advance in combat, speed, range, and transport capacity. They have the commerce ability of the Galley, but cannot build Wonders. Caravels can attack and travel on rivers and do shore attacks. As a Commerce unit, the Caravel does not cause unhappiness.
+
Caravels upgrade older sea units, and are a big advance in combat, speed, range, and transport capacity. They have the commerce ability of the Galley, but cannot build Wonders. Caravels can attack and travel on rivers and do shore attacks. As a Commerce unit, the Caravel does not cause unhappiness.
-
-Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3592,13 +3618,11 @@
Caravel
➤ Can carry and refuel up to 3 Land, LandNoKill, or LandAirSea units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Upgrade
• upgrades to Galleon or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3630,13 +3654,11 @@
Galleon
Firepower: 1
Hitpoints: 20
Obsolete by: Transport
-
The Galleon is a heavily armed transport ship that can carry up to 4 units. Galleons can attack and travel on rivers, and also do shore attacks. Galleon crews repair their ship an extra +1hp per turn, regardless of whether it has moved. Although the Galleon is not a Commerce unit, it retains one vestige of the commercial abilities for the ships it upgrades. It can enter Peace waters to deliver commercial cargo, and does not cause unhappiness.
+
The Galleon is a heavily armed transport ship that can carry up to 4 units. Galleons can attack and travel on rivers, and also do shore attacks. Galleon crews repair their ship an extra +1hp per turn, regardless of whether it has moved. Although the Galleon is not a Commerce unit, it retains one vestige of the commercial abilities for the ships it upgrades. It can enter Peace waters to deliver commercial cargo, and does not cause unhappiness.
-
-Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
-
-Galleons can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining moves than the attacker.
+Galleons can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining moves than the attacker.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3649,6 +3671,7 @@
Galleon
➤ Can carry and refuel up to 4 Land, LandNoKill, LandAirSea, or Balloon units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can Upgrade
• upgrades to Transport.
@@ -3681,10 +3704,9 @@
Frigate
Firepower: 1
Hitpoints: 20
Obsolete by: Ironclad
-
The Frigate is versatile — it's a superior offensive unit and also a decent transport ship. Frigates can attack and travel on rivers, or make shore attacks from sea. Frigate crews repair their ship an extra +1hp per turn, regardless of whether it has moved.
+
The Frigate is versatile — it's a superior offensive unit and also a decent transport ship. Frigates can attack and travel on rivers, or make shore attacks from sea. Frigate crews repair their ship an extra +1hp per turn, regardless of whether it has moved.
-
-From Frigates onward, all sea attacks end with a single victor. Frigates can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining move points than the attacker.
+From Frigates onward, all sea attacks end with a single victor. Frigates can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining move points than the attacker.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3698,6 +3720,7 @@
Frigate
➤ Can carry and refuel up to 2 Land, LandNoKill, or LandAirSea units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can Upgrade
• upgrades to Ironclad or, when possible, to the unit type it upgrades to.
@@ -3730,7 +3753,7 @@
Cargo Ship
Firepower: 1
Hitpoints: 30
Obsolete by: None
-
Cargo ships are Commerce units: they can establish Trade Routes and help build Wonders. They can travel rivers and oceans. Cargo ships can also carry four land units: thus, they are useful for transporting units by river or sea.
+
Cargo ships are Commerce units: they can establish Trade Routes and help build Wonders. They can travel rivers and oceans. Cargo ships can also carry four land units: thus, they are useful for transporting units by river or sea.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -3745,14 +3768,11 @@
Cargo Ship
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Cargo Ship.
@@ -3772,7 +3792,7 @@
Transport
Firepower: 1
Hitpoints: 30
Obsolete by: None
-
The Transport cannot attack but can defend itself when under attack. Marines and Anti-Aircraft Artillery can directly attack from a Transport. Its large cargo capacity makes it a significant logistical upgrade for sea transportation. The Transport is considered a military vessel: unwelcome in the waters of nations with whom you are at Peace. Transports do not cause unhappiness.
+
The Transport cannot attack but can defend itself when under attack. Marines and Anti-Aircraft Artillery can directly attack from a Transport. Its large cargo capacity makes it a significant logistical upgrade for sea transportation. The Transport is considered a military vessel: unwelcome in the waters of nations with whom you are at Peace. Transports do not cause unhappiness.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
@@ -3784,6 +3804,7 @@
Transport
➤ Can carry and refuel up to 8 Land, LandNoKill, LandAirSea, or Balloon units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working its tile.
➤ Can Pillage
➤ May acquire veteran status.
• Veterans have increased strength in combat.
@@ -3813,10 +3834,9 @@
Ironclad
Firepower: 1
Hitpoints: 30
Obsolete by: Destroyer
-
The Ironclad is an armored ship that is much more sturdy than the Frigate but loses transport capability and the ability to navigate rivers. From the Ironclad onward, warships are excellent at attacking shore targets, and can also pillage buoys.
+
The Ironclad is an armored ship that is much more sturdy than the Frigate but loses transport capability and the ability to navigate rivers. From the Ironclad onward, warships are excellent at attacking shore targets, and can also pillage buoys.
-
-Ironclads and all modern ships can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining move points than the attacker.
+Ironclads and all modern ships can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining move points than the attacker.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
@@ -3830,6 +3850,7 @@
Ironclad
➤ Unable to attack air units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Upgrade
@@ -3864,7 +3885,7 @@
Destroyer
Firepower: 1
Hitpoints: 30
Obsolete by: Missile Destroyer
-
Destroyers are the first of the modern ships to start your modern navy. Their main roles are fast scouting, seek-and-destroy, anti-submarine warfare, shore bombardment of lighter targets, and supporting the needs of larger fleets. 4× ASW defence gives a 35% chance defending vs Submarines.
+
Destroyers are the first of the modern ships to start your modern navy. Their main roles are fast scouting, seek-and-destroy, anti-submarine warfare, shore bombardment of lighter targets, and supporting the needs of larger fleets. 4× ASW defence gives a 35% chance defending vs Submarines.
Destroyers can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining move points than the attacker.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
@@ -3880,6 +3901,7 @@
Destroyer
➤ Unable to attack air units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Upgrade
@@ -3914,8 +3936,8 @@
Cruiser
Firepower: 2
Hitpoints: 30
Obsolete by: AEGIS Cruiser
-
The Cruiser is a large and fast surface warship with equally strong offensive and defensive strength. More often than not, its 2× ASW defence allows it to survive a Submarine attack.
-The main duties of the Cruiser are offensive strikes against lesser ships, and escorting weaker ships.
+
The Cruiser is a large and fast surface warship with equally strong offensive and defensive strength. More often than not, its 2× ASW defence allows it to survive a Submarine attack.
+The main duties of the Cruiser are offensive strikes against lesser ships, and escorting weaker ships.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
@@ -3930,6 +3952,7 @@
Cruiser
➤ Unable to attack air units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Upgrade
@@ -3964,7 +3987,7 @@
Missile Destroyer
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
The Missile Destroyer has double the firepower of a Destroyer, and shares the same roles as its ancestor. It gains 2× defense against Air and Missile units. It has a 2× ASW bonus and can can carry one Missile.
+
The Missile Destroyer has double the firepower of a Destroyer, and shares the same roles as its ancestor. It gains 2× defense against Air and Missile units. It has a 2× ASW bonus and can carry one Missile.
Missile Destroyers can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining move points than the attacker.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
@@ -3982,6 +4005,7 @@
Missile Destroyer
➤ Can carry and refuel 1 Missile unit.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
@@ -4014,8 +4038,8 @@
AEGIS Cruiser
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
The AEGIS Cruiser has an Anti-Air missile system. Radar and sonar give superior vision and 2× defense bonus against Submarines. The AEGIS can carry two Missiles, and is the only ship that can attack Air and Missile units.
-Excellent vision and 3× Anti-Air bonus are ideal for scouting and escorting.
+
The AEGIS Cruiser has an Anti-Air missile system. Radar and sonar give superior vision and 2× defense bonus against Submarines. The AEGIS can carry two Missiles, and is the only ship that can attack Air and Missile units.
+Excellent vision and 3× Anti-Air bonus are ideal for scouting and escorting.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
@@ -4032,6 +4056,7 @@
AEGIS Cruiser
➤ Can carry and refuel up to 2 Missile units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Aircraft, Helicopter, or Missile units, which are usually not reachable.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
@@ -4064,7 +4089,7 @@
Battleship
Firepower: 2
Hitpoints: 40
Obsolete by: None
-
The Battleship is the supreme naval unit with excellent offensive and defensive strength. It has a 2× defense bonus vs Submarines. Battleships may expend 5 move points to do a 3 round bombardment which hits up to 4 units within a stack, of which only a maximum of one can be killed. Defending Battleships get a free bombardment against Special Unit Attacks.
+
The Battleship is the supreme naval unit with excellent offensive and defensive strength. It has a 2× defense bonus vs Submarines. Battleships may expend 5 move points to do a 3 round bombardment which hits up to 4 units within a stack, of which only a maximum of one can be killed. Defending Battleships get a free bombardment against Special Unit Attacks.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 3 hit points.
• Speed is not affected by terrain.
@@ -4079,6 +4104,7 @@
Battleship
➤ Unable to attack air units.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 3 free rounds of combat against up to 4 units in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -4119,9 +4145,9 @@
Submarine
Firepower: 2
Hitpoints: 28
Obsolete by: None
-
Submarines are strong attackers but weak defenders. They can carry 8 Missiles. They are unreachable by Air units, but do not block air attacks on surface ships. Submarines cannot attack units on shore. Submarines cannot be seen by other players unless they have a unit or city adjacent to it.
+
Submarines are strong attackers but weak defenders. They can carry 8 Missiles. They are unreachable by Air units, but do not block air attacks on surface ships. Submarines cannot attack units on shore. Submarines cannot be seen by other players unless they have a unit or city adjacent to it.
-Submarines have superior attrition rates when attacking in numbers. They excel at hit-and-run against weaker ships.
+Submarines have superior attrition rates when attacking in numbers. They excel at hit-and-run against weaker ships.
Submarines can Stack-Escape:a 75% chance to escape a killed stack if they have more remaining move points than the attacker.➤ Belongs to Submarine unit class.
• Invisible except when next to an enemy unit or city.
@@ -4141,6 +4167,7 @@
Submarine
➤ Can only attack units on native tiles.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Pillage
@@ -4173,8 +4200,8 @@
Carrier
Firepower: 2
Hitpoints: 40
Obsolete by: None
-
The Carrier is a mobile airport that can carry Air units, Helicopters, AAA, Marines, and Missiles. Fighters can Vigil on a Carrier if they have enough moves and the Carrier has not moved since the Vigil order was given.
-Carriers are very expensive and thus, usually protected by a fleet of scouts and escorts.
+
The Carrier is a mobile airport that can carry Air units, Helicopters, AAA, Marines, and Missiles. Fighters can Vigil on a Carrier if they have enough moves and the Carrier has not moved since the Vigil order was given.
+Carriers are very expensive and thus, usually protected by a fleet of scouts and escorts.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 3 hit points.
• Speed is not affected by terrain.
@@ -4185,10 +4212,11 @@
Carrier
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 9 Missile, LandAirSea, Helicopter, Air, AirPillage, AirProtect, or Balloon units.
+➤ Can carry and refuel up to 10 Missile, LandAirSea, Helicopter, Air, Air_High_Altitude, AirProtect, or Balloon units.
➤ Can only attack units on native tiles.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
@@ -4221,14 +4249,14 @@
Cruise Missile
Firepower: 3
Hitpoints: 10
Obsolete by: None
-
A Cruise Missile can strike distant targets. It cannot attack Air units, but Air units never block a strike on reachable surface units. Cruise Missiles can be relocated by ending their turn on a city, airbase, Submarine, Missile Destroyer, AEGIS Cruiser, Carrier, or Mobile SAM. A Cruise Missile cannot block attacks on other units on its tile.
+
A Cruise Missile can strike distant targets. It cannot attack Air units, but Air units never block a strike on reachable surface units. Cruise Missiles can be relocated by ending their turn on a city, airbase, Submarine, Missile Destroyer, AEGIS Cruiser, Carrier, or Mobile SAM. A Cruise Missile cannot block attacks on other units on its tile.➤ Belongs to Missile unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
➤ AEGIS and Armor II have a defense bonus against this unit.
@@ -4257,14 +4285,14 @@
Atom Bomb
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
If you have Nuclear Fission and the Manhattan Project has been built by any player, you can make Atom Bombs. A Bomber can drop it within two tiles of its location. Unload the bomb and fly the Bomber out of range, then attack the target (or detonate by pressing D twice.) The blast destroys ALL cardinally adjacent tiles. City population is reduced by half. Land tiles may get nuclear fallout.
-Fallout reduces tile output and increases risk of nuclear winter.
+
If you have Nuclear Fission and the Manhattan Project has been built by any player, you can make Atom Bombs. A Bomber can drop it within two tiles of its location. Unload the bomb and fly the Bomber out of range, then attack the target (or detonate by pressing D twice.) The blast destroys ALL cardinally adjacent tiles. City population is reduced by half. Land tiles may get nuclear fallout.
+Fallout reduces tile output and increases risk of nuclear winter.➤ Belongs to Bomb unit class.
• Speed is not affected by terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
➤ Can perform a Nuclear Detonation obliterating all cardinally adjacent tiles.
@@ -4294,16 +4322,16 @@
Hydrogen Bomb
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Hydrogen Bombs make Atom Bombs seem tame. Any unprotected city at Ground Zero will be absolutely annihilated. In other cities in the blast radius, population is reduced by about 75%. Blast area is 21 tiles: the same area as an entire city (5×5 minus the corners.) All units within the blast are destroyed.
+
Hydrogen Bombs make Atom Bombs seem tame. Any unprotected city at Ground Zero will be absolutely annihilated. In other cities in the blast radius, population is reduced by about 75%. Blast area is 21 tiles: the same area as an entire city (5×5 minus the corners.) All units within the blast are destroyed.
A Bomber can drop a Hydrogen Bomb within two tiles of its location. Unload the bomb, fly the Bomber out of range, then detonate by pressing D twice.
If you have Fusion Power and the Manhattan Project was built, a Hydrogen Bomb can be made in a city with an Enrichment Facility.
-Fallout from a Hydrogen Bomb is grave, with more than double the contaminated area.
+Fallout from a Hydrogen Bomb is grave, with more than double the contaminated area.➤ Belongs to Bomb unit class.
• Speed is not affected by terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can only be built if there is Enrichment Facility in the city.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
@@ -4334,16 +4362,16 @@
Doomsday Bomb
Firepower: 1
Hitpoints: 30
Obsolete by: None
-
The Doomsday Bomb is the mother of all bombs. Blast area is about 100 tiles. Only one can be made at a time. A city at Ground Zero will be annihilated. Other cities in the blast area lose about 75% of their population. All units within the blast are completely destroyed.
+
The Doomsday Bomb is the mother of all bombs. Blast area is about 100 tiles. Only one can be made at a time. A city at Ground Zero will be annihilated. Other cities in the blast area lose about 75% of their population. All units within the blast are completely destroyed.
A Bomber can drop the Bomb within two tiles of its location. Unload the Bomb and pray that you can fly the Bomber out of range. Detonate by pressing D twice.
If you have Fusion Power and the Manhattan Project was built, a Doomsday Bomb can be made in a city with an Enrichment Facility.
-The Fallout from a Doomsday Bomb is a sure ticket to Nuclear Winter.
+The Fallout from a Doomsday Bomb is a sure ticket to Nuclear Winter.➤ Belongs to Bomb unit class.
• Speed is not affected by terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can only be built if there is Enrichment Facility in the city.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
@@ -4376,15 +4404,15 @@
Nuclear Missile
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
If you have Space Flight and the Manhattan Project has been built by any player, you can make Nuclear Missiles. Nuclear blasts destroy ALL units in a 3×3 area. City population is reduced by half. Land tiles may get nuclear fallout. Fallout reduces tile output and increases risk of nuclear winter.
-If Fallout is cleaned on the turn it appears, the chance of nuclear winter is reduced.
+
If you have Space Flight and the Manhattan Project has been built by any player, you can make Nuclear Missiles. Nuclear blasts destroy ALL units in a 3×3 area. City population is reduced by half. Land tiles may get nuclear fallout. Fallout reduces tile output and increases risk of nuclear winter.
+If Fallout is cleaned on the turn it appears, the chance of nuclear winter is reduced.➤ Belongs to Missile unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
➤ Can perform a Nuclear Detonation obliterating all adjacent tiles.
@@ -4414,7 +4442,7 @@
Tactical Nuke
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Tactical Nukes are very small warheads attached to missiles. A Tactical Nuke destroys all units on a single tile. City population is reduced by half. The target tile may get Fallout. In theory, Tactical Nukes are small enough to get the advantages of nuclear armaments without the severity of consequences. In reality, they might be a gateway that escalates toward mutual assured destruction.
+
Tactical Nukes are very small warheads attached to missiles. A Tactical Nuke destroys all units on a single tile. City population is reduced by half. The target tile may get Fallout. In theory, Tactical Nukes are small enough to get the advantages of nuclear armaments without the severity of consequences. In reality, they might be a gateway that escalates toward mutual assured destruction.
If you have Fusion Power and the Manhattan Project was built, a Tactical Nuke can be made in a city with an Enrichment Facility. ➤ Belongs to Missile unit class.
• Speed is not affected by terrain.
@@ -4422,7 +4450,7 @@
Tactical Nuke
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can only be built if there is Enrichment Facility in the city.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
@@ -4451,7 +4479,7 @@
Diplomat
Attack: 0
Defense: 0
Firepower: 1
-
Hitpoints: 10
+
Hitpoints: 8
Obsolete by: Spy
A Diplomat performs official or covert actions. Covert actions make incidents which let Senates break treaties. Diplomats in cities may defend such acts with diplomatic combat. Diplomats can • Bribe a lone unit • Establish embassy • Investigate City • Sabotage random production or buildings • Steal tech • Steal maps • Incite city revolt.
Except for Bribing and Investigate City, diplomatic actions will spend the Diplomat, making it unavailable for further use. Diplomats may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Foreign tile claims require the Diplomat be accompanied by another unit.
@@ -4468,7 +4496,7 @@
Diplomat
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can do Diplomatic Operations:
• uses up the Diplomat.
• is done to foreign cities.
@@ -4518,10 +4546,10 @@
Spy
Attack: 0
Defense: 0
Firepower: 1
-
Hitpoints: 10
+
Hitpoints: 8
Obsolete by: None
-
Spies can do what a Diplomat can, and also: • Survive ops • Sabotage lone units • Poison city water • Steal specific tech • Steal tech from cities more than once • Sabotage specific targets in cities.
-Spies who survive ops escape to the nearest friendly city. Spies have a 25% advantage over Diplomats in combat: a base 75% chance to win. Spies may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Tile claims require the Spy to be accompanied by a military unit. Full rules are in the Manual.
+
Spies can do what a Diplomat can, and also: • Survive ops • Sabotage lone units • Poison city water • Steal specific tech • Steal tech from cities more than once • Sabotage specific targets in cities.
+Spies who survive ops escape to the nearest friendly city. Spies have a 25% advantage over Diplomats in combat: a base 75% chance to win. Spies may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Tile claims require the Spy to be accompanied by another unit. Full rules are in the Manual.➤ Belongs to Land unit class.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
@@ -4534,7 +4562,7 @@
Spy
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can do Diplomatic Operations:
• is done to foreign cities.
* 'Establish Embassy'
@@ -4594,7 +4622,6 @@
Caravan
Compared to other early Commerce units, the Caravan is the only one who can travel by land off roads and rails, and cause no military threat to trade partners. It can defend itself and also fortify. They have 2 7/9 moves.
Commerce units can help build Wonders in any city with whom you are not at war. In foreign cities 20+ tiles distant, they can create Trade Routes, increasing net trade in both cities relative to their combined trade. Routes are inactive during war and (re)activate during peace.
-Commerce units can enter the Marketplace of non-hostile foreign cities to sell their wares:revenue derives from total trade in both cities. This is only profitable between cities with very high trade.
Commerce units can build any Wonders for which you have the tech requirement. To use them for a Trade Route, they need to have given a Home City.
All Commerce units can carry Goods as cargo.➤ Belongs to Land unit class.
@@ -4604,19 +4631,15 @@
Caravan
➤ Can carry up to 2 Cargo units.
➤ Never imposes a zone of control.
➤ Not subject to zones of control imposed by other units.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Caravan.
@@ -4647,10 +4670,9 @@
Wagon
Firepower: 1
Hitpoints: 10
Obsolete by: Truck
-
Wagons are the first Commerce unit that can travel by land. Compared to a Caravan, they are defenseless. They must stay on roads and quays. Wagons are useful for transporting slower units. Wagons have 3⅓ move points, giving a range of 10 tiles on roads.
+
Wagons are the first Commerce unit that can travel by land. Compared to a Caravan, they are defenseless. They must stay on roads and quays. Wagons are useful for transporting slower units. Wagons have 3⅓ move points, giving a range of 10 tiles on roads.
-
-Wagons allow units with less than 3 move points to travel your roads faster. Any cargo can board a Wagon anywhere; but needs a city, quay, or base to deboard. Cargo can disembark anywhere, but it uses up all move points.
+Wagons allow units with less than 3 move points to travel your roads faster. Any cargo can board a Wagon anywhere; but needs a city, quay, or base to deboard. Cargo can disembark anywhere, but it uses up all move points.
Read the help on Caravans to see what Commerce units can do.➤ Belongs to LandRoad unit class.
• Only able to move on road tiles.
@@ -4667,15 +4689,12 @@
Wagon
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Wagon.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Wagon.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Capturable: can be captured if conditions allow it.
➤ Expellable: can be expelled from foreign tiles.
➤ Can Upgrade
@@ -4695,7 +4714,7 @@
Train
Firepower: 1
Hitpoints: 30
Obsolete by: None
-
Trains are Commerce units with a bonus:they allow units with less than 3 move points to travel your rails at distances comparable to other units. Transport logistics for Trains are more restricted than for Wagons and Trucks. Except for Foot soldiers, all units need to be in a city to get on or off a Train.
+
Trains are Commerce units with a bonus:they allow units with less than 3 move points to travel your rails at distances comparable to other units. Transport logistics for Trains are more restricted than for Wagons and Trucks. Except for Foot soldiers, all units need to be in a city to get on or off a Train.
Foot Soldiers use Trains the same as Wagons and Trucks, and can:
1. Board anywhere;
2. Deboard in a Base or Quay.;
@@ -4720,15 +4739,12 @@
Train
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Train.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Train.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Capturable: can be captured if conditions allow it.
➤ Will never achieve veteran status.
@@ -4745,9 +4761,9 @@
Truck
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
The Truck replaces the Caravan as the basic Commerce unit on Land. It moves at more than twice the speed; but is restricted to the same roads and quays as Wagons, and can't use rails. Each Truck used to build a Wonder will add 50 shields. See the entry on Caravan to read what Commerce units may do.
+
The Truck replaces the Caravan as the basic Commerce unit on Land. It moves at more than twice the speed; but is restricted to the same roads and quays as Wagons, and can't use rails. Each Truck used to build a Wonder will add 50 shields. See the entry on Caravan to read what Commerce units may do.
-Trucks allow units with less than 3 move points to travel your roads faster: Like the Wagon, any cargo can board a Truck anywhere, but a city, quay, or base is needed to deboard. Disembarking is legal for all cargo types, but uses up all moves (except Marines.)
+Trucks allow units with less than 3 move points to travel your roads faster: Like the Wagon, any cargo can board a Truck anywhere, but a city, quay, or base is needed to deboard. Disembarking is legal for all cargo types, but uses up all moves (except Marines.)➤ Belongs to LandRoad unit class.
• Can only travel on roads.
• Slowed down while damaged.
@@ -4755,23 +4771,15 @@
Truck
• Units with less than 3 moves: e.g., Foot Soldier, Artillery, Workers, Caravan, Freight, Bomb units.
• Some cargo cannot be loaded/unloaded except in a City.
➤ Never imposes a zone of control.
-➤ Not subject to zones of control imposed by other units.
-➤ Can Help Build Wonder
- • is done in friendly cities. Contributes the unit's shield cost to current production
- • uses up the Truck.
- • adds 35 production.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Truck.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Truck.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Truck.
@@ -4795,9 +4803,9 @@
Goods
Hitpoints: 1
Obsolete by: Freight
Goods are commodities. The cargo containers of the ancient world were amphorae, which carried for trade goods.
-Goods can be used to establish Trade Route or Enter Marketplace. They can be carried by Commerce units, allowing you to establish multiple trade routes in one voyage. Also, Goods can Recycle Production with only a 25% penalty, rendering 19 shields into a city production target.
+Goods can be used to establish Trade Route. They can be carried by Commerce units, allowing you to establish multiple trade routes in one voyage. Also, Goods can Recycle Production with only a 25% penalty, rendering 19 shields into a city production target.
-By themselves, Goods can only move onto tiles with a City, Quay, Fortress, or Naval Base. Commerce units, Tribesmen, and Galleons may carry them as cargo. If adjacent to a foreign city, use the D command (D) for Commerce functions.
+By themselves, Goods can only move onto tiles with a City, Quay, Fortress, Airbase, or Naval Base. Commerce units, Tribesmen, and Galleons may carry them as cargo. If adjacent to a foreign city, use the D command (D) for Commerce functions.➤ Belongs to Cargo unit class.
• Is NOT a unit. It is goods and materials for production and commerce.
• Only able to move onto Cities, Quays, Fortresses, and Naval Bases.
@@ -4815,9 +4823,6 @@
Goods
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Goods.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Goods.
➤ Can Upgrade
• upgrades to Freight.
➤ Capturable: can be captured if conditions allow it.
@@ -4836,38 +4841,31 @@
Freight
Firepower: 1
Hitpoints: 1
Obsolete by: None
-
The methodical logistics of modern shipping render the Freight unit: a cargo container separate from the transport vehicle. Freight brings benefits of organized efficiency. Like all Commerce units, it can Help Build Wonders, and Establish Trade Route or Enter Marketplace (with a lower unit cost.) Unlike other Commerce units, it can Disband to Recycle Production with no net loss in shields. This gives Freight a variety of creative industrial uses.
+
The methodical logistics of modern shipping render the Freight unit: a cargo container separate from the transport vehicle. Freight brings benefits of organized efficiency. Like all Commerce units, it can Help Build Wonders, and Establish Trade Route. Unlike other Commerce units, it can Disband to Recycle Production with no net loss in shields. This gives Freight a variety of creative industrial uses.
-By itself, Freight is only able to move to tiles with loading infrastructure: Cities, Quays, Fortresses, and Naval Bases. To be transported, it must be carried by Train, Truck, Airplane, or ship. If adjacent to a foreign city, use the D key to do Commerce functions.
+By itself, Freight is only able to move to tiles with loading infrastructure: Cities, Quays, Fortresses, Airbases, and Naval Bases. To be transported, it must be carried by Train, Truck, Airplane, or ship. If adjacent to a foreign city, use the D key to do Commerce functions.➤ Belongs to Cargo class.
• Is NOT a unit. It is goods and materials for production and commerce.
• Only able to move onto Cities, Quays, Fortresses, and Naval Bases.
• Pays no penalty for recycling production into Units, Buildings, or Wonders.
• Subject to zones of control.
• Can't Fortify.
- • Can be carried by Train, Truck, Cargo Ship, Transport.
+ • Can be carried by Train, Truck, Cargo Ship, Transport, Airplane.
➤ Never imposes a zone of control.
➤ Not subject to zones of control imposed by other units.
-➤ Can Help Build Wonder
- • is done in friendly cities. Contributes the unit's shield cost to current production
- • uses up the Freight.
- • adds 35 production.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Freight.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Freight.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Freight.
- • adds 50 production.
+ • adds 25 production.
➤ Capturable: can be captured if conditions allow it.
• does not count as an extra unit on tile to prevent capture.
@@ -4895,7 +4893,7 @@
Explorer
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Investigate City (does not spend the unit)
➤ Can Upgrade
• upgrades to Partisan.
@@ -4923,9 +4921,9 @@
Leader
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
This is you. If you lose this unit, you lose the game.
-Will not unleash barbarians from huts.
-You can use the DO command to change the gender of the Leader.
+
This is you. If you lose this unit, you lose the game.
+Will not unleash barbarians from huts.
+You can use the DO command to change the gender of the Leader.➤ Belongs to Land unit class.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
@@ -4943,7 +4941,7 @@
Leader
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Destroy City
• is done to domestic individual cities.
➤ Can Pillage
@@ -4967,9 +4965,9 @@
Queen
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
(Alternate form of Leader. Behaves identically in-game.) This is you. If you lose this unit, you lose the game.
-Will not unleash barbarians from huts.
-You can use the DO command to change the gender of the Leader.
+
(Alternate form of Leader. Behaves identically in-game.) This is you. If you lose this unit, you lose the game.
+Will not unleash barbarians from huts.
+You can use the DO command to change the gender of the Leader.➤ Belongs to Land unit class.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
@@ -4987,7 +4985,7 @@
Queen
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Destroy City
• is done to domestic individual cities.
➤ Can Pillage
@@ -5011,8 +5009,8 @@
Barbarian Leader
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
One Barbarian Leader appears every time there is a barbarian uprising somewhere in the world.
-When a Barbarian Leader is killed on a tile without any defending units, the 100 gold ransom is paid, but only to land units and helicopters.
+
One Barbarian Leader appears every time there is a barbarian uprising somewhere in the world.
+When a Barbarian Leader is killed on a tile without any defending units, the 100 gold ransom is paid, but only to land units and helicopters.➤ Belongs to Land unit class.
• Slowed down while damaged.
➤ May not be built in cities.
@@ -5025,7 +5023,7 @@
Barbarian Leader
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Pillage
➤ Can Fortify. Only makes the Barbarian Leader stay put.
➤ Can't be bribed.
@@ -5036,7 +5034,7 @@
Barbarian Leader
Peasants
Cost: 10 shields
-
Upkeep: 2 Food
+
Upkeep: 0
Moves: 2
Vision: 2.00 tiles
Attack: 0
@@ -5044,7 +5042,7 @@
Peasants
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Population Cost: 1. Adds Population: 1.
+
Population Cost: 1. Adds Population: 1.
Peasants are the common people of Monarchies. You can encourage them by royal edict to migrate to other cities and help colonize new areas. But to have such access, you must be a Constitutional Monarchy with the Magna Carta wonder: Peasants can only be made in the city with Magna Carta, so choose that city carefully.
If managed correctly, Peasants can increase your national population growth. They give a tremendous advantage for growing new colonies and settlements faster.
@@ -5062,7 +5060,7 @@
Peasants
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ Can Add to City
• is done to domestic individual cities.
diff --git a/freeciv-web/src/derived/webapp/man/mp2-dragoon6.bak.html b/freeciv-web/src/derived/webapp/man/mp2-dragoon6.bak.html
index d1845ee85..d58549776 100644
--- a/freeciv-web/src/derived/webapp/man/mp2-dragoon6.bak.html
+++ b/freeciv-web/src/derived/webapp/man/mp2-dragoon6.bak.html
@@ -1,4 +1,8 @@
-
+
+
+
+
+
@@ -11,20 +15,54 @@
Anarchy
Anarchy offers slightly less corruption than Despotism, but slightly more unhappiness.
-
+Celebrating cities cannot rapture.
No citizens are made unhappy by each aggressive unit.
-25% Base corruption in cities
-2% Extra corruption per each tile in distance from capital
-
+25% base corruption in cities
+2% extra corruption per each tile in distance from a capital
+
+
+City Happiness vs. Empire Size
+
+
Cities:
+
0-9
+
10-15
+
16-21
+
22-27
+
28-33
+
34-39
+
40-45
+
46-51
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
Features:
-• Trade production will suffer some losses.
-• Trade losses will increase with distance from capital.
• Each of your cities will avoid paying 3 Shield upkeep for your units.
• If you lose your capital, the base chance of civil war is 90%.
• You can have up to 9 cities before an additional unhappy citizen appears in each city due to civilization size.
• After the first unhappy citizen due to civilization size, for each 6 additional cities another unhappy citizen will appear.
-• Has unlimited science/gold/luxuries rates.
+• Science/gold/luxuries rates will be ignored, and all trade will be alotted to luxury.
• Your units may impose martial law. Each military unit inside a city will force 1 unhappy citizen to become content.
• Each worked tile that gives more than 2 Food, Shield, or Trade suffers a -1 penalty unless its city is celebrating. (Cities below size 3 will not celebrate.)
@@ -36,18 +74,57 @@
Despotism
Under Despotism, you are the absolute ruler of your people. Your control over your citizens is maintained largely by martial law. Under Despotism, tiles suffer a -1 tile output penalty if they produce 3 resource points.
-
Despotism suffers the highest level of corruption of all forms of government.
+Celebrating cities cannot rapture.
No citizens are made unhappy by each aggressive unit.
-37% Base corruption in cities
-4% Extra corruption per each tile in distance from capital
-Gulag:You can starve cities without disorder, with 2 martial law units.
+37% base corruption in cities
+4% extra corruption per each tile in distance from a capital
+Gulag:You can starve cities without disorder, with 2 martial law units or a Police Station.
+Authoritarian: Police Stations force an additional 2 citizens to be content.
+
+
+City Happiness vs. Empire Size
+
+
Cities:
+
0-10
+
11-20
+
21-30
+
31-40
+
41-50
+
51-60
+
61-70
+
71-80
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
Features:
+• Capital city (with Palace) gains +75% shield production.
• Newly founded cities get +2 food (+4 for capital.)
-• Trade production will suffer massive losses.
-• Trade losses will increase quickly with distance from capital.
+• Trade production suffers massive losses.
+• Trade losses increase quickly with distance from capital.
• Each of your cities will avoid paying 3 Shield upkeep for your units.
• If you lose your capital, the base chance of civil war is 80%.
• You can have up to 10 cities before an additional unhappy citizen appears in each city due to civilization size.
@@ -66,17 +143,54 @@
Monarchy
Monarchy suffers the same small amount of corruption that the Republic does.
-
+Celebrating cities cannot rapture.
No citizens are made unhappy by each aggressive unit.
-15% Base corruption in cities
-2% Extra corruption per each tile in distance from capital
+15% base corruption in cities
+2% extra corruption per each tile in distance from a capital
Requires knowledge of the technology Monarchy.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-11
+
12-23
+
24-35
+
36-47
+
48-59
+
60-71
+
72-83
+
84-95
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
Features:
-• Trade production will suffer some losses.
-• Trade losses will increase with distance from capital.
+• Capital city (with Palace) gains +50% shield production.
+• Trade losses increase with distance from capital.
• Each of your cities will avoid paying 3 Shield upkeep for your units.
+• Each of your cities will avoid paying 1 Gold upkeep for your units.
• If you lose your capital, the base chance of civil war is 70%.
• You can have up to 11 cities before an additional unhappy citizen appears in each city due to civilization size.
• After the first unhappy citizen due to civilization size, for each 12 additional cities another unhappy citizen will appear.
@@ -89,27 +203,100 @@
Monarchy
Constitutional Monarchy
Constitutional Monarchy is somewhat between Monarchy and representative government.
-
+Celebrating cities founded by you can rapture every turn.
+Celebrating cities not founded by you rapture 1 out of 4 turns.
+Celebrating cities not founded by you can rapture 3 out of 5 turns, if they have no foreign citizens.
1 citizen is made unhappy by each aggressive unit*.
(* first two aggressive units do not cause unhappy)
-15% Base corruption in cities
-2% Extra corruption per each tile in distance from capital
+15% base corruption in cities
+2% extra corruption per each tile in distance from a capital
Requires knowledge of the technology Monarchy and the Magna Carta Wonder.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-23
+
24-35
+
36-47
+
48-59
+
60-71
+
72-83
+
84-95
+
96-107
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
+
+ City Rapture Rates
+
+
Foreigners:
+
60%+
+
50%
+
40%
+
30%
+
20%
+
10%
+
0%
+
+
+
Original:
+
80%
+
80%
+
80%
+
80%
+
80%
+
80%
+
80%
+
+
+
Conquered:
+
25%
+
25%
+
25%
+
25%
+
25%
+
25%
+
60%
+
+
+
Features:
-• Trade production will suffer some losses.
-• Trade losses will increase with distance from capital.
+• Capital city (with Palace) gains +50% shield production.
+• Trade losses increase with distance from capital.
• Each of your cities will avoid paying 1 Shield upkeep for your units.
+• Each of your cities will avoid paying 1 Gold upkeep for your units.
• If you lose your capital, the base chance of civil war is 50%.
• You can have up to 23 cities before an additional unhappy citizen appears in each city due to civilization size.
• After the first unhappy citizen due to civilization size, for each 12 additional cities another unhappy citizen will appear.
• The maximum rate you can set for science, gold, or luxuries is 70%.
• One unit may impose martial law, forcing 1 unhappy citizen to become content.
• • •
-• +1 trade on each Land tile that produces trade.
-• Each worked Sea tile yields +1 more Trade while its city is celebrating. (Cities below size 3 will not celebrate.)
-• Cities originally founded by you can grow by means of rapture. (Cities below size 3 cannot grow in this way.)
+• When not celebrating, +1 Trade on each Land tile that produces trade.
+• When celebrating, +1 Trade on every tile that produces Trade. (Cities below size 3 will not celebrate.)
+• Celebrating cities can grow by rapture. (Cities below size 3 cannot grow in this way.)
• Military units away from home and field units will each cause 1 citizen to become unhappy.
• Each of your cities will avoid 2 unhappiness caused by aggressive units.
• Allows you to build Peasants in the city with Magna Carta.
@@ -124,17 +311,86 @@
Republic
Under a Republican government, citizens hold an election to select a representative who will govern them; since elected leaders must remain popular to remain in control, citizens are given a greater degree of freedom. Citizens under the Republic become unhappy easily, but the self-sufficiency of your citizens allows high levels of trade.
-
+Celebrating cities can rapture every turn.
1 citizen is made unhappy by each aggressive unit*.
(* first aggressive unit does not cause unhappy)
-15% Base corruption in cities
-2% Extra corruption per each tile in distance from capital
+15% base corruption in cities
+2% extra corruption per each tile in distance from a capital
Requires knowledge of the technology The Republic.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-13
+
14-27
+
28-41
+
42-55
+
56-69
+
70-83
+
84-97
+
98-111
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
+
+ City Rapture Rates
+
+
Foreigners:
+
60%+
+
50%
+
40%
+
30%
+
20%
+
10%
+
0%
+
+
+
Original:
+
90%
+
90%
+
90%
+
90%
+
90%
+
90%
+
90%
+
+
+
Conquered:
+
33%
+
33%
+
33%
+
33%
+
33%
+
33%
+
80%
+
+
+
Features:
-• Trade production will suffer some losses.
-• Trade losses will increase with distance from capital.
+• Trade losses increase with distance from capital.
• Each of your cities will avoid 1 unhappiness caused by units.
• You pay 2 times normal Food upkeep for your units.
• Military units away from home and field units will each cause 1 citizen to become unhappy.
@@ -142,7 +398,7 @@
Republic
• You can have up to 13 cities before an additional unhappy citizen appears in each city due to civilization size.
• After the first unhappy citizen due to civilization size, for each 14 additional cities another unhappy citizen will appear.
• The maximum rate you can set for science, gold, or luxuries is 80%.
-• You may grow your cities by means of rapture. (Cities below size 3 cannot grow in this way.)
+• Celebrating cities may grow by rapture. (Cities below size 3 cannot grow in this way.)
• Each worked tile with at least 1 Trade will yield +1 more Trade.
• Has a senate that may prevent declaration of war.
• May ignore senate if you have the Statue of Liberty.
@@ -155,23 +411,84 @@
Democracy
Under Democracy, citizens govern directly by voting on issues. Democracy offers the highest level of trade. Base corruption is half that of Republic or Monarchy, and corruption from distance to a capital is very low. Citizens become very upset during wars. Democracy revolts to Anarchy if any city remains in disorder for two turns. Cities and units belonging to Democracy have triple the cost to incite or bribe, respectively.
-
+Celebrating cities with less than 10% foreign citizens can rapture every turn.
+Celebrating cities with 10% or more foreign citizens can rapture 1 out of 2 turns.
2 citizens are made unhappy by each aggressive unit.
-8% Base corruption in cities
-0.4% Extra corruption per each tile in distance from capital
+8% base corruption in cities
+0.4% extra corruption per each tile in distance from a capital
Requires knowledge of the technology Democracy.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-14
+
15-30
+
31-46
+
47-62
+
63-78
+
79-94
+
95-110
+
111-126
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
+
+ City Rapture Rates
+
+
Foreigners:
+
60%+
+
50%
+
40%
+
30%
+
20%
+
10%
+
0%
+
+
+
All Cities:
+
25%
+
25%
+
25%
+
25%
+
50%
+
100%
+
100%
+
+
+
Features:
-• Trade production will suffer a small amount of losses.
-• Trade losses will increase slowly with distance from capital.
+• Trade production suffers small losses.
+• Trade losses increase very slowly with distance from capital.
• You pay 2 times normal Food upkeep for your units.
• Military units away from home and field units will each cause 2 citizens to become unhappy.
• If you lose your capital, the base chance of civil war is 30%.
• You can have up to 14 cities before an additional unhappy citizen appears in each city due to civilization size.
• After the first unhappy citizen due to civilization size, for each 16 additional cities another unhappy citizen will appear.
• Has unlimited science/gold/luxuries rates.
-• You may grow your cities by means of rapture. (Cities below size 3 cannot grow in this way.)
+• Celebrating cities can grow by rapture. (Cities below size 3 cannot grow in this way.)
• Each worked tile with at least 1 Trade will yield +1 more Trade.
• If a city is in disorder for more than 2 turns in a row, government will fall into anarchy.
• Has a senate that may prevent declaration of war.
@@ -185,35 +502,113 @@
Theocracy
Theocracy is formed on religious beliefs which are the law of the land.
-The people are devoted and often willing to die for their faith. Cities very near a capital have very low corruption. Cost for enemies to incite cities or bribe units is 2x, but Zealots cannot be bribed. Enemies cannot establish embassies without first making Ceasefire or Peace.
+The people are devoted and often willing to die for their faith. Each city has +1 happy citizen. Cities very near a capital have very low corruption. Cost for enemies to incite cities or bribe units is 2x, but Zealots cannot be bribed. Cost to incite foreign cities is reduced by a third. Enemies cannot establish embassies without first making Ceasefire or Peace.
-Improvements that appease unhappy citizens produce 1 gold in tithes for each citizen appeased. Tithes also boost gold income by +20%, but science output suffers -20%. Palace gives +50% to gold income in its city. In cities, two military units may impose martial law, affecting one citizen each. Pilgrims can be made to migrate and grow cities.
+Improvements that appease unhappy citizens add +1 base gold in tithes for each citizen appeased. Tithes also boost gold income by +20%, but science output suffers -20%. Palace gives +50% to gold income in its city. In cities, two military units may impose martial law, affecting one citizen each. Pilgrims can be made to migrate and grow cities.
+Having an Ecclesiastical Palace allows instant and orderly transition to this government.
+Celebrating cities can rapture 2 out of 5 turns.
+Celebrating cities can rapture 4 out of 7 turns if they have a Cathedral building present in the city.
+Each city gains +1 happy citizen.
No citizens are made unhappy by each aggressive unit.
-2% Base corruption in cities
-2% Extra corruption per each tile in distance from capital
+0% base corruption in cities
+1% extra corruption per each tile in distance from a capital
Requires knowledge of the technology Theocracy.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-14
+
15-29
+
30-44
+
45-59
+
60-74
+
75-89
+
90-104
+
105-119
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
+
+ City Rapture Rates
+
+
Foreigners:
+
60%+
+
50%
+
40%
+
30%
+
20%
+
10%
+
0%
+
+
+
No Cathedral:
+
40%
+
40%
+
40%
+
40%
+
40%
+
40%
+
40%
+
+
+
With Cathedral:
+
57%
+
57%
+
57%
+
57%
+
57%
+
57%
+
57%
+
+
+
Features:
-• Trade production will suffer a small amount of losses.
-• Trade losses will increase with distance from capital.
+• Capital city (with Palace) gains +50% gold production.
+• No trade losses from base corruption.
+• Trade losses only accrue based on distance to a capital, at a slow rate.
• You pay 2 times normal Food upkeep for your units.
• Each of your cities will avoid paying 10 Shield upkeep for your units.
+• Each of your cities will avoid paying 1 Gold upkeep for your units.
• You can have up to 14 cities before an additional unhappy citizen appears in each city due to civilization size.
• After the first unhappy citizen due to civilization size, for each 15 additional cities another unhappy citizen will appear.
• The maximum rate you can set for science, gold, or luxuries is 80%.
+• Celebrating cities can grow by rapture. (Cities below size 3 cannot grow in this way.)
• Your units may impose martial law. Each military unit inside a city will force 1 unhappy citizen to become content.
• A maximum of 2 units in each city can enforce martial law.
• Each worked tile with Trade will yield +1 more Trade while its city is celebrating. (Cities below size 3 will not celebrate.)
• Science production is decreased 20%.
-• Buildings and Wonders that make citizens content or happy give 1 base gold per citizen affected.
• Gold production is increased 20%.
+• Buildings and Wonders that make citizens content or happy give 1 base gold per citizen affected.
• Pays no upkeep for Pilgrims or Zealots.
-• Allows you to build Pilgrims.
-• Allows you to build Falconeers.
-• Allows you to build Zealots.
+• Allows Pilgrims.
+• Allows Falconeers.
+• Allows Patriarch (with Ecclesiastical Palace.)
+• Allows Zealots (with Conscription.)
@@ -223,33 +618,104 @@
Communism
In Communism, all work, output, and goods are owned by the state.
-Each city gets 4 shields of free unit upkeep. Buildings with 1 upkeep are free. The city with a Palace gets +25% production.
+Content cities get a +1 trade bonus on irrigated tiles that produce trade. Each city gets 4 shields and 3 gold of free unit upkeep. Buildings with 1 upkeep are free. The city with a Palace gets +40% production.
-Riflemen cost 5 less; Armor and Dive Bombers, 10 less. Diplomats and Spies get +1 vet level. The effect of Cathedrals and Michelangelo is reduced -1.
+Each city gets 4 shields of free unit upkeep. Buildings with 1 upkeep are free. The city with a Palace gets +40% production.
-Communists can transfer Proletarians between cities to re-distribute population, or use them as workers on State projects. The Communism tech allows a Communist government to convert Workers to Riflemen and vice versa, if in domestic territory and possessing the necessary tech.
+Riflemen cost 5 less; Armor and Dive Bombers, 10 less. Diplomats and Spies get +1 vet level. The effect of Cathedrals and Michelangelo is reduced -1. Each city gets the effects of a Supermarket for free.
+Communists can transfer Proletarians between cities to re-distribute population, or use them as workers on State projects. The Communism tech allows a Communist government to convert Workers to Riflemen and vice versa, if in domestic territory and possessing the necessary tech.
+Your nation is completely unaffected by empire size and number of cities.
+Celebrating cities can rapture 2 out of 5 turns.
+Celebrating cities which have a Granary gain +1 food output.
No citizens are made unhappy by aggressive units.
-20% Base corruption in cities
-No Extra corruption per each tile in distance from capital
-Gulag:You can starve cities without disorder, with 2 martial law units.
+20% base corruption in cities
+No extra corruption per each tile in distance from a capital
+Gulag:You can starve cities without disorder, with 2 martial law units or 1 martial law unit and a Police Station.
+Authoritarian: Police Stations force an additional 2 citizens to be content.
Requires knowledge of the technology Communism.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-∞
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
First Unhappy:
+
5
+
5
+
5
+
5
+
5
+
5
+
5
+
5
+
+
+
Extra Unhappy:
+
0
+
0
+
0
+
0
+
0
+
0
+
0
+
0
+
+
+
+
+ City Rapture Rates
+
+
Foreigners:
+
60%+
+
50%
+
40%
+
30%
+
20%
+
10%
+
0%
+
+
+
All Cities:
+
40%
+
40%
+
40%
+
40%
+
40%
+
40%
+
40%
+
+
+
Features:
-• Trade production will suffer some losses.
+• Capital city (with Palace) gains +40% shield production.
+• Trade production will suffer moderate losses.
+• Distance from capital does not increase trade losses.
• Each of your cities will avoid paying 4 Shield upkeep for your units.
+• Each of your cities will avoid paying 3 Gold upkeep for your units.
• If you lose your capital, the base chance of civil war is 50%.
-• You can have up to 12 cities before an additional unhappy citizen appears in each city due to civilization size.
+• You may have an unlimited number of cities without an increase in unhappy citizens.
• The maximum rate you can set for science, gold, or luxuries is 80%.
+• Celebrating cities can grow by rapture. (Cities below size 3 cannot grow in this way.)
+• When not celebrating, +1 trade on each irrigated tile that produces trade.
+• When celebrating, +1 trade on each tile that produces trade.
• Your units may impose martial law. Each military unit inside a city will force 2 unhappy citizens to become content.
• A maximum of 3 units in each city can enforce martial law.
-• New Diplomat units will be veteran.
+• New Emissary, Diplomat, and Spy units will be veteran.
• Each worked tile with Trade will yield +1 more Trade while its city is celebrating. (Cities below size 3 will not celebrate.)
• Cathedral and Michelangelo effects diminished by -1.
• Disqualified from all bonuses to Coinage.
-• Allows you to build Proletarians.
+• Allows Proletarians.
@@ -257,43 +723,118 @@
Communism
Nationalism
-
Nationalism glorifies national supremacy over other peoples. Zealous citizens and corporatations align under a dictator who rules over a unified populace, military, and isolationist economy.
+
Nationalism glorifies national supremacy over other peoples. Citizens and corporatations align under a dictator who rules over a unified populace, military, and isolationist economy.
-Content cities get a +1 trade bonus on Land tiles only. Celebrating cities also get Ocean tiles. Cities may rapture if founded under the original national hegemony. Luxury suffers a 15% penalty. Government mandated Science gets +15% in original cities.
+Content cities get a +1 trade bonus on Land tiles only. Celebrating cities also get Ocean tiles. Cities may rapture if founded under the original national hegemony. Luxury suffers a 15% penalty. Government mandated Science gets +20% in original cities.
Each city gets 1 shield of free unit upkeep. The city with a Palace gets +15% production. Original cities have a 3x incite cost, otherwise cost one third.
-Police Stations suppress +1 unhappy Foreign National and cost 10 shields less. Five military units may impose martial law, affecting one citizen each. Migrants can be made to help grow cities. Tile workers gain a ⅑ move bonus. Cost of upgrades is 25% less. Riflemen are produced at +1 vet level. Armor gets a +1 move bonus. Fighters and Dive Bombers have 50% higher odds of promotion.
+Police Stations cost 10 shields less. Five military units may impose martial law, affecting one citizen each. Migrants can be made to help grow cities. Tile workers gain a ⅑ move bonus. Cost of upgrades is 25% less. Riflemen are produced at +1 vet level. Armor gets a +1 move bonus. Fighters and Dive Bombers have 50% higher odds of promotion.
+Celebrating cities founded by you can rapture every turn.
+Celebrating cities not founded by you can rapture 1 out of 3 turns.
+Celebrating cities not founded by you can rapture 2 out of 3 turns, if they have no foreign citizens.
No citizens are made unhappy by each aggressive unit.
-8% Base corruption in capital,
-2% Extra corruption per each tile in distance from capital
+8% base corruption in capital,
+2% extra corruption per each tile in distance from a capital
+Authoritarian: Police Stations force an additional 2 citizens to be content.
Requires knowledge of the technology Nationalism.
+
+City Happiness vs. Empire Size
+
Cities:
+
0-20
+
21-32
+
33-44
+
45-56
+
57-68
+
69-80
+
81-92
+
93-104
+
+
+
First Unhappy:
+
5
+
4
+
3
+
2
+
1
+
+
+
+
+
+
Extra Unhappy:
+
0
+
+1
+
+2
+
+3
+
+4
+
+5
+
+6
+
+7
+
+
+
+
+ City Rapture Rates
+
+
Foreigners:
+
60%+
+
50%
+
40%
+
30%
+
20%
+
10%
+
0%
+
+
+
Original:
+
50%
+
50%
+
50%
+
50%
+
50%
+
50%
+
100%
+
+
+
Conquered:
+
33%
+
33%
+
33%
+
33%
+
33%
+
33%
+
67%
+
+
+
Features:
-• Trade production will suffer a small amount of losses.
-• Trade losses will increase with distance from capital.
+• Capital city (with Palace) gains +15% shield production.
+• Trade production suffers a small amount of losses.
+• Trade losses increase with distance from capital.
• You pay 2 times normal Food upkeep for your units.
• Each of your cities will avoid paying 1 Shield upkeep for your units.
• If you lose your capital, the base chance of civil war is 40%.
• You can have up to 20 cities before an additional unhappy citizen appears in each city due to civilization size.
• After the first unhappy citizen due to civilization size, for each 12 additional cities another unhappy citizen will appear.
• The maximum rate you can set for science, gold, or luxuries is 90%.
+• Celebrating cities can grow by rapture. (Cities below size 3 cannot grow in this way.)
• Your units may impose martial law. Each military unit inside a city will force 1 unhappy citizen to become content.
• A maximum of 5 units in each city can enforce martial law.
* New Riflemen units will have the rank of veteran.
* Fighter and Dive Bomber units are promoted 50% more frequently.
• Luxury production is decreased 15%.
-• Science production is increased 15% in original cities.
-• Cities originally founded by you can grow by means of rapture. (Cities below size 3 cannot grow in this way.)
-• +1 trade on each Land tile that produces trade.
+• Science production is increased 20% in original cities.
+• When not celebrating, +1 trade on each Land tile that produces trade.
• Each worked Sea tile yields +1 more Trade while its city is celebrating. (Cities below size 3 will not celebrate.)
-• Allows you to build Migrants.
+• Allows Migrants.
diff --git a/freeciv-web/src/derived/webapp/man/mp2-dragoon7.bak.html b/freeciv-web/src/derived/webapp/man/mp2-dragoon7.bak.html
index e4b2050ad..505f1a083 100644
--- a/freeciv-web/src/derived/webapp/man/mp2-dragoon7.bak.html
+++ b/freeciv-web/src/derived/webapp/man/mp2-dragoon7.bak.html
@@ -16,8 +16,7 @@
Founders
Firepower: 1
Hitpoints: 25
Obsolete by: None
-
At game start, Founders are a more populous portion of your nomadic tribe.
-
+
At game start, Founders are a more populous portion of your nomadic tribe.Founders are Settlers who found a city of size 2—usually the first city and capital of a tribe. They are a start unit that cannot be built. In all other respects they are exactly like Settlers, except they cannot be bribed.➤ Belongs to Land unit class.
• Subject to zones of control.
@@ -29,16 +28,16 @@
Founders
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, and Naval Base on tiles.
➤ Can clean Pollution from tiles.
➤ Can clean Fallout from tiles.
➤ Can Build City
- • uses up the Settlers.
- • initial population: 1.
+ • uses up the Founders.
+ • initial population: 2.
➤ Can Add to City
- • uses up the Settlers.
+ • uses up the Founders.
• max target size: 39.
• adds 1 population.
➤ Can Pillage
@@ -85,7 +84,7 @@
Settlers
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, and Naval Base on tiles.
➤ Can clean Pollution from tiles.
@@ -135,11 +134,12 @@
Tribesmen
• Slowed down while damaged.
• Can't attack as Cargo. Must first unload.
➤ A Start Unit only: may not be built in cities.
-➤ Can carry up to 1 Goods unit.
+➤ Can carry 1 Goods unit.
+ • Cargo not visible except to allies.
➤ Can build infrastructure until T20 (2000BC).
• Can Road, 2 turns. (4-6 turns for slower roaded terrain.)
• Can Mine, 10 turns.
- • Can Irrigate, 6 turns.
+ • Can Irrigate, 5 turns.
➤ Ignores terrain effects (moving costs at most ⅓ MP per tile).
➤ Never imposes a zone of control.
➤ Not subject to zones of control imposed by other units.
@@ -151,7 +151,7 @@
Tribesmen
• Can't conquer cities.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Investigate City (does not spend the unit)
➤ Can Recycle
• Must be done in a domestic city making a Building, Wonder, or Coinage.
@@ -175,52 +175,92 @@
Tribesmen
-
Well-Digger
+
Peasants
-
Cost: 5 shields
-
Upkeep: 2 Food, 5 Shield, 2 Gold
-
Moves: 3
+
Cost: 10 shields
+
Upkeep: 0
+
Moves: 2
Vision: 2.00 tiles
Attack: 0
Defense: 1
Firepower: 1
-
Hitpoints: 8
-
Obsolete by: Workers
-
This unit can fix unlucky starts, but has very high upkeep.
-If you have no water, make a Well-Digger in a city that can support it, create a water source, then disband the unit.
-This unit can dig wells to create irrigation sources. It will cost its home city -2 Food -2 Prod. This unit WILL NOT WORK: • Outside your borders, • After Alphabet or Pottery, • After any player discovers Writing, • After it reaches an age of 10 turns. >> Don't fool around: high upkeep will permanently hinder you. Only make this unit if you lack water when the game starts!
+
Hitpoints: 10
+
Obsolete by: None
+
Population Cost: 1. Adds Population: 1.
+
+Peasants are the common people of Monarchies. You can encourage them by royal edict to migrate to other cities and help colonize new areas. But to have such access, you must be a Constitutional Monarchy with the Magna Carta wonder: Peasants can only be made in the city with Magna Carta, so choose that city carefully.
+If managed correctly, Peasants can increase your national population growth. They give a tremendous advantage for growing new colonies and settlements faster.
+Peasants can only be made in one city, but you can make more than one per turn under the right conditions. When a city completes a unit, it will try to make more of them if it doesn’t have a worklist. So be careful!➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
-➤ Each player may only have one of this type of unit.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ Can only be built with Constitutional Monarchy as government.
+➤ Can only be built if there is Magna Carta in the city.
+➤ Costs 1 population to build.
+➤ May load onto and unload from Transport Helicopters even when underway.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
-➤ Can Dig Well or Irrigate tiles with no water.
-➤ Can clean Pollution from tiles.
-➤ Can clean Fallout from tiles.
-➤ Can Recycle Unit
- • is done in cities. Contributes half the unit's shield cost to current production
- • uses up the Well-Digger.
-➤ Can Disband
- • uses up the Well-Digger. Recycles cost if done in a city.
-➤ Can Upgrade
- • upgrades to Workers.
-➤ Can Pillage
-➤ Can Road
-➤ Can Irrigate
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
+➤ Can Add to City
+ • is done to domestic individual cities.
+ • uses up the Pilgrims.
+ • max target size: 39.
+ • adds 1 population.
+➤ Capturable: can be captured if conditions allow it.
➤ Expellable: can be expelled from foreign tiles.
+➤ Unable to pillage tiles.
➤ Will never achieve veteran status.
+
+
+
+
Pilgrims
+
+
Cost: 10 shields
+
Upkeep: 2 Food
+
Moves: 2
+
Vision: 2.00 tiles
+
Attack: 0
+
Defense: 1
+
Firepower: 1
+
Hitpoints: 10
+
Obsolete by: None
+
Population Cost: 0. Adds Population: 1.
+
+Pilgrims are wayfaring migrants from Theocratic nations. Like Zealots, they have no upkeep if under a Theocratic government. Heeding the call of ecclesiastic authority, Pilgrims can be guided from one city to another in order to transfer population from city to city. Pilgrims have no population cost, so can be thought of as geographically controlled rapture for the price of 10 shields.
+➤ Belongs to Land unit class.
+ • Subject to zones of control.
+ • Slowed down while damaged.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ Can only be built with Theocracy as government.
+➤ Costs no population to build (0).
+➤ May load onto and disembark from Transport Helicopters even when underway.
+➤ Never imposes a zone of control.
+➤ A non-military unit:
+ • Cannot attack.
+ • Doesn't impose martial law.
+ • Can enter foreign territory regardless of peace treaty.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Can Add to City
+ • is done to domestic individual cities.
+ • uses up the Pilgrims.
+ • max target size: 39.
+ • adds 1 population.
+➤ Capturable: can be captured if conditions allow it.
+➤ Expellable: can be expelled from foreign tiles.
+➤ Unable to pillage tiles.
+➤ Will never achieve veteran status.
Transport Loading LogisticsTransport Unloading Logistics
-
+
Proletarians
Cost: 10 shields
@@ -250,7 +290,7 @@
Proletarians
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, Naval Base, Airbase, Radar, and Buoy on tiles.
➤ Can clean Pollution from tiles.
@@ -273,8 +313,8 @@
Proletarians
-
-
Pilgrims
+
+
Migrants
Cost: 10 shields
Upkeep: 2 Food
@@ -285,25 +325,25 @@
Pilgrims
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Population Cost: 0. Adds Population: 1.
+
Population Cost: 1. Adds Population: 1.
-Pilgrims are wayfaring migrants from Theocratic nations. Like Zealots, they have no upkeep if under a Theocratic government. Heeding the call of ecclesiastic authority, Pilgrims can be guided from one city to another in order to transfer population from city to city. Pilgrims have no population cost, so can be thought of as rapture for the price of 10 shields.
-➤ Belongs to Land unit class.
+Migrants are citizens under Nationalist governments who are encouraged to settle in new cities, often in conquered or colonial areas. They can be guided from one city to another in order to transfer population from city to city. If managed correctly, they can increase the overall rate of national population growth.
+ ➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Can only be built with Theocracy as government.
-➤ Costs no population to build (0).
+➤ Can only be built with Nationalism as government.
+➤ Costs 1 population to build.
➤ May load onto and disembark from Transport Helicopters even when underway.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Add to City
• is done to domestic individual cities.
- • uses up the Pilgrims.
+ • uses up the Migrants.
• max target size: 39.
• adds 1 population.
➤ Capturable: can be captured if conditions allow it.
@@ -316,51 +356,68 @@
Pilgrims
-
-
Migrants
+
+
Workers
-
Cost: 10 shields
-
Upkeep: 2 Food
+
Cost: 30 shields
+
Upkeep: 1 Shield
Moves: 2
Vision: 2.00 tiles
Attack: 0
Defense: 1
Firepower: 1
-
Hitpoints: 10
-
Obsolete by: None
-
Population Cost: 1. Adds Population: 1.
-
-Migrants are citizens under Nationalist governments who are encouraged to settle in new cities, often in conquered or colonial areas. They can be guided from one city to another in order to transfer population from city to city. If managed correctly, they can increase the overall rate of national population growth.
- ➤ Belongs to Land unit class.
+
Hitpoints: 8
+
Obsolete by: Workers II
+
Workers can improve terrain tiles. See the manual on Terrain for details.
+Masonry lets Workers build Forts. Construction lets them build Fortresses and Oil Wells. Engineering lets them build Canals and Naval Bases.
+Feudalism with Construction lets them build Castles. Steel lets them build Bunkers and Sea Bridges. Radio lets them build Airbases and Buoys, which Settlers cannot. Workers must be on a ship to build Buoys.
+Communism tech allows Communist governments to conscript Workers into Riflemen via the Convert order. And vice versa.
+➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Can only be built with Nationalism as government.
-➤ Costs 1 population to build.
+➤ Under Communism, may be obtained by conversion of Riflemen.
➤ May load onto and disembark from Transport Helicopters even when underway.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
-➤ Can Add to City
- • is done to domestic individual cities.
- • uses up the Migrants.
- • max target size: 39.
- • adds 1 population.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
+➤ Can build Fort, Fortress, Naval Base, Airbase, Radar, and Buoy on tiles.
+➤ Can clean Pollution from tiles.
+➤ Can clean Fallout from tiles.
+➤ Can Upgrade
+ • upgrades to Engineers.
+➤ Can Pillage
+➤ Can Road
+➤ Can Convert Unit
+ • is converted into Riflemen (takes 2 MP).
+➤ Can Build Bases
+➤ Can Mine and make Oil Well
+➤ Can Irrigate
➤ Capturable: can be captured if conditions allow it.
➤ Expellable: can be expelled from foreign tiles.
-➤ Unable to pillage tiles.
-➤ Will never achieve veteran status.
+➤ May not acquire veteran status.
+➤ May retain veteran status after conversion.
+
+
+
Veteran level
Work factor
Promotion Odds
Move bonus
+
+
apprentice
100%
0%
-
+
journeyman
100%
0%
+ 1/9
+
foreman
100%
0%
+ 1/9
+
chief engineer
100%
0%
+ 1/9
+
Transport Loading LogisticsTransport Unloading Logistics
-
-
Workers
+
+
Workers II
Cost: 30 shields
Upkeep: 1 Shield
@@ -371,10 +428,16 @@
Workers
Firepower: 1
Hitpoints: 8
Obsolete by: Engineers
-
Workers can improve terrain tiles. See the manual on Terrain for details.
-Masonry lets Workers build Forts. Construction lets them build Fortresses and Oil Wells. Engineering lets them build Canals and Naval Bases.
- Feudalism with Construction lets them build Castles. Steel lets them build Bunkers and Sea Bridges. Radio lets them build Airbases and Buoys, which Settlers cannot. Workers must be on a ship to build Buoys.
-Communism tech allows Communist governments to conscript Workers into Riflemen via the Convert order. And vice versa.
+
As civilization progresses, socioeconomic trends improve education, efficiency and methods: for each turn of work completed, Workers II gain a small chance of promotion.
+• v1 do 6 turns of work in 5 turns (Journeyman);
+• v2 do 5 turns of work in 4 turns (Foreman);
+• v3 do 4 turns of work in 3 turns (Master Workman).
+These bonuses stack with other work rate bonuses.
+
+In other respects, this unit is identical to Workers I.
+Masonry lets Workers build Forts. Construction lets them build Fortresses and Oil Wells. Engineering lets them build Canals and Naval Bases. Feudalism with Construction lets them build Castles. Steel lets them build Bunkers and Sea Bridges. Radio lets them build Airbases and Buoys, which Settlers cannot. Workers must be on a ship to build Buoys.
+Until the discovery of Explosives, Workers will immediately upgrade for free to Workers II. No moves are spent, no city is needed, and the current job is not interrupted.
+Communism tech allows Communist governments to conscript Workers II into Riflemen via the Convert order. And vice versa.➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
@@ -386,7 +449,7 @@
Workers
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, Naval Base, Airbase, Radar, and Buoy on tiles.
➤ Can clean Pollution from tiles.
@@ -403,19 +466,24 @@
Workers
➤ Capturable: can be captured if conditions allow it.
➤ Expellable: can be expelled from foreign tiles.
➤ May acquire veteran status.
+➤ Veteran bonuses stack with other work rate bonuses.
-Veteran level Move bonus
-green 100% -
-army worker + 1/9
-army engineer + 1/9
-chief engineer + 1/9
+Veteran Levels for Workers II are unique to this unit type:
+
+
Veteran level
Work factor
Promotion Odds per turn/work
Move bonus
+
+
apprentice
100%
7%
-
+
journeyman
120%
5%
+ 1/9
+
foreman
125%
3%
+ 1/9
+
master workman
133%
0%
+ 1/9
+
Transport Loading LogisticsTransport Unloading Logistics
-
+
Engineers
Cost: 40 shields
@@ -427,7 +495,7 @@
Engineers
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Engineers can do everything Workers can do, at twice the speed. Unlike Workers, Engineers can Transform, such as converting Desert to Plains. Converting Ocean to Swamp requires being on a ship on a tile bordering 2 land tiles.
+
Engineers can do everything Workers can do, at twice the speed. Unlike Workers, Engineers can Transform, such as converting Desert to Plains. Converting Ocean to Swamp requires being on a ship on a tile bordering 2 land tiles. Constructing a Sea Bridge requires being on a ship on a tile cardinally adjacent to land.➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
@@ -438,7 +506,7 @@
Engineers
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, Naval Base, Airbase, Radar, and Buoy on tiles.
➤ Can clean Pollution from tiles.
@@ -453,7 +521,7 @@
Engineers
➤ Will never achieve veteran status.
-
+
Warriors
Cost: 8 shields
@@ -478,8 +546,9 @@
Warriors
• Can't attack as Cargo. Must first unload.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can Upgrade
• upgrades to Pikemen or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -510,7 +579,7 @@
Warriors
-
+
Phalanx
Cost: 16 shields
@@ -543,14 +612,15 @@
Phalanx
• No unit can be killed.
• Cannot be done to Cities, Forts, Fortresses, Naval Bases, nor Oceanic tiles.
• Can do action while fortified and remain fortified afterwards.
- - Using the D command to click the target will preserve fortified status of Phalanx.
+➤ Can attack while fortified and remain fortified afterwards.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Pikemen or, when possible, to the unit type it upgrades to.
@@ -581,7 +651,7 @@
Phalanx
-
+
Archers
Cost: 24 shields
@@ -593,12 +663,11 @@
Archers
Firepower: 1
Hitpoints: 10
Obsolete by: Musketeers
-
Archers fight with bows and arrows. They have good offense and decent defense. Rather than fight to the death, Archers can also do a ranged Volley Attack which avoids hand-to-hand retaliation: two volleys of arrows are fired on up to 7 enemy units on the tile, causing 1-2hp of damage to any units who are hit. This includes Oceanic tiles if not transported. This is useful for softening enemies prior to battle, or for deterring an approach to a strategic location. (Volley Attack is not possible on Cities or Fortresses.) Archers can do a retaliatory Volley attack against any unit which does a Special Unit Attack on them.
+
Archers fight with bows and arrows. They have good offense and decent defense. Rather than fight to the death, Archers can also do a ranged Volley Attack which avoids hand-to-hand retaliation: two volleys of arrows are fired on up to 3 enemy units on the tile, causing 1-2hp of damage to any units who are hit. This includes Oceanic tiles if not transported. This is useful for softening enemies prior to battle, or for deterring an approach to a strategic location. (Volley Attack is not possible on Cities or Fortresses.) Archers can do a retaliatory Volley attack against any unit which does a Special Unit Attack on them.
With the discovery of Conscription, Archers can convert to Musketeers in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -613,18 +682,20 @@
Archers
• No unit can be killed.
• Cannot be done to Cities, Fortresses, nor Naval Bases.
• Can do action while fortified and remain fortified afterwards.
- - Using the D command to click the target will preserve fortified status of Archers.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 2 free rounds of combat against up to 7 units in an enemy stack.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Musketeers or, when possible, to the unit type it upgrades to.
@@ -655,10 +726,10 @@
Archers
-
+
Legion
-
Cost: 32 shields
+
Cost: 30 shields
Upkeep: 1 Shield, 1 Unhappy
Moves: 2
Vision: 2.00 tiles
@@ -670,11 +741,9 @@
Legion
Legions are heavily armed well disciplined soldiers with excellent offensive strength. They are famous for their engineering abilities: with the required technology they can build Forts and Fortresses. They can build Roads outside domestic national territory and inside Forts and Fortresses.
Legions are feared for how they start battle: a Pilum Assault targets the shields of the enemy. A pilum stuck in a shield renders it useless, leaving the front line vulnerable to a full-on charge. The Pilum Assault does 1 round of combat at 2× attack bonus on up to 2 units on the tile.
-
-With the discovery of Conscription, Legions can convert to Musketeers in any domestic city.
+With the discovery of Conscription, Legions can convert to Musketeers in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -692,13 +761,16 @@
Legion
➤ Can build Quay, Road, Railroad, Maglev, Canal, and Waterway on tiles.
➤ Can build Fort, Fortress, and Naval Base on tiles.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Musketeers or, when possible, to the unit type it upgrades to.
@@ -733,7 +805,7 @@
Legion
-
+
Pikemen
Cost: 18 shields
@@ -747,11 +819,9 @@
Pikemen
Obsolete by: Musketeers
Long sharp pikes give Pikemen a counter to the height and mobility of mounted units. They enjoy a 2× defense bonus against mounted units.
-
-With the discovery of Conscription, Pikemen can convert to Musketeers in any domestic city.
+With the discovery of Conscription, Pikemen can convert to Musketeers in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -761,11 +831,12 @@
Pikemen
➤ 2× defense bonus if attacked by Horsemen, Chariot, Elephants, Crusaders, Knights, or Dragoons.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Musketeers or, when possible, to the unit type it upgrades to.
@@ -796,7 +867,7 @@
Pikemen
-
+
Musketeers
Cost: 30 shields
@@ -808,10 +879,9 @@
Musketeers
Firepower: 1
Hitpoints: 20
Obsolete by: Riflemen
-
Musketeers are infantry equipped with early firearms. They replace Pikemen as the preferred city defender, and replace Archers and Legions for offensive foot soldiers. The discovery of Labor Union allows upgrading Musketeers to Riflemen for free in any domestic city.
+
Musketeers are infantry equipped with early firearms. They replace Pikemen as the preferred city defender, and replace Archers and Legions for offensive foot soldiers. The discovery of Labor Union allows converting Musketeers to Riflemen for free in any domestic city.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -821,13 +891,16 @@
Musketeers
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Riflemen.
@@ -858,7 +931,7 @@
Musketeers
-
+
Falconeers
Cost: 20 shields
@@ -872,8 +945,7 @@
Falconeers
Obsolete by: Zealots
Falconeers are Theocratic holy warriors armed with Falconets—the largest hand cannons humans can carry. Ottoman Janissaries, Reconquistadores, Persian Safavid, and Indian Mughal used them to prove whose side God is on.
Falconets are huge and heavy. They have woeful accuracy but lethal firepower:they either kill or totally miss. On paper, attack odds are closest to a Musketeer (A3). Yet there is a high chance of losing when a win is expected, or winning when a loss is expected. The outcome is in God's hands! Falconeers defend a bit worse than they attack.
-Righteous faith is needed to fight with slow heavy inaccurate weaponry: only Theocracies can train Falconeers. Other governments will incur high upkeep or see them retire.
-
+Righteous faith is needed to fight with slow heavy inaccurate weaponry: only Theocracies can train Falconeers. Other governments will incur high upkeep or see them retire.
➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -883,12 +955,15 @@
Falconeers
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+ • Prevents enemy cities from working the tile it's on.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Zealots.
@@ -924,11 +999,11 @@
Falconeers
-
-
Zealots
+
+
Partisan
-
Cost: 20 shields
-
Upkeep: 1 Shield, 2 Unhappy
+
Cost: 45 shields
+
Upkeep: 1 Unhappy
Moves: 2
Vision: 2.00 tiles
Attack: 4
@@ -936,37 +1011,36 @@
Zealots
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Zealots are warriors devoted to a higher cause. Their faith is strong and they cannot be bribed.
-Theocratic nations can maintain Zealots without paying their steep upkeep. (Zealots are unhappy if not under Theocracy, requiring high upkeep to stay content.) Zealots produced in a city with Ecclesiastical Palace are inspired by fervorous faith to +1 higher veteran level.
-Zealots zealously defend their homeland, and can do surprise skirmish assaults to snipe and injure foreign occupants: Up to 4 invaders on the tile will endure three combat rounds without defense. Skirmish assaults can also be done to foreign occupied cities if they have a remaining Foreign National population.
+
Partisans are guerilla fighters who can use the terrain to their advantage. Like the Scout that they upgrade, they can slip through ZOC and live off the land with no upkeep, and gain +²⁄₉ move per veteran level. Partisans provide sentry intel even if not sentried.
-
-
-The discovery of Banking changes upkeep from shields to gold.
+Proportional to city size, up to 8 Partisans appear when an enemy conquers your city. They randomly fortify on any tile inside the circle defined by its workable radius. Partisans prefer defensive tiles, and ignore tile nationality. They spawn only when:
+➣ Guerilla Warfare is known by any player.
+➣ The city was originally built by you.
+➣ You must be Theocratic, OR,
+➣ You must be Democracy or Communist, AND know Communism AND Gunpowder.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
- • Subject to zones of control.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
• Can't attack as Cargo. Must first unload.
-➤ Can only be built with Theocracy as government.
-➤ Can safely conduct Skirmish Assaults. (Ranged Attack)
- • Gives 3 free rounds of combat against up to 4 units in an enemy stack.
- • Uses 15⁄9 movement points.
- • Can do this action while transported.
- • Must be inside domestic territory OR attacking a City with foreign nationals.
- • Cannot be done to Fortresses, Naval Bases, or Oceanic Tiles.
- • No unit can be killed.
+➤ Never has a home city.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
+➤ Ignores terrain effects (moving costs at most 1/3 MP per tile).
➤ May impose a zone of control on its adjacent tiles.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+ • Prevents enemy cities from working the tile it's on.
+➤ Not subject to zones of control imposed by other units.
+➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
@@ -975,76 +1049,80 @@
Zealots
• Gets the 50% defensive bonus automatically while in cities.
➤ Can Make Hideout
• Requires Forest, Jungle, Swamp, or Mountains. Land units will be invisible.
-➤ Can't be bribed.
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Zealots are the standard ruleset default:
+Veteran Levels for Partisans are unique to this unit type:
Veteran level
Power factor
Promotion Odds
Move bonus
green
100%
50%
-
-
veteran
150%
33%
-
-
hardened
175%
20%
-
-
elite
200%
15%
-
-
crack
210%
15%
+ 1/9
-
master
220%
15%
+ 1/9
-
champion
230%
-
+ 1/9
+
veteran
150%
33%
+ 2/9
+
hardened
175%
20%
+ 4/9
+
elite
200%
15%
+ 2/3
+
crack
210%
15%
+ 8/9
+
master
220%
15%
+1 1/9
+
wind walker
230%
-
+1 1/3
Transport Loading LogisticsTransport Unloading Logistics
-
-
Partisan
+
+
Riflemen
-
Cost: 50 shields
-
Upkeep: 1 Unhappy
+
Cost: 40 shields
+
Upkeep: 1 Gold, 1 Unhappy
Moves: 2
Vision: 2.00 tiles
-
Attack: 4
+
Attack: 5
Defense: 4
Firepower: 1
Hitpoints: 20
-
Obsolete by: None
-
Partisans are guerilla fighters who can use the terrain to their advantage. Like the Scout that they upgrade, they can slip through ZOC and live off the land with no upkeep.
-Proportional to city size, up to 8 Partisans appear when an enemy conquers your city. They randomly fortify on any tile inside the circle defined by its workable radius. Partisans prefer defensive tiles, and ignore tile nationality. They spawn only when:
-➣ Guerilla Warfare is known by any player.
-➣ The city was originally built by you.
-➣ You must be Theocratic, OR,
-➣ You must be Democracy or Communist, AND know Communism AND Gunpowder.
+
Obsolete by: Ground Troops
+
Riflemen are World War-era infantry. They are good at defending cities and strategic Fortresses. They also have good attack capability.
+
+Under Communism this unit costs 5 less. Also, Communism tech allows Communist governments to de-commission Riflemen into Workers II via the Convert order. And vice versa.
+
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
+ • Subject to zones of control.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
• Can't attack as Cargo. Must first unload.
-➤ Never has a home city.
+➤ Under Communism, may be obtained by conversion of Workers.
➤ Unable to attack air units.
-➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
-➤ Ignores terrain effects (moving costs at most 1/3 MP per tile).
➤ May impose a zone of control on its adjacent tiles.
-➤ Not subject to zones of control imposed by other units.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
+➤ Can Upgrade
+ • upgrades to Ground Troops.
+ • upgrade cost is 4 gold.
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
➤ Can Pillage
➤ Can Fortify, granting a 50% defensive bonus when not in a city.
• Gets the 50% defensive bonus automatically while in cities.
+➤ Can Convert Unit
+ • is converted into Workers (takes 2 MP).
➤ Can Make Hideout
• Requires Forest, Jungle, Swamp, or Mountains. Land units will be invisible.
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Partisans are the standard ruleset default:
+Veteran Levels for Riflemen are the standard ruleset default:
Veteran level
Power factor
Promotion Odds
Move bonus
@@ -1061,22 +1139,22 @@
Partisan
-
-
Riflemen
+
+
Zealots
-
Cost: 40 shields
-
Upkeep: 1 Shield, 1 Unhappy
+
Cost: 20 shields
+
Upkeep: 1 Shield, 2 Unhappy
Moves: 2
Vision: 2.00 tiles
-
Attack: 5
+
Attack: 4
Defense: 4
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Riflemen are World War-era infantry. They are good at defending cities and strategic Fortresses. They also have good attack capability.
-
-Under Communism this unit costs 5 less. Also, Communism tech allows Communist governments to de-commission Riflemen into Workers via the Convert order. And vice versa.
-
+
Zealots are warriors devoted to a higher cause. Their faith is strong and they cannot be bribed.
+Theocratic nations can maintain Zealots without paying their steep upkeep. (Zealots are unhappy if not under Theocracy, requiring high upkeep to stay content.) Zealots produced in a city with Ecclesiastical Palace are inspired by fervorous faith to +1 higher veteran level.
+Zealots fervently defend their homeland, and can do surprise skirmish assaults to snipe and injure foreign occupants: Up to 4 invaders on the tile will endure three combat rounds without defense. Skirmish assaults can also be done to foreign occupied cities if they have a remaining Foreign National population.
+Zealots can Estabish Home City in any allied city that is Theocracy, allowing control to pass between nations who share the same faith.
The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
@@ -1085,34 +1163,40 @@
Riflemen
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
• Can't attack as Cargo. Must first unload.
-➤ Under Communism, may be obtained by conversion of Workers.
+➤ Can only be built with Theocracy as government.
+➤ Can safely conduct Skirmish Assaults. (Ranged Attack)
+ • Gives 3 free rounds of combat against up to 4 units in an enemy stack.
+ • Uses 15⁄9 movement points.
+ • Can do this action while transported.
+ • Must be inside domestic territory OR attacking a City with foreign nationals.
+ • Cannot be done to Fortresses, Naval Bases, or Oceanic Tiles.
+ • No unit can be killed.
➤ Unable to attack air units.
+➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
-➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+ • Prevents enemy cities from working the tile it's on.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
-➤ Can Upgrade
- • upgrades to Ground Troops.
- • upgrade cost is 4 gold.
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
➤ Can Pillage
➤ Can Fortify, granting a 50% defensive bonus when not in a city.
• Gets the 50% defensive bonus automatically while in cities.
-➤ Can Convert Unit
- • is converted into Workers (takes 2 MP).
➤ Can Make Hideout
• Requires Forest, Jungle, Swamp, or Mountains. Land units will be invisible.
+➤ Can't be bribed.
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Riflemen are the standard ruleset default:
+Veteran Levels for Zealots are the standard ruleset default:
Veteran level
Power factor
Promotion Odds
Move bonus
@@ -1129,22 +1213,21 @@
Riflemen
-
+
Alpine Troops
Cost: 50 shields
-
Upkeep: 1 Shield, 1 Unhappy
+
Upkeep: 1 Gold, 1 Unhappy
Moves: 2
Vision: 2.00 tiles
Attack: 5
Defense: 5
Firepower: 1
Hitpoints: 20
-
Obsolete by: None
+
Obsolete by: Ground Troops
Alpine Troops are highly mobile units and excellent defenders. Similar to a Scout, they treat every land tile like a road were on it.
-
-The discovery of Banking changes upkeep from shields to gold.
+The discovery of Banking changes upkeep from shields to gold.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1155,14 +1238,17 @@
Alpine Troops
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ Ignores terrain effects (moving costs at most ⅓ MP per tile).
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Ground Troops.
@@ -1194,11 +1280,11 @@
Alpine Troops
-
+
Marines
-
Cost: 60 shields
-
Upkeep: 1 Shield, 1 Unhappy
+
Cost: 55 shields
+
Upkeep: 1 Gold, 1 Unhappy
Moves: 2
Vision: 2.83 tiles
Attack: 8
@@ -1206,24 +1292,27 @@
Marines
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Marines are experts at all-terrain warfare. They are the strongest foot unit in the game, and are armed with diverse weaponry.
-Unlike other units, Marines can attack Sea units (at a 50% penalty). Air units do not stop them from attacking reachable targets. They can attack while fortified and maintain fortified status. They can attack from a Transport or Helicopter, and can offload without losing a turn of movement. Marines can make Forts and Airbases. Marines get an extra 2/9 movement points for each veteran level they possess. Starting at v2, they gain the ability to do long-range bazooka attacks for 3 combat rounds, hitting up to 2 units on the tile and killing a maximum of 1.
-Bazooka attacks can be done to Ocean tiles if the Marines aren't transported.
-
-Marines built in a city with an Airport, Port Facility, and Barracks III receive an extra veteran level.
-
-
-The discovery of Banking changes upkeep from shields to gold.
+
Marines are experts at all-terrain warfare. Armed with diverse weaponry, they are the strongest foot unit in the game. Marines have special abilities. They can:
+ ▪ Attack Sea units (at a 50% penalty).
+ ▪ Attack while fortified and maintain fortified status.
+ ▪ Attack from a Transport or Helicopter.
+ ▪ Attack reachable targets on a tile without being blocked by Fighters.
+ ▪ Do Bazooka attacks, if vet-2: 3 rounds on 2 units, max. 1 kill.
+ ▪ Do Bazooka attacks on ocean tiles, if not transported.
+ ▪ Disembark from non-native tiles using only 1 move point.
+ ▪ (De)board and (un)load without using or needing move points.
+ ▪ Make Forts and Airbases.
+
+Marines gain greater strength bonuses with veterancy, and get +2/9 move points for each veteran level. Marines built in a city with an Airport, Port Facility, and Barracks III receive an extra veteran level.➤ Belongs to LandAirSea unit class.
• Can attack from Ships and Helicopters.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
• AIRLIFT: Can be airlifted if it has remaining moves.
- • Can launch attack from non-native tiles.
+ • Can attack ocean tiles, even while transported.
+ • Can attack from ocean tiles, while transported.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
-➤ Can launch attack from non-native tiles and while transported.
➤ Can safely conduct Bazooka Attacks. (Ranged Attack)
• Gives 3 free rounds of combat against up to 2 units in an enemy stack.
• Uses 15⁄9 movement points.
@@ -1233,24 +1322,37 @@
Marines
• Cannot be done to a City, Fortress, or Naval Base.
• A maximum of 1 unit can be killed.
• Can do action while fortified and remain fortified afterwards.
- - Using the D command to click the target will preserve fortified status of Marines.
+➤ Can attack while fortified and remain fortified afterwards.
+➤ Can attack reachable units on ocean tiles.
➤ May load and unload from Trains and Helicopters even when underway.
-➤ Won't lose all movement when moving from non-native terrain to native terrain, or unloading from transport.
+➤ Loses only 1 move point when disembarking from non-native terrain to native terrain.
+ • Native disembark uses terrain move cost.
+➤ Transport Board and Transport Deboarddo not use or need move points.
+ • The same is true when the AAA is the affected cargo of Transport Load, Transport Unload.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
+➤ Can attack against Sea units, which are usually not reachable.
+ • attacks at a -50% penalty.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
➤ Can Pillage
➤ Can Fortify, granting a 50% defensive bonus when not in a city.
• Gets the 50% defensive bonus automatically while in cities.
+➤ Can Build Bases
+ • can build Fort and Airbase.
+ • cannot build Fortress, Naval Base, Castle, Bunker, Radar, or Buoy.
+ • veteran work rates apply.
➤ Can Make Hideout
• Requires Forest, Jungle, Swamp, or Mountains. Land units will be invisible.
➤ May acquire veteran status.
@@ -1273,7 +1375,7 @@
Marines
-
+
Ground Troops
Cost: 40 shields
@@ -1285,7 +1387,7 @@
Ground Troops
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Ground Troops upgrade earlier infantry with better weaponry. The upgrade cost for Riflemen and Alpine Troops is 4 gold. Veteran Ground Troops enjoy a +⅓ move bonus. Ground Troops can stay fortified while attacking, and veterans enjoy a +⅓ move bonus.
+
Ground Troops upgrade earlier infantry with better weaponry. The upgrade cost for Riflemen and Alpine Troops is 4 gold. Ground Troops can stay fortified while attacking, and veterans enjoy a +⅓ move bonus.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1294,13 +1396,16 @@
Ground Troops
• Can't attack as Cargo. Must first unload.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+ • Prevents enemy cities from working the tile it's on.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
@@ -1319,23 +1424,23 @@
Ground Troops
Veteran level
Power factor
Promotion Odds
Move bonus
green
100%
50%
-
-
veteran
150%
33%
-
-
hardened
175%
20%
-
-
elite
200%
15%
-
-
crack
210%
15%
+ 1/9
-
master
220%
15%
+ 1/9
-
champion
230%
-
+ 1/9
+
veteran
150%
33%
+ 1/3
+
hardened
175%
20%
+ 1/3
+
elite
200%
15%
+ 1/3
+
crack
210%
15%
+ 1/3
+
master
220%
15%
+ 1/3
+
champion
230%
-
+ 1/3
Transport Loading LogisticsTransport Unloading Logistics
-
+
Paratroopers
-
Cost: 60 shields
-
Upkeep: 1 Shield, 1 Unhappy
+
Cost: 55 shields
+
Upkeep: 1 Gold, 1 Unhappy
Moves: 2
Vision: 2.00 tiles
Attack: 6
@@ -1356,14 +1461,17 @@
Paratroopers
➤ Unable to attack air units.
➤ May load onto and disembark from Trains or Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Paradrop
• target must be between 1 and 14 tiles away.
@@ -1397,11 +1505,11 @@
Paratroopers
-
+
Mechanized Infantry
Cost: 50 shields
-
Upkeep: 1 Shield, 1 Unhappy
+
Upkeep: 1 Gold, 1 Unhappy
Moves: 6
Vision: 2.00 tiles
Attack: 6
@@ -1411,8 +1519,7 @@
Mechanized Infantry
Obsolete by: None
Mechanized Infantry have the strongest general defense strength of any land unit. They have decent attack strength in open field engagements, and excellent mobility.
-
-Upkeep for Mechanized infantry is paid in gold instead of shields.
+Upkeep for Mechanized infantry is paid in gold instead of shields.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1422,12 +1529,13 @@
Mechanized Infantry
➤ Unable to attack air units.
➤ May load onto Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ The discovery of Banking converts the shield upkeep of this unit to 1 gold upkeep.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
@@ -1454,7 +1562,63 @@
Mechanized Infantry
-
+
+
Scout
+
+
Cost: 30 shields
+
Upkeep: 0
+
Moves: 2
+
Vision: 2.24 tiles
+
Attack: 0
+
Defense: 1
+
Firepower: 1
+
Hitpoints: 10
+
Obsolete by: Partisan
+
Scouts are explorers of unknown territory. They live off the land with no upkeep. They can slip through ZOC. They ignore terrain penalties, treating every tile as if it had a road. They can move 4 tiles per turn (6 after discovering Map Making.) For each veteran level, they gain an additional +²⁄₉ move point. Scouts can Investigate City to gather intel, and their terrain scouting can give tactical advantage. Scouts can make contact with far-off lands to arrange Cease-fire or Peace. In later times, Scouts upgrade to Partisans.
+
+Scouts provide sentry intel even if not sentried. Scouts gain the same veteran levels from buildings and wonders as other land units.
+➤ Belongs to Land unit class.
+ • Slowed down while damaged.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ Ignores terrain effects (moving costs at most 1/3 MP per tile).
+➤ Map Making gives +2/3 moves, giving a range of +2 tiles.
+➤ Never imposes a zone of control.
+➤ Not subject to zones of control imposed by other units.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
+➤ A non-military unit:
+ • Cannot attack.
+ • Doesn't impose martial law.
+ • Can enter foreign territory regardless of peace treaty.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Can Investigate City (does not spend the unit)
+➤ Can Upgrade
+ • upgrades to Partisan.
+➤ Can Fortify, granting a 50% defensive bonus when not in a city.
+ • Gets the 50% defensive bonus automatically while in cities.
+➤ Capturable: can be captured if conditions allow it.
+➤ Expellable: can be expelled from foreign tiles.
+➤ May acquire veteran status.
+ • Can get veteran status when built, from Barracks and wonders.
+ • Veterans have increased strength in combat.
+
+Veteran Levels for Scouts are unique to this unit type:
+
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Knights or, when possible, to the unit type it upgrades to.
@@ -1508,7 +1673,7 @@
Horsemen
-
+
Chariot
Cost: 27 shields
@@ -1531,11 +1696,14 @@
Chariot
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Knights or, when possible, to the unit type it upgrades to.
@@ -1564,7 +1732,7 @@
Chariot
-
+
Elephants
Cost: 36 shields
@@ -1587,11 +1755,14 @@
Elephants
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Crusaders or, when possible, to the unit type it upgrades to.
@@ -1620,34 +1791,40 @@
Elephants
-
-
Crusaders
+
+
Knights
Cost: 38 shields
Upkeep: 1 Shield, 1 Unhappy
Moves: 4
Vision: 2.00 tiles
-
Attack: 5
-
Defense: 1
+
Attack: 4
+
Defense: 2
Firepower: 1
Hitpoints: 10
Obsolete by: Dragoons
-
Crusaders are mounted warriors driven by a higher cause. They have superior attack to Knights, but are poor at defending. They are ideally suited for leading the charge in offensive campaigns.
-➤ Belongs to Land unit class.
+
Knights are heavily armored mounted warriors. They defend at D3 against mounted units. Their noble status allows them to attack cities without population reduction, and conquer a size 1 city without destroying it.
+➤ Belongs to LandNoKill unit class.
+ • Does not reduce population when attacking city.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
• Can't attack as Cargo. Must first unload.
+➤ 3× defense bonus if attacked by Horsemen, Chariot, Elephants, Crusaders, Knights, or Dragoons.
+➤ 2× defense bonus if attacked by Foot Soldiers.
➤ Attack value halved when attacking Pikemen.
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Dragoons or, when possible, to the unit type it upgrades to.
@@ -1659,7 +1836,7 @@
Crusaders
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Crusaders are the standard ruleset default:
+Veteran Levels for Knights are the standard ruleset default:
Veteran level
Power factor
Promotion Odds
Move bonus
@@ -1676,37 +1853,37 @@
Crusaders
-
-
Knights
+
+
Crusaders
Cost: 38 shields
Upkeep: 1 Shield, 1 Unhappy
Moves: 4
Vision: 2.00 tiles
-
Attack: 4
-
Defense: 1
+
Attack: 5
+
Defense: 1.5
Firepower: 1
Hitpoints: 10
Obsolete by: Dragoons
-
Knights are heavily armored mounted warriors. They defend at D:3 against mounted units. They defend at D:2 against foot units. They defend at D:1 against everything else. Their noble status allows them to attack cities without population reduction.
-➤ Belongs to LandNoKill unit class.
- • Does not reduce population when attacking city.
+
Crusaders are mounted warriors driven by a higher cause. They have superior attack to Knights, but are only half as good defending against mounted units. They are ideally suited for leading the charge in offensive campaigns.
+➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
• Can't attack as Cargo. Must first unload.
-➤ 3× defense bonus if attacked by Horsemen, Chariot, Elephants, Crusaders, Knights, or Dragoons.
-➤ 2× defense bonus if attacked by Foot Soldiers.
➤ Attack value halved when attacking Pikemen.
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Dragoons or, when possible, to the unit type it upgrades to.
@@ -1718,7 +1895,7 @@
Knights
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Knights are the standard ruleset default:
+Veteran Levels for Crusaders are the standard ruleset default:
Veteran level
Power factor
Promotion Odds
Move bonus
@@ -1735,7 +1912,7 @@
Knights
-
+
Dragoons
Cost: 50 shields
@@ -1758,11 +1935,14 @@
Dragoons
➤ Knights defend at 3 when attacked by this unit.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Cavalry or, when possible, to the unit type it upgrades to.
@@ -1791,7 +1971,7 @@
Dragoons
-
+
Cavalry
Cost: 60 shields
@@ -1813,12 +1993,15 @@
Cavalry
➤ -50% attack penalty when attacking Helicopter.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Capture Unit
• target unit must be alone, not on Mountains, and not in a Fort, Fortress, or Naval Base.
+ • uses ⅔ movement point
+ • actor stays on its own tile
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Armor or, when possible, to the unit type it upgrades to.
@@ -1847,19 +2030,21 @@
Cavalry
-
+
Armor
Cost: 80 shields
Upkeep: 1 Shield, 1 Unhappy
Moves: 6
Vision: 2.00 tiles
-
Attack: 10
+
Attack: 11
Defense: 5
Firepower: 1
Hitpoints: 30
Obsolete by: Armor II
-
Armor is the mechanized equivalent of a Cavalry unit. They have high attack strength, superb mobility, and the highest defense of any offensive unit. They are massive all-terrain vehicles, well-suited for penetrating defensive fortifications: Units inside Forts get no defense bonus against Armor. Units inside Fortresses get a reduced bonus of 1.67× instead of 2×.
+
Armor is the mechanized equivalent of a Cavalry unit. They have high attack strength, superb mobility, and the highest defense of any offensive unit. They are massive all-terrain vehicles, well-suited for penetrating defensive fortifications: Units inside Forts get no defense bonus against Armor. Units inside Fortresses get a reduced bonus of 1.67× instead of 2×.
+Communists pay 10 less for this unit.
+Stack-Escape:40% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1868,16 +2053,17 @@
Armor
• Can't attack as Cargo. Must first unload.
➤ Can safely iPillage: Instant Blitz Sack against tile infrastructure.
• 75% odds of success. +5% for each veteran level.
- • Destroys 1 selected infrastructural improvement on the tile, instantly.
+ • Destroys 1 selected infrastructural improvement on the tile, instantly.
• Uses 2 movement points.
➤ Unable to attack air units.
➤ Defending Forts get no bonus.+33% attack bonus vs. Fortress (reduces it to 1.67× defense).
➤ May load onto Helicopter transports even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Armor II.
@@ -1906,7 +2092,7 @@
Armor
-
+
Armor II
Cost: 85 shields
@@ -1918,7 +2104,9 @@
Armor II
Firepower: 1
Hitpoints: 30
Obsolete by: None
-
Armor II is the ultra-modern upgrade to Armor. It features high tech composite armor and electronic countermeasures (ECM). Forts and Fortresses gain no bonus against its ability to blast and break through fortifications. ECM and composite armor give a 2× defense bonus against Missiles. Unlike most land units, Armor II can attack reachable units regardless of whether unreachable units protect the tile.
+
Armor II is the ultra-modern upgrade to Armor. It features high tech composite armor and electronic countermeasures (ECM). Forts and Fortresses gain no bonus against its ability to blast and break through fortifications. ECM and composite armor give a 50% defense bonus against Missiles. Unlike most land units, Armor II can attack reachable units regardless of whether unreachable units protect the tile.
+
+Stack-Escape:40% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
@@ -1927,7 +2115,7 @@
Armor II
• Can't attack as Cargo. Must first unload.
➤ Can safely iPillage: Instant Blitz Sack against tile infrastructure.
• 75% odds of success. +5% for each veteran level.
- • Destroys 1 selected infrastructural improvement on the tile, instantly.
+ • Destroys 1 selected infrastructural improvement on the tile, instantly.
• Uses 2 movement points.
➤ 2× defense bonus if attacked by Cruise Missile.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
@@ -1936,10 +2124,11 @@
Armor II
➤ Defending Fortresses get no bonus.
➤ May load onto Helicopter transports even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
@@ -1966,7 +2155,7 @@
Armor II
-
+
Catapult
Cost: 34 shields
@@ -1978,14 +2167,17 @@
Catapult
Firepower: 1
Hitpoints: 10
Obsolete by: Cannon
-
Catapults are large rock-throwing war machines. The ballistic delivery of massive stones diminishes the defense bonus vs land attacks by Fortifications and City Walls by -25%, yielding a 1.25× and 2.75× bonus, respectively.
+
Catapults are rock-throwing war machines. Massive stones diminish the defense bonus of City Walls by -25%, down to 1.75×.
+If they haven't moved, Catapults can do 4 rounds of Bombardment on a single unit, which may possibly be killed. When special attacked from range or rammed in a Fortress, they can retaliate with the same type of Bombardment.
+
+Catapults are weak defenders, and need an escort to be effective.
-Catapults are weak defenders, and will need an escort to be effective. Even so, it is better to rush attack them than do special unit attacks from range:when attacked from range or rammed in a Fortress, Catapults can retaliate for 4 rounds of combat.
+VIGIL:the Vigil order lets Catapults auto-Bombard adjacent military units if they have better attack odds. Can be done in cities or bases if it has used no moves.
➤ Belongs to Land unit class, Ballistic sub-class.
- • 25% is subtracted from the bonus of City defense improvements against Land attacks:
- • Fortifications are reduced from 1.5× to 1.25×
- • City Walls are reduced from 3× to 2.75×
+ • 25% is subtracted from the bonus of City Walls:
+ • Bonus reduced from 2× to 1.75×
+ • VIGIL: can be given the Vigil order to auto-Bombard adjacent military units.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
@@ -1993,12 +2185,23 @@
Catapult
• Can't attack as Cargo. Must first unload.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can safely conduct Bombard Attack.
+ • Gives 4 free rounds of combat against 1 unit in an enemy stack.
+ • Uses 2 movement points.
+ • A maximum of 1 unit can be killed.
+ • Can be done to ocean tiles if not transported.
+ • Cannot be done to Cities, Fortresses, Naval Bases, Castles, and Bunkers.
+ • Can do action while fortified and remain fortified afterwards.
+ • Action requires:
+ - a minimum of 2 move points
+ - Catapult to have used no moves this turn.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 4 free rounds of combat against up to 1 unit in an enemy stack.
• A maximum of 1 unit can be killed.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Cannon or, when possible, to the unit type it upgrades to.
@@ -2027,7 +2230,7 @@
Catapult
-
+
Siege Ram
Cost: 45 shields
@@ -2056,6 +2259,8 @@
Siege Ram
• Can't attack as Cargo. Must first unload.
➤ Can Destroy City Walls
• Requires one full remaining move point, at minimum.
+➤ Can carry and 1 Foot soldier unit.
+ • Cargo not visible except to allies.
➤ Can safely conduct Ram Fortress. (Ranged Attack)
• Gives 4 free round of combat on all units in an enemy Fortress or Naval Base.
• Attack odds receive an 8.75× bonus.
@@ -2066,6 +2271,7 @@
Siege Ram
• Cannot be done to any tile except a Fortress or Naval Base.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Cannon or, when possible, to the unit type it upgrades to.
➤ Can Pillage
@@ -2074,8 +2280,66 @@
Siege Ram
➤ May acquire veteran status.
• Veterans have increased strength in unit-to-unit combat.
-
-
+
+
+
Ballista
+
+
Cost: 45 shields
+
Upkeep: 1 Shield, 1 Unhappy
+
Moves: 2
+
Vision: 2.00 tiles
+
Attack: 1
+
Defense: 1
+
Firepower: 5
+
Hitpoints: 10
+
Obsolete by: Cannon
+
Ballistae don’t conventionally attack. Ranged Attack fires massive bolts through enemy formations for 5 rounds, targeting up to 2 units who can’t conventionally retaliate. This may also target Ocean tiles. They must have all their move points to do attack, and can't target Fortresses or Cities. Though they only have A1 accuracy, 5FP causes 5× damage.
+
+When conventionally defending against foot and mounted units, Ballistae defend at 20% defense strength (0.2). This roughly cancels the 5× damage, making them like a D1 defender. However, a lucky hit or strong defensive terrain can reveal the 5FP as very effective. Against other attackers, the 5× damage is a deadly deterrent.
+
+Ballistae excel at degrading forts and well-fortified tiles. Defensively, they are capable of Special Unit Defense, reciprocating against ranged attacks with their own retaliatory attack.
+
+VIGIL:the Vigil order lets Ballistae automatically Range-Attack adjacent military units if they have better attack odds. Can be done in cities or bases if it has used no moves.
+➤ Belongs to Land unit class, Ballistic sub-class.
+ • VIGIL: can be given the Vigil order to auto-Bombard adjacent military units.
+ • Can occupy empty enemy cities.
+ • Subject to zones of control.
+ • Slowed down while damaged.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+ • Can safely conduct Ranged Attacks.
+ • Cannot do conventional combat attacks.
+➤ -80% defense bonus if attacked by Mounted units or Foot Soldiers.
+➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can safely conduct Ranged Attack.
+ • Gives 5 free rounds of combat on 2 units in an enemy stack.
+ • Uses 2 movement points.
+ • A maximum of 2 units can be killed.
+ • Cannot do this action while transported.
+ • Can be done to Oceanic tiles.
+ • Cannot be done to Cities, Fortresses, Naval Bases, Castles, and Bunkers.
+ • Action requires:
+ - a minimum of 2 move points
+ - Ballista to have used no moves this turn.
+➤ Has Special Defense against Special Attacks / Ranged Attacks.
+ • Gives 5 free rounds of combat against up to 2 units in an enemy stack.
+ • A maximum of 2 units can be killed.
+➤ Can Expel
+ • target unit must be alone and not on Mountains.
+ • uses ⅔ movement point
+ • actor stays on its own tile
+➤ Can Upgrade
+ • upgrades to Cannon or, when possible, to the unit type it upgrades to.
+➤ Attacking with fractional move points gives fractional attack power.
+➤ Can Conquer City
+➤ Can Pillage
+➤ Can Fortify, granting a 50% defensive bonus when not in a city.
+ • Gets the 50% defensive bonus automatically while in cities.
+➤ May acquire veteran status.
+ • Veterans have increased strength in combat.
+
+
+
Cannon
Cost: 40 shields
@@ -2087,14 +2351,15 @@
Cannon
Firepower: 1
Hitpoints: 20
Obsolete by: Artillery
-
Cannons are huge guns that use gunpowder to fire massive metallic projectiles much faster and more accurately than earlier ballistic weapons. The aerial delivery of high speed massive metal gives an edge against Fortifications and City Walls. The defense bonuses vs. land attacks from those are reduced by -50%. This leaves Fortifications only with their terrain bonus, and reduces City walls to 2.5x.
+
Cannons are guns that fire massive metallic projectiles faster and more accurately than earlier ballistic weapons. This reduces the defense bonuses of City Walls in half, down to 1.5×.
+If they haven't moved, Cannons can do 5 rounds of Bombardment on two units, with one possible fatality. When special attacked from range or rammed in a Fortress, they can retaliate with the same type of Bombardment.
-While Cannons are strong attackers, they are also weak defenders. Yet they are not without defensive purpose. They can retaliate 6 rounds of bombardment against special unit attacks from other units.
+VIGIL:the Vigil order lets Cannons auto-Bombard adjacent military units if they have better attack odds. Can be done in cities or bases if it has used no moves.
➤ Belongs to Land unit class, Ballistic sub-class.
- • 50% is subtracted from the bonus of City defense improvements against Land attacks:
- • Fortifications are reduced to terrain bonus only.
- • City Walls are reduced from 3× to 2.5×
+ • 50% is subtracted from the bonus of City Walls:
+ • Bonus reduced from 2× to 1.5×
+ • VIGIL: can be given the Vigil order to auto-Bombard adjacent military units.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
@@ -2103,12 +2368,23 @@
Cannon
➤ Unable to attack air units.
➤ May load onto and disembark from Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can safely conduct Bombard Attack.
+ • Gives 5 free rounds of combat against up to 2 units in an enemy stack.
+ • Uses 2 movement points.
+ • A maximum of 1 unit can be killed.
+ • Can be done to ocean tiles if not transported.
+ • Cannot be done to Cities, Fortresses, Naval Bases, Castles, and Bunkers.
+ • Can do action while fortified and remain fortified afterwards.
+ • Action requires:
+ - a minimum of 2 move points
+ - Cannon to have used no moves this turn.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 5 free rounds of combat against up to 2 units in an enemy stack.
• A maximum of 1 unit can be killed.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Artillery or, when possible, to the unit type it upgrades to.
@@ -2120,7 +2396,7 @@
Cannon
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Siege Rams are the standard ruleset default:
+Veteran Levels for Cannons are the standard ruleset default:
Veteran level
Power factor
Promotion Odds
Move bonus
@@ -2137,7 +2413,7 @@
Cannon
-
+
Artillery
Cost: 50 shields
@@ -2149,14 +2425,15 @@
Artillery
Firepower: 2
Hitpoints: 20
Obsolete by: Howitzer
-
Artillery are a major upgrade to Cannons, with doubled firepower. Accuracy and projectile speed go to a whole new level. Thus, they have an even greater bonus than their predecessors against Fortifications and City Walls. Such bonuses are reduced -75%. This leaves Fortifications only with their terrain bonus, and reduces the bonus from Walls down to 2.25x.
+
Artillery upgrade Cannons with doubled firepower. Against Artillery, the bonus of City Walls is reduced -50% down to 1.5×.
+If they haven’t moved, Artillery can do 6 rounds of Bombardment on three units, with one possible fatality. When special attacked from range, they retaliate with the same type of Bombardment.
-Like their predecessors, Artillery are poor at defense, and need an escort to be effective. However, they can retaliate 6 rounds of bombardment against special unit attacks from other units.
+VIGIL:the Vigil order lets Artillery auto-Bombard adjacent military units if they have better attack odds. Can be done in cities or bases if it has used no moves.
➤ Belongs to Land unit class, Ballistic sub-class.
- • 75% is subtracted from the bonus of City defense improvements against Land attacks:
- • Fortifications are reduced to terrain bonus only.
- • City Walls are reduced from 3× to 2.25×.
+ • 50% is subtracted from the bonus of City Walls:
+ • Bonus reduced from 2× to 1.5×.
+ • VIGIL: can be given the Vigil order to auto-Bombard adjacent military units.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
@@ -2165,12 +2442,23 @@
Artillery
➤ Unable to attack air units.
➤ May load onto and disembark from Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can safely conduct Bombard Attack.
+ • Gives 6 free rounds of combat against up to 3 units in an enemy stack.
+ • Uses 2 movement points.
+ • A maximum of 1 unit can be killed.
+ • Can be done to ocean tiles if not transported.
+ • Cannot be done to Cities, Fortresses, Naval Bases, Castles, and Bunkers.
+ • Can do action while fortified and remain fortified afterwards.
+ • Action requires:
+ - a minimum of 2 move points
+ - Artillery to have used no moves this turn.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 6 free rounds of combat against up to 3 units in an enemy stack.
• A maximum of 1 unit can be killed.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Can Upgrade
• upgrades to Howitzer.
@@ -2199,11 +2487,11 @@
Artillery
-
+
Anti-Aircraft Artillery
Cost: 50 shields
-
Upkeep: 1 Shield, 1 Unhappy
+
Upkeep: 1 Gold, 1 Unhappy
Moves: 2
Vision: 2.83 tiles
Attack: 2
@@ -2211,32 +2499,34 @@
Anti-Aircraft Artillery
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Anti-Aircraft Artillery (AAA) can attack almost anything from anywhere. AAA get a 2× bonus against Air units. AAA qualify as weaponry for Marines and have the same abilities: they can be transported on any unit that carries Marines, can attack from or to non-native tiles, and do not lose a turn when unloading. Transported AAA defend their tile on both Land and Sea. (AAA cannot attack Submarines, Missiles, or Jet Bombers.)
-
+
Anti-Aircraft Artillery (AAA) get a 2× bonus against Air units. They can attack any tile. They can be transported by any unit that can carry Marines. Transported AAA can defend their tile, even at sea. AAA can’t attack while transported, and cannot attack Submarines, Missiles, and High Altitude aircraft.
-After Space Flight and 10 turns of service, AAA can be retrofitted to Mobile SAM. The Convert order must be done in your capital, and takes 4 turns.
+After Space Flight and 9 turns of service, AAA can be retrofitted to Mobile SAM. The Convert order must be done in a domestic capital, and takes 3 turns.
➤ Belongs to LandAirSea unit class.
- • Can attack from Ships and Helicopters.
+ • Can be cargo on Ships and Helicopters.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
+ • Can attack reachable units on ocean tiles.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Can launch attack from non-native tiles and while transported.
-➤ 2× attack bonus when attacking Fighter, Escort Fighter, Dive Bomber, Medium Bomber, Heavy Bomber, Strategic Bomber, Helicopter, Jet Fighter, Ground Strike Fighter, Stealth Fighter, or Stealth Bomber.
-➤ 2× defense bonus if attacked by Fighter, Escort Fighter, Dive Bomber, Medium Bomber, Heavy Bomber, Strategic Bomber, Helicopter, Jet Fighter, Ground Strike Fighter, Jet Bomber, Stealth Fighter, or Stealth Bomber.
+ • Can't attack as Cargo. Must first unload.
+➤ 2× attack bonus when attacking Airplanes and Helicopters.
+➤ 2× defense bonus if attacked by Airplanes and Helicopters.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
-➤ Can launch attack from non-native tiles.
-➤ Can defend while transported on non-native tiles.
+➤ Can defend while transported on land and ocean tiles.
➤ Anti-Air bonus of this unit is less effective against Stealth, which has a 25% bonus against this unit.
-➤ May load and unload from Trains and Helicopters even when underway.
+➤ May load onto and disembark from Transport Helicopters even when underway.
➤ May impose a zone of control on its adjacent tiles.
-➤ Can attack against Helicopter units, which are usually not reachable.
-➤ Can attack against Air units, which are usually not reachable.
-➤ Can attack against AirProtect units, which are usually not reachable.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can attack while fortified and remain fortified afterwards.
+➤ Can attack units which are usually not reachable:
+ • Helicopter types.
+ • Sea units (with 50% penalty).
+ • Air and AirProtect units.
+➤ Cannot attack Submarines, Cruise Missiles, Nuclear Missiles, Tactical Nukes, Jet Bombers, Spy Planes, or Satellites.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
@@ -2244,9 +2534,9 @@
Anti-Aircraft Artillery
➤ Can Fortify, granting a 50% defensive bonus when not in a city.
• Gets the 50% defensive bonus automatically while in cities.
➤ Can Convert Unit
- • is converted into Mobile SAM (takes 4 turns).
- • must be converted in capital city with same nationality as the unit.
- • age of unit must be 10 turns or more
+ • is converted into Mobile SAM (takes 3 turns).
+ • must be converted in a domestic city with the same nationality as the unit.
+ • age of unit must be 9 turns or more
➤ May acquire veteran status.
• Veterans have increased strength in combat.
@@ -2262,12 +2552,12 @@
Anti-Aircraft Artillery
master
220%
15%
+ 1/9
champion
230%
-
+ 1/9
Transport Loading Logistics
-
+
Transport Unloading Logistics
-
+
-
+
Mobile SAM
Cost: 75 shields
@@ -2279,30 +2569,34 @@
Mobile SAM
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
The Mobile SAM is the strongest Anti-Air unit on the ground. It can attack nearby aircraft. It has a 2× bonus against all Air units. The Mobile SAM can also carry one Missile of any type. Unlike the AAA, it cannot attack non-native tiles or attack while transported.
+
The Mobile SAM is the strongest Anti-Air unit on the ground. It can attack nearby aircraft. It has a 2× bonus against all Air and Missile units. The Mobile SAM can also carry two Missiles of any type. Like the AAA, it can attack non-native tiles but not while transported. Transported Mobile SAMs can always defend their tile, even at sea. The discovery of Laser gives all units on its tile a 33% chance to survive nuclear attack.
-
-AAA can upgrade to Mobile SAM for free on its 10th turn of service. The Convert order must be done in the capital city and takes 4 turns.
+AAA can upgrade to Mobile SAM for free on its 9th turn of service. The Convert order must be done in a domestic city and takes 3 turns.➤ Belongs to Land unit class.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
+ • Can attack reachable units on ocean tiles.
• AIRLIFT: Can be airlifted if it has remaining moves.
• Can't attack as Cargo. Must first unload.
➤ 2× attack bonus when attacking Airplanes and Helicopters.
-➤ 2× defense bonus if attacked by Airplanes and Helicopters.
+➤ 2× defense bonus if attacked by Aircraft, Helicopter, or Cruise Missile.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
➤ May be obtained by conversion of Anti-Aircraft Artillery.
➤ Anti-Air bonus of this unit is less effective against Stealth, which has a 25% bonus against this unit.
-➤ Can carry and refuel 1 Missile unit.
+➤ Can carry and refuel up to 2 Missile units.
+ • Cargo not visible except to allies.
➤ May impose a zone of control on its adjacent tiles.
-➤ Can attack against Helicopter units, which are usually not reachable.
-➤ Can attack against Air units, which are usually not reachable.
-➤ Can attack against AirPillage units, which are usually not reachable.
-➤ Can attack against AirProtect units, which are usually not reachable.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can attack units which are usually not reachable:
+ • Helicopter types.
+ • Sea units (with 50% penalty).
+ • Air, AirProtect, and AirHighAltitude units.
+➤ Can defend units on its tile from Nuclear Attack:
+ • 33% chance of success, all units on tile will survive.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
@@ -2313,7 +2607,7 @@
Mobile SAM
• Veterans have increased strength in combat.
-
+
Howitzer
Cost: 70 shields
@@ -2325,13 +2619,14 @@
Howitzer
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
Howitzers are upgraded Artillery with a terrifying increase to mobility and attack strength. Fortifications get no bonus except their terrain bonus, and City Walls are completely ineffective against Howitzers.
+
Howitzers are upgraded Artillery with a terrifying increase to mobility and attack strength. City Walls are completely ineffective against Howitzers.
+If they haven't moved, Howitzers can do 7 rounds of Bombardment on four units, with one possible fatality. When special attacked from range, they can retaliate with the same type of Bombardment. This is a strong deterrent.
-While Howitzers are vulnerable when not escorted, they can retaliate 7 rounds of bombardment against special unit attacks from other units. This is a strong deterrent.
+VIGIL:the Vigil order lets Howitzers auto-Bombard adjacent military units if they have better attack odds. Can be done in cities or bases if it has used no moves.
➤ Belongs to Land unit class, Ballistic sub-class.
- • Fortifications are reduced to terrain bonus only.
• City Walls get no bonus against this unit.
+ • VIGIL: can be given the Vigil order to auto-Bombard adjacent military units.
• Can occupy empty enemy cities.
• Subject to zones of control.
• Slowed down while damaged.
@@ -2339,12 +2634,23 @@
Howitzer
• Can't attack as Cargo. Must first unload.
➤ Unable to attack air units.
➤ May impose a zone of control on its adjacent tiles.
-➤ Has Special Defense against Special Attacks / Ranged Attacks.
- • Gives 7 free rounds of combat against up to 4 units in an enemy stack.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can safely conduct Bombard Attack.
+ • Gives 7 free rounds of combat against up to 4 units in an enemy stack.
+ • Uses 2 movement points.
+ • A maximum of 1 unit can be killed.
+ • Can be done to ocean tiles if not transported.
+ • Cannot be done to Cities, Fortresses, Naval Bases, Castles, and Bunkers.
+ • Can do action while fortified and remain fortified afterwards.
+ • Action requires:
+ - a minimum of 2 move points
+ - Howitzer to have used no moves this turn.
+➤ Has Special Defense against Special Attacks / Ranged Attacks.
+ • Gives 7 free rounds of combat against up to 4 units in an enemy stack.
• A maximum of 1 unit can be killed.
➤ Can Expel
• target unit must be alone and not on Mountains.
- • uses one movement point
+ • uses ⅔ movement point
• actor stays on its own tile
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Conquer City
@@ -2371,7 +2677,7 @@
Howitzer
-
+
Balloon
Cost: 25 shields
@@ -2383,7 +2689,7 @@
Balloon
Firepower: 1
Hitpoints: 10
Obsolete by: None
-
Balloons gather intel on potential battle areas. They have great vision and are unreachable by units prior to Riflemen and Ironclads. They can stay in the air for one Turn Change before landing in a City, Quay, Base, or transport unit with cargo capacity of 4+. Balloons do not block units under them from being attacked, and cannot fly over mountains.
+
Balloons gather intel on potential battle areas. They have great vision and are unreachable by units prior to Riflemen and Ironclads. They can stay in the air for one Turn Change before landing in a City, Quay, Base, or transport unit with cargo capacity of 4+. Balloons give sentry reports of nearby unit movements even when not on sentry. Balloons do not block units under them from being attacked, and cannot fly over mountains.
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Balloon unit class.
@@ -2397,30 +2703,31 @@
Balloon
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ Unit must be in a City, Fort, Fortress, Naval Base, Airbase; or on a Galleon, Cargo Ship, Transport, or Carrier, after 2 turns.
➤ Can't be bribed.
➤ Can't be sabotaged.
➤ Will never achieve veteran status.
-
+
Zeppelin
Cost: 45 shields
Upkeep: 0
-
Moves: 6
+
Moves: 7
Vision: 4.90 tiles
Attack: 2
Defense: 1
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
As new technology inspires the upgrade of Balloons, the Zeppelin appears. In theory, it can be used for tactical advantage in combat. However, vulnerability and cost make it better for intel. It can do limited attack with bombs, or 20 combat rounds with medium caliber ordnance. It can stay out for two Turn Changes before landing in a City, Quay, Fortress, Naval Base, Airbase, or transport with 6+ capacity.
-Zeppelins have great vision and are unreachable by units prior to Marines and Destroyers. Zeppelins do not block units under them from being attacked. Unlike Balloons, they can fly over Mountains.
+
As new technology inspires the upgrade of Balloons, the Zeppelin appears. In theory, it can be used for tactical advantage in combat. However, vulnerability and cost make it better for intel. It can do limited attack with bombs, or 20 combat rounds with medium caliber ordnance. It has 7 turns of fuel so can stay out for many turns before landing in a City, Quay, Fortress, Naval Base, Airbase, or transport with 6+ capacity.
+Zeppelins have great vision and are unreachable by units prior to Marines and Destroyers. Zeppelins do not block units under them from being attacked. Zeppelins give sentry reports of nearby unit movements even when not on sentry. Unlike Balloons, they can fly over Mountains.
-NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to Zeppelin unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -2433,6 +2740,7 @@
Zeppelin
• A maximum of 1 unit can be killed.
• Can be done to Cities, Forts, Fortresses, Naval Bases, Land Tiles, and Oceanic Tiles.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Unit has to be in a City, Fortress, Naval Base, Airbase; or on a Carrier, Transport, or Train, after 3 turns.
➤ Can Attack
@@ -2455,8 +2763,8 @@
Zeppelin
ace
220%
15%
+ 1
top gun
230%
-
+ 1
-
-
+
+
Airplane
Cost: 50 shields
@@ -2468,22 +2776,23 @@
Airplane
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
Airplanes can carry a single diplomatic unit. They can land in any domestic or allied city. For missions to non-allied nations, an empty Airbase must be available for landing. Airplanes have two turns of fuel. Each new aviation tech upgrades range by +2 moves per turn.
+
Airplanes can carry a diplomatic unit or a Freight unit. They can land in any domestic or allied city. For missions to non-allied nations, an empty Airbase must be available for landing. Airplanes have two turns of fuel. Each new aviation tech upgrades range by +2 moves per turn.
-Airplanes can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to Air unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Never imposes a zone of control.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
-➤ Can carry and refuel 1 Diplomat or Spy.
+➤ Can carry 1 Emissary, Diplomat, or Spy.
• Cargo cannot be loaded or unloaded except in a City or a Airbase.
+ • Cargo not visible except to allies.
➤ Unit has to be in a City, Airbase, or on a Carrier after 2 turns.
➤ Can't be bribed.
➤ Can't be sabotaged.
@@ -2492,7 +2801,7 @@
Airplane
-
+
Fighter
Cost: 60 shields
@@ -2504,11 +2813,11 @@
Fighter
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Fighter
-
Fighters are the first offensive Air units. Their superior mobility and firepower alter the very nature of warfare. They can move anywhere and attack any unit (except Submarines and Jet Bombers.)
+
Fighters are the first offensive Air units. Their superior mobility and firepower alter the very nature of warfare. They can move anywhere and attack any unit except Submarines, Jet Bombers, Spy Planes, and Satellites. Fighters need fuel to avoid crashing and thus must end every turn in a city, Airbase, or Carrier. Air units can't conquer undefended enemy cities. Fighters can attack twice per turn.
INTERCEPTOR: the Vigil order lets Fighters auto-attack adjacent Air units if they have better attack odds. A Fighter can Vigil if it uses 2 move points or less.
-Fighters can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to AirProtect unit class.
• INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
• AIR COVER: Most units who cannot attack this unit also cannot attack other units on the tile.
@@ -2516,10 +2825,11 @@
Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ Can attack a maximum of 2 times per turn.
➤ Anti-Air units have bonuses against this unit.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Never imposes a zone of control.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Can attack against Missile units, which are usually not reachable.
@@ -2546,9 +2856,14 @@
Fighter
crack
210%
10%
+ 1
ace
220%
10%
+ 1
top gun
230%
-
+ 1
+
+Fighters types on Vigil will (usually) engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
-
+
Escort Fighter
Cost: 80 shields
@@ -2559,11 +2874,11 @@
Escort Fighter
Defense: 5
Firepower: 2
Hitpoints: 20
-
Obsolete by: Jet Fighter
-
Escort Fighters are large fighters with high fuel capacity. Higher mass and stronger construction allow them to absorb more damage. They do not attack as well as standard Fighters, but can return home on the next turn. They are good for long-range missions, defensive air support, and escorting bombers.
+
Obsolete by: Multi-Fighter
+
Escort Fighters are large fighters with high fuel capacity. Higher mass and stronger construction allow them to absorb more damage. They do not attack as well as standard Fighters, but can return home on the next turn. They are good for long-range missions, defensive air support, and escorting bombers. Escort Fighters can attack twice per turn.
INTERCEPTOR: the Vigil order lets Escort Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it uses 3 move points or less.
-This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to AirProtect unit class.
• INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
• AIR COVER: Most units who cannot attack this unit also cannot attack other units on the tile.
@@ -2571,11 +2886,13 @@
Escort Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ Can attack a maximum of 2 times per turn.
➤ Anti-Air units have bonuses against this unit.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Can attack against Missile units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
@@ -2600,21 +2917,27 @@
Escort Fighter
elite
200%
15%
-
crack
210%
15%
+ 1
ace
220%
15%
+ 1
-
top gun
230%
-
+ 1
+
top gun
230%
-
+ 1
+
+Fighters types on Vigil will (usually) engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
+
-
+
Dive Bomber
Cost: 60 shields
Upkeep: 1 Shield
Moves: 18
Vision: 3.46 tiles
-
Attack: 3
+
Attack: 4.5
Defense: 3
Firepower: 2
Hitpoints: 20
Obsolete by: Ground Strike Fighter
-
Dive Bombers are fitted with armor and armaments for Ground Attack and Sea combat. They do better attacking surface targets and defending vs Anti-Air, but have reduced range and Air-to-Air ability. They do not block attacks on their same tile, but exert ZoC on adjacent land tiles and can iPillage. The Dive Bomber is not an interceptor. Role specialization yields the following gains and losses over a Fighter:
+
Dive Bombers are fitted with armor and armaments for Ground Attack and Sea combat. They do better attacking surface targets and defending vs Anti-Air, but have reduced range and Air-to-Air ability. They do not block attacks on their same tile, but exert ZoC on adjacent land tiles and can iPillage. The Dive Bomber is not an interceptor, and can only attack once per turn. Role specialization yields the following gains and losses over a Fighter:
GAINS:
• A:4½ vs Surface units (Land or Sea)
@@ -2624,27 +2947,30 @@
Dive Bomber
LOSSES:
• A3 instead of A4 in Air-to-Air
• Can't block its tile
+• One attack per turn.
• Can't intercept
-Communists pay 10 less for this unit. Can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. GOTO prevents unit loss from lack of fuel. Override this by ordering adjacent moves.
+Communists pay 10 less for this unit. Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. GOTO prevents unit loss from lack of fuel. Override this by ordering adjacent moves.
➤ Belongs to Air unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Ground Strike against tile infrastructure.
• 50% odds of success. +5% for each veteran level.
- • Destroys 1 selected infrastructural improvement on the tile, instantly.
+ • Destroys 1 selected infrastructural improvement on the tile, instantly.
• Uses 6 movement points. May result in Dive Bomber being unable to re-fuel.
-➤ +50% attack bonus when attacking all Surface units:
+➤ Can attack a maximum of 1 time per turn.
+➤ Anti-Air units have bonuses against this unit.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
+➤ Attacks at 4.5 when attacking all Surface units:
• Applies to all Land units
• Applies to all Sea units (except Submarines are unreachable.)
+➤ Attacks at 3 when attacking Air units.
➤ +33% defense bonus if attacked by Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
-➤ Anti-Air units have bonuses against this unit.
➤ May impose a zone of control on its adjacent tiles.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Can attack against Helicopter units, which are usually not reachable.
@@ -2671,7 +2997,7 @@
Dive Bomber
top gun
230%
-
+ 1
-
+
Medium Bomber
Cost: 85 shields
@@ -2683,23 +3009,28 @@
Medium Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Bomber
-
The Medium Bomber is well suited for moderately strong targets or multiple weak targets. It may not attack other Air units. It is a Soft Field Unit and can't carry Bomb units, causing less discontent than other types of Bombers.
+
The Medium Bomber is well suited for hitting a moderately strong target. It may not attack other Air units, and can only attack once per turn. It can only carry conventional Bombs, not nuclear bombs units. This makes it a Soft Field Unit, causing less discontent than other types of Bombers.
➤ Unhappy effect for Soft Field units:
Republic:0 Democracy:1 (non-aggressive)
Republic:1 Democracy:2 (aggressive)
-This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to Air unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ Can attack a maximum of 1 time per turn.
➤ Anti-Air units have bonuses against this unit.
-➤ Unable to attack air units.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
+➤ Unable to attack Air units.
+➤ Can carry and refuel 1 Bombs unit.
+ • Can't carry nuclear bombs.
+ • Cargo cannot be loaded except in a city or a base native to this transport.
+ • Cargo not visible except to allies.
➤ Never imposes a zone of control.
➤ Soft Field unit: unhappiness may apply even when non-aggressive.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
@@ -2724,7 +3055,7 @@
Medium Bomber
top gun
230%
-
+ 1
-
+
Heavy Bomber
Cost: 120 shields
@@ -2736,24 +3067,26 @@
Heavy Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Bomber
-
Heavy Bombers are large and sturdy Bombers with larger payload and numerous gunner defense stations. They are excellent for hitting strong well-defended ground targets. They cannot attack Air units. They can carry one Bomb. Like most Bombers, the Heavy Bomber is a Field Unit. Field Units cause the same unhappiness no matter if aggressively or peacefully deployed.
+
Heavy Bombers are large and sturdy Bombers with larger payload and numerous gunner defense stations. They are excellent for hitting strong well-defended ground targets, and can attack once per turn. They cannot attack Air units. They can carry one Bomb. Like most Bombers, the Heavy Bomber is a Field Unit. Field Units cause the same unhappiness no matter if aggressively or peacefully deployed.
➤ Unhappy effect for Field units:
Republic:1 Democracy:2
-This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to Air unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ Can attack a maximum of 1 time per turn.
➤ Anti-Air units have bonuses against this unit.
-➤ Unable to attack air units.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
+➤ Unable to attack Air units.
➤ Can carry and refuel 1 Bomb unit.
• Cargo cannot be loaded except in a city or a base native to this transport.
+ • Cargo not visible except to allies.
➤ Never imposes a zone of control.
➤ Field unit: one unhappiness applies even when non-aggressive.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
@@ -2778,7 +3111,7 @@
Heavy Bomber
top gun
230%
-
+ 1
-
+
Strategic Bomber
Cost: 135 shields
@@ -2790,26 +3123,28 @@
Strategic Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: Jet Bomber
-
The Strategic Bomber offers a small improvement in attack. Higher altitude offers a good upgrade in defense. It has considerably longer range, with one more turn of fuel. Upgraded payload enables instant-Pillage bombing. May carry two Bombs. May carry two Bombs.
+
The Strategic Bomber can do two attacks per turn. Higher altitude offers a good upgrade in defense. It has considerably longer range, with one more turn of fuel. Upgraded payload enables instant-Pillage bombing. May carry two Bombs. May carry two Bombs.
-This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to Air unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Bomb against tile infrastructure.
• 60% odds of success. +5% for each veteran level.
• Destroys 1 random infrastructural improvement on the tile, instantly.
• Uses 3 movement points. May result in Strategic Bomber being unable to re-fuel.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ Can attack a maximum of 2 times per turn.
➤ Anti-Air units have bonuses against this unit.
-➤ Unable to attack air units.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
+➤ Unable to attack Air units.
➤ Can carry and refuel up to 2 Bomb units.
• Cargo cannot be loaded except in a city or a base native to this transport.
+ • Cargo not visible except to allies.
➤ Never imposes a zone of control.
➤ Field unit: one unhappiness applies even when non-aggressive.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
@@ -2834,7 +3169,7 @@
Strategic Bomber
top gun
230%
-
+ 1
-
+
AWACS
Cost: 140 shields
@@ -2848,17 +3183,18 @@
AWACS
Obsolete by: Spy Plane
The AWACS has great fuel capacity for long range flights, and advanced radar that can determine the location of enemy units over a wide area. The AWACS has 2 turns of fuel.
-AWACS can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to Air unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Unit must be in a City, Airbase, or on a Carrier after 2 turns.
➤ Can't be bribed.
@@ -2868,7 +3204,7 @@
AWACS
-
+
Spy Plane
Cost: 160 shields
@@ -2882,7 +3218,7 @@
Spy Plane
Obsolete by: None
The Spy Plane is an ultrasonic high altitude aircraft with long range. It gathers intel over a wide area. Stealth technology makes it invisible, except to enemies on adjacent tiles. Spy Planes have 2 turns of fuel. They are unreachable to primitive propeller based aircraft.
-Spy Planes can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining moves than the attacker.
+Stack-Escape:67% odds to evade stack-death if attacker doesn’t have more moves than it does.
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air_High_Altitude unit class.
• Invisible except when next to an enemy unit or city.
@@ -2891,11 +3227,12 @@
Spy Plane
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Is invisible except when next to an enemy unit or city.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Unit has to be in a City, Airbase, or on a Carrier after 2 turns.
➤ Can't be bribed.
@@ -2904,7 +3241,7 @@
Spy Plane
-
+
Satellite
Cost: 140 shields
@@ -2918,7 +3255,7 @@
Satellite
Obsolete by: None
Satellites orbit the Earth above the atmosphere. They are unreachable to anything but missiles. Their orbital velocity allows them to circumnavigate the planet in a couple turns. Telescopic cameras allow them to Investigate City, even if the city has a Police Station. Satellites cannot be seen by others unless adjacent. Satellites require Laser tech, and can only be built in the city with the Apollo Program.
-Satellites can Stack-Escape:a 100% chance to escape a killed stack if they have more remaining move points than the attacker.
+Stack-Escape:100% odds to evade stack-death if attacker doesn’t have more moves than it does.
NOTE: Due to game mechanics, Satellites block terrestrial movement over a tile. This is solved by killing it with any Missile. To avoid this, end your turn far away from developed or trafficked areas.➤ Belongs to Space unit class.
• Speed is not affected by terrain.
@@ -2926,17 +3263,18 @@
Satellite
• Not subject to zones of control.
• Unreachable. Only missile types can attack this unit.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
-➤ STACK ESCAPE: 100% odds to escape if stack defender loses, if it has more moves left than attacker.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ STACK ESCAPE: 100% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Is invisible except when next to an enemy unit or city.
➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
➤ Can't be bribed.
➤ Can't be sabotaged.
➤ Will never achieve veteran status.
-
+
Transport Helicopter
Cost: 90 shields
@@ -2949,11 +3287,11 @@
Transport Helicopter
Hitpoints: 21
Obsolete by: None
Transport Helicopters can carry three land units, which they can pick up from anywhere
- Foot and artillery types can unload by stepping off to any adjacent tile, but will lose all moves (except Marine types). Other types need a City, Airbase, or Naval Base to unload.
+Foot and artillery types can unload by stepping off to any adjacent tile, but will lose all moves (except Marine types). Other types need a City, Airbase, or Naval Base to unload.
- Helicopters lose 1 hp for each turn not ended in a City, Base, or Carrier. +1 move is awarded if starting a turn in a City or Airbase.
+Helicopters lose 1 hp for each turn not ended in a City, Base, or Carrier. +1 move is awarded if starting a turn in a City or Airbase. The first turn spent in a city, all Helicopter types regain 10hp.
- All Helicopters can be attacked by land from Riflemen onward; by sea from Ironclad onward; get +50% defence vs. foot units; and are unreachable to artillery types. Unlike attack Helicopters, Transport Helicopters are intercepted by Fighters on vigil. Transport Helicopters can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker.
+All Helicopters can be attacked by land from Riflemen onward; by sea from Ironclad onward; get +50% defence vs. foot units; and are unreachable to artillery types. Unlike attack Helicopters, Transport Helicopters are intercepted by Fighters on vigil. Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to Helicopter unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
@@ -2961,13 +3299,15 @@
Transport Helicopter
• Unreachable. Artillery, Howitzer, and units prior to Riflemen and Ironclad can't attack this unit.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ +50% defense bonus if attacked by Foot Soldiers or Cavalry.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Anti-Air units have bonuses against this unit.
➤ Loses 1hp for every turn in the air.
➤ Gets +1 move point if starting its turn in a City or Airbase.
-➤ Can carry and refuel up to 3 qualifying Land units.
+➤ The first turn spent in a city, all Helicopter types regain 10hp.
+➤ Can carry up to 3 qualifying Land units.
• Some cargo cannot be loaded or unloaded except in a City or a Base native to this transport.
+ • Cargo not visible except to allies.
➤ Never imposes a zone of control.
➤ Can't be bribed.
➤ Can't be sabotaged.
@@ -2986,7 +3326,7 @@
Transport Helicopter
-
+
Helicopter
Cost: 95 shields
@@ -3000,12 +3340,12 @@
Helicopter
Obsolete by: None
Helicopters are the Air Cavalry of modern armies. They can attack multiple targets and conquer cities. Unaffected by terrain, they are good for Partisan suppression. They can transport Marines and AAA and are thus well-suited for commando ops.
-For each turn not ended in a City, Base, or Carrier, 1hp is lost. +1 move is awarded if starting a turn in a City or Airbase.
+For each turn not ended in a City, Base, or Carrier, 1hp is lost. +1 move is awarded if starting a turn in a City or Airbase. The first turn spent in a city, all Helicopter types regain 10hp.
Helicopters fly low to avoid Interception. This invites attacks by ground units from Riflemen onward. They are unreachable to Artillery types and get +50% defense vs. foot units. Helicopters can be attacked by Sea from Ironclad onward.
Helicopters can retaliate 3 rounds against Special Unit Attacks.
-Helicopters can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. Helicopters can transport one Marines or AAA unit.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. Helicopters can transport one Marines or AAA unit.➤ Belongs to Helicopter unit class.
• Can occupy empty enemy cities.
• Speed is not affected by terrain.
@@ -3014,12 +3354,15 @@
Helicopter
• Unreachable. Artillery, Howitzer, and units prior to Riflemen and Ironclad can't attack this unit.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ +50% defense bonus if attacked by Foot Soldiers or Cavalry.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Anti-Air units have bonuses against this unit.
➤ Loses 1hp for every turn in the air.
➤ Gets +1 move point if starting its turn in a City or Airbase.
-➤ Can carry and refuel 1 Marines or Anti-Aircraft Artillery unit.
+➤ The first turn spent in a city, all Helicopter types regain 10hp.
+➤ Can carry 1 Marines or Anti-Aircraft Artillery unit.
+ • Cargo not visible except to allies.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 3 free rounds of combat against up to 3 units in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -3044,23 +3387,23 @@
Helicopter
top gun
230%
-
+ 1
-
+
Jet Fighter
Cost: 70 shields
Upkeep: 1 Shield
Moves: 24
Vision: 3.46 tiles
-
Attack: 6
-
Defense: 5
+
Attack: 7
+
Defense: 6
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Jet Fighters upgrade the Fighter with improved attack, defense, and range.
+
Jet Fighters upgrade the Fighter with improved attack, defense, and range. A Jet Fighter can do a maximum of three attacks per turn.
INTERCEPTOR: the Vigil order lets Jet Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it uses 3 move points or less.
-Helicopters can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to AirProtect unit class.
• INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
• AIR COVER: Most units who cannot attack this unit also cannot attack other units on the tile.
@@ -3068,16 +3411,16 @@
Jet Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Anti-Air units have bonuses against this unit.
➤ Never imposes a zone of control.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
➤ Can attack against Missile units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can attack against Air units, which are usually not reachable.
-➤ Can attack against AirPillage units, which are usually not reachable.
+➤ Can attack against Air_High_Altitude units, which are usually not reachable.
➤ Can attack against AirProtect units, which are usually not reachable.
➤ Unit must be in a City, Airbase, or on a Carrier after 1 turn.
➤ Can Expel Airplane and AWACS.
@@ -3097,37 +3440,102 @@
Jet Fighter
crack
210%
10%
+ 1
ace
220%
10%
+ 1
top gun
230%
-
+ 1
+
+Fighters types on Vigil will (usually) engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
-
+
+
Multi-Fighter
+
+
Cost: 90 shields
+
Upkeep: 1 Shield
+
Moves: 22
+
Vision: 3.46 tiles
+
Attack: 6
+
Defense: 7
+
Firepower: 2
+
Hitpoints: 20
+
Obsolete by: None
+
Multi-Fighters are fitted for mission versatility. They are sturdy, defend better, and have two turns of fuel. They are good for escorting bombers, pursuing enemy bombers on long range missions, defensive ground support, naval missions, reconnaissance, and interception. A Multi-Fighter can vigil at any range, and can do a maximum of two attacks per turn.
+
+INTERCEPTOR:the Vigil order lets Multi-Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it has 1 turn of fuel or more.
+
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+➤ Belongs to AirProtect unit class.
+ • INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
+ • AIR COVER: Most units who cannot attack this unit also cannot attack other units on the tile.
+ • Speed is not affected by terrain.
+ • Does not get defense bonuses from terrain.
+ • Not subject to zones of control.
+ • Unreachable. Most units cannot attack this one.
+ • Doesn't prevent enemy cities from working the tile it's on.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
+➤ Anti-Air units have bonuses against this unit.
+➤ Never imposes a zone of control.
+➤ Reconnaissance: reports visible enemy movement even if not sentried.
+➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
+>➤ Can attack against Helicopter units, which are usually not reachable.
+➤ Can attack against Air units, which are usually not reachable.
+➤ Can attack against Air_High_Altitude units, which are usually not reachable.
+➤ Can attack against AirProtect units, which are usually not reachable.
+➤ Unit must be in a City, Airbase, or on a Carrier after 1 turn.
+➤ Can Expel Airplane and AWACS.
+➤ Attacking with fractional move points gives fractional attack power.
+➤ Can't be bribed.
+➤ Can't be sabotaged.
+➤ May acquire veteran status.
+
+Veteran Levels for Multi-Fighters are the same as Escort Fighters:
+
+
Veteran level
Power factor
Promotion Odds
Move bonus
+
+
green
100%
50%
-
+
veteran
150%
33%
-
+
hardened
175%
20%
-
+
elite
200%
15%
-
+
crack
210%
15%
+ 1
+
ace
220%
15%
+ 1
+
top gun
230%
-
+ 1
+
+Fighters types on Vigil will (usually) engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
+
+
+
Ground Strike Fighter
Cost: 80 shields
Upkeep: 1 Shield
Moves: 21
Vision: 3.46 tiles
-
Attack: 7
-
Defense: 4
+
Attack: 8
+
Defense: 5
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Ground Strike Fighters upgrade the Dive Bomber. They cannot block attacks on other units on their tile, nor engage against Fighters. However, like the Dive Bomber, they can interdict ZoC over land tiles. High fuel capacity allows two turns in the air. No unit can stop this unit from attacking surface units on a tile. This unit can also do pinpointed surgical strikes to pillage tiles. This aircraft is a specialized niche unit. It is not an interceptor.
-This aircraft can Stack-Escape:a 60% chance to escape a killed stack if it has more remaining moves than the attacker. NOTE:GOTO prevents unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+
Ground Strike Fighters upgrade the Dive Bomber. They cannot block attacks on other units on their tile, nor engage against Fighters. However, like the Dive Bomber, they can interdict ZoC over land tiles. High fuel capacity allows two turns in the air. No unit can stop this unit from attacking surface units on a tile. This unit has no limit to attacks per turn and can also do pinpointed surgical strikes to pillage tiles. This aircraft is a specialized niche unit. It is not an interceptor.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO prevents unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Ground Strike against tile infrastructure.
• 75% odds of success. +5% for each veteran level.
- • Destroys 1 selected infrastructural improvement on the tile, instantly.
+ • Destroys 1 selected infrastructural improvement on the tile, instantly.
• Uses 3 movement points. May result in Ground Strike Fighter being unable to re-fuel.
➤ +33% defense bonus if attacked by Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Anti-Air units have bonuses against this unit.
➤ May impose a zone of control on its adjacent tiles.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
@@ -3153,7 +3561,7 @@
Ground Strike Fighter
top gun
230%
-
+ 1
-
+
Jet Bomber
Cost: 145 shields
@@ -3165,27 +3573,28 @@
Jet Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
Jet Bombers are stratospheric long-range bombers with 3 turns of fuel: they can be airborne for two turn-changes. Their range makes them useful even after Stealth Bombers are available. Stratospheric altitude is unreachable to propeller-based Fighters and AAA. Jet Bombers can carpet-bomb, pillaging tiles from the air. May carry three Bombs.
+
Jet Bombers are stratospheric long-range bombers with 3 turns of fuel: they can be airborne for two turn-changes. Their range makes them useful even after Stealth Bombers are available. A Jet Bomber may do a maximum of three attacks per turn. Stratospheric altitude is unreachable to propeller-based Fighters and AAA. Jet Bombers can carpet-bomb, pillaging tiles from the air. May carry three Bombs.
-Jet Bombers can Stack-Escape:a 60% chance to escape a killed stack if they have more remaining moves than the attacker. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does. NOTE:GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.
➤ Belongs to Air_High_Altitude unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one, including AAA and propeller aircraft.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can safely iPillage: Instant Carpet Bomb against tile infrastructure.
• 75% odds of success. +5% for each veteran level.
• Destroys up to 2 random infrastructural improvements on the tile, instantly.
• Odds of hitting targets are calculated separately.
• Uses 3 movement points. May result in Jet Bomber being unable to re-fuel.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Anti-Air units have bonuses against this unit.
➤ Unable to attack air units.
➤ Can carry and refuel up to 3 Bomb units.
• Cargo cannot be loaded except in a city or a base native to this transport.
+ • Cargo not visible except to allies.
➤ Never imposes a zone of control.
➤ Field unit: one unhappiness applies even when non-aggressive.
➤ INTERCEPTED by Fighter types on Vigil: they auto-attack if they have better odds attacking than defending.
@@ -3208,7 +3617,7 @@
Jet Bomber
top gun
230%
-
+ 1
-
+
Stealth Fighter
Cost: 80 shields
@@ -3220,11 +3629,11 @@
Stealth Fighter
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
The most advanced Fighter, with improved attack and longer range. Stealth Fighters cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to a smaller 25% bonus.
+
The most advanced Fighter, with improved attack and longer range. Stealth Fighters cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to a smaller 25% bonus. A Stealth Fighter has no limit to the number of attacks per turn it can do.
INTERCEPTOR: the Vigil order lets Stealth Fighters auto-attack adjacent Air units if they have better attack odds. This unit can Vigil if it uses 4 move points or less.
-Stealth Fighters can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining moves than the attacker.
+Stack-Escape:67% odds to evade stack-death if attacker doesn’t have more moves than it does.
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to AirProtect unit class.
• INTERCEPTOR: can be given the Vigil order to auto-attack adjacent Air units.
@@ -3233,11 +3642,11 @@
Stealth Fighter
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ +25% attack bonus when attacking Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
➤ +25% defense bonus if attacked by Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Anti-Air units have bonuses against this unit.
➤ Unreachable AND can protect its tile from units unable to attack this unit.
➤ Invisible except when next to an enemy unit or city.
@@ -3246,7 +3655,7 @@
Stealth Fighter
➤ Can attack against Missile units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can attack against Air units, which are usually not reachable.
-➤ Can attack against AirPillage units, which are usually not reachable.
+➤ Can attack against Air_High_Altitude units, which are usually not reachable.
➤ Can attack against AirProtect units, which are usually not reachable.
➤ Unit must be in a City, Airbase, or on a Carrier after 1 turn.
➤ Can Expel Airplane and AWACS.
@@ -3266,9 +3675,14 @@
Stealth Fighter
crack
210%
10%
+ 1
ace
220%
10%
+ 1
top gun
230%
-
+ 1
+
+Fighters types on Vigil will (usually) engage the following units:
+
+* Note: Propeller-type fighters are unable to reach high altitude aircraft (Jet Bomber, Spy Plane.)
+
-
+
Stealth Bomber
Cost: 160 shields
@@ -3280,9 +3694,9 @@
Stealth Bomber
Firepower: 2
Hitpoints: 20
Obsolete by: None
-
The deadliest Bomber, with improved attack and speed. Stealth Bombers cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to only a 25% bonus. May carry two Bombs.
+
The deadliest Bomber, with improved attack and speed. Stealth Bombers cannot be seen unless adjacent to an enemy. Stealth evasion gives a 25% bonus against all Anti-Air units, and reduces SAM Batteries down to only a 25% bonus. A Stealth Bomber may do a maximum of three attacks per turn. Stealth Bombers can Bome Strike, an instant-Pillage of tiles from the air. May carry two Bombs.
-Stealth Bombers can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining moves than the attacker.
+Stack-Escape:67% odds to evade stack-death if attacker doesn’t have more moves than it does.
NOTE: GOTO disallows unit loss from lack of fuel. You can override this by ordering moves to adjacent tiles.➤ Belongs to Air unit class.
• Speed is not affected by terrain.
@@ -3290,15 +3704,16 @@
Stealth Bomber
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ +25% attack bonus when attacking Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
➤ +25% defense bonus if attacked by Anti-Aircraft Artillery, Mobile SAM, or AEGIS Cruiser.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Anti-Air units have bonuses against this unit.
➤ Unable to attack air units.
➤ Can carry and refuel up to 2 Bomb units.
• Cargo cannot be loaded except in a city or a base native to this transport.
+ • Cargo not visible except to allies.
➤ Invisible except when next to an enemy unit or city.
➤ Never imposes a zone of control.
➤ Field unit: one unhappiness applies even when non-aggressive.
@@ -3322,12 +3737,12 @@
Stealth Bomber
top gun
230%
-
+ 1
-
+
Boat
Cost: 10 shields
Upkeep: 0
-
Moves: 4
+
Moves: 5
Vision: 2.00 tiles
Attack: 0
Defense: 1
@@ -3340,8 +3755,10 @@
Boat
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
+ • Crew RepairAutomatically repairs +1hp of its damage each turn, regardless of movement.
➤ Can help build a Wonder in any non-hostile city.
-➤ Can carry and refuel 1 Land, LandNoKill, or LandAirSea unit.
+➤ Can carry 1 Land, LandNoKill, or LandAirSea unit.
+ • Cargo not visible except to allies.
➤ Must stay next to safe coast.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ A non-military unit:
@@ -3349,14 +3766,11 @@
Boat
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
➤ Never imposes a zone of control.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Boat.
@@ -3379,7 +3793,7 @@
Boat
sea wolf
230%
-
+ 1
-
+
Trireme
Cost: 20 shields
@@ -3393,29 +3807,30 @@
Trireme
Obsolete by: Galley
Triremes are used for exploration, transport, and Commerce. The entry on Caravan explains what Commerce units may do.
-
-Triremes can enter Deep Ocean, but there is risk: they must end every second turn on river, coastline, or a city—or else be lost at sea. They can attack and travel on rivers, but cannot attack the shore.
+Triremes can enter Deep Ocean, but there is risk: they must end every second turn on river, coastline, or a city—or else be lost at sea. They can attack and travel on rivers, but cannot attack the shore.
-
-Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in a victor. Triremes do not cause unhappiness.
+Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in a victor. Triremes do not cause unhappiness.➤ Belongs to Trireme unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+1hp)
+➤ Can enter foreign territory regardless of peace treaty.
➤ Can help build a Wonder in any non-hostile city.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 2 Land, LandNoKill, or LandAirSea units.
+➤ Can carry up to 2 Land, LandNoKill, or LandAirSea units.
+ • Cargo not visible except to allies.
+➤ Can only attack units on native tiles.
+ • Battle will last 15 combat rounds, which may or may not result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Unit must be next to safe coast, in a city or a base after 2 turns.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Trireme.
@@ -3439,7 +3854,7 @@
Trireme
sea wolf
230%
-
+ 1
-
+
Longboat
Cost: 25 shields
@@ -3453,26 +3868,28 @@
Longboat
Obsolete by: Caravel
The Longboat is a warship. It can attack at sea, attack the shore, and carry one land unit. It is useful for ancient sea campaigns. Longboats can travel on rivers.
-
-Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+1hp)
➤ Unable to attack air units.
-➤ Can carry and refuel 1 Land, LandNoKill, or LandAirSea unit.
+➤ Can carry 1 Land, LandNoKill, or LandAirSea unit.
+ • Cargo not visible except to allies.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 15 combat rounds, which may or may not result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Caravel or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
➤ May acquire veteran status.
• Veterans have increased strength in combat.
-Veteran Levels for Zeppelins:
+Veteran Levels for Longboat:
Veteran level
Power factor
Promotion Odds
Move bonus
@@ -3485,12 +3902,12 @@
Longboat
sea wolf
230%
-
+ 1
-
+
Galley
Cost: 30 shields
Upkeep: 1 Shield
-
Moves: 6
+
Moves: 7
Vision: 2.00 tiles
Attack: 2
Defense: 2
@@ -3499,26 +3916,27 @@
Galley
Obsolete by: Caravel
The Galley is an all-purpose sea unit and upgrades the Trireme. It has decent combat strength, can transport, and is a Commerce unit. The entry on Caravan explains what Commerce units may do. Galleys can attack and travel on rivers, but cannot attack the shore. Galleys do not cause unhappiness.
-
-Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hp lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+1hp)
+➤ Can enter foreign territory regardless of peace treaty.
➤ Can help build a Wonder in any non-hostile city.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 2 Land, LandNoKill, or LandAirSea units.
+➤ Can carry up to 2 Land, LandNoKill, or LandAirSea units.
+ • Cargo not visible except to allies.
➤ Can only attack units on native tiles.
+ • Battle will last 15 combat rounds, which may or may not result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Galley.
@@ -3542,12 +3960,12 @@
Galley
sea wolf
230%
-
+ 1
-
+
War Galley
Cost: 40 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 6
+
Moves: 7
Vision: 2.00 tiles
Attack: 3
Defense: 3
@@ -3556,19 +3974,21 @@
War Galley
Obsolete by: Caravel
The War Galley has improved offense, defense, and cargo capacity, but lacks commerce ability. War Galleys can attack and travel on rivers, and do shore attacks from the sea.
-
-Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
+Like most ancient sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+1hp)
➤ Unable to attack air units.
-➤ Can carry and refuel up to 3 Land, LandNoKill, or LandAirSea units.
+➤ Can carry up to 3 Land, LandNoKill, or LandAirSea units.
+ • Cargo not visible except to allies.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 15 combat rounds, which may or may not result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Caravel or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3588,7 +4008,7 @@
War Galley
sea wolf
230%
-
+ 1
-
+
Ram Ship
Cost: 35 shields
@@ -3606,10 +4026,13 @@
Ram Ship
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+1hp)
➤ Unable to attack air units.
➤ Can only attack units on native tiles.
+ • Battle will result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Caravel or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3630,12 +4053,12 @@
Ram Ship
sea wolf
230%
-
+ 1
-
+
Caravel
Cost: 40 shields
Upkeep: 1 Shield
-
Moves: 7
+
Moves: 8
Vision: 2.00 tiles
Attack: 4
Defense: 4
@@ -3644,26 +4067,26 @@
Caravel
Obsolete by: Galleon
Caravels upgrade older sea units, and are a big advance in combat, speed, range, and transport capacity. They have the commerce ability of the Galley, but cannot build Wonders. Caravels can attack and travel on rivers and do shore attacks. As a Commerce unit, the Caravel does not cause unhappiness.
-
-Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates an attack, there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+1hp)
+➤ Can enter foreign territory regardless of peace treaty.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 3 Land, LandNoKill, or LandAirSea units.
+➤ Can carry up to 3 Land, LandNoKill, or LandAirSea units.
+ • Cargo not visible except to allies.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 15 combat rounds, which may or may not result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Upgrade
• upgrades to Galleon or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3683,38 +4106,39 @@
Caravel
sea wolf
230%
-
+ 1
-
+
Galleon
Cost: 40 shields
Upkeep: 1 Shield
-
Moves: 8
+
Moves: 9
Vision: 2.00 tiles
Attack: 2
Defense: 3
Firepower: 1
Hitpoints: 20
Obsolete by: Transport
-
The Galleon is a heavily armed transport ship that can carry up to 4 units. Galleons can attack and travel on rivers, and also do shore attacks. Galleon crews repair their ship an extra +1hp per turn, regardless of whether it has moved. Although the Galleon is not a Commerce unit, it retains one vestige of the commercial abilities for the ships it upgrades. It can enter Peace waters to deliver commercial cargo, and does not cause unhappiness.
+
The Galleon is a heavily armed transport ship that can carry up to 4 units. Galleons can attack and travel on rivers, and also do shore attacks. Galleon crews repair their ship an extra +2hp per turn, regardless of whether it has moved. Although the Galleon is not a Commerce unit, it retains one vestige of the commercial abilities for the ships it upgrades. It can enter Peace waters to deliver commercial cargo, and does not cause unhappiness.
-
-Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
+Like most early sea units, when it initiates combat there will be 15 rounds of combat and 15 total hitpoints lost between both units. This may or may not result in the loss of one of the units.
-
-Galleons can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining moves than the attacker.
+Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+2hp)
+➤ Can enter foreign territory regardless of peace treaty.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 4 Land, LandNoKill, LandAirSea, or Balloon units.
+➤ Can carry up to 4 Land, LandNoKill, LandAirSea, or Balloon units.
+ • Cargo not visible except to allies.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 15 combat rounds, which may or may not result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
-➤ Can attack against Submarine units, which are usually not reachable.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Transport.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3734,36 +4158,37 @@
Galleon
sea wolf
230%
-
+ 1
-
+
Frigate
Cost: 50 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 8
+
Moves: 10
Vision: 2.00 tiles
Attack: 4
Defense: 3
Firepower: 1
Hitpoints: 20
Obsolete by: Ironclad
-
The Frigate is versatile — it's a superior offensive unit and also a decent transport ship. Frigates can attack and travel on rivers, or make shore attacks from sea. Frigate crews repair their ship an extra +1hp per turn, regardless of whether it has moved.
+
The Frigate is versatile — it's a superior offensive unit and also a decent transport ship. Frigates can attack and travel on rivers, or make shore attacks from sea. Frigate crews repair their ship an extra +2hp per turn, regardless of whether it has moved.
-
-From Frigates onward, all sea attacks end with a single victor. Frigates can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining move points than the attacker.
+From Frigates onward, all sea attacks end with a single victor. Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to RiverShip unit class.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+2hp)
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 2 Land, LandNoKill, or LandAirSea units.
+➤ Can carry up to 2 Land, LandNoKill, or LandAirSea units.
+ • Cargo not visible except to allies.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will result in a victor.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
-➤ Can attack against Submarine units, which are usually not reachable.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Upgrade
• upgrades to Ironclad or, when possible, to the unit type it upgrades to.
➤ Attacking with fractional move points gives fractional attack power.
@@ -3783,12 +4208,66 @@
Frigate
sea wolf
230%
-
+ 1
-
+
+
Trawler
+
+
Cost: 30 shields
+
Upkeep: 0
+
Moves: 9
+
Vision: 2.00 tiles
+
Attack: 0
+
Defense: 1
+
Firepower: 1
+
Hitpoints: 15
+
Obsolete by: None
+
Trawlers are high-powered work-boats used for fishing and ocean work. They work at the same rate as Workers, and can put down Fishtraps, Buoys, Sea Bridges, Road types, or clean Pollution and Fallout. Trawlers can carry one Land unit.
+
+Trawlers can tow ships who haven’t moved, via a Load, Board, or Embark order. Towing helps damaged ships return to port for repair. Crew repair rates for towed ships are improved by letting the ships rest while towed.
+
+When towing, encumbrance slows the Trawler to 6 move points. Remaining moves reduce proportional to this slower speed. If a Trawler stops towing a ship via Unload or Deboard, it regains pro-rated moves proportional to its natural speed.
+
+Towed ships lose all moves after release.
+➤ Belongs to RiverShip unit class.
+ • Speed is not affected by terrain.
+ • Does not get defense bonuses from terrain.
+ • Not subject to zones of control.
+ • Slowed down while damaged.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
+➤ Never imposes a zone of control.
+➤ Can build some types of infrastructure.
+ • Can build Quay, Road, Railroad, Maglev, Sea Bridge, and Canal on tiles.
+ • Can build Buoy.
+ • Can build Fishtrap.
+ * must be adjacent to a Fish resource
+ * must NOT be cardinally adjacent to another Fishtrap
+➤ Can clean Pollution or Fallout from river tiles.
+➤ Can carry and refuel 1 Cargo, Land, LandNoKill, LandAirSea, or LandRoad unit.
+ • Cargo is visible to all nations who can see the Trawler.
+➤ Can "tow" a ship or submarine as cargo.
+ • Can't tow other Trawlers.
+ • Towing reduces the remaining percentage of move points proportional to a move speed of 6 move points per turn.
+ • Pro-rated move points for the speed of 9 move points are proportionally recovered when a towed ship is released.
+ • Towed ships are visible to all nations who can see the Trawler.
+ • Towed ships lose all moves after unload, deboard, or disembark.
+ • Towed ships who take no action during the turn will experience 10% crew repair and 10% resting repair for a total of 20% repair.
+ ➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
+➤ A non-military unit:
+ • Cannot attack.
+ • Doesn't impose martial law.
+ • Can enter foreign territory regardless of peace treaty.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Capturable: can be captured on rivers, if conditions allow it.
+➤ Expellable: can be expelled from rivers located on foreign tiles.
+➤ Will never achieve veteran status.
+
+
+
+
Cargo Ship
Cost: 45 shields
Upkeep: 0
-
Moves: 7
+
Moves: 8
Vision: 2.00 tiles
Attack: 0
Defense: 1
@@ -3801,23 +4280,21 @@
Cargo Ship
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
➤ Can help build a Wonder in any non-hostile city.
➤ Can carry and refuel up to 4 Land, LandNoKill, LandAirSea, or Balloon units.
+ • Cargo not visible except to allies.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Cargo Ship.
@@ -3825,12 +4302,12 @@
Cargo Ship
➤ Will never achieve veteran status.
-
+
Transport
Cost: 50 shields
Upkeep: 1 Shield
-
Moves: 10
+
Moves: 11
Vision: 3.46 tiles
Attack: 0
Defense: 3
@@ -3844,11 +4321,14 @@
Transport
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Can carry and refuel up to 8 Land, LandNoKill, LandAirSea, or Balloon units.
+ • Cargo not visible except to allies.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can Pillage
➤ May acquire veteran status.
• Veterans have increased strength in combat.
@@ -3866,12 +4346,12 @@
Transport
sea wolf
230%
-
+ 1
-
+
Ironclad
Cost: 60 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 8
+
Moves: 9
Vision: 2.83 tiles
Attack: 4
Defense: 4
@@ -3880,21 +4360,23 @@
Ironclad
Obsolete by: Destroyer
The Ironclad is an armored ship that is much more sturdy than the Frigate but loses transport capability and the ability to navigate rivers. From the Ironclad onward, warships are excellent at attacking shore targets, and can also pillage buoys.
-
-Ironclads and all modern ships can Stack-Escape: a 60% chance to escape a killed stack if they have more remaining move points than the attacker.
+Ironclads and all modern ships can Stack-Escape:60% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Unable to attack air units.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 20 combat rounds, which may or may not result in a victor.
+ • Chance of attacker promotion is half frequency: 2 battles required for same chance of promotion as 1 sea battle.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Upgrade
@@ -3917,12 +4399,12 @@
Ironclad
sea wolf
230%
-
+ 1
-
+
Destroyer
Cost: 60 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 12
+
Moves: 13
Vision: 3.87 tiles
Attack: 5
Defense: 5
@@ -3930,21 +4412,24 @@
Destroyer
Hitpoints: 30
Obsolete by: Missile Destroyer
Destroyers are the first of the modern ships to start your modern navy. Their main roles are fast scouting, seek-and-destroy, anti-submarine warfare, shore bombardment of lighter targets, and supporting the needs of larger fleets. 4× ASW defence gives a 35% chance defending vs Submarines.
-Destroyers can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining move points than the attacker.
+Stack-Escape:67% odds to evade stack-death if attacker doesn’t have more moves than it does.
➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
➤ 4× defense bonus if attacked by Submarine.
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 67% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 67% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Unable to attack air units.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 20 combat rounds, which may or may not result in a victor.
+ • Chance of attacker promotion is half frequency: 2 battles required for same chance of promotion as 1 sea battle.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Upgrade
@@ -3967,12 +4452,12 @@
Destroyer
sea wolf
230%
-
+ 1
-
+
Cruiser
Cost: 80 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 11
+
Moves: 12
Vision: 3.61 tiles
Attack: 6
Defense: 6
@@ -3987,14 +4472,19 @@
Cruiser
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
➤ 2× defense bonus if attacked by Submarine.
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Unable to attack air units.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 20 combat rounds, which may or may not result in a victor.
+ • Chance of attacker promotion is half frequency: 2 battles required for same chance of promotion as 1 sea battle.
+ • Firepower is reduced to 1. Attack strength is increased +33%
+ * Overall effect is -33% reduction in attack strength.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Can Upgrade
@@ -4017,36 +4507,42 @@
Cruiser
sea wolf
230%
-
+ 1
-
+
Missile Destroyer
Cost: 60 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 12
+
Moves: 13
Vision: 4.00 tiles
Attack: 5
Defense: 5
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
The Missile Destroyer has double the firepower of a Destroyer, and shares the same roles as its ancestor. It gains 2× defense against Air and Missile units. It has a 2× ASW bonus and can can carry one Missile.
-Missile Destroyers can Stack-Escape:a 67% chance to escape a killed stack if they have more remaining move points than the attacker.
+
The Missile Destroyer has double the firepower of a Destroyer, and shares the same roles as its ancestor. It gains 2× defense against Air and Missile units. It has a 2× ASW bonus and can carry two Missiles.
+Stack-Escape:67% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
➤ 2× defense bonus if attacked by Submarine.
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
➤ 2× defense bonus if attacked by Aircraft, Helicopter, or Cruise Missile.
-➤ STACK ESCAPE: 67% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 67% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Unable to attack air units.
-➤ Can carry and refuel 1 Missile unit.
+➤ Can carry and refuel up to 2 Missile units.
+ • Cargo not visible except to allies.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 20 combat rounds, which may or may not result in a victor.
+ • Chance of attacker promotion is half frequency: 2 battles required for same chance of promotion as 1 sea battle.
+ • Firepower is reduced to 1. Attack strength is increased +33%
+ * Overall effect is -33% reduction in attack strength.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
@@ -4067,19 +4563,19 @@
Missile Destroyer
sea wolf
230%
-
+ 1
-
+
AEGIS Cruiser
Cost: 100 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 11
+
Moves: 12
Vision: 4.36 tiles
Attack: 8
Defense: 8
Firepower: 2
Hitpoints: 30
Obsolete by: None
-
The AEGIS Cruiser has an Anti-Air missile system. Radar and sonar give superior vision and 2× defense bonus against Submarines. The AEGIS can carry two Missiles, and is the only ship that can attack Air and Missile units.
+
The AEGIS Cruiser has an Anti-Air missile system. Radar and sonar give superior vision and 2× defense bonus against Submarines. The AEGIS can carry five Missiles, and is the only ship that can attack Air and Missile units. The discovery of Laser gives all units on the tile a 50% chance to resist nuclear attack.Excellent vision and 3× Anti-Air bonus are ideal for scouting and escorting.➤ Belongs to Sea unit class.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
@@ -4087,16 +4583,24 @@
AEGIS Cruiser
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
➤ 3× defense bonus if attacked by Aircraft, Helicopter, or Cruise Missile.
➤ 2× defense bonus if attacked by Submarine.
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Anti-Air bonus of this unit is less effective against Stealth, which has a 25% bonus against this unit.
-➤ Can carry and refuel up to 2 Missile units.
-➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can carry and refuel up to 5 Missile units.
+ • Cargo not visible except to allies.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 20 combat rounds, which may or may not result in a victor.
+ • Chance of attacker promotion is half frequency: 2 battles required for same chance of promotion as 1 sea battle.
+ • Firepower is reduced to 1. Attack strength is increased +33%
+ * Overall effect is -33% reduction in attack strength.
+➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
+➤ Can defend units on its tile from Nuclear Attack:
+ • 50% chance of success, all units on tile will survive.
➤ Can attack against Aircraft, Helicopter, or Missile units, which are usually not reachable.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
@@ -4117,12 +4621,12 @@
AEGIS Cruiser
sea wolf
230%
-
+ 1
-
+
Battleship
Cost: 160 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 10
+
Moves: 11
Vision: 3.46 tiles
Attack: 12
Defense: 12
@@ -4136,14 +4640,19 @@
Battleship
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can attack units on non-native tiles.
- • Can launch attack from non-native tiles.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+4hp)
➤ 2× defense bonus if attacked by Submarine.
➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Unable to attack air units.
+➤ Can Attack reachable targets on land tiles.
+ • Battle will last 20 combat rounds, which may or may not result in a victor.
+ • Chance of attacker promotion is half frequency: 2 battles required for same chance of promotion as 1 sea battle.
+ • Firepower is reduced to 1. Attack strength is increased +33%
+ * Overall effect is -33% reduction in attack strength.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Has Special Defense against Special Attacks / Ranged Attacks.
• Gives 3 free rounds of combat against up to 4 units in an enemy stack.
• A maximum of 1 unit can be killed.
@@ -4172,41 +4681,43 @@
Battleship
sea wolf
230%
-
+ 1
-
+
Submarine
Cost: 50 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 10
+
Moves: 11
Vision: 3.46 tiles
Attack: 12
Defense: 2
Firepower: 2
Hitpoints: 28
-
Obsolete by: None
-
Submarines are strong attackers but weak defenders. They can carry 8 Missiles. They are unreachable by Air units, but do not block air attacks on surface ships. Submarines cannot attack units on shore. Submarines cannot be seen by other players unless they have a unit or city adjacent to it.
+
Obsolete by: Missile Submarine
+
Submarines are strong attackers but weak defenders. They are unreachable by Air units, but do not block air attacks on surface ships. Submarines cannot attack units on shore. Submarines cannot be seen by other players unless they have a unit or city adjacent to it.
-Submarines have superior attrition rates when attacking in numbers. They excel at hit-and-run against weaker ships.
-Submarines can Stack-Escape:a 75% chance to escape a killed stack if they have more remaining move points than the attacker.
+Submarines have superior attrition rates when attacking in numbers. They excel at hit-and-run against weaker ships. They have a minimum speed of 5 if damaged.
+Stack-Escape:75% odds to evade stack-death if attacker doesn’t have more moves than it does.➤ Belongs to Submarine unit class.
• Invisible except when next to an enemy unit or city.
• Crew Repair: each turn, regardless of movement, recovers 2 hit points.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
- • Slowed down while damaged.
+ • Slowed down while damaged
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
• Unreachable. Air and Land units cannot attack this one.
- ◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Can launch attack from non-native tiles.
-➤ 2× defense bonus if attacked by Submarine.
-➤ STACK ESCAPE: 75% odds to escape if stack defender loses, if it has more moves left than attacker.
+ ◦ Doesn't prevent enemy units from attacking other units on its tile.
+ • Cannot perform attacks on Land or cities.
+➤ 2× defense bonus if attacked by Submarine or Missile Submarine.
+➤ STACK ESCAPE: 75% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
➤ Attack value reduced against some ships.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 8 Missile units.
➤ Can only attack units on native tiles.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
+➤ Can attack against Balloons, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
➤ Can Pillage
➤ Can't be sabotaged.
@@ -4226,34 +4737,99 @@
Submarine
sea wolf
230%
-
+ 1
-
-
Carrier
+
+
Missile Submarine
-
Cost: 155 shields
+
Cost: 55 shields
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 10
+
Moves: 11
Vision: 3.46 tiles
-
Attack: 1
-
Defense: 9
+
Attack: 12
+
Defense: 4
Firepower: 2
-
Hitpoints: 40
+
Hitpoints: 30
Obsolete by: None
-
The Carrier is a mobile airport that can carry Air units, Helicopters, AAA, Marines, and Missiles. Fighters can Vigil on a Carrier if they have enough moves and the Carrier has not moved since the Vigil order was given.
-Carriers are very expensive and thus, usually protected by a fleet of scouts and escorts.
-➤ Belongs to Sea unit class.
+
A Missile Submarine is an upgraded Submarine. It gains 2hp, greater defense, and capacity for 4 Missiles. It can’t be seen except by an adjacent unit or city. The DIVE DEEP order makes Missile Submarines completely invisible to adjacent units on the following turn: enemies only discover it if moving directly to its tile, at which point they may attack. It can only be done in Deep Ocean while the Missile Submarine stays quietly in place.
+Missile Submarines are unreachable by Air units, but don’t block air attacks on surface ships.
+
+Minimum speed is 5, when damaged.
+
+Stack-Escape:75% odds to evade stack-death if attacker doesn’t have more moves than it does.
+➤ Belongs to Submarine unit class.
+ • Invisible except when next to an enemy unit or city.
• Crew Repair: each turn, regardless of movement, recovers 3 hit points.
• Speed is not affected by terrain.
• Does not get defense bonuses from terrain.
• Not subject to zones of control.
• Slowed down while damaged.
- • Can launch attack from non-native tiles.
-➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
-➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if it has more moves left than attacker.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+3hp)
+ • Unreachable. Air and Land units cannot attack this one.
+ ◦ Doesn't prevent enemy units from attacking other units on its tile.
+ • Cannot perform attacks on Land or cities.
+➤ 2× defense bonus if attacked by Submarine or Missile Submarine.
+➤ STACK ESCAPE: 75% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
+➤ Can DIVE DEEP, becoming totally invisible if it stays motionless on its tile.
+ • Becomes totally invisible even to adjacent units and cities, on the next turn.
+ • Bonus is lost if the Submarine moves.
+ • Other Submarines joining the tile will automatically dive deep to join it at depth.
+➤ Attack value reduced against some ships.
➤ Unable to attack air units.
-➤ Can carry and refuel up to 9 Missile, LandAirSea, Helicopter, Air, AirPillage, AirProtect, or Balloon units.
➤ Can only attack units on native tiles.
➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
+➤ Can attack against Submarine units, which are usually not reachable.
+➤ Can attack against Balloons, which are usually not reachable.
+➤ Attacking with fractional move points gives fractional attack power.
+➤ Can Pillage
+➤ Can't be sabotaged.
+➤ May acquire veteran status.
+ • Veterans have increased strength in combat.
+
+Veteran Levels for Missile Submarines:
+
+
Veteran level
Power factor
Promotion Odds
Move bonus
+
+
green
100%
50%
-
+
veteran
150%
33%
-
+
hardened
175%
20%
-
+
elite
200%
15%
-
+
crack
210%
15%
+ 1
+
master
220%
15%
+ 1
+
sea wolf
230%
-
+ 1
+
+
+
+
Carrier
+
+
Cost: 155 shields
+
Upkeep: 1 Shield, 1 Unhappy
+
Moves: 11
+
Vision: 3.46 tiles
+
Attack: 1
+
Defense: 9
+
Firepower: 2
+
Hitpoints: 40
+
Obsolete by: None
+
The Carrier is a mobile airport that can carry Air units, Helicopters, AAA, Marines, and Missiles. Fighters can Vigil on a Carrier if they have enough moves and the Carrier has not moved since the Vigil order was given.
+Carriers are very expensive and thus, usually protected by a fleet of scouts and escorts.
+➤ Belongs to Sea unit class.
+ • Crew Repair: each turn, regardless of movement, recovers 3 hit points.
+ • Speed is not affected by terrain.
+ • Does not get defense bonuses from terrain.
+ • Not subject to zones of control.
+ • Slowed down while damaged.
+ • Crew RepairAutomatically repairs 10% of its hitpoints each turn, regardless of movement (+4hp)
+ • Cannot perform attacks on Land or cities.
+➤ 2× defense bonus if attacked by Marines or Anti-Aircraft Artillery.
+➤ STACK ESCAPE: 60% odds to escape if stack defender loses, if attacker doesn’t have more moves than it does.
+➤ Unable to attack air units.
+➤ Can carry and refuel up to 10 Missile, LandAirSea, Helicopter, Air, Air_High_Altitude, AirProtect, or Balloon units.
+ • Cargo not visible except to allies.
+➤ Can only attack units on native tiles.
+➤ PORT PENALTY: If attacked in a city, firepower is set to 1 and firepower of attacker is doubled.
+➤ May impose a zone of control on its adjacent tiles.
+ • Prevents enemy cities from working the tile it's on.
➤ Can attack against Submarine units, which are usually not reachable.
➤ Can attack against Helicopter units, which are usually not reachable.
➤ Attacking with fractional move points gives fractional attack power.
@@ -4274,7 +4850,7 @@
Carrier
sea wolf
230%
-
+ 1
-
+
Cruise Missile
Cost: 60 shields
@@ -4293,7 +4869,7 @@
Cruise Missile
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
➤ AEGIS and Armor II have a defense bonus against this unit.
@@ -4310,7 +4886,118 @@
Cruise Missile
• Veterans have increased strength in combat.
-
+
+
Nuclear Missile
+
+
Cost: 160 shields
+
Upkeep: 1 Shield, 1 Unhappy
+
Moves: 24
+
Vision: 1.41 tiles
+
Attack: 100
+
Defense: 0
+
Firepower: 1
+
Hitpoints: 10
+
Obsolete by: None
+
If you have Space Flight and the Manhattan Project has been built by any player, you can make Nuclear Missiles. Nuclear blasts destroy ALL units in a 3×3 area. City population is reduced by half. Land tiles may get nuclear fallout. Fallout reduces tile output and increases risk of nuclear winter.
+If Fallout is cleaned on the turn it appears, the chance of nuclear winter is reduced.
+➤ Belongs to Missile unit class.
+ • Speed is not affected by terrain.
+ • Does not get defense bonuses from terrain.
+ • Not subject to zones of control.
+ • Unreachable. Most units cannot attack this one.
+ ◦ Doesn't prevent enemy units from attacking other units on its tile.
+ • Doesn't prevent enemy cities from working the tile it's on.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ Unblockable: unreachable units can't block its attacks on reachable units.
+➤ Can perform a Nuclear Detonation obliterating all adjacent tiles.
+➤ Never imposes a zone of control.
+➤ Field unit: one unhappiness applies even when non-aggressive.
+➤ Unit must be in a City, Airbase, Mobile SAM, Missile Destroyer, AEGIS Cruiser, Submarine, or Carrier after 1 turn.
+➤ Can Explode Nuclear
+ • uses up the Nuclear Missile.
+➤ Can Attack
+ • uses up the Nuclear Missile.
+ • causes Nuclear Detonation centered on the target tile.
+ • creates International Outrage: all nations have casus belli.
+➤ Can't be bribed.
+➤ Can't be sabotaged.
+➤ Will never achieve veteran status.
+
+
+
+
Tactical Nuke
+
+
Cost: 140 shields
+
Upkeep: 1 Shield, 1 Unhappy
+
Moves: 18
+
Vision: 1.41 tiles
+
Attack: 100
+
Defense: 0
+
Firepower: 1
+
Hitpoints: 10
+
Obsolete by: None
+
Tactical Nukes are very small warheads attached to missiles. A Tactical Nuke destroys all units on a single tile. City population is reduced by half. The target tile may get Fallout. In theory, Tactical Nukes are small enough to get the advantages of nuclear armaments without the severity of consequences. In reality, they might be a gateway that escalates toward mutual assured destruction.
+If you have Fusion Power and the Manhattan Project was built, a Tactical Nuke can be made in a city with an Enrichment Facility.
+➤ Belongs to Missile unit class.
+ • Speed is not affected by terrain.
+ • Does not get defense bonuses from terrain.
+ • Not subject to zones of control.
+ • Unreachable. Most units cannot attack this one.
+ ◦ Doesn't prevent enemy units from attacking other units on its tile.
+ • Doesn't prevent enemy cities from working the tile it's on.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ Can only be built if there is Enrichment Facility in the city.
+➤ Unblockable: unreachable units can't block its attacks on reachable units.
+➤ Can perform a Nuclear Detonation obliterating only the targeted tile.
+➤ Never imposes a zone of control.
+➤ Field unit: unhappiness applies even when non-aggressive.
+➤ Unit must be in a City, Airbase, Mobile SAM, Missile Destroyer, AEGIS Cruiser, Submarine, or Carrier after 1 turn.
+➤ Can Explode Nuclear
+ • uses up the Tactical Nuke.
+➤ Can Attack
+ • uses up the Tactical Nuke.
+ • causes Nuclear Detonation only on the target tile.
+ • creates International Outrage: all nations have casus belli.
+➤ Can't be bribed.
+➤ Can't be sabotaged.
+➤ Will never achieve veteran status.
+
+
+
+
Bombs
+
+
Cost: 25 shields
+
Upkeep: 1 Gold, 1 Unhappy
+
Moves: 1
+
Vision: 0.00 tiles
+
Attack: 10
+
Defense: 0
+
Firepower: 1
+
Hitpoints: 10
+
Obsolete by: None
+
Bombs are several tons of explosive payload. While bombers are considered to carry standard weaponry included, the Bombs unit represents heavier payloads of larger, higher grade weaponry. Only bombers with high capacity can carry this unit.
+Bombs can only be moved by bombers or the airlift order. They can’t attack from inside a domestic city. Bombs are cheap, but they damage more often than kill, and even victory has a cost. Hidden expenses lie in logistics, delivery risk, and non-reusability.
+
+Multislot: Under certain conditions, cities can make one (or more) multislot units per turn, plus one of any other type.
+➤ Belongs to Bomb unit class.
+ • Speed is not affected by terrain.
+ • Unreachable. Most units cannot attack this one.
+ ◦ Doesn't prevent enemy units from attacking other units on its tile.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
+➤ Never imposes a zone of control.
+➤ Unit has to be in a city, an Airbase, or on a Medium Bomber, Heavy Bomber, Strategic Bomber, Jet Bomber, or Stealth Bomber after 1 turn.
+➤ Can Explode
+ • Must be done while carried by a bomber-type while not inside a friendly city.
+ • Uses up the Bombs.
+ • AEGIS and Armor II have a defense bonus against this unit.
+ • Unable to attack air units.
+➤ Can't be bribed.
+➤ Can't be sabotaged.
+➤ Will never achieve veteran status.
+
+
+
Atom Bomb
Cost: 150 shields
@@ -4329,7 +5016,7 @@
Atom Bomb
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
➤ Can perform a Nuclear Detonation obliterating all cardinally adjacent tiles.
@@ -4347,7 +5034,7 @@
Atom Bomb
➤ Will never achieve veteran status.
-
+
Hydrogen Bomb
Cost: 190 shields
@@ -4368,7 +5055,7 @@
Hydrogen Bomb
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can only be built if there is Enrichment Facility in the city.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
@@ -4387,7 +5074,7 @@
Hydrogen Bomb
➤ Will never achieve veteran status.
-
+
Doomsday Bomb
Cost: 1000 shields
@@ -4408,7 +5095,7 @@
Doomsday Bomb
• Not subject to zones of control.
• Unreachable. Most units cannot attack this one.
◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
• AIRLIFT: Can be airlifted if it has remaining moves.
➤ Can only be built if there is Enrichment Facility in the city.
➤ Unblockable: unreachable units can't block its attacks on reachable units.
@@ -4429,84 +5116,73 @@
Doomsday Bomb
➤ Will never achieve veteran status.
-
-
Nuclear Missile
+
+
Emissary
-
Cost: 160 shields
-
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 24
-
Vision: 1.41 tiles
-
Attack: 100
+
Cost: 24 shields
+
Upkeep: 0
+
Moves: 3
+
Vision: 2.00 tiles
+
Attack: 0
Defense: 0
Firepower: 1
-
Hitpoints: 10
-
Obsolete by: None
-
If you have Space Flight and the Manhattan Project has been built by any player, you can make Nuclear Missiles. Nuclear blasts destroy ALL units in a 3×3 area. City population is reduced by half. Land tiles may get nuclear fallout. Fallout reduces tile output and increases risk of nuclear winter.
-If Fallout is cleaned on the turn it appears, the chance of nuclear winter is reduced.
-➤ Belongs to Missile unit class.
- • Speed is not affected by terrain.
- • Does not get defense bonuses from terrain.
- • Not subject to zones of control.
- • Unreachable. Most units cannot attack this one.
- ◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
+
Hitpoints: 8
+
Obsolete by: Diplomat
+
An Emissary performs official or covert actions. Covert actions make incidents which let Senates break treaties. Emissaries in cities may defend such acts with diplomatic combat. Emissaries can • Bribe a lone unit • Establish embassy • Investigate City • Steal tech • Steal maps • Incite city revolt.
+Except for Bribing and Investigate City, diplomatic actions will spend the Emissary, making him unavailable for further use. Emissaries may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Foreign tile claims require the Emissary to be accompanied by another unit.
+Emissaries have no upgrade cost. Full rules for Emissaries are in the Manual.
+➤ Belongs to Land unit class.
+ • Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Unblockable: unreachable units can't block its attacks on reachable units.
-➤ Can perform a Nuclear Detonation obliterating all adjacent tiles.
+ • Can't do actions as Cargo. Must first unload.
+➤ Defends cities against diplomatic actions.
➤ Never imposes a zone of control.
-➤ Field unit: one unhappiness applies even when non-aggressive.
-➤ Unit must be in a City, Airbase, Mobile SAM, Missile Destroyer, AEGIS Cruiser, Submarine, or Carrier after 1 turn.
-➤ Can Explode Nuclear
- • uses up the Nuclear Missile.
-➤ Can Attack
- • uses up the Nuclear Missile.
- • causes Nuclear Detonation centered on the target tile.
- • creates International Outrage: all nations have casus belli.
-➤ Can't be bribed.
-➤ Can't be sabotaged.
-➤ Will never achieve veteran status.
-
-
-
-
Tactical Nuke
+➤ Not subject to zones of control imposed by other units.
+➤ A non-military unit:
+ • Cannot attack.
+ • Doesn't impose martial law.
+ • Can enter foreign territory regardless of peace treaty.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Can do Diplomatic Operations:
+ • is done to foreign cities.
+ * 'Become Ambassador'
+ · creates a permanent Embassy.
+ • uses up the Emissary.
+ · can't be done at War if city is Theocratic or has Police Station.
+ * getting caught before performing this action during Armistice, Cease-fire, or Peace gives the victim Casus Belli against you.
+ * 'Investigate City' (does not spend the unit)
+➤ Can do Hostile Diplomatic Operations:
+ • uses up the Emissary.
+ • is done to foreign cities.
+ • can lead to a diplomatic battle against a defender.
+ * 'Steal Technology'
+ * 'Incite a Revolt'
+ * 'Steal Map Fragments'
+➤ Can do Field Operations:
+ • does not use up the Emissary.
+ • is done to foreign individual units alone on a tile.
+ * 'Bribe Enemy Unit' (does not spend the unit)
+➤ Can Upgrade
+ • upgrades to Diplomat.
+➤ Can Fortify. Only makes the Emissary stay put.
+➤ Expellable: can be expelled from foreign tiles.
+➤ May acquire veteran status.
+ • Veterans have improved chances in diplomatic contests.
-
Cost: 140 shields
-
Upkeep: 1 Shield, 1 Unhappy
-
Moves: 18
-
Vision: 1.41 tiles
-
Attack: 100
-
Defense: 0
-
Firepower: 1
-
Hitpoints: 10
-
Obsolete by: None
-
Tactical Nukes are very small warheads attached to missiles. A Tactical Nuke destroys all units on a single tile. City population is reduced by half. The target tile may get Fallout. In theory, Tactical Nukes are small enough to get the advantages of nuclear armaments without the severity of consequences. In reality, they might be a gateway that escalates toward mutual assured destruction.
-If you have Fusion Power and the Manhattan Project was built, a Tactical Nuke can be made in a city with an Enrichment Facility.
-➤ Belongs to Missile unit class.
- • Speed is not affected by terrain.
- • Does not get defense bonuses from terrain.
- • Not subject to zones of control.
- • Unreachable. Most units cannot attack this one.
- ◦ Doesn't prevent enemy units from attacking other units on its tile.
- • Doesn't prevent enemy cities from working the tile it's on.
- • AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Can only be built if there is Enrichment Facility in the city.
-➤ Unblockable: unreachable units can't block its attacks on reachable units.
-➤ Can perform a Nuclear Detonation obliterating only the targeted tile.
-➤ Never imposes a zone of control.
-➤ Field unit: unhappiness applies even when non-aggressive.
-➤ Unit must be in a City, Airbase, Mobile SAM, Missile Destroyer, AEGIS Cruiser, Submarine, or Carrier after 1 turn.
-➤ Can Explode Nuclear
- • uses up the Tactical Nuke.
-➤ Can Attack
- • uses up the Tactical Nuke.
- • causes Nuclear Detonation only on the target tile.
- • creates International Outrage: all nations have casus belli.
-➤ Can't be bribed.
-➤ Can't be sabotaged.
-➤ Will never achieve veteran status.
+Emissaries have their own veteran levels:
+
+
Veteran level
Power factor
Promotion odds
Move bonus
+
+
courier
100%
60%
-
+
messenger
110%
50%
-
+
envoy
115%
40%
-
+
legate
120%
30%
-
+
consul
125%
20%
⅓
+
minister
130%
15%
⅓
+
manus regis
135%
-
⅓
-
+
Diplomat
Cost: 30 shields
@@ -4516,12 +5192,13 @@
Diplomat
Attack: 0
Defense: 0
Firepower: 1
-
Hitpoints: 10
-
Obsolete by: Spy
-
A Diplomat performs official or covert actions. Covert actions make incidents which let Senates break treaties. Diplomats in cities may defend such acts with diplomatic combat. Diplomats can • Bribe a lone unit • Establish embassy • Investigate City • Sabotage random production or buildings • Steal tech • Steal maps • Incite city revolt.
-Except for Bribing and Investigate City, diplomatic actions will spend the Diplomat, making it unavailable for further use. Diplomats may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Foreign tile claims require the Diplomat be accompanied by another unit.
+
Hitpoints: 8
+
Obsolete by: None
+
A Diplomat performs official or covert actions. Covert actions make incidents which let Senates break treaties. Diplomats can do what Emissaries can, and also: • Move faster • Establish Embassy without spending unit • Sabotage random production or buildings.
+Except for Bribing, Establishing Embassy, and Investigate City, diplomatic actions will spend the Diplomat, making him unavailable for further use. Diplomats may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Foreign tile claims require the Diplomat to be accompanied by another unit.
+Espionage tech allows Diplomats to establish hostile embassies vs. Courthouse and Homeland Security (but not vs. Police Station or Theocracy.)
-Full rules for Diplomats are in the Manual.
+Diplomats can be upgraded from Emissaries for free. Full rules for Diplomats are in the Manual.➤ Belongs to Land unit class.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
@@ -4533,13 +5210,13 @@
Diplomat
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can do Diplomatic Operations:
- • uses up the Diplomat.
• is done to foreign cities.
- * 'Become Ambassador'
+ * 'Establish Embassy' (does not spend the unit)
· creates a permanent Embassy.
· can't be done at War if city is Theocratic or has Police Station.
+ * getting caught before performing this action during Armistice, Cease-fire, or Peace gives the victim Casus Belli against you.
* 'Investigate City' (does not spend the unit)
➤ Can do Hostile Diplomatic Operations:
• uses up the Diplomat.
@@ -4553,14 +5230,12 @@
Diplomat
• does not use up the Diplomat.
• is done to foreign individual units alone on a tile.
* 'Bribe Enemy Unit' (does not spend the unit)
-➤ Can Upgrade
- • upgrades to Spy.
➤ Can Fortify. Only makes the Diplomat stay put.
➤ Expellable: can be expelled from foreign tiles.
➤ May acquire veteran status.
• Veterans have improved chances in diplomatic contests.
-Veteran levels for Diplomats are unique to diplomatic type units:
+Diplomats have their own veteran levels:
Veteran level
Power factor
Promotion odds
Move bonus
@@ -4568,12 +5243,12 @@
Diplomat
secretary
110%
50%
-
envoy
115%
40%
-
ambassador
120%
30%
-
-
emissary
125%
20%
⅓
-
statesman
130%
15%
⅓
+
statesmen
125%
20%
⅓
+
premier
130%
15%
⅓
plenipotentiary
135%
-
⅓
-
+
Spy
Cost: 35 shields
@@ -4583,10 +5258,10 @@
Spy
Attack: 0
Defense: 0
Firepower: 1
-
Hitpoints: 10
+
Hitpoints: 8
Obsolete by: None
-
Spies can do what a Diplomat can, and also: • Survive ops • Sabotage lone units • Poison city water • Steal specific tech • Steal tech from cities more than once • Sabotage specific targets in cities.
-Spies who survive ops escape to the nearest friendly city. Spies have a 25% advantage over Diplomats in combat: a base 75% chance to win. Spies may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Tile claims require the Spy to be accompanied by a military unit. Full rules are in the Manual.
+
Except for establishing an embassy, Spies can do what a Diplomat can, and also:• Survive ops • Sabotage lone units • Poison city water • Steal specific tech • Steal tech from cities more than once • Sabotage specific targets in cities.
+Spies who survive ops escape to the nearest friendly city. Spies have a 25% advantage over Diplomats in combat:a base 75% chance to win. Spies may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Tile claims require the Spy to be accompanied by another unit. Full rules are in the Manual.➤ Belongs to Land unit class.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
@@ -4599,11 +5274,9 @@
Spy
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can do Diplomatic Operations:
• is done to foreign cities.
- * 'Establish Embassy'
- · can't be done at War if city is Theocratic or has Police Station.
* 'Investigate City' (does not spend the unit)
· can be done while Spy is transported cargo, using the 'D' (Do) command.
➤ Can do Hostile Diplomatic Operations:
@@ -4631,7 +5304,7 @@
Spy
• Veterans have improved chances in diplomatic contests.
• Veterans are more likely to survive missions.
-Veteran levels for Spies are unique to diplomatic type units:
+Spies have their own veteran levels:
Veteran level
Power factor
Promotion odds
Move bonus
@@ -4644,63 +5317,72 @@
Spy
spymaster
135%
-
⅓
-
-
Caravan
+
+
Patriarch
-
Cost: 30 shields
+
Cost: 55 shields
Upkeep: 0
-
Moves: 2
+
Moves: 4
Vision: 2.00 tiles
Attack: 0
-
Defense: 1
+
Defense: 0
Firepower: 1
-
Hitpoints: 10
-
Obsolete by: Truck
-
The Caravan is versatile and rugged. It is the only all-terrain land Commerce unit. It is also the only one that presents no potential military threat to trade partners. It can defend itself, fortify, and has 2 7/9 moves.
-
-Commerce units can help build Wonders in any city with whom you are not at war. In foreign cities 20+ tiles distant, they can create Trade Routes, increasing net trade in both cities relative to their combined trade. Routes are inactive during war and (re)activate during peace.
-Commerce units can enter the Marketplace of non-hostile foreign cities to sell their wares:revenue derives from total trade in both cities. This is only profitable between cities with very high trade.
-Commerce units can build any Wonders for which you have the tech requirement. To use them for a Trade Route, they need to have given a Home City.
-All Commerce units can carry Goods as cargo.
- ➤ Belongs to Land unit class.
+
Hitpoints: 8
+
Obsolete by: None
+
The Patriarch is unique—you can make only one, and only with Theocracy in a city with an Ecclesiastical Palace. He can do what Diplomats can do, with extra powers of persuasion. Under Theocracy, the Patriarch converts units to join your side at 1/3 the cost of a normal Bribe; and incites cities to become yours at one third the normal cost.
+Investigating, Converting, Establishing Embassy, and Inciting can be done multiple times if successful. Other actions spend the Patriarch, allowing no further use. A Patriarch may claim a tile for your nation if adjacent to a tile claim made inside your own national border. Foreign tile claims require the Patriarch to be accompanied by another unit.
+Espionage tech allows the Patriarch to establish hostile embassies vs. Courthouse and Homeland Security (but not vs. Police Station or Theocracy.)
+
+In diplomatic contests, the wiles, power, and influence of the Patriarch make him equal to a Spy in strength.
+➤ Belongs to Land unit class.
• Slowed down while damaged.
• AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Can help build a Wonder in any non-hostile city.
-➤ Can carry up to 2 Cargo units.
+ • Can't do actions as Cargo. Must first unload.
+➤ Each player may only have one of this type of unit.
+➤ Defends cities against diplomatic actions.
➤ Never imposes a zone of control.
➤ Not subject to zones of control imposed by other units.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
-➤ Can Establish Trade Route
- • is done to foreign cities 15 or more tiles distant.
- • inactive during War.
- • uses up the Caravan.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Caravan.
-➤ Can Help Build Wonder
- • is done in friendly cities. Contributes the unit's shield cost to current production
- • uses up the Caravan.
- • adds 30 production.
-➤ Can Upgrade
- • upgrades to Truck.
-➤ Can Fortify, granting a 50% defensive bonus when not in a city.
- • Gets the 50% defensive bonus automatically while in cities.
-➤ Capturable: can be captured if conditions allow it.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Can do Diplomatic Operations:
+ • is done to foreign cities.
+ * 'Establish Embassy' (does not spend the unit)
+ · creates a permanent Embassy.
+ · can't be done at War if city is Theocratic or has Police Station.
+ * getting caught before performing this action during Armistice, Cease-fire, or Peace gives the victim Casus Belli against you.
+ * 'Investigate City' (does not spend the unit)
+➤ Can 'Incite a Revolt and Escape':
+ • Is 1/3 the cost if done with Theocracy government.
+ • does not spend the Patriarch.
+ • is done to foreign cities.
+ • can lead to a diplomatic battle against a defender.
+➤ Can do Hostile Diplomatic Operations:
+ • uses up the Patriarch.
+ • is done to foreign cities.
+ • can lead to a diplomatic battle against a defender.
+ * 'Sabotage City' selects random building or production.
+ * 'Steal Technology'
+ * 'Steal Map Fragments'
+➤ Can do Field Operations:
+ • does not use up the Patriarch.
+ • is done to foreign individual units alone on a tile.
+ * 'Convert Unit' (does not spend the unit)
+ * Is 1/3 the cost of 'Bribe Enemy Unit' if done with Theocracy government.
+➤ Can Fortify. Only makes the Patriarch stay put.
➤ Expellable: can be expelled from foreign tiles.
-➤ May acquire veteran status.
- • Veterans have increased strength in combat.
- Transport Loading Logistics
-
-Transport Unloading Logistics
-
+➤ Will never achieve veteran status.
+
+The Patriarch has his own veteran level:
+
+
Veteran level
Power factor
Promotion odds
Move bonus
+
+
chosen one
125%
0%
-
-
+
Wagon
Cost: 25 shields
@@ -4714,16 +5396,16 @@
Wagon
Obsolete by: Truck
Wagons are the first Commerce unit that can travel by land. Compared to a Caravan, they are defenseless. They must stay on roads and quays; or flat grasslands, plains, and tundra. Wagons are useful for transporting slower units. Wagons have 3⅓ move points, giving a range of 10 tiles on roads.
-
-Wagons allow units with less than 3 move points to travel your roads faster. Any cargo can board a Wagon anywhere; but needs a city, quay, or base to deboard. Cargo can disembark anywhere, but it uses up all move points.
+Wagons allow units with less than 3 move points to travel your roads faster. Any cargo can board a Wagon anywhere; but needs a city, quay, or base to deboard. Cargo can disembark anywhere, but it uses up all move points.
Read the help on Caravans to see what Commerce units can do.➤ Belongs to LandRoad unit class.
• Only able to move on road tiles.
• Subject to zones of control.
-➤ Can carry up to 2 Foot units.
+➤ Can carry up to 2 Foot soldiers, Goods, or Freight units.
• Cargo can load outside a City.
• Cargo can step off outside a City, and loses all moves.
• Cargo cannot unload (T) outside a City, Quay, Fortress, or Naval Base.
+ • Cargo is visible to all nations who can see the Wagon.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Wagon.
@@ -4732,23 +5414,72 @@
Wagon
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Wagon.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Wagon.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Capturable: can be captured if conditions allow it.
➤ Expellable: can be expelled from foreign tiles.
➤ Can Upgrade
• upgrades to Truck.
➤ Will never achieve veteran status.
-
-
+
+
+
Caravan
+
+
Cost: 30 shields
+
Upkeep: 0
+
Moves: 2
+
Vision: 2.00 tiles
+
Attack: 0
+
Defense: 1
+
Firepower: 1
+
Hitpoints: 10
+
Obsolete by: Truck
+
The Caravan is versatile and rugged. It is the only all-terrain land Commerce unit. It is also the only one that presents no potential military threat to trade partners. It can defend itself, fortify, and carry 3 Goods or Freight.
+
+Commerce units can help build Wonders in any city with whom you are not at war. In foreign cities 20+ tiles distant, they can create Trade Routes, increasing net trade in both cities relative to their combined trade. Routes are inactive during war and (re)activate during peace.
+Commerce units can build any Wonders for which you have the tech requirement. To use them for a Trade Route, they need to have given a Home City.
+All Commerce units can carry Goods as cargo.
+ ➤ Belongs to Land unit class.
+ • Slowed down while damaged.
+ • AIRLIFT: Can be airlifted if it has remaining moves.
+➤ Can help build a Wonder in any non-hostile city.
+➤ Can carry up to 3 Goods or Freight units.
+ • Cargo not visible except to allies.
+➤ Never imposes a zone of control.
+➤ Not subject to zones of control imposed by other units.
+➤ A non-military unit:
+ • Cannot attack.
+ • Doesn't impose martial law.
+ • Can enter foreign territory regardless of peace treaty.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Can Establish Trade Route
+ • is done to foreign cities 15 or more tiles distant.
+ • inactive during War.
+ • uses up the Caravan.
+➤ Can Help Build Wonder
+ • is done in friendly cities. Contributes the unit's shield cost to current production
+ • uses up the Caravan.
+ • adds 30 production.
+➤ Can Upgrade
+ • upgrades to Truck.
+➤ Can Fortify, granting a 50% defensive bonus when not in a city.
+ • Gets the 50% defensive bonus automatically while in cities.
+➤ Capturable: can be captured if conditions allow it.
+➤ Expellable: can be expelled from foreign tiles.
+➤ May acquire veteran status.
+ • Veterans have increased strength in combat.
+ Transport Loading Logistics
+
+Transport Unloading Logistics
+
+
+
+
Train
Cost: 35 shields
@@ -4763,7 +5494,7 @@
Train
Trains are Commerce units with a bonus:they allow units with less than 3 move points to travel your rails at distances comparable to other units. Transport logistics for Trains are more restricted than for Wagons and Trucks. Except for Foot soldiers, all units need to be in a city to get on or off a Train.
Foot Soldiers use Trains the same as Wagons and Trucks, and can:
1. Board anywhere;
- 2. Deboard in a Base or Quay.;
+ 2. Deboard in a Base or Quay;
3. Disembark outside a City (loses all moves.)
On non-allied foreign rails, Trains incur full terrain movement cost. Pre-gunpowder units can’t attack Trains.
@@ -4774,9 +5505,10 @@
Train
• Subject to zones of control.
• Unreachable to pre-gunpowder units.
➤ Can carry up to 6 qualifying units.
- • Units with less than 3 moves: e.g., Foot Soldier, Artillery, Workers, Caravan, Bomb units.
- • May also carry and refuel Balloon units.
+ • Units with less than 3 moves: e.g., Foot soldier, Ballistic class, Workers, Migrant types, Caravan, Freight, Goods, and Bomb units.
+ • Can also carry and refuel Balloons and Zeppelins.
• Some cargo cannot be loaded/unloaded except in a City.
+ • Cargo not visible except to allies.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Train.
@@ -4785,20 +5517,17 @@
Train
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Train.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Train.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Capturable: can be captured if conditions allow it.
➤ Will never achieve veteran status.
-
+
Truck
Cost: 50 shields
@@ -4810,33 +5539,26 @@
Truck
Firepower: 1
Hitpoints: 20
Obsolete by: None
-
The Truck replaces the Caravan as the basic Commerce unit on Land. It moves at more than twice the speed; but is restricted to the same roads and flatland tiles as Wagons, and can't use rails. Each Truck used to build a Wonder will add 50 shields. See the entry on Caravan to read what Commerce units may do.
+
The Truck replaces the Caravan as the basic Commerce unit on Land. It moves at more than twice the speed; but is restricted to roads and quays, and can't use rails. Each Truck used to build a Wonder will add 50 shields. See the entry on Caravan to read what Commerce units may do.
Trucks allow units with less than 3 move points to travel your roads faster: Like the Wagon, any cargo can board a Truck anywhere, but a city, quay, or base is needed to deboard. Disembarking is legal for all cargo types, but uses up all moves (except Marines.)➤ Belongs to LandRoad unit class.
• Can only travel on roads.
• Slowed down while damaged.
➤ Can carry up to 3 qualifying units.
- • Units with less than 3 moves: e.g., Foot Soldier, Artillery, Workers, Caravan, Freight, Bomb units.
+ • Units with less than 3 moves: e.g., Foot Soldier, Artillery, Workers, Caravan, Goods, Freight, Bomb units.
• Some cargo cannot be loaded/unloaded except in a City.
+ • Cargo not visible except to allies.
➤ Never imposes a zone of control.
-➤ Not subject to zones of control imposed by other units.
-➤ Can Help Build Wonder
- • is done in friendly cities. Contributes the unit's shield cost to current production
- • uses up the Truck.
- • adds 35 production.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Truck.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Truck.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Truck.
@@ -4847,7 +5569,7 @@
Truck
➤ Expellable: can be expelled from foreign tiles.
-
+
Goods
Cost: 20 shields
@@ -4859,10 +5581,9 @@
Goods
Firepower: 1
Hitpoints: 1
Obsolete by: Freight
-
Goods are commodities. The cargo containers of the ancient world were amphorae, which carried for trade goods.
-Goods can be used to establish Trade Route or Enter Marketplace. They can be carried by Commerce units, allowing you to establish multiple trade routes in one voyage. Also, Goods can Recycle Production with only a 15% penalty, rendering 17 shields into a city production target.
+
Goods are used to establish Trade Routes, Help Build Wonders, and contribute to city production. Recycle Production has a penalty of only 3 shields, yielding 17 shields into a city’s production target.
-By themselves, Goods can only move onto tiles with a City, Quay, Fortress, or Naval Base. Commerce units, Tribesmen, and Galleons may carry them as cargo. If adjacent to a foreign city, use the D command (D) for Commerce functions.
+Goods can only move to tiles with a City, Quay, Fortress, Airbase, or Naval Base. Otherwise they must be transported. If adjacent to a foreign city, use the Do command (D) for Commerce functions.➤ Belongs to Cargo unit class.
• Is NOT a unit. It is goods and materials for production and commerce.
• Only able to move onto Cities, Quays, Fortresses, and Naval Bases.
@@ -4872,6 +5593,10 @@
Goods
• Can't Fortify.
• Can be carried by any Commerce Unit, and Galleons.
➤ Never imposes a zone of control.
+➤ Can Help Build Wonder
+ • is done in friendly cities. Contributes the unit's shield cost to current production
+ • uses up the Goods.
+ • adds 20 production.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
@@ -4880,16 +5605,13 @@
Goods
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Goods.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Goods.
➤ Can Upgrade
• upgrades to Freight.
➤ Capturable: can be captured if conditions allow it.
• does not count as an extra unit on tile to prevent capture.
-
+
Freight
Cost: 25 shields
@@ -4901,83 +5623,37 @@
Freight
Firepower: 1
Hitpoints: 1
Obsolete by: None
-
The methodical logistics of modern shipping render the Freight unit: a cargo container separate from the transport vehicle. Freight brings benefits of organized efficiency. Like all Commerce units, it can Help Build Wonders, and Establish Trade Route or Enter Marketplace (with a lower unit cost.) Unlike other Commerce units, it can Disband to Recycle Production with no net loss in shields. This gives Freight a variety of creative industrial uses.
-
-By itself, Freight is only able to move to tiles with loading infrastructure: Cities, Quays, Fortresses, and Naval Bases. To be transported, it must be carried by Train, Truck, Airplane, or ship. If adjacent to a foreign city, use the D key to do Commerce functions.
+
Freight brings benefits of organized efficiency. Like all Commerce units, it can Help Build Wonders, and Establish Trade Route. Unlike other Commerce units, it can Disband to Recycle Production with no net loss in shields. This gives Freight a variety of industrial uses. Similar to Infantry, Freight can use extra build slots a city may have.
+
+By itself, Freight is only able to move to tiles with loading infrastructure: Cities, Quays, Fortresses, Airbases, and Naval Bases. To be transported, it must be carried as cargo. If adjacent to a foreign city, use the D key to do Commerce functions.➤ Belongs to Cargo class.
• Is NOT a unit. It is goods and materials for production and commerce.
• Only able to move onto Cities, Quays, Fortresses, and Naval Bases.
• Pays no penalty for recycling production into Units, Buildings, or Wonders.
• Subject to zones of control.
• Can't Fortify.
- • Can be carried by Train, Truck, Cargo Ship, Transport.
+ • Can be carried by Train, Truck, Cargo Ship, Transport, Airplane.
➤ Never imposes a zone of control.
➤ Not subject to zones of control imposed by other units.
-➤ Can Help Build Wonder
- • is done in friendly cities. Contributes the unit's shield cost to current production
- • uses up the Freight.
- • adds 35 production.
+➤ Multislot: Under certain conditions, cities can make one (or more) multislot unit(s) per turn, plus one of any other unit type.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Establish Trade Route
• is done to foreign cities 15 or more tiles distant.
• inactive during War.
• uses up the Freight.
-➤ Can Enter Marketplace
- • is done to foreign cities not at war.
- • uses up the Freight.
➤ Can Help Build Wonder
• is done in friendly cities. Contributes the unit's shield cost to current production
• uses up the Freight.
- • adds 50 production.
+ • adds 25 production.
➤ Capturable: can be captured if conditions allow it.
• does not count as an extra unit on tile to prevent capture.
-
-
Scout
-
-
Cost: 30 shields
-
Upkeep: 0
-
Moves: 2
-
Vision: 2.24 tiles
-
Attack: 0
-
Defense: 1
-
Firepower: 1
-
Hitpoints: 10
-
Obsolete by: Partisan
-
Scouts are brave explorers of unknown territory. They can slip through enemy ZOC. Like Tribesmen, they ignore all terrain penalties, treating every tile as if it had a road. This means they can move 4 tiles per turn (6 with the discovery of Map Making.) As a non-military traveler, they can also visit far off cities to Investigate City and gather intel. Sometimes generals will use their scouting information for strategic advantage. Scouts can make contact with far-off lands to arrange cease-fire or peace. Best of all, Scouts live off the land and have no upkeep. In later times, Scouts upgrade to Partisans.
-➤ Belongs to Land unit class.
- • Slowed down while damaged.
- • AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Ignores terrain effects (moving costs at most 1/3 MP per tile).
-➤ Map Making gives +2/3 moves, giving a range of +2 tiles.
-➤ Never imposes a zone of control.
-➤ Not subject to zones of control imposed by other units.
-➤ A non-military unit:
- • Cannot attack.
- • Doesn't impose martial law.
- • Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
-➤ Can Investigate City (does not spend the unit)
-➤ Can Upgrade
- • upgrades to Partisan.
-➤ Can Fortify, granting a 50% defensive bonus when not in a city.
- • Gets the 50% defensive bonus automatically while in cities.
-➤ Capturable: can be captured if conditions allow it.
-➤ Expellable: can be expelled from foreign tiles.
-➤ May acquire veteran status.
- • Veterans have increased strength in combat.
- Transport Loading Logistics
-
-Transport Unloading Logistics
-
-
-
-
+
Leader
Cost: 10 shields
@@ -5009,7 +5685,7 @@
Leader
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Destroy City
• is done to domestic individual cities.
➤ Can Pillage
@@ -5021,7 +5697,8 @@
Leader
➤ Will never achieve veteran status.
-
+
+
Queen
Cost: 10 shields
@@ -5053,7 +5730,7 @@
Queen
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Destroy City
• is done to domestic individual cities.
➤ Can Pillage
@@ -5065,7 +5742,7 @@
Queen
➤ Will never achieve veteran status.
-
+
Barbarian Leader
Cost: 40 shields
@@ -5091,54 +5768,104 @@
Barbarian Leader
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
+ • Doesn't prevent enemy cities from working the tile it's on.
➤ Can Pillage
➤ Can Fortify. Only makes the Barbarian Leader stay put.
➤ Can't be bribed.
➤ Will never achieve veteran status.
-
-
Peasants
+
+
Well-Digger
-
Cost: 10 shields
-
Upkeep: 2 Food
-
Moves: 2
+
Cost: 5 shields
+
Upkeep: 2 Food, 5 Shield, 2 Gold
+
Moves: 3
Vision: 2.00 tiles
Attack: 0
Defense: 1
Firepower: 1
-
Hitpoints: 10
-
Obsolete by: None
-
Population Cost: 1. Adds Population: 1.
-
-Peasants are the common people of Monarchies. You can encourage them by royal edict to migrate to other cities and help colonize new areas. But to have such access, you must be a Constitutional Monarchy with the Magna Carta wonder: Peasants can only be made in the city with Magna Carta, so choose that city carefully.
-If managed correctly, Peasants can increase your national population growth. They give a tremendous advantage for growing new colonies and settlements faster.
-Peasants can only be made in one city, but you can make more than one per turn under the right conditions. When a city completes a unit, it will try to make more of them if it doesn’t have a worklist. So be careful!
+
Hitpoints: 8
+
Obsolete by: Workers
+
This unit can fix unlucky starts, but has very high upkeep.
+If you have no water, make a Well-Digger in a city that can support it, create a water source, then disband the unit.
+This unit can dig wells to create irrigation sources. It will cost its home city -2 Food -2 Prod. This unit WILL NOT WORK: • Outside your borders, • After Alphabet or Pottery, • After any player discovers Writing, • After it reaches an age of 10 turns. >> Don't fool around: high upkeep will permanently hinder you. Only make this unit if you lack water when the game starts!➤ Belongs to Land unit class.
• Subject to zones of control.
• Slowed down while damaged.
- • AIRLIFT: Can be airlifted if it has remaining moves.
-➤ Can only be built with Constitutional Monarchy as government.
-➤ Can only be built if there is Magna Carta in the city.
-➤ Costs 1 population to build.
-➤ May load onto and unload from Transport Helicopters even when underway.
+➤ Each player may only have one of this type of unit.
➤ Never imposes a zone of control.
➤ A non-military unit:
• Cannot attack.
• Doesn't impose martial law.
• Can enter foreign territory regardless of peace treaty.
- • Doesn't prevent enemy cities from working the tile it's on.
-➤ Under certain conditions, cities can make more than one of this unit per turn, plus one of any other unit type.
-➤ Can Add to City
- • is done to domestic individual cities.
- • uses up the Pilgrims.
- • max target size: 39.
- • adds 1 population.
-➤ Capturable: can be captured if conditions allow it.
+ • Doesn't prevent enemy cities from working the tile it's on.
+➤ Can Dig Well or Irrigate tiles with no water.
+➤ Can clean Pollution from tiles.
+➤ Can clean Fallout from tiles.
+➤ Can Recycle Unit
+ • is done in cities. Contributes half the unit's shield cost to current production
+ • uses up the Well-Digger.
+➤ Can Disband
+ • uses up the Well-Digger. Recycles cost if done in a city.
+➤ Can Upgrade
+ • upgrades to Workers.
+➤ Can Pillage
+➤ Can Road
+➤ Can Irrigate
➤ Expellable: can be expelled from foreign tiles.
-➤ Unable to pillage tiles.
➤ Will never achieve veteran status.
+ Transport Loading Logistics
+
+Transport Unloading Logistics
+
+
+
Magnum Turret
+
+
Cost: 160 shields
+
Upkeep: 1 Shield
+
Moves: 0
+
Vision: 2.00 tiles
+
Attack: 10
+
Defense: 7
+
Firepower: 3
+
Hitpoints: 40
+
Obsolete by: None
+
On rare occasions in history, nations constructed monumental turret guns. Cost-benefit is debatable among military theorists due to scale, cost, and immobility. The Magnum Turret stays where it’s built. You may build only one.
+
+Housed in armored defenses, the Magnum Turret deters attackers and nearby enemy encampments. It may attack or bombard ocean tiles.
+
+If it has moves left, the Magnum Turret can do 7 rounds of Bombardment up to 2 tiles distant upon five units, with two possible fatalities. When special attacked from range, it can retaliate with the same type of Bombardment.
+
+VIGIL:the Vigil order instructs the Magnum Turret to auto-Bombard adjacent military units if they have better attack odds.
+➤ Belongs to LandImmobile unit class, Ballistic sub-class.
+ • VIGIL: can be given the Vigil order to auto-Bombard adjacent military units.
+ • Cannot move.
+ • Cannot airlift.
+ • Cannot be transported.
+ • Gets a 50% defensive bonus while in cities.
+ • Gets a 50% defensive bonus while in cities.
+ • NOTb> slowed while damaged.
+➤ Unable to attack air units.
+➤ Each player may only have one of this type of unit.
+➤ Can safely conduct Bombard Attack.
+ • Can do the action multiple times per turn.
+ • Has a range of 2 tiles.
+ • Has a range of 2 tiles.
+ • Gives 7 free rounds of combat against up to 5 units in an enemy stack.
+ • Uses 1 movement point.
+ • A maximum of 2 units can be killed.
+ • Can be done to Ocean tiles if not transported.
+ • Can be done to Cities, Forts, Airbases, Fortresses, Naval Bases, Castles, and Bunkers.
+ • Action requires:
+ - Magnum Turret to have not yet taken action this turn.
+➤ Has Special Defense against Special Attacks / Ranged Attacks.
+ • Gives 7 free rounds of combat against up to 5 units in an enemy stack.
+ • A maximum of 2 units can be killed.
+➤ Can Fortify
+ • Gets the 50% defensive bonus automatically while in cities.
+➤ May acquire veteran status.
+ • Veterans have increased strength in combat.
diff --git a/freeciv-web/src/main/java/org/freeciv/persistence/DbManager.java b/freeciv-web/src/main/java/org/freeciv/persistence/DbManager.java
index 448b0fbbb..2d0c3d7aa 100644
--- a/freeciv-web/src/main/java/org/freeciv/persistence/DbManager.java
+++ b/freeciv-web/src/main/java/org/freeciv/persistence/DbManager.java
@@ -22,7 +22,7 @@ public static String getQueryCountServersByHost() {
}
public static String getQuerySaltHash() {
- return " SELECT secure_hashed_password FROM auth " //
+ return " SELECT secure_hashed_password,digest_pw FROM auth " //
+ " WHERE LOWER(username) = LOWER(?) AND activated = '1' " //
+ " LIMIT 1 ";
}
@@ -148,8 +148,8 @@ public static StringBuilder getQueryUpdateServers(boolean serverExists, List.
+ *******************************************************************************/
+package org.freeciv.servlet;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.freeciv.services.Statistics;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Hall Of Fame list
+ *
+ * URL: /hall_of_fame
+ */
+public class PrivacyPolicy extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+
+ RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/jsp/game/privacy.jsp");
+ rd.forward(request, response);
+ }
+
+}
\ No newline at end of file
diff --git a/freeciv-web/src/main/java/org/freeciv/servlet/TokenSignin.java b/freeciv-web/src/main/java/org/freeciv/servlet/TokenSignin.java
index 56dbd7eeb..eac6e9f6f 100644
--- a/freeciv-web/src/main/java/org/freeciv/servlet/TokenSignin.java
+++ b/freeciv-web/src/main/java/org/freeciv/servlet/TokenSignin.java
@@ -53,9 +53,7 @@ public class TokenSignin extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final JacksonFactory jacksonFactory = new JacksonFactory();
- private String google_signin_key;
-
- public void init(ServletConfig config) throws ServletException {
+ private String google_signin_key;public void init(ServletConfig config) throws ServletException {
super.init(config);
try {
@@ -67,6 +65,8 @@ public void init(ServletConfig config) throws ServletException {
}
}
+
+
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
diff --git a/freeciv-web/src/main/resources/db/migration/V1_15__add_google_ip_log.sql b/freeciv-web/src/main/resources/db/migration/V1_15__add_google_ip_log.sql
new file mode 100644
index 000000000..bf5874bb9
--- /dev/null
+++ b/freeciv-web/src/main/resources/db/migration/V1_15__add_google_ip_log.sql
@@ -0,0 +1,7 @@
+create table google_ip_log(
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `username` varchar(32) NOT NULL,
+ `ip_list` JSON NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `username` (`username`)
+);
\ No newline at end of file
diff --git a/freeciv-web/src/main/resources/db/migration/V1_16__encrypt_mode.sql b/freeciv-web/src/main/resources/db/migration/V1_16__encrypt_mode.sql
new file mode 100644
index 000000000..6bf8e123d
--- /dev/null
+++ b/freeciv-web/src/main/resources/db/migration/V1_16__encrypt_mode.sql
@@ -0,0 +1,2 @@
+alter table auth add column `digest_pw` BOOLEAN;
+update auth set digest_pw = FALSE;
\ No newline at end of file
diff --git a/freeciv-web/src/main/resources/log4j.properties b/freeciv-web/src/main/resources/log4j.properties
deleted file mode 100644
index 65010ea41..000000000
--- a/freeciv-web/src/main/resources/log4j.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-log4j.rootLogger = DEBUG, stdout
-
-log4j.category.com.opensymphony.sitemesh=DEBUG
-log4j.category.org.apache.struts2=INFO
-log4j.category.org.apache=INFO
-log4j.category.catalia=INFO
-
-log4j.appender.stdout = org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.Threshold = DEBUG
-log4j.appender.stdout.Target = System.out
-log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern = %d{ISO8601} %-5p [%F:%L] : %m%n
-
-
-
diff --git a/freeciv-web/src/main/webapp/WEB-INF/jsp/errors/404.jsp b/freeciv-web/src/main/webapp/WEB-INF/jsp/errors/404.jsp
index 7509e64e4..aaa0aaf5f 100644
--- a/freeciv-web/src/main/webapp/WEB-INF/jsp/errors/404.jsp
+++ b/freeciv-web/src/main/webapp/WEB-INF/jsp/errors/404.jsp
@@ -6,11 +6,12 @@
-
\ No newline at end of file
+
diff --git a/freeciv-web/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp b/freeciv-web/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp
index a7b654b4d..98c0bcaf7 100644
--- a/freeciv-web/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp
+++ b/freeciv-web/src/main/webapp/WEB-INF/jsp/fragments/footer.jsp
@@ -2,6 +2,6 @@
- A Play-By-Email game is a deathmatch on a small map with up to 4 human players, playing
- with alternating turns, and players get an e-mail every time it is
- their turn to play. These games are often played over a long time
- period, each player has 7 days to complete their turn.
-
-
- To start a new Play-By-Email game,
- log in here. To play your turn
- in a running Play-By-Email game, click on the link in the last
- e-mail you got from Freeciv-web. Games are expired after 7 days if
- you don't play your turn.
-
+ A Play-By-Email game is a deathmatch on a small map with up to 4 human players, playing
+ with alternating turns, and players get an e-mail every time it is
+ their turn to play. These games are often played over a long time
+ period, each player has 7 days to complete their turn.
+
+
+ To start a new Play-By-Email game,
+ log in here. To play your turn
+ in a running Play-By-Email game, click on the link in the last
+ e-mail you got from Freeciv-web. Games are expired after 7 days if
+ you don't play your turn.
+
FCW is a private gaming community and a private gathering.
+
+
+ Freeciv-Web ("FCW") is a preferred privacy protection site. That is,
+ 1) the data we collect is used only to deliver services the best we can,
+ 2) your data is not shared with third parties,
+ 3) we maintain strict privacy protection policies in our Terms of
+ Service and code of conduct to ensure our users don't violate
+ each other's privacy.
+
+
+
+ What information does FCW create or collect?
+ Computers by their nature create information during digital interactions. Information is created:
+ 1. When you register on our site, the information you used to register with.
+ 2. If you register on our Discord, the information and messages that are created in those acts.
+ 3. If you register on our mailing list, the information you submit to that list.
+ 4. Any data that is created in the act of you playing the game.
+
+
+
+ What information is used or reviewed?
+ 1. We review data when you participate in the Discord or participate on our site creating content there.
+ 2. When registering on our site, you may be asked to enter an e-mail address and username.
+ 3. Your e-mail address may be verified by Google for use of the website, Discord for use of our discord group,
+ and/or verified using a unique consent link. If a link is visited, it would be used to verify that you control the
+ e-mail address, and/or that your IP address is not affiliated with malicious sources.
+ 4. We may retain server logs which include the IP address of every request to our server.
+
+
+
+ What do we use your information for?
+ Any of the information we collect from you may be used in one of the following ways:
+ 1. To enable the delivery of our services.
+ 2. To improve our site and user experiences - Information and feedback supplied by users may be used to improve
+ the site experience.
+ 3. To send emails - PBEM games email you when it's your turn. If you subscribe, the Longturn Notification system
+ notifies you of upcoming games.
+ 4. Site security - Server logs may be used in conventional server security operations, identifying and preventing
+ malicious sources.
+
+
+
+ How do we protect your privacy and/or information?
+ We implement security measures to maintain the privacy and safety of your personal information.
+
+
+
+ What is your data retention policy?
+ We make a good faith effort to:
+ 1. Retain server logs containing the IP address of all requests.
+ 2. Retain the IP addresses associated with registered users and their posts no more than 5 years.
+
+
+
+ Does FCW use cookies? If so, what kind?
+ 1. FCW does not use marketing, advertising, or commercial cookies.
+ 2. FCW does not use reporting/tracking cookies that communicate with our server or third party servers.
+ 3. FCW uses User Convenience Cookies.
+ What is a 'User Convenience Cookie?'
+ User convenience cookies may remember your personal setup preferences you may submit for graphic display options,
+ game preferences, and suchlike.
+ They may remember your username and password and store it locally in your browser, so that you do not have to
+ re-enter this information when re-visiting the site.
+ User convenience cookies are not shared with any third parties and are not stored or accessed on our server. They
+ are only used by your browser to fill in information you have indicated you would like your browser to remember,
+ when interacting with our site.
+
+
+
+ Does FCW disclose any information to outside parties?
+ 1. FCW enforces policies against users supplying any information or communications you share within our sphere of
+ services, to outside parties.
+ 2. We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information.
+ 3. This does not include trusted third parties whose services integrate to our site for the purpose of operating
+ our site, conducting our business, or servicing you, so long as the policies of those parties do not violate our
+ own privacy policies.
+ 4. For example, your email address is used during Google_Auth logins that use your gmail account.
+ 5. We may use or release information when we believe that is appropriate to comply with the law, enforce our site
+ policies, or protect ours or others rights, property, or safety.
+ 6. However, non-personally identifiable visitor information will not be provided to other parties for marketing or
+ advertising, without your explicit consent.
+
+
+
+ Third party links
+ Occasionally, at our discretion, we may include or offer third party products or services on our site. These third
+ party sites have separate and independent privacy policies. We therefore have no responsibility or liability for
+ the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and
+ welcome any feedback about these sites.
+
+
+
+ Children's Online Privacy Protection Act Compliance
+ Our site, products and services are all directed to people who are at least 13 years old or older. If you are
+ under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act), do not use this
+ site without parental supervision.
+
+
+
+ Online Privacy Policy Only
+ This online privacy policy applies only to information collected through our site and services and not to
+ information collected outside of such.
+
+
+
+ Your Consent
+ By using our site and other services, you consent to our service-wide Privacy Policy and Terms of Service.
+
+
+
+ User Privacy Obligations
+ Community members (Users) who partake in the private gathering of FCW are bound by the Terms of Service which
+ contain restrictions on what actions users may take with respect to each other's privacy.
+ Please review the Terms of Service for further information on user privacy obligations.
+
+
+
+ Changes to our Privacy Policy
+ If we decide to change our privacy policy, we will post those changes on this page.
+
- FCW is a private gaming community. By playing in it, you accept membership in the commmunity and represent that you will abide by the following rules:
+ FCW is a private gaming community. By playing in it, you accept membership in the commmunity and represent that
+ you will abide by the following rules:
- 1. Be polite.
- 2. Don't cheat.
- 3. Keep in-game conflicts in-the-game.
- 4. No abuse, spam, harrassment.
- 5. Don't promote, spam, recruit, or link to other servers and sites.
- 6. Compare or politick against servers or sites somewhere else.
- 7. Constructive ideas are welcome. Directly or indirectly disparaging the site or its members is not.
- 8. Do religious or political discussions somewhere else.
- 9. Private DMs require FCW Discord or FCW Game Server to bridge them and thus fall under these rules.
- 10. FCW is a private gathering and not a public square. Keep speech and conduct appropriate.
-
- Clarification:
-
-
- 1. Be polite: This is rule #1 for a reason. We take it very seriously. It covers a lot but is easy enough to follow: be nice. You cannot politely harrass, threaten, DOX, copy-paste someone's content off-site without permission, violate privacy, etc. If you're not sure, ask.
- 2.1. No multi-accounts.
- 2.2. Browser hacking, console hacking, packet hacking, server hacking, and any other form of hacking is 100% considered as cheating.
- 2.3. Information, actions, or access not commonly available through normal use of the game's interface is competitive inequality. At Freeciv-web this is considered cheating without exception.
- 3. Keep in-game conflicts in-the-game.
- 3.1 Remember, your enemy may be your ally in a future game.
- 3.2. You may role play impolite hostility in-game if you represent yourself as your country's fictional leader and do the same for the person you are speaking about. e.g. "The Oldenburgian nation are lying warmongers and cannot be trusted" is acceptable.
- 4. No abuse, spam, harrassment. This includes but is not limited to, using another person or "bot" as the agent for violating any site rules.
- 4.1 Offsite links are almost always spam. Within reason, preserve the game entertainment ambience by keeping in-game chat relative to the in-game play of your player-nation.
- 4.2 Dominating a community discussion in Discord by simply posting many more messages than anyone is also bad form, let others give their views.
- 5. FCW is not the appropriate forum for publicly or privately promoting, recruiting, or poaching to other servers or sites. Ask admin permission.
- 6. FCW is not the appropriate forum for comparison or politicking for or against servers or sites.
- 7. All constructive ideas and contributions are welcome. Politicking against the site or its members can be freely done in locations other than FCW.
- 8. Religious and political discussions are encouraged in locations other than FCW. Such discussions may be deleted from FCW.
- 9. Private DMs must abide by community rules when those DMs were bridged by mutual belonging to FCW Discord or the FCW Game Server which make those communications possible. Private complaints about DMs to other members will be treated the same as if they had been made in public, if those DMs were bridged or conveyed by a FCW server.
- 10. FCW is not a public square with guaranteed free speech. It is more like a friend's house party where you are expected to be nice and enjoy the offerings. We reserve the right to remove posts and ensure posted material is not potentially harmful. As supporters of freedom of speech in general, we exercise these rights carefully with an administrator audit trail and try to mamimize the good spirits of the house party.
+ 1. Be polite.
+ 2. Don't cheat.
+ 3. Keep in-game conflicts in-the-game.
+ 4. No abuse, spam, harrassment, doxxing, personal threats.
+ 5. Don't promote, spam, recruit, or link to other servers and sites.
+ 6. Compare or politick against servers or sites somewhere else.
+ 7. Constructive ideas are welcome. Directly or indirectly disparaging the site or its members is not.
+ 8. Do religious or political discussions somewhere else.
+ 9. Private DMs require FCW Discord or FCW Game Server to bridge them and thus fall under these rules.
+ 10. FCW is a private gathering and not a public square. Keep speech and conduct appropriate.
+ 11. Information, communications, and content that take place within the FCW private gathering may not be shared or
+ distributed offsite without permission.
+
+ Clarification:
+
+
+ 1. Be polite: This is rule #1 for a reason. We take it very seriously. It covers a lot but is easy enough to
+ follow: be nice. You cannot politely harrass, threaten, DOX, copy-paste someone's content off-site without
+ permission, violate privacy, etc. If you're not sure, ask.
+ 2.1. No multi-accounts.
+ 2.2. Browser hacking, console hacking, packet hacking, server hacking, and any other form of hacking is 100%
+ considered as cheating.
+ 2.3. Information, actions, or access not commonly available through normal use of the game's interface is
+ competitive inequality. At Freeciv-web this is considered cheating without exception.
+ 3. Keep in-game conflicts in-the-game.
+ 3.1 Remember, your enemy may be your ally in a future game.
+ 3.2. You may role play impolite hostility in-game if you represent yourself as your country's fictional leader and
+ do the same for the person you are speaking about. e.g. "The Oldenburgian nation are lying warmongers and cannot
+ be trusted" is acceptable.
+ 4. No abuse, spam, harrassment. This includes but is not limited to, using another person or "bot" as the agent
+ for violating any site rules.
+ 4.1 Offsite links are almost always spam. Within reason, preserve the game entertainment ambience by keeping
+ in-game chat relative to the in-game play of your player-nation.
+ 4.2 Dominating a community discussion in Discord by simply posting many more messages than anyone is also bad
+ form, let others give their views.
+ 5. FCW is not the appropriate forum for publicly or privately promoting, recruiting, or poaching to other servers
+ or sites. Ask admin permission.
+ 6. FCW is not the appropriate forum for comparison or politicking for or against servers or sites.
+ 7. All constructive ideas and contributions are welcome. Politicking against the site or its members can be freely
+ done in locations other than FCW.
+ 8. Religious and political discussions are encouraged in locations other than FCW. Such discussions may be deleted
+ from FCW.
+ 9. Private DMs must abide by community rules when those DMs were bridged by mutual belonging to FCW Discord or the
+ FCW Game Server which make those communications possible. Private complaints about DMs to other members will be
+ treated the same as if they had been made in public, if those DMs were bridged or conveyed by a FCW server.
+ 10. FCW is not a public square with guaranteed free speech. It is more like a friend's house party where you are
+ expected to be nice and enjoy the offerings. We reserve the right to remove posts and ensure posted material is
+ not potentially harmful. As supporters of freedom of speech in general, we exercise these rights carefully with an
+ administrator audit trail and try to mamimize the good spirits of the house party.
+ 11. What happens at FCW stays at FCW: FCW is a private party gathering, our strict Privacy Policy represents to
+ our users that what they do at FCW takes place in a private gathering on a Preferred Privacy Protection site.
+ Users need not react to, nor concern themselves about distribution of their information or communications outside
+ of the FCW services. Do not screenshot, copy-paste, relay, report, journalize, log, transliterate, translate, or
+ otherwise convey information, communications, or any form of Private Identity Data, within the FCW services to
+ anywhere outside the FCW privacy sphere, without first obtaining permission. Violation of this policy means you
+ are no longer deemed to be an authorized user of our site. Private Identity Data includes but is not limited to
+ real name, address, location, nationality, telephone or digital contact information, and so on.
+ 11a. General sharing permission is already granted by default, for game experiences available from the perspective
+ of the nation you play in any game, and any message you make that does not interact with or imply or help infer,
+ another user's private content.
In-game Rules
- 11. Idle players may lose their nation.
- 12. You may request account deletion at any time.
- 13. Delegation should be a last resort, should not last too long, nor happen too frequently.
- 14. Victory conditions are set by the Gamemaster (GM) at the start.
- 14a. Don't declare victory for anyone but yourself. Your surrender is not a surrender for everyone else.
- 15. Customary practices which are not rules, but are generally accepted:
- 15.1 Turn length extension in the late game
- 15.2 Game settings error correction in the early game (at gamesmaster discretion)
- 15.3 Sharing of screenshots and story lines at the conclusion of a game
- 15.4 Victory conditions, unless specified to be different by game master before game start, are:
- 15.4a A player, or an alliance, kills all other players, or all other players surrender
- 15.4b If space race is on, a player lands on alpha centauri
- 15.4c A player, or group of players make a statement about how the game should end, that goes uncontested for 72 hours (or two turns, whichever is longer)
- 16. Allow players to take their turns in peace
- 16.1 No camping. No RTS (when requested)
-
- Clarification:
-
+ 11. Idle players may lose their nation.
+ 12. You may request account deletion at any time.
+ 13. Delegation should be a last resort, should not last too long, nor happen too frequently.
+ 14. Victory conditions are set by the Gamemaster (GM) at the start.
+ 14a. Don't declare victory for anyone but yourself. Your surrender is not a surrender for everyone else.
+ 15. Customary practices which are not rules, but are generally accepted:
+ 15.1 Turn length extension in the late game
+ 15.2 Game settings error correction in the early game (at gamesmaster discretion)
+ 15.3 Sharing of screenshots and story lines at the conclusion of a game
+ 15.4 Victory conditions, unless specified to be different by game master before game start, are:
+ 15.4a A player, or an alliance, kills all other players, or all other players surrender
+ 15.4b If space race is on, a player lands on alpha centauri
+ 15.4c A player, or group of players make a statement about how the game should end, that goes uncontested for 72
+ hours (or two turns, whichever is longer)
+ 16. Allow players to take their turns in peace
+ 16.1 No camping. No RTS (when requested)
+
+ Clarification:
+
- 11. Idle players can lose their nations on: turns 1-13: idle 3 turns; turns 14,15,16: idle 4,5,6 turns; turns 17,18,19: 7,8,9 turns. turn 20 onward: 10 turns.
- 12. Players are allowed to ask for account deletion to change their name, to prevent the same feuds and alliances in every game or for other data protection reasons.
- 13. Delegation should be a last resort, should not last too long, nor happen too frequently. GM will arbitrate complaints.
- 13.1 One delegation per player is allowed, only if the delegate plans to come back, and only up to a week without special permission
- 13.2 Long-term delegations can be approved by the Gamemaster (GM).
- 13.3 Delegations can't be done before turn 3
- 14. Victory conditions are set by the Gamemaster (GM) at the start. When they are met, the game ends. Unless the GM specified against at the outset, scores are published
- 14a. Players are unaware of all game events. Your alliance may be defeated and feel your surrender means victory for your enemies, but perhaps the opposing alliance is about to splinter or a small nation secretly built a large nuclear arsenal. You may declare victory for yourself or your own alliance only. Surrender does not equate to a victory declaration for others. Do NOT declare someone else's victory.
- 15. Customary practices which are not rules, but are generally expected and accepted:
- 15.1 FCW typically extends turn length in the late game of longturn games. (25h, 35h, 37h, or 47h). This meets the needs of players who have successfully grown large nations. Turn length extension will be decided on a case by case basis by the GM who will make a judgement based on their discretion, after the first active player makes a public request. For example, generally, after turn 100, an extension to 35h turns is common practice.
- 15.2 For obvious settings errors, a Gamemaster (GM) can fix any such settings before T10 provided they do not affect any player and provided the GM fully announces such changes, giving at least 1 turn notice before making the change. Exception: announcing creates an exploit, in which case the GM may elect not to give notice (but must inform those members of the admin council who are non-players in the game.)
- 16. Allow players to take their turns in peace. This includes respecting requests not to RTS. RTS is to be expected at turn change, just prior to turn change, and at unitwaittime before and after turn change. All other times, RTS is not allowed if a player requests to take their turn in peace.
- 16.1 No camping. A player should not be online for most the day. Remaining online all day is unfair to everyone else.
- 16.1a Rule 16.1 is relaxed after turn 100 in games or upon discovery of Flight (whichever comes sooner), until a fix for large nations is in place. Gamemaster discretion is final, if players are in dispute.
- 17. Regarding multiple players in the same household or sharing the same device: The Gamemaster for a particular game can approve such cases if asked privately in advance of you joining the game; otherwise it is forbidden. Gamemaster decision will be final, and GM may make special rules for allowing the case like making all players aware of it, or forbidding alliance between the two players.
+ 11. Idle players can lose their nations on: turns 1-13: idle 3 turns; turns 14,15,16: idle 4,5,6 turns; turns
+ 17,18,19: 7,8,9 turns. turn 20 onward: 10 turns.
+ 12. Players are allowed to ask for account deletion to change their name, to prevent the same feuds and alliances
+ in every game or for other data protection reasons.
+ 13. Delegation should be a last resort, should not last too long, nor happen too frequently. GM will arbitrate
+ complaints.
+ 13.1 One delegation per player is allowed, only if the delegate plans to come back, and only up to a week without
+ special permission
+ 13.2 Long-term delegations can be approved by the Gamemaster (GM).
+ 13.3 Delegations can't be done before turn 3
+ 14. Victory conditions are set by the Gamemaster (GM) at the start. When they are met, the game ends. Unless the
+ GM specified against at the outset, scores are published
+ 14a. Players are unaware of all game events. Your alliance may be defeated and feel your surrender means victory
+ for your enemies, but perhaps the opposing alliance is about to splinter or a small nation secretly built a large
+ nuclear arsenal. You may declare victory for yourself or your own alliance only. Surrender does not equate to a
+ victory declaration for others. Do NOT declare someone else's victory.
+ 15. Customary practices which are not rules, but are generally expected and accepted:
+ 15.1 FCW typically extends turn length in the late game of longturn games. (25h, 35h, 37h, or 47h). This meets the
+ needs of players who have successfully grown large nations. Turn length extension will be decided on a case by
+ case basis by the GM who will make a judgement based on their discretion, after the first active player makes a
+ public request. For example, generally, after turn 100, an extension to 35h turns is common practice.
+ 15.2 For obvious settings errors, a Gamemaster (GM) can fix any such settings before T10 provided they do not
+ affect any player and provided the GM fully announces such changes, giving at least 1 turn notice before making
+ the change. Exception: announcing creates an exploit, in which case the GM may elect not to give notice (but must
+ inform those members of the admin council who are non-players in the game.)
+ 16. Allow players to take their turns in peace. This includes respecting requests not to RTS. RTS is to be
+ expected at turn change, just prior to turn change, and at unitwaittime before and after turn change. All other
+ times, RTS is not allowed if a player requests to take their turn in peace.
+ 16.1 No camping. A player should not be online for most the day. Remaining online all day is unfair to everyone
+ else.
+ 16.1a Rule 16.1 is relaxed after turn 100 in games or upon discovery of Flight (whichever comes sooner), until a
+ fix for large nations is in place. Gamemaster discretion is final, if players are in dispute.
+ 17. Regarding multiple players in the same household or sharing the same device: The Gamemaster for a particular
+ game can approve such cases if asked privately in advance of you joining the game; otherwise it is forbidden.
+ Gamemaster decision will be final, and GM may make special rules for allowing the case like making all players
+ aware of it, or forbidding alliance between the two players.
-
- FCW may alter these rules over time. This takes place after admin consultations and such changes will be posted on our Discord #rules channel.
-
-
-
- <%@include file="/WEB-INF/jsp/fragments/footer.jsp"%>
-
+
+ FCW may alter these rules over time. This takes place after admin consultations and such changes will be posted
+ on our Discord #rules channel.
+
+
+
+ <%@include file="/WEB-INF/jsp/fragments/footer.jsp" %>
+
Toggle national colors on minimap: primary,secondary,tertiary,diplomatic state
alt-shift-B
Animate borders for visibility assistance
@@ -178,6 +184,7 @@ The current and complete list of Freeciv-web controls can be found here:
ctrl-shift-C
Cycle city info display mode on map
ctrl-alt-D
Disconnect (disconnected viewing mode)
ctrl-E
Emoji selector
+
alt-F
Apply filters to console messages (include/exclude types)
alt-shift-F
Focus lock: center on active unit
ctrl-G
Map grid on/off
ctrl-alt-shift-I
Change style of the letter capital 'I'
@@ -258,3 +265,10 @@ The current and complete list of Freeciv-web controls can be found here:
+
Admin only:
Ctrl-Shift-E
Toggle Debug Log Messages
+
Ctrl-Alt-Shift-S
Toggle Supercow Interaction Lock
+
+
+
+
+
\ No newline at end of file
diff --git a/freeciv-web/src/main/webapp/fonts/arial.ttf b/freeciv-web/src/main/webapp/fonts/arial.ttf
deleted file mode 100644
index 8682d9462..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/arial.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/arialb.ttf b/freeciv-web/src/main/webapp/fonts/arialb.ttf
deleted file mode 100644
index a6037e686..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/arialb.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/arialn.ttf b/freeciv-web/src/main/webapp/fonts/arialn.ttf
deleted file mode 100644
index 94907a3df..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/arialn.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/arialnb.ttf b/freeciv-web/src/main/webapp/fonts/arialnb.ttf
deleted file mode 100644
index 62437f02f..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/arialnb.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/arialrndb.ttf b/freeciv-web/src/main/webapp/fonts/arialrndb.ttf
deleted file mode 100644
index 20b92a27d..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/arialrndb.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/ariblk.ttf b/freeciv-web/src/main/webapp/fonts/ariblk.ttf
deleted file mode 100644
index e7ae345aa..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/ariblk.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/cinzel.variable.ttf b/freeciv-web/src/main/webapp/fonts/cinzel.variable.ttf
new file mode 100644
index 000000000..d218a0b9c
Binary files /dev/null and b/freeciv-web/src/main/webapp/fonts/cinzel.variable.ttf differ
diff --git a/freeciv-web/src/main/webapp/fonts/cinzel.variable.woff2 b/freeciv-web/src/main/webapp/fonts/cinzel.variable.woff2
new file mode 100644
index 000000000..4550dc3e1
Binary files /dev/null and b/freeciv-web/src/main/webapp/fonts/cinzel.variable.woff2 differ
diff --git a/freeciv-web/src/main/webapp/fonts/consola.ttf b/freeciv-web/src/main/webapp/fonts/consola.ttf
deleted file mode 100644
index e881ca4b5..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/consola.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/consolab.ttf b/freeciv-web/src/main/webapp/fonts/consolab.ttf
deleted file mode 100644
index 77f5d6052..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/consolab.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/copperb.ttf b/freeciv-web/src/main/webapp/fonts/copperb.ttf
deleted file mode 100644
index 764965d4a..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/copperb.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/copperl.ttf b/freeciv-web/src/main/webapp/fonts/copperl.ttf
deleted file mode 100644
index a0edd5c63..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/copperl.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/edward.ttf b/freeciv-web/src/main/webapp/fonts/edward.ttf
deleted file mode 100644
index dcc21fddb..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/edward.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/freeciv.b.ttf b/freeciv-web/src/main/webapp/fonts/freeciv.b.ttf
deleted file mode 100644
index 2967c9ccd..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/freeciv.b.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/freeciv.bl.ttf b/freeciv-web/src/main/webapp/fonts/freeciv.bl.ttf
deleted file mode 100644
index 4b6895ff6..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/freeciv.bl.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/freeciv.m.ttf b/freeciv-web/src/main/webapp/fonts/freeciv.m.ttf
deleted file mode 100644
index cd419d257..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/freeciv.m.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/freeciv.otf b/freeciv-web/src/main/webapp/fonts/freeciv.otf
deleted file mode 100644
index fc526ae37..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/freeciv.otf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/freeciv.ttf b/freeciv-web/src/main/webapp/fonts/freeciv.ttf
deleted file mode 100644
index fc526ae37..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/freeciv.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/seg.b.ttf b/freeciv-web/src/main/webapp/fonts/seg.b.ttf
deleted file mode 100644
index ff196d601..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/seg.b.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/seg.bl.ttf b/freeciv-web/src/main/webapp/fonts/seg.bl.ttf
deleted file mode 100644
index b2966570e..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/seg.bl.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/seg.l.ttf b/freeciv-web/src/main/webapp/fonts/seg.l.ttf
deleted file mode 100644
index 23fb04db8..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/seg.l.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/seg.sb.ttf b/freeciv-web/src/main/webapp/fonts/seg.sb.ttf
deleted file mode 100644
index 90b39f72f..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/seg.sb.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/seg.sl.ttf b/freeciv-web/src/main/webapp/fonts/seg.sl.ttf
deleted file mode 100644
index a276476a7..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/seg.sl.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/seg.ttf b/freeciv-web/src/main/webapp/fonts/seg.ttf
deleted file mode 100644
index 0f52cbd9c..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/seg.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/tahoma.ttf b/freeciv-web/src/main/webapp/fonts/tahoma.ttf
deleted file mode 100644
index b7b708633..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/tahoma.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/tahomab.ttf b/freeciv-web/src/main/webapp/fonts/tahomab.ttf
deleted file mode 100644
index 2b694bf1f..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/tahomab.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/verdana.ttf b/freeciv-web/src/main/webapp/fonts/verdana.ttf
deleted file mode 100644
index 9a3499751..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/verdana.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/fonts/verdanab.ttf b/freeciv-web/src/main/webapp/fonts/verdanab.ttf
deleted file mode 100644
index fe8e6879b..000000000
Binary files a/freeciv-web/src/main/webapp/fonts/verdanab.ttf and /dev/null differ
diff --git a/freeciv-web/src/main/webapp/gltf/AEGIS Cruiser.glb b/freeciv-web/src/main/webapp/gltf/AEGIS Cruiser.glb
new file mode 100644
index 000000000..307ae3781
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/AEGIS Cruiser.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/AWACS.glb b/freeciv-web/src/main/webapp/gltf/AWACS.glb
new file mode 100644
index 000000000..3179d49a8
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/AWACS.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Airbase.glb b/freeciv-web/src/main/webapp/gltf/Airbase.glb
new file mode 100644
index 000000000..d3f66e628
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Airbase.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Alpine Troops.glb b/freeciv-web/src/main/webapp/gltf/Alpine Troops.glb
new file mode 100644
index 000000000..66c80b7a3
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Alpine Troops.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Archers.glb b/freeciv-web/src/main/webapp/gltf/Archers.glb
new file mode 100644
index 000000000..f5d52d978
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Archers.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Armor.glb b/freeciv-web/src/main/webapp/gltf/Armor.glb
new file mode 100644
index 000000000..353ddbb79
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Armor.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Artillery.glb b/freeciv-web/src/main/webapp/gltf/Artillery.glb
new file mode 100644
index 000000000..d48992ba4
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Artillery.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Barbarian Leader.glb b/freeciv-web/src/main/webapp/gltf/Barbarian Leader.glb
new file mode 100644
index 000000000..7de237fb2
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Barbarian Leader.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Battleship.glb b/freeciv-web/src/main/webapp/gltf/Battleship.glb
new file mode 100644
index 000000000..b93a23b0a
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Battleship.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Bomber.glb b/freeciv-web/src/main/webapp/gltf/Bomber.glb
new file mode 100644
index 000000000..5c00472c6
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Bomber.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Cannon.glb b/freeciv-web/src/main/webapp/gltf/Cannon.glb
new file mode 100644
index 000000000..8c0ad12cc
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Cannon.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Caravan.glb b/freeciv-web/src/main/webapp/gltf/Caravan.glb
new file mode 100644
index 000000000..28e464a53
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Caravan.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Caravel.glb b/freeciv-web/src/main/webapp/gltf/Caravel.glb
new file mode 100644
index 000000000..49b084b95
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Caravel.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Carrier.glb b/freeciv-web/src/main/webapp/gltf/Carrier.glb
new file mode 100644
index 000000000..928cd5d7a
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Carrier.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Catapult.glb b/freeciv-web/src/main/webapp/gltf/Catapult.glb
new file mode 100644
index 000000000..046efcf9c
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Catapult.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Cavalry.glb b/freeciv-web/src/main/webapp/gltf/Cavalry.glb
new file mode 100644
index 000000000..56b6c8eaf
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Cavalry.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Chariot.glb b/freeciv-web/src/main/webapp/gltf/Chariot.glb
new file mode 100644
index 000000000..592cacf9c
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Chariot.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Cruise Missile.glb b/freeciv-web/src/main/webapp/gltf/Cruise Missile.glb
new file mode 100644
index 000000000..b9a4ff8cc
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Cruise Missile.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Cruiser.glb b/freeciv-web/src/main/webapp/gltf/Cruiser.glb
new file mode 100644
index 000000000..bfb0905dd
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Cruiser.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Destroyer.glb b/freeciv-web/src/main/webapp/gltf/Destroyer.glb
new file mode 100644
index 000000000..cbc93aa6d
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Destroyer.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Diplomat.glb b/freeciv-web/src/main/webapp/gltf/Diplomat.glb
new file mode 100644
index 000000000..6f77096f6
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Diplomat.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Dragoons.glb b/freeciv-web/src/main/webapp/gltf/Dragoons.glb
new file mode 100644
index 000000000..6ec7ef635
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Dragoons.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Engineers.glb b/freeciv-web/src/main/webapp/gltf/Engineers.glb
new file mode 100644
index 000000000..ff03dfff6
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Engineers.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Explorer.glb b/freeciv-web/src/main/webapp/gltf/Explorer.glb
new file mode 100644
index 000000000..57ea517ea
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Explorer.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Fighter.glb b/freeciv-web/src/main/webapp/gltf/Fighter.glb
new file mode 100644
index 000000000..46720724d
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Fighter.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Fish.glb b/freeciv-web/src/main/webapp/gltf/Fish.glb
new file mode 100644
index 000000000..72a04e6eb
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Fish.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Fortress.glb b/freeciv-web/src/main/webapp/gltf/Fortress.glb
new file mode 100644
index 000000000..1789e03b8
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Fortress.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Freight.glb b/freeciv-web/src/main/webapp/gltf/Freight.glb
new file mode 100644
index 000000000..2dd9a249d
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Freight.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Frigate.glb b/freeciv-web/src/main/webapp/gltf/Frigate.glb
new file mode 100644
index 000000000..77093a0be
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Frigate.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Galleon.glb b/freeciv-web/src/main/webapp/gltf/Galleon.glb
new file mode 100644
index 000000000..9dc861bb3
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Galleon.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Helicopter.glb b/freeciv-web/src/main/webapp/gltf/Helicopter.glb
new file mode 100644
index 000000000..6c8f30635
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Helicopter.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Horsemen.glb b/freeciv-web/src/main/webapp/gltf/Horsemen.glb
new file mode 100644
index 000000000..33f97d384
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Horsemen.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Howitzer.glb b/freeciv-web/src/main/webapp/gltf/Howitzer.glb
new file mode 100644
index 000000000..36eae60f2
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Howitzer.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Hut.glb b/freeciv-web/src/main/webapp/gltf/Hut.glb
new file mode 100644
index 000000000..4d3bf1dee
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Hut.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Ironclad.glb b/freeciv-web/src/main/webapp/gltf/Ironclad.glb
new file mode 100644
index 000000000..5444899d8
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Ironclad.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Knights.glb b/freeciv-web/src/main/webapp/gltf/Knights.glb
new file mode 100644
index 000000000..8fb3e2e36
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Knights.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Legion.glb b/freeciv-web/src/main/webapp/gltf/Legion.glb
new file mode 100644
index 000000000..3ed74ee7d
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Legion.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Marines.glb b/freeciv-web/src/main/webapp/gltf/Marines.glb
new file mode 100644
index 000000000..71c1605d4
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Marines.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Mech. Inf..glb b/freeciv-web/src/main/webapp/gltf/Mech. Inf..glb
new file mode 100644
index 000000000..33ae7ab9c
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Mech. Inf..glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Migrants.glb b/freeciv-web/src/main/webapp/gltf/Migrants.glb
new file mode 100644
index 000000000..976f13955
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Migrants.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Mine.glb b/freeciv-web/src/main/webapp/gltf/Mine.glb
new file mode 100644
index 000000000..92ddcad68
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Mine.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Musketeers.glb b/freeciv-web/src/main/webapp/gltf/Musketeers.glb
new file mode 100644
index 000000000..8493d31ac
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Musketeers.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Nuclear.glb b/freeciv-web/src/main/webapp/gltf/Nuclear.glb
new file mode 100644
index 000000000..3b04b4c9f
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Nuclear.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Paratroopers.glb b/freeciv-web/src/main/webapp/gltf/Paratroopers.glb
new file mode 100644
index 000000000..8fe9be344
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Paratroopers.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Partisan.glb b/freeciv-web/src/main/webapp/gltf/Partisan.glb
new file mode 100644
index 000000000..5af0ad6ea
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Partisan.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Phalanx.glb b/freeciv-web/src/main/webapp/gltf/Phalanx.glb
new file mode 100644
index 000000000..3a9932bcf
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Phalanx.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Pikemen.glb b/freeciv-web/src/main/webapp/gltf/Pikemen.glb
new file mode 100644
index 000000000..3b338fc0e
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Pikemen.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/README.md b/freeciv-web/src/main/webapp/gltf/README.md
new file mode 100644
index 000000000..9c3770d6e
--- /dev/null
+++ b/freeciv-web/src/main/webapp/gltf/README.md
@@ -0,0 +1,7 @@
+glTF 3D models for Freeciv WebGL
+================================
+
+This directory contains glTF 2.0 binary .glb files,
+which have been exported from the /blender directory.
+
+
diff --git a/freeciv-web/src/main/webapp/gltf/Riflemen.glb b/freeciv-web/src/main/webapp/gltf/Riflemen.glb
new file mode 100644
index 000000000..937432773
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Riflemen.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Ruins.glb b/freeciv-web/src/main/webapp/gltf/Ruins.glb
new file mode 100644
index 000000000..9817d4279
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Ruins.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Settlers.glb b/freeciv-web/src/main/webapp/gltf/Settlers.glb
new file mode 100644
index 000000000..40b8b828a
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Settlers.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Spy.glb b/freeciv-web/src/main/webapp/gltf/Spy.glb
new file mode 100644
index 000000000..999a9b184
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Spy.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Stealth Bomber.glb b/freeciv-web/src/main/webapp/gltf/Stealth Bomber.glb
new file mode 100644
index 000000000..6b887e2ed
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Stealth Bomber.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Stealth Fighter.glb b/freeciv-web/src/main/webapp/gltf/Stealth Fighter.glb
new file mode 100644
index 000000000..fbadae108
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Stealth Fighter.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Submarine.glb b/freeciv-web/src/main/webapp/gltf/Submarine.glb
new file mode 100644
index 000000000..d460723dd
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Submarine.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Transport.glb b/freeciv-web/src/main/webapp/gltf/Transport.glb
new file mode 100644
index 000000000..fa9d35ce8
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Transport.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Trireme.glb b/freeciv-web/src/main/webapp/gltf/Trireme.glb
new file mode 100644
index 000000000..47a3e5297
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Trireme.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Warriors.glb b/freeciv-web/src/main/webapp/gltf/Warriors.glb
new file mode 100644
index 000000000..ab48c4ee9
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Warriors.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Whales.glb b/freeciv-web/src/main/webapp/gltf/Whales.glb
new file mode 100644
index 000000000..bb1548d89
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Whales.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/Workers.glb b/freeciv-web/src/main/webapp/gltf/Workers.glb
new file mode 100644
index 000000000..1513af6e4
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/Workers.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_european_0.glb b/freeciv-web/src/main/webapp/gltf/city_european_0.glb
new file mode 100644
index 000000000..55a5deb5e
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_european_0.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_european_1.glb b/freeciv-web/src/main/webapp/gltf/city_european_1.glb
new file mode 100644
index 000000000..288afc673
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_european_1.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_european_2.glb b/freeciv-web/src/main/webapp/gltf/city_european_2.glb
new file mode 100644
index 000000000..3a900677f
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_european_2.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_european_3.glb b/freeciv-web/src/main/webapp/gltf/city_european_3.glb
new file mode 100644
index 000000000..56eb5871d
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_european_3.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_european_4.glb b/freeciv-web/src/main/webapp/gltf/city_european_4.glb
new file mode 100644
index 000000000..50a1e0b56
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_european_4.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_modern_0.glb b/freeciv-web/src/main/webapp/gltf/city_modern_0.glb
new file mode 100644
index 000000000..971ee043a
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_modern_0.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_modern_1.glb b/freeciv-web/src/main/webapp/gltf/city_modern_1.glb
new file mode 100644
index 000000000..f6dec0dce
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_modern_1.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_modern_2.glb b/freeciv-web/src/main/webapp/gltf/city_modern_2.glb
new file mode 100644
index 000000000..b38cd9dd4
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_modern_2.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_modern_3.glb b/freeciv-web/src/main/webapp/gltf/city_modern_3.glb
new file mode 100644
index 000000000..7059bbc8e
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_modern_3.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/city_modern_4.glb b/freeciv-web/src/main/webapp/gltf/city_modern_4.glb
new file mode 100644
index 000000000..cb384c18e
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/city_modern_4.glb differ
diff --git a/freeciv-web/src/main/webapp/gltf/citywalls.glb b/freeciv-web/src/main/webapp/gltf/citywalls.glb
new file mode 100644
index 000000000..9609ef114
Binary files /dev/null and b/freeciv-web/src/main/webapp/gltf/citywalls.glb differ
diff --git a/freeciv-web/src/main/webapp/images/bg-alt-dark.jpg b/freeciv-web/src/main/webapp/images/bg-alt-dark.jpg
new file mode 100644
index 000000000..bfff653d0
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/bg-alt-dark.jpg differ
diff --git a/freeciv-web/src/main/webapp/images/bg-dark50.png b/freeciv-web/src/main/webapp/images/bg-dark50.png
new file mode 100644
index 000000000..b00947c2f
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/bg-dark50.png differ
diff --git a/freeciv-web/src/main/webapp/images/bg-odin.jpg b/freeciv-web/src/main/webapp/images/bg-odin.jpg
new file mode 100644
index 000000000..769974afd
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/bg-odin.jpg differ
diff --git a/freeciv-web/src/main/webapp/images/bg-smooth-mdark.jpg b/freeciv-web/src/main/webapp/images/bg-smooth-mdark.jpg
new file mode 100644
index 000000000..e009b9dfa
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/bg-smooth-mdark.jpg differ
diff --git a/freeciv-web/src/main/webapp/images/chapter_mark.png b/freeciv-web/src/main/webapp/images/chapter_mark.png
new file mode 100644
index 000000000..22b5c8b29
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/chapter_mark.png differ
diff --git a/freeciv-web/src/main/webapp/images/dark-check-orange.png b/freeciv-web/src/main/webapp/images/dark-check-orange.png
index 5a8dbf567..9796c6f3c 100644
Binary files a/freeciv-web/src/main/webapp/images/dark-check-orange.png and b/freeciv-web/src/main/webapp/images/dark-check-orange.png differ
diff --git a/freeciv-web/src/main/webapp/images/dark-check-yellow.png b/freeciv-web/src/main/webapp/images/dark-check-yellow.png
new file mode 100644
index 000000000..4e0821f9a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/dark-check-yellow.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-bear-.png b/freeciv-web/src/main/webapp/images/e/-bear-.png
new file mode 100644
index 000000000..44d51b2a3
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-bear-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-crocodile-.png b/freeciv-web/src/main/webapp/images/e/-crocodile-.png
new file mode 100644
index 000000000..11ff5c537
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-crocodile-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-giantsquid-.png b/freeciv-web/src/main/webapp/images/e/-giantsquid-.png
new file mode 100644
index 000000000..9e00b87e8
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-giantsquid-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-hippo-.png b/freeciv-web/src/main/webapp/images/e/-hippo-.png
new file mode 100644
index 000000000..ba696afce
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-hippo-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-leopard-.png b/freeciv-web/src/main/webapp/images/e/-leopard-.png
new file mode 100644
index 000000000..489f991a0
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-leopard-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-lion-.png b/freeciv-web/src/main/webapp/images/e/-lion-.png
new file mode 100644
index 000000000..f7d60d6d4
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-lion-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-polarbear-.png b/freeciv-web/src/main/webapp/images/e/-polarbear-.png
new file mode 100644
index 000000000..3cf6c443b
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-polarbear-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-rhino-.png b/freeciv-web/src/main/webapp/images/e/-rhino-.png
new file mode 100644
index 000000000..486e55ed5
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-rhino-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/-wolf-.png b/freeciv-web/src/main/webapp/images/e/-wolf-.png
new file mode 100644
index 000000000..4c1c19bc7
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/-wolf-.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/a_left.png b/freeciv-web/src/main/webapp/images/e/a_left.png
new file mode 100644
index 000000000..c7ca04c12
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/a_left.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/a_rght.png b/freeciv-web/src/main/webapp/images/e/a_rght.png
new file mode 100644
index 000000000..8db164050
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/a_rght.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/anger.png b/freeciv-web/src/main/webapp/images/e/anger.png
new file mode 100644
index 000000000..95834e426
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/anger.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/arght.png b/freeciv-web/src/main/webapp/images/e/arght.png
new file mode 100644
index 000000000..8db164050
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/arght.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/ballista.png b/freeciv-web/src/main/webapp/images/e/ballista.png
new file mode 100644
index 000000000..64b0d60fb
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/ballista.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/bombs.png b/freeciv-web/src/main/webapp/images/e/bombs.png
new file mode 100644
index 000000000..5027e1854
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/bombs.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/boot.png b/freeciv-web/src/main/webapp/images/e/boot.png
new file mode 100644
index 000000000..f6279caf9
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/boot.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/briefcase.png b/freeciv-web/src/main/webapp/images/e/briefcase.png
new file mode 100644
index 000000000..3a64ecb47
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/briefcase.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/bullseye.png b/freeciv-web/src/main/webapp/images/e/bullseye.png
new file mode 100644
index 000000000..2ba33ac36
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/bullseye.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/caravan.png b/freeciv-web/src/main/webapp/images/e/caravan.png
index 65efde1d3..afa3ca973 100644
Binary files a/freeciv-web/src/main/webapp/images/e/caravan.png and b/freeciv-web/src/main/webapp/images/e/caravan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/codeofhammurabi.png b/freeciv-web/src/main/webapp/images/e/codeofhammurabi.png
new file mode 100644
index 000000000..247801759
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/codeofhammurabi.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/commissariatofagriculture.png b/freeciv-web/src/main/webapp/images/e/commissariatofagriculture.png
new file mode 100644
index 000000000..229914d95
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/commissariatofagriculture.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/emissary.png b/freeciv-web/src/main/webapp/images/e/emissary.png
new file mode 100644
index 000000000..59387b8bb
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/emissary.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/equal.png b/freeciv-web/src/main/webapp/images/e/equal.png
index b6aa02885..208edcec0 100644
Binary files a/freeciv-web/src/main/webapp/images/e/equal.png and b/freeciv-web/src/main/webapp/images/e/equal.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/events/citydestroy.png b/freeciv-web/src/main/webapp/images/e/events/citydestroy.png
new file mode 100644
index 000000000..c8e887180
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/events/citydestroy.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/events/democracy.png b/freeciv-web/src/main/webapp/images/e/events/democracy.png
new file mode 100644
index 000000000..c84f65e6d
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/events/democracy.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/events/theology.png b/freeciv-web/src/main/webapp/images/e/events/theology.png
new file mode 100644
index 000000000..c074cb3a2
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/events/theology.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/eye.png b/freeciv-web/src/main/webapp/images/e/eye.png
new file mode 100644
index 000000000..7324961d2
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/eye.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/fanatics.png b/freeciv-web/src/main/webapp/images/e/fanatics.png
index 489134b3f..d6a66774c 100644
Binary files a/freeciv-web/src/main/webapp/images/e/fanatics.png and b/freeciv-web/src/main/webapp/images/e/fanatics.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/abkhazia.png b/freeciv-web/src/main/webapp/images/e/flag/abkhazia.png
index b9a7a56ee..70877f592 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/abkhazia.png and b/freeciv-web/src/main/webapp/images/e/flag/abkhazia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/antarctica_alt.png b/freeciv-web/src/main/webapp/images/e/flag/antarctica_alt.png
new file mode 100644
index 000000000..3ed97c006
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/antarctica_alt.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/antigua_and_barbuda.png b/freeciv-web/src/main/webapp/images/e/flag/antigua_and_barbuda.png
new file mode 100644
index 000000000..37ec86582
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/antigua_and_barbuda.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/argentina.png b/freeciv-web/src/main/webapp/images/e/flag/argentina.png
index c73277162..65bc954b1 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/argentina.png and b/freeciv-web/src/main/webapp/images/e/flag/argentina.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/armenia.png b/freeciv-web/src/main/webapp/images/e/flag/armenia.png
index 19750a4d6..c9f0e934b 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/armenia.png and b/freeciv-web/src/main/webapp/images/e/flag/armenia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/azerbaijan.png b/freeciv-web/src/main/webapp/images/e/flag/azerbaijan.png
index d75ef3ef6..47e52f19d 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/azerbaijan.png and b/freeciv-web/src/main/webapp/images/e/flag/azerbaijan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/bahamas.png b/freeciv-web/src/main/webapp/images/e/flag/bahamas.png
index 1218a2742..1fe80335d 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/bahamas.png and b/freeciv-web/src/main/webapp/images/e/flag/bahamas.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/belarus.png b/freeciv-web/src/main/webapp/images/e/flag/belarus.png
index c95a214b7..47c81b033 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/belarus.png and b/freeciv-web/src/main/webapp/images/e/flag/belarus.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/benin_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/benin_ancient.png
new file mode 100644
index 000000000..d0763ea15
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/benin_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/bosnia.png b/freeciv-web/src/main/webapp/images/e/flag/bosnia.png
index 019b70671..b04874657 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/bosnia.png and b/freeciv-web/src/main/webapp/images/e/flag/bosnia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/brasil.png b/freeciv-web/src/main/webapp/images/e/flag/brasil.png
index 0ae8451f5..997c228f1 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/brasil.png and b/freeciv-web/src/main/webapp/images/e/flag/brasil.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/brunei.png b/freeciv-web/src/main/webapp/images/e/flag/brunei.png
index dda615ba3..aefc70ba2 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/brunei.png and b/freeciv-web/src/main/webapp/images/e/flag/brunei.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/burkina_faso.png b/freeciv-web/src/main/webapp/images/e/flag/burkina_faso.png
new file mode 100644
index 000000000..7413b235a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/burkina_faso.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/canada.png b/freeciv-web/src/main/webapp/images/e/flag/canada.png
index fe9f3ae4c..68d29548e 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/canada.png and b/freeciv-web/src/main/webapp/images/e/flag/canada.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/canada_old.png b/freeciv-web/src/main/webapp/images/e/flag/canada_old.png
new file mode 100644
index 000000000..5e4faab11
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/canada_old.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/cape_verde.png b/freeciv-web/src/main/webapp/images/e/flag/cape_verde.png
new file mode 100644
index 000000000..008c7b273
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/cape_verde.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/central_america.png b/freeciv-web/src/main/webapp/images/e/flag/central_america.png
new file mode 100644
index 000000000..3dc78406a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/central_america.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/central_lithuania.png b/freeciv-web/src/main/webapp/images/e/flag/central_lithuania.png
new file mode 100644
index 000000000..283b62e51
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/central_lithuania.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/costa_rica.png b/freeciv-web/src/main/webapp/images/e/flag/costa_rica.png
new file mode 100644
index 000000000..49b63b0d3
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/costa_rica.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/crimean_tatar.png b/freeciv-web/src/main/webapp/images/e/flag/crimean_tatar.png
new file mode 100644
index 000000000..db686b955
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/crimean_tatar.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/crimeantatar.png b/freeciv-web/src/main/webapp/images/e/flag/crimeantatar.png
index b282aa2bf..c6c322331 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/crimeantatar.png and b/freeciv-web/src/main/webapp/images/e/flag/crimeantatar.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/croatia.png b/freeciv-web/src/main/webapp/images/e/flag/croatia.png
index d94a5bdb3..6bdf58c73 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/croatia.png and b/freeciv-web/src/main/webapp/images/e/flag/croatia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/cuba.png b/freeciv-web/src/main/webapp/images/e/flag/cuba.png
index 666daa662..a4d5c3a79 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/cuba.png and b/freeciv-web/src/main/webapp/images/e/flag/cuba.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/czech.png b/freeciv-web/src/main/webapp/images/e/flag/czech.png
index dc509e638..38114a100 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/czech.png and b/freeciv-web/src/main/webapp/images/e/flag/czech.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/czechoslovakia.png b/freeciv-web/src/main/webapp/images/e/flag/czechoslovakia.png
index e8498f155..2655678ac 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/czechoslovakia.png and b/freeciv-web/src/main/webapp/images/e/flag/czechoslovakia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/dominica.png b/freeciv-web/src/main/webapp/images/e/flag/dominica.png
index d7eabfe5e..4451b0a47 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/dominica.png and b/freeciv-web/src/main/webapp/images/e/flag/dominica.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/dominican_republic.png b/freeciv-web/src/main/webapp/images/e/flag/dominican_republic.png
new file mode 100644
index 000000000..c75dccf8c
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/dominican_republic.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/dr_congo.png b/freeciv-web/src/main/webapp/images/e/flag/dr_congo.png
new file mode 100644
index 000000000..e9a9c842b
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/dr_congo.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/east_timor.png b/freeciv-web/src/main/webapp/images/e/flag/east_timor.png
new file mode 100644
index 000000000..0571f507a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/east_timor.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/egypt_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/egypt_ancient.png
new file mode 100644
index 000000000..96b0ed2e2
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/egypt_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/el_salvador.png b/freeciv-web/src/main/webapp/images/e/flag/el_salvador.png
new file mode 100644
index 000000000..81aca2d81
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/el_salvador.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/equatorial_guinea.png b/freeciv-web/src/main/webapp/images/e/flag/equatorial_guinea.png
new file mode 100644
index 000000000..6ba353a10
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/equatorial_guinea.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/eritrea.png b/freeciv-web/src/main/webapp/images/e/flag/eritrea.png
index 377d12930..2fac5f81e 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/eritrea.png and b/freeciv-web/src/main/webapp/images/e/flag/eritrea.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/ethiopia.png b/freeciv-web/src/main/webapp/images/e/flag/ethiopia.png
index 455088d43..5c79c27dd 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/ethiopia.png and b/freeciv-web/src/main/webapp/images/e/flag/ethiopia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/ethiopia_old.png b/freeciv-web/src/main/webapp/images/e/flag/ethiopia_old.png
new file mode 100644
index 000000000..4e54c89e1
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/ethiopia_old.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/etruscan.png b/freeciv-web/src/main/webapp/images/e/flag/etruscan.png
index 3dfaaea2a..d0de8a51a 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/etruscan.png and b/freeciv-web/src/main/webapp/images/e/flag/etruscan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/europe.png b/freeciv-web/src/main/webapp/images/e/flag/europe.png
index 3b6c1cec7..44fc645de 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/europe.png and b/freeciv-web/src/main/webapp/images/e/flag/europe.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/finland.png b/freeciv-web/src/main/webapp/images/e/flag/finland.png
index 0cbb7c5b3..310af49b4 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/finland.png and b/freeciv-web/src/main/webapp/images/e/flag/finland.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/france_old.png b/freeciv-web/src/main/webapp/images/e/flag/france_old.png
new file mode 100644
index 000000000..99d737d97
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/france_old.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/french_polynesia.png b/freeciv-web/src/main/webapp/images/e/flag/french_polynesia.png
new file mode 100644
index 000000000..44c2fa77e
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/french_polynesia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/germany.png b/freeciv-web/src/main/webapp/images/e/flag/germany.png
index 66357a9fa..168c9479d 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/germany.png and b/freeciv-web/src/main/webapp/images/e/flag/germany.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/ghana_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/ghana_ancient.png
new file mode 100644
index 000000000..f7a16bfa1
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/ghana_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/ghaznavid.png b/freeciv-web/src/main/webapp/images/e/flag/ghaznavid.png
index 5d7a2c817..5c2997227 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/ghaznavid.png and b/freeciv-web/src/main/webapp/images/e/flag/ghaznavid.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/golden_horde.png b/freeciv-web/src/main/webapp/images/e/flag/golden_horde.png
new file mode 100644
index 000000000..6af1469de
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/golden_horde.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/greater_poland.png b/freeciv-web/src/main/webapp/images/e/flag/greater_poland.png
new file mode 100644
index 000000000..9f7e23610
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/greater_poland.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/greece_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/greece_ancient.png
new file mode 100644
index 000000000..3fe341f5d
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/greece_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/guatemala.png b/freeciv-web/src/main/webapp/images/e/flag/guatemala.png
index 4f8025f83..3c89462e6 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/guatemala.png and b/freeciv-web/src/main/webapp/images/e/flag/guatemala.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/guinea-bissau.png b/freeciv-web/src/main/webapp/images/e/flag/guinea-bissau.png
index 3f5637086..5a0ef347f 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/guinea-bissau.png and b/freeciv-web/src/main/webapp/images/e/flag/guinea-bissau.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/honduras.png b/freeciv-web/src/main/webapp/images/e/flag/honduras.png
index 12d5bad47..1cef38707 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/honduras.png and b/freeciv-web/src/main/webapp/images/e/flag/honduras.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/hungary.png b/freeciv-web/src/main/webapp/images/e/flag/hungary.png
index cb8604a38..b781bd9b4 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/hungary.png and b/freeciv-web/src/main/webapp/images/e/flag/hungary.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/iceland.png b/freeciv-web/src/main/webapp/images/e/flag/iceland.png
index b7bc08d3b..c13a6656d 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/iceland.png and b/freeciv-web/src/main/webapp/images/e/flag/iceland.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/iran_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/iran_ancient.png
new file mode 100644
index 000000000..a34ff282b
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/iran_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/iraq_old.png b/freeciv-web/src/main/webapp/images/e/flag/iraq_old.png
new file mode 100644
index 000000000..d3ec5d04d
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/iraq_old.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/ireland.png b/freeciv-web/src/main/webapp/images/e/flag/ireland.png
index 00104706c..4e36f1309 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/ireland.png and b/freeciv-web/src/main/webapp/images/e/flag/ireland.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/israel.png b/freeciv-web/src/main/webapp/images/e/flag/israel.png
index a007cdf96..65d0ea739 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/israel.png and b/freeciv-web/src/main/webapp/images/e/flag/israel.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/israel_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/israel_ancient.png
new file mode 100644
index 000000000..045ca8ed6
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/israel_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/italian_greek.png b/freeciv-web/src/main/webapp/images/e/flag/italian_greek.png
new file mode 100644
index 000000000..c314510bb
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/italian_greek.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/ivory_coast.png b/freeciv-web/src/main/webapp/images/e/flag/ivory_coast.png
new file mode 100644
index 000000000..9f32c3e12
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/ivory_coast.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/jamaica.png b/freeciv-web/src/main/webapp/images/e/flag/jamaica.png
index d8324b711..374d18bb3 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/jamaica.png and b/freeciv-web/src/main/webapp/images/e/flag/jamaica.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/japan.png b/freeciv-web/src/main/webapp/images/e/flag/japan.png
index f46492577..413520b7d 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/japan.png and b/freeciv-web/src/main/webapp/images/e/flag/japan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/jordan.png b/freeciv-web/src/main/webapp/images/e/flag/jordan.png
index 7d8675402..160f1c8c9 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/jordan.png and b/freeciv-web/src/main/webapp/images/e/flag/jordan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/kazakhstan.png b/freeciv-web/src/main/webapp/images/e/flag/kazakhstan.png
index 532c8604e..2bc3a09f5 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/kazakhstan.png and b/freeciv-web/src/main/webapp/images/e/flag/kazakhstan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/kiribati.png b/freeciv-web/src/main/webapp/images/e/flag/kiribati.png
index 7704a0ad2..1c1412a01 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/kiribati.png and b/freeciv-web/src/main/webapp/images/e/flag/kiribati.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/korea_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/korea_ancient.png
new file mode 100644
index 000000000..26a8576d1
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/korea_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/kuna_yala.png b/freeciv-web/src/main/webapp/images/e/flag/kuna_yala.png
new file mode 100644
index 000000000..5b84ce2a1
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/kuna_yala.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/kurd.png b/freeciv-web/src/main/webapp/images/e/flag/kurd.png
index 7b73bb0ce..265b73df3 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/kurd.png and b/freeciv-web/src/main/webapp/images/e/flag/kurd.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/kuwait.png b/freeciv-web/src/main/webapp/images/e/flag/kuwait.png
index e4101930b..b24f025a4 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/kuwait.png and b/freeciv-web/src/main/webapp/images/e/flag/kuwait.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/latin_empire.png b/freeciv-web/src/main/webapp/images/e/flag/latin_empire.png
new file mode 100644
index 000000000..aa7dac5dc
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/latin_empire.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/latvia.png b/freeciv-web/src/main/webapp/images/e/flag/latvia.png
index 1b956d940..2f5db1c5c 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/latvia.png and b/freeciv-web/src/main/webapp/images/e/flag/latvia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/lebanon.png b/freeciv-web/src/main/webapp/images/e/flag/lebanon.png
index f54131040..9998e9180 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/lebanon.png and b/freeciv-web/src/main/webapp/images/e/flag/lebanon.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/lesotho_old.png b/freeciv-web/src/main/webapp/images/e/flag/lesotho_old.png
new file mode 100644
index 000000000..000ac97bb
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/lesotho_old.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/libya.png b/freeciv-web/src/main/webapp/images/e/flag/libya.png
index a02c7d6d4..4bc23da88 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/libya.png and b/freeciv-web/src/main/webapp/images/e/flag/libya.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/libya_old.png b/freeciv-web/src/main/webapp/images/e/flag/libya_old.png
new file mode 100644
index 000000000..b51255518
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/libya_old.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/macedonia.png b/freeciv-web/src/main/webapp/images/e/flag/macedonia.png
index 47a816366..315932e37 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/macedonia.png and b/freeciv-web/src/main/webapp/images/e/flag/macedonia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/malaysia.png b/freeciv-web/src/main/webapp/images/e/flag/malaysia.png
index 3dc0d6335..967fe8cfd 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/malaysia.png and b/freeciv-web/src/main/webapp/images/e/flag/malaysia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/mali_ancient.png b/freeciv-web/src/main/webapp/images/e/flag/mali_ancient.png
new file mode 100644
index 000000000..06a839705
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/mali_ancient.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/man.png b/freeciv-web/src/main/webapp/images/e/flag/man.png
index 69efe2bf8..90f4b1173 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/man.png and b/freeciv-web/src/main/webapp/images/e/flag/man.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/marshall_islands.png b/freeciv-web/src/main/webapp/images/e/flag/marshall_islands.png
new file mode 100644
index 000000000..553f44159
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/marshall_islands.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/moldova.png b/freeciv-web/src/main/webapp/images/e/flag/moldova.png
index 877dd4eef..6095a77b0 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/moldova.png and b/freeciv-web/src/main/webapp/images/e/flag/moldova.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/moluccas.png b/freeciv-web/src/main/webapp/images/e/flag/moluccas.png
index 4e7ecb54d..e36f1df16 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/moluccas.png and b/freeciv-web/src/main/webapp/images/e/flag/moluccas.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/monaco_alternative.png b/freeciv-web/src/main/webapp/images/e/flag/monaco_alternative.png
new file mode 100644
index 000000000..b938aceca
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/monaco_alternative.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/monacoalternative.png b/freeciv-web/src/main/webapp/images/e/flag/monacoalternative.png
index 42dbab89f..b938aceca 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/monacoalternative.png and b/freeciv-web/src/main/webapp/images/e/flag/monacoalternative.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/mongolia.png b/freeciv-web/src/main/webapp/images/e/flag/mongolia.png
index aeff4f7b5..6b0d88c69 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/mongolia.png and b/freeciv-web/src/main/webapp/images/e/flag/mongolia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/myanmar_old.png b/freeciv-web/src/main/webapp/images/e/flag/myanmar_old.png
new file mode 100644
index 000000000..4503720e2
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/myanmar_old.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/nagorno_karabakh.png b/freeciv-web/src/main/webapp/images/e/flag/nagorno_karabakh.png
new file mode 100644
index 000000000..b791eb4ac
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/nagorno_karabakh.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/netherlands_antilles.png b/freeciv-web/src/main/webapp/images/e/flag/netherlands_antilles.png
new file mode 100644
index 000000000..0fc778bd5
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/netherlands_antilles.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/newzealand.png b/freeciv-web/src/main/webapp/images/e/flag/newzealand.png
index 0220069b3..b6f7bd931 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/newzealand.png and b/freeciv-web/src/main/webapp/images/e/flag/newzealand.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/nez_perce.png b/freeciv-web/src/main/webapp/images/e/flag/nez_perce.png
new file mode 100644
index 000000000..8e286cf34
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/nez_perce.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/nigeria.png b/freeciv-web/src/main/webapp/images/e/flag/nigeria.png
index bfbaafb9b..96918a672 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/nigeria.png and b/freeciv-web/src/main/webapp/images/e/flag/nigeria.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/north_korea.png b/freeciv-web/src/main/webapp/images/e/flag/north_korea.png
new file mode 100644
index 000000000..9ddb664f8
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/north_korea.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/northkorea.png b/freeciv-web/src/main/webapp/images/e/flag/northkorea.png
index a3ea0c639..173bfc9cf 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/northkorea.png and b/freeciv-web/src/main/webapp/images/e/flag/northkorea.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/norway.png b/freeciv-web/src/main/webapp/images/e/flag/norway.png
index a97003a74..c2f1d1d90 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/norway.png and b/freeciv-web/src/main/webapp/images/e/flag/norway.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/oldenburg.png b/freeciv-web/src/main/webapp/images/e/flag/oldenburg.png
index 06db1addb..25e5b4d60 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/oldenburg.png and b/freeciv-web/src/main/webapp/images/e/flag/oldenburg.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/papuanewguinea (2).png b/freeciv-web/src/main/webapp/images/e/flag/papua_newguinea.png
similarity index 100%
rename from freeciv-web/src/main/webapp/images/e/flag/papuanewguinea (2).png
rename to freeciv-web/src/main/webapp/images/e/flag/papua_newguinea.png
diff --git a/freeciv-web/src/main/webapp/images/e/flag/philippines.png b/freeciv-web/src/main/webapp/images/e/flag/philippines.png
index b2456fa43..784405b77 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/philippines.png and b/freeciv-web/src/main/webapp/images/e/flag/philippines.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/poland.png b/freeciv-web/src/main/webapp/images/e/flag/poland.png
index bc47e891e..81d3d17b2 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/poland.png and b/freeciv-web/src/main/webapp/images/e/flag/poland.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/puerto_rico.png b/freeciv-web/src/main/webapp/images/e/flag/puerto_rico.png
new file mode 100644
index 000000000..447576a01
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/puerto_rico.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/r_congo.png b/freeciv-web/src/main/webapp/images/e/flag/r_congo.png
new file mode 100644
index 000000000..aac0105d6
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/r_congo.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/rapa_nui.png b/freeciv-web/src/main/webapp/images/e/flag/rapa_nui.png
new file mode 100644
index 000000000..e35b1713a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/rapa_nui.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/russia.png b/freeciv-web/src/main/webapp/images/e/flag/russia.png
index 55058c7ad..15ddc9719 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/russia.png and b/freeciv-web/src/main/webapp/images/e/flag/russia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/saint_kitts_and_nevis.png b/freeciv-web/src/main/webapp/images/e/flag/saint_kitts_and_nevis.png
new file mode 100644
index 000000000..fae002057
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/saint_kitts_and_nevis.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/saint_lucia.png b/freeciv-web/src/main/webapp/images/e/flag/saint_lucia.png
new file mode 100644
index 000000000..abc5f9085
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/saint_lucia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/san_marino.png b/freeciv-web/src/main/webapp/images/e/flag/san_marino.png
new file mode 100644
index 000000000..cfb502607
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/san_marino.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/sao_tome_and_principe.png b/freeciv-web/src/main/webapp/images/e/flag/sao_tome_and_principe.png
new file mode 100644
index 000000000..cf7e31e97
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/sao_tome_and_principe.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/saudi_arabia.png b/freeciv-web/src/main/webapp/images/e/flag/saudi_arabia.png
new file mode 100644
index 000000000..cd901fbfe
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/saudi_arabia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/scotland.png b/freeciv-web/src/main/webapp/images/e/flag/scotland.png
index 652744775..956b58345 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/scotland.png and b/freeciv-web/src/main/webapp/images/e/flag/scotland.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/seleucid.png b/freeciv-web/src/main/webapp/images/e/flag/seleucid.png
index 206b6764e..6e02364cd 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/seleucid.png and b/freeciv-web/src/main/webapp/images/e/flag/seleucid.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/sierra_leone.png b/freeciv-web/src/main/webapp/images/e/flag/sierra_leone.png
new file mode 100644
index 000000000..778470e30
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/sierra_leone.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/solomon_islands.png b/freeciv-web/src/main/webapp/images/e/flag/solomon_islands.png
new file mode 100644
index 000000000..aefbc0eba
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/solomon_islands.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/south_africa.png b/freeciv-web/src/main/webapp/images/e/flag/south_africa.png
new file mode 100644
index 000000000..a054144bf
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/south_africa.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/south_yemen.png b/freeciv-web/src/main/webapp/images/e/flag/south_yemen.png
new file mode 100644
index 000000000..6ec7fb1f3
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/south_yemen.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/southern_cross.png b/freeciv-web/src/main/webapp/images/e/flag/southern_cross.png
new file mode 100644
index 000000000..169f91d47
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/southern_cross.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/southern_sudan.png b/freeciv-web/src/main/webapp/images/e/flag/southern_sudan.png
new file mode 100644
index 000000000..457e22146
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/southern_sudan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/spartan.png b/freeciv-web/src/main/webapp/images/e/flag/spartan.png
index f0a09a0b6..25c5d9d2a 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/spartan.png and b/freeciv-web/src/main/webapp/images/e/flag/spartan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/sweden.png b/freeciv-web/src/main/webapp/images/e/flag/sweden.png
index 6d37d5f7d..1746eae31 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/sweden.png and b/freeciv-web/src/main/webapp/images/e/flag/sweden.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/switzerland.png b/freeciv-web/src/main/webapp/images/e/flag/switzerland.png
index 681974623..5029914d2 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/switzerland.png and b/freeciv-web/src/main/webapp/images/e/flag/switzerland.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/tannu_tuva.png b/freeciv-web/src/main/webapp/images/e/flag/tannu_tuva.png
new file mode 100644
index 000000000..a45dc88d8
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/tannu_tuva.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/teutonic_order.png b/freeciv-web/src/main/webapp/images/e/flag/teutonic_order.png
new file mode 100644
index 000000000..413959906
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/teutonic_order.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/trinidad_and_tobago.png b/freeciv-web/src/main/webapp/images/e/flag/trinidad_and_tobago.png
new file mode 100644
index 000000000..e992911b2
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/trinidad_and_tobago.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/united_kingdom.png b/freeciv-web/src/main/webapp/images/e/flag/united_kingdom.png
new file mode 100644
index 000000000..b0cdad596
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/united_kingdom.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/united_nations.png b/freeciv-web/src/main/webapp/images/e/flag/united_nations.png
new file mode 100644
index 000000000..c1c6bc6ff
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/united_nations.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/unitedkingdom.png b/freeciv-web/src/main/webapp/images/e/flag/unitedkingdom.png
index 339cc4697..b0cdad596 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/unitedkingdom.png and b/freeciv-web/src/main/webapp/images/e/flag/unitedkingdom.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/usa.png b/freeciv-web/src/main/webapp/images/e/flag/usa.png
index c04eac5d1..2247a3252 100644
Binary files a/freeciv-web/src/main/webapp/images/e/flag/usa.png and b/freeciv-web/src/main/webapp/images/e/flag/usa.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/volga_bulgar.png b/freeciv-web/src/main/webapp/images/e/flag/volga_bulgar.png
new file mode 100644
index 000000000..74d73c293
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/volga_bulgar.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/volga_german.png b/freeciv-web/src/main/webapp/images/e/flag/volga_german.png
new file mode 100644
index 000000000..272cfee37
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/volga_german.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/west_indies_federation.png b/freeciv-web/src/main/webapp/images/e/flag/west_indies_federation.png
new file mode 100644
index 000000000..fab35d29d
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/west_indies_federation.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/flag/west_papua.png b/freeciv-web/src/main/webapp/images/e/flag/west_papua.png
new file mode 100644
index 000000000..c53aed34b
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/flag/west_papua.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/fuel.png b/freeciv-web/src/main/webapp/images/e/fuel.png
index 65259dc09..718b1ccd5 100644
Binary files a/freeciv-web/src/main/webapp/images/e/fuel.png and b/freeciv-web/src/main/webapp/images/e/fuel.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/hourglass.png b/freeciv-web/src/main/webapp/images/e/hourglass.png
index 6de0f8ea0..c5ca3aa45 100644
Binary files a/freeciv-web/src/main/webapp/images/e/hourglass.png and b/freeciv-web/src/main/webapp/images/e/hourglass.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/legion.png b/freeciv-web/src/main/webapp/images/e/legion.png
index 6b1f07e82..1f7fbe8e7 100644
Binary files a/freeciv-web/src/main/webapp/images/e/legion.png and b/freeciv-web/src/main/webapp/images/e/legion.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/magnumturret.png b/freeciv-web/src/main/webapp/images/e/magnumturret.png
new file mode 100644
index 000000000..5e1ff3046
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/magnumturret.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/mechinf..png b/freeciv-web/src/main/webapp/images/e/mechinf..png
new file mode 100644
index 000000000..643c16aaa
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/mechinf..png differ
diff --git a/freeciv-web/src/main/webapp/images/e/medal.png b/freeciv-web/src/main/webapp/images/e/medal.png
new file mode 100644
index 000000000..39f13be70
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/medal.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/migrants.png b/freeciv-web/src/main/webapp/images/e/migrants.png
index da9347c72..650d594d6 100644
Binary files a/freeciv-web/src/main/webapp/images/e/migrants.png and b/freeciv-web/src/main/webapp/images/e/migrants.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/militia.png b/freeciv-web/src/main/webapp/images/e/militia.png
new file mode 100644
index 000000000..c73a5aaa1
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/militia.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/minus.png b/freeciv-web/src/main/webapp/images/e/minus.png
new file mode 100644
index 000000000..4a584129e
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/minus.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/missilesubmarine.png b/freeciv-web/src/main/webapp/images/e/missilesubmarine.png
new file mode 100644
index 000000000..19e6ddc20
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/missilesubmarine.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/multi-fighter.png b/freeciv-web/src/main/webapp/images/e/multi-fighter.png
new file mode 100644
index 000000000..7b4887bcf
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/multi-fighter.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/partisan.png b/freeciv-web/src/main/webapp/images/e/partisan.png
index e21536a78..a2842730c 100644
Binary files a/freeciv-web/src/main/webapp/images/e/partisan.png and b/freeciv-web/src/main/webapp/images/e/partisan.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/patriarch.png b/freeciv-web/src/main/webapp/images/e/patriarch.png
new file mode 100644
index 000000000..9dfcca3f8
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/patriarch.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/plus.png b/freeciv-web/src/main/webapp/images/e/plus.png
new file mode 100644
index 000000000..a9a727636
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/plus.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/propagandaministry.png b/freeciv-web/src/main/webapp/images/e/propagandaministry.png
new file mode 100644
index 000000000..28e6f9b8c
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/propagandaministry.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/quavers.png b/freeciv-web/src/main/webapp/images/e/quavers.png
new file mode 100644
index 000000000..61387bc8a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/quavers.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/reddiamond.png b/freeciv-web/src/main/webapp/images/e/reddiamond.png
new file mode 100644
index 000000000..fa7baf5ba
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/reddiamond.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/riflemen.png b/freeciv-web/src/main/webapp/images/e/riflemen.png
index c6700e05a..1dd0cb1db 100644
Binary files a/freeciv-web/src/main/webapp/images/e/riflemen.png and b/freeciv-web/src/main/webapp/images/e/riflemen.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/running.png b/freeciv-web/src/main/webapp/images/e/running.png
new file mode 100644
index 000000000..6260c542d
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/running.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/sail.png b/freeciv-web/src/main/webapp/images/e/sail.png
new file mode 100644
index 000000000..f24b6a7b8
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/sail.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/scroll.png b/freeciv-web/src/main/webapp/images/e/scroll.png
new file mode 100644
index 000000000..94ac08f74
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/scroll.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/skull.png b/freeciv-web/src/main/webapp/images/e/skull.png
new file mode 100644
index 000000000..8b95886b1
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/skull.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/swords.png b/freeciv-web/src/main/webapp/images/e/swords.png
new file mode 100644
index 000000000..574c9adcc
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/swords.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/chemistry.png b/freeciv-web/src/main/webapp/images/e/techs/chemistry.png
index 02c0732ef..aa4d2d921 100644
Binary files a/freeciv-web/src/main/webapp/images/e/techs/chemistry.png and b/freeciv-web/src/main/webapp/images/e/techs/chemistry.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/literacy.png b/freeciv-web/src/main/webapp/images/e/techs/literacy.png
index 7b29e17e9..36e76444c 100644
Binary files a/freeciv-web/src/main/webapp/images/e/techs/literacy.png and b/freeciv-web/src/main/webapp/images/e/techs/literacy.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/mechanization.png b/freeciv-web/src/main/webapp/images/e/techs/mechanization.png
new file mode 100644
index 000000000..43acceefa
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/techs/mechanization.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/philosophy.png b/freeciv-web/src/main/webapp/images/e/techs/philosophy.png
index 1eb38ff44..ee206c20c 100644
Binary files a/freeciv-web/src/main/webapp/images/e/techs/philosophy.png and b/freeciv-web/src/main/webapp/images/e/techs/philosophy.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/railroad.png b/freeciv-web/src/main/webapp/images/e/techs/railroad.png
index 518e89a00..9bd4adedb 100644
Binary files a/freeciv-web/src/main/webapp/images/e/techs/railroad.png and b/freeciv-web/src/main/webapp/images/e/techs/railroad.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/steamengine.png b/freeciv-web/src/main/webapp/images/e/techs/steamengine.png
index 2a0042f9d..a474a0b4c 100644
Binary files a/freeciv-web/src/main/webapp/images/e/techs/steamengine.png and b/freeciv-web/src/main/webapp/images/e/techs/steamengine.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/university.png b/freeciv-web/src/main/webapp/images/e/techs/university.png
index ec5410c04..551adfed7 100644
Binary files a/freeciv-web/src/main/webapp/images/e/techs/university.png and b/freeciv-web/src/main/webapp/images/e/techs/university.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/techs/unknown.png b/freeciv-web/src/main/webapp/images/e/techs/unknown.png
new file mode 100644
index 000000000..cecf2f26e
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/techs/unknown.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/theolympics.png b/freeciv-web/src/main/webapp/images/e/theolympics.png
index 86b251cb0..c4d18087c 100644
Binary files a/freeciv-web/src/main/webapp/images/e/theolympics.png and b/freeciv-web/src/main/webapp/images/e/theolympics.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/trawler.png b/freeciv-web/src/main/webapp/images/e/trawler.png
new file mode 100644
index 000000000..52cddb41d
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/trawler.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/ts.png b/freeciv-web/src/main/webapp/images/e/ts.png
new file mode 100644
index 000000000..f9bdba6c8
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/ts.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/village.png b/freeciv-web/src/main/webapp/images/e/village.png
new file mode 100644
index 000000000..16d4e32da
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/village.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/warning.png b/freeciv-web/src/main/webapp/images/e/warning.png
index c69fabc55..7ae2673de 100644
Binary files a/freeciv-web/src/main/webapp/images/e/warning.png and b/freeciv-web/src/main/webapp/images/e/warning.png differ
diff --git a/freeciv-web/src/main/webapp/images/e/workersii.png b/freeciv-web/src/main/webapp/images/e/workersii.png
new file mode 100644
index 000000000..fef336a70
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/e/workersii.png differ
diff --git a/freeciv-web/src/main/webapp/images/filter.png b/freeciv-web/src/main/webapp/images/filter.png
new file mode 100644
index 000000000..7c637a9cb
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/filter.png differ
diff --git a/freeciv-web/src/main/webapp/images/filter1.png b/freeciv-web/src/main/webapp/images/filter1.png
new file mode 100644
index 000000000..fdfee1b78
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/filter1.png differ
diff --git a/freeciv-web/src/main/webapp/images/luxury2.png b/freeciv-web/src/main/webapp/images/luxury2.png
index fd1691f15..c586145f6 100644
Binary files a/freeciv-web/src/main/webapp/images/luxury2.png and b/freeciv-web/src/main/webapp/images/luxury2.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/logistics_artillery_unload.png b/freeciv-web/src/main/webapp/images/man/logistics_artillery_unload.png
index 2d962917b..ea7995ac5 100644
Binary files a/freeciv-web/src/main/webapp/images/man/logistics_artillery_unload.png and b/freeciv-web/src/main/webapp/images/man/logistics_artillery_unload.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/logistics_foot_load.png b/freeciv-web/src/main/webapp/images/man/logistics_foot_load.png
index 4d782b8ad..079958dbe 100644
Binary files a/freeciv-web/src/main/webapp/images/man/logistics_foot_load.png and b/freeciv-web/src/main/webapp/images/man/logistics_foot_load.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/logistics_foot_unload.png b/freeciv-web/src/main/webapp/images/man/logistics_foot_unload.png
index e2b7304b3..a15ee6124 100644
Binary files a/freeciv-web/src/main/webapp/images/man/logistics_foot_unload.png and b/freeciv-web/src/main/webapp/images/man/logistics_foot_unload.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/logistics_generic_unload.png b/freeciv-web/src/main/webapp/images/man/logistics_generic_unload.png
index dfd73bb07..c576098a4 100644
Binary files a/freeciv-web/src/main/webapp/images/man/logistics_generic_unload.png and b/freeciv-web/src/main/webapp/images/man/logistics_generic_unload.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/logistics_landairsea_unload.png b/freeciv-web/src/main/webapp/images/man/logistics_landairsea_unload.png
index e08e58095..34e0163c7 100644
Binary files a/freeciv-web/src/main/webapp/images/man/logistics_landairsea_unload.png and b/freeciv-web/src/main/webapp/images/man/logistics_landairsea_unload.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/logistics_mounted_unload.png b/freeciv-web/src/main/webapp/images/man/logistics_mounted_unload.png
index 50b0eb133..116704e39 100644
Binary files a/freeciv-web/src/main/webapp/images/man/logistics_mounted_unload.png and b/freeciv-web/src/main/webapp/images/man/logistics_mounted_unload.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/vigil_mp2c.png b/freeciv-web/src/main/webapp/images/man/vigil_mp2c.png
new file mode 100644
index 000000000..11c352a1b
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/man/vigil_mp2c.png differ
diff --git a/freeciv-web/src/main/webapp/images/man/vigil_mp2d.png b/freeciv-web/src/main/webapp/images/man/vigil_mp2d.png
new file mode 100644
index 000000000..2c00b2d4a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/man/vigil_mp2d.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/blank_button3.png b/freeciv-web/src/main/webapp/images/orders/blank_button3.png
new file mode 100644
index 000000000..fda43856e
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/blank_button3.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/bunker.png b/freeciv-web/src/main/webapp/images/orders/bunker.png
new file mode 100644
index 000000000..f6b5ba248
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/bunker.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/capture.png b/freeciv-web/src/main/webapp/images/orders/capture.png
new file mode 100644
index 000000000..fc5694afc
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/capture.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/castle.png b/freeciv-web/src/main/webapp/images/orders/castle.png
new file mode 100644
index 000000000..caf3fff9e
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/castle.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/deepdive.png b/freeciv-web/src/main/webapp/images/orders/deepdive.png
new file mode 100644
index 000000000..8c50d0bb6
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/deepdive.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/expel.png b/freeciv-web/src/main/webapp/images/orders/expel.png
new file mode 100644
index 000000000..c580b847b
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/expel.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/fishtrap.png b/freeciv-web/src/main/webapp/images/orders/fishtrap.png
new file mode 100644
index 000000000..68bb36ed1
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/fishtrap.png differ
diff --git a/freeciv-web/src/main/webapp/images/orders/fort.png b/freeciv-web/src/main/webapp/images/orders/fort.png
new file mode 100644
index 000000000..16929814a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/orders/fort.png differ
diff --git a/freeciv-web/src/main/webapp/images/riverland.png b/freeciv-web/src/main/webapp/images/riverland.png
new file mode 100644
index 000000000..601b0bf1f
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/riverland.png differ
diff --git a/freeciv-web/src/main/webapp/images/terrain/desert1.png b/freeciv-web/src/main/webapp/images/terrain/desert1.png
index 02dfe25be..d732c2d74 100644
Binary files a/freeciv-web/src/main/webapp/images/terrain/desert1.png and b/freeciv-web/src/main/webapp/images/terrain/desert1.png differ
diff --git a/freeciv-web/src/main/webapp/images/terrain/forest3.png b/freeciv-web/src/main/webapp/images/terrain/forest3.png
index 2d72e171b..5ece6836b 100644
Binary files a/freeciv-web/src/main/webapp/images/terrain/forest3.png and b/freeciv-web/src/main/webapp/images/terrain/forest3.png differ
diff --git a/freeciv-web/src/main/webapp/images/terrain/grassland1.png b/freeciv-web/src/main/webapp/images/terrain/grassland1.png
index dbdaf9e4a..63a066c9c 100644
Binary files a/freeciv-web/src/main/webapp/images/terrain/grassland1.png and b/freeciv-web/src/main/webapp/images/terrain/grassland1.png differ
diff --git a/freeciv-web/src/main/webapp/images/terrain/hills2.png b/freeciv-web/src/main/webapp/images/terrain/hills2.png
index 13f15a911..382c36613 100644
Binary files a/freeciv-web/src/main/webapp/images/terrain/hills2.png and b/freeciv-web/src/main/webapp/images/terrain/hills2.png differ
diff --git a/freeciv-web/src/main/webapp/images/terrain/plains2.png b/freeciv-web/src/main/webapp/images/terrain/plains2.png
index 903d2c303..8eb8d5361 100644
Binary files a/freeciv-web/src/main/webapp/images/terrain/plains2.png and b/freeciv-web/src/main/webapp/images/terrain/plains2.png differ
diff --git a/freeciv-web/src/main/webapp/images/terrain/tundra2.png b/freeciv-web/src/main/webapp/images/terrain/tundra2.png
index 958d96cd0..374101260 100644
Binary files a/freeciv-web/src/main/webapp/images/terrain/tundra2.png and b/freeciv-web/src/main/webapp/images/terrain/tundra2.png differ
diff --git a/freeciv-web/src/main/webapp/images/unnamedbk-dk.jpg b/freeciv-web/src/main/webapp/images/unnamedbk-dk.jpg
new file mode 100644
index 000000000..60cb2d26d
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/unnamedbk-dk.jpg differ
diff --git a/freeciv-web/src/main/webapp/images/woodgrey-bg.jpg b/freeciv-web/src/main/webapp/images/woodgrey-bg.jpg
new file mode 100644
index 000000000..92689104a
Binary files /dev/null and b/freeciv-web/src/main/webapp/images/woodgrey-bg.jpg differ
diff --git a/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview.js b/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview.js
index ab661ae4a..8e3ef08f0 100644
--- a/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview.js
+++ b/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview.js
@@ -43,7 +43,7 @@ var loaded_images = 0;
var sprites_init = false;
-var canvas_text_font = "16px Helvetica, sans serif"; // with canvas text support
+var canvas_text_font = "16px Candara, sans serif"; // with canvas text support
var fullfog = [];
@@ -211,7 +211,11 @@ function init_sprites()
}
} else {
// already loaded
- $.unblockUI();
+ if (renderer == RENDERER_WEBGL) {
+ webgl_preload();
+ } else {
+ $.unblockUI();
+ }
}
}
@@ -225,7 +229,11 @@ function preload_check()
if (loaded_images == tileset_image_count) {
init_cache_sprites();
- $.unblockUI();
+ if (renderer == RENDERER_WEBGL) {
+ webgl_preload();
+ } else {
+ $.unblockUI();
+ }
}
}
@@ -283,7 +291,7 @@ function mapview_window_resized ()
if (active_city != null || !resize_enabled) return;
setup_window_size();
- update_map_canvas_full();
+ if (renderer == RENDERER_2DCANVAS) update_map_canvas_full();
}
/**************************************************************************
@@ -479,7 +487,7 @@ function mapview_put_city_bar(pcanvas, city, canvas_x, canvas_y) {
mood_text = celeb;
break;
}
- if (happy_people >= city['size']*0.4999 && unhappy_angry_people==0 && city['size']>2) {
+ if (happy_people >= city['size']*0.4999 && unhappy_angry_people==0 && city['size']>=city_celebrate_size(city)) {
// case handling: city is going to celebrate next turn.
if (mood_text == peace) start_celeb = true;
}
@@ -545,36 +553,38 @@ function mapview_put_city_bar(pcanvas, city, canvas_x, canvas_y) {
var txt_measure = pcanvas.measureText(text);
var size_measure = pcanvas.measureText(size);
- pcanvas.globalAlpha = 0.7;
- pcanvas.fillStyle = "rgba(0, 0, 0, 0.55)";
+ pcanvas.globalAlpha = 0.72;
+ pcanvas.fillStyle = "rgba(0, 0, 0, 0.40)";
pcanvas.fillRect (canvas_x - Math.floor(txt_measure.width / 2) - 14, canvas_y - 17,
txt_measure.width + 20, 20);
+
pcanvas.fillStyle = color;
- pcanvas.fillRect(canvas_x + Math.floor(txt_measure.width / 2) + 5, canvas_y - 19,
- (prod_type != null) ? size_measure.width + 35 : size_measure.width + 8, 24);
+ pcanvas.fillRect(canvas_x + Math.floor(txt_measure.width / 2) + 5, canvas_y - 17,
+ (prod_type != null) ? size_measure.width + 35 : size_measure.width + 8, 20);
var city_flag = get_city_flag_sprite(city);
+ pcanvas.globalAlpha = 0.77;
pcanvas.drawImage(sprites[city_flag['key']],
- canvas_x - Math.floor(txt_measure.width / 2) - 45, canvas_y - 17);
+ canvas_x - Math.floor(txt_measure.width / 2) - 44, canvas_y - 17);
pcanvas.drawImage(sprites[get_city_occupied_sprite(city)],
- canvas_x - Math.floor(txt_measure.width / 2) - 14, canvas_y - 16);
-
+ canvas_x - Math.floor(txt_measure.width / 2) - 12, canvas_y - 16);
+/*
pcanvas.strokeStyle = color;
- pcanvas.lineWidth = 1.5;
+ pcanvas.lineWidth = 1;
pcanvas.beginPath();
- pcanvas.moveTo(canvas_x - Math.floor(txt_measure.width / 2) - 46, canvas_y - 18);
- pcanvas.lineTo(canvas_x + Math.floor(txt_measure.width / 2) + size_measure.width + 13,
+ pcanvas.moveTo(canvas_x - Math.floor(txt_measure.width / 2) - 47, canvas_y - 18);
+ pcanvas.lineTo(canvas_x + Math.floor(txt_measure.width / 2) + size_measure.width + 14,
canvas_y - 18);
- pcanvas.moveTo(canvas_x + Math.floor(txt_measure.width / 2) + size_measure.width + 13,
+ pcanvas.moveTo(canvas_x + Math.floor(txt_measure.width / 2) + size_measure.width + 14,
canvas_y + 4);
- pcanvas.lineTo(canvas_x - Math.floor(txt_measure.width / 2) - 46, canvas_y + 4);
- pcanvas.lineTo(canvas_x - Math.floor(txt_measure.width / 2) - 46, canvas_y - 18);
+ pcanvas.lineTo(canvas_x - Math.floor(txt_measure.width / 2) - 47, canvas_y + 4);
+ pcanvas.lineTo(canvas_x - Math.floor(txt_measure.width / 2) - 47, canvas_y - 18);
pcanvas.moveTo(canvas_x - Math.floor(txt_measure.width / 2) - 15, canvas_y - 17);
pcanvas.lineTo(canvas_x - Math.floor(txt_measure.width / 2) - 15, canvas_y + 3);
pcanvas.stroke();
-
+*/
pcanvas.globalAlpha = 1.0;
if (prod_type != null) {
@@ -583,7 +593,7 @@ function mapview_put_city_bar(pcanvas, city, canvas_x, canvas_y) {
if (tag == null) return;
pcanvas.drawImage(sprites[tag],
canvas_x + Math.floor(txt_measure.width / 2) + size_measure.width + 13,
- canvas_y - 19, 28, 24);
+ canvas_y - 17, 27, 20);
}
var shadow_offset_fix = (lose_celeb || city_map_display_mode) ? 1 : 0; //shadow offsets of 2 only look good under white
@@ -621,7 +631,8 @@ function mapview_put_tile_label(pcanvas, tile, canvas_x, canvas_y) {
/**************************************************************************
Renders the national border lines onto the canvas.
-**************************************************************************/
+ DEPRECATED BECAUSE CPU LOAD WAS TOO HEAVY
+***************************************************************************
function mapview_put_grid_line(pcanvas, dir, color, canvas_x, canvas_y) {
var x = canvas_x + 47;
var y = canvas_y + 3;
@@ -648,6 +659,7 @@ function mapview_put_grid_line(pcanvas, dir, color, canvas_x, canvas_y) {
pcanvas.closePath();
pcanvas.stroke();
}
+*/
/**************************************************************************
Renders the national border lines onto the canvas.
@@ -910,9 +922,10 @@ function set_default_mapview_active()
if (show_compass) $("#compass").show();
else $("#compass").hide();
- mapview_canvas_ctx = mapview_canvas.getContext("2d");
- mapview_canvas_ctx.font = canvas_text_font;
-
+ if (renderer == RENDERER_2DCANVAS) {
+ mapview_canvas_ctx = mapview_canvas.getContext("2d");
+ mapview_canvas_ctx.font = canvas_text_font;
+ }
var active_tab = $('#tabs').tabs('option', 'active');
if (active_tab == TAB_CITIES) { // cities dialog is active
diff --git a/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.bm b/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.bm
index d706f0e04..94f46b398 100644
--- a/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.bm
+++ b/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.bm
@@ -597,11 +597,11 @@ function update_map_canvas_check()
{
if (mapview_active==false || freeze) return; // (freeze currently used for tax sliders)
var time = new Date().getTime() - last_redraw_time;
- if ((time > MAPVIEW_REFRESH_INTERVAL)) {
+ if ((time > MAPVIEW_REFRESH_INTERVAL) && renderer == RENDERER_2DCANVAS) {
update_map_canvas_full();
}
try {
- if ( window.requestAnimationFrame != null) requestAnimationFrame(update_map_canvas_check);
+ if ( renderer == RENDERER_2DCANVAS && window.requestAnimationFrame != null) requestAnimationFrame(update_map_canvas_check);
} catch (e) {
if (e.name == 'NS_ERROR_NOT_AVAILABLE') {
setTimeout(update_map_canvas_check, 100);
diff --git a/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.js b/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.js
index bf6906eb2..311820458 100644
--- a/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.js
+++ b/freeciv-web/src/main/webapp/javascript/2dcanvas/mapview_common.js
@@ -433,8 +433,9 @@ function update_map_canvas(canvas_x, canvas_y, width, height)
for (city_id in cities) {
if (cities[city_id]['traderoute_count']>0) {
for (tr = 0; tr < cities[city_id]['traderoute_count']; tr++) {
- if (city_trade_routes[city_id][tr]) {
+ if (city_trade_routes && city_trade_routes[city_id] && city_trade_routes[city_id][tr]) {
var correct_x, src=city_id, dest=city_trade_routes[city_id][tr]['partner'];
+ if (!cities[src] || !cities[dest]) continue;
src_x = tiles[cities[src]['tile']]['x'];
src_y = tiles[cities[src]['tile']]['y'];
dest_x = tiles[cities[dest]['tile']]['x'];
@@ -463,7 +464,13 @@ function update_map_canvas(canvas_x, canvas_y, width, height)
// console.log(to);
// }
mapview_canvas_ctx.lineWidth = 3;
- mapview_canvas_ctx.strokeStyle = 'rgb(255,0,255)';
+ if (!observing) {
+ if (cities[src].owner !=null && cities[src].owner != client.conn.playing.playerno
+ && cities[dest].owner != null && cities[dest].owner != client.conn.playing.playerno) {
+ mapview_canvas_ctx.strokeStyle = 'rgb(255,128,0)'; // Orange: others' traderoutes
+ } else mapview_canvas_ctx.strokeStyle = 'rgb(255,255,0)'; // Yellow: my traderoutes
+ } else mapview_canvas_ctx.strokeStyle = 'rgb(255,128,0)'; // Orange: observer (i.e. others')
+
mapview_canvas_ctx.beginPath();
mapview_canvas_ctx.moveTo(from['gui_dx']-mapview.gui_x0, from['gui_dy']-mapview.gui_y0);
mapview_canvas_ctx.lineTo(to['gui_dx']-mapview.gui_x0, to['gui_dy']-mapview.gui_y0);
@@ -559,8 +566,6 @@ function put_drawn_sprites(pcanvas, canvas_x, canvas_y, pdrawn, fog)
mapview_put_city_bar(pcanvas, pdrawn[i]['city'], canvas_x + offset_x, canvas_y + offset_y);
} else if (pdrawn[i]['key'] == "border" ) {
mapview_put_border_line(pcanvas, pdrawn[i]['dir'], pdrawn[i]['color'], pdrawn[i]['color2'], pdrawn[i]['color3'], canvas_x, canvas_y);
- } else if (pdrawn[i]['key'] == "mapgrid" ) {
- mapview_put_grid_line(pcanvas, pdrawn[i]['dir'], pdrawn[i]['color'], canvas_x, canvas_y);
} /* else if (pdrawn[i]['key'] == "territory" ) {
mapview_territory_fill(pcanvas, pdrawn[i]['color'], canvas_x, canvas_y);
} */ else if (pdrawn[i]['key'] == "goto_line" ) {
@@ -663,11 +668,11 @@ function update_map_canvas_check()
{
if (/*mapview_active==false ||*/ freeze) return; // (freeze currently used for tax sliders)
var time = new Date().getTime() - last_redraw_time;
- if (time > MAPVIEW_REFRESH_INTERVAL) {
+ if (time > MAPVIEW_REFRESH_INTERVAL && renderer == RENDERER_2DCANVAS) {
update_map_canvas_full();
}
try {
- if (window.requestAnimationFrame != null) requestAnimationFrame(update_map_canvas_check);
+ if (renderer == RENDERER_2DCANVAS && window.requestAnimationFrame != null) requestAnimationFrame(update_map_canvas_check);
} catch (e) {
if (e.name == 'NS_ERROR_NOT_AVAILABLE') {
setTimeout(update_map_canvas_check, 100);
diff --git a/freeciv-web/src/main/webapp/javascript/2dcanvas/tilespec.js b/freeciv-web/src/main/webapp/javascript/2dcanvas/tilespec.js
index b7e328243..01bcc359c 100644
--- a/freeciv-web/src/main/webapp/javascript/2dcanvas/tilespec.js
+++ b/freeciv-web/src/main/webapp/javascript/2dcanvas/tilespec.js
@@ -47,7 +47,8 @@ var border_flag_offsets = {
const CITY_WALLS = 1,
CITY_COASTAL_DEFENSE = 2,
CITY_FORTIFICATIONS = 4,
- CITY_CITADEL = 8;
+ CITY_CITADEL = 8,
+ CITY_SAM = 16;
var current_select_sprite = 0;
var max_select_sprite = 4;
@@ -284,7 +285,7 @@ function fill_sprite_array(layer, ptile, pedge, pcorner, punit, pcity, citymode)
var pterrain = tile_terrain(ptile);
sprite_array = sprite_array.concat(fill_terrain_sprite_layer(2, ptile, pterrain, tterrain_near));
-
+ if (draw_map_grid) sprite_array = sprite_array.concat({"key":"grid.map"});
sprite_array = sprite_array.concat(fill_irrigation_sprite_array(ptile, pcity));
}
break;
@@ -300,8 +301,7 @@ function fill_sprite_array(layer, ptile, pedge, pcorner, punit, pcity, citymode)
if (ptile != null) {
// TEST: borders moved from last sub-layer of LAYER_SPECIAL1 to here:
// it was drawing on top of resources, and seemed better here:
- if (draw_map_grid) sprite_array = sprite_array.concat(get_grid_line_sprites(ptile));
- sprite_array = sprite_array.concat(get_border_line_sprites(ptile));
+ sprite_array = sprite_array.concat(get_border_line_sprites(ptile));
var spec_sprite = get_tile_specials_sprite(ptile);
if (spec_sprite != null) sprite_array.push(spec_sprite);
@@ -359,7 +359,9 @@ function fill_sprite_array(layer, ptile, pedge, pcorner, punit, pcity, citymode)
if (layer_sprite) sprite_array.push(layer_sprite);
layer_sprite = get_city_coastal_overlay_sprite(pcity);
if (layer_sprite) sprite_array.push(layer_sprite);
-
+ layer_sprite = get_city_sam_battery_overlay_sprite(pcity);
+ if (layer_sprite) sprite_array.push(layer_sprite);
+
if (polluted) sprite_array.push({"key" : "grid.pollute_icon"}); //pollution icon clearly over top
// starving: show empty plate
@@ -1153,31 +1155,6 @@ function get_border_line_sprites(ptile)
return result;
}
-/**********************************************************************
-...ADD_SPRITE_SIMPLE(t->sprites.grid.main[pedge->type] used in gtk client
-***********************************************************************/
-function get_grid_line_sprites(ptile)
-{
- var result = [];
-
- // first test run, just draw the player's border on every tile
-
- for (var i = 0; i < num_cardinal_tileset_dirs/2; i++) {
- var dir = cardinal_tileset_dirs[i];
- var checktile = mapstep(ptile, dir);
-
- if (checktile != null) {
- if (terrains[ptile['terrain']]['name'] == "Deep Ocean")
- result.push({"key" : "mapgrid", "dir" : dir, "color": "rgba(66,57,47,1.0)" }); //stronger contrast on deep ocean
- else
- result.push({"key" : "mapgrid", "dir" : dir, "color": "rgba(0,0,0,0.35)" });
- }
- }
-
- return result;
-}
-
-
/**********************************************************************
...returns the shield (not the flag)
***********************************************************************/
@@ -1636,7 +1613,13 @@ function get_unit_activity_sprite(punit)
"offset_y" : - unit_activity_offset_y};
}
}
-
+ if (client_rules_flag[CRF_MP2_D]) {
+ if (tile_has_extra(tiles[punit['tile']], EXTRA_DEEPDIVE) && get_unit_class_name(punit) == "Submarine") {
+ return {"key" : "unit.hidden",
+ "offset_x" : unit_activity_offset_x,
+ "offset_y" : - unit_activity_offset_y};
+ }
+ }
return null;
}
function get_city_coastal_overlay_sprite(pcity) {
@@ -1669,6 +1652,12 @@ function get_city_fortifications_underlay_sprite(pcity) {
}
return null; // no underlay.
}
+function get_city_sam_battery_overlay_sprite(pcity) {
+ if ((pcity['walls'] & CITY_SAM)) {
+ return {"key": "city.sam_overlay", "offset_x" : -16, "offset_y" : -24};
+ }
+ return null; // no underlay.
+}
/*
/****************************************************************************
@@ -2376,6 +2365,9 @@ function fill_layer2_sprite_array(ptile, pcity)
result_sprites.push(get_base_flag_sprite(ptile));
result_sprites.push({"key" : "base.buoy_mg",
"offset_y" : -normal_tile_height / 2});
+ } else if (typeof EXTRA_FISHTRAP !== 'undefined' && tile_has_extra(ptile, EXTRA_FISHTRAP)) {
+ result_sprites.push({"key" : "ts.fishtrap",
+ "offset_y" : 0});
}
if (typeof EXTRA_TILE_CLAIM !== 'undefined' && tile_has_extra(ptile, EXTRA_TILE_CLAIM)) {
result_sprites.push({"key" : "base.tileclaim_mg",
@@ -2665,7 +2657,7 @@ function create_unit_offset_arrays()
vx += 9; vy += 4;
break;
case "Legion":
- vx += 1; vy -= 7;
+ vx += 1; vy -= 8;
break;
case "Marines":
dx += 2; dy += 2;
@@ -2684,6 +2676,14 @@ function create_unit_offset_arrays()
dx -= 3; dy -= 3;
vx -= 12; vy += 11;
break;
+ case "Missile Submarine":
+ dx -= 2; dy -= 6;
+ vx -= 12; vy += 5;
+ break;
+ case "Magnum Turret":
+ dx -= 16; dy -= 2;
+ vx += 8; vy -= 14;
+ break;
case "Musketeers":
dx += 1; dy -= 4;
vx += 1; vy -= 19;
@@ -2750,6 +2750,10 @@ function create_unit_offset_arrays()
case "Transport Helicopter":
vx -= 24; vy += 15;
break;
+ case "Trawler":
+ dx -= 13; dy += 6;
+ mx -= 6; my += 7;
+ break;
case "Trireme":
vx += 2; vy += 1;
break;
diff --git a/freeciv-web/src/main/webapp/javascript/action_dialog.js b/freeciv-web/src/main/webapp/javascript/action_dialog.js
index 2d7497250..e6c42181c 100644
--- a/freeciv-web/src/main/webapp/javascript/action_dialog.js
+++ b/freeciv-web/src/main/webapp/javascript/action_dialog.js
@@ -28,11 +28,16 @@ var did_not_decide = false;
var action_images = {
-// 2: "e/gold", // investigate city
+ 2: "e/eyes", // investigate city
+ 4: "e/snake", // poison city
+ 5: "e/snake", // poison city escape
20: "e/gold", // trade route
21: "e/marketplace", // enter marketplace
- 22: "e/camel24", // build wonder
+ 22: "e/camel24", // build wonder
23: "corrupt", // bribe unit
+ 24: "e/bomb", // sabotage enemy units
+ 25: "e/bomb", // sabotage enemy units and escape
+ 26: "orders/capture", // capture unit
27: "orders/build_city_default",
28: "city_user_row", // join city
29: "e/earth", // steal maps
@@ -43,7 +48,8 @@ var action_images = {
36: "e/nuclearexplosion",
37: "e/nuclearexplosion",
38: "e/nuclearexplosion",
- 39: "orders/pillage", // destroy city
+ 39: "orders/pillage", // destroy city
+ 40: "orders/expel", // expel units
41: "orders/disband_recycle",
42: "orders/disband_default",
43: "orders/rehome_default", // home city
@@ -51,7 +57,7 @@ var action_images = {
45: "orders/paradrop",
46: "orders/airlift",
47: "e/redexclamation", // Attack
- 48: "e/cruisemissile",
+ 48: "e/boom",
51: "cities_tab_icon", // conquer city
54: "orders/transform_default",
55: "orders/forest_remove_default",
@@ -78,10 +84,15 @@ var action_images = {
"targetextra": "e/targetextra",
"autoattack": "e/redexclamations",
"Airbase": "orders/airbase",
+ "Canal, coastal": "orders/canal",
+ "Canal, inland": "orders/canal",
"Canal": "orders/canal",
"Waterway": "orders/canal",
"Buoy": "orders/buoy",
+ "Hideout": "orders/hideout",
+ "Depth": "orders/deepdive",
"": "orders/hideout",
+ "": "orders/deepdive", // contains a zero width space to be different from hideout
"Maglev": "orders/maglev",
"Naval Base": "orders/navalbase",
"Oil Well": "orders/oil_well",
@@ -92,15 +103,18 @@ var action_images = {
"Sea Bridge": "orders/seabridge",
"River": "orders/well",
"Tile Claim": "nation_tab_icon",
- "Fort": "orders/fortress",
+ "Fort": "orders/fort",
"Fortress": "orders/fortress",
- "Castle": "orders/fortress",
- "Bunker": "orders/fortress",
+ "Fishtrap": "orders/fishtrap",
+ "Castle": "orders/castle",
+ "Bunker": "orders/bunker",
"Fallout": "orders/fallout",
"Irrigation": "orders/irrigate_default",
"Farmland": "orders/irrigation",
"Mine": "orders/mine_default",
"Pollution": "orders/pollution",
+ "Vigil": "orders/vigil",
+ "Sentry": "orders/sentry",
"Walls": "e/greatwall"
};
@@ -351,6 +365,14 @@ function act_sel_click_function(parent_id,
};
send_request(JSON.stringify(packet));
is_more_user_input_needed = true;
+ /* Siege Rams do targeted sabotage but do NOT select their target:
+ Only City Walls are the only allowed target. We must set
+ is_more_user_input_needed = false to avoid the UI getting stuck */
+ if (action_id == ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC
+ && client_rules_flag[CRF_MP2_C]
+ && unit_type(units[actor_unit_id]).name == "Siege Ram") {
+ is_more_user_input_needed = false;
+ }
remove_action_selection_dialog(parent_id, actor_unit_id, true);
};
case ACTION_FOUND_CITY:
@@ -404,9 +426,9 @@ function act_sel_click_function(parent_id,
/**************************************************************************
Puts an icon into a button image.
**************************************************************************/
-function render_action_image_into_button(text, action_id, sub_tgt)
+function render_action_image_into_button(text, action_id, sub_tgt, order, activity)
{
- //console.log("Render action %d:%s sub_tgt=%d",action_id,text,sub_tgt)
+ //console.log("Render action %d:%s sub_tgt=%s",action_id,text,sub_tgt)
if (action_images[action_id]) {
var key = action_id;
//console.log(" Key starts at %s",key);
@@ -417,13 +439,36 @@ function render_action_image_into_button(text, action_id, sub_tgt)
//console.log(" sub_tgt not undefined etc.");
if (action_id == ACTION_ROAD || action_id == ACTION_BASE) {
//console.log(" action_id is ROAD or BASE");
- if (action_images[extras[sub_tgt]['name']]) {
- key = extras[sub_tgt]['name'];
+ if (action_images[extras[sub_tgt]['rule_name']]) {
+ key = extras[sub_tgt]['rule_name'];
//console.log(" Reassigned key:%s",key);
+ if (key == "Depth") text = "Dive Deep";
+ else if (key=="Hideout") text = "Build Hideout";
}
}
};
+ /* ACTION_COUNT could mean 1. NO ACTION, or, 2. a new ACTIVITY instead
+ of an ACTION performance: */
+ if (key == ACTION_COUNT) {
+ if (order && order == ORDER_ACTIVITY && activity) {
+ switch (activity) {
+ case ACTIVITY_SENTRY:
+ key = "Sentry";
+ break;
+ case ACTIVITY_FALLOUT:
+ key = "Fallout";
+ break;
+ case ACTIVITY_POLLUTION:
+ key = "Pollution";
+ break;
+ case ACTIVITY_VIGIL:
+ key = "Vigil";
+ break;
+ }
+ }
+ }
+
text = " "
+ text;
@@ -449,8 +494,10 @@ function create_act_sel_button(parent_id,
// Fix inaccurate "Conquer City" to "Raze City" for size 1 city:
if (button_text.includes("Conquer")) {
var pcity = cities[tgt_id];
- if (pcity && pcity['size']==1)
+ if (pcity && pcity['size']==1
+ && unit_has_class_flag(units[actor_unit_id], UCF_KILLCITIZEN)) {
button_text = button_text.replace("Conquer", "Raze");
+ }
}
if (action_id == ACTION_BASE && button_text.includes("Base")) {
@@ -461,10 +508,14 @@ function create_act_sel_button(parent_id,
//console.log("\nmaking target=%d subtarget=%d",tgt_id, sub_tgt_id);
button_text = button_text.replace("Road", extras[sub_tgt_id]['name'])
sub_target_override = sub_tgt_id;
- }
+ } else if (action_id == ACTION_SPY_BRIBE_UNIT) {
+ if (unit_type(units[actor_unit_id])['name'] == "Patriarch")
+ button_text = button_text.replace("Bribe Enemy", "Convert");
+ }
if (action_images[action_id]) {
button_text = render_action_image_into_button(button_text, action_id, sub_target_override);
+ button_text = button_text.replace("Build Dive Deep","Dive Deep");
}
/* Create the initial button with this action */
var button = {
@@ -614,19 +665,26 @@ function popup_action_selection(actor_unit, action_probabilities,
}
}
if (action_id == ACTION_TRANSPORT_DEBOARD) {
- if (!unit_can_do_unload(actor_unit))
+ if (!unit_can_deboard(actor_unit))
continue;
}
if (action_id == ACTION_TRANSPORT_UNLOAD) {
- if (!unit_can_do_unload(target_unit))
+ if (!unit_can_deboard(target_unit))
continue;
}
// if (action_id == ACTION_TRANSPORT_LOAD) {} action not backported yet
//------------------------------------------------------------------------------------
- buttons.push(create_act_sel_button(id, actor_unit['id'],
- tgt_id, sub_tgt_id, action_id,
- action_probabilities));
- if (action_id==ACTION_ATTACK) focus_button = "act_sel_"+ACTION_ATTACK+"_"+actor_unit['id'];
+
+ if (action_id==ACTION_ATTACK || action_id==ACTION_CONQUER_CITY) {
+ // Attack and Conquer should always be first on menu to avoid
+ // regrettable misclick issues...
+ focus_button = "act_sel_"+action_id+"_"+actor_unit['id'];
+ buttons.unshift(create_act_sel_button(id, actor_unit['id'],
+ tgt_id, sub_tgt_id, action_id, action_probabilities));
+ } else {
+ buttons.push(create_act_sel_button(id, actor_unit['id'],
+ tgt_id, sub_tgt_id, action_id, action_probabilities));
+ }
}
}
}
@@ -774,7 +832,7 @@ function popup_action_selection(actor_unit, action_probabilities,
+ "units (up to 40%/turn): emulating damage reduction to the\n"
+ "Fortress defense bonus. HP healing of units (up to 40%/turn),\n"
+ "emulates resistance and repairing Fortress damage over the\n"
- + "course of a long siege.\n"
+ + "course of a long siege."
}
else if (buttons[button_id].html.includes("Targeted Sabotage")) {
buttons[button_id].html = "Attack City Walls ([25%, 50%])"
@@ -786,6 +844,20 @@ function popup_action_selection(actor_unit, action_probabilities,
+ "of the City Walls or the loss of the Siege Ram."
}
} break;
+ case "Ballista": for (button_id in buttons) {
+ if (buttons[button_id].html.includes("Special Attack")) {
+ buttons[button_id].html = buttons[button_id].html.replace("Special Attack", utype_get_bombard_name(ptype))
+ buttons[button_id].title = "Odds of survival: 100%\n"
+ + "Combat: 5 rounds\n"
+ + "Targets: 2 units\n"
+ + "Move cost: 2 moves\n"
+ + "Min. moves: hasn't moved\n"
+ + "Max. casualties: 2\n"
+ + "\nPoor accuracy but deadly firepower is represented by\n"
+ + "5 rounds at 5x firepower vs up to 2 units. Targets\n"
+ + "will either be missed, badly damaged, or killed."
+ }
+ } break;
case "Phalanx": for (button_id in buttons) {
if (buttons[button_id].html.includes("Special Attack")) {
buttons[button_id].html = buttons[button_id].html.replace("Special Attack", utype_get_bombard_name(ptype))
@@ -797,7 +869,7 @@ function popup_action_selection(actor_unit, action_probabilities,
+ "Casualties: --\n"
+ "\nA 3 round rumble against one unit on the target tile\n"
+ "represents the Phalanx safely pushing from a held\n"
- + "position vs. a weak defender who comes too close.\n"
+ + "position vs. a weak defender who comes too close."
}
} break;
case "Archers": for (button_id in buttons) {
@@ -864,7 +936,59 @@ function popup_action_selection(actor_unit, action_probabilities,
+ "Move cost: 1 5/9 moves\n"
+ "Max. casualties: 1\n"
+ "\nHardened Marines use agility/mobility over terrain features for hit-and-run\n"
- + "ballistic attacks: 3 rounds of combat on up to 4 occupants of a tile.\n"
+ + "ballistic attacks: 3 rounds of combat on up to 4 occupants of a tile."
+ }
+ } break;
+ case "Catapult": for (button_id in buttons) {
+ if (buttons[button_id].html.includes("Special Attack")) {
+ buttons[button_id].html = buttons[button_id].html.replace("Special Attack", utype_get_bombard_name(ptype))
+ buttons[button_id].title = "Odds of survival: 100%\n"
+ + "Combat: 4 rounds\n"
+ + "Targets: 1 unit\n"
+ + "Move cost: 2 moves\n"
+ + "Min. moves: hasn't moved\n"
+ + "Max. casualties: 1\n"
+ + "\nHurls rocks on 1 unit on the target tile for\n"
+ + "4 rounds. Can't be done to Fortress or City."
+ }
+ } break;
+ case "Cannon": for (button_id in buttons) {
+ if (buttons[button_id].html.includes("Special Attack")) {
+ buttons[button_id].html = buttons[button_id].html.replace("Special Attack", utype_get_bombard_name(ptype))
+ buttons[button_id].title = "Odds of survival: 100%\n"
+ + "Combat: 5 rounds\n"
+ + "Targets: 2 units\n"
+ + "Move cost: 2 moves\n"
+ + "Min. moves: hasn't moved\n"
+ + "Max. casualties: 1\n"
+ + "\nBombards up to 2 units on the target tile for\n"
+ + "5 rounds. Can't be done to Fortress or City."
+ }
+ } break;
+ case "Artillery": for (button_id in buttons) {
+ if (buttons[button_id].html.includes("Special Attack")) {
+ buttons[button_id].html = buttons[button_id].html.replace("Special Attack", utype_get_bombard_name(ptype))
+ buttons[button_id].title = "Odds of survival: 100%\n"
+ + "Combat: 6 rounds\n"
+ + "Targets: 3 units\n"
+ + "Move cost: 2 moves\n"
+ + "Min. moves: hasn't moved\n"
+ + "Max. casualties: 1\n"
+ + "\nBombards up to 3 units on the target tile for\n"
+ + "6 rounds. Can't be done to Fortress or City."
+ }
+ } break;
+ case "Howitzer": for (button_id in buttons) {
+ if (buttons[button_id].html.includes("Special Attack")) {
+ buttons[button_id].html = buttons[button_id].html.replace("Special Attack", utype_get_bombard_name(ptype))
+ buttons[button_id].title = "Odds of survival: 100%\n"
+ + "Combat: 7 rounds\n"
+ + "Targets: 4 units\n"
+ + "Move cost: 4 moves\n"
+ + "Min. moves: hasn't moved\n"
+ + "Max. casualties: 1\n"
+ + "\nBombards up to 4 units on the target tile for\n"
+ + "7 rounds. Can't be done to Fortress or City."
}
} break;
case "Battleship": for (button_id in buttons) {
@@ -876,7 +1000,7 @@ function popup_action_selection(actor_unit, action_probabilities,
+ "Move cost: 5 moves\n"
+ "Max. casualties: 1\n"
+ "\nUses the range advantage of massive large guns to safely\n"
- + "shell and degrade up to 4 distant targets on a tile or city.\n"
+ + "shell and degrade up to 4 distant targets on a tile or city."
}
} break;
case "Zeppelin": for (button_id in buttons) {
@@ -888,7 +1012,7 @@ function popup_action_selection(actor_unit, action_probabilities,
+ "Move cost: 2 moves\n"
+ "Max. casualties: 1\n"
+ "\nDrops shrapnel bombs in the vicinity of enemies,\n"
- + "affecting up to 2 units and possibly killing one.\n"
+ + "affecting up to 2 units and possibly killing one."
}
} break;
}
@@ -926,10 +1050,12 @@ function popup_bribe_dialog(actor_unit, target_unit, cost, act_id)
.appendTo("div#game_page");
dhtml += "Treasury contains " + unit_owner(actor_unit)['gold'] + " gold. ";
- dhtml += "The price of bribing "
- + nations[unit_owner(target_unit)['nation']]['adjective']
- + " " + unit_types[target_unit['type']]['name']
- + " is " + cost + ". ";
+
+ dhtml += "The cost of "
+ + (unit_type(actor_unit)['name'] == "Patriarch" ? "converting " : "bribing ")
+ + nations[unit_owner(target_unit)['nation']]['adjective']
+ + " " + unit_types[target_unit['type']]['name']
+ + " is " + cost + ". ";
bribe_possible = cost <= unit_owner(actor_unit)['gold'];
@@ -1475,15 +1601,16 @@ function create_select_tgt_extra_button(parent_id, actor_unit_id,
var text = "";
var button = {};
var target_tile = index_to_tile(target_tile_id);
- var title = extras[target_extra_id]['name'];
+ var title = extras[target_extra_id]['rule_name'];
if (title=="") title = "Hideout";
+ else if (title=="Depth") title = "Deep Depth";
var spacer = " " + (title == "Walls" ? " " : "");
// UI Heuristic: buttons for stuff you can never do get rejected:
if (title.includes("Quay") && title.length == 5) return null; //quay+"" 0-width char can't be made
//
- text += render_action_image_into_button(spacer+title, extras[target_extra_id]['name'],
+ text += render_action_image_into_button(spacer+title, extras[target_extra_id]['rule_name'],
target_extra_id);
if (tile_has_extra(target_tile, target_extra_id)) {
@@ -1665,6 +1792,11 @@ function select_last_action()
var id = "#sel_last_action_dialog";
var dhtml = "";
var buttons = [];
+ var ptype = current_focus.length ? unit_type(current_focus[0]) : null;
+ var uname = ptype ? ptype.name : null;
+ var can_vigil = ptype ? /*utype_has_flag(ptype, UTYF_NONPROVOKEVIGIL) || commented out because !moved_this_turn req*/
+ uname.includes("Fighter") : true;
+ const msub = (uname == "Missile Submarine") /* "DIVE DEEP" fools utype_can_do_action
/* Reset dialog page. */
remove_active_dialog(id);
@@ -1680,9 +1812,7 @@ function select_last_action()
/* TODO:
=========================================================================================================
- make non-working //commented-out actions to work
- expel and capture units trying to do to same tile instead of next one.
- order_wants_direction() isn't working somehow? */
+ make non-working //commented-out actions to work */
//function add_action_last_button(buttons, action_id, override_name, order, activity, target, subtarget)
buttons = add_action_last_button(buttons, ACTION_ATTACK);
@@ -1694,65 +1824,87 @@ function select_last_action()
buttons = add_action_last_button(buttons, ACTION_TRANSPORT_BOARD, "Board");
*/
buttons = add_action_last_button(buttons, ACTION_SPY_BRIBE_UNIT, "Bribe");
- if (tech_known('Radio'))
+ if (tech_known('Radio') && (!uname || uname =="Trawler"))
+ buttons = add_action_last_button(buttons, ACTION_BASE, "Build Buoy", ORDER_PERFORM_ACTION, null, null, EXTRA_BUOY);
+ if (tech_known('Radio') && uname !="Trawler" && !msub)
buttons = add_action_last_button(buttons, ACTION_BASE, "Build Airbase", ORDER_PERFORM_ACTION, null, null, EXTRA_AIRBASE);
buttons = add_action_last_button(buttons, ACTION_FOUND_CITY, "Build City");
- if (client_rules_flag[CRF_MASONRY_FORT] && tech_known('Masonry'))
+ if (client_rules_flag[CRF_MASONRY_FORT] && tech_known('Masonry') && uname !="Trawler" && !msub)
buttons = add_action_last_button(buttons, ACTION_BASE, "Build Fort", ORDER_PERFORM_ACTION, null, null, EXTRA_FORT);
- if (tech_known('Construction'))
+ if (tech_known('Construction') && uname != "Trawler" && !msub)
buttons = add_action_last_button(buttons, ACTION_BASE, "Build Fortress", ORDER_PERFORM_ACTION, null, null, EXTRA_FORTRESS);
- if (client_rules_flag[CRF_CANALS] && tech_known('Engineering')) {
+ if (client_rules_flag[CRF_EXTRA_HIDEOUT] && server_settings.hideouts.val && tech_known('Warrior Code') && (!ptype || utype_has_flag(ptype,UTYF_FOOTSOLDIER)))
+ buttons = add_action_last_button(buttons, ACTION_BASE, "Build Hideout", ORDER_PERFORM_ACTION, null, null, EXTRA_);
+ if (client_rules_flag[CRF_CANALS] && tech_known('Engineering') && !msub) {
buttons = add_action_last_button(buttons, ACTION_ROAD, "Canal, coastal", ORDER_PERFORM_ACTION, null, null, EXTRA_CANAL);
buttons = add_action_last_button(buttons, ACTION_ROAD, "Canal, inland", ORDER_PERFORM_ACTION, null, null, EXTRA_WATERWAY);
}
-// buttons = add_action_last_button(buttons, ACTION_CAPTURE_UNITS); // acts on wrong tile, as do the below:
-// buttons = add_action_last_button(buttons, ACTION_CLEAN_POLLUTION, "Clean Pollution", ORDER_PERFORM_ACTION, ACTIVITY_CLEAN_POLLUTION, EXTRA_POLLUTION, EXTRA_POLLUTION);
-// buttons = add_action_last_button(buttons, ACTION_CLEAN_FALLOUT, "Clean Fallout", ORDER_PERFORM_ACTION, null, null, EXTRA_FALLOUT);
+ buttons = add_action_last_button(buttons, ACTION_CAPTURE_UNITS, "Capture Unit");
+
+ if (!msub) {
+ buttons = add_action_last_button(buttons, ACTION_COUNT, "Clean Pollution", ORDER_ACTIVITY, ACTIVITY_POLLUTION, null, EXTRA_POLLUTION);
+ buttons = add_action_last_button(buttons, ACTION_COUNT, "Clean Fallout", ORDER_ACTIVITY, ACTIVITY_FALLOUT, null, EXTRA_FALLOUT);
+ }
+
buttons = add_action_last_button(buttons, ACTION_CONQUER_CITY);
buttons = add_action_last_button(buttons, ACTION_CONVERT);
- buttons = add_action_last_button(buttons, ACTION_CULTIVATE);
+ if (!msub)
+ buttons = add_action_last_button(buttons, ACTION_CULTIVATE);
buttons = add_action_last_button(buttons, ACTION_SUICIDE_ATTACK, "Detonate Missile");
buttons = add_action_last_button(buttons, ACTION_NUKE, "Detonate Nuke");
+ if (client_rules_flag[CRF_MP2_D] && (!uname || uname == "Missile Submarine"))
+ buttons = add_action_last_button(buttons, ACTION_BASE, "Dive Deep", ORDER_PERFORM_ACTION, null, null, EXTRA_DEEPDIVE);
buttons = add_action_last_button(buttons, ACTION_TRANSPORT_EMBARK, "Embark");
buttons = add_action_last_button(buttons, ACTION_TRADE_ROUTE);
+ buttons = add_action_last_button(buttons, ACTION_EXPEL_UNIT);
buttons = add_action_last_button(buttons, ACTION_FORTIFY);
buttons = add_action_last_button(buttons, ACTION_HELP_WONDER);
buttons = add_action_last_button(buttons, ACTION_HOME_CITY, "Home City");
- buttons = add_action_last_button(buttons, ACTION_IRRIGATE, "Irrigate"); // works on blank tiles but not farmland
- if (tech_known('Refrigeration'))
+ if (uname != "Trawler" && !msub)
+ buttons = add_action_last_button(buttons, ACTION_IRRIGATE, "Irrigate"); // works on blank tiles but not farmland
+ if (tech_known('Refrigeration') && uname != "Trawler" && !msub)
buttons = add_action_last_button(buttons, ACTION_IRRIGATE, "Irrigate Farmland", ORDER_PERFORM_ACTION, null, null, EXTRA_FARMLAND);
buttons = add_action_last_button(buttons, ACTION_JOIN_CITY);
//buttons = add_action_last_button(buttons, ACTION_TRANSPORT_LOAD); // GET FROM SVEINUNG WHO FINISHED THIS RECENTLY
- if (client_rules_flag[CRF_MAGLEV] && tech_known('Superconductors'))
+ if (client_rules_flag[CRF_MAGLEV] && tech_known('Superconductors') && !msub)
buttons = add_action_last_button(buttons, ACTION_ROAD, "MagLev", ORDER_PERFORM_ACTION, null, null, EXTRA_MAGLEV);
+ if (uname !="Trawler") {
+ if (!msub) {
+ buttons = add_action_last_button(buttons, ACTION_MINE, "Mine", ORDER_PERFORM_ACTION, null, null, EXTRA_MINE);
+ }
+ buttons = add_action_last_button(buttons, ACTION_PILLAGE, "Pillage Anything", ORDER_PERFORM_ACTION, null, null, -1);
+ }
+
+ if (!msub) {
+ buttons = add_action_last_button(buttons, ACTION_PLANT, "Plant");
+ buttons = add_action_last_button(buttons, ACTION_SPY_POISON_ESC, "Poison City");
- buttons = add_action_last_button(buttons, ACTION_MINE, "Mine", ORDER_PERFORM_ACTION, null, null, EXTRA_MINE);
- buttons = add_action_last_button(buttons, ACTION_PILLAGE, "Pillage Anything", ORDER_PERFORM_ACTION, null, null, -1);
- buttons = add_action_last_button(buttons, ACTION_PLANT, "Plant");
- buttons = add_action_last_button(buttons, ACTION_SPY_POISON_ESC, "Poison City");
-
- if (tech_known('Railroad'))
- buttons = add_action_last_button(buttons, ACTION_ROAD, "Railroad", ORDER_PERFORM_ACTION, null, null, EXTRA_RAILROAD);
- buttons = add_action_last_button(buttons, ACTION_ROAD, "Road", ORDER_PERFORM_ACTION, null, null, EXTRA_ROAD);
+ if (tech_known('Railroad')) {
+ buttons = add_action_last_button(buttons, ACTION_ROAD, "Railroad", ORDER_PERFORM_ACTION, null, null, EXTRA_RAILROAD);
+ }
+ buttons = add_action_last_button(buttons, ACTION_ROAD, "Road", ORDER_PERFORM_ACTION, null, null, EXTRA_ROAD);
+ }
buttons = add_action_last_button(buttons, ACTION_RECYCLE_UNIT);
- buttons = add_action_last_button(buttons, ACTION_SPY_SABOTAGE_UNIT_ESC);
+ buttons = add_action_last_button(buttons, ACTION_SPY_SABOTAGE_UNIT_ESC, "Sabotage Unit");
+ buttons = add_action_last_button(buttons, ACTION_COUNT, "Sentry", ORDER_ACTIVITY, ACTIVITY_SENTRY, null, -1);
buttons = add_action_last_button(buttons, ACTION_SPY_ATTACK, "Spy vs. Spy");
- buttons = add_action_last_button(buttons, ACTION_STEAL_MAPS);
- buttons = add_action_last_button(buttons, ACTION_STEAL_MAPS_ESC);
+ buttons = add_action_last_button(buttons, ACTION_STEAL_MAPS, "Steal Map");
+ buttons = add_action_last_button(buttons, ACTION_STEAL_MAPS_ESC, "Steal Map Escape");
buttons = add_action_last_button(buttons, ACTION_TRANSFORM_TERRAIN);
- // buttons = add_action_last_button(buttons, ACTION_EXPEL_UNIT); act on wrong tile
/* Currently disallowed until actionenablers can guarantee this is legal at the server level.
TODO: when ruleset has actionenablers regulating ACTION_TRANSPORT_UNLOAD legality, this button can return.
buttons = add_action_last_button(buttons, ACTION_TRANSPORT_UNLOAD);
*/
buttons = add_action_last_button(buttons, ACTION_UPGRADE_UNIT);
+ if (can_vigil)
+ buttons = add_action_last_button(buttons, ACTION_COUNT, "Vigil", ORDER_ACTIVITY, ACTIVITY_VIGIL, null, -1);
buttons = add_action_last_button(buttons, ACTION_COUNT, "NO ACTION", ORDER_LAST);
var close_button = {
- html: render_action_image_into_button("Cancel (𝗪)", "cancel"),
+ html: render_action_image_into_button("Cancel (𝗪)", "cancel"),
click: function() {
remove_active_dialog(id);
deactivate_goto(false);
@@ -1799,7 +1951,7 @@ function add_action_last_button(buttons, action_id, override_name, order, activi
if (!override_name) override_name = actions[action_id]['ui_name'].replace("%s", "").replace("%s","");
var new_button = create_action_last_button(override_name, action_id, order, activity, target, subtarget);
- new_button.html = render_action_image_into_button(new_button.html, action_id);
+ new_button.html = render_action_image_into_button(new_button.html, action_id, subtarget, order, activity);
buttons.push(new_button);
return buttons;
diff --git a/freeciv-web/src/main/webapp/javascript/banlist.js b/freeciv-web/src/main/webapp/javascript/banlist.js
index 1e060f30e..fb33e9f15 100644
--- a/freeciv-web/src/main/webapp/javascript/banlist.js
+++ b/freeciv-web/src/main/webapp/javascript/banlist.js
@@ -21,7 +21,7 @@
/* This is a list of banned users of Freeciv-web.
Note that user accounts can also be disabled by setting activated=0 in the auth DB table.
*/
-var banned_users = ["Blank", "DMX", "Canik"];
+var banned_users = ["Blank", "KickedPlayer"];
/**************************************************************************
Returns false if the text contains a banned user.
diff --git a/freeciv-web/src/main/webapp/javascript/city.js b/freeciv-web/src/main/webapp/javascript/city.js
index 9a35f25ec..c48ce9b68 100644
--- a/freeciv-web/src/main/webapp/javascript/city.js
+++ b/freeciv-web/src/main/webapp/javascript/city.js
@@ -153,6 +153,7 @@ function remove_city(pcity_id)
var update = client.conn.playing.playerno && city_owner(pcity).playerno == client.conn.playing.playerno;
var ptile = city_tile(cities[pcity_id]);
delete cities[pcity_id];
+ if (renderer == RENDERER_WEBGL) update_city_position(ptile);
if (update) {
city_screen_updater.update();
@@ -377,7 +378,7 @@ function show_city_dialog(pcity)
} else {
city_dialog_title += "";
+ +">";
}
}
city_dialog_title += "";
@@ -508,12 +509,17 @@ function show_city_dialog(pcity)
$("#worklist_dialog_headline").unbind('click');
$("#worklist_dialog_headline").click(function(ev) { ev.stopImmediatePropagation(); city_remove_current_prod()} );
+ var orig_renderer = renderer;
+ renderer = RENDERER_2DCANVAS;
+
set_citydlg_dimensions(pcity);
set_city_mapview_active();
// Center map on area around city for when they leave the city
+ //save_map_return_position(city_tile(pcity)); //save tile locations for shift-spacebar return position function
center_tile_mapcanvas(city_tile(pcity));
update_map_canvas(0, 0, mapview['store_width'], mapview['store_height']);
+ renderer = orig_renderer;
var pop_string = is_small_screen() ? city_population(pcity)+"K" : numberWithCommas(city_population(pcity)*1000);
var change_string = pcity['granary_turns'] < 0 ? "Starves in: " : "Growth in: ";
@@ -656,7 +662,7 @@ function show_city_dialog(pcity)
"";
@@ -685,8 +691,15 @@ function show_city_dialog(pcity)
var sunits = get_supported_units(pcity);
if (sunits != null) {
var supported_units_html = "";
+ var upkeep_str = "";
+ var fu=0,gu=0,su=0; // total upkeep counters
for (var t = 0; t < sunits.length; t++) {
punit = sunits[t];
+ if (punit['upkeep'] != null) {
+ su += parseInt(punit['upkeep'][O_SHIELD],10);
+ fu += parseInt(punit['upkeep'][O_FOOD],10);
+ gu += parseInt(punit['upkeep'][O_GOLD],10);
+ }
sprite = get_unit_image_sprite(punit);
if (sprite == null) {
console.log("Missing sprite for " + punit);
@@ -705,6 +718,11 @@ function show_city_dialog(pcity)
+ get_html_hp_sprite(punit,false)
+ get_html_activity_sprite(punit);
}
+ if (fu) upkeep_str += "" + fu + "";
+ if (su) upkeep_str += "" + su + "";
+ if (gu) upkeep_str += "" + gu + "";
+
+ $("#city_supported_units_title").html("Supported Units: "+upkeep_str+"");
$("#city_supported_units_list").html(supported_units_html);
// trick to compensate for removed scrollbar clipping the top:
$("#city_supported_units_list").css({"margin-top":"-10px","padding-top":"20px"});
@@ -737,11 +755,11 @@ function show_city_dialog(pcity)
var trade_txt2 = pcity['surplus'][O_TRADE]==pcity['prod'][O_TRADE] ? "" : "(" + pcity['prod'][O_TRADE] + ")";
if (pcity.traderoute_count) {
trade_txt2 +=
- ""
+ ""
+ ""
+ "" + get_city_traderoute_revenue(pcity['id'])+"";
}
-
+
var gold_txt = "";
if (pcity['surplus'][O_GOLD] > 0) gold_txt += "+";
else gold_txt += "";
@@ -876,7 +894,7 @@ function show_city_dialog(pcity)
var happy_people = pcity['ppl_happy'][FEELING_FINAL];
var unhappy_angry_people = pcity['ppl_unhappy'][FEELING_FINAL]+pcity['ppl_angry'][FEELING_FINAL];
// Color code for upcoming state under current configuration of tiles/luxury rate/improvements/deployed units:
- if (happy_people >= pcity['size']*0.4999 && unhappy_angry_people==0 && pcity['size']>2) {
+ if (happy_people >= pcity['size']*0.4999 && unhappy_angry_people==0 && pcity['size']>=city_celebrate_size(pcity)) {
next_state = "Celebrating"; $('#rapture_status').attr('title', "Celebration next turn");
if (!pissed) {
rapture_status_class = "city_dialog_celeb";
@@ -895,15 +913,59 @@ function show_city_dialog(pcity)
$('#rapture_status').attr('title', get_city_state_description(get_city_state(pcity),next_state));
$('#rapture_food').html(rapture_food_status_html);
-
- $('#rapture_status').html("
"+get_city_state(pcity)+"
");
+
+ var rapture_status_icon = "";
+ /* 4-bit code. BIT values:
+ 1 = raptured this turn
+ 2 = raptures next turn. (pause this turn)
+ 4 = raptures in 2 turns. (pause next turn)
+ 8 = raptures in 3 turns. (pause 2 turns)
+ */
+ const RLAST = 1,
+ RNOW = 2,
+ R2 = 4,
+ R3 = 8;
+ switch (pcity.rapture_status) {
+ case RLAST:
+ rapture_status_icon = ""
+ break;
+ /* all possibilities of bit 2 being on, i.e., can rapture this turn*/
+ case RNOW:
+ case RNOW+RLAST:
+ case RNOW+R2:
+ case RNOW+RLAST+R2:
+ case RNOW+R3:
+ case RNOW+R3+RLAST:
+ case RNOW+R2+R3:
+ case RNOW+RLAST+R2+R3:
+ rapture_status_icon = ""
+ break;
+ /* bit 4, can rapture next turn BUT NOT this turn */
+ case R2:
+ case R2+RLAST:
+ case R2+R3:
+ case R2+R3+RLAST:
+ rapture_status_icon = ""
+ break;
+ /* bit 8 can rapture in 2 turns BUT NOT this turn or next turn */
+ case R3:
+ case R3+RLAST:
+ rapture_status_icon = ""
+ break;
+ }
+ $('#rapture_status').html("
");
$('#rapture_status').tooltip({
tooltipClass: "wider-tooltip" , position: { my:"center bottom", at: "center top-3"},
show: { delay:200, effect:"none", duration: 0 }, hide: {delay:120, effect:"none", duration: 0}
});
- if (pcity['size'] >= 27) $("#city_canvas_top_div").width(pcity['size']*15-30); // Fix the specialist panel overlapping with the citizen amounts panel for bigger cities
-
+ /* Fix the citzen panel overlaps the "citizen amounts" panel for bigger cities with 5 city_radius_sq
+ * Those with larger city_radius_sq get a bigger canvas with more room */
+ if (pcity.city_radius_sq<=5 && pcity['size'] >= 27) {
+ $("#city_canvas_top_div").width(pcity['size']*15-30);
+ }
+
$('#disbandable_city').off();
$('#disbandable_city').prop('checked',
pcity['city_options'] != null && pcity['city_options'].isSet(CITYO_DISBAND));
@@ -989,6 +1051,21 @@ function city_change_tab(tab_num) {
city_tab_index = tab_num;
}
+/**************************************************************************
+ The size at which a city can celebrate.
+**************************************************************************/
+function city_celebrate_size(pcity) {
+ var csize = game_info.celebratesize;
+
+ // TODO: evaluate effects[137] (EFT_CELEBRATE_SIZE_ADD) instead of hard-coded:
+ if (client_rules_flag[CRF_MP2_D]) {
+ if (player_has_wonder(players[pcity.owner].playerno, improvement_id_by_name(B_ANGKOR_WAT))) {
+ csize -= 1;
+ }
+ }
+ return csize;
+}
+
/**************************************************************************
Returns the name and sprite of the current city production.
**************************************************************************/
@@ -1521,7 +1598,7 @@ function close_city_dialog()
if (active_city) { // map will be centered on city that was being viewed
center_tile_mapcanvas(city_tile(active_city));
active_city = null;
- update_map_canvas_full();
+ if (renderer == RENDERER_2DCANVAS) update_map_canvas_full();
}
// Closing city dialog re-shows container for minimized windows:
@@ -1534,31 +1611,36 @@ function close_city_dialog()
**************************************************************************/
function do_city_map_click(ptile)
{
- // updated==if cityhand.c:handle_city_specialist() looks at
- // the top 3 bits in city_id for the type of specialst to make:
- var updated = true;
-
var packet = null;
var city_id = active_city['id'];
if (ptile['worked'] == city_id) {
- // Server defaults to make tile-worker into an entertainer.
- // Override it if user has a selected_specialist type:
- if (updated) { // only run this feature on updated server
- var s = selected_specialist;
- if (s >= 4) {city_id += 32768; s-= 4;}
- if (s >= 2) {city_id += 16384; s-= 2;}
- if (s >= 1) {city_id += 8192; }
- }
+
+ // Disallow attempting specialist if clicking someone else's worked tile:
+ if (ptile.owner != client.conn.playing.playerno
+ && ptile.owner != UNCLAIMED_LAND) {
+ play_sound("click_illegal.ogg");
+ return;
+ }
+
packet = {"pid" : packet_city_make_specialist,
"city_id" : city_id,
- "tile_id" : ptile['index']
+ "tile_id" : ptile['index'],
+ "specialist_to": (selected_specialist ? selected_specialist : 0)
};
} else {
+ // Disallow attempting to work a tile not in the city's workable map:
+ if (!city_map_includes_tile(ptile, active_city)) {
+ play_sound("click_illegal.ogg");
+ return;
+ }
+
packet = {"pid" : packet_city_make_worker,
"city_id" : city_id,
"tile_id" : ptile['index']};
}
+
send_request(JSON.stringify(packet));
+ //play_sound("click_legal.ogg");
}
/**************************************************************************
@@ -1931,6 +2013,33 @@ function get_city_tile_map_for_pos(x, y)
return get_city_tile_map_for_pos(x, y);
}
+/**************************************************************************
+ Returns true if a tile is legally workable by a city
+**************************************************************************/
+function city_map_includes_tile(ptile, pcity)
+{
+ if (!ptile || !pcity) return false;
+
+ var radius = pcity.city_radius_sq;
+ var ctile = city_tile(pcity);
+ var dist = sq_map_distance(ptile, ctile);
+
+ /* To be in the workable city map, a tile must be:
+ 1. ...in the city's workable city_radius_sq
+ 2. ...not owned by a player other than city's owner */
+ if (dist > radius) {
+ return false;
+ }
+ else if (ptile.owner == UNCLAIMED_LAND) {
+ return true;
+ }
+ else if (ptile.owner != pcity.owner) {
+ return false;
+ }
+
+ return true;
+}
+
/**************************************************************************
Toggles specialist control pane in city title bar.
**************************************************************************/
@@ -2257,10 +2366,17 @@ function show_city_traderoutes()
tcity = cities[tcity_id];
if (tcity == null) continue;
- //msg += good['name'] + " trade with " + tcity['name'];
- //msg += " gives +" + routes[i]['value'] + " base trade each turn." + " ";
- msg += "Trade Route to " + tcity['name'] +": ";
- msg += "+" + routes[i]['value'] + " base trade each turn." + " ";
+ var city_flag_tag = nations[players[tcity['owner']]['nation']]['graphic_str'];
+ var city_flag_url = "/images/flags/" + city_flag_tag + "-web" + get_tileset_file_extention();
+ var city_flag = "";
+ var city_link = "" + tcity.name+"";
+ msg += city_flag + "
Trade Route to the " + nations[players[tcity.owner].nation].adjective
+ + " city of " + city_link + ": ";
+ msg += "+" + routes[i]['value'] + " base trade each turn." + "
";
}
if (msg == "") {
@@ -2599,7 +2715,7 @@ function populate_worklist_production_choices(pcity)
+ "
";
@@ -2650,6 +2766,7 @@ function populate_worklist_production_choices(pcity)
return;
}
send_city_worklist_add(pcity['id'], kind, value);
+ production_selection = [];
});
} else {
$(".kindvalue_item").click(function() {
@@ -3467,7 +3584,7 @@ function update_city_screen()
var citizen_types = ["unhappy","content","happy"]
var sprite;
var city_list_citizen_html = "";
- var updown_sort_arrows = "";
+ var updown_sort_arrows = "";
// Used for generating micro-icon of current prooduction:
var prod_sprite;
var prod_img_html;
@@ -3486,15 +3603,15 @@ function update_city_screen()
city_list_html = "
"
+ "
"
+ "
Name"+updown_sort_arrows+"
Size"+updown_sort_arrows+"
"+city_list_citizen_html
- + "
State
"
- + "
"
- + "
"
- + "
"
- + "
"
+ + "
State
"
+ + "
"
+ + "
"
+ + "
"
+ + "
"
// + "
"
- + "
"
- + "
"
- + "
"
+ + "
"
+ + "
"
+ + "
"
+ "
Grows In"+updown_sort_arrows+"
Granary"
+updown_sort_arrows+"
Producing"+updown_sort_arrows+"
"
+ "
Turns"+updown_sort_arrows
@@ -3510,13 +3627,13 @@ function update_city_screen()
+ "
"
+ "
Name"+"
Pop"+"
"+city_list_citizen_html
+ "
Mood
"
- + "
"
- + "
"
- + "
"
+ + "
"
+ + "
"
+ + "
"
+ "
"
- + "
"
- + "
"
- + "
"
+ + "
"
+ + "
"
+ + "
"
/*+ "
Grows"+"
Grain"*/
+ ( (!tiny_screen) ? ("
Grows"+"
Grain")
: ("
Grow"+"
Food") )
@@ -3537,13 +3654,13 @@ function update_city_screen()
+ "
"
+ "
Name"+"
Pop"+"
"+city_list_citizen_html
+ "
Mood
"
- + "
"
- + "
"
- + "
"
+ + "
"
+ + "
"
+ + "
"
+ "
"
- + "
"
- + "
"
- + "
"
+ + "
"
+ + "
"
+ + "
"
+ "
Grow"+"
Food"
+"
Build"+"
"
+ "
in
"
@@ -3659,7 +3776,7 @@ function update_city_screen()
let pissed = pcity['hangry'] || (server_settings.fulldisorder.val && pcity['anarchy']);
// Color code for upcoming state under current configuration of tiles/luxury rate/improvements/deployed units:
- if (happy_people >= pcity['size']*0.4999 && unhappy_angry_people==0 && pcity['size']>2) {
+ if (happy_people >= pcity['size']*0.4999 && unhappy_angry_people==0 && pcity['size']>=city_celebrate_size(pcity)) {
next_state = "Celebrating";
if (!pissed)
city_state_span = "id='city_state"+pcity.id+"' class='redux_centre mobile_centre hint_of_green' style='cursor:help; text-align:center;'>"+city_state; // half or more happy, no unhappy = city will (continue to) celebrate, green code.
@@ -3713,7 +3830,7 @@ function update_city_screen()
break;
case CURV_POLLUTION:
city_user = "
"
@@ -3879,7 +4002,10 @@ function update_city_screen()
$("#city_table").html("You have no cities. Build new cities with the Settlers unit.");
}
- $('#cities_scroll').css("height", $(window).height() - 200);
+ /* old method : assume 200px above the element to safely fit it (works but doesn't use all real estate)
+ $('#cities_scroll').css("height", $(window).height() - 200); */
+ // New method: Try to calculate height to use all vertical real estate on the screen:
+ $('#cities_scroll').css("height", $(window).height() - $("#cities_scroll").offset().top-3);
$("#city_table").tablesorter({theme:"dark", sortList: sortList});
@@ -4056,7 +4182,8 @@ function cma_clipboard_macro(event, called_by_CMA)
// Emulates clicking the city centre, causing auto-arrange tiles:
var packet = {"pid" : packet_city_make_specialist,
"city_id" : parseInt(city_id),
- "tile_id" : city_tile(cities[city_id]).index
+ "tile_id" : city_tile(cities[city_id]).index,
+ "specialist_to": -1
};
send_request(JSON.stringify(packet));
city_checkbox_states[city_id] = true;
@@ -4234,7 +4361,7 @@ function get_city_state(pcity)
{
if (pcity == null) return;
- if (pcity['was_happy'] && pcity['size'] >= 3) {
+ if (pcity['was_happy'] && pcity['size'] >= city_celebrate_size(pcity)) {
return "Celebrating";
} else if (pcity['hangry']) {
return "Famine";
@@ -4468,7 +4595,7 @@ function city_get_foreign_pct(city_id)
/**************************************************************************
Returns the 3d model name for the given city.
-**************************************************************************
+**************************************************************************/
function city_to_3d_model_name(pcity)
{
var size = 0;
@@ -4493,7 +4620,7 @@ function city_to_3d_model_name(pcity)
}
return "city_" + city_style_name + "_" + size;
-}*/
+}
/**************************************************************************
Returns the city walls scale of for the given city.
@@ -4531,6 +4658,7 @@ function set_citydlg_dimensions(pcity)
case 8:
case 9:
citydlg_map_height += tileset_height;
+ citydlg_map_width += tileset_width;
break;
default:
if (pcity.city_radius_sq > 17) {
@@ -4542,6 +4670,7 @@ function set_citydlg_dimensions(pcity)
//console.log("%d,%d",citydlg_map_width,citydlg_map_height)
$("#city_canvas_div").css({"width":citydlg_map_width, "height":citydlg_map_height});
+ $("#city_canvas_top_div").css({"width":citydlg_map_width, "height":citydlg_map_height});
//$("#city_canvas").css({"width":citydlg_map_width, "height":citydlg_map_height});
$("#city_canvas").attr('width', citydlg_map_width);
$("#city_canvas").attr('height', citydlg_map_height);
diff --git a/freeciv-web/src/main/webapp/javascript/civclient.js b/freeciv-web/src/main/webapp/javascript/civclient.js
index d2caf1065..5f95f8b32 100644
--- a/freeciv-web/src/main/webapp/javascript/civclient.js
+++ b/freeciv-web/src/main/webapp/javascript/civclient.js
@@ -33,6 +33,10 @@ var fc_seedrandom = null;
var game_type = "";
var link_game_type = "";
+var RENDERER_2DCANVAS = 1; // default HTML5 Canvas
+var RENDERER_WEBGL = 2; // WebGL + Three.js
+var renderer = RENDERER_2DCANVAS; // This variable specifies which map renderer to use, 2d Canvas or WebGL.
+
var last_turn_change_time = 0;
var turn_change_elapsed = 0;
var seconds_to_phasedone = 0;
@@ -105,6 +109,12 @@ function civclient_init()
return;
}
+ if ($.getUrlVar('renderer') == "webgl") {
+ renderer = RENDERER_WEBGL;
+ }
+ if (renderer == RENDERER_2DCANVAS) init_mapview();
+ if (renderer == RENDERER_WEBGL) init_webgl_renderer();
+
init_mapview();
game_init();
@@ -118,7 +128,11 @@ function civclient_init()
statusTimerId = setInterval(update_game_status_panel, 6000);
if (overviewTimerId == -1) {
- OVERVIEW_REFRESH = 6000;
+ if (renderer == RENDERER_WEBGL) {
+ OVERVIEW_REFRESH = 12000;
+ } else {
+ OVERVIEW_REFRESH = 6000;
+ }
overviewTimerId = setInterval(redraw_overview, OVERVIEW_REFRESH);
}
@@ -280,6 +294,14 @@ function civclient_init()
play_music = is_longturn(); // singleplayer defaults off for server bandwidth savings
}
+ show_timestamps = simpleStorage.get('tstamps');
+ if (show_timestamps == null) {
+ show_timestamps = is_longturn();
+ }
+ if (!show_timestamps) {
+ changeCss(".ts", "display:none");
+ }
+
audio_initialize();
//------------------------------------------------------------------------------------------------
@@ -423,7 +445,7 @@ function init_common_intro_dialog() {
"NOTE: Rulesets named 'Multiplayer' are simply the more modern rulesets that work well with any number of players. "+
"You can play Singleplayer with a Multiplayer ruleset if you wish to improve for multiplayer games with other humans. "+
" • If you do this, try: /set sciencebox 80 (or similar) to set a singleplayer research pace. /help sciencebox for more info. "
- : "WARNING: Default rules for Multiplayer are Multiplayer+.
" ) +
+ : "Default rules for multiplayer games are: Multiplayer II Evolution Avant-garde. (mp2-ag)
" ) +
"Click Game button to select Game Version (ruleset).
";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+ dhtml += ""
+ + " ";
+
+
+
+ $(id).html(dhtml);
+ $("#f_combat").prop("checked", console_filters['combat']);
+ $("#f_actions").prop("checked", console_filters['actions']);
+ $("#f_sentry").prop("checked", console_filters['sentry']);
+ $("#f_tech").prop("checked", console_filters['tech']);
+ $("#f_governor").prop("checked", console_filters['governor']);
+ $("#f_diplomacy").prop("checked", console_filters['diplomacy']);
+ $("#f_cityprod").prop("checked", console_filters['cityprod']);
+ $("#f_citywarn").prop("checked", console_filters['citywarn']);
+ $("#f_chat").prop("checked", console_filters['chat']);
+ $("#f_setting").prop("checked", console_filters['setting']);
+ $("#f_pollution").prop("checked", console_filters['pollution']);
+
+ var buttons = { 'Set All': function() {console_filters_set(true);},
+ 'Clear': function() {console_filters_set(false);},
+ 'Flip':function() {console_filters_set('flip');},
+ 'Do it!': function() {console_filter_radio_clicked(); remove_active_dialog(id);}
+ };
+
+ $(id).dialog({
+ title : "Filter console messages",
+ bgiframe : true,
+ modal : false,
+ width : (is_small_screen() ? "98%" : "360px"),
+ buttons : buttons });
+
+ $(id).dialog('open');
+ $(id).parent().css("zIndex", 151); // force placement over other windows.
+ $(id).css("background","url(/images/bg-dark50.png)");
+ $(id).next().css("text-align", "center");
+ $(id).dialog('widget').position({my:"left top", at:"left center", of:window})
+
+ dialog_register(id);
+ $(id).dialog().next().children().children()[3].focus();
+}
diff --git a/freeciv-web/src/main/webapp/javascript/control.js b/freeciv-web/src/main/webapp/javascript/control.js
index 8d4be220b..c19a657fe 100644
--- a/freeciv-web/src/main/webapp/javascript/control.js
+++ b/freeciv-web/src/main/webapp/javascript/control.js
@@ -40,9 +40,6 @@ const GOTO_CLICK_COOLDOWN = 475; // Cooldown period before that tile can be clic
var keyboard_input = true;
var unitpanel_active = false;
var allow_right_click = false;
-var DEBUG_UNITS = false; // console log tools for debugging unit issues
-var DEBUG_FOCUS = false;
-var DEBUG_ACTION_PACKETS = false;
// performance: is_touch_device() was being called many times per second
var touch_device = null; // TO DO: replace all is_touch_device() function calls
@@ -140,6 +137,10 @@ var end_turn_info_message_shown = false;
var action_selection_in_progress_for = 0; /* before IDENTITY_NUMBER_ZERO */
var is_more_user_input_needed = false;
+/* If a supercow leaves that mode with CTRL-ALT-SHIFT S, this var will
+ keep track of the fact that they were one, so they can toggle back */
+var was_supercow = false;
+
/****************************************************************************
...
****************************************************************************/
@@ -148,7 +149,11 @@ function control_init()
urgent_focus_queue = [];
touch_device = is_touch_device();
- mapctrl_init_2d();
+ if (renderer == RENDERER_2DCANVAS) {
+ mapctrl_init_2d();
+ } else {
+ init_webgl_mapctrl();
+ }
$(document).keydown(global_keyboard_listener);
$(window).resize(mapview_window_resized);
@@ -227,7 +232,7 @@ function control_init()
}, false);
var context_options = {
- selector: '#canvas',
+ selector: (renderer == RENDERER_2DCANVAS) ? '#canvas' : '#canvas_div' ,
zIndex: 5000,
autoHide: true,
callback: function(key, options) {
@@ -251,9 +256,14 @@ function control_init()
if (!touch_device) {
context_options['position'] = function(opt, x, y){
if (touch_device) return;
- //var new_top = mouse_y + $("#canvas_div").offset().top;
- var new_top = mouse_y + $("#canvas").offset().top-52;
- opt.$menu.css({top: new_top , left: mouse_x+16});
+ //if (renderer == RENDERER_2DCANVAS) var new_top = mouse_y + $("#canvas_div").offset().top;
+ if (renderer == RENDERER_2DCANVAS) {
+ var new_top = mouse_y + $("#canvas").offset().top-52;
+ opt.$menu.css({top: new_top , left: mouse_x+16});
+ } else {
+ var new_top = mouse_y + $("#canvas_div").offset().top-52;
+ opt.$menu.css({top: new_top , left: mouse_x+16});
+ }
};
} else {
context_options['position'] = function(opt, x, y){
@@ -443,7 +453,7 @@ function mouse_moved_cb(e)
mouse_y = e.clientY;
}
}
- if (active_city == null && mapview_canvas != null
+ if (renderer == RENDERER_2DCANVAS && active_city == null && mapview_canvas != null
&& $("#canvas").length) {
mouse_x = mouse_x - $("#canvas").offset().left;
mouse_y = mouse_y - $("#canvas").offset().top;
@@ -455,6 +465,22 @@ function mouse_moved_cb(e)
mapview['gui_x0'] += diff_x;
mapview['gui_y0'] += diff_y;
+ touch_start_x = mouse_x;
+ touch_start_y = mouse_y;
+ update_mouse_cursor();
+ }
+ } else if (renderer == RENDERER_WEBGL && active_city == null && $("#canvas_div").length) {
+ mouse_x = mouse_x - $("#canvas_div").offset().left;
+ mouse_y = mouse_y - $("#canvas_div").offset().top;
+
+ if (mapview_mouse_movement && !goto_active) {
+ // move the mapview using mouse movement.
+ var spos = webgl_canvas_pos_to_map_pos(touch_start_x, touch_start_y);
+ var epos = webgl_canvas_pos_to_map_pos(mouse_x, mouse_y);
+ if (spos != null && epos != null) {
+ camera_look_at(camera_current_x + spos['x'] - epos['x'], camera_current_y, camera_current_z + spos['y'] - epos['y']);
+ }
+
touch_start_x = mouse_x;
touch_start_y = mouse_y;
update_mouse_cursor();
@@ -493,7 +519,12 @@ function update_mouse_cursor()
}
//console.log("update_mouse_cursor() mmm:1 g_a:0, came_from_context_menu:"+came_from_context_menu);
- var ptile = canvas_pos_to_tile(mouse_x, mouse_y);
+ var ptile;
+ if (renderer == RENDERER_2DCANVAS) {
+ ptile = canvas_pos_to_tile(mouse_x, mouse_y);
+ } else {
+ ptile = webgl_canvas_pos_to_tile(mouse_x, mouse_y);
+ }
if (ptile == null) return; /* TO DO: this is the only way this function returns without forcing real_mouse_move_mode=false, presumably because
@@ -803,15 +834,43 @@ function is_unprefixed_message(message) {
...
****************************************************************************/
function check_text_input(event,chatboxtextarea) {
- if (event.keyCode == 13 && event.shiftKey == 0) {
- send_text_input(chatboxtextarea);
- }
- // allows ctrl-E hotkey while inside text input area.
- if (event.ctrlKey && String.fromCharCode(event.keyCode) == 'E') {
- event.preventDefault(); // override possible browser shortcut
- emoji_popup();
+ if (event.keyCode == 13) {
+ if (event.shiftKey == 0) send_text_input(chatboxtextarea);
+ else if (C_S_RUNNING == client_state()) send_end_turn();
+ }
+ if (event.ctrlKey) {
+ // allow ctrl-E hotkey to pop-up emoji selector, when inside chat text input:
+ if (!event.shiftKey && !event.altKey && String.fromCharCode(event.keyCode) == 'E') {
+ event.preventDefault();
+ emoji_popup();
+ }
+ // allow ctrl-shift-E hotkey for error logging, when inside chat text input:
+ else if (event.shiftKey && !event.altKey && String.fromCharCode(event.keyCode) == 'E') {
+ event.preventDefault();
+ toggle_error_logging();
+ }
+ // allow ctrl-S hotkey while inside text input area (prevents annoying page-save):
+ else if (!event.shiftKey && !event.altKey && String.fromCharCode(event.keyCode) == 'S') {
+ event.preventDefault();
+ quicksave();
+ }
+ }
+ else if (event.altKey) {
+ if (String.fromCharCode(event.keyCode) == 'F') {
+ event.preventDefault();
+ console_filter_dialog();
+ }
}
}
+/****************************************************************************
+ Toggles the visual display of the server's error log events:
+****************************************************************************/
+function toggle_error_logging()
+{
+ $(".e_log_error").toggle();
+ add_client_message("Error logging is now "
+ + ($(".e_log_error").is(":visible") ? "ON." : "OFF.") );
+}
/**********************************************************************//**
Attempts to send content of chatbox. See function above.
**************************************************************************/
@@ -1176,7 +1235,7 @@ function update_unit_focus()
}
/**************************************************************************
- This function may be called from packhand.c, via update_unit_focus(),
+ This function may be called from packhand.js, via update_unit_focus(),
as a result of packets indicating change in activity for a unit. Also
called when user press the "Wait" command.
@@ -1356,6 +1415,7 @@ function advance_focus_inactive_units()
current_focus = []; /* Reset focus units. */
unit_may_have_lost_focus();
waiting_units_list = []; /* Reset waiting units list */
+ if (renderer == RENDERER_WEBGL) webgl_clear_unit_focus();
update_active_units_dialog();
$("#game_unit_orders_default").hide();
}
@@ -1466,9 +1526,14 @@ function update_unit_order_commands()
$("#order_quay").hide();
$("#order_canal").hide();
$("#order_well").hide();
+ $("#order_fort").hide();
$("#order_fortress").hide();
+ $("#order_castle").hide();
+ $("#order_bunker").hide();
$("#order_buoy").hide();
+ $("#order_fishtrap").hide();
$("#order_hideout").hide();
+ $("#order_deepdive").hide();
$("#order_navalbase").hide();
$("#order_airbase").hide();
$("#order_radar").hide();
@@ -1544,7 +1609,7 @@ function update_unit_order_commands()
ptype['name']=="Leader" || ptype['name']=="Queen"
// Workers/Riflemen can convert between each other in Communism:
|| ((governments[client.conn.playing['government']]['name']=="Communism"
- && ((ptype['name']=="Workers") || ptype['name']=="Riflemen"))
+ && (ptype['name'].startsWith("Workers") || ptype['name']=="Riflemen"))
&& tech_known('Communism'))
// AAA can convert to Mobile SAM under qualifying conditions:
|| ( ptype['name']=="Anti-Aircraft Artillery"
@@ -1573,6 +1638,7 @@ function update_unit_order_commands()
var infra_type = false;
if (ptype['name'] == "Workers" || ptype['name'] == "Migrants"
|| (ptype['name'] == "Tribesmen" && client_rules_flag[CRF_MP2_C])
+ || (ptype['name'] == "Trawler")
|| (ptype['name'] =="Proletarians" && governments[client.conn.playing['government']]['name']=="Communism")) {
worker_type = true;
@@ -1605,8 +1671,10 @@ function update_unit_order_commands()
const TILECLAIMS = (typeof EXTRA_TILE_CLAIM !== 'undefined');
const AIRBASES = (typeof EXTRA_AIRBASE !== 'undefined');
const BUOYS = (typeof EXTRA_BUOY !== 'undefined');
+ const FISHTRAPS = (typeof EXTRA_FISHTRAP !== 'undefined');
const RADAR = (typeof EXTRA_RADAR !== 'undefined');
const QUAYS = (typeof EXTRA_QUAY !== 'undefined');
+ const DEEPDIVE = (typeof EXTRA_DEEPDIVE !== 'undefined');
/* Whether player has tech for the Base. */
const HIDEOUT_TECH = tech_known("Warrior Code");
const FORT_TECH = tech_known("Construction") || (tech_known("Masonry") && client_rules_flag[CRF_MASONRY_FORT]);
@@ -1616,6 +1684,7 @@ function update_unit_order_commands()
const BUNKER_TECH = tech_known("Steel");
const AIRBASE_TECH = tech_known("Radio");
const BUOY_TECH = tech_known("Radio");
+ const FISHTRAP_TECH = tech_known("Refrigeration");
const RADAR_TECH = tech_known("Radar");
/* Whether the tile has pre-existing bases, which may be reqs or blockers for other bases to be built. */
const TILE_HAS_HIDEOUT = HIDEOUTS && tile_has_extra(ptile,EXTRA_);
@@ -1627,7 +1696,9 @@ function update_unit_order_commands()
const TILE_HAS_CLAIM = TILECLAIMS && tile_has_extra(ptile,EXTRA_TILE_CLAIM);
const TILE_HAS_AIRBASE = AIRBASES && tile_has_extra(ptile,EXTRA_AIRBASE);
const TILE_HAS_BUOY = BUOYS && tile_has_extra(ptile,EXTRA_BUOY);
+ const TILE_HAS_FISHTRAP = FISHTRAPS && tile_has_extra(ptile,EXTRA_FISHTRAP);
const TILE_HAS_RADAR = RADAR && tile_has_extra(ptile,EXTRA_RADAR);
+ const TILE_HAS_DEEPDIVE = DEEPDIVE && tile_has_extra(ptile,EXTRA_DEEPDIVE);
//-- Misc reqs:
const TILE_HAS_RIVER = tile_has_extra(ptile,EXTRA_RIVER);
const NO_RIVER_BASE = client_rules_flag[CRF_NO_BASES_ON_RIVERS];
@@ -1647,25 +1718,31 @@ function update_unit_order_commands()
//const CAN_TILE_CLAIM = !pcity && !oceanic && TILECLAIMS && !TILE_HAS_CLAIM && (client.conn.playing.playerno==tile_owner(ptile) ||
const CAN_TILE_AIRBASE = !pcity && !oceanic && AIRBASES && !TILE_HAS_AIRBASE && !(TILE_HAS_RIVER && NO_RIVER_BASE);
const CAN_TILE_BUOY = !pcity && oceanic && BUOYS && !TILE_HAS_BUOY && !(TILE_HAS_RIVER && NO_RIVER_BASE);
+ const CAN_TILE_FISHTRAP = !pcity && oceanic && FISHTRAPS && !TILE_HAS_FISHTRAP // this is only a 'half true' qualifier for tile can do fishtrap: further checks done later below
const CAN_TILE_RADAR = !pcity && !oceanic && RADAR && !TILE_HAS_RADAR && TILE_HAS_AIRBASE && !(TILE_HAS_RIVER && NO_RIVER_BASE);
+ const CAN_TILE_DEEPDIVE = terrain_name == "Deep Ocean" && !TILE_HAS_DEEPDIVE && !TILE_HAS_BUOY && !TILE_HAS_FISHTRAP;
/* Currently iterating unit is able to build bases on this tile? */
const UNIT_CAN_HIDEOUT = CAN_TILE_HIDEOUT && HIDEOUT_TECH && utype_has_flag(ptype,UTYF_FOOTSOLDIER);
- const UNIT_CAN_FORT = CAN_TILE_FORT && FORT_TECH && (worker_type || infra_type || (ptype['name'] == "Legion" && client_rules_flag[CRF_LEGION_WORK]) || (ptype['name'] == "Marines" && client_rules_flag[CRF_MARINE_BASES]));
- const UNIT_CAN_FORTRESS = CAN_TILE_FORTRESS && FORTRESS_TECH && (worker_type || infra_type || (ptype['name'] == "Legion" && client_rules_flag[CRF_LEGION_WORK]));
+ const UNIT_CAN_FORT = CAN_TILE_FORT && FORT_TECH && (worker_type || infra_type || (ptype['name'] == "Legion" && client_rules_flag[CRF_LEGION_WORK]) || (ptype['name'] == "Marines" && client_rules_flag[CRF_MARINE_BASES])) && ptype['name'] != "Trawler";
+ const UNIT_CAN_FORTRESS = CAN_TILE_FORTRESS && FORTRESS_TECH && (worker_type || infra_type || (ptype['name'] == "Legion" && client_rules_flag[CRF_LEGION_WORK])) && ptype['name'] != "Trawler";
const UNIT_CAN_NAVALBASE = CAN_TILE_NAVALBASE && NAVALBASE_TECH && (worker_type || infra_type || (ptype['name'] == "Legion" && client_rules_flag[CRF_LEGION_WORK])) && can_build_naval_base(punit,ptile);
const UNIT_CAN_CASTLE = CAN_TILE_CASTLE && CASTLE_TECH && (worker_type || infra_type);
const UNIT_CAN_BUNKER = CAN_TILE_BUNKER && BUNKER_TECH && (worker_type || infra_type);
const UNIT_CAN_AIRBASE = CAN_TILE_AIRBASE && AIRBASE_TECH && (worker_type || infra_type || (ptype['name'] == "Marines" && client_rules_flag[CRF_MARINE_BASES])) && ptype['name'] != "Settlers";
const UNIT_CAN_BUOY = CAN_TILE_BUOY && BUOY_TECH && (worker_type || infra_type) && ptype['name'] != "Settlers";
+ const UNIT_CAN_FISHTRAP = CAN_TILE_FISHTRAP && FISHTRAP_TECH && (worker_type || infra_type);
const UNIT_CAN_RADAR = CAN_TILE_RADAR && RADAR_TECH && (worker_type || infra_type) && ptype['name'] != "Settlers";
+ const UNIT_CAN_DEEPDIVE = CAN_TILE_DEEPDIVE && ptype['name'] == "Missile Submarine";
// ******************************************************************************************************************* ***
if (UNIT_CAN_HIDEOUT) {
unit_actions["hideout"] = {name: "Hideout (shift-H)"}; $("#order_hideout").show();
+ } else if (UNIT_CAN_DEEPDIVE) {
+ unit_actions["Dive Deep"] = {name: "Dive Deep (ctrl-D)"}; $("#order_deepdive").show();
}
//--
if (UNIT_CAN_FORT) {
- unit_actions["fortress"] = {name: "Build Fort (shift-F)"}; $("#order_fortress").show();
- $("#order_fortress").prop("title", "Build Fort (shift-F)");
+ unit_actions["fort"] = {name: "Build Fort (shift-F)"}; $("#order_fort").show();
+ $("#order_fort").prop("title", "Build Fort (shift-F)");
} else if (UNIT_CAN_FORTRESS) { // Fortress over Bunker allowed, to remove it. (Bunkers are pillage-proof)
if (TILE_HAS_BUNKER) {
unit_actions["fortress"] = {name: "Remove Bunker (shift-F)"};
@@ -1676,15 +1753,22 @@ function update_unit_order_commands()
}
$("#order_fortress").show();
} else if (UNIT_CAN_CASTLE) {
- unit_actions["fortress"] = {name: "Build Castle (shift-F)"}; $("#order_fortress").show();
- $("#order_fortress").prop("title", "Build Castle (shift-F)");
+ unit_actions["castle"] = {name: "Build Castle (shift-F)"}; $("#order_castle").show();
+ $("#order_castle").prop("title", "Build Castle (shift-F)");
} else if (UNIT_CAN_BUNKER) {
- unit_actions["fortress"] = {name: "Build Bunker (shift-F)"}; $("#order_fortress").show();
- $("#order_fortress").prop("title", "Build Bunker (shift-F)");
+ unit_actions["bunker"] = {name: "Build Bunker (shift-F)"}; $("#order_bunker").show();
+ $("#order_bunker").prop("title", "Build Bunker (shift-F)");
} else if (UNIT_CAN_BUOY) {
- unit_actions["fortress"] = {name: "Lay Buoy (shift-F)"}; $("#order_buoy").show();
+ unit_actions["buoy"] = {name: "Lay Buoy (shift-F)"}; $("#order_buoy").show();
}
//--
+ if (UNIT_CAN_FISHTRAP) { // half-true prequalifier for ability to fishtrap... check the rest here:
+ if (!is_extra_adjacent(ptile, EXTRA_FISHTRAP, true /* true == cadjacent */)) {
+ if (is_extra_adjacent(ptile, EXTRA_FISH, false /* false == adjacent */)) {
+ unit_actions["fishtrap"] = {name: "Lay Fishtrap (I)"}; $("#order_fishtrap").show();
+ }
+ }
+ }
if (UNIT_CAN_NAVALBASE) {
unit_actions["navalbase"] = {name: "Naval Base (shift-N)"}; $("#order_navalbase").show();
}
@@ -1737,7 +1821,8 @@ function update_unit_order_commands()
unit_actions["road"] = {name: "Road (R)"};
} else $("#order_road").hide();
}
- } //---------------------------------------------------------------------------------------------------
+ }
+ //---------------------------------------------------------------------------------------------------
// Figure out default of whether pillage is legal and show it, before applying special rules later
@@ -1779,26 +1864,31 @@ function update_unit_order_commands()
if (!tile_has_extra(ptile, EXTRA_ROAD)) {
if ( !client_rules_flag[CRF_SEABRIDGE] || !tile_has_extra(ptile, EXTRA_SEABRIDGE)) {
- $("#order_road").show();
- $("#order_railroad").hide();
+ if (is_ocean_tile(ptile)) { // an ocean tile with no sea bridge extra or ruleset: can't build roads
+ $("#order_road").hide();
+ $("#order_railroad").hide();
+ } else {
+ $("#order_road").show();
+ $("#order_railroad").hide();
+ }
if (!(tile_has_extra(ptile, EXTRA_RIVER) && !tech_known('Bridge Building'))) {
unit_actions["road"] = {name: "Road (R)"};
}
}
- } else if (tech_known('Railroad')
+ }
+ if (tech_known('Railroad')
&& (tile_has_extra(ptile, EXTRA_ROAD) || (client_rules_flag[CRF_SEABRIDGE] && tile_has_extra(ptile, EXTRA_SEABRIDGE)))
&& !tile_has_extra(ptile, EXTRA_RAIL)) {
$("#order_road").hide();
$("#order_railroad").show();
unit_actions['railroad'] = {name: "Railroad (R)"};
- } else if (can_build_maglev(punit, ptile)) {
- $("#order_road").hide();
- $("#order_railroad").hide();
+ }
+ if (can_build_maglev(punit, ptile)) {
$("#order_maglev").show();
- unit_actions['maglev'] = {name: "MagLev (R)"};
- } else {
- $("#order_road").hide();
- $("#order_railroad").hide();
+ var maglev_hotkey = "";
+ if ($("#order_road").is(":hidden") && $("#order_railroad").is(":hidden"))
+ maglev_hotkey = " (R)";
+ unit_actions['maglev'] = {name: "MagLev"+maglev_hotkey};
}
if (can_build_sea_bridge(punit, ptile)) {
unit_actions["road"] = {name: "Sea Bridge (R)"};
@@ -1949,23 +2039,25 @@ function update_unit_order_commands()
}
if (pcity != null && city_has_building(pcity, improvement_id_by_name(B_AIRPORT_NAME))) {
- if (pcity["airlift"]>0 && punit['movesleft']>0) {
+ const can_airlift = unit_can_do_action(punit, ACTION_AIRLIFT);
+ if (pcity["airlift"]>0 && punit['movesleft']>0 && can_airlift) {
unit_actions["airlift"] = {name: "Airlift (shift-L)"};
$("#order_airlift").show();
- //$("#order_airlift_disabled").hide();
} else {
- //$("#order_airlift").hide();
- $("#order_airlift_disabled").show();
+ if (can_airlift) $("#order_airlift_disabled").show();
}
}
// Upgrade unit: 1. Check if possible, 2. Check if upgrade unit can itself be upgraded. 3. Calculate upgrade cost. 4. Display orders with cost included.
- if (pcity != null && ptype != null
- && unit_types[ptype['obsoleted_by']]
- && (can_player_build_unit_direct(client.conn.playing, unit_types[ptype['obsoleted_by']])
- // handle the case of "can updade to type after next"; e.g., warriors to musketeers without having feudalism:
- || can_player_build_unit_direct(client.conn.playing, unit_types[unit_types[ptype['obsoleted_by']]['obsoleted_by']]))
- ) {
+ if (ptype != null &&
+ ( // unit is in a city and player is allowed to build the type to which it obsoletes: this means the unit can upgrade:
+ (pcity != null && unit_types[ptype['obsoleted_by']] && can_player_build_unit_direct(client.conn.playing, unit_types[ptype['obsoleted_by']]))
+ || // the case where it "can upgrade to type after next"; e.g., warriors to musketeers without having feudalism:
+ (pcity != null && ptype.obsoleted_by < getLength(unit_types) && can_player_build_unit_direct(client.conn.playing, unit_types[unit_types[ptype['obsoleted_by']]['obsoleted_by']]))
+ || // MP2D Workers can upgrade to Workers II anywhere at all
+ (client_rules_flag[CRF_MP2_D] && ptype.name == "Workers" && can_player_build_unit_direct(client.conn.playing, unit_types[ptype['obsoleted_by']]) && !tech_known("Explosives"))
+ )
+ ) {
//console.log(ptype['name']+" is allowed AT LEAST ONE upgrade. Beginning loop to check for higher upgrades.");
var upgrade_type = unit_types[ptype['obsoleted_by']];
@@ -2013,6 +2105,9 @@ function update_unit_order_commands()
if (ptype.name == 'Alpine Troops' || ptype.name == 'Riflemen') {
upgrade_cost = 4;
}
+ else if (upgrade_name == "Workers II" || upgrade_name == "Diplomat") {
+ upgrade_cost = "free";
+ }
}
/* *********************************************************************************************** */
@@ -2060,7 +2155,7 @@ function update_unit_order_commands()
// Deboard transport ----------------------------------------
if (unit_can_do_action(punit, ACTION_TRANSPORT_DEBOARD)) {
if (punit.transported_by) {
- if (unit_can_do_unload(punit)) {
+ if (unit_can_deboard(punit)) {
let ttitle = "Deboard from "+unit_type(units[punit.transported_by]).name
+ " (shift-T)";
unit_actions["unit_deboard"] = {name: ttitle};
@@ -2104,7 +2199,7 @@ function update_unit_order_commands()
for (var r = 0; r < units_on_tile.length; r++) {
let tunit = units_on_tile[r];
if (tunit['transported'] && tunit['transported_by'] == punit.id) {
- if (unit_can_do_unload(tunit)) {
+ if (unit_can_deboard(tunit)) {
unloadable ++;
tcandidate = tunit.id;
if (utype_has_flag(unit_types[tunit['type']], UTYF_MARINES)) show_cargo = true;
@@ -2139,6 +2234,25 @@ function update_unit_order_commands()
Auto-attack, UI modes, jump to last screen focus, etc.
*/
+ /* FORCED EXCLUSION RULES */
+ // TRAWLERS, they can't do lots of things that Workers can.
+ if ((client_rules_flag[CRF_MP2_D]) && ptype['name'] == "Trawler") {
+ $("#order_irrigate").hide(); delete unit_actions["irrigation"];
+ $("#order_build_farmland").hide(); // ""
+ $("#order_mine").hide(); delete unit_actions["mine"];
+ $("#order_oil_well").hide(); // ""
+ $("#order_plant_forest").hide(); // ""
+ $("#order_make_swamp").hide(); // ""
+ $("#order_forest_remove").hide(); delete unit_actions["forest"];
+ $("#order_radar").hide(); delete unit_actions["airbase"];
+ $("#order_airbase").hide() // "
+ if (unit_actions["fortress"] && unit_actions["fortress"]["name"] != "Lay Buoy (shift-F)") {
+ delete unit_actions["fortress"];
+ $("#order_fortress").hide(); // ""
+ }
+ $("#order_navalbase").hide(); // ""
+ }
+
var num_tile_units = tile_units(ptile);
if (num_tile_units != null) {
if (num_tile_units.length >= 2 && !touch_device) { // Touch devices have no buttons or keys to issue multiple orders
@@ -2399,6 +2513,7 @@ function set_unit_focus(punit)
} else {
current_focus[0] = punit;
action_selection_next_in_focus(IDENTITY_NUMBER_ZERO);
+ if (renderer == RENDERER_WEBGL) update_unit_position(index_to_tile(punit['tile']));
}
if (punit) warcalc_set_default_vals(punit);
@@ -2426,6 +2541,10 @@ function click_unit_in_panel(e, punit)
}
}
+ // though doing the exact same thing as single-click, shift-click was losing the other units in the panel, so
+ // try to emulate everything else it does, as a test to get those units displayed in the panel even though
+ // not in focus:
+ if (renderer == RENDERER_WEBGL) update_unit_position ( index_to_tile(punit['tile']));
auto_center_on_focus_unit();
update_active_units_dialog(); //previously only doing this but it lost unselected units in the panel
@@ -2451,11 +2570,13 @@ function set_unit_focus_and_redraw(punit)
if (punit == null) {
current_focus = [];
unit_may_have_lost_focus();
+ if (renderer == RENDERER_WEBGL) webgl_clear_unit_focus();
} else {
current_focus[0] = punit;
unit_may_have_lost_focus();
action_selection_next_in_focus(IDENTITY_NUMBER_ZERO);
warcalc_set_default_vals(punit); // warcalc default vals as last clicked units
+ if (renderer == RENDERER_WEBGL) update_unit_position(index_to_tile(punit['tile']));
}
//shift-spacebar to return to last location:
@@ -2509,6 +2630,7 @@ function auto_center_on_focus_unit()
if (ptile != null && auto_center_on_unit) {
center_tile_mapcanvas(ptile);
+ update_unit_position(ptile);
}
}
@@ -2637,6 +2759,10 @@ function order_wants_direction(order, act_id, ptile) {
/* Always illegal to perform to a target on a neighbor tile. */
return false;
}
+ // FIXME: The 2 cases below fell through cracks and returned false, so now we hack
+ // them in. (These actions would work sans hack, if they had min_distance == 1)
+ if (act_id = ACTION_EXPEL_UNIT) return true;
+ if (act_id = ACTION_CAPTURE_UNITS) return true;
/* FIXME: allied units and cities shouldn't always make actions be
* performed from the neighbor tile. */
@@ -2747,13 +2873,20 @@ function paste_tile_target_for_prod(canvas_x, canvas_y)
**************************************************************************/
function worked_tile_click(ptile)
{
+ if (observing) return;
+
var work_city_id = ptile['worked'];
/* This is how selector_city gets set, when you click on a worked tile,
* the city working it becomes the new selector_city: */
- if (work_city_id) selector_city = work_city_id;
+ if (work_city_id) {
+ // Abort if the city owning the tile is foreign:
+ if (cities[work_city_id].owner != client.conn.playing.playerno) return;
+ // Sets operative city when clicking any new tiles to work or unwork:
+ selector_city = work_city_id;
+ }
- // Unworked tile and no selector_city = no action can be specified here.
+ // Unworked tile and no selector_city = no action can be specified here:
if (!selector_city) return;
draw_city_output = true; // failsafe insurance: this mode should be on
@@ -2761,15 +2894,29 @@ function worked_tile_click(ptile)
var packet = null;
if (ptile['worked'] != 0) {
+ // Disallow attempting specialist if clicking someone else's worked tile:
+ if (ptile.owner != client.conn.playing.playerno
+ && ptile.owner != UNCLAIMED_LAND) {
+ play_sound("click_illegal.ogg");
+ return;
+ }
packet = {"pid" : packet_city_make_specialist,
"city_id" : work_city_id,
- "tile_id" : ptile['index']};
+ "tile_id" : ptile['index'],
+ "specialist_to": -1
+ };
} else {
+ // Disallow attempting to work a tile not in the city's workable map:
+ if (!city_map_includes_tile(ptile, cities[selector_city])) {
+ play_sound("click_illegal.ogg");
+ return;
+ }
packet = {"pid" : packet_city_make_worker,
"city_id" : selector_city,
"tile_id" : ptile['index']};
}
send_request(JSON.stringify(packet));
+ //play_sound("click_legal.ogg");
}
/**************************************************************************
@@ -2837,6 +2984,7 @@ function do_map_click(ptile, qtype, first_time_called)
if (goto_active /*&& !touch_device*/) { //(allow clicking same tile when giving a Nuke order.)
deactivate_goto(false);
}
+ if (renderer == RENDERER_2DCANVAS) {
if (!mouse_click_mod_key['shiftKey']) { // normal left-click
/* CONDITIONS FOR SHOWING A CONTEXT MENU:
1.Automatic context menu when clicking unit, if 'unit_click_menu' user PREF is on.
@@ -2861,6 +3009,7 @@ function do_map_click(ptile, qtype, first_time_called)
came_from_context_menu = true;
}
}
+ }
} else if (!mouse_click_mod_key['shiftKey'] && unit_click_menu
&& !should_ask_server_for_actions(current_focus[0])) {
// 3D handling of above. TO DO: test/integrate same 2D functionality above for 3D if appropriate
@@ -2964,10 +3113,12 @@ function do_map_click(ptile, qtype, first_time_called)
if ( Math.abs(tile_dx)<=1 && Math.abs(tile_dy) <=1 // adjacent
&& goto_last_action == ACTION_COUNT // not an appended Go...And command
&& !connect_active // not in connect mode to make multiple roads/irrigation
- && (true_goto_path_length <= 1) // don't override path>=2 which has better legal way to get to adjacent tile
- ) // "illegal" adjacent goto attempts render goto_path.length == undefined (true_goto_path_length will then be 0)
+ && (true_goto_path_length <= 1) // don't override path>=2 which has better legal way to get to adjacent tile. "illegal" adjacent
+ // goto attemps render goto_path.length == undefined (true_goto_path_length will then be 0)
+ && !mouse_click_mod_key['shiftKey'] // shift gives player a way to circumvent override; this allows a delayed GOTO if there's UWT
+ )
{
- console.log("GO TO overridden because adjacent tile.")
+ console.log("GOTO changed to MOVE because adjacent tile.\nShift-click tile to force GOTO instead of MOVE.")
switch (tile_dy)
{
case 0: // neither north nor south:
@@ -3170,10 +3321,12 @@ function do_map_click(ptile, qtype, first_time_called)
} else if (!has_movesleft_warning_been_shown) {
has_movesleft_warning_been_shown = true;
var ptype = unit_type(punit);
+ var has = is_word_plural(ptype['name']) ? "have" : "has";
message_log.update({
event: E_BAD_COMMAND,
- message: (is_longturn() ? ptype['name'] + " has no moves left."
- : ptype['name'] + " has no moves left. Press turn done for the next turn.")
+ message: ( delayed_goto_active ? (ptype['name'] + " will move at turn change.") :
+ (is_longturn() ? ptype['name'] + " " + has + " no moves left."
+ : ptype['name'] + " " + has + " no moves left. Press turn done for the next turn."))
});
}
@@ -3251,7 +3404,11 @@ function do_map_click(ptile, qtype, first_time_called)
set_unit_focus_and_redraw(sunits[0]);
if (city_click_goto_cooldown(ptile) && !should_ask_server_for_actions(sunits[0])) {
// don't show contextmenu if in the cooldown period for a double tap GOTO
- $("#canvas").contextMenu();
+ if (renderer == RENDERER_2DCANVAS) {
+ $("#canvas").contextMenu();
+ } else { //3D handling can potentially be made different later
+ $("#canvas_div").contextMenu();
+ }
}
return; // move the commented-out return from below up here
} else if (!goto_active) { //if GOTO active then the click is a move command, not a show city command
@@ -3425,7 +3582,7 @@ function global_keyboard_listener(ev)
}
civclient_handle_key(keyboard_key, ev.keyCode, ev['ctrlKey'], ev['altKey'], ev['shiftKey'], ev);
- $("#canvas").contextMenu('hide');
+ if (renderer == RENDERER_2DCANVAS) $("#canvas").contextMenu('hide');
}
/**************************************************************************
@@ -3456,6 +3613,13 @@ function civclient_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_even
}
break;
+ case 'F':
+ if (alt && !ctrl && !shift) {
+ the_event.preventDefault();
+ console_filter_dialog();
+ }
+ break;
+
case 'G':
if (alt && !ctrl && !shift) {
the_event.preventDefault(); // override possible browser shortcut
@@ -3493,9 +3657,14 @@ function civclient_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_even
the_event.preventDefault(); // override possible browser shortcut
quicksave();
}
- else if (alt) {
+ else if (!ctrl && !shift && alt) {
the_event.preventDefault(); // override possible browser shortcut
show_fullscreen_window();
+ } else if (ctrl && shift && alt) {
+ if (is_supercow() || was_supercow) {
+ flip_supercow();
+ was_supercow = true;
+ }
}
break;
@@ -3635,7 +3804,10 @@ map_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_event)
// CTRL-ALT-D user forced disconnect
the_event.preventDefault(); // override possible browser shortcut
clinet_disconnect_from_server();
- } else if (!(alt || ctrl)) {
+ } else if (ctrl && !shift && !alt) {
+ the_event.preventDefault(); // override possible browser shortcut
+ key_unit_dive();
+ } else if (!(alt || ctrl || shift)) {
key_unit_action_select();
}
break;
@@ -3644,10 +3816,10 @@ map_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_event)
if (shift && !ctrl && !alt) {
key_unit_airbase();
}
- else if (ctrl && shift) {
+ else if (ctrl && shift && !alt) {
the_event.preventDefault(); // override possible browser shortcut
// show/hide the dev/debug messages sent from server to supercow users
- $(".e_log_error").toggle();
+ toggle_error_logging();
}
else if (ctrl && !shift && !alt) {
the_event.preventDefault(); // override possible browser shortcut
@@ -3662,9 +3834,13 @@ map_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_event)
add_client_message("Alt-Shift-F. Focus-lock set to "+(focuslock ? "ON." : "OFF."));
simpleStorage.set('focuslock', focuslock);
}
- else if (shift) {
+ else if (shift && !alt && !ctrl) {
key_unit_fortress();
- } else {
+ } else if (alt && !ctrl && !shift) {
+ the_event.preventDefault();
+ console_filter_dialog();
+ }
+ else {
key_unit_fortify();
}
break;
@@ -4032,7 +4208,11 @@ map_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_event)
came_from_context_menu = false;
/* Abort any context menu blocking. */
context_menu_active = true;
- $("#canvas").contextMenu(true);
+ if (renderer == RENDERER_2DCANVAS) {
+ $("#canvas").contextMenu(true);
+ } else {
+ $("#canvas_div").contextMenu(true);
+ }
/* Abort target tile selection. */
paradrop_active = false;
@@ -4043,15 +4223,17 @@ map_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_event)
// shift-space, return to previous map position
// space, will clear selection and goto.
case 32:
- if (shift) auto_center_last_location();
- else if (ctrl && alt) {
+ if (shift &&!ctrl && !alt) auto_center_last_location();
+ else if (ctrl && alt && !shift) {
the_event.preventDefault();
key_paste_link_under_cursor();
- } else {
+ }
+ else {
save_last_unit_focus();
current_focus = [];
unit_may_have_lost_focus();
+ if (renderer == RENDERER_WEBGL) webgl_clear_unit_focus();
clear_all_modes();
$("#canvas_div").css("cursor", "default");
goto_request_map = {};
@@ -4078,10 +4260,37 @@ map_handle_key(keyboard_key, key_code, ctrl, alt, shift, the_event)
break;
case 107:
+ //zoom in
+ if (renderer == RENDERER_WEBGL) {
+ let new_camera_dy = camera_dy - 60;
+ let new_camera_dx = camera_dx - 45;
+ let new_camera_dz = camera_dz - 45;
+ if (new_camera_dy < 350 || new_camera_dy > 1200) {
+ return;
+ } else {
+ camera_dx = new_camera_dx;
+ camera_dy = new_camera_dy;
+ camera_dz = new_camera_dz;
+ }
+ camera_look_at(camera_current_x, camera_current_y, camera_current_z);
+ }
break;
case 109:
//zoom out
+ if (renderer == RENDERER_WEBGL) {
+ let new_camera_dy = camera_dy + 60;
+ let new_camera_dx = camera_dx + 45;
+ let new_camera_dz = camera_dz + 45;
+ if (new_camera_dy < 350 || new_camera_dy > 1200) {
+ return;
+ } else {
+ camera_dx = new_camera_dx;
+ camera_dy = new_camera_dy;
+ camera_dz = new_camera_dz;
+ }
+ camera_look_at(camera_current_x, camera_current_y, camera_current_z);
+ }
break;
}
@@ -4148,7 +4357,7 @@ function handle_context_menu_callback(key)
break;
case "maglev":
- key_unit_road();
+ key_unit_maglev();
break;
case "quay":
@@ -4204,6 +4413,10 @@ function handle_context_menu_callback(key)
key_unit_airbase();
break;
+ case "fishtrap":
+ key_unit_fishtrap();
+ break;
+
case "transform":
key_unit_transform();
break;
@@ -4813,6 +5026,11 @@ function key_unit_load(scoop_units)
boarded ++;
boarded_list += " [`" + freemoji_name_from_universal(unit_type(punit).name) + "`] ";
boarded_on[punit.id] = tunit.id;
+ /* This line and the and the one under the commented out line below seem to fix an edge
+ case that happens when giving a non-boardable cargo unit an order to board wherein
+ boarded_on[punit.id] is undefined; we keep track of the LAST transport that was
+ successfully boarded on by having reached this code. */
+ var last_successful_transport = boarded_on[punit.id];
}
}
}
@@ -4820,7 +5038,9 @@ function key_unit_load(scoop_units)
add_client_message("Boarding " + pluralize("unit", boarded)
+ ". " + boarded_list
+ (tunit ? "→ [`"
- + freemoji_name_from_universal(unit_type(units[boarded_on[punit.id]]).name) +"`] " : "") );
+// + freemoji_name_from_universal(unit_type(units[boarded_on[punit.id]]).name) +"`] " : "") );
+ + freemoji_name_from_universal(unit_type(units[last_successful_transport]).name) +"`] " : "") );
+
setTimeout(update_active_units_dialog, update_focus_delay);
}
// Don't advance focus if more than one dialog open, it would reset our focus units
@@ -4835,26 +5055,51 @@ function key_unit_load(scoop_units)
function key_unit_deboard()
{
var funits = get_units_in_focus();
- var unloaded = 0;
+ var deboarded = 0;
+ var deboarders_ready=0;
+ var untransported=0;
+ var unable_to_deboard=0;
+ var unable_list = "";
var deboard_list = "";
for (var i = 0; i < funits.length; i++) {
var punit = funits[i];
- if (unit_can_do_unload(punit)) {
+ if (unit_can_deboard(punit)) {
request_unit_do_action(ACTION_TRANSPORT_DEBOARD, punit['id'], punit['transported_by']);
- unloaded++;
+ deboarded++;
+ deboarders_ready += punit.movesleft;
deboard_list += " [`" + freemoji_name_from_universal(unit_type(punit).name) + "`]";
+ } else if (punit.transported) {
+ unable_to_deboard++;
+ unable_list += " [`" + freemoji_name_from_universal(unit_type(punit).name) + "`]";
+ } else {
+ untransported++;
}
}
deactivate_goto(false);
- if (unloaded) {
- add_client_message("Deboarding "+unloaded+" unit"+ (unloaded>1 ? "s. " : ". ")
+ // Indicate units that we (strongly believe) the server will let us successfully deboard:
+ if (deboarded) {
+ add_client_message("Deboarding "+deboarded+" unit"+ (deboarded>1 ? "s: " : ": ")
+deboard_list);
- setTimeout(function() {advance_unit_focus(false)}, update_focus_delay);
+ }
+ // Indicate units that we know can't deboard (because we didn't ask the server):
+ if (unable_to_deboard) {
+ add_client_message("Unable to deboard "+unable_to_deboard+" unit"+ (unable_to_deboard>1 ? "s: " : ": ")
+ +unable_list, "e_unit_illegal_action");
+ }
+ // Deboard command given to untransported unit AND no one else was transported/deboarding:
+ if (untransported && !deboarded && !unable_to_deboard) {
+ add_client_message("Only transported units can deboard.", "e_unit_illegal_action");
+ }
+ /* If we deboarded units AND none have moves left AND no one was disallowed to deboard,
+ advance unit focus: */
+ if (deboarded && deboarders_ready==0 && unable_to_deboard==0) {
+ setTimeout(function() {advance_unit_focus(false)}, update_focus_delay+250);
}
}
+
/*******************************************************************************
V3: (1) If Transport is selected unit: try to "Unload cargo"
(2) If Passenger is selected unit AND couldn't unload cargo, deboard it.
@@ -4867,8 +5112,10 @@ function key_unit_deboard()
*******************************************************************************/
function key_unit_unload()
{
- var unloaded =0; // counter for unloaded units
- var deboarded=0; // counter for deboarded units
+ var unloaded =0; // counter for unloaded units
+ var deboarded=0; // counter for deboarded units
+ var unloaded_cargo_ready=0; // unloaded cargo that can move
+ var deboarders_ready=0; // deboarded cargo that can move
var unload_list = "";
var deboard_list = "";
@@ -4894,16 +5141,17 @@ function key_unit_unload()
let this_unit_unloaded = 0;
// Selected Unit is: selected and has transport capacity. Therefore, check
// each unit on tile to see if it's on this transporter, and unload it.
- if (sunits[s] && stype.transport_capacity>0) { //console.log(" sunit[i] has a transport capacity. attempting unload cargo");
+ if (sunits[s] && stype.transport_capacity>0) { //console.log(" sunits[i] has a transport capacity. attempting unload cargo");
for (var i = 0; i < units_on_tile.length; i++) {
var punit = units_on_tile[i]; //console.log("punit["+i+"]");
// If iterated tile unit is being transported by selected unit, then UNLOAD it!
if (punit['transported'] && punit['transported_by'] == sunits[s]['id']) {
- if (unit_can_do_unload(punit)) {
+ if (unit_can_deboard(punit)) {
request_unit_do_action(ACTION_TRANSPORT_UNLOAD, punit['transported_by'], punit['id']);
unloaded++; // Total number of all unloadings from all selected units.
this_unit_unloaded++; // Number of unloadings just from this transport.
+ unloaded_cargo_ready += punit.movesleft;
unload_list += " [`" + freemoji_name_from_universal(unit_type(punit).name) + "`]"
}
}
@@ -4914,9 +5162,10 @@ function key_unit_unload()
// command, but we know what you wanted. So fine, we'll let you do it.
if (sunits[s] && this_unit_unloaded==0 && sunits[s]['transported']) {
//console.log(" sunit[i] is cargo)");
- if (unit_can_do_unload(sunits[s])) {
+ if (unit_can_deboard(sunits[s])) {
request_unit_do_action(ACTION_TRANSPORT_DEBOARD, sunits[s]['id'], sunits[s]['transported_by']);
deboarded++;
+ deboarders_ready += sunits[s].movesleft;
deboard_list += " [`" + freemoji_name_from_universal(unit_type(sunits[s]).name) + "`] "
}
}
@@ -4927,24 +5176,37 @@ function key_unit_unload()
// Show # of units who successfully unloaded and/or deboarded:
if (unloaded) {
- add_client_message("Unloading "+unloaded+" unit"+ (unloaded>1 ? "s. " : ". ")
+ add_client_message("Unloading "+unloaded+" unit"+ (unloaded>1 ? "s: " : ": ")
+ unload_list);
}
if (deboarded) {
- add_client_message("Deboarding "+deboarded+" unit"+ (deboarded>1 ? "s. " : ". ")
+ add_client_message("Deboarding "+deboarded+" unit"+ (deboarded>1 ? "s: " : ": ")
+ deboard_list );
}
else if (!unloaded && !deboarded) {
if (sunits.length == 1) {
- add_client_message(unit_type(sunits[0]).rule_name+" couldn't unload cargo here.")
+ add_client_message(unit_type(sunits[0]).rule_name+" couldn't unload here.", "e_unit_illegal_action")
}
- else if (!deboarded) {
- add_client_message("Can't unload cargo here.");
+ else {
+ add_client_message("Can't unload cargo here.", "e_unit_illegal_action");
}
return; // avoid advancing unit focus after failed command.
}
- setTimeout(function() {advance_unit_focus(false)}, update_focus_delay);
+ /* OPTIONAL heuristics:
+ 1. Don't advance focus on an unloading transport that has moves
+ left if it's unloading cargo who doesn't have moves left: */
+ if (unloaded && sunit.movesleft && !unloaded_cargo_ready) {
+ return;
+ }
+ /* 2. If we deboarded only (without an unloader-transport also
+ active), AND any deboarders had move left, don't advance focus: */
+ if (deboarded && !unloaded && deboarders_ready) {
+ return;
+ }
+ /* 3. Give a little extra time for server to unsentry unloaded cargo so we
+ will advance focus to it */
+ setTimeout(function() {advance_unit_focus(false)}, update_focus_delay+250);
}
/**************************************************************************
@@ -5452,6 +5714,25 @@ function key_unit_hideout()
setTimeout(update_unit_focus, update_focus_delay);
}
+/**************************************************************************
+ Tell the units in focus to dive deep (build a "deep depth" "base")
+**************************************************************************/
+function key_unit_dive()
+{
+ const deepdive_rules = client_rules_flag[CRF_MP2_D];
+ if (!deepdive_rules) return;
+
+ var funits = get_units_in_focus();
+ for (var i = 0; i < funits.length; i++) {
+ var punit = funits[i];
+ request_new_unit_activity(punit, ACTIVITY_BASE, EXTRA_DEEPDIVE);
+ // Focused unit got orders, make sure not on waiting_list now:
+ remove_unit_id_from_waiting_list(punit['id']);
+ }
+ deactivate_goto(false);
+ setTimeout(update_unit_focus, update_focus_delay);
+}
+
/**************************************************************************
Tell the units in focus to build airbase (or Radar on top of it).
**************************************************************************/
@@ -5493,8 +5774,16 @@ function key_unit_irrigate()
var funits = get_units_in_focus();
for (var i = 0; i < funits.length; i++) {
var punit = funits[i];
- /* EXTRA_NONE -> server decides */
- request_new_unit_activity(punit, ACTIVITY_IRRIGATE, EXTRA_NONE);
+
+ /* MP2-D onward: "I" for irrigate, when done on ocean tiles, means,
+ to lay a Fishtrap: */
+ if (client_rules_flag[CRF_MP2_D] && is_ocean_tile(unit_tile(punit))) {
+ const punit = funits[i];
+ request_new_unit_activity(punit, ACTIVITY_BASE, EXTRA_FISHTRAP);
+ } else { // Normal irrigation of land;
+ /* EXTRA_NONE -> server decides */
+ request_new_unit_activity(punit, ACTIVITY_IRRIGATE, EXTRA_NONE);
+ }
// Focused unit got orders, make sure not on waiting_list now:
remove_unit_id_from_waiting_list(punit['id']);
}
@@ -5890,15 +6179,29 @@ function unit_can_vigil(punit)
{
// Hard coded for now until we get action enablers
var ptype = unit_type(punit);
+ var ptile = unit_tile(punit);
var name = ptype['name'];
var moves_used = (parseFloat(ptype['move_rate']) / parseFloat(SINGLE_MOVE))
- (parseFloat(punit['movesleft']) / parseFloat(SINGLE_MOVE));
+ var fuel = punit['fuel'];
// For now Vigil is only for these server settings:
if (server_settings["autoattack"]["val"] != true) return false;
if (server_settings["autoattack_style"]["val"] == 0) return false;
switch (name) {
+ case "Catapult":
+ case "Ballista":
+ case "Cannon":
+ case "Artillery":
+ case "Magnum Turret":
+ case "Howitzer":
+ if (client_rules_flag[CRF_MP2_D]) {
+ if (moves_used <= 0 && (tile_city(ptile) || does_tile_have_base(ptile))) {
+ return true;
+ }
+ }
+ break;
case "Fighter":
if (moves_used <= 2)
return true;
@@ -5911,10 +6214,14 @@ function unit_can_vigil(punit)
if (moves_used <= 3)
return true;
break;
+ case "Multi-Fighter":
+ if (fuel > 1)
+ return true;
+ break;
case "Stealth Fighter":
if (moves_used <= 4)
return true;
- break;
+ break;
}
return false;
}
@@ -5958,10 +6265,20 @@ function can_build_sea_bridge(punit, ptile)
**************************************************************************/
function can_build_maglev(punit, ptile)
{
+ const seabridge_rules = (typeof EXTRA_SEABRIDGE !== "undefined");
+
return ((typeof EXTRA_MAGLEV !== "undefined")
&& (punit != null && ptile != null)
&& (!tile_has_extra(ptile, EXTRA_MAGLEV))
- && (tile_has_extra(ptile, EXTRA_RAIL))
+
+ && (!client_rules_flag[CRF_MP2_D] // MP2D on requires road or seabridge under it
+ || (tile_has_extra(ptile, EXTRA_ROAD) || tile_has_extra(ptile, EXTRA_SEABRIDGE))
+ )
+
+ && (!is_ocean_tile(ptile)
+ || (seabridge_rules && tile_has_extra(ptile, EXTRA_SEABRIDGE))
+ )
+
&& (unit_can_do_action(punit, ACTION_ROAD))
&& tech_known('Superconductors')
);
@@ -6015,8 +6332,11 @@ function can_build_canal(punit, ptile)
// TODO: Deprecate all pre-MP2C logic to simplify to the superior solution
if (typeof EXTRA_CANAL === "undefined") return 0;
+ if (typeof EXTRA_WATERWAY === "undefined") return 0;
+
if (tile_terrain(ptile) == null) return 0;
- if (punit == null || ptile == null) return 0;
+ if (punit == null || ptile == null) return 0;
+ if (is_ocean_tile(ptile)) return 0;
var is_lowland = (tile_terrain(ptile)['name'] != 'Hills'
&& tile_terrain(ptile)['name'] != 'Mountains');
@@ -6050,6 +6370,7 @@ function can_build_canal(punit, ptile)
}
if ( !tile_has_extra(ptile, EXTRA_CANAL)
+ && !tile_has_extra(ptile, EXTRA_WATERWAY)
&& unit_can_do_action(punit, ACTION_ROAD)
&& is_lowland
&& water_near
@@ -6248,6 +6569,29 @@ function key_unit_naval_base()
setTimeout(update_unit_focus, update_focus_delay);
}
+/**************************************************************************
+ Tell the units in focus to build naval base. NOTE: This is not called by
+ a key_unit keypress "I" like other key_unit commands. But it gets called
+ by a context menu click or orders button click. If the "I" key is pressed
+ then key_unit_irrigate does the same code as this if it is on an ocean
+ tile.
+**************************************************************************/
+function key_unit_fishtrap()
+{
+ if (typeof EXTRA_FISHTRAP === "undefined") return;
+
+ var funits = get_units_in_focus();
+
+ for (var i = 0; i < funits.length; i++) {
+ const punit = funits[i];
+ request_new_unit_activity(punit, ACTIVITY_BASE, EXTRA_FISHTRAP);
+ remove_unit_id_from_waiting_list(punit['id']);
+ }
+
+ deactivate_goto(false);
+ setTimeout(update_unit_focus, update_focus_delay);
+}
+
/**************************************************************************
Tell the units in focus to build road or railroad.
**************************************************************************/
@@ -6291,7 +6635,32 @@ function key_unit_road()
deactivate_goto(false);
setTimeout(update_unit_focus, update_focus_delay);
}
+/**************************************************************************
+ Tell the units in focus to build maglev. Separated from key_unit_road
+ because it's possible to build MagLev without road/rail in some
+ rulesets.
+**************************************************************************/
+function key_unit_maglev()
+{
+ var funits = get_units_in_focus();
+ for (var i = 0; i < funits.length; i++) {
+ var punit = funits[i];
+ var ptile = index_to_tile(punit['tile']);
+ // On ocean, MagLev requires a SeaBridge (if ruleset has seabridge.)
+ if (is_ocean_tile(ptile)) {
+ if (typeof EXTRA_SEABRIDGE !== "undefined" && !tile_has_extra(ptile, EXTRA_SEABRIDGE)) {
+ return;
+ }
+ }
+ if (can_build_maglev(punit, ptile)) {
+ request_new_unit_activity(punit, ACTIVITY_GEN_ROAD, extras['Maglev']['id']);
+ remove_unit_id_from_waiting_list(punit['id']); // Unit received orders, don't ask orders later
+ }
+ }
+ deactivate_goto(false);
+ setTimeout(update_unit_focus, update_focus_delay);
+}
/**************************************************************************
Tell the units in focus to build a quay
@@ -6326,7 +6695,7 @@ function key_unit_homecity()
var pcity = tile_city(ptile);
if (pcity != null) {
- request_unit_do_action(ACTION_HOME_CITY, punit['id'], pcity['id']);
+ request_unit_do_action(ACTION_HOME_CITY, punit['id'], pcity['id'], -1);
$("#order_change_homecity").hide();
}
}
@@ -6460,7 +6829,6 @@ function request_new_unit_activity(punit, activity, target)
action_decision_clear_want(punit['id']);
var packet = {"pid" : packet_unit_change_activity, "unit_id" : punit['id'],
"activity" : activity, "target" : target };
- //if (DEBUG_LOG_PACKETS) console.log("Sending action request: "+JSON.stringify(packet));
send_request(JSON.stringify(packet));
}
@@ -6914,11 +7282,15 @@ function check_request_goto_path()
if (goto_active && current_focus.length > 0
&& (prev_mouse_x != mouse_x || prev_mouse_y != mouse_y || prev_goto_tile<=LAST_FORCED_CHECK)) {
- ptile = canvas_pos_to_tile(mouse_x, mouse_y);
+ if (renderer == RENDERER_2DCANVAS) {
+ ptile = canvas_pos_to_tile(mouse_x, mouse_y);
+ } else {
+ ptile = webgl_canvas_pos_to_tile(mouse_x, mouse_y);
+ }
if (ptile != null) {
if (ptile['tile'] != prev_goto_tile) {
- clear_goto_tiles(); // TO DO: goto tiles should not be in tiles[tild_id][goto_dir] which takes forever to clear, but their own array instead
+ clear_goto_tiles(); // TODO: goto tiles should not be in tiles[tild_id][goto_dir] which takes forever to clear, but their own array instead
/* Send request for goto_path to server. */
for (var i = 0; i < current_focus.length; i++) {
request_goto_path(current_focus[i]['id'], ptile['x'], ptile['y']);
@@ -6993,6 +7365,7 @@ function update_goto_path(goto_packet)
// Don't bother checking goto for same tile unit is on
if (ptile==goaltile) return;
+ if (renderer == RENDERER_2DCANVAS) {
for (var i = 0; i < goto_packet['dir'].length; i++) {
if (ptile == null) break;
var dir = goto_packet['dir'][i];
@@ -7005,6 +7378,9 @@ function update_goto_path(goto_packet)
ptile['goto_dir'] = dir;
ptile = mapstep(ptile, dir);
}
+ } else {
+ webgl_render_goto_line(ptile, goto_packet['dir']);
+ }
current_goto_turns = goto_packet['turns'];
@@ -7039,8 +7415,11 @@ function show_goto_path(goto_packet)
**************************************************************************/
function center_tile_mapcanvas(ptile)
{
-
- center_tile_mapcanvas_2d(ptile);
+ if (renderer == RENDERER_2DCANVAS) {
+ center_tile_mapcanvas_2d(ptile);
+ } else {
+ center_tile_mapcanvas_3d(ptile);
+ }
}
@@ -7049,7 +7428,13 @@ function center_tile_mapcanvas(ptile)
**************************************************************************/
function popit()
{
- var ptile = canvas_pos_to_tile(mouse_x, mouse_y);
+ var ptile;
+
+ if (renderer == RENDERER_2DCANVAS) {
+ ptile = canvas_pos_to_tile(mouse_x, mouse_y);
+ } else {
+ ptile = webgl_canvas_pos_to_tile(mouse_x, mouse_y);
+ }
if (ptile == null) return;
@@ -7081,11 +7466,18 @@ function popit_req(ptile, goto_only)
if (ptile == null) return;
+ let GM_supercow = is_supercow() && observing;
// copies tile string to clipboard for later pasting
// %%% instead of %% puts it in a format that will send the message privately
// to oneself
- copy_string_to_clipboard("%%%"+"tile"+ptile['index']+"~%");
+ if (GM_supercow) {
+ copy_string_to_clipboard("/label "+ptile.x+","+ptile.y+" ");
+ } else {
+ copy_string_to_clipboard("%%%"+"tile"+ptile['index']+"~%");
+ }
+
+/*
if (!goto_only && tile_get_known(ptile) == TILE_KNOWN_UNSEEN) {
//TODO: The 2 lines below should be removed after next FCW server restart
//comment made on 3 March 2020
@@ -7093,8 +7485,9 @@ function popit_req(ptile, goto_only)
return;
// Let server give us tile info. OPTIONAL TODO: reconstruct some more based on our
// own last known knowledge of the tile. Server only sends Terrain and that's it.
- } else if (!goto_only && tile_get_known(ptile) == TILE_UNKNOWN) {
+ } else*/ if (GM_supercow || (!goto_only && tile_get_known(ptile) == TILE_UNKNOWN)) {
show_dialog_message("Tile info", "Location: x:" + ptile['x'] + " y:" + ptile['y']);
+
return;
}
@@ -7478,8 +7871,7 @@ function update_active_units_dialog()
var fuel_left = (aunit['fuel']-1) + aunit['movesleft']/ptype['move_rate'];
var fuel_color = "";
var size_adj = mobile_mode ? "font-size:100%" : "font-size:100%;";
- if (aunit['movesleft']==0) fuel_color = ""; // no moves left, fuel indicator is dimmed down
- else if (fuel_left>2.001) fuel_color = ""; // more than 2 turns of fuel = blue skies ahead
+ if (fuel_left>2.001) fuel_color = ""; // more than 2 turns of fuel = blue skies ahead
else if (fuel_left>1.001) fuel_color = ""; // more than 1 turn of fuel = blue skies ahead
else if (fuel_left>0.85) fuel_color = ""; // full turn of fuel = green
else if (fuel_left>0.67) fuel_color = ""; // most moves = green-yellow
@@ -7489,13 +7881,12 @@ function update_active_units_dialog()
else if (fuel_left>0.50) fuel_color = "";
else fuel_color = "";
- if (aunit['movesleft']==0 && fuel_left<1.0001) { fuel_left="";} // no moves and exactly 1 or less fuel are special cases like airlift/refueling/etc where we don't need to show fuel
- else {
- if (mobile_mode) { // mobile version of text
- active_uinfo += " F:" + fuel_color + fuel_left.toFixed(fuel_left<1?2:1) + "";
- } else { // normal non-mobile text
- active_uinfo += " Fuel:" + fuel_color + fuel_left.toFixed(fuel_left<1?2:1) + "";
- }
+ if (mobile_mode) { // mobile version of text
+ active_uinfo += " F:" + fuel_color
+ + fuel_left.toFixed((fuel_left>0 && fuel_left<1)?2:1) + "";
+ } else { // normal non-mobile text
+ active_uinfo += " Fuel:" + fuel_color
+ + fuel_left.toFixed((fuel_left>0 && fuel_left<1)?2:1) + "";
}
}
active_uinfo += "";
@@ -7623,31 +8014,32 @@ function check_mouse_drag_unit(ptile)
/**************************************************************************
Pop-up at game start to enter fullscreen. We might use a browser but we
are a full-screen game, damn it!
+ Removed. Needs a do not ask again opton.
**************************************************************************/
-function popup_fullscreen_enter_game_dialog()
-{
+//function popup_fullscreen_enter_game_dialog()
+//{
// Don't show popup for small screen, we set enter it elsewhere.
- if (is_small_screen()) return;
+// if (is_small_screen()) return;
- id = "#fullscreen_dialog";
- remove_active_dialog(id); /* Reset dialog page. */
- $("#fullscreen_dialog").css("white-space","pre-wrap"); // allow \n to work.
- $("").appendTo("div#game_page");
-
- if (!is_small_screen())
- $(id).html("Enters Full Screen when you interact with game.
ALT-S toggles Full Screen. ESC exits full screen.");
- else
- $(id).html("Play in Full Screen Mode.");
-
- var buttons = { 'Yes!': function()
- { openFullscreen(); remove_active_dialog(id); },
- 'No': function() {remove_active_dialog(id);} };
- $(id).attr("title", "Full Immersion Mode");
- var dwidth = is_small_screen() ? "90%" : "480";
- $(id).dialog({bgiframe: true, modal: true, buttons: (buttons), height: "auto", width: dwidth});
- $(id).dialog('open'); $(id).dialog('widget').position({my:"center", at:"center", of:window})
- dialog_register(id);
-}
+// id = "#fullscreen_dialog";
+// remove_active_dialog(id); /* Reset dialog page. */
+// $("#fullscreen_dialog").css("white-space","pre-wrap"); // allow \n to work.
+// $("").appendTo("div#game_page");
+
+// if (!is_small_screen())
+// $(id).html("Enters Full Screen when you interact with game.
ALT-S toggles Full Screen. ESC exits full screen.");
+// else
+// $(id).html("Play in Full Screen Mode.");
+
+// var buttons = { 'Yes!': function()
+// { openFullscreen(); remove_active_dialog(id); },
+// 'No': function() {remove_active_dialog(id);} };
+// $(id).attr("title", "Full Immersion Mode");
+// var dwidth = is_small_screen() ? "90%" : "480";
+// $(id).dialog({bgiframe: true, modal: true, buttons: (buttons), height: "auto", width: dwidth});
+// $(id).dialog('open'); $(id).dialog('widget').position({my:"center", at:"center", of:window})
+// dialog_register(id);
+//}
/****************************************************************************
This function opens full screen mode.
****************************************************************************/
diff --git a/freeciv-web/src/main/webapp/javascript/diplomacy.js b/freeciv-web/src/main/webapp/javascript/diplomacy.js
index 999c5280f..0edb4793b 100644
--- a/freeciv-web/src/main/webapp/javascript/diplomacy.js
+++ b/freeciv-web/src/main/webapp/javascript/diplomacy.js
@@ -242,9 +242,41 @@ function client_diplomacy_clause_string(counterpart, giver, type, value)
var pplayer = players[giver];
var nation = nations[pplayer['nation']]['adjective'];
+ /* FIXME: server sends out giver and counterpart as the same player when
+ it offers a dipl_state clause to the counterpart (i.e., to the
+ receiver/non-giver). This hack fixes it. TODO: fix the server @
+ diplhand.c::handle_diplomacy_create_clause_req */
+ if (counterpart == giver && value == 1) {
+ counterpart = client.conn.playing.playerno;
+ }
+
var cb1 = players[counterpart].diplstates[giver]['has_reason_to_cancel'];
var cb2 = players[giver].diplstates[counterpart]['has_reason_to_cancel'];
const casus_belli = cb1 || cb2;
+ const giver_state = players[giver].diplstates[counterpart].state;
+ const counterpart_state = players[counterpart].diplstates[giver].state;
+ /* giver and counterpart diplstate should always be the same but the info
+ for one or the other may not be known. OR'ing them together always
+ results in the correct dipl_state: */
+ const dipl_state = giver_state | counterpart_state;
+ const new_clause_is_old_state =
+ (type == CLAUSE_CEASEFIRE && dipl_state == DS_CEASEFIRE)
+ || (type == CLAUSE_PEACE && dipl_state == DS_ARMISTICE)
+ || (type == CLAUSE_PEACE && dipl_state == DS_PEACE)
+ || (type == CLAUSE_ALLIANCE && dipl_state == DS_ALLIANCE);
+ const re_affirm = new_clause_is_old_state;
+
+ /* DEBUG
+ console.log("--------DIPL LANGUAGE DEBUG----------")
+ console.log("cprt, giver, type, value = %d,%d,%d,%d",counterpart,giver,type,value);
+ console.log("giver_state "+giver_state);
+ console.log("counterpart_state "+counterpart_state);
+ console.log("dipl_state "+dipl_state);
+ console.log("new_clause_is_old "+new_clause_is_old_state);
+ console.log("casus_belli "+casus_belli);
+ console.log("re_affirm "+re_affirm);
+ console.log("--------------------------------------")
+ */
switch (type) {
case CLAUSE_ADVANCE:
@@ -271,11 +303,23 @@ function client_diplomacy_clause_string(counterpart, giver, type, value)
case CLAUSE_SEAMAP:
return "The " + nation + " give their seamap";
case CLAUSE_CEASEFIRE:
- return casus_belli ? "The parties re-affirm cease-fire." : "The parties agree on a cease-fire";
+ if (re_affirm && casus_belli) return "The parties re-affirm cease-fire and clear casus belli.";
+ // The case below occurs when cease-fire will expire in <3 turns and a renewal is offered:
+ if (re_affirm) return "The parties renew and extend the cease-fire.";
+ if (casus_belli) return "The parties agree on a cease-fire and clear casus belli.";
+ return "The parties agree on a cease-fire."
case CLAUSE_PEACE:
- return casus_belli ? "The parties re-affirm peace" : "The parties agree on a peace";
+ if (re_affirm && casus_belli) return "The parties re-affirm peace and clear casus belli.";
+ // The case below shouldn't happen, but could happen when server gets limited duration peace pacts:
+ if (re_affirm) return "The parties renew and extend the peace.";
+ if (casus_belli) return "The parties agree on a peace and clear casus belli.";
+ return "The parties agree on a peace."
case CLAUSE_ALLIANCE:
- return casus_belli ? "The parties re-affirm the alliance" : "The parties create an alliance";
+ if (re_affirm && casus_belli) return "The parties re-affirm the alliance and clear casus belli.";
+ // The case below shouldn't happen, but could happen when server gets limited duration alliances:
+ if (re_affirm) return "The parties renew and extend the alliance.";
+ if (casus_belli) return "The parties create an alliance and clear casus belli.";
+ return "The parties create an alliance.";
case CLAUSE_VISION:
return "The " + nation + " give shared vision";
case CLAUSE_EMBASSY:
diff --git a/freeciv-web/src/main/webapp/javascript/effects.js b/freeciv-web/src/main/webapp/javascript/effects.js
index a6e48a133..bb0907349 100644
--- a/freeciv-web/src/main/webapp/javascript/effects.js
+++ b/freeciv-web/src/main/webapp/javascript/effects.js
@@ -171,7 +171,16 @@ const EFT_IMPR_BUILD_COST_PM = 131;
const EFT_ACTION_RESIST_PCT = 132;
const EFT_OUTPUT_ADD_BONUS = 133;
const EFT_TERRAIN_DEFEND_ADD_BONUS = 134;
-const EFT_LAST = 135;
+const EFT_RAPTURE_RATE_PM = 135
+const EFT_REHOME_PCT = 136
+const EFT_CELEBRATE_SIZE_ADD = 137
+const EFT_OUTPUT_WASTE_MIN_REDUCE = 138
+const EFT_OUTPUT_WASTE_REDUCE = 139
+const EFT_ACTOR_BRIBE_COST_PCT = 140
+const EFT_ACTOR_INCITE_COST_PCT = 141
+const EFT_TILE_NUKE_PROOF = 142
+const EFT_RAZE_BUILDING_PCT = 143;
+const EFT_LAST = 144;
*/
diff --git a/freeciv-web/src/main/webapp/javascript/emoji_list.js b/freeciv-web/src/main/webapp/javascript/emoji_list.js
index 6ef7eec20..d024a357c 100644
--- a/freeciv-web/src/main/webapp/javascript/emoji_list.js
+++ b/freeciv-web/src/main/webapp/javascript/emoji_list.js
@@ -25,6 +25,7 @@ var freemoji = [
'concerned',
'confounded',
'confused',
+"anger",
'cool',
'disappointed',
'dizzy',
@@ -33,6 +34,7 @@ var freemoji = [
'fedup',
'flushed',
'fuming',
+'skull',
'ghost',
'goblin',
'angrydevil',
@@ -97,9 +99,11 @@ var freemoji = [
//body parts and emotives
'ear',
+'eye',
'eyes',
'lips',
'nose',
+'running',
'zzz',
'cool2',
'shit',
@@ -249,11 +253,16 @@ var freemoji = [
'anchor',
'bath',
'bomb',
+'boom',
+'boot',
+'briefcase',
'bulb',
+'bullseye',
'camera',
'castle',
'check',
'cityscape',
+'village',
'cloud',
'cookie',
'crown',
@@ -278,6 +287,7 @@ var freemoji = [
'megaphone',
'knife',
'lightning',
+'medal',
'microscope',
'muscles',
'newspaper',
@@ -293,6 +303,7 @@ var freemoji = [
'ribbon',
'satellitedish',
'scissors',
+'scroll',
'search',
'seashell',
'ski',
@@ -301,6 +312,7 @@ var freemoji = [
'snowman',
'sparkle',
'spiral',
+'swords',
'telescope',
'toilet',
'trophy',
@@ -352,9 +364,12 @@ var freemoji = [
'200',
'airlift',
'bluediamond',
+'reddiamond',
'chat',
'clock',
+'plus',
'equal',
+'minus',
'hammer',
'headstone',
'hourglass',
@@ -571,6 +586,7 @@ var freemoji = [
'divebomber',
'escortfighter',
'jetfighter',
+"multi-fighter",
'groundstrikefighter',
'stealthfighter',
'mediumbomber',
diff --git a/freeciv-web/src/main/webapp/javascript/empire.js b/freeciv-web/src/main/webapp/javascript/empire.js
index 73d01352b..1839d2424 100644
--- a/freeciv-web/src/main/webapp/javascript/empire.js
+++ b/freeciv-web/src/main/webapp/javascript/empire.js
@@ -55,6 +55,7 @@ var empire_upkeep_show_food = true;
var empire_upkeep_show_gold = true;
var empire_upkeep_show_shields = true;
var empire_upkeep_show_free = true;
+var empire_upkeep_show_zero = false; // Default to not show zero-upkeep units
empire_screen_updater = new EventAggregator(update_empire_screen, 250, EventAggregator.DP_NONE, 250, 3, 250);
@@ -159,14 +160,16 @@ function empire_unit_homecity_screen(wide_screen,narrow_screen,small_screen,
panel_html += "";
panel_html += " show upkeep: "
- + ""
- + " "
- + ""
- + " "
- + ""
- + " ";
- panel_html += ""
- + "";
+ + ""
+ + " "
+ + ""
+ + " "
+ + ""
+ + " ";
+ panel_html += ""
+ + " ";
+ panel_html += ""
+ + "";
$("#empire_mode_panel").html(panel_html);
$("#show_hp").prop("checked", empire_show_hitpoints);
@@ -175,6 +178,7 @@ function empire_unit_homecity_screen(wide_screen,narrow_screen,small_screen,
$("#show_gold").prop("checked", empire_upkeep_show_gold);
$("#show_shield").prop("checked", empire_upkeep_show_shields);
$("#show_free").prop("checked", empire_upkeep_show_free);
+ $("#show_zero").prop("checked", empire_upkeep_show_zero);
$('#empire_scroll').css({"height": $(window).height()-160, "overflow-y":"scroll", "overflow-x":"hidden" });
@@ -218,7 +222,7 @@ function empire_unit_homecity_screen(wide_screen,narrow_screen,small_screen,
//console.log("MODE: Small Narrow")
}
// First row for totals
- empire_list_html += "
"
+ empire_list_html += "
"
+ "
"
+ "!" // tiny invisible ! will sort it to top of list
+ "TOTAL UPKEEP
"
@@ -244,9 +248,17 @@ function empire_unit_homecity_screen(wide_screen,narrow_screen,small_screen,
}
for (var unit_id in units) { // pre-sort units belonging to player, by type, into this array
var sunit = units[unit_id];
+ var stype = unit_types[sunit.type];
if (client.conn.playing != null && unit_owner(sunit).playerno == client.conn.playing.playerno) {
// See if unit qualifies to be displayed based on upkeep types desired to be shown
var show_unit = false;
+ var f1,g1,s1,z=false;
+ if (stype['upkeep'] != null) {
+ s1 = parseInt(stype['upkeep'][O_SHIELD],10);
+ f1 = parseInt(stype['upkeep'][O_FOOD],10);
+ g1 = parseInt(stype['upkeep'][O_GOLD],10);
+ if (f1+g1+s1 == 0) z = true;
+ }
var f,g,s,l;
if (sunit['upkeep'] != null) {
s = parseInt(sunit['upkeep'][O_SHIELD],10);
@@ -258,6 +270,8 @@ function empire_unit_homecity_screen(wide_screen,narrow_screen,small_screen,
else if (empire_upkeep_show_gold && g>0) show_unit=true;
else if (empire_upkeep_show_free && l==0) show_unit=true;
} else if (empire_upkeep_show_free) show_unit=true;
+ if (!empire_upkeep_show_zero && z==true) show_unit=false;
+ else if (empire_upkeep_show_zero && z==true) show_unit=true;
if (show_unit) {
units_sorted_by_type[sunit['type']].push(sunit);
}
@@ -273,8 +287,14 @@ function empire_unit_homecity_screen(wide_screen,narrow_screen,small_screen,
var ukg = new Array(cities.length);
var uks = new Array(cities.length);
- // Go through each city and pluck out units by type so they're arranged by type
- for (var city_id in cities) { //rows (cities)
+// Go through each city in the order sorted in the cities list, and pluck out units by type, so they're arranged by type:
+ if ($("#city_table_head").length == 0) update_city_screen(); // If cities list has never been created, create it.
+ var current_city_row = $("#city_table_head").next().children().first(); // Get first row of the sorted cities list.
+ // Iterate through all player cities in the order they're sorted in the sorted cities list:
+ while (current_city_row.length > 0) {
+ //for (var city_id in cities) { //rows (cities)
+ var city_id = current_city_row.attr('id').substr(12);
+ current_city_row = current_city_row.next(); // bump up to next city row for when we iterate the loop back up here
var pcity = cities[city_id];
ukf[city_id] = 0; // set counters at 0 before adding it up
@@ -288,8 +308,8 @@ function empire_unit_homecity_screen(wide_screen,narrow_screen,small_screen,
//TO DO, we can only adjust height later after we add a unit_count tally then would have to do a $().css("height",rheight)
var rheight = 28 * Math.ceil( (/*unit_count*/11*40) / ($(window).width()-140) );
- unit_row_html = "
";
- unit_row_html += "
"+pcity['name']+"
";
+ unit_row_html = "
";
+ unit_row_html += "
"+pcity['name']+"
";
unit_row_html += "
";
unit_row_html += "
";
unit_row_html += "
";
@@ -524,8 +544,14 @@ function empire_unitcity_screen(wide_screen,narrow_screen,small_screen,
var sumHPD = new Array(cities.length);
- // Go through each city and pluck out units by type so they're arranged by type
- for (var city_id in cities) { //rows (cities)
+// Go through each city in the order sorted in the cities list, and pluck out units by type, so they're arranged by type:
+ if ($("#city_table_head").length == 0) update_city_screen(); // If cities list has never been created, create it.
+ var current_city_row = $("#city_table_head").next().children().first(); // Get first row of the sorted cities list.
+ // Iterate through all player cities in the order they're sorted in the sorted cities list:
+ while (current_city_row.length > 0) {
+ //for (var city_id in cities) { //rows (cities)
+ var city_id = current_city_row.attr('id').substr(12);
+ current_city_row = current_city_row.next(); // bump up to next city row for when we iterate the loop back up here
var pcity = cities[city_id];
// Only process legal cities owned by player
@@ -537,7 +563,7 @@ function empire_unitcity_screen(wide_screen,narrow_screen,small_screen,
//TO DO, we can only adjust height later after we add a unit_count tally then would have to do a $().css("height",rheight)
var rheight = 28 * Math.ceil( (/*unit_count*/11*40) / ($(window).width()-140) );
- unit_row_html = "
";
+ unit_row_html = "
";
unit_row_html += "
";
unit_row_html += "
"+pcity['name']+"
";
unit_row_html += "
";
@@ -778,8 +804,14 @@ function empire_econ_improvements_screen(wide_screen,narrow_screen,small_screen,
var improvements_html = "";
var city_count = 0; // number of cities (total rows)
- // Go through each city
- for (var city_id in cities) {
+// Go through each city in the order sorted in the cities list, and pluck out units by type, so they're arranged by type:
+ if ($("#city_table_head").length == 0) update_city_screen(); // If cities list has never been created, create it.
+ var current_city_row = $("#city_table_head").next().children().first(); // Get first row of the sorted cities list.
+ // Iterate through all player cities in the order they're sorted in the sorted cities list:
+ while (current_city_row.length > 0) {
+ //for (var city_id in cities) { //rows (cities)
+ var city_id = current_city_row.attr('id').substr(12);
+ current_city_row = current_city_row.next(); // bump up to next city row for when we iterate the loop back up here
var pcity = cities[city_id];
// Only process legal cities owned by player
@@ -789,7 +821,7 @@ function empire_econ_improvements_screen(wide_screen,narrow_screen,small_screen,
//TO DO, we can only adjust height later after we add a unit_count tally then would have to do a $().css("height",rheight)
var rheight = 28 * Math.ceil( (/*col_count*/22*40) / ($(window).width()-140) );
- improvements_html = "
";
+ improvements_html = "
";
improvements_html += "
"+pcity['name']+"
";
improvements_html += "
";
@@ -970,7 +1002,7 @@ function empire_econ_upkeep_screen(wide_screen,narrow_screen,small_screen,
+ "
";
}
// First row for total
- empire_list_html += "
"
+ empire_list_html += "
"
+ "
"
+ "!" // tiny invisible ! will sort it to top of list
+ "TOTAL UPKEEP
"
@@ -984,8 +1016,14 @@ function empire_econ_upkeep_screen(wide_screen,narrow_screen,small_screen,
var city_count = 0; // number of cities (total rows)
let city_upkeep = new Array(cities.length);
- // Go through each city
- for (var city_id in cities) {
+ // Go through each city in the order sorted in the cities list, and pluck out units by type, so they're arranged by type:
+ if ($("#city_table_head").length == 0) update_city_screen(); // If cities list has never been created, create it.
+ var current_city_row = $("#city_table_head").next().children().first(); // Get first row of the sorted cities list.
+ // Iterate through all player cities in the order they're sorted in the sorted cities list:
+ while (current_city_row.length > 0) {
+ //for (var city_id in cities) { //rows (cities)
+ var city_id = current_city_row.attr('id').substr(12);
+ current_city_row = current_city_row.next(); // bump up to next city row for when we iterate the loop back up here
var pcity = cities[city_id];
city_upkeep[city_id] = 0; // start counter
// Only process legal cities owned by player
@@ -995,11 +1033,11 @@ function empire_econ_upkeep_screen(wide_screen,narrow_screen,small_screen,
//TO DO, we can only adjust height later after we add a unit_count tally then would have to do a $().css("height",rheight)
var rheight = 28 * Math.ceil( (/*col_count*/22*40) / ($(window).width()-140) );
- improvements_html = "
";
+ improvements_html = "
";
improvements_html += "
"+pcity['name']+"
";
improvements_html += "
"
- improvements_html += "
";
+ improvements_html += "
";
// Go through the improvements one at a time and determine whether to show it
for (var z = 0; z < ruleset_control.num_impr_types-1 /*-1 don't show coinage*/; z ++) {
@@ -1214,8 +1252,14 @@ function empire_econ_worklists_screen(wide_screen,narrow_screen,small_screen,
var queue_html = "";
var city_count = 0; // number of cities (total rows)
- // Go through each city
- for (var city_id in cities) {
+// Go through each city in the order sorted in the cities list, and pluck out units by type, so they're arranged by type:
+ if ($("#city_table_head").length == 0) update_city_screen(); // If cities list has never been created, create it.
+ var current_city_row = $("#city_table_head").next().children().first(); // Get first row of the sorted cities list.
+ // Iterate through all player cities in the order they're sorted in the sorted cities list:
+ while (current_city_row.length > 0) {
+ //for (var city_id in cities) { //rows (cities)
+ var city_id = current_city_row.attr('id').substr(12);
+ current_city_row = current_city_row.next(); // bump up to next city row for when we iterate the loop back up here
var pcity = cities[city_id];
// Only process legal cities owned by player
if (client.conn.playing == null || city_owner(pcity) == null || city_owner(pcity).playerno != client.conn.playing.playerno) {
@@ -1229,7 +1273,7 @@ function empire_econ_worklists_screen(wide_screen,narrow_screen,small_screen,
var empty_row_click = is_small_screen()
? " onclick='tap_empty_production_row(event, "+city_id+")'"
: " title='CLICK: paste worklist\nCTRL-CLICK: clear worklist\nSHIFT-CLICK: copy worklist' onclick='tap_empty_production_row(event, "+city_id+")'";
- queue_html = "
";
diff --git a/freeciv-web/src/main/webapp/javascript/fc_types.js b/freeciv-web/src/main/webapp/javascript/fc_types.js
index 79240885c..772165db1 100644
--- a/freeciv-web/src/main/webapp/javascript/fc_types.js
+++ b/freeciv-web/src/main/webapp/javascript/fc_types.js
@@ -77,6 +77,19 @@ var ASTK_EXTRA = 3;
var ASTK_EXTRA_NOT_THERE = 4;
var ASTK_COUNT = 5;
+/* The unit_orders enum from unit.h */
+var ORDER_MOVE = 0;
+var ORDER_ACTIVITY = 1;
+var ORDER_FULL_MP = 2;
+var ORDER_ACTION_MOVE = 3;
+var ORDER_PERFORM_ACTION = 4;
+var ORDER_LAST = 5;
+
+/* The unit_ss_data_type enum from unit.h */
+var USSDT_QUEUE = 0;
+var USSDT_UNQUEUE = 1;
+var USSDT_BATTLE_GROUP = 2;
+
/* Actions */
var ACTION_ESTABLISH_EMBASSY = 0;
var ACTION_ESTABLISH_EMBASSY_STAY = 1;
@@ -280,3 +293,6 @@ const GENUS_GREAT_WONDER = 0;
const GENUS_SMALL_WONDER = 1; // genus <= GENUS_SMALL_WONDER means it's a wonder
const GENUS_IMPROVEMENT = 2;
const GENUS_COINAGE = 3;
+
+const TECH_USER_1 = 32;
+const TECH_SPECIAL_TECH = 32; // reserved for cul-de-sac "specialization" techs which enhance what a parent tech can do
diff --git a/freeciv-web/src/main/webapp/javascript/game.js b/freeciv-web/src/main/webapp/javascript/game.js
index 68a5cc6c1..9c74ceb85 100644
--- a/freeciv-web/src/main/webapp/javascript/game.js
+++ b/freeciv-web/src/main/webapp/javascript/game.js
@@ -104,15 +104,18 @@ function city_size_sum(playerno) {
...
**************************************************************************/
function update_game_status_panel() {
-
- if (C_S_RUNNING != client_state() ) return;
+ if (C_S_RUNNING != client_state() || was_supercow) {
+ // was_supercow makes a "fake player" to turn off observer interaction lock
+ return;
+ }
var status_html = "";
var msg_title_prefix = unread_messages > 0 ? "Unread messages.
Click = " : ""
// unread message counter with toggle message window
status_html = ""
+ + (current_message_dialog_state == "minimized" ? "Show" : "Hide")
+ + " message window'>"
+ " "
+ ((unread_messages>0) ? unread_messages : "")
+"";
@@ -128,7 +131,7 @@ function update_game_status_panel() {
var net_income = pplayer['expected_income'].toString();
var income_str = (net_income.slice(-2) == ".5") ? (net_income.substring(0, net_income.length - 2) + "½") : net_income;
- if (pplayer['expected_income'] > 0) {
+ if (pplayer['expected_income'] >= 0) {
net_income = "+" + income_str;
} else net_income = income_str;
@@ -173,23 +176,28 @@ function update_game_status_panel() {
if (!is_small_screen()) status_html += ""; // type 2 is demographics
if (!is_small_screen()) status_html += " ";
if (!is_small_screen()) status_html += "" + city_size_sum(client.conn.playing.playerno) + " ";
- if (!is_small_screen()) status_html += " " + get_year_string() + "";
- status_html += " ";
+ if (!is_small_screen()) status_html += " " + get_year_string() + " ";
+ status_html += " ";
var income_color = " 0) income_color += income_calculated_by_client
- ? " style='color:#89c06a' title='Income'" : " style='color:#a2b095' title='Income'" // slight hint whether accurate client calc or from server.
+ else if (pplayer['expected_income'] > 0) {
+ income_color += income_calculated_by_client
+ ? " style='color:#89c06a; cursor:default' title='Income'" : " style='color:#a2b095; cursor:default' title='Income'";
+ }
+ else { income_color += income_calculated_by_client
+ ? " style='color:#919191; cursor:default' title='No Income'" : " style='color:#a2a2a2; cursor:default' title='No Income'"
+ }
status_html += ""+pplayer['gold']
+ " "+income_color+" style='cursor:default;'>" + net_income + ""+" ";
- status_html += ""
- + tax + "% ";
- status_html += " " + lux + "% ";
+ status_html += ""
+ + tax + "% ";
+ status_html += " " + lux + "% ";
const sci_title = "Science:\n"+(techs[client.conn.playing['researching']]===undefined ? "" : techs[client.conn.playing['researching']]['name']) +"\n"
- + client.conn.playing['bulbs_researched'] + " / " + client.conn.playing['researching_cost'] +"\n" + bulb_output_text;
- status_html += " " + sci + "% ";
+ + client.conn.playing['bulbs_researched'] + " / " + client.conn.playing['researching_cost'] +" bulbs\n" + bulb_output_text;
+ status_html += " " + sci + "% ";
} else if (server_settings != null && server_settings['metamessage'] != null) { // Status message for gamemasters/admins/supercows:
status_html += "Observing - Turn " + game_info['turn'] + " -";
diff --git a/freeciv-web/src/main/webapp/javascript/helpdata.js b/freeciv-web/src/main/webapp/javascript/helpdata.js
index 80866efd7..2619fc402 100644
--- a/freeciv-web/src/main/webapp/javascript/helpdata.js
+++ b/freeciv-web/src/main/webapp/javascript/helpdata.js
@@ -123,6 +123,10 @@ function show_help_intro()
{
$.get( "/docs/help_intro.txt", function( data ) {
$("#help_info_page").html(data);
+ var help_banner = "/static/images/fcw-front-page"
+ + Math.ceil(Math.random()*61) + ".png";
+
+ $("#help_banner").prop("src",help_banner);
});
}
@@ -429,6 +433,7 @@ function generate_help_text(key)
num_resources = 1;
if (client_rules_flag[CRF_MP2_B]) num_resources++;
break;
+ case "Glacier":
case "Arctic":
num_resources = 2;
if (client_rules_flag[CRF_MP2_B]) num_resources++;
@@ -460,11 +465,32 @@ function generate_help_text(key)
break;
}
if (num_resources) msg += " Special resources: ";
- for (let r=1; r <= num_resources; r++) {
- msg += ""
+ // MP2 rulesets.
+ if (client_rules_flag[CRF_MP2]) {
+ for (let r=1; r <= num_resources; r++) {
+ msg += ""
+ }
+ }
+ // General construction of terrain help for other rulesets: TODO: when we can extract an extra sprite and put into a
+ // element with position:absolute; left:r*96, then helptext_pane class gets property position:relative, we can
+ // put pretty images like the mp2 stuff above (and remove all hardcoding too)
+ else {
+ msg += "
"
+ for (let r=0; r < terrain.resources.length; r++) {
+ msg += "
"
+ var extra_name = extras[terrain.resources[r]].name;
+ if (extra_name.startsWith("?")) extra_name = extra_name.split(":")[1]; // chops out first part of "?animals:Game"
+ msg += extra_name +":
"
}
+
msg += "";
} else if (key.indexOf("help_gen_improvements") != -1 || key.indexOf("help_gen_wonders") != -1) {
var improvement = improvements[parseInt(key.replace("help_gen_wonders_", "").replace("help_gen_improvements_", ""))];
@@ -510,6 +536,9 @@ function generate_help_text(key)
//var as = punit_type['attack_strength'];
// Display base attack relative to v0 vet power which may be non-100:
var as = fractionalize(utype_real_base_attack_strength(punit_type));
+ if (pstats.max_attacks>0) {
+ as += " (max. "+pstats.max_attacks+" attack"+(pstats.max_attacks>1?"s":"")+" per turn)"
+ }
msg += span1 + "Attack: " + span_end + span2 + as + div_end;
// DEFENSE
msg += "
";
diff --git a/freeciv-web/src/main/webapp/javascript/improvement.js b/freeciv-web/src/main/webapp/javascript/improvement.js
index 89dceb5e1..643f0f473 100644
--- a/freeciv-web/src/main/webapp/javascript/improvement.js
+++ b/freeciv-web/src/main/webapp/javascript/improvement.js
@@ -208,6 +208,13 @@ function get_universal_discount_price(ptype, pcity)
return ptype['build_cost'] - 5;
}
}
+ if (client_rules_flag[CRF_MP2_D]) {
+ if (ptype['name'] == "Fortifications"
+ && (player_invention_state(players[playerno], tech_id_by_name('Metallurgy')) == TECH_KNOWN)) {
+ return ptype['build_cost'] + 10;
+ }
+ }
+
// MP2 discounts for having Colossus
if (pcity && client_rules_flag[CRF_COLOSSUS_DISCOUNT]
&& player_invention_state(players[playerno], tech_id_by_name('Steam Engine')) != TECH_KNOWN
diff --git a/freeciv-web/src/main/webapp/javascript/libs/.bower.json b/freeciv-web/src/main/webapp/javascript/libs/.bower.json
new file mode 100644
index 000000000..68b062d3d
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/libs/.bower.json
@@ -0,0 +1,46 @@
+{
+ "name": "dragscroll",
+ "version": "0.0.8",
+ "homepage": "https://github.com/asvd/dragscroll",
+ "authors": [
+ "Dmitry Prokashev "
+ ],
+ "description": "Tiny library for drag-n-drop scrolling style",
+ "main": "dragscroll.js",
+ "moduleType": [
+ "amd",
+ "globals"
+ ],
+ "keywords": [
+ "dragscroll",
+ "scroll",
+ "scrolling",
+ "mouse",
+ "hold",
+ "click-and-hold",
+ "drag-and-drop",
+ "drag-n-drop",
+ "click-n-hold",
+ "click-and-hold",
+ "drag",
+ "viewport"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "_release": "0.0.8",
+ "_resolution": {
+ "type": "version",
+ "tag": "v0.0.8",
+ "commit": "bb4cd42f3f850bedf9f9fd99a388017a09e29b52"
+ },
+ "_source": "https://github.com/asvd/dragscroll.git",
+ "_target": "^0.0.8",
+ "_originalSource": "dragscroll",
+ "_direct": true
+}
\ No newline at end of file
diff --git a/freeciv-web/src/main/webapp/javascript/libs/LICENSE b/freeciv-web/src/main/webapp/javascript/libs/LICENSE
new file mode 100644
index 000000000..88744e20a
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/libs/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 asvd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/freeciv-web/src/main/webapp/javascript/libs/bower.json b/freeciv-web/src/main/webapp/javascript/libs/bower.json
new file mode 100644
index 000000000..cc105f85e
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/libs/bower.json
@@ -0,0 +1,36 @@
+{
+ "name": "dragscroll",
+ "version": "0.0.8",
+ "homepage": "https://github.com/asvd/dragscroll",
+ "authors": [
+ "Dmitry Prokashev "
+ ],
+ "description": "Tiny library for drag-n-drop scrolling style",
+ "main": "dragscroll.js",
+ "moduleType": [
+ "amd",
+ "globals"
+ ],
+ "keywords": [
+ "dragscroll",
+ "scroll",
+ "scrolling",
+ "mouse",
+ "hold",
+ "click-and-hold",
+ "drag-and-drop",
+ "drag-n-drop",
+ "click-n-hold",
+ "click-and-hold",
+ "drag",
+ "viewport"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+}
diff --git a/freeciv-web/src/main/webapp/javascript/libs/dragscroll.js b/freeciv-web/src/main/webapp/javascript/libs/dragscroll.js
new file mode 100644
index 000000000..fd1cf8ffd
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/libs/dragscroll.js
@@ -0,0 +1,92 @@
+/**
+ * @fileoverview dragscroll - scroll area by dragging
+ * @version 0.0.8
+ *
+ * @license MIT, see http://github.com/asvd/dragscroll
+ * @copyright 2015 asvd
+ */
+
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['exports'], factory);
+ } else if (typeof exports !== 'undefined') {
+ factory(exports);
+ } else {
+ factory((root.dragscroll = {}));
+ }
+}(this, function (exports) {
+ var _window = window;
+ var _document = document;
+ var mousemove = 'mousemove';
+ var mouseup = 'mouseup';
+ var mousedown = 'mousedown';
+ var EventListener = 'EventListener';
+ var addEventListener = 'add'+EventListener;
+ var removeEventListener = 'remove'+EventListener;
+ var newScrollX, newScrollY;
+
+ var dragged = [];
+ var reset = function(i, el) {
+ for (i = 0; i < dragged.length;) {
+ el = dragged[i++];
+ el = el.container || el;
+ el[removeEventListener](mousedown, el.md, 0);
+ _window[removeEventListener](mouseup, el.mu, 0);
+ _window[removeEventListener](mousemove, el.mm, 0);
+ }
+
+ // cloning into array since HTMLCollection is updated dynamically
+ dragged = [].slice.call(_document.getElementsByClassName('dragscroll'));
+ for (i = 0; i < dragged.length;) {
+ (function(el, lastClientX, lastClientY, pushed, scroller, cont){
+ (cont = el.container || el)[addEventListener](
+ mousedown,
+ cont.md = function(e) {
+ if (!el.hasAttribute('nochilddrag') ||
+ _document.elementFromPoint(
+ e.pageX, e.pageY
+ ) == cont
+ ) {
+ pushed = 1;
+ lastClientX = e.clientX;
+ lastClientY = e.clientY;
+
+ e.preventDefault();
+ }
+ }, 0
+ );
+
+ _window[addEventListener](
+ mouseup, cont.mu = function() {pushed = 0;}, 0
+ );
+
+ _window[addEventListener](
+ mousemove,
+ cont.mm = function(e) {
+ if (pushed) {
+ (scroller = el.scroller||el).scrollLeft -=
+ newScrollX = (- lastClientX + (lastClientX=e.clientX));
+ scroller.scrollTop -=
+ newScrollY = (- lastClientY + (lastClientY=e.clientY));
+ if (el == _document.body) {
+ (scroller = _document.documentElement).scrollLeft -= newScrollX;
+ scroller.scrollTop -= newScrollY;
+ }
+ }
+ }, 0
+ );
+ })(dragged[i++]);
+ }
+ }
+
+
+ if (_document.readyState == 'complete') {
+ reset();
+ } else {
+ _window[addEventListener]('load', reset, 0);
+ }
+
+ exports.reset = reset;
+}));
+
diff --git a/freeciv-web/src/main/webapp/javascript/libs/package.json b/freeciv-web/src/main/webapp/javascript/libs/package.json
new file mode 100644
index 000000000..3b15b3be7
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/libs/package.json
@@ -0,0 +1,35 @@
+{
+ "author": "Dmitry Prokashev ",
+ "name": "dragscroll",
+ "description": "Tiny library for drag-n-drop scrolling style",
+ "version": "0.0.8",
+ "keywords": [
+ "dragscroll",
+ "scroll",
+ "scrolling",
+ "mouse",
+ "hold",
+ "drag-n-drop",
+ "drag-and-drop",
+ "click-n-hold",
+ "click-and-hold",
+ "drag",
+ "viewport"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/asvd/dragscroll.git"
+ },
+ "browser":
+ {
+ "fs": false,
+ "child_process": false
+ },
+ "main": "dragscroll.js",
+ "dependencies": {},
+ "devDependencies": {},
+ "optionalDependencies": {},
+ "engines": {
+ "node": "*"
+ }
+}
diff --git a/freeciv-web/src/main/webapp/javascript/map.js b/freeciv-web/src/main/webapp/javascript/map.js
index 3d25cba89..f5a3cb288 100644
--- a/freeciv-web/src/main/webapp/javascript/map.js
+++ b/freeciv-web/src/main/webapp/javascript/map.js
@@ -270,6 +270,19 @@ function MAP_TO_NATURAL_POS(map_x, map_y)
return {'nat_y' : pnat_y, 'nat_x' : pnat_x};
}
+/*******************************************************************//**
+ Return squared distance between two tiles.
+***********************************************************************/
+function sq_map_distance(tile0, tile1)
+{
+ /* We assume map_distance_vector gives us the vector with the
+ minimum squared distance. Right now this is true. */
+ var vector = map_distance_vector(tile0, tile1);
+ var dx = vector[0], dy = vector[1];
+
+ return map_vector_to_sq_distance(dx, dy);
+}
+
/****************************************************************************
Return the squared distance for a map vector
****************************************************************************/
@@ -461,10 +474,18 @@ function dir_ccw(dir)
**************************************************************************/
function clear_goto_tiles()
{
+ if (renderer == RENDERER_2DCANVAS) {
for (var x = 0; x < map['xsize']; x++) {
for (var y = 0; y < map['ysize']; y++) {
tiles[x + y * map['xsize']]['goto_dir'] = null;
}
}
-
+ } else {
+ if (scene != null && goto_lines != null) {
+ for (var i = 0; i < goto_lines.length; i++) {
+ scene.remove(goto_lines[i]);
+ }
+ goto_lines = [];
+ }
+ }
}
diff --git a/freeciv-web/src/main/webapp/javascript/messages.js b/freeciv-web/src/main/webapp/javascript/messages.js
index e1a81635d..5bdfc7ba3 100644
--- a/freeciv-web/src/main/webapp/javascript/messages.js
+++ b/freeciv-web/src/main/webapp/javascript/messages.js
@@ -277,10 +277,11 @@ function reclassify_chat_message(text)
to produce messages independently of incoming packets (e.g., notifying
of changes to prefs, etc.)
**************************************************************************/
-function add_client_message(message)
+function add_client_message(message, ev_class)
{
+ var msg_event = ev_class ? ev_class : "e_client_msg";
if (message.includes("%%") || message.includes("[`")) message = decode_user_hyperlinks(message);
- var fake_packet = {"message": ""+message+""};
+ var fake_packet = {"message": ""+message+""};
fake_packet.event = E_CHAT_MSG;
add_chatbox_text(fake_packet);
}
@@ -464,6 +465,8 @@ function wait_for_text(text, runnable)
{
var chatbox_text = get_chatbox_text();
if (chatbox_text != null && chatbox_text.indexOf(text) != -1) {
+ if (DEBUG_PICK_NATION)
+ console.log("wait_for_text("+text+") engaged.");
runnable();
} else {
setTimeout(function () {
diff --git a/freeciv-web/src/main/webapp/javascript/music.js b/freeciv-web/src/main/webapp/javascript/music.js
index 292c7c5f3..ec1c90335 100644
--- a/freeciv-web/src/main/webapp/javascript/music.js
+++ b/freeciv-web/src/main/webapp/javascript/music.js
@@ -75,7 +75,7 @@ function pick_next_track() {
// based on YEAR, civilization style, turn #, etc., and use more special
// "DJ tag logic" to pick a song.
if (client_is_observer()) {
- if (game_info.turn <= 12) category = "tribal"
+ if (game_info.turn <= 17) category = "tribal"
else if (game_info.turn < 45) category = "ancient"
else if (game_info.turn < 70) category = "middle"
else if (game_info.turn < 85) category = "colonial"
@@ -92,11 +92,12 @@ function pick_next_track() {
track_name = music_list[category][Math.floor(Math.random() * music_list[category].length)]+ ".mp3";
dj_approved = true;
} else { // Not a break track, pick a main selection at random
- if (game_info.turn <= 12 && !tech_known("Alphabet")) category = "tribal";
- else if (!tech_known("Feudalism") && !tech_known("Monotheism")) category = "ancient";
+ if (game_info.turn <= 17 && !tech_known("Alphabet")) category = "tribal";
+ else if (!tech_known("Feudalism") && !tech_known("Navigation") // catch all paths to gunpowder || magnetism
+ && !tech_known("Philosophy") && !tech_known("Invention")) category = "ancient";
else if (!tech_known("Gunpowder") && !tech_known("Magnetism")) category = "middle";
else if (!tech_known("Industrialization") || !tech_known("Conscription")) category = "colonial"; // need both to leave colonial
- else if (!tech_known("Rocketry") && !tech_known("Robotics")) category = "industrial";
+ else if (!tech_known("Rocketry") && !tech_known("Robotics")) category = "industrial";
else category = "modern";
// Pick a random track with the approved qualities for the game context:
diff --git a/freeciv-web/src/main/webapp/javascript/nation.js b/freeciv-web/src/main/webapp/javascript/nation.js
index 63f3d8f9d..60367d356 100644
--- a/freeciv-web/src/main/webapp/javascript/nation.js
+++ b/freeciv-web/src/main/webapp/javascript/nation.js
@@ -132,8 +132,12 @@ function update_nation_screen()
var their_cb = !observer ? players[player_id].diplstates[client.conn.playing['playerno']]['has_reason_to_cancel'] : 0;
var contact_time=0;
if (!observer && client.conn.playing != null && diplstates[player_id] != null && player_id != client.conn.playing['playerno']) {
- contact_time = pplayer.diplstates[client.conn.playing.playerno].contact_turns_left; //set this here because it needs the same 'if'
-
+ /* Former way: we checked the other player's contact turns with us instead of ours with them; yet we are not always privvy
+ to that and don't get real-time updates for that...
+ contact_time = pplayer.diplstates[client.conn.playing.playerno].contact_turns_left; */
+ /* Current way: check our own contact turns with the player instead of theirs with us: */
+ contact_time = players[client.conn.playing.playerno].diplstates[player_id].contact_turns_left;
+
var pact_time = !observer ? pplayer.diplstates[client.conn.playing.playerno].turns_left : 0;
var dstate = get_diplstate_text(diplstates[player_id]);
if (dstate == "None") dstate = "+" + dstate; // sorting hack
diff --git a/freeciv-web/src/main/webapp/javascript/options.js b/freeciv-web/src/main/webapp/javascript/options.js
index 0c4e69487..014ef0626 100644
--- a/freeciv-web/src/main/webapp/javascript/options.js
+++ b/freeciv-web/src/main/webapp/javascript/options.js
@@ -17,6 +17,9 @@
***********************************************************************/
+// Server's Local Time relative to GMT. e.g., London 0, Paris 1, SF -8
+const SERVER_GMT_OFFSET = 1; // helps localize timestamps to client's time zone
+
/* The server will send information about its settings. It is stored here.
* You can look up a setting by its name or by its id number. */
var server_settings = {};
@@ -101,6 +104,7 @@ var save_options_on_exit = true;
var fullscreen_mode = false;
var replace_capital_i = null; // option to fix bad capital I in some default sans fonts
var play_music = true;
+var show_timestamps = false; // show ec_info timestamps on critical events that occurred while not logged in (useful in longturn only)
/** Local Options: **/
var scroll_narrow_x = false; // wider scrollable table rows for mobile to see more info
@@ -426,6 +430,21 @@ function init_options_dialog()
if (show_compass) $("#compass").show();
else $("#compass").hide();
});
+
+ $('#show_timestamps').prop('checked', show_timestamps);
+ $('#show_timestamps').change(function() {
+ show_timestamps = this.checked;
+ simpleStorage.set('tstamps', show_timestamps);
+ if (show_timestamps) {
+ $(".ts").show();
+ $(".ts").css("display", "inline");
+ }
+ else {
+ $(".ts").hide();
+ $(".ts").css("display", "none");
+ }
+ });
+
// Graphic Theme
graphic_theme_path = simpleStorage.get('grtheme');
if (!graphic_theme_path) graphic_theme_path = "themes/greek/";
@@ -440,6 +459,22 @@ function init_options_dialog()
//----------------------------------------------------------------^^USER OPTIONS^^
$('#graphic_theme').change();
+ if (!is_longturn()) {
+ if (renderer == RENDERER_WEBGL) {
+ $("#switch_renderer_button").html("Use 2D HTML5 graphics");
+ $("#renderer_help").html("Switch to 2D isometric graphics.")
+ } else {
+ $("#switch_renderer_button").html("Use 3D WebGL graphics");
+ $("#renderer_help").html("Use 3D WebGL graphics. Make sure your computer supports 3D WebGL graphics.")
+ $("#update_model_button").hide();
+ }
+
+ if (!Detector.webgl) {
+ $("#switch_renderer_button").hide();
+ $("#renderer_help").html("3D WebGL not supported.")
+ }
+ }
+
$("#title_setting_div").hide();
if (is_longturn()) {
diff --git a/freeciv-web/src/main/webapp/javascript/overview.js b/freeciv-web/src/main/webapp/javascript/overview.js
index a04af4ef4..07aa3d0b9 100644
--- a/freeciv-web/src/main/webapp/javascript/overview.js
+++ b/freeciv-web/src/main/webapp/javascript/overview.js
@@ -208,10 +208,18 @@ function generate_overview_hash(cols, rows) {
}
}
- var r = base_canvas_to_map_pos(0, 0);
- if (r != null) {
- hash += r['map_x'];
- hash += r['map_y'];
+ if (renderer == RENDERER_2DCANVAS) {
+ var r = base_canvas_to_map_pos(0, 0);
+ if (r != null) {
+ hash += r['map_x'];
+ hash += r['map_y'];
+ }
+ } else {
+ var ptile = webgl_canvas_pos_to_tile($(window).width() / 6, $(window).height() / 6);
+ if (ptile != null) {
+ hash += ptile['x'];
+ hash += ptile['y'];
+ }
}
return hash;
@@ -226,6 +234,7 @@ function render_viewrect()
var path = [];
+ if (renderer == RENDERER_2DCANVAS && mapview['gui_x0'] != 0 && mapview['gui_y0'] != 0) {
var point = base_canvas_to_map_pos(0, 0);
path.push([point.map_x, point.map_y]);
point = base_canvas_to_map_pos(mapview['width'], 0);
@@ -234,7 +243,24 @@ function render_viewrect()
path.push([point.map_x, point.map_y]);
point = base_canvas_to_map_pos(0, mapview['height']);
path.push([point.map_x, point.map_y]);
+ } else {
+ var w = $(window).width();
+ var h = $(window).height();
+
+ var ptile = webgl_canvas_pos_to_tile(w / 6, h / 6);
+ if (ptile == null) return;
+ path.push([ptile.x, ptile.y]);
+ var ptile = webgl_canvas_pos_to_tile(5 * w / 6, h / 6);
+ if (ptile == null) return;
+ path.push([ptile.x, ptile.y]);
+ var ptile = webgl_canvas_pos_to_tile(5 * w / 6, h - height_offset);
+ if (ptile == null) return;
+ path.push([ptile.x, ptile.y]);
+ var ptile = webgl_canvas_pos_to_tile(w / 6, h - height_offset);
+ if (ptile == null) return;
+ path.push([ptile.x, ptile.y]);
+ }
var viewrect_canvas = document.getElementById('overview_viewrect');
if (viewrect_canvas == null) return;
@@ -262,6 +288,23 @@ function render_viewrect()
viewrect_ctx.restore();
}
+ if (topo_has_flag(TF_WRAPY)) {
+ viewrect_ctx.translate(0, map.ysize);
+ add_closed_path(viewrect_ctx, path);
+ viewrect_ctx.translate(0, -2 * map.ysize);
+ add_closed_path(viewrect_ctx, path);
+ if (topo_has_flag(TF_WRAPX)) {
+ viewrect_ctx.translate(-map.xsize, 0);
+ add_closed_path(viewrect_ctx, path);
+ viewrect_ctx.translate(0, 2 * map.ysize);
+ add_closed_path(viewrect_ctx, path);
+ viewrect_ctx.translate(2 * map.xsize, 0);
+ add_closed_path(viewrect_ctx, path);
+ viewrect_ctx.translate(0, -2 * map.ysize);
+ add_closed_path(viewrect_ctx, path);
+ }
+ }
+
viewrect_ctx.stroke();
}
@@ -412,6 +455,7 @@ function overview_clicked (x, y)
var ptile = map_pos_to_tile(x1, y1);
if (ptile != null) {
//reposition events save the position to return to with shift-spacebar
+ //save_map_return_position(ptile);
center_tile_mapcanvas(ptile);
}
diff --git a/freeciv-web/src/main/webapp/javascript/packhand.js b/freeciv-web/src/main/webapp/javascript/packhand.js
index b2230c296..d75efc5ee 100644
--- a/freeciv-web/src/main/webapp/javascript/packhand.js
+++ b/freeciv-web/src/main/webapp/javascript/packhand.js
@@ -16,6 +16,245 @@
along with this program. If not, see .
***********************************************************************/
+/* Many packets of same type may come sequentially, for which we don't
+ want to update the UI until they are all completed and
+ handle_processing_finished() is called. These global vars keep track
+ whether certain UI data will get a refresh on the next
+ handle_processing_finished() call which 'thaws' things for a UI update: */
+var ui_update_nations_info = false; // Nations tab and Empire tab (for now)
+var ui_update_cities_info = false; // Cities tab
+var ui_update_bulbs_info = false; // Bulb information
+//
+var DEBUG_LOG_PACKETS = false; // verbose packet logging
+var DEBUG_SHORT_PACKETS = false; // show terse packet log
+var DEBUG_EXPAND_PACKETS = true; // log expandable packet under terse packet
+var DEBUG_ACTION_PACKETS = false; // for debugging outgoing actions only
+//
+var DEBUG_PICK_NATION = false; // for debugging longturn pick nation issues
+var DEBUG_UNITS = false; // console log tools for debugging unit issues
+var DEBUG_FOCUS = false; // for debugging advancing unit focus glitches
+//
+/* Prevent hard-coded checking of extras from failing in rulesets
+ which don't have those extras: */
+const EXTRA_NOT_EXIST = 65535;
+//
+/* Commenting out a key/value pair will result in packets of that type
+ not being logged in DEBUG_SHORT_PACKETS mode. That is, it's helpful
+ for screening your packet analysis during debugging. */
+const packet_names = {
+ "PROCESSING_STARTED": 0,
+ "PROCESSING_FINISHED": 1,
+ "SERVER_JOIN_REQ": 4,
+ "SERVER_JOIN_REPLY": 5,
+ "AUTHENTICATION_REQ": 6,
+ "AUTHENTICATION_REPLY": 7,
+ "SERVER_SHUTDOWN": 8,
+ "NATION_SELECT_REQ": 10,
+ "PLAYER_READY": 11,
+ "ENDGAME_REPORT": 12,
+ "ENDGAME_PLAYER": 223,
+ /*
+ "TILE_INFO": 15,
+ */
+ "GAME_INFO": 16,
+ "CALENDAR_INFO": 255,
+ "TIMEOUT_INFO": 244,
+ "MAP_INFO": 17,
+ "NUKE_TILE_INFO": 18,
+ /*
+ "TEAM_NAME_INFO": 19,
+ */
+ "ACHIEVEMENT_INFO": 238,
+ "CHAT_MSG": 25,
+ "EARLY_CHAT_MSG": 28,
+ "CHAT_MSG_REQ": 26,
+ "CONNECT_MSG": 27,
+ "SERVER_INFO": 29,
+ "CITY_REMOVE": 30,
+ "CITY_INFO": 31,
+ "CITY_SHORT_INFO": 32,
+ "TRADEROUTE_INFO": 249,
+ "CITY_SELL": 33,
+ "CITY_BUY": 34,
+ "CITY_CHANGE": 35,
+ "CITY_WORKLIST": 36,
+ "CITY_MAKE_SPECIALIST": 37,
+ "CITY_MAKE_WORKER": 38,
+ "CITY_CHANGE_SPECIALIST": 39,
+ "CITY_RENAME": 40,
+ "CITY_OPTIONS_REQ": 41,
+ "CITY_REFRESH": 42,
+ "CITY_NAME_SUGGESTION_REQ": 43,
+ "CITY_NAME_SUGGESTION_INFO": 44,
+ "CITY_SABOTAGE_LIST": 45,
+ "CITY_MANAGER": 139,
+ "CITY_RALLY_POINT": 138,
+ "WORKER_TASK": 241,
+ "PLAYER_REMOVE": 50,
+ "PLAYER_INFO": 51,
+ "PLAYER_PHASE_DONE": 52,
+ "PLAYER_RATES": 53,
+ "PLAYER_CHANGE_GOVERNMENT": 54,
+ "PLAYER_PLACE_INFRA": 61,
+ "PLAYER_ATTRIBUTE_BLOCK": 57,
+ "PLAYER_ATTRIBUTE_CHUNK": 58,
+ /*
+ "PLAYER_DIPLSTATE": 59,
+ */
+ "PLAYER_MULTIPLIER": 242,
+ "RESEARCH_INFO": 60,
+ "PLAYER_TECH_GOAL": 56,
+ "UNIT_REMOVE": 62,
+ "UNIT_INFO": 63,
+ "UNIT_SHORT_INFO": 64,
+ "UNIT_COMBAT_INFO": 65,
+ "UNIT_SSCS_SET": 71,
+ "UNIT_ORDERS": 73,
+ "UNIT_AUTOSETTLERS": 74,
+ "UNIT_ACTION_QUERY": 82,
+ "UNIT_TYPE_UPGRADE": 83,
+ "UNIT_DO_ACTION": 84,
+ "UNIT_ACTION_ANSWER": 85,
+ "UNIT_GET_ACTIONS": 87,
+ "UNIT_ACTIONS": 90,
+ "UNIT_CHANGE_ACTIVITY": 222,
+ "DIPLOMACY_INIT_MEETING_REQ": 95,
+ "DIPLOMACY_INIT_MEETING": 96,
+ "DIPLOMACY_CANCEL_MEETING_REQ": 97,
+ "DIPLOMACY_CANCEL_MEETING": 98,
+ "DIPLOMACY_CREATE_CLAUSE_REQ": 99,
+ "DIPLOMACY_CREATE_CLAUSE": 100,
+ "DIPLOMACY_REMOVE_CLAUSE_REQ": 101,
+ "DIPLOMACY_REMOVE_CLAUSE": 102,
+ "DIPLOMACY_ACCEPT_TREATY_REQ": 103,
+ "DIPLOMACY_ACCEPT_TREATY": 104,
+ "DIPLOMACY_CANCEL_PACT": 105,
+ "PAGE_MSG": 110,
+ "PAGE_MSG_PART": 250,
+ "REPORT_REQ": 111,
+ "CONN_INFO": 115,
+ /*
+ "CONN_PING_INFO": 116,
+ "CONN_PING": 88,
+ "CONN_PONG": 89,
+ */
+ "CLIENT_HEARTBEAT": 254,
+ "CLIENT_INFO": 119,
+ "END_PHASE": 125,
+ "START_PHASE": 126,
+ "NEW_YEAR": 127,
+ "BEGIN_TURN": 128,
+ "END_TURN": 129,
+ "FREEZE_CLIENT": 130,
+ "THAW_CLIENT": 131,
+ "SPACESHIP_LAUNCH": 135,
+ "SPACESHIP_PLACE": 136,
+ /*
+ "SPACESHIP_INFO": 137,
+ */
+ /* commenting out packets lets us turn off reporting of them
+ "RULESET_UNIT": 140,
+ "RULESET_UNIT_BONUS": 228,
+ "RULESET_UNIT_FLAG": 229,
+ "RULESET_UNIT_CLASS_FLAG": 230,
+ "RULESET_GAME": 141,
+ "RULESET_SPECIALIST": 142,
+ "RULESET_GOVERNMENT_RULER_TITLE": 143,
+ "RULESET_TECH": 144,
+ "RULESET_TECH_CLASS": 9,
+ "RULESET_TECH_FLAG": 234,
+ "RULESET_GOVERNMENT": 145,
+ "RULESET_TERRAIN_CONTROL": 146,
+ "RULESETS_READY": 225,
+ "RULESET_NATION_SETS": 236,
+ "RULESET_NATION_GROUPS": 147,
+ "RULESET_NATION": 148,
+ "NATION_AVAILABILITY": 237,
+ "RULESET_STYLE": 239,
+ "RULESET_CITY": 149,
+ "RULESET_BUILDING": 150,
+ "RULESET_TERRAIN": 151,
+ "RULESET_TERRAIN_FLAG": 231,
+ "RULESET_UNIT_CLASS": 152,
+ "RULESET_EXTRA": 232,
+ "RULESET_EXTRA_FLAG": 226,
+ "RULESET_BASE": 153,
+ "RULESET_ROAD": 220,
+ "RULESET_GOODS": 248,
+ "RULESET_DISASTER": 224,
+ "RULESET_ACHIEVEMENT": 233,
+ "RULESET_TRADE": 227,
+ "RULESET_ACTION": 246,
+ "RULESET_ACTION_ENABLER": 235,
+ "RULESET_ACTION_AUTO": 252,
+ "RULESET_MUSIC": 240,
+ "RULESET_MULTIPLIER": 243,
+ "RULESET_EFFECT": 175,
+ "RULESET_RESOURCE": 177,
+ "RULESET_CLAUSE": 512,*/
+ "RULESET_CONTROL": 155,
+ /*
+ "RULESET_SUMMARY": 251,
+ "RULESET_DESCRIPTION_PART": 247,
+ */
+ "SINGLE_WANT_HACK_REQ": 160,
+ "SINGLE_WANT_HACK_REPLY": 161,
+ "RULESET_CHOICES": 162,
+ "GAME_LOAD": 163,
+ "SERVER_SETTING_CONTROL": 164,
+ /*
+ "SERVER_SETTING_CONST": 165,
+ "SERVER_SETTING_BOOL": 166,
+ "SERVER_SETTING_INT": 167,
+ "SERVER_SETTING_STR": 168,
+ "SERVER_SETTING_ENUM": 169,
+ "SERVER_SETTING_BITWISE": 170,
+ */
+ "SET_TOPOLOGY": 253,
+ "SCENARIO_INFO": 180,
+ "SCENARIO_DESCRIPTION": 13,
+ "SAVE_SCENARIO": 181,
+ "VOTE_NEW": 185,
+ "VOTE_UPDATE": 186,
+ "VOTE_REMOVE": 187,
+ "VOTE_RESOLVE": 188,
+ "VOTE_SUBMIT": 189,
+ "EDIT_MODE": 190,
+ "EDIT_RECALCULATE_BORDERS": 197,
+ "EDIT_CHECK_TILES": 198,
+ "EDIT_TOGGLE_FOGOFWAR": 199,
+ "EDIT_TILE_TERRAIN": 200,
+ "EDIT_TILE_EXTRA": 202,
+ "EDIT_STARTPOS": 204,
+ "EDIT_STARTPOS_FULL": 205,
+ "EDIT_TILE": 206,
+ "EDIT_UNIT_CREATE": 207,
+ "EDIT_UNIT_REMOVE": 208,
+ "EDIT_UNIT_REMOVE_BY_ID": 209,
+ "EDIT_UNIT": 210,
+ "EDIT_CITY_CREATE": 211,
+ "EDIT_CITY_REMOVE": 212,
+ "EDIT_CITY": 213,
+ "EDIT_PLAYER_CREATE": 214,
+ "EDIT_PLAYER_REMOVE": 215,
+ "EDIT_PLAYER": 216,
+ "EDIT_PLAYER_VISION": 217,
+ "EDIT_GAME": 218,
+ "EDIT_SCENARIO_DESC": 14,
+ "EDIT_OBJECT_CREATED": 219,
+ "PLAY_MUSIC": 245,
+ "WEB_CITY_INFO_ADDITION": 256,
+ /*
+ "WEB_PLAYER_INFO_ADDITION": 257,
+ "WEB_RULESET_UNIT_ADDITION": 258,
+ */
+ "GOTO_PATH_REQ": 287,
+ "GOTO_PATH": 288,
+ "INFO_TEXT_REQ": 289,
+ "INFO_TEXT_MESSAGE": 290,
+ "ONGOING_LONGTURN_NATION_SELECT_REQ": 291,
+ "RALLY_PATH_REQ": 292
+ };
const auto_attack_actions = [
ACTION_ATTACK, ACTION_SUICIDE_ATTACK,
@@ -33,9 +272,6 @@ var last_ground_attack_time = 0;
This file contains the handling-code for packets from the civserver.
*/
-var DEBUG_LOG_PACKETS = false;
-
-
function handle_processing_started(packet)
{
client_frozen = true;
@@ -44,6 +280,28 @@ function handle_processing_started(packet)
function handle_processing_finished(packet)
{
client_frozen = false;
+ update_ui_after_thaw();
+}
+
+/* Called after "thaw" or "processing_finished", to process queued UI
+ updates (prevents refreshing UI more times than needed.) */
+function update_ui_after_thaw() {
+ if (ui_update_nations_info) {
+ ui_update_nations_info = false;
+ /* Update relevant active tabs with altered players/nations info: */
+ var active_tab = $("#tabs").tabs("option", "active");
+ if (active_tab == TAB_EMPIRE) empire_screen_updater.update();
+ else if (active_tab = TAB_NATIONS) update_nation_screen();
+ }
+ if (ui_update_cities_info) {
+ ui_update_cities_info = false;
+ var active_tab = $("#tabs").tabs("option", "active");
+ if (active_tab == TAB_CITIES) city_screen_updater.update();
+ }
+ if (ui_update_bulbs_info) {
+ ui_update_bulbs_info = false;
+ bulbs_output_updater.update();
+ }
}
function handle_freeze_hint(packet)
@@ -54,6 +312,7 @@ function handle_freeze_hint(packet)
function handle_thaw_hint(packet)
{
client_frozen = false;
+ update_ui_after_thaw();
}
/* 100% */
@@ -98,6 +357,9 @@ function handle_server_join_reply(packet)
}
if (autostart) {
+ if (renderer == RENDERER_WEBGL) {
+ $.blockUI({ message: '
Generating terrain map model...
' });
+ }
if (loadTimerId == -1) {
wait_for_text("You are logged in as", pregame_start_game);
} else {
@@ -180,6 +442,12 @@ function handle_tile_info(packet)
if (tiles != null) {
packet['extras'] = new BitVector(packet['extras']);
+ if (renderer == RENDERER_WEBGL) {
+ var old_tile = $.extend({}, tiles[packet['tile']]);
+ webgl_update_tile_known(tiles[packet['tile']], packet);
+ update_tile_extras($.extend(old_tile, packet));
+ }
+
tiles[packet['tile']] = $.extend(tiles[packet['tile']], packet);
}
}
@@ -195,22 +463,24 @@ function handle_iPillage_event(message, tile_id) // iPillage.
var ptype;
var delay = 0;
- message=message.substring(8); // removes "💥 Your ", leaving, "Ground Strike Fighter destroyed the Roman Quay with a Ground Strike"
-
+ //console.log("Intercepted an iPillage");
for (ptype_id in unit_types) {
// Find out the unit_type who did this hostile deed !
ptype = unit_types[ptype_id];
- if (message.startsWith(ptype['name'])) {
+ //needed to prevent false positives
+ if (message.includes(ptype['name']) && utype_can_iPillage(ptype)) {
// The alt sound is preferred for alternate attack types:
var sskey = ptype['sound_fight_alt'];
- if (!sskey) sskey = ptype['sound_fight']; //fallback
- if (!soundset[sskey]) sskey = ptype['sound_fight']; //fallback
- //console.log("calling playsound with sskey:"+sskey);
+ if (sskey) sskey = sskey.replace(/ /g, "_").toLowerCase();
+ else sskey = ptype['sound_fight']; //fallback1
+ if (!soundset[sskey]) sskey = "bomb"; //fallback2
play_hostile_event_sound(sskey, ptile, ptype);
-
break;
- }
+ }
}
+ //final fallback3 if we couldn't find a ptype for ipillage sound:
+ if (!ptype) play_hostile_event_sound("bomb", ptile, ptype);
+
// Activate explosion on the tile also.
delay = 500; // synchronise explosion with sound effect
// times for each unit are in the top of unit.js:
@@ -248,7 +518,7 @@ function handle_city_governor_event(message)
function handle_chat_msg(packet)
{
var message = packet['message'];
- var event = packet['event'];
+ var event = packet['event'];
var conn_id = packet['conn_id'];
var tile_id = packet['tile'];
@@ -261,8 +531,21 @@ function handle_chat_msg(packet)
packet['event'] = E_UNDEFINED;
}
+ /* Localize event timestamps */
+ if (message.includes("class='ts'")) message = time_localize(message);
+
/* Event interceptions... used to trigger client processing */
switch (event) {
+ case E_CITY_LOST:
+ /* play_sound(soundset["e_conquer"]
+ */
+ break;
+ case E_UNIT_WIN_ATT:
+ if (!suppress_event_sound()) {
+ if (message.includes("liberated")) play_sound(soundset["e_liberate"]);
+ else if (message.includes("looting")) play_sound(soundset["e_victor"]);
+ }
+ break;
case E_UNIT_ACTION_TARGET_HOSTILE:
handle_iPillage_event(message, tile_id);
break;
@@ -275,6 +558,27 @@ function handle_chat_msg(packet)
case E_CHAT_MSG_PRIVATE_RCVD:
if (!suppress_event_sound()) play_sound("iphone1.ogg");
break;
+ case E_ENEMY_DIPLOMAT_SABOTAGE:
+ play_sound("e_demolition.ogg");
+ break;
+ case E_MY_DIPLOMAT_SABOTAGE:
+ if (!suppress_event_sound()) {
+ if (message.includes("destroyed the City Walls")) play_sound("e_destroy_walls.ogg");
+ else play_sound("e_demolition.ogg");
+ }
+ break;
+ case E_TECH_GAIN:
+ /* in MP2D, discovery of democracy upgrades all workers to workers II */
+ if (client_rules_flag[CRF_MP2_D] && !client_is_observer() && message.includes("Democracy")
+ && tech_known('Democracy')) {
+ let upgrade_type = utype_id_by_name("Workers");
+ let upgrade_packet = {
+ "pid" : packet_unit_type_upgrade,
+ "type" : upgrade_type
+ };
+ setTimeout(send_request(JSON.stringify(upgrade_packet),1000));
+ }
+ break;
//case E_CHAT_MSG_PRIVATE_SENT:
// break;
}
@@ -302,20 +606,8 @@ function handle_chat_msg(packet)
packet['message'] = message;
add_chatbox_text(packet);
-
- // Do sounds for special messages
- //console.log("Event type = "+packet['event']);
- // TODO: these can now go into event interceptor above by looking at event class instead of here.
- if (packet['event']==1 || packet['event']==77) /* 1==E_CITY_LOST, 77==E_UNIT_WIN_ATT, there are in events.h and are the two
- types of event that come back for cities being conquered by someone.*/
- {
- if (message.includes(" your lootings accumulate to ")) play_sound(soundset["e_victor"]);
- else if (message.includes("You have liberated ")) play_sound(soundset["e_liberate"]);
- else if (message.includes(" gold from the city.")) play_sound(soundset["e_conquer"]);
- }
}
-
/**************************************************************************
Recenter the map to a tile and mark it with an explosion
TODO: replace with a flare or similar animation
@@ -358,6 +650,7 @@ function decode_user_hyperlinks(message)
var freemoji_name = message.substring(message.indexOf("[`") + 2, message.indexOf("`]"));
var replace_me = "[`"+freemoji_name+"`]";
+ freemoji_name = freemoji_name.replace("[","-").replace("]","-"); // replace [] with - in animal emoji names
var element = html_emoji_from_universal(freemoji_name);
message = message.replace( replace_me, (element) );
@@ -447,7 +740,7 @@ function handle_early_chat_msg(packet)
/***************************************************************************
The city_info packet is used when the player has full information about a
- city, including it's internals.
+ city, including its internals.
It is followed by web_city_info_addition that gives additional
information only needed by Freeciv-web. Its processing will therefore
@@ -488,7 +781,7 @@ function handle_city_info(packet)
The web_city_info_addition packet is a follow up packet to
city_info packet. It gives some information the C clients calculates on
their own. It is used when the player has full information about a city,
- including it's internals.
+ including its internals.
***************************************************************************/
function handle_web_city_info_addition(packet)
{
@@ -518,13 +811,18 @@ function handle_web_city_info_addition(packet)
if (worklist_dialog_active && active_city != null) {
city_worklist_dialog(active_city);
}
+
+ if (renderer == RENDERER_WEBGL) {
+ update_city_position(index_to_tile(packet['tile']));
+ }
+
/* Update active tabs affected by this info */
- bulbs_output_updater.update();
+ ui_update_bulbs_info = true;
var active_tab = $("#tabs").tabs("option", "active");
if (active_tab == TAB_CITIES) {
- city_screen_updater.update();
+ ui_update_cities_info = true;
} else if (active_tab == TAB_EMPIRE) {
- empire_screen_updater.update(); // TEST:is this the right way to update empire screen?
+ ui_update_nations_info = true;
}
income_needs_refresh = true;
}
@@ -545,13 +843,17 @@ function handle_city_short_info(packet)
cities[packet['id']] = $.extend(cities[packet['id']], packet);
}
+ if (renderer == RENDERER_WEBGL) {
+ update_city_position(index_to_tile(packet['tile']));
+ }
+
/* Update active tabs affected by this info */
- bulbs_output_updater.update();
+ ui_update_bulbs_info = true;
var active_tab = $("#tabs").tabs("option", "active");
if (active_tab == TAB_CITIES) {
- city_screen_updater.update();
+ ui_update_cities_info = true;
} else if (active_tab == TAB_EMPIRE) {
- empire_screen_updater.update(); // TEST:is this the right way to update empire screen?
+ ui_update_nations_info = true;
}
income_needs_refresh = true;
}
@@ -594,11 +896,8 @@ function handle_player_info(packet)
nations[pplayer['nation']]['color'] = pcolor;
}
} */
-
- /* Update active tabs affected by this info */
- var active_tab = $("#tabs").tabs("option", "active");
- if (active_tab == TAB_EMPIRE) {
- empire_screen_updater.update(); // TEST:is this the right way to update empire screen?
+ if (C_S_RUNNING == client_state()) {
+ ui_update_nations_info = true;
}
}
@@ -614,6 +913,15 @@ function handle_web_player_info_addition(packet)
if (client.conn.playing != null) {
if (packet['playerno'] == client.conn.playing['playerno']) {
client.conn.playing = players[packet['playerno']];
+
+ /* Force update of bulbs_researched which doesn't get server update
+ after LUA Player:give_bulbs(..). TODO: Remove after upstream fixes
+ this bug: */
+ var cur_tech = client.conn.playing['researching'];
+ client.conn.playing['bulbs_researched'] = client.conn.playing.advance_saved_bulbs[cur_tech];
+
+ // TODO: should this be handled only once in handle_processing_finished() which is flagged
+ // to be processed there by setting ui_update_nations_info = true (similar to func above?)
update_game_status_panel();
update_net_income();
income_needs_refresh = false;
@@ -672,13 +980,26 @@ function handle_map_info(packet)
mapdeco_init();
+ if (renderer == RENDERER_WEBGL) {
+ mapview_model_width = Math.floor(MAPVIEW_ASPECT_FACTOR * map['xsize']);
+ mapview_model_height = Math.floor(MAPVIEW_ASPECT_FACTOR * map['ysize']);
+ }
+
}
/* 100% complete */
function handle_game_info(packet)
{
game_info = packet;
- if (is_ongoing_longturn() && C_S_PREPARING == client_state()) wait_for_text("You are logged in as", pick_nation_ongoing_longturn);
+ if (is_ongoing_longturn() && C_S_PREPARING == client_state()) {
+ if (DEBUG_PICK_NATION) {
+ console.log("handle_game_info is doing wait_for_text in order to pick nation");
+ }
+ wait_for_text("You are logged in as", pick_nation_ongoing_longturn);
+ } else {
+ if (DEBUG_PICK_NATION)
+ console.log("handle_game_info won't pick nation: (is_ongoing_longturn() && C_S_PREPARING == client_state()) == false");
+ }
}
/**************************************************************************
@@ -728,7 +1049,6 @@ function handle_ruleset_control(packet)
// Legacy rules block:
switch (rules) {
case "MP2":
- case "Avant-garde 2":
case "Avant-garde":
client_rules_flag[CRF_MP2_A]=true;
client_rules_flag[CRF_RADAR_TOWER]=true;
@@ -839,31 +1159,26 @@ function handle_ruleset_control(packet)
improvements_init();
- /* handle_ruleset_extra defines some variables dynamically */
+ /* handle_ruleset_extra() defines some variables dynamically */
for (var extra in extras) {
var ename = extras[extra]['name'];
delete window["EXTRA_" + ename.toUpperCase()];
if (ename == "Railroad") delete window["EXTRA_RAIL"];
else if (ename == "Oil Well") delete window["EXTRA_OIL_WELL"];
else if (ename == "Minor Tribe Village") delete window["EXTRA_HUT"];
- else if (typeof EXTRA_SEABRIDGE !== 'undefined') {
- if (ename == "Sea Bridge") delete window["EXTRA_SEABRIDGE"];
- }
- else if (typeof EXTRA_FORT !== 'undefined') { //makes sure it's defined first
- if (ename == "Fort") delete window["EXTRA_FORT"]; /////
- } else if (typeof EXTRA_NAVALBASE !== 'undefined') {
- if (ename =="Naval Base") delete window["EXTRA_NAVALBASE"]
- } else if (typeof EXTRA_CASTLE !== 'undefined') {
- if (ename =="Castle") delete window["EXTRA_CASTLE"]
- } else if (typeof EXTRA_BUNKER !== 'undefined') {
- if (ename =="Bunker") delete window["EXTRA_BUNKER"]
- } else if (typeof EXTRA_TILE_CLAIM !== 'undefined') {
- if (ename =="Tile Claim") delete window["EXTRA_TILE_CLAIM"]
- } else if (typeof EXTRA_WALLS !== 'undefined') {
- if (ename =="Walls") delete window["EXTRA_WALLS"]
+ else if (typeof EXTRA_SEABRIDGE !== 'undefined' && ename == "Sea Bridge") delete window["EXTRA_SEABRIDGE"];
+ else if (typeof EXTRA_FORT !== 'undefined' && ename == "Fort") delete window["EXTRA_FORT"];
+ else if (typeof EXTRA_NAVALBASE !== 'undefined' && ename =="Naval Base") delete window["EXTRA_NAVALBASE"];
+ else if (typeof EXTRA_CASTLE !== 'undefined' && ename =="Castle") delete window["EXTRA_CASTLE"];
+ else if (typeof EXTRA_BUNKER !== 'undefined' && ename =="Bunker") delete window["EXTRA_BUNKER"];
+ else if (typeof EXTRA_TILE_CLAIM !== 'undefined' && ename =="Tile Claim") delete window["EXTRA_TILE_CLAIM"];
+ else if (typeof EXTRA_WALLS !== 'undefined' && ename =="Walls") delete window["EXTRA_WALLS"];
+ else if (extras[extra].rule_name == "Depth") {
+ delete window["EXTRA_"]
}
}
extras = {};
+ set_blank_extras();
/* Reset legal diplomatic clauses. */
clause_infos = {};
@@ -912,7 +1227,15 @@ function handle_non_integer_combat_scores(key)
unit_types[key].attack_strength += 0.5;
}
else if (unit_types[key]['name']=="Fighter") {
- unit_types[key].defense_strength += 0.5; // easier than *= 1.666forever
+ unit_types[key].defense_strength += 0.5;
+ }
+ else if (unit_types[key]['name']=="Crusaders") {
+ if (client_rules_flag[CRF_MP2_D])
+ unit_types[key].defense_strength += 0.5;
+ }
+ else if (unit_types[key]['name']=="Dive Bomber") {
+ if (client_rules_flag[CRF_MP2_D])
+ unit_types[key].attack_strength += 1.5; //represents the majority of its targets it has a 50% or +1.5 bonus on.
}
else {
return; // skip message
@@ -969,7 +1292,11 @@ function handle_nuke_tile_info(packet)
{
var ptile = index_to_tile(packet['tile']);
- ptile['nuke'] = 60;
+ if (renderer == RENDERER_WEBGL) {
+ render_nuclear_explosion(ptile);
+ } else {
+ ptile['nuke'] = 60;
+ }
play_sound('nuclear_distant.ogg');
@@ -1074,6 +1401,10 @@ function handle_unit_remove(packet)
clear_tile_unit(punit);
client_remove_unit(punit);
+ if (renderer == RENDERER_WEBGL) {
+ update_unit_position(index_to_tile(punit['tile']));
+ }
+
}
/* 100% complete */
@@ -1083,7 +1414,7 @@ function handle_unit_info(packet)
/* Update active tabs affected by this info */
var active_tab = $("#tabs").tabs("option", "active");
if (active_tab == TAB_EMPIRE) {
- empire_screen_updater.update(); // TEST:is this the right way to update empire screen?
+ ui_update_nations_info = true;
}
}
@@ -1093,8 +1424,8 @@ function handle_unit_short_info(packet)
handle_unit_packet_common(packet);
/* Update active tabs affected by this info */
var active_tab = $("#tabs").tabs("option", "active");
- if (active_tab == TAB_EMPIRE) {
- empire_screen_updater.update(); // TEST:is this the right way to update empire screen?
+ if (active_tab == TAB_EMPIRE) {
+ ui_update_nations_info = true;
}
}
@@ -1253,6 +1584,11 @@ function handle_unit_packet_common(packet_unit)
}
}
}
+ if (renderer == RENDERER_WEBGL) {
+ if (punit != null) update_unit_position(old_tile);
+ update_unit_position(index_to_tile(units[packet_unit['id']]['tile']));
+ }
+
/* TODO: update various dialogs and mapview. */
}
@@ -1265,7 +1601,11 @@ function handle_unit_combat_info(packet)
var tile_x = tiles[attacker['tile']]['x'];
var tile_y = tiles[attacker['tile']]['y'];
-
+ if (renderer == RENDERER_WEBGL) {
+ if (attacker_hp == 0) animate_explosion_on_tile(attacker['tile'], 0, false);
+ if (defender_hp == 0) animate_explosion_on_tile(defender['tile'], 0, false);
+ // TO DO: WEBGL is missing out on all this below, which we should put in after it's final
+ } else {
// Might be null/false if observer
var pplayer = null;
@@ -1376,6 +1716,7 @@ function handle_unit_combat_info(packet)
//setTimeout(update_unit_focus, 700); // remove this if unit redraw still doesn't work
// --------------------------------------------------------------------------------------------------------------------
}
+ }
}
/**************************************************************************
@@ -1426,12 +1767,12 @@ function handle_unit_action_answer(packet)
return;
}
} else if (action_type == ACTION_UPGRADE_UNIT) {
- if (target_city == null) {
+ /*if (target_city == null) { // Target city no longer required.
console.log("Bad target city (" + target_id
+ ") in unit action answer.");
act_sel_queue_done(diplomat_id);
return;
- } else {
+ } else */ {
popup_unit_upgrade_dlg(actor_unit, target_city, cost, action_type);
return;
}
@@ -1688,6 +2029,7 @@ function handle_freeze_client(packet)
function handle_thaw_client(packet)
{
client_frozen = false;
+ update_ui_after_thaw();
}
function handle_spaceship_info(packet)
@@ -2105,6 +2447,9 @@ function handle_player_diplstate(packet)
contact_turns_left: packet['contact_turns_left'],
has_reason_to_cancel: packet['has_reason_to_cancel']
};
+
+ // Update Nations tab with incoming changed state (if it's active)
+ ui_update_nations_info = true;
}
/**************************************************************************
@@ -2146,6 +2491,31 @@ function handle_ruleset_extra(packet)
if (packet['name'] == "Bunker") window["EXTRA_BUNKER"] = packet['id'];
if (packet['name'] == "Tile Claim") window["EXTRA_TILE_CLAIM"] = packet['id'];
if (packet['name'] == "Walls") window["EXTRA_WALLS"] = packet['id'];
+ if (packet['rule_name'] == "Depth") window["EXTRA_DEEPDIVE"] = packet['id'];
+}
+
+/**************************************************************************
+ Much of FCW was hard-coded expecting certain rulesets which all share
+ specific extras. This made problems if rulesets lack those Extras.
+ This HACK is a bandage for bad hard-coding that fails because of
+ those hard-coded expectations. TODO: make this function do nothing, find
+ where those rulesets crash, then alter that hard-coding to not assume
+ those certain extras exist.
+**************************************************************************/
+function set_blank_extras()
+{
+ window["EXTRA_FARMLAND"] = EXTRA_NOT_EXIST;
+ window["EXTRA_IRRIGATION"] = EXTRA_NOT_EXIST;
+ window["EXTRA_RIVER"] = EXTRA_NOT_EXIST;
+ window["EXTRA_FORTRESS"] = EXTRA_NOT_EXIST;
+ window["EXTRA_HUT"] = EXTRA_NOT_EXIST;
+ window["EXTRA_RAILROAD"] = EXTRA_NOT_EXIST;
+ window["EXTRA_RAIL"] = EXTRA_NOT_EXIST;
+ window["EXTRA_OIL_WELL"] = EXTRA_NOT_EXIST;
+ window["EXTRA_FALLOUT"] = EXTRA_NOT_EXIST;
+ window["EXTRA_AIRBASE"] = EXTRA_NOT_EXIST;
+ window["EXTRA_BUOY"] = EXTRA_NOT_EXIST;
+ window["EXTRA_RUINS"] = EXTRA_NOT_EXIST;
}
/**************************************************************************
@@ -2271,7 +2641,7 @@ function handle_research_info(packet)
}
if (is_tech_tree_init && tech_dialog_active) update_tech_screen();
- bulbs_output_updater.update();
+ ui_update_bulbs_info = true;
}
function handle_worker_task(packet)
diff --git a/freeciv-web/src/main/webapp/javascript/pages.js b/freeciv-web/src/main/webapp/javascript/pages.js
index 1c1d60b5d..22f8d1a79 100644
--- a/freeciv-web/src/main/webapp/javascript/pages.js
+++ b/freeciv-web/src/main/webapp/javascript/pages.js
@@ -67,6 +67,7 @@ function set_client_page(page)
break;
case PAGE_GAME:
$("#game_page").show();
+ if (renderer == RENDERER_WEBGL) webgl_start_renderer();
set_chat_direction(null);
break;
diff --git a/freeciv-web/src/main/webapp/javascript/player.js b/freeciv-web/src/main/webapp/javascript/player.js
index e4649b2bb..334078473 100644
--- a/freeciv-web/src/main/webapp/javascript/player.js
+++ b/freeciv-web/src/main/webapp/javascript/player.js
@@ -321,15 +321,28 @@ function get_invalid_username_reason(username)
else if (username.length >= 32) {
return "too long";
}
- username = username.toLowerCase();
- if (username == "pbem") {
+ if (username.toLowerCase() == "pbem"
+ || username.toLowerCase() == "kickedplayer"
+ || username.toLowerCase().startsWith("newavailableplayer")
+ ) {
return "not available";
- }
- /*else if (username.search(/^[a-z][a-z0-9]*$/g) != 0) {
- return "invalid: only English letters and numbers are allowed, and must start with a letter";
- } */
+ }
+ else if (username.includes(" ")) {
+ return "invalid: name cannot have spaces";
+ }
+ else if (!is_alphabetic(username.charAt(0))) {
+ return "invalid: the first symbol must be an unaccented Latin letter";
+ }
+ else if (username.includes("'") || username.includes('&')
+ || username.includes('@') || username.includes('!')
+ || username.includes('(') || username.includes(')')
+ || username.includes('+') || username.includes('*')
+ || username.includes('/') || username.includes(',')
+ || username.includes('#') || username.includes(';') ) {
+ return "disallowed. Illegal symbols: \" ' \\ & ^ % : $ ! ~ + / * = ? , etc."
+ }
else if (username != alphanumeric_cleaner(username)) {
- return "invalid: only alphabetic letters and numbers are allowed, and must start with a letter";
+ return "invalid: only alphanumeric and hyphenation allowed";
}
else if (!check_text_with_banlist_exact(username)) {
return "banned";
diff --git a/freeciv-web/src/main/webapp/javascript/pregame.js b/freeciv-web/src/main/webapp/javascript/pregame.js
index 33a389dcd..6b6543cae 100644
--- a/freeciv-web/src/main/webapp/javascript/pregame.js
+++ b/freeciv-web/src/main/webapp/javascript/pregame.js
@@ -35,6 +35,9 @@ var google_user_token = null;
****************************************************************************/
function pregame_start_game()
{
+ if (DEBUG_PICK_NATION) {
+ console.log("pregame_start_game called by "+pregame_start_game.caller)
+ }
// Successfully prevents the keyboard from popping up on Mobile; which also
// force-exits from Fullscreen on Firefox Mobile and some other browsers:
if (is_touch_device()) $("#game_text_input").hide();
@@ -103,7 +106,7 @@ function check_browser_compatibility()
// Safari 3.0+ "[object HTMLElementConstructor]"
var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
*/
- var isIE = /*@cc_on!@*/false || !!document.documentMode; // Internet Explorer 6-11
+ var isIE = /* @cc_on!@ || */ !!document.documentMode; // Internet Explorer 6-11
/*
var isEdge = !isIE && !!window.StyleMedia; // Edge 20+
var isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime); // Chrome 1 - 71
@@ -230,7 +233,7 @@ function update_player_info_pregame_real()
var nation_text = "";
if (player['nation'] in nations) {
nation_text = " - " + nations[player['nation']]['adjective'];
- var flag_html = $("");
+ var flag_html = $("");
$("#pregame_plr_"+id).prepend(flag_html);
var flag_canvas = document.getElementById('pregame_nation_flags_' + id);
if (flag_canvas == null) continue;
@@ -340,10 +343,30 @@ function pick_nation_ongoing_longturn()
$("#pick_nation_button").hide();
if (!is_loaded_game()) {
$("#start_game_button").hide();
+ if (DEBUG_PICK_NATION) console.log("is_loaded_game()==false triggers player to pick nation")
pick_nation(null);
+ } else {
+ if (DEBUG_PICK_NATION) console.log("is_loaded_game()==true disallows player to pick nation")
}
}
+/**********************************************************************//**
+ Returns the # of turns idle a player has to be, at this point in the
+ game, to be taken over by a latejoiner player. Keep this code
+ identical with connecthand.c:can_take_idler_turns() ********* !!!
+**************************************************************************/
+function can_take_idler_turns()
+{
+ // Keep this code identical with connecthand.c.:can_take_idler_turns() !!!
+ /* Turns 1-12: replace idle 3. T12+ increase idle cutoff until max cutoff of 10 */
+ var threshold = 3;
+ if (game_info.turn > 12) threshold += (game_info.turn - 12);
+ if (threshold > 10) threshold = 10;
+ //
+
+ return threshold;
+}
+
/****************************************************************************
Shows the pick nation dialog.
****************************************************************************/
@@ -355,27 +378,37 @@ function pick_nation(player_id)
var pplayer = players[player_id];
var player_nations = {};
- if (pplayer == null && !is_ongoing_longturn()) return;
+ if (pplayer == null && !is_ongoing_longturn()) {
+ if (DEBUG_PICK_NATION) console.log("Unable to pick nation because pplayer==null && !is_ongoing_longturn()");
+ return;
+ }
choosing_player = player_id;
var player_name = !is_ongoing_longturn() ? pplayer['name'] : simpleStorage.get("username", "");
if (is_ongoing_longturn()) {
- /* We only show the nations that are not already taken by human players */
+ /* If player took an idler they can't change the nation of it: */
+ var idle_cutoff = can_take_idler_turns();
+ if (pplayer && pplayer['nturns_idle'] !== undefined && pplayer['nturns_idle'] >= idle_cutoff) {
+ if (DEBUG_PICK_NATION) console.log("Unable to pick nation it's assumed we took an idler.");
+ return;
+ }
+ /* We only show the nations that are not already taken by human players */
for (id in players) {
if (players[id]['name'].indexOf("NewAvailablePlayer") == -1) {
- // Keep this code identical with connecthand.c:can_take_idler_turns() ********* !!!
- var idle_cutoff = 3;
- if (game_info.turn > 12) idle_cutoff += (game.info.turn - 12);
- if (idle_cutoff > 10) idle_cutoff = 10;
- //
- if (players[id]['nturns_idle'] >= idle_cutoff) return; /* The joining player would have taken over an idler, don't show the dialog */
- else player_nations[players[id]['nation']] = true;
+ /* 22Nov2021 We no longer can rely on the existence of an idler to mean that
+ the player was assigned an idler; we actually try to assign non-idlers
+ first ....
+ if (players[id]['nturns_idle'] >= idle_cutoff) return;
+ else
+ // The joining player would have taken over an idler, don't show the dialog
+ */
+ player_nations[players[id]['nation']] = true;
}
}
if (Object.keys(players).length == Object.keys(player_nations).length) {
- add_client_message("Unable to join the game, it is full.");
- return;
+ add_client_message("Unable to join the game, it is full ... Please ignore this message if you were the first to join.");
+ return;
}
}
@@ -646,7 +679,7 @@ function ruledir_from_ruleset_name(ruleset_name, fall_back_dir)
case "Multiplayer-Evolution ruleset":
return "mp2";
case "Avant-garde":
- return "ag";
+ return "mp2-ag";
default:
/* Prevent the need of hardcoding this client function for every new ruleset, by
making a way for fall_back_dir to match the name of the ruleset automatically:
@@ -685,7 +718,7 @@ function show_ruleset_description_full() {
},
height : $("#pregame_settings").dialog("option",
"height"),
- width : "80%"
+ width : ($(window).width<900?"95%":856)
});
$(id).css("color", default_dialog_text_color);
}
@@ -707,13 +740,16 @@ function pregame_settings()
+ "
"
+ "
Ruleset:
"
+ "
"
+ "
Game title:
" +
"
" +
@@ -774,7 +810,7 @@ function pregame_settings()
"
" +
"
" +
"
Font on map:
" +
- "
" +
+ "
" +
"
" +
"
Enable speech audio messages
" +
"
" +
@@ -1299,26 +1335,27 @@ function show_intro_dialog(title, message) {
**************************************************************************/
function show_longturn_intro_dialog() {
- var title = "Welcome to Freeciv-web: One Turn per Day!";
-
- var message = " This is a Freeciv-web: One Turn per Day game, which is a Freeciv multiplayer game "+
- "where the turns are 23 hours each, so players logs in once every day to do their turn. This format allows for more players to "+
- "play at once, more time to strategize, more time to coordinate with other players, and less rushing to get things done, which can "+
- "occur in a standard multi-player Freeciv game. It takes a lot longer to play a game, about 2 to 6 months, but you can play it just a "+
- "little bit every day.
"+
- "Please be polite to the other players and don't cheat. "+
- "You will get to play for turn immediately after signing up, and your next turn tomorrow. Please join the game only if you are interested in playing one turn every day. " +
- "Players who are idle for more than 12 turns can be replaced by new players. This means that idle players will continually be replaced by new players.
" +
- "Joining this game requires signing in with a player name and validated Google Account."+
+ var title = "Welcome to Freeciv-web Longturn!";
+
+ var message = "This is a Freeciv-Web Longturn game. "+
+ "Turns are 23 hours: one turn per day. There are more players, "+
+ "and more time for strategy and cooperation. "+
+ "Games last 3-4 months. You play a little bit every day.
"+
+ "Please join only if you are interested in playing one turn every day. " +
+ "Players who are idle can be replaced by new players.
Important: " +
+ "1. A Google Gmail account is needed to sign in. 2. You must access this page with \"www.\" in the address bar. "+
+ "3. Disable your ad-blocker so Google can do a sign-in pop-up. " +
"
Player name:
" +
"
" +
- "
(Please disable adblockers, then reload the page, for Google login button to work)";
+ "
(Please disable adblockers, then reload the page using www. address, for Google login to work)";
if (is_small_screen()) {
- message = "Welcome to this Freeciv-web: One Turn per Day game! Enter your player name:"+
+ title = "Freeciv-Web Longturn Game"
+ message = "This is a Freeciv-Web Longturn game. Turns are 23 hours, with more players and time for strategy. "+
+ "To sign in, use a gmail account, disable ad-block, and put www. in the url."+
"
");
$("#mouse_info_box").css('cursor', "help");
$("#mouse_info_box").tooltip({
show: { delay:0, effect:"none", duration: 0 }, hide: {delay:120, effect:"none", duration: 0}
@@ -210,6 +232,10 @@ function init_tech_screen()
}
is_tech_tree_init = true;
clicked_tech_id = null;
+
+ //Make tech tree draggable to scroll it.
+ $("#technologies").addClass("dragscroll");
+ dragscroll.reset();
}
/**************************************************************************
...
@@ -218,8 +244,6 @@ function update_tech_tree()
{
if (freeze) return;
var hy = 24;
- var hx = 48 + 160;
- hx = tech_item_width;
tech_canvas_ctx.clearRect(0, 0, 5824, 726);
@@ -237,6 +261,7 @@ function update_tech_tree()
var dx = Math.floor(reqtree[rid+'']['x'] * tech_xscale); //scale in X direction.
var dy = reqtree[rid+'']['y'];
+ var hx = get_tech_item_width(techs[ptech['req'][i]]);
// Alternating line colour sequence, each tech gets a different line colour to differentiate.
var sequence = 1+Math.round(dy/55)+Math.round(dx/45); // Create a "seed" that bumps up as we span the canvas vertically and horizontally
@@ -250,16 +275,16 @@ function update_tech_tree()
}
else { // else differentiate line colours to make tracing them easier
if (sequence == 9) tech_canvas_ctx.strokeStyle = 'rgba(144, 134, 134, 0.95)'; // grey
- else if (sequence == 8) tech_canvas_ctx.strokeStyle = 'rgba(55, 83, 204, 0.83)'; // egyptian blue
- else if (sequence == 7) tech_canvas_ctx.strokeStyle = 'rgba(81, 146, 187, 0.8)'; // medium teal-blue
+ else if (sequence == 8) tech_canvas_ctx.strokeStyle = 'rgba(55, 83, 204, 0.89)'; // egyptian blue
+ else if (sequence == 7) tech_canvas_ctx.strokeStyle = 'rgba(81, 146, 187, 0.88)'; // medium teal-blue
else if (sequence == 6) tech_canvas_ctx.strokeStyle = 'rgba(121, 127, 82, 0.88)'; // olive / ochre
- else if (sequence == 5) tech_canvas_ctx.strokeStyle = 'rgba(138, 36, 78, 0.8)'; // wine
+ else if (sequence == 5) tech_canvas_ctx.strokeStyle = 'rgba(152, 40, 85, 0.88)'; // wine
else if (sequence == 4) tech_canvas_ctx.strokeStyle = 'rgba(161, 227, 243, 0.8)'; // bright sky
else if (sequence == 3) tech_canvas_ctx.strokeStyle = 'rgba(60, 187, 146, 0.8)'; // bronze sea spray (strong green-cyan)
else if (sequence == 2) tech_canvas_ctx.strokeStyle = 'rgba(124, 108, 167, 0.95)'; // periwinkle
else if (sequence == 1) tech_canvas_ctx.strokeStyle = 'rgba(223, 223, 223, 0.8)'; // white
else tech_canvas_ctx.strokeStyle = 'rgba(189, 91, 79, 0.85)'; // coral / salmon
- tech_canvas_ctx.lineWidth = 3;
+ tech_canvas_ctx.lineWidth = 2;
}
var node_offset = 3;
@@ -291,6 +316,13 @@ function update_tech_tree()
for (var tech_id in techs) {
var ptech = techs[tech_id];
+ const is_special = (ptech.flags[0] == TECH_SPECIAL_TECH); // specialty add-on tech
+ var twidth = get_tech_item_width(ptech);
+ var theight = get_tech_item_height(ptech);
+ var tfont = get_tech_item_font(ptech);
+ const thoriz = !is_special ? 51 : 1;
+ const tvert = !is_special ? 15 : 11;
+
if (!(tech_id+'' in reqtree) || reqtree[tech_id+''] == null) continue;
var x = Math.floor(reqtree[tech_id+'']['x'] * tech_xscale)+2; //scale in X direction.
@@ -298,18 +330,18 @@ function update_tech_tree()
/* KNOWN TECHNOLOGY */
if (player_invention_state(client.conn.playing, ptech['id']) == TECH_KNOWN) {
-
- var tag = tileset_tech_graphic_tag(ptech);
tech_canvas_ctx.fillStyle = KNOWN_TECH_FILL;
- tech_canvas_ctx.fillRect(x-2, y-2, tech_item_width, tech_item_height);
+ tech_canvas_ctx.fillRect(x-2, y-2, twidth, theight);
tech_canvas_ctx.strokeStyle = KNOWN_TECH_FRAME;
- tech_canvas_ctx.strokeRect(x-2, y-2, tech_item_width, tech_item_height);
- mapview_put_tile(tech_canvas_ctx, tag, x+1, y)
+ tech_canvas_ctx.strokeRect(x-2, y-2, twidth, theight);
+ if (!is_special) {
+ var tag = tileset_tech_graphic_tag(ptech);
+ mapview_put_tile(tech_canvas_ctx, tag, x+1, y)
+ }
// tech names >17 overflow their boxes on mobile, so use redux font for those
- if (ptech['name'].length>17) tech_canvas_ctx.font = tech_canvas_text_font_alt;
- else tech_canvas_ctx.font = tech_canvas_text_font;
+ tech_canvas_ctx.font = tfont;
tech_canvas_ctx.fillStyle = "rgba(0, 0, 0, 1)";
- tech_canvas_ctx.fillText(ptech['name'], x + 50, y + 15);
+ tech_canvas_ctx.fillText(ptech['name'], x + thoriz, y + tvert);
if (x > maxleft) maxleft = x;
@@ -321,13 +353,13 @@ function update_tech_tree()
tech_canvas_ctx.lineWidth=6;
tech_canvas_ctx.strokeStyle = CUR_TECH_FRAME;
} else if (client.conn.playing['tech_goal'] == ptech['id']) {
- tech_canvas_ctx.lineWidth=6;
+ tech_canvas_ctx.lineWidth=5;
bgcolor = POSSIBLE_AND_FUTURE_FILL; // show as future goal but differentiate to also show tech is possible now
tech_canvas_ctx.strokeStyle = FUTURE_TECH_FRAME;
tech_canvas_ctx.fillStyle = 'rgb(0, 0, 0)';
}
var bp = player_has_blueprints(client.conn.playing, ptech['id']);
- var tag = tileset_tech_graphic_tag(ptech);
+ if (!is_special) var tag = tileset_tech_graphic_tag(ptech);
if (bp) {
tag = "a.blueprints";
if (client.conn.playing['researching'] != ptech['id']) { // No bright blue box for current research
@@ -335,61 +367,59 @@ function update_tech_tree()
}
}
tech_canvas_ctx.fillStyle = bgcolor;
- tech_canvas_ctx.fillRect(x-2, y-2, tech_item_width, tech_item_height);
- if (tech_canvas_ctx.lineWidth<6) { // don't override current research frame colour
+ tech_canvas_ctx.fillRect(x-2, y-2, twidth, theight);
+ if (tech_canvas_ctx.lineWidth<5) { // don't override current research frame colour
tech_canvas_ctx.lineWidth=2;
tech_canvas_ctx.strokeStyle = bp ? BLUEPRINT_TECH_FRAME : POSSIBLE_TECH_FRAME;
}
- tech_canvas_ctx.strokeRect(x-2, y-2, tech_item_width, tech_item_height);
+ tech_canvas_ctx.strokeRect(x-2, y-2, twidth, theight);
tech_canvas_ctx.lineWidth=1;
- mapview_put_tile(tech_canvas_ctx, tag, x+1, y)
-
+ if (!is_special) {
+ mapview_put_tile(tech_canvas_ctx, tag, x+1, y)
+ }
if (client.conn.playing['researching'] == ptech['id']) {
tech_canvas_ctx.fillStyle = 'rgb(0, 0, 0)';
// tech names >17 overflow their boxes when bold, so use redux font for those
- if (ptech['name'].length>17) tech_canvas_ctx.font = "Bold "+tech_canvas_text_font_redux;
- else tech_canvas_ctx.font = "Bold " + tech_canvas_text_font;
+ tech_canvas_ctx.font = "Bold " + tfont;
} else {
// tech names >17 overflow their boxes on mobile, so use redux font for those
- if (ptech['name'].length>17) tech_canvas_ctx.font = tech_canvas_text_font_alt;
- else tech_canvas_ctx.font = tech_canvas_text_font;
+ tech_canvas_ctx.font = tfont;
tech_canvas_ctx.fillStyle = 'rgb(255, 255, 255)';
}
- tech_canvas_ctx.fillText(ptech['name'], x + 51, y + 16);
+ tech_canvas_ctx.fillText(ptech['name'], x + thoriz, y + tvert);
/* UNKNOWN TECHNOLOGY. */
} else if (player_invention_state(client.conn.playing, ptech['id']) == TECH_UNKNOWN) {
var bgcolor = (client.conn.playing != null && is_tech_req_for_goal(ptech['id'], client.conn.playing['tech_goal'])) ? "rgb(133, 167, 212)" : "rgb(61, 95, 130)";
if (client.conn.playing['tech_goal'] == ptech['id']) {
- tech_canvas_ctx.lineWidth=6;
+ tech_canvas_ctx.lineWidth=5;
tech_canvas_ctx.strokeStyle = FUTURE_TECH_FRAME;
}
- var tag = tileset_tech_graphic_tag(ptech);
+ if (!is_special) var tag = tileset_tech_graphic_tag(ptech);
if (player_has_blueprints(client.conn.playing, ptech['id'])) {
bgcolor = BLUEPRINT_TECH_FILL;
tag = "a.blueprints";
}
tech_canvas_ctx.fillStyle = bgcolor;
- tech_canvas_ctx.fillRect(x-2, y-2, tech_item_width, tech_item_height);
- if (tech_canvas_ctx.lineWidth<6) // don't override current research frame colour
+ tech_canvas_ctx.fillRect(x-2, y-2, twidth, theight);
+ if (tech_canvas_ctx.lineWidth<5) // don't override current research frame colour
tech_canvas_ctx.strokeStyle = UNKNOWN_TECH_FRAME;
- tech_canvas_ctx.strokeRect(x-2, y-2, tech_item_width, tech_item_height);
+ tech_canvas_ctx.strokeRect(x-2, y-2, twidth, theight);
tech_canvas_ctx.lineWidth=1;
- mapview_put_tile(tech_canvas_ctx, tag, x+1, y)
-
+ if (!is_special) {
+ mapview_put_tile(tech_canvas_ctx, tag, x+1, y)
+ }
if (client.conn.playing['tech_goal'] == ptech['id']) {
tech_canvas_ctx.fillStyle = 'rgb(0, 0, 0)';
// tech names >17 overflow their boxes when bold, so use redux font for those
- if (ptech['name'].length>17) tech_canvas_ctx.font = "Bold "+tech_canvas_text_font_redux;
- else tech_canvas_ctx.font = "Bold " + tech_canvas_text_font;
+ tech_canvas_ctx.font = "Bold " + tfont;
} else {
tech_canvas_ctx.fillStyle = 'rgb(255, 255, 255)';
// tech names >17 overflow their boxes on mobile, so use redux font for those
- if (ptech['name'].length>17) tech_canvas_ctx.font = tech_canvas_text_font_alt;
- else tech_canvas_ctx.font = tech_canvas_text_font;
+ tech_canvas_ctx.font = tfont;
}
- tech_canvas_ctx.fillText(ptech['name'], x + 51, y + 16);
+ tech_canvas_ctx.fillText(ptech['name'], x + thoriz, y + tvert);
}
var tech_things = 0;
@@ -408,7 +438,7 @@ function update_tech_tree()
var sprite = sprites[tileset_unit_type_graphic_tag(ptype)];
if (sprite != null) {
- tech_canvas_ctx.drawImage(sprite, x + 50 + ((tech_things++) * 30), y + 23, 28, 24);
+ tech_canvas_ctx.drawImage(sprite, x + thoriz + ((tech_things++) * 30), y + tvert+7, 28, 24);
}
}
@@ -430,12 +460,61 @@ function update_tech_tree()
var sprite = sprites[tileset_building_graphic_tag(pimpr)];
if (sprite != null) {
- tech_canvas_ctx.drawImage(sprite, x + 50 + ((tech_things++) * 30), y + 23, 28, 24);
+ tech_canvas_ctx.drawImage(sprite, x + thoriz + ((tech_things++) * 30), y + tvert+7, 28, 24);
}
}
}
}
+/**************************************************************************
+ Get dynamic heights, widths, font, for tech boxes.
+**************************************************************************/
+function get_tech_item_width(ptech) {
+
+ if (!ptech.boxwidth) {
+ const is_special = (ptech.flags[0] == TECH_SPECIAL_TECH); // "child" techs
+ var twidth = !is_special ? tech_item_width : tech_item_width/2;
+ /* If we don't know how many things this tech enables, set it */
+ if (!techs[ptech.id].things) {
+ techs[ptech.id].things = get_improvements_from_tech(ptech.id).length;
+ techs[ptech.id].things += get_utypes_from_tech(ptech.id).length;
+ }
+ /* Minimize box size to needed for text and enabled prod items. */
+ if (!is_special) {
+ var tw1 = twidth - 7 * (18-ptech.name.length); // 7px = width char
+ var tw2 = twidth - 28 * (5-techs[ptech.id].things); // 28px = size of an image
+ if (is_small_screen()) {
+ tw1 = twidth - 7 * (15-ptech.name.length); // 7px = width char
+ tw1 = tw1 < tw2 ? tw2 : tw1 // set tw1 as the greater of the two
+ twidth = twidth < tw1 ? tw1: twidth; // pick greater of twidth or tw1
+ } else {
+ var twidth = tw1 < tw2 ? tw2 : tw1; // pick the greater of the 2
+ }
+ } else { // special child tech:
+ var tw1 = twidth - 6 * (16-ptech.name.length); // 5px = width char
+ var tw2 = twidth - 28 * (3-techs[ptech.id].things); // 28px = size of an image
+ var twidth = tw1 < tw2 ? tw2 : tw1; // pick the greater of the 2
+ }
+ techs[ptech.id].boxwidth = twidth;
+ }
+
+ return techs[ptech.id].boxwidth;
+}
+function get_tech_item_height(ptech) {
+ const is_special = (ptech.flags[0] == TECH_SPECIAL_TECH); // special add-on specialty sub-techs are smaller
+ var theight = !is_special ? tech_item_height : tech_item_height * .80; // height of tech box
+ return theight;
+}
+function get_tech_item_font(ptech) {
+ const is_special = (ptech.flags[0] == TECH_SPECIAL_TECH); // special add-on specialty sub-techs are smaller
+ tfont = !is_special ? tech_canvas_text_font : tech_canvas_text_font_special;// font size of tech box (special add_on techs are smaller)
+ if (ptech['name'].length>17) { // possible redux for long tech names
+ if (!is_special) tfont = tech_canvas_text_font_redux;
+ else tfont = tech_canvas_text_font_special_redux;
+ }
+ return tfont;
+}
+
/**************************************************************************
Determines if the technology 'check_tech_id' is a requirement
for reaching the technology 'goal_tech_id'.
@@ -491,6 +570,8 @@ function update_tech_screen()
{
if (client_is_observer() || client.conn.playing == null) {
+ $("#technologies").width($(window).width() - 20);
+ $("#technologies").height($(window).height() - $("#technologies").offset().top - 15);
show_observer_tech_dialog();
return;
}
@@ -538,7 +619,7 @@ function update_tech_screen()
if (touch_device) $("#tech_results").css("margin-left","-22px");
$("#tech_result_text").html("" + get_advances_text(clicked_tech_id)
- +" "+(is_wide_screen ? "" /*tech_help_text*/ : "") + "");
+ +" "+(is_wide_screen ? "" /*tech_help_text*/ : "") + "");
$("#tech_advance_helptext").tooltip({
disabled: false,
show: { delay:350, effect:"none", duration: 0 }, hide: {delay:220, effect:"none", duration: 220}
@@ -557,7 +638,7 @@ function update_tech_screen()
var research_help_text = html_safe(cleaned_text(techs[client.conn.playing['researching']].helptext));
$("#tech_result_text").html("" + get_advances_text(client.conn.playing['researching'])
- +" "+(is_wide_screen ? "" /*research_help_text*/ : "") +"");
+ +" "+(is_wide_screen ? "" /*research_help_text*/ : "") +"");
$("#tech_advance_helptext").tooltip({
disabled: false,
show: { delay:350, effect:"none", duration: 0 }, hide: {delay:220, effect:"none", duration: 220}
@@ -697,13 +778,14 @@ function tech_mapview_mouse_click(e)
var x = Math.floor(reqtree[tech_id+'']['x'] * tech_xscale)+2; //scale in X direction.
var y = reqtree[tech_id+'']['y']+2;
+ var twidth = get_tech_item_width(ptech);
if (is_small_screen()) {
x = x * 0.6;
y = y * 0.6;
}
- if (tech_mouse_x > x && tech_mouse_x < x + tech_item_width
+ if (tech_mouse_x > x && tech_mouse_x < x + twidth
&& tech_mouse_y > y && tech_mouse_y < y + tech_item_height) {
if (mouse_button == 2 || (mouse_button == 3 && e.altKey)) send_player_tech_goal(ptech['id']);
else if (player_invention_state(client.conn.playing, ptech['id']) == TECH_PREREQS_KNOWN) {
@@ -1040,7 +1122,7 @@ function update_tech_dialog_cursor()
}
// We caught the cursor hovering inside a tech!
- if (tech_mouse_x > x && tech_mouse_x < x + tech_item_width
+ if (tech_mouse_x > x && tech_mouse_x < x + get_tech_item_width(ptech)
&& tech_mouse_y > y && tech_mouse_y < y + tech_item_height) {
if (player_invention_state(client.conn.playing, ptech['id']) == TECH_PREREQS_KNOWN) {
tech_canvas.style.cursor = "pointer";
@@ -1095,17 +1177,63 @@ function show_observer_tech_dialog()
{
$("#tech_info_box").hide();
$("#tech_canvas").hide();
- var msg = "
Research
";
+ var msg = "
Global Technology Report
";
+ msg += "
Name
Nation
Highest
Research
Bulb Sum
"
for (var player_id in players) {
- var pplayer = players[player_id];
- var pname = pplayer['name'];
- var pr = research_get(pplayer);
- if (pr == null) continue;
-
- var researching = pr['researching'];
- if (techs[researching] != null) {
- msg += pname + ": " + techs[researching]['name'] + " ";
+ let pplayer = players[player_id];
+ if (!pplayer.is_alive) continue;
+ let bulb_sum = 0;
+ // Start Player row
+ msg += "
";
+ // Name
+ msg += "
"+pplayer.name+"
";
+ // Nation
+ msg += "
"
+ + nations[pplayer['nation']]['adjective']+"
";
+
+ // Most advanced tech
+ let highest_tech_name = null;
+ let highest_tech_cost = 0;
+ for (var tech_id in techs) {
+ if (player_invention_state(pplayer, tech_id) == TECH_KNOWN) {
+ if (techs[tech_id].cost > highest_tech_cost) {
+ highest_tech_name = techs[tech_id].name;
+ highest_tech_cost = techs[tech_id].cost;
+ }
+ if (techs[tech_id].cost > 0) bulb_sum += techs[tech_id].cost;
+ }
+ }
+ if (highest_tech_name) {
+ msg += "
";
+ } else {
+ msg += "
"
+ }
+
+ // Current research
+ let researching = pplayer.researching;
+ if (!techs[researching]) {
+ if (techs[pplayer['tech_goal']] && techs[pplayer['tech_goal']].name) { // No current research but has a real future goal
+ msg += "
"
+ } else {
+ msg += "
"
+ }
+ } else {
+ if (techs[researching].name) { // A legitimate tech
+ msg += "
"
+ } else {
+ msg += "
"
+ }
}
+
+ // Bulb sum
+ if (pplayer.bulbs_researched) bulb_sum += pplayer.bulbs_researched;
+ msg += "
"+Math.trunc(bulb_sum)+"
"
+ // End Player row
+ msg += "
";
}
$("#technologies").html(msg);
$("#technologies").css("color", "#dcb");
diff --git a/freeciv-web/src/main/webapp/javascript/tile.js b/freeciv-web/src/main/webapp/javascript/tile.js
index abe6f0e3f..0a92b8c0e 100644
--- a/freeciv-web/src/main/webapp/javascript/tile.js
+++ b/freeciv-web/src/main/webapp/javascript/tile.js
@@ -65,6 +65,34 @@ function tile_resource(tile)
return null;
}
+/**************************************************************************
+ Returns true iff the specified tile has an adjacent tile which contains
+ the specified extra. The 'cardinal' parameter tells us to only look
+ for CAdjacent tiles.
+**************************************************************************/
+function is_extra_adjacent(ptile, extra, cardinal)
+{
+ if (ptile == null || extra == null) return false;
+
+ for (dir = 0; dir < 8; dir++) {
+
+ if (cardinal && !is_cardinal_dir(dir)) {
+ continue;
+ }
+
+ let tile1 = mapstep(ptile, dir);
+
+ if (tile1 != null && tile_get_known(tile1) != TILE_UNKNOWN) {
+ if (tile_has_extra(tile1, extra)) {
+ return true;
+ }
+ }
+
+ }
+
+ return false;
+}
+
/************************************************************************//**
Check if tile contains an extra type that claim territory
****************************************************************************/
@@ -197,19 +225,19 @@ function improve_tile_info_dialog(message)
added_text += "Irrigate:" + Math.ceil(ttype['irrigation_time']/wt)+""
if (ttype['irrigation_food_incr']) added_text+= " (+"+ttype['irrigation_food_incr']+")";
}
- if (ttype['irrigation_result'] && ttype['irrigation_result'] != tindex && ttype['irrigation_result'] != tinvalid)
+ if (ttype['irrigation_result'] != null && ttype['irrigation_result'] != tindex && ttype['irrigation_result'] != tinvalid)
added_text+="➡"+terrains[ttype['irrigation_result']]['name']
if (ttype['mining_time']) {
added_text += " Mine:" + Math.ceil(ttype['mining_time']/wt)+"";
if (ttype['mining_shield_incr']) added_text+= " (+"+ttype['mining_shield_incr']+")";
}
- if (ttype['mining_result'] && ttype['mining_result'] != tindex && ttype['mining_result'] != tinvalid)
+ if (ttype['mining_result'] != null && ttype['mining_result'] != tindex && ttype['mining_result'] != tinvalid)
added_text+="➡"+terrains[ttype['mining_result']]['name']
if (ttype['transform_time'])
added_text += " Transform:" + Math.ceil(ttype['transform_time']/wt)+"";
- if (ttype['transform_result'] && ttype['transform_result'] != tindex && ttype['transform_result'] != tinvalid)
+ if (ttype['transform_result'] != null && ttype['transform_result'] != tindex && ttype['transform_result'] != tinvalid)
added_text+="➡"+terrains[ttype['transform_result']]['name']
if (ttype['road_time'])
diff --git a/freeciv-web/src/main/webapp/javascript/tracklist.js b/freeciv-web/src/main/webapp/javascript/tracklist.js
index 1e65f8a36..ff68cd867 100644
--- a/freeciv-web/src/main/webapp/javascript/tracklist.js
+++ b/freeciv-web/src/main/webapp/javascript/tracklist.js
@@ -89,7 +89,9 @@ from the project and be non-essential to the function of the Instance.
"breaks/BoysEye",
"breaks/30secondbreak",
"breaks/30secondbreak",
- "breaks/ColdWindBreak"
+ "breaks/ColdWindBreak",
+ "breaks/StreetOfDeviants",
+ "breaks/DeeperSilence45s"
],
"brk_style2": // classical *
[
@@ -115,7 +117,9 @@ from the project and be non-essential to the function of the Instance.
"breaks/BoysEye",
"breaks/30secondbreak",
"breaks/30secondbreak",
- "breaks/ColdWindBreak"
+ "breaks/ColdWindBreak",
+ "breaks/StreetOfDeviants",
+ "breaks/DeeperSilence45s"
],
"brk_style3": // tropical *
[
@@ -138,7 +142,9 @@ from the project and be non-essential to the function of the Instance.
"breaks/BoysEye",
"breaks/30secondbreak",
"breaks/30secondbreak",
- "breaks/ColdWindBreak"
+ "breaks/ColdWindBreak",
+ "breaks/StreetOfDeviants",
+ "breaks/DeeperSilence45s"
],
"brk_style4": // asian *
[
@@ -164,7 +170,9 @@ from the project and be non-essential to the function of the Instance.
"breaks/BoysEye",
"breaks/30secondbreak",
"breaks/30secondbreak",
- "breaks/ColdWindBreak"
+ "breaks/ColdWindBreak",
+ "breaks/StreetOfDeviants",
+ "breaks/DeeperSilence45s"
],
"brk_style5": // babylonian *
[
@@ -188,7 +196,10 @@ from the project and be non-essential to the function of the Instance.
"breaks/30secondbreak",
"breaks/30secondbreak",
"breaks/ColdWindBreak",
- "middle/AlKadir"
+ "middle/AlKadir",
+ "breaks/StreetOfDeviants",
+ "breaks/DeeperSilence45s",
+ "breaks/Homayoun"
],
"brk_style6": // celtic *
[
@@ -210,7 +221,9 @@ from the project and be non-essential to the function of the Instance.
"breaks/BoysEye",
"breaks/30secondbreak",
"breaks/30secondbreak",
- "breaks/ColdWindBreak"
+ "breaks/ColdWindBreak",
+ "breaks/StreetOfDeviants",
+ "breaks/DeeperSilence45s"
],
"tribal":
[
@@ -263,11 +276,36 @@ from the project and be non-essential to the function of the Instance.
"tribal/Humani",
"tribal/Origins",
"tribal/Echolalia",
- "tribal/MotherTongue"
+ "tribal/MotherTongue",
+ "tribal/MossCarpetSkyBlanket",
+ "tribal/ProfligateEarth",
+ "tribal/Raku",
+ "tribal/VoiceOfRust",
+ "tribal/SoftRainsFall",
+ "tribal/Transpiration",
+ "tribal/CorvidCollections",
+ "tribal/AerialOnWarmSeas",
+ "tribal/NeverHunger",
+ "tribal/WhatWeLeftBehind",
+ "tribal/SeekingEden",
+ "tribal/TheGateIsOpen",
+ "tribal/UndulatingTerrain",
+ "tribal/Artifacts",
+ "tribal/ClayWoodBoneDirt",
+ "tribal/InTheEyesOfTheSpirit",
+ "tribal/TheFaceInTheFire",
+ "tribal/Hunter",
+ "tribal/ErodingColumns",
+ "tribal/FlowStone",
+ "tribal/SuonoIpogeo",
+ "tribal/UnderwaterFields",
+ "tribal/EtherealAbyss",
+ "tribal/TowerOfSet1",
+ "tribal/FromPastToPresent",
+ "tribal/Hunter2"
],
"ancient":
[
- "tribal/anvildrumintro",
"tribal/Foreigner",
"ancient/Intro1",
"ancient/Intro3",
@@ -300,7 +338,6 @@ from the project and be non-essential to the function of the Instance.
"ancient/xMusic15",
"ancient/xwon",
"ancient/xwon2",
- "ancient/scrolls",
"ancient/SeaDragon",
"ancient/xwon3",
"ancient/WarDrums_Early",
@@ -308,7 +345,6 @@ from the project and be non-essential to the function of the Instance.
"ancient/AncientSoundtrack1",
"ancient/AncientSoundtrack3",
"ancient/AncientSoundtrack4",
- "ancient/TowerOfSet",
"ancient/Hovern",
"ancient/Walrus",
"ancient/Attila",
@@ -336,13 +372,72 @@ from the project and be non-essential to the function of the Instance.
"ancient/HouseHur",
"ancient/EnterJerusalem",
"ancient/ArriusParty",
+ "tribal/anvildrumintro",
+ "ancient/scrolls",
"ancient/Orgy",
+ "ancient/TowerOfSet",
+ "ancient/AnvilOfCrom", //
+ "ancient/AtlanteanSword",
+ "ancient/BattleOfTheMounds1",
+ "ancient/BattleOfTheMounds2",
+ "ancient/Byzantium",
+ "ancient/Capture",
+ "ancient/Cleopatra",
+ "ancient/ColumnOfSadnessWheelOfPain",
+ "ancient/Defilers",
+ "ancient/DisciplineOfSteelFreedomCouncil",
+ "ancient/EchoesOfRomanRuins",
+ "ancient/FuneralPyre",
+ "ancient/GiftOfFury1",
+ "ancient/HallOfKingOsric",
+ "ancient/Indulgence",
+ "ancient/Kitchen1",
+ "ancient/MountainOfPowerProcession",
+ "ancient/Orgy1",
+ "ancient/PitFights",
+ "ancient/RiddleOfSteelRidersOfDoom",
+ "ancient/TheologyCivilization",
+ "ancient/TowerOfSet2",
+ "ancient/WarmWelcome",
+ "ancient/TowerOfSetSnakeAttack",
+ "ancient/WelcomeToKostantiniyye",
+ "ancient/WolfWitch",
+ "ancient/RohanSuite",
"ancient/Hector",
"ancient/Sakura",
"ancient/TombHorus",
"ancient/TombOsiris",
"ancient/TombIsis",
- "ancient/TombAnubis"
+ "ancient/TombAnubis",
+ "ancient/SongOfSophia",
+ "ancient/RebirthsPortalUnveiled",
+ "ancient/Saraghaz",
+ "ancient/BeamOfSunlight",
+ "ancient/IntroDiAde",
+ "ancient/EnteringIntimateFragranceOfBeatitude",
+ "ancient/Bija",
+ "ancient/Bija2",
+ "ancient/RoadToWirikuta",
+ "ancient/SpiritSword",
+ "ancient/DoYouRemember",
+ "ancient/Cantiga384AlfonsoX",
+ "ancient/CantigasDeSantaMaria1",
+ "ancient/CantigasDeSantaMaria4",
+ "ancient/ChachanehTsaghadzoreh",
+ "ancient/Ecstasy",
+ "ancient/FoliadaAlfonsoX",
+ "ancient/Ghamangiz",
+ "ancient/Jamedaran",
+ "ancient/Kord-E-Bayat",
+ "ancient/LamentoDiTristano",
+ "ancient/SoloUd",
+ "ancient/LyreOfMegiddo",
+ "ancient/VasnMeroPerkutian",
+ "ancient/PansisAncientGreekLyreRuiFuBendir",
+ "ancient/Illyricum",
+ "ancient/CarthageIntroRomeTW_OST",
+ "ancient/AetasRomana",
+ "ancient/EasternIntroRomeTW_OST"
],
"middle":
[
@@ -420,6 +515,21 @@ from the project and be non-essential to the function of the Instance.
"middle/OldTree",
"middle/Summertide",
"middle/TimberTown",
+ "middle/RaptureRecall",
+ "middle/Promentory",
+ "middle/SpanishDance2",
+ "middle/TearsOfMuses",
+ "middle/Tavern",
+ "middle/Tavern2",
+ "middle/SilverSword",
+ "middle/CantigasDeSantaMaria2",
+ "middle/CantigasDeSantaMaria3",
+ "middle/FoliasGalegasMurcia",
+ "middle/PraetoriusDancesTerpsichore",
+ "middle/MiguelRinconViseeKapsberger",
+ "middle/OmwoldonSamhain",
+ "middle/PlayfordDance",
+ "middle/RaghseChoupani"
],
"colonial":
[
@@ -475,10 +585,26 @@ from the project and be non-essential to the function of the Instance.
"industrial/Coronation",
"industrial/Esther",
"industrial/Judea",
- "industrial/Eventide"
+ "industrial/Eventide",
+ "colonial/LaCatedral2",
+ "colonial/HesAPirate",
+ "colonial/HighlandSpirit",
+ "colonial/HiddenFalls",
+ "colonial/SongOfTimeAndStore",
+ "colonial/Wilderness",
+ "colonial/Brothers",
+ "colonial/YradierLaPaloma",
+ "colonial/ChoroDaSaudade",
+ "colonial/LegendaryGuardian",
+ "colonial/lArpeggiata",
+ "colonial/Elegie",
+ "colonial/TheLeavingTheSearch",
+ "colonial/CanariosKapsberger",
+ "colonial/DreamQuest"
],
"industrial":
[
+ "ancient/FuneralPyre",
"colonial/SerenadeVW",
"colonial/DarkeFantasy",
"industrial/into_the_shadows", // GPL in AGPL
@@ -524,7 +650,17 @@ from the project and be non-essential to the function of the Instance.
"industrial/Nightfall",
"industrial/SolentVW",
"industrial/Tallis1VW",
- "industrial/Tallis2VW"
+ "industrial/Tallis2VW",
+ "industrial/GenerosityOfSolitude1",
+ "industrial/NorthCountrySketchesWinter",
+ "industrial/NorthCountrySketches3",
+ "industrial/NorthCountrySketches1",
+ "industrial/HeadChop",
+ "industrial/OrphansOfDoomAwakening",
+ "industrial/TheSnakeInfidels",
+ "industrial/TreeOfWoeRecovery",
+ "industrial/Wifeing",
+ "industrial/DuelingWizards"
],
"modern":
[
@@ -539,6 +675,7 @@ from the project and be non-essential to the function of the Instance.
"tribal/CeremonyStrata",
"tribal/Mammoth",
"tribal/LunaStrata",
+ "tribal/UnderwaterFields",
"ancient/SeaDragon",
"modern/Stellar",
"modern/Chevaliers",
@@ -645,7 +782,18 @@ from the project and be non-essential to the function of the Instance.
"modern/HeartOffense",
"modern/Mauvais",
"modern/ScaredDark",
- "modern/TwoRun"
+ "modern/TwoRun",
+ "modern/Rhizome",
+ "modern/AfterUs",
+ "modern/MeetingFaceToFace",
+ "modern/GlassMishimaQuartet",
+ "modern/MemoriesOfWandering2",
+ "modern/MemoriesOfHome",
+ "modern/Erinacea",
+ "modern/Flatlands",
+ "modern/OutOfSource",
+ "modern/DistantTraveler",
+ "modern/BattleOfTheMounds3",
+ "modern/BattleOfTheMounds3Original"
]
};
-
\ No newline at end of file
diff --git a/freeciv-web/src/main/webapp/javascript/unit.js b/freeciv-web/src/main/webapp/javascript/unit.js
index 281642150..bc1d938b4 100644
--- a/freeciv-web/src/main/webapp/javascript/unit.js
+++ b/freeciv-web/src/main/webapp/javascript/unit.js
@@ -17,6 +17,7 @@
***********************************************************************/
+const ACTIVITY_FACTOR = 100;
var units = {};
@@ -26,21 +27,28 @@ var units = {};
var unit_pillage_sound_delay_times = {
"Dive Bomber": 3000,
"Ground Strike Fighter": 300,
+ "Stealth Bomber": 300,
"Jet Bomber": 300,
"Armor": 400,
"Armor II": 400
};
var unit_bombard_attack_names = {
- "Phalanx": "Rumble Attack",
- "Archers": "Volley Attack",
- "Legion": "Pilum Assult",
- "Siege Ram": "Ram Fortress",
- "Fanatics": "Skirmish Assault",
- "Zealots": "Skirmish Assault",
- "Marines": "Bazooka Attack",
- "Zeppelin": "Bomb",
- "Battleship": "Bombard"
+ "Phalanx": "Rumble Attack",
+ "Archers": "Volley Attack",
+ "Legion": "Pilum Assult",
+ "Siege Ram": "Ram Fortress",
+ "Fanatics": "Skirmish Assault",
+ "Zealots": "Skirmish Assault",
+ "Marines": "Bazooka Attack",
+ "Zeppelin": "Bomb",
+ "Battleship": "Bombard",
+ "Ballista": "Ranged Attack",
+ "Catapult": "Bombard",
+ "Cannon": "Bombard",
+ "Artillery": "Bombard",
+ "Howitzer": "Bombard",
+ "Magnum Turret": "Bombard"
};
// Determines if victory by this unit shows crossed swords or gunpowder explosion.
@@ -55,7 +63,9 @@ var units_pregunpowder = [
"Elephants",
"Knights",
"Crusaders",
-"Catapult",
+//"Catapult", // explosion looks better since it's non-melee
+//"Ballista",
+"Scout",
"Explorer",
"Tribesmen",
"Well-Digger",
@@ -78,19 +88,6 @@ var ANIM_STEPS = 8;
var anim_units_max = 30;
var anim_units_count = 0;
-/* The unit_orders enum from unit.h */
-var ORDER_MOVE = 0;
-var ORDER_ACTIVITY = 1;
-var ORDER_FULL_MP = 2;
-var ORDER_ACTION_MOVE = 3;
-var ORDER_PERFORM_ACTION = 4;
-var ORDER_LAST = 5;
-
-/* The unit_ss_data_type enum from unit.h */
-var USSDT_QUEUE = 0;
-var USSDT_UNQUEUE = 1;
-var USSDT_BATTLE_GROUP = 2;
-
/****************************************************************************
...
****************************************************************************/
@@ -117,6 +114,7 @@ function client_remove_unit(punit)
if (unit_is_in_focus(punit)) {
current_focus = [];
+ if (renderer == RENDERER_WEBGL) webgl_clear_unit_focus();
}
delete units[punit['id']];
@@ -270,8 +268,7 @@ function unit_has_cargo_room(punit) {
}
/**************************************************************************
- * Return true if this unit can DEBOARD. TODO: needs renaming since
- * "unload" now means "transporter ejecting its cargo"
+ * Return true if this unit can DEBOARD.
*
* This function is currently hard-coded as a placeholder for proper
* ruleset actionenablers, but has dual use as logic of when to show
@@ -279,7 +276,7 @@ function unit_has_cargo_room(punit) {
* are in all rulesets AND 2) client can somehow test the actionenabler
* legality itself.
**************************************************************************/
-function unit_can_do_unload(punit)
+function unit_can_deboard(punit)
{
if (!punit) return false;
var rules = ruleset_control['name'];
@@ -305,7 +302,16 @@ function unit_can_do_unload(punit)
//****************************************************************** */
// COMMON
if (pcity) return true;
- if (utype_has_flag(ptype, UTYF_MARINES) && !is_ocean_tile(ptile)) return true;
+ /* UTYF_MARINES flag is a mishmash intersection of old capabilities of Marines programmed first as a hard-coded server flag,
+ * next migrated over to a custom ruleset flag, and now is a confused holdover for backward compatibility to older rulesets.
+ * The whole thing is getting deprecated so it can be split into several flags handling distinctly DIFFERENT behaviours instead
+ * of all together. Therefore the line below is commented out and we'll have to see what breaks or needs changing as we
+ * extricate ourselves from that legacy mess. The line below that is the new handling. May change behaviour specifically for AAA
+ * in MP2A-C, though not necessarily in a bad way (since AAA acting like full Marines when it comes to transports is a bad thing.)
+ //if (utype_has_flag(ptype, UTYF_MARINES) && !is_ocean_tile(ptile)) return true;
+ */
+ if (ptype.rule_name == "Marines" && !is_ocean_tile(ptile)) return true;
+
if ((typeof EXTRA_NAVALBASE !== "undefined") && tile_has_extra(ptile, EXTRA_NAVALBASE)) return true;
if (pclass == "Air") return true;
if (pclass == "AirProtect") return true;
@@ -315,6 +321,10 @@ function unit_can_do_unload(punit)
if (pclass == "Helicopter") return true;
if (pclass == "Missile") return true;
+ if (pclass.startsWith("Land")) {
+ if (is_ocean_tile(ptile)) return false;
+ }
+
// currently decided it can unload in airbase also, below.
//if (pclass == "Bomb") return false; // only allowed in city, handled above.
@@ -333,14 +343,14 @@ function unit_can_do_unload(punit)
}
// Brava onward:
if (tclass.rule_name == "LandRail"
- || tclass.rule_name == "LandRoad") { // Foot soldiers can unload from Train/Truck on any Base or Quay.
+ || tclass.rule_name == "LandRoad") { // Foot soldiers can deboard from Train/Truck on any Base or Quay.
if (tile_has_extra(ptile, EXTRA_AIRBASE)) return true;
if (quay_rules && tile_has_extra(ptile, EXTRA_QUAY)) return true;
if (typeof EXTRA_FORT !== 'undefined' && tile_has_extra(ptile, EXTRA_FORT)) return true;
if (tile_has_extra(ptile, EXTRA_FORTRESS)) return true;
if ((typeof EXTRA_CASTLE !== "undefined") && tile_has_extra(ptile, EXTRA_CASTLE)) return true;
if ((typeof EXTRA_BUNKER !== "undefined") && tile_has_extra(ptile, EXTRA_BUNKER)) return true;
- return false; // City already unloaded; Marines, Balloons, and AAA already got off higher above.
+ return false; // City already unloaded; Marines and Balloons already got off higher above.
}
if (tile_has_extra(ptile, EXTRA_QUAY)) return true;
if (tile_has_extra(ptile, EXTRA_RIVER)) return false;
@@ -354,7 +364,7 @@ function unit_can_do_unload(punit)
The server won't tell us for sure. Knowing the unit CAN'T load is a
pragmatic way to prevent long GUI lists of invalid transport candidates
to load onto. It generalises what's true for the mainstream rulesets.
- It shouldn't ever be called for non-mainstream rulesets.
+ It should be circumvented/ignored in non-mainstream rulesets.
**************************************************************************/
function unit_could_possibly_load(punit, ptype, ttype, tclass)
{
@@ -362,13 +372,38 @@ function unit_could_possibly_load(punit, ptype, ttype, tclass)
if (!punit || !ptype || !ttype || !tclass) return false;
var pclass = get_unit_class_name(punit);
+ var ptile = tiles[punit['tile']];
+ var can_load = false;
+
//console.log(" pclass=="+pclass);
+ // In MP2D, only special classes can board/load without moves left:
+ if (client_rules_flag[CRF_MP2_D]) {
+ if (punit.movesleft <= 0) {
+ if ( ptype.rule_name == "Marines"
+ || pclass == "Air"
+ || pclass == "AirProtect"
+ || pclass == "Air_High_Altitude"
+ || pclass == "Missile"
+ || pclass == "Cargo"
+ || pclass == "Balloon"
+ || pclass == "Zeppelin"
+ || pclass == "Helicopter") {
+
+ // These classes may proceed to see if they may board
+ } else {
+ return false; // No other classes can board with 0 moves left.
+ }
+ }
+ // Can't get on a Trawler if you moved this turn:
+ if (pclass == "Sea" || pclass == "Submarine") {
+ if (unit_has_moved(punit)) return false;
+ }
+ }
+
// Transported units can't swap transports except under some conditions:
// TO DO: when actionenabler_load is in server, we can put all this in game.ruleset actionenablers.
if ( (pclass.startsWith("Land") || pclass=="Cargo") && punit['transported']) {
- var ptile = tiles[punit['tile']];
- var can_load = false;
var from_unit = units[punit['transported_by']]; // unit currently transporting the cargo who wants to swap transports
var from_class = get_unit_class(from_unit); // unit_class of the transport currently transporting the cargo
@@ -376,7 +411,7 @@ function unit_could_possibly_load(punit, ptype, ttype, tclass)
// Can "transport swap" where 1) unloading then loading again is legal anyway (cities, naval bases, quays) ...
if (tile_city(ptile)) can_load = true;
- else if (unit_can_do_unload(punit)) can_load = true; // Whenever unloading is already legal, don't force micro-managing an extra step to unload.
+ else if (unit_can_deboard(punit)) can_load = true; // When deboarding is legal, don't force micro-managing an extra step to unload.
//commented out: e.g., Riflemen can't necessarily get off a Heli on a Quay.
//else if (typeof EXTRA_NAVALBASE !== undefined && tile_has_extra(EXTRA_NAVALBASE)) can_load = true;
//else if (client_rules_flag[CRF_EXTRA_QUAY] && tile_has_extra(EXTRA_QUAY)) can_load=true;
@@ -410,14 +445,23 @@ function unit_could_possibly_load(punit, ptype, ttype, tclass)
}
//////////// End of handling for already-transported units doing a transport-swap ///////////////////////
+ //console.log("2, can_load == "+can_load);
+
// Disqualify all units who can never be cargo.
if (pclass == "Sea" ||
+ pclass == "LandImmobile" ||
pclass == "RiverShip" ||
pclass == "Submarine" ||
pclass == "Trireme" ||
pclass == "LandRail" ||
pclass == "Space") {
- return false;
+ // Trawler is exception who can "rescue tug" sea units.
+ if (ttype.name == "Trawler") {
+ if (pclass != "LandRail" && pclass != "Space") {
+ return true;
+ }
+ }
+ return false;
}
// Disqualify all units who can never be transports
@@ -451,14 +495,25 @@ function unit_could_possibly_load(punit, ptype, ttype, tclass)
if (!ttype.name.includes("Bomber")
&& tclass.rule_name != "LandRail"
&& tclass.rule_name != "LandRoad" ) return false;
- if (ttype.cargo[0]==0) return false; // Dive-Bomber or Bomber who can't carry bombs.
+ if (ttype.cargo[0]==0) return false; // any "Bomber" who can't carry bombs.
}
else if (pclass == "Missile") {
if (ttype.name=="Missile Destroyer" ||
ttype.name=="AEGIS Cruiser" ||
- ttype.name=="Submarine" ||
+ (tclass.rule_name=="Submarine" && !client_rules_flag[CRF_MP2_D]) ||
+ ttype.name=="Missile Submarine" ||
ttype.name=="Mobile SAM" ||
- ttype.name=="Carrier") return true;
+ ttype.name=="Carrier") {
+ //starting in MP2D missiles must load in a city or be a transport swap
+ if (client_rules_flag[CRF_MP2_D]) {
+ if (tile_city(ptile)) return true;
+ else if (can_load) return true;
+ else return false;
+ }
+ //pre-MP2D: missiles can board on the qualifying transport types above
+ return true;
+ }
+ //the transport type is one who can't carry missiles:
return false;
}
else if (pclass.startsWith("Land")) { // Land, LandNoKill, LandAirSea, LandRail, LandRoad
@@ -466,7 +521,7 @@ function unit_could_possibly_load(punit, ptype, ttype, tclass)
if (tclass.rule_name == "Land") return false; // can't load on Caravans, the only Land class with cargo capacity.
if (tclass.rule_name == "Submarine") return false;
if (tclass.rule_name == "LandRail" || tclass.rule_name == "LandRoad") {
- if (utype_real_base_move_rate(ptype) >= 3 * SINGLE_MOVE) return false; // Rail equality: units with <3 moves can use trains
+ if (utype_real_base_move_rate(ptype) >= 3 * SINGLE_MOVE) return false; // Equality: units with <3 moves can use wagon/train/truck
//if (!unit_has_type_flag(punit, UTYF_FOOTSOLDIER)) return false; //used to be foot only, now it's line above
}
if (tclass.rule_name == "Air") {
@@ -549,18 +604,19 @@ function move_points_text(moves, make_fraction)
// change 3/6 to 1/2, 3/9 to 1/3, etc:
numerator = Math.floor(moves % SINGLE_MOVE);
denominator = SINGLE_MOVE;
-
+
var simplified_fraction = fraction_reduce(numerator, denominator);
numerator = simplified_fraction.numerator;
denominator = simplified_fraction.denominator;
-
- if (Math.floor(moves / SINGLE_MOVE) > 0) {
+ if (Math.floor(moves / SINGLE_MOVE) > 0 && numerator>0) {
result = "" + Math.floor(moves / SINGLE_MOVE)
+ spacer + numerator
+ div_symbol + denominator;
- } else {
+ } else if (numerator>0) {
result = "" + numerator + div_symbol + denominator;
+ } else {
+ result = Math.floor(moves / SINGLE_MOVE);
}
} else {
result = Math.floor(moves / SINGLE_MOVE);
@@ -602,7 +658,7 @@ function update_unit_anim_list(old_unit, new_unit)
if (anim_units_count > anim_units_max) return;
- if (!is_unit_visible(new_unit)) return;
+ if (renderer == RENDERER_2DCANVAS && !is_unit_visible(new_unit)) return;
if (old_unit['anim_list'] == null) old_unit['anim_list'] = [];
@@ -648,6 +704,12 @@ function get_unit_anim_offset(punit)
{
var offset = {};
+ if (renderer == RENDERER_WEBGL) {
+ offset['x'] = 0;
+ offset['y'] = 0;
+ return offset;
+ }
+
if (punit['anim_list'] != null && punit['anim_list'].length >= 2) {
var anim_tuple_src = punit['anim_list'][0];
var anim_tuple_dst = punit['anim_list'][1];
@@ -743,6 +805,7 @@ function get_unit_homecity_name(punit)
function is_unit_visible(punit)
{
if (punit == null || punit['tile'] == null) return false;
+ if (renderer == RENDERER_WEBGL) return false; // not supported by 3D version.
var u_tile = index_to_tile(punit['tile']);
var r = map_to_gui_pos(u_tile['x'], u_tile['y']);
@@ -891,6 +954,9 @@ function get_unit_city_info(punit, plaintext)
case ACTIVITY_FORTIFIED:
result += "\nActivity: FORTIFIED";
break;
+ case ACTIVITY_VIGIL:
+ result += "\nActivity: VIGIL";
+ break;
case ACTIVITY_FORTIFYING:
result += "\nActivity: FORTIFYING";
break;
@@ -933,6 +999,15 @@ function get_unit_city_info(punit, plaintext)
}
}
+ if (punit['activity_count']) {
+ const is_2x_rules = unit_types[0].move_rate / SINGLE_MOVE; // in 2x rules, 1 Worker-Turn = 2 work-units
+ var work = punit.activity_count / is_2x_rules;
+ var plural = (work / ACTIVITY_FACTOR) > (1+1/SINGLE_MOVE) ? "s" : ""
+ result += "\nWork Done: "
+ + move_points_text(work * SINGLE_MOVE / ACTIVITY_FACTOR, true)
+ + " unit"+plural;
+ }
+
result += "\n"; // Space for separating key stats
//VETERAN LEVEL
@@ -991,7 +1066,8 @@ function get_what_can_unit_pillage_from(punit, ptile)
if (ptype['name'] != "Ground Strike Fighter"
&& ptype['name'] != "Dive Bomber"
&& ptype['name'] != "Jet Bomber"
- && ptype['name'] != "Strategic Bomber"
+ && ptype['name'] != "Strategic Bomber"
+ && ptype['name'] != "Stealth Bomber"
) {
return targets;
}
@@ -1300,7 +1376,9 @@ function utype_get_extra_stats(ptype) {
pstats.iPillage_random_targets = (BB & 0b11000000000) >> 9;
// Bit 11-15: # of rounds of Bombard retaliation
pstats.bombard_retaliate_rounds =
- (BB & 0b11111100000000000) >> 11;
+ (BB & 0b1111100000000000) >> 11;
+ // Bit 16-18: # of max attacks per turn
+ pstats.max_attacks = (BB & 0b1110000000000000000) >> 16;
/* Adjustments of the raw encoded values to match their purpose: */
diff --git a/freeciv-web/src/main/webapp/javascript/unittype.js b/freeciv-web/src/main/webapp/javascript/unittype.js
index 3dcd0c4eb..4961cefa8 100644
--- a/freeciv-web/src/main/webapp/javascript/unittype.js
+++ b/freeciv-web/src/main/webapp/javascript/unittype.js
@@ -51,9 +51,13 @@ const UCF_USER_FLAG_10 = 21;
const UCF_USER_FLAG_11 = 22;
const UCF_USER_FLAG_12 = 23;
// Custom unit class flags (MP2 sequence/order; TO DO: universalize/uniform order in other rules)
-const UCF_AIRLIFTABLE = UCF_USER_FLAG_1;
-const UCF_BORDERPOLICE = UCF_USER_FLAG_2;
-const UCF_ATTACK_FROM_NON_NATIVE = UCF_USER_FLAG_3;
+const UCF_ATTACK_FROM_NON_NATIVE = UCF_USER_FLAG_1;
+const UCF_MISSILE = UCF_USER_FLAG_2;
+const UCF_CANPILLAGE = UCF_USER_FLAG_3
+const UCF_AIRLIFTABLE = UCF_USER_FLAG_4;
+const UCF_BORDERPOLICE = UCF_USER_FLAG_5;
+const UCF_CARGO_RESTRAINED = UCF_USER_FLAG_6;
+const UCF_WATER_VESSEL = UCF_USER_FLAG_7;
// Unit flags
const UTYF_CANT_FORTIFY = 0; /* Unable to Fortify */
@@ -90,45 +94,62 @@ const UTYF_NEWCITY_GAMES_ONLY = 29; /* Unit can't be built in scenarios wh
const UTYF_CANESCAPE = 30; /* 50% chance to escape when killstack occours if more moves remaining than attacker */
const UTYF_CANKILLESCAPING = 31; /* Can kill escaping units */
const UTYF_NEVER_BLOCKED = 32; /* Overrides unreachable_protects server setting for attacker */
- /* some of these USER_FLAG may be mis-aligned, some of these may have hard-coded behaviour*/
-const UTYF_USER_FLAG_1 = 33; /* Reserved for replacing Shield2Gold as flag for using multiple city_build_slots */
-const UTYF_USER_FLAG_2 = 34; /* Can make hideouts */
-const UTYF_USER_FLAG_3 = 35; /* Will never autoattack */
-const UTYF_USER_FLAG_4 = 36; /* Transport Defender: will defend stack as cargo even on non-native tiles FLAG=34 on server */
-const UTYF_USER_FLAG_5 = 37; /* Non-Mil Attack - as NonMil can enter Peace tiles, but if not at Peace, can attack e.g., Trireme */
-const UTYF_USER_FLAG_6 = 38;
-const UTYF_USER_FLAG_7 = 39;
-const UTYF_USER_FLAG_8 = 40;
-const UTYF_USER_FLAG_9 = 41;
-const UTYF_USER_FLAG_10 = 42;
-const UTYF_USER_FLAG_11 = 43;
-const UTYF_USER_FLAG_12 = 44;
-const UTYF_USER_FLAG_13 = 45;
-const UTYF_USER_FLAG_14 = 46;
-const UTYF_USER_FLAG_15 = 47;
-const UTYF_USER_FLAG_16 = 48;
-const UTYF_USER_FLAG_17 = 49;
-const UTYF_USER_FLAG_18 = 50;
-const UTYF_USER_FLAG_19 = 51;
-const UTYF_USER_FLAG_20 = 52;
-const UTYF_USER_FLAG_21 = 53;
-const UTYF_USER_FLAG_22 = 54;
-const UTYF_USER_FLAG_23 = 55;
-const UTYF_USER_FLAG_24 = 56;
-const UTYF_USER_FLAG_25 = 57;
-const UTYF_USER_FLAG_26 = 58;
-const UTYF_USER_FLAG_27 = 59;
-const UTYF_USER_FLAG_28 = 60;
-const UTYF_USER_FLAG_29 = 61;
-const UTYF_USER_FLAG_30 = 62;
-const UTYF_USER_FLAG_31 = 63;
-const UTYF_USER_FLAG_32 = 64;
-const UTYF_USER_FLAG_33 = 65;
-const UTYF_USER_FLAG_34 = 66;
-const UTYF_USER_FLAG_35 = 67;
-const UTYF_USER_FLAG_36 = 68;
-const UTYF_USER_FLAG_37 = 69;
-const UTYF_USER_FLAG_38 = 70;
+const UTYF_MULTISLOT = 33; /* if server setting slot_control is ON, unit types with this flag can use extra city_build_slots*/
+const UTYF_TRANSPORTDEFENDER = 34; /* Unit can always defend while transported, even on non-native terrain */
+const UTYF_SENTRYALWAYS = 35; /* These units always behave as sentry (e.g. fortified snipers, air reconnaissance units who
+ can't sentry because !refuel tile, etc.) */
+const UTYF_NONPROVOKEVIGIL = 36; /* will attack non-provoking units on vigil */
+const UTYF_RESERVED1 = 36;
+const UTYF_RESERVED2 = 37; /* RESERVED for future use */
+const UTYF_RESERVED3 = 38;
+
+
+const UTYF_USER_FLAG_1 = 39; // Airbase: can build airbases
+const UTYF_USER_FLAG_2 = 40; // Transform: can transform terrain
+const UTYF_USER_FLAG_3 = 41; // CanRoad: Able to build roads
+const UTYF_USER_FLAG_4 = 42; // CanFortress: can build forts/fortresses
+const UTYF_USER_FLAG_5 = 43; // Bombarder (SUA)
+const UTYF_USER_FLAG_6 = 44; // AirAttacker
+const UTYF_USER_FLAG_7 = 45; // Horse
+const UTYF_USER_FLAG_8 = 46; // FootSoldier
+const UTYF_USER_FLAG_9 = 47; // Helicopter
+const UTYF_USER_FLAG_10 = 48; // Submarine
+const UTYF_USER_FLAG_11 = 49; // Unbribable
+const UTYF_USER_FLAG_12 = 50; // Traderoute
+const UTYF_USER_FLAG_13 = 51; // HelpWonder
+const UTYF_USER_FLAG_14 = 52; // Capturer
+const UTYF_USER_FLAG_15 = 53; // Capturable
+const UTYF_USER_FLAG_16 = 54; // Cities - can found cities
+const UTYF_USER_FLAG_17 = 55; // AddToCity - can add pop to city
+const UTYF_USER_FLAG_18 = 56; // Nuclear
+const UTYF_USER_FLAG_19 = 57; // Missile
+const UTYF_USER_FLAG_20 = 58; // Well-Digger
+const UTYF_USER_FLAG_21 = 59; // Infra
+const UTYF_USER_FLAG_22 = 60; // Proletarian
+const UTYF_USER_FLAG_23 = 61; // Paratroopers
+const UTYF_USER_FLAG_24 = 62; // Marines (thru MP2C), "AttackNonNative" (MP2D onward)
+const UTYF_USER_FLAG_25 = 63; // Expellable
+const UTYF_USER_FLAG_26 = 64; // AirProtector - unreachable and can protect its tile from units unable to reach it
+const UTYF_USER_FLAG_27 = 65; // CantReachAir - unable to attack air units
+const UTYF_USER_FLAG_28 = 66; // FortBuster - defending forts get no bonus, 33% attack bonus vs. Fortress
+const UTYF_USER_FLAG_29 = 67; // FortressBuster - defending fortresses get no bonus
+const UTYF_USER_FLAG_30 = 68; // AntiAir - Anti-Air bonus vs air, less effective vs Stealth
+const UTYF_USER_FLAG_31 = 69; // CanHide - can make Hideouts
+const UTYF_USER_FLAG_32 = 70; // WillNever
+const UTYF_USER_FLAG_33 = 71; // NonMilAttack - has military capacity but can enter territories with whom you're at peace
+const UTYF_USER_FLAG_34 = 72; // Cant_Pillage
+const UTYF_USER_FLAG_35 = 73; // CanClaim - can make a Tile Claim
+const UTYF_USER_FLAG_36 = 74; // CantAttack - can't do conventional attacks (usually means bombard only)
+const UTYF_USER_FLAG_37 = 75; // Workers - lets ruleset target Workers and Workers II with a single effect or actionenabler
+const UTYF_USER_FLAG_38 = 76;
+const UTYF_USER_FLAG_39 = 77;
+const UTYF_USER_FLAG_40 = 78; // unused , reserved for future use .....
+const UTYF_USER_FLAG_41 = 79;
+const UTYF_USER_FLAG_42 = 80;
+const UTYF_USER_FLAG_43 = 81;
+const UTYF_USER_FLAG_44 = 82;
+const UTYF_USER_FLAG_45 = 83;
+
// ^^ ...these can be continued up to const UTYF_USER_FLAG_44 = 76;
// Custom unit flags (MP2 sequence/order; TO DO: universalize/uniform order in other rules)
@@ -155,17 +176,21 @@ const UTYF_WELLDIGGER = UTYF_USER_FLAG_20 // Can dig a well and irrigate til
const UTYF_INFRA = UTYF_USER_FLAG_21 // Can build infrastructure
const UTYF_PROLETARIAN = UTYF_USER_FLAG_22 // Controllable only by communist governments
const UTYF_PARATROOPERS = UTYF_USER_FLAG_23 // Can be paradropped from a friendly city or suitable base.
-const UTYF_MARINES = UTYF_USER_FLAG_24 // Can launch attack from non-native tiles
+const UTYF_MARINES = UTYF_USER_FLAG_24 // Can launch attack from non-native tiles (thru MP2C)
+const UTYF_NONNATIVEATTACK = UTYF_USER_FLAG_24// Can attack non-native tiles (MP2D onward) (same flag position #24 as above).
const UTYF_EXPELLABLE = UTYF_USER_FLAG_25 // Can be peacefully expelled from foreign tiles.
const UTYF_AIRPROTECTOR = UTYF_USER_FLAG_26 // Is Unreachable AND can protect its tile form units unable to attack this unit
const UTYF_CANT_REACH_AIR = UTYF_USER_FLAG_27 // Unable to attack air units.
const UTYF_FORTBUSTER = UTYF_USER_FLAG_28 // Defending Forts get no bonus. Has (33%) attack bonus vs. Fortresses
const UTYF_FORTRESSBUSTER = UTYF_USER_FLAG_29 // Defending Fortresses get no bonus.
const UTYF_ANTIAIR = UTYF_USER_FLAG_30; // Anti-Air unit. e.g., AEGIS, AAA, Mobile SAM
-const UTYF_MULTISLOT = UTYF_USER_FLAG_31; /* Reserved for replacing Shield2Gold as flag for using multiple city_build_slots */
-const UTYF_CANHIDE = UTYF_USER_FLAG_32; /* Can make hideouts */
-const UTYF_WILLNEVER = UTYF_USER_FLAG_33; // Doesn't auto-attack.
-const UTYF_TRANSPORTDEFENDER = UTYF_USER_FLAG_34 // Can defend while transported on non-native tiles //
+const UTYF_CANHIDE = UTYF_USER_FLAG_31; /* Can make hideouts */
+const UTYF_WILLNEVER = UTYF_USER_FLAG_32; // Doesn't auto-attack.
+const UTYF_NONMILATTACK = UTYF_USER_FLAG_33; // NonMilAttack - can attack and enter peace waters/territory
+const UTYF_CANT_PILLAGE = UTYF_USER_FLAG_34; // CantPillage - unable to pillage tiles
+const UTYF_CAN_CLAIM = UTYF_USER_FLAG_35; // CanClaim - able to make Tile Claims
+const UTYF_CANT_ATTACK = UTYF_USER_FLAG_36; // CantAttack - unable to do conventional attacks (usually bombard only)
+const UTYF_WORKERS = UTYF_USER_FLAG_37; // Workers - allows manipulating Workers and Workers II with single effect or actionenabler
/**********************************************************************//**
Return true iff units of the given type can do the specified generalized
(ruleset defined) action enabler controlled action.
@@ -213,6 +238,20 @@ function utype_build_shield_cost(pcity, punittype)
return MAX(base * game.info.shieldbox / 100, 1);
}*/
+
+/**************************************************************************
+Fetch the unit type id by the matching name. Returns NULL if doesn't exist
+**************************************************************************/
+function utype_id_by_name(name)
+{
+ var utype = Object.entries(unit_types).filter(abc => {
+ return abc[1].name == name
+ })
+ if (utype.length == 1) { /* found it */
+ return parseInt(utype[0][0]);
+ }
+ return null;
+}
/**************************************************************************
Whether player can build given unit somewhere,
ignoring whether unit is obsolete and assuming the
diff --git a/freeciv-web/src/main/webapp/javascript/utility.js b/freeciv-web/src/main/webapp/javascript/utility.js
index c75a691ff..5e2adb6dc 100644
--- a/freeciv-web/src/main/webapp/javascript/utility.js
+++ b/freeciv-web/src/main/webapp/javascript/utility.js
@@ -241,6 +241,82 @@ function copy_string_to_clipboard (str) {
document.body.removeChild(el);
}
+/**************************************************************************
+ Allows changing the properties of a css class itself, instead of just
+ changing properties of existing elements with that class. This is useful
+ when you want the class itself to have the new property, so that it will
+ universally affect all instances of that class which appear in the future,
+ instead of changing only currently existing elements with that class.
+**************************************************************************/
+function changeCss(className, classValue) {
+ // we need invisible container to store additional css definitions
+ var cssMainContainer = $('#css-modifier-container');
+ if (cssMainContainer.length == 0) {
+ var cssMainContainer = $('');
+ cssMainContainer.hide();
+ cssMainContainer.appendTo($('body'));
+ }
+
+ // and we need one div for each class
+ classContainer = cssMainContainer.find('div[data-class="' + className + '"]');
+ if (classContainer.length == 0) {
+ classContainer = $('');
+ classContainer.appendTo(cssMainContainer);
+ }
+
+ // append additional style
+ classContainer.html('');
+}
+
+/**************************************************************************
+ Finds a time string in a message that has a class .ts timestamp and then
+ localizes the time from GMT to the browser's local time zone, and returns
+ the original message with local time in it instead of GMT.
+**************************************************************************/
+function time_localize(message) {
+ const t_start = message.indexOf("class='ts'") + 11;
+ const t_end = message.indexOf("class='ts'") + 16;
+
+ const GMT = message.substring(t_start, t_end);
+ const GMT_hour = parseInt(GMT.substring(0,2));
+ const GMT_minute = GMT.substring(3,5);
+
+ const date = new Date()
+ const minutes_offset = date.getTimezoneOffset();
+ const hours_offset = minutes_offset / 60;
+
+ var local_hour = GMT_hour - hours_offset - SERVER_GMT_OFFSET;
+
+ if (local_hour < 0) {
+ local_hour = 24 + local_hour;
+ }
+ else if (local_hour >= 24) {
+ local_hour -= 24;
+ }
+
+ const local_hour_string = "" + (local_hour < 10 ? "0" : "") + local_hour.toString();
+ var zone = date.toLocaleTimeString(undefined,{timeZoneName:'short'}).split(' ')[2];
+ if (!zone || zone=="undefined") zone = "";
+ const local_time = local_hour_string + ":" + GMT_minute + " " + zone;
+
+ return message.replace(GMT, local_time);
+}
+
+/**************************************************************************
+ Comes back false (null) if it is not an unaccented lower or upper case
+ character in the Latin alphabet.
+**************************************************************************/
+function is_alphabetic(text)
+{
+ var regex = /([\u0041-\u005A\u0061-\u007A]+)/g;
+ var result = text.match(regex);
+
+ // If there are illegal characters, they become separators and result comes back as an array
+ if (result == null || result.length>1) return null;
+
+ return result;
+}
+
/**************************************************************************
Returns original string iff it is composed of legal alphanumeric characters
and/or basic ASCII spaces and punctuation. Otherwise returns null.
@@ -344,6 +420,13 @@ function getHash(input){
return Math.abs(hash).toString();
}
+/**************************************************************************
+ Reverse lookup: get an object key by its value
+**************************************************************************/
+function getKeyByValue(object, value) {
+ return Object.keys(object).find(key => object[key] === value);
+}
+
/**************************************************************************
Force execution to delay by x milliseconds.
**************************************************************************/
@@ -444,14 +527,16 @@ function is_word_plural(word)
var len = word.length;
if (len<3) return false;
if (word.endsWith("ss")) return false; // -ss are singular
+ if (word.endsWith("s II")) return true; // Workers II, Barracks II
if (word.endsWith("men")) return true; // Pikemen, Riflemen, etc.
+ if (word.endsWith("nfantry")) return true; // Mechanized Infantry
+ if (word.endsWith("rtillery")) return true;
/* Exceptions I */
if (word.endsWith("AWACS")) return false; // names end in small 's'
if (word.endsWith("JTIDS")) return false; // names end in small 's'
if (word.endsWith("United Nations")) return false; // debatable ;)*/
/* Exceptions II, currently not needed / unimportant.
- if (word.endsWith("s II")) return true; // Barracks II, et similia
if (word.endsWith("s III")) return true; // Barracks III, et similia
if (word.endsWith("solos")) return false; // Mausoleum of Mausolos
if (word.endsWith("temis")) return false; // Temple of Artemis
diff --git a/freeciv-web/src/main/webapp/javascript/warcalc.js b/freeciv-web/src/main/webapp/javascript/warcalc.js
index f5b97940d..8fa98d48e 100644
--- a/freeciv-web/src/main/webapp/javascript/warcalc.js
+++ b/freeciv-web/src/main/webapp/javascript/warcalc.js
@@ -75,14 +75,14 @@ function warcalc_update_titles()
$("#wcamsg").prop("title", "Your "+unit_types[units[my_uid]['type']]['name']+" is set as attacker.\n\nOnly Veteran bonus is auto-calculated");
$("#wcamsg").html("A:⭐"+unit_types[units[my_uid]['type']]['name']);
- $("#wcdmsg").prop("title", "Foreign "+unit_types[units[their_uid]['type']]['name']+" is set as defender.\n\nVeteran, Terrain, and Fortify bonuses are included.\n\nNot included:\n Base, Unit-type bonus, City modifiers");
+ $("#wcdmsg").prop("title", "Foreign "+unit_types[units[their_uid]['type']]['name']+" is set as defender.\n\nOnly Veteran, Terrain, and Fortify bonuses are included.");
$("#wcdmsg").html("D:"+unit_types[units[their_uid]['type']]['name']);
}
else if (warcalc_role_mode == WARCALC_DEFENDING) {// swapped role
$("#wcamsg").prop("title", "Foreign "+unit_types[units[their_uid]['type']]['name']+" is set as attacker.\n\nOnly Veteran bonus is auto-calculated");
$("#wcamsg").html("A:"+unit_types[units[their_uid]['type']]['name']);
- $("#wcdmsg").prop("title", "Your "+unit_types[units[my_uid]['type']]['name']+" is set as defender.\n\nVeteran, Terrain, and Fortify bonuses are included.\n\nNot included:\n Base, Unit-type bonus, City modifiers");
+ $("#wcdmsg").prop("title", "Your "+unit_types[units[my_uid]['type']]['name']+" is set as defender.\n\nOnly Veteran, Terrain, and Fortify bonuses are included.");
$("#wcdmsg").html("D:⭐"+unit_types[units[my_uid]['type']]['name']);
}
}
@@ -433,27 +433,27 @@ function warcalc_set_tooltips()
}
// DEFEND BUTTONS
$("#wc500").hide(); // no 5x bonus in MP2/AG
- $("#wc125").prop("title", bl+"In city with SAM Battery vs. Stealth Aircraft");
- $("#wc133").prop("title", bl+"River"+nbl+"Swamp"+nbl+"Forest"+nbl+"Land/Heli in Fort vs. Land/Sea/Missile (not Armor)"+nbl+"Fighter over Fort/Fortress vs. Land/Sea/Missile (not Armor)"+nbl+"Sea unit in Naval base");
+ $("#wc125").prop("title", bl+"In city with SAM Battery dvs Stealth Aircraft");
+ $("#wc133").prop("title", bl+"River"+nbl+"Swamp"+nbl+"Forest"+nbl+"Land/Heli in Fort dvs Land/Sea/Missile (not Armor)"+nbl+"Fighter over Fort/Fortress dvs Land/Sea/Missile (not Armor)"+nbl+"Sea unit in Naval base");
$("#wc150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Jungle"+nbl+"Land unit Fortified OR inside city");
- $("#wc167").prop("title", bl+"Land/Heli in Fortress vs Armor/Aircraft"+nbl+"Land/Heli in Naval Base vs Armor/Aircraft");
+ $("#wc167").prop("title", bl+"Land/Heli in Fortress dvs Armor/Aircraft"+nbl+"Land/Heli in Naval Base dvs Armor/Aircraft");
$("#wc175").prop("title", bl+"Veteran-2 ('Hardened')");
- $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Hills"+nbl+"Land/Heli in Fortress vs. Land/Sea/Missile"+nbl+"In city with Coastal Defense vs. Sea"+nbl
- + "In city with SAM Battery vs. Air (not Stealth)"+nbl+"In city with SDI vs. Missile"+nbl+"Pikemen vs Horse (not Cavalry)"+nbl+"Knight vs. Foot soldier"+nbl
- + "Cruiser,Battleship,M.Destroyer,AEGIS vs Submarine"+nbl+"Sea unit vs. Marines"+nbl+"AAA/Mobile SAM vs. Aircraft"+nbl+"Missile Destroyer vs. Air/Missile"+nbl+"Armor II vs. Missile");
+ $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Hills"+nbl+"Land/Heli in Fortress dvs Land/Sea/Missile"+nbl+"In city with Coastal Defense dvs Sea"+nbl
+ + "In city with SAM Battery dvs Air (not Stealth)"+nbl+"In city with SDI dvs Missile"+nbl+"Pikemen dvs Horse (not Cavalry)"+nbl+"Knight dvs Foot soldier"+nbl
+ + "Cruiser,Battleship,M.Destroyer,AEGIS dvs Submarine"+nbl+"Sea unit dvs Marines"+nbl+"AAA/Mobile SAM dvs Aircraft"+nbl+"Missile Destroyer dvs Air/Missile"+nbl+"Armor II dvs Missile");
$("#wc210").prop("title", bl+"Veteran-4 ('Crack')");
$("#wc220").prop("title", bl+"Veteran-5 ('Master')");
$("#wc230").prop("title", bl+"Veteran-6 ('Champion')");
- $("#wc300").prop("title", bl+"Mountains"+nbl+"In city with City Walls vs Land (not Howitzer)"+nbl+"Knight vs. Horse (not Cavalry)"+nbl+"AEGIS vs. Air/Missile");
- $("#wc400").prop("title", bl+"Destroyer vs. Submarine");
+ $("#wc300").prop("title", bl+"Mountains"+nbl+"In city with City Walls dvs Land (not Howitzer)"+nbl+"Knight dvs Horse (not Cavalry)"+nbl+"AEGIS dvs Air/Missile");
+ $("#wc400").prop("title", bl+"Destroyer dvs Submarine");
// ATTACK BUTTONS
$("#wca133").hide(); // unused except for table alignment
$("#wca167").hide(); // " " " " "
- $("#wca125").prop("title", bl+"Stealth Aircraft vs. AAA/Mobile SAM/AEGIS Cruiser");
+ $("#wca125").prop("title", bl+"Stealth Aircraft avs AAA/Mobile SAM/AEGIS Cruiser");
$("#wca150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Phalanx/Pikemen + Agoge of Sparta");
$("#wca175").prop("title", bl+"Veteran-2 ('Hardened')");
- $("#wca200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"AAA/Mobile SAM vs. Aircraft"+nbl+"Fighter vs Heli (also: Heli FP=1)")+nbl+"Any unit vs Ship in a city: Defend FP1, Attack FP x2";
+ $("#wca200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"AAA/Mobile SAM avs Aircraft"+nbl+"Fighter dvs Heli (also: Heli FP=1)")+nbl+"Any unit avs Ship in a city: Defend FP1, Attack FP x2";
$("#wca210").prop("title", bl+"Veteran-4 ('Crack')");
$("#wca220").prop("title", bl+"Veteran-5 ('Master')");
$("#wca230").prop("title", bl+"Veteran-6 ('Champion')");
@@ -466,28 +466,44 @@ function warcalc_set_tooltips()
if (ruleset_control['name'].startsWith("Avant-garde")
|| ruleset_control['name'].startsWith("MP2")) //MP2 Brava onward starts with "MP2"
- { // AG distinctions
- $("#wc133").prop("title", bl+"River"+nbl+"Swamp"+nbl+"Forest"+nbl+"Land/Heli in Fort vs. Land/Sea/Missile (not Armor)"+nbl+"Fighter over Fort/Fortress vs. Land/Sea/Missile (not Armor)"
- +nbl+"Dive Bomber, Ground Strike Fighter vs. Anti-Air");
+ {
+ $("#wc133").prop("title", bl+"River"+nbl+"Swamp"+nbl+"Forest"+nbl+"Land/Heli in Fort dvs Land/Sea/Missile (not Armor)"+nbl+"Fighter over Fort/Fortress dvs Land/Sea/Missile (not Armor)"
+ +nbl+"Dive Bomber, Ground Strike Fighter dvs Anti-Air");
$("#wc150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Jungle"+nbl+"Land unit Fortified OR inside city"
- +nbl+"Helicopter vs. Foot or Mounted units");
- $("#wc167").prop("title", bl+"Land/Heli in Fortress vs Armor/Aircraft"+nbl+"Land/Heli/Sea in Naval Base vs Armor/Aircraft");
- $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Hills"+nbl+"Land/Heli in Fortress vs. Land/Sea/Missile"+nbl+"Land/Heli/Sea in Naval Base vs. Land/Sea/Missile"+nbl+"In city with Coastal Defense vs. Sea"+nbl
- + "In city with SAM Battery vs. Air/Heli (not Stealth)"+nbl+"In city with SDI vs. Missile"+nbl+"Pikemen vs Horse (not Cavalry)"+nbl+"Knight vs. Foot soldier"+nbl
- + "Cruiser,Battleship,M.Destroyer,AEGIS vs Submarine"+nbl+"Sea unit vs. Marines"+nbl+"AAA/Mobile SAM vs. Aircraft"+nbl+"Missile Destroyer vs. Air/Missile"+nbl+"Armor II vs. Missile");
-
- $("#wca150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Phalanx/Pikemen + Agoge of Sparta"+nbl+"Dive Bomber vs. Land or Sea");
- $("#wca200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"AAA/Mobile SAM vs. Aircraft");
+ +nbl+"Helicopter dvs Foot or Mounted units");
+ $("#wc167").prop("title", bl+"Land/Heli in Fortress dvs Armor/Aircraft"+nbl+"Land/Heli/Sea in Naval Base dvs Armor/Aircraft");
+ $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Hills"+nbl+"Land/Heli in Fortress dvs Land/Sea/Missile"+nbl+"Land/Heli/Sea in Naval Base dvs Land/Sea/Missile"+nbl+"In city with Coastal Defense dvs Sea"+nbl
+ + "In city with SAM Battery dvs Air/Heli (not Stealth)"+nbl+"In city with SDI dvs Missile"+nbl+"Pikemen dvs Horse (not Cavalry)"+nbl+"Knight dvs Foot soldier"+nbl
+ + "Cruiser,Battleship,M.Destroyer,AEGIS dvs Submarine"+nbl+"Sea unit dvs Marines"+nbl+"AAA/Mobile SAM dvs Aircraft"+nbl+"Missile Destroyer dvs Air/Missile"+nbl+"Armor II dvs Missile");
+
+ $("#wca150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Phalanx/Pikemen + Agoge of Sparta"+nbl+"Dive Bomber avs Land or Sea");
+ $("#wca200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"AAA/Mobile SAM avs Aircraft");
}
if (client_rules_flag[CRF_MP2_C]) {
+ $("#wc133").prop("title", bl+"Swamp"+nbl+"Forest"+nbl+"Land/Heli in Fort dvs Land/Sea/Missile (not Armor)"+nbl+"Fighter over Fort/Fortress dvs Land/Sea/Missile (not Armor)"
+ +nbl+"Dive Bomber, Ground Strike Fighter dvs Anti-Air");
$("#wc175").prop("title", bl+"Veteran-2 ('Hardened')");
$("#wc150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Jungle"+nbl+"Land unit Fortified OR inside city"
- +nbl+"Helicopter vs. Foot or Mounted units"+nbl+"In city with Fortifications vs Land (not Ballistic)");
- $("#wc167").prop("title", bl+"Land/Heli in Fortress vs Armor/Aircraft"+nbl+"Land/Heli/Sea in Naval Base vs Armor/Aircraft"+nbl+"Jungle in city with Fortifications");
- $("#wc125").prop("title", bl+"In city with Fortifications vs Catapult"+nbl+"In city with SAM Battery vs. Stealth Aircraft");
- $("#wca125").prop("title", bl+"Stealth Aircraft vs. AAA/Mobile SAM/AEGIS Cruiser");
- $("#wca150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Phalanx/Pikemen + Agoge of Sparta"+nbl+"Dive Bomber vs. Land or Sea");
- $("#wca175").prop("title", bl+"Veteran-2 ('Hardened')"+nbl+"Artillery vs City Walls");
+ +nbl+"Helicopter dvs Foot or Mounted units"+nbl+"In city with Fortifications dvs Land (not Ballistic)");
+ $("#wc167").prop("title", bl+"Land/Heli in Fortress dvs Armor/Aircraft"+nbl+"Land/Heli/Sea in Naval Base dvs Armor/Aircraft"+nbl+"Jungle in city with Fortifications");
+ $("#wc125").prop("title", bl+"In city with Fortifications dvs Catapult"+nbl+"In city with SAM Battery dvs Stealth Aircraft");
+ $("#wca125").prop("title", bl+"Stealth Aircraft avs AAA/Mobile SAM/AEGIS Cruiser");
+ $("#wca150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Phalanx/Pikemen + Agoge of Sparta"+nbl+"Dive Bomber avs Land or Sea");
+ $("#wca175").prop("title", bl+"Veteran-2 ('Hardened')");
+ }
+ if (client_rules_flag[CRF_MP2_D]) {
+ $("#wca133").show();
+ $("#wca133").prop("title", bl+"FP2 ships attacking land (Firepower is reduced to 1)");
+ $("#wc125").prop("title", bl+"In city with City Walls dvs Artillery"+nbl+"In city with SAM Battery dvs Stealth Aircraft");
+ $("#wc150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Jungle"+nbl+"Flatland terrain in city with Fortifications"+nbl+"Land unit Fortified OR inside city"+nbl+"In city with City Walls dvs Cannon"
+ +nbl+"Helicopter dvs Foot or Mounted units"+nbl+"Armor II dvs Missile" );
+ $("#wc167").prop("title", bl+"Swamp in city with Fortifications"+nbl+"Forest in city with Fortifications"+nbl+"Land/Heli in Fortress dvs Armor/Aircraft"+nbl+"Land/Heli/Sea in Naval Base dvs Armor/Aircraft");
+ $("#wc175").prop("title", bl+"Veteran-2 ('Hardened')"+nbl+"In city with City Walls dvs Catapult");
+ $("#wc300").prop("title", bl+"Mountains"+nbl+"Knight dvs Horse (not Cavalry)"+nbl+"AEGIS dvs Air/Missile");
+ $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Hills"+nbl+"In city with City Walls dvs Land (not Ballistic class)"+nbl+"In city with Coastal Defense dvs Sea"
+ +nbl+"In city with SAM Battery dvs Air/Heli (not Stealth)"+nbl+"In city with SDI dvs Missile"+nbl+"Land/Heli in Fortress dvs Land/Sea/Missile"+nbl+"Land/Heli/Sea in Naval Base dvs Land/Sea/Missile"
+ +nbl+"Pikemen dvs Horse (not Cavalry)"+nbl+"Knight dvs Foot soldier"
+ +nbl+"Cruiser,Battleship,M.Destroyer,AEGIS dvs Submarine"+nbl+"Sea unit dvs Marines"+nbl+"AAA/Mobile SAM dvs Aircraft"+nbl+"Mobile SAM dvs Missile"+nbl+"Missile Destroyer dvs Air/Missile");
}
return;
}
@@ -505,13 +521,13 @@ function warcalc_set_tooltips()
$("#wc150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"River"+nbl+"Swamp"+nbl+"Forest"+nbl+"Jungle"+nbl+"Land unit Fortified OR inside city");
$("#wc175").prop("title", bl+"Veteran-2 ('Hardened')");
- $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Hills"+nbl+"Land/Heli in Fortress vs. Land/Sea units"+nbl+"Pikemen vs. Horse (not Cavalry)"+nbl+"In city with Coastal Defense vs. Sea"+nbl+"In city with SAM Battery vs. Aircraft"+nbl+"In city with SDI vs. Missile");
- $("#wc300").prop("title", bl+"Mountains"+nbl+"In city with City Walls vs Land/Heli (not Howitzer)");
- $("#wc500").prop("title", bl+"AEGIS vs. Air/Missile");
+ $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Hills"+nbl+"Land/Heli in Fortress dvs Land/Sea units"+nbl+"Pikemen dvs Horse (not Cavalry)"+nbl+"In city with Coastal Defense dvs Sea"+nbl+"In city with SAM Battery dvs Aircraft"+nbl+"In city with SDI dvs Missile");
+ $("#wc300").prop("title", bl+"Mountains"+nbl+"In city with City Walls dvs Land/Heli (not Howitzer)");
+ $("#wc500").prop("title", bl+"AEGIS dvs Air/Missile");
// ATTACK BUTTONS
$("#wca150").prop("title", bl+"Veteran-1 ('Veteran')");
$("#wca175").prop("title", bl+"Veteran-2 ('Hardened')");
- $("#wca200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Fighter vs Heli (also: Heli FP=1)");
+ $("#wca200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Fighter avs Heli (also: Heli FP=1)");
// TOOLTIPS NEEDING MORE SPACE
$("#wc200").tooltip({open: function (event, ui) {ui.tooltip.css("max-width", "400px");}});
$("#wc300").tooltip({open: function (event, ui) {ui.tooltip.css("max-width", "400px");}});
@@ -522,13 +538,13 @@ function warcalc_set_tooltips()
// DEFEND BUTTONS
$("#wc300").hide(); // unused
$("#wc125").prop("title", bl+"River"+nbl+"Swamp"+nbl+"Forest"+nbl+"Jungle");
- $("#wc150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Hills"+nbl+"Land unit Fortified OR inside city"+nbl+"Sea unit inside city"+nbl+"Land unit in Fort vs. Land/Sea"+nbl
- + "On Airstrip vs. Aircraft"+nbl+"In Airbase vs. Land/Sea"+nbl+"In city and Nation has Great Wall");
+ $("#wc150").prop("title", bl+"Veteran-1 ('Veteran')"+nbl+"Hills"+nbl+"Land unit Fortified OR inside city"+nbl+"Sea unit inside city"+nbl+"Land unit in Fort dvs Land/Sea"+nbl
+ + "On Airstrip dvs Aircraft"+nbl+"In Airbase dvs Land/Sea"+nbl+"In city and Nation has Great Wall");
$("#wc175").prop("title", bl+"Veteran-2 ('Hardened')");
- $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Mountains"+nbl+"Destroyer vs. Submarine"+nbl+""
- + "Land unit in Fortress vs. Land/Sea"+nbl+"In Airbase vs. Aircraft"+nbl+"In city with City Walls vs Land unit"+nbl+"In city with Coastal Defense vs. Sea"+nbl
- + "In city with SAM Battery vs. Air/Heli"+nbl+"In city with SDI vs. Missile" );
- $("#wc500").prop("title", bl+"AEGIS vs. Air/Missile");
+ $("#wc200").prop("title", bl+"Veteran-3 ('Elite')"+nbl+"Mountains"+nbl+"Destroyer dvs Submarine"+nbl+""
+ + "Land unit in Fortress dvs Land/Sea"+nbl+"In Airbase dvs Aircraft"+nbl+"In city with City Walls dvs Land unit"+nbl+"In city with Coastal Defense dvs Sea"+nbl
+ + "In city with SAM Battery dvs Air/Heli"+nbl+"In city with SDI dvs Missile" );
+ $("#wc500").prop("title", bl+"AEGIS dvs Air/Missile");
// ATTACK BUTTONS
$("#wca150").prop("title", bl+"Veteran-1 ('Veteran')");
$("#wca175").prop("title", bl+"Veteran-2 ('Hardened')");
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/README.md b/freeciv-web/src/main/webapp/javascript/webgl/README.md
new file mode 100644
index 000000000..8a45da204
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/README.md
@@ -0,0 +1,30 @@
+WebGL renderer for Freeciv-web
+==============================
+
+This is the WebGL + Three.js renderer for Freeciv-web.
+
+[Three.js](https://threejs.org/) is the 3D engine used in Freeciv-web.
+
+Custom GLSL Fragment and Vertex shaders can be found in the shaders subdirectory.
+
+![Freeciv-web](https://raw.githubusercontent.com/freeciv/freeciv-web/develop/freeciv-web/src/main/webapp/javascript/webgl/freeciv-webgl.png "Freeciv-web WebGL screenshot")
+
+The Blender 3D models can be found here: https://github.com/freeciv/freeciv-web/tree/develop/blender
+
+TODO for 3D WebGL client
+---------------------
+- The 3D WebGL client currently needs webgl_vision_cheat_temporary.patch in order to get terrain heights correctly.
+- Separate Java Webapp for gltf 3d models and textures for improved build performance.
+- Update Three.js version to latest version.
+
+
+
+Building and testing
+--------------------
+Build Freeciv-web as normal with Vagrant as described in the main README file.
+You can then test it in your instance.
+
+You may use the WebGL interface live at:
+- [moving borders](https://fcw.movingborders.es)
+
+Developer: Andreas Rosdal [@andreasrosdal](http://www.github.com/andreasrosdal)
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/animation.js b/freeciv-web/src/main/webapp/javascript/webgl/animation.js
new file mode 100644
index 000000000..44eb51968
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/animation.js
@@ -0,0 +1,160 @@
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2017 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+
+var anim_objs = {};
+
+/****************************************************************************
+ Updates unit movement animation.
+****************************************************************************/
+function update_animated_objects()
+{
+ for (var unit_id in anim_objs) {
+
+ var punit = units[anim_objs[unit_id]['unit']];
+ var mesh = anim_objs[unit_id]['mesh'];
+ var flag = anim_objs[unit_id]['flag'];
+
+ if (punit == null || mesh == null) {
+ delete anim_objs[unit_id];
+ continue;
+ }
+ var anim_list = punit['anim_list'];
+ if (anim_list[0] == null || anim_list[1] == null) {
+ delete anim_objs[unit_id];
+ continue;
+ }
+
+ var tile_start = tiles[anim_list[0]['tile']];
+ var tile_end = tiles[anim_list[1]['tile']];
+ var pos_start = map_to_scene_coords(tile_start['x'], tile_start['y']);
+ var pos_end = map_to_scene_coords(tile_end['x'], tile_end['y']);
+ var delta_x = (pos_end['x'] - pos_start['x']) / ANIM_STEPS;
+ var delta_y = (pos_end['y'] - pos_start['y']) / ANIM_STEPS;
+ var delta_z = ((tile_end['height'] - tile_start['height']) * 100) / ANIM_STEPS;
+
+ mesh.rotateOnAxis(new THREE.Vector3(0,1,0).normalize(), -1 * (convert_unit_rotation(punit['facing']) * Math.PI * 2 / 8));
+ mesh.translateOnAxis(new THREE.Vector3(1,0,0).normalize(), delta_x);
+ mesh.translateOnAxis(new THREE.Vector3(0,1,0).normalize(), delta_z);
+ mesh.translateOnAxis(new THREE.Vector3(0,0,1).normalize(), delta_y);
+ mesh.rotateOnAxis(new THREE.Vector3(0,1,0).normalize(), (convert_unit_rotation(punit['facing']) * Math.PI * 2 / 8));
+ mesh.updateMatrix();
+
+ if (flag != null) {
+ flag.rotateOnAxis(new THREE.Vector3(0,1,0).normalize(), -1 * Math.PI / 4);
+ flag.translateOnAxis(new THREE.Vector3(1,0,0).normalize(), delta_x);
+ flag.translateOnAxis(new THREE.Vector3(0,1,0).normalize(), delta_z);
+ flag.translateOnAxis(new THREE.Vector3(0,0,1).normalize(), delta_y);
+ flag.rotateOnAxis(new THREE.Vector3(0,1,0).normalize(), Math.PI / 4);
+ flag.updateMatrix();
+ }
+
+ anim_list[0]['i'] = anim_list[0]['i'] - 1;
+ if (anim_list[0]['i'] == 0) {
+ punit['anim_list'].splice(0, 1);
+ if (punit['anim_list'].length == 1) {
+ punit['anim_list'].splice(0, 1);
+ }
+ }
+ if (anim_list.length <= 1) {
+ punit['anim_list'] = [];
+ delete anim_objs[unit_id];
+ update_unit_position(tile_end);
+ anim_units_count--;
+ }
+
+ }
+
+
+}
+
+
+/****************************************************************************
+ Renders an explosion animation on the given tile.
+****************************************************************************/
+function animate_explosion_on_tile(tile_id, animation_frame)
+{
+ if (scene == null) return;
+
+ var ptile = tiles[tile_id];
+ if (ptile == null) return;
+
+ var height = 5 + ptile['height'] * 100;
+
+ if (ptile['explosion_mesh'] != null) {
+ scene.remove(ptile['explosion_mesh']);
+ ptile['explosion_mesh'] = null;
+ }
+ if (animation_frame == 5) {
+ scene.remove(ptile['explosion_mesh']);
+ ptile['explosion_mesh'] = null;
+ return;
+ }
+
+ var explosion_mesh = get_unit_explosion_mesh(animation_frame);
+ var pos = map_to_scene_coords(ptile['x'], ptile['y']);
+ explosion_mesh.matrixAutoUpdate = false;
+ explosion_mesh.translateOnAxis(new THREE.Vector3(1,0,0).normalize(), pos['x'] - 6);
+ explosion_mesh.translateOnAxis(new THREE.Vector3(0,1,0).normalize(), height + 2);
+ explosion_mesh.translateOnAxis(new THREE.Vector3(0,0,1).normalize(), pos['y'] - 6);
+ explosion_mesh.rotation.y = Math.PI / 4;
+ explosion_mesh.updateMatrix();
+ if (scene != null && explosion_mesh != null) {
+ ptile['explosion_mesh'] = explosion_mesh;
+ scene.add(explosion_mesh);
+ }
+
+ if (animation_frame <= 4) setTimeout("animate_explosion_on_tile(" + tile_id + "," + (animation_frame + 1) + ")", 350);
+
+}
+
+/****************************************************************************
+ Renders a nuclear explosion animation on the given tile.
+****************************************************************************/
+function render_nuclear_explosion(ptile)
+{
+ if (ptile == null) return;
+ var height = 5 + ptile['height'] * 100;
+
+ var explosion_mesh = get_nuke_explosion_mesh();
+ var pos = map_to_scene_coords(ptile['x'], ptile['y']);
+ explosion_mesh.matrixAutoUpdate = false;
+ explosion_mesh.translateOnAxis(new THREE.Vector3(1,0,0).normalize(), pos['x'] + 30);
+ explosion_mesh.translateOnAxis(new THREE.Vector3(0,1,0).normalize(), height + 50);
+ explosion_mesh.translateOnAxis(new THREE.Vector3(0,0,1).normalize(), pos['y'] + 30);
+ explosion_mesh.rotation.y = Math.PI / 4;
+ explosion_mesh.updateMatrix();
+
+ if (scene != null && explosion_mesh != null) {
+ scene.add(explosion_mesh);
+ ptile['nuclear_mesh'] = explosion_mesh;
+ }
+
+ setTimeout("remove_nuclear_explosion(" + ptile['index'] + ")", 3000);
+
+}
+
+/****************************************************************************
+...
+****************************************************************************/
+function remove_nuclear_explosion(tile_id)
+{
+ var ptile = tiles[tile_id];
+ scene.remove(ptile['nuclear_mesh']);
+}
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/benchmark.js b/freeciv-web/src/main/webapp/javascript/webgl/benchmark.js
new file mode 100644
index 000000000..410cd3df4
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/benchmark.js
@@ -0,0 +1,170 @@
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2017 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+var benchmark_start;
+var benchmark_enabled = false;
+var benchmark_frames_count = 0;
+var initial_benchmark_enabled = true;
+
+/****************************************************************************
+ Runs a benchmark of the WebGL version
+****************************************************************************/
+function webgl_benchmark_run()
+{
+ benchmark_enabled = true;
+ $("#dialog").dialog('close');
+ $("#pregame_settings").dialog('close');
+ send_message("/set mapseed 420");
+ send_message("/set gameseed 420");
+ send_message("/start");
+ setTimeout(benchmark_check, 1000);
+}
+
+
+/****************************************************************************
+...
+****************************************************************************/
+function benchmark_check()
+{
+ try {
+ $("#dialog").dialog('close');
+ } catch (err) {}
+ try {
+ $(".diplomacy_dialog").dialog('close');
+ } catch (err) {}
+ try {
+ $("#tech_dialog").dialog('close');
+ } catch (err) {}
+
+ if (game_info != null && game_info['turn'] >= 30) {
+ var time_elapsed = (new Date().getTime() - benchmark_start) / 1000;
+ var fps = Math.floor(benchmark_frames_count / time_elapsed);
+
+ $("#benchmark_dialog").remove();
+ $("").appendTo("div#game_page");
+
+ $("#benchmark_dialog").html("Total time: " + time_elapsed + " seconds. "
+ + "Frames per second: " + fps);
+ $("#benchmark_dialog").attr("title", "Benchmark results:");
+ $("#benchmark_dialog").dialog({
+ bgiframe: true,
+ modal: true,
+ width: "50%",
+ buttons: {
+ Ok: function() {
+ $("#benchmark_dialog").dialog('close');
+ }
+ }
+ });
+
+ $("#benchmark_dialog").dialog('open');
+
+ benchmark_enabled = false;
+ return;
+ }
+ if (game_info != null && game_info['turn'] == 1 && units[101] != null) {
+ request_unit_do_action(ACTION_FOUND_CITY,
+ 101, units[101]['tile'], 0, "Hello world");
+ }
+
+ key_unit_auto_explore();
+ if (game_info != null) send_end_turn();
+
+ setTimeout(benchmark_check, 1000);
+}
+
+/****************************************************************************
+ Measure the fps for the first 10 seconds of the game, and show an error
+ if the fps is too low to play.
+****************************************************************************/
+function initial_benchmark_check()
+{
+ var time_elapsed = (new Date().getTime() - benchmark_start) / 1000;
+ var fps = Math.floor(benchmark_frames_count / time_elapsed);
+ initial_benchmark_enabled = false;
+ if (fps < 4) {
+ var renderer_name = "-";
+ var gl = document.createElement('canvas').getContext('webgl');
+ if (gl != null) {
+ var extension = gl.getExtension('WEBGL_debug_renderer_info');
+ if (extension != undefined) {
+ renderer_name = gl.getParameter(extension.UNMASKED_RENDERER_WEBGL);
+ }
+ }
+
+ var quality_string = "";
+ if (graphics_quality == QUALITY_MEDIUM) quality_string = "Medium quality";
+ if (graphics_quality == QUALITY_HIGH) quality_string = "High quality";
+
+ var message = "The game is running too slowly! Please check if the drivers for your graphics driver needs to be updated and that you have a graphics card which supports WebGL 3D. "
+ + "3D WebGL rendering requires updated drivers and a good 3D graphics card with 3D hardware acceleration. The current WebGL renderer is: " + renderer_name + ". ";
+ if (renderer_name == "Google SwiftShader") message += " Warning: Google SwiftShader is a software renderer which has very poor performance and will not work with this game. ";
+ message += " You can try playing the 2D version instead, or configure lower graphics quality.
";
+ message += "Current graphics level: " + quality_string;
+
+ show_slow_game_warning_message("3D game is running very slowly!", message);
+
+ console.error("WebGL 3D is running slowly. FPS: " + fps + ", Quality:" + quality_string + ", Renderer: " + renderer_name);
+
+ }
+
+}
+
+
+/**************************************************************************
+ Shows a generic message dialog.
+**************************************************************************/
+function show_slow_game_warning_message(title, message) {
+
+ // reset dialog page.
+ $("#generic_dialog").remove();
+ $("").appendTo("div#game_page");
+
+ speak(title);
+ speak(message);
+
+ $("#generic_dialog").html(message);
+ $("#generic_dialog").attr("title", title);
+ $("#generic_dialog").dialog({
+ bgiframe: true,
+ modal: true,
+ width: is_small_screen() ? "90%" : "50%",
+ close: closing_dialog_message,
+ buttons: {
+ "Play 2D version - Restart!" : function() {
+ window.location.href = '/';
+ },
+ "Configure graphics settings" : function() {
+ $("#generic_dialog").remove();
+ $("").appendTo("div#game_page");
+ $("").appendTo("div#pregame_page");
+ pregame_settings();
+ $("#pregame_settings_tabs").tabs({ active: 1 });
+ },
+ "Play anyway": close_dialog_message
+
+ }
+ });
+
+ $("#generic_dialog").dialog('open');
+ $("#game_text_input").blur();
+
+ $('#generic_dialog').css("max-height", "450px");
+
+}
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/borders.js b/freeciv-web/src/main/webapp/javascript/webgl/borders.js
new file mode 100644
index 000000000..86b35deeb
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/borders.js
@@ -0,0 +1,172 @@
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2017 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+var border_image_resolution = 512;
+var borders_palette = [];
+var borders_texture;
+var borders_hash = -1;
+
+/****************************************************************************
+ Initialize borders image.
+****************************************************************************/
+function init_borders_image()
+{
+ borders_palette = [];
+ borders_palette.push([142, 0, 0]);
+ for (var player_id in players) {
+ var pplayer = players[player_id];
+ var nation_colors;
+ if (nations[pplayer['nation']].color != null) {
+ nation_colors = nations[pplayer['nation']].color.replace("rgb(", "").replace(")", "").split(",");
+ if (nation_colors[0] < nation_colors[1] - 10 && nation_colors[2] < nation_colors[1] - 10) nation_colors = [0, 20, 0]; //darken green
+ } else {
+ nation_colors = [0, 0, 0];
+ }
+ borders_palette.push([parseInt(nation_colors[0]) * 0.65, parseInt(nation_colors[2]) * 0.65, parseInt(nation_colors[1]) * 0.65]);
+ }
+
+ bmp_lib.render('borders_image',
+ generate_borders_image(),
+ borders_palette);
+ borders_texture = new THREE.Texture();
+ borders_texture.magFilter = THREE.NearestFilter;
+ borders_texture.minFilter = THREE.NearestFilter;
+ borders_texture.image = document.getElementById("borders_image");
+ borders_texture.image.onload = function () {
+ borders_texture.needsUpdate = true;
+ };
+
+ if (graphics_quality == QUALITY_MEDIUM) setInterval(update_borders_image, 15000);
+ if (graphics_quality == QUALITY_HIGH) setInterval(update_borders_image, 4000);
+}
+
+/****************************************************************************
+ Returns a texture containing one pixel for each map tile, where the color of each pixel
+ contains the border color.
+****************************************************************************/
+function update_borders_image()
+{
+ var hash = generate_borders_image_hash();
+
+ if (hash != borders_hash) {
+ borders_palette = [];
+ borders_palette.push([142, 0, 0]);
+ for (var player_id in players) {
+ var pplayer = players[player_id];
+ var nation_colors;
+ if (nations[pplayer['nation']].color != null) {
+ nation_colors = nations[pplayer['nation']].color.replace("rgb(", "").replace(")", "").split(",");
+ if (nation_colors[0] < nation_colors[1] - 10 && nation_colors[2] < nation_colors[1] - 10) nation_colors = [0, 20, 0]; //darken green
+ } else {
+ nation_colors = [0,0,0];
+ }
+
+ borders_palette.push([parseInt(nation_colors[0]) * 0.65, parseInt(nation_colors[2]) * 0.65, parseInt(nation_colors[1]) * 0.65]);
+ }
+
+ bmp_lib.render('borders_image',
+ generate_borders_image(),
+ borders_palette);
+ borders_texture.image = document.getElementById("borders_image");
+ borders_texture.image.onload = function () {
+ borders_texture.needsUpdate = true;
+ };
+ borders_hash = hash;
+
+ return borders_texture;
+ }
+}
+
+/****************************************************************************
+
+****************************************************************************/
+function generate_borders_image() {
+
+ var row;
+ // The grid of points that make up the image.
+ var grid = Array(border_image_resolution);
+ for (row = 0; row < border_image_resolution ; row++) {
+ grid[row] = Array(border_image_resolution);
+ }
+
+ for (var x = 0; x < border_image_resolution ; x++) {
+ for (var y = 0; y < border_image_resolution; y++) {
+ var gx = Math.floor(map.ysize * x / border_image_resolution);
+ var gy = Math.floor(map.xsize * y / border_image_resolution);
+ grid[x][y] = border_image_color(gy, gx);
+ }
+ }
+
+ var result = Array(border_image_resolution);
+ for (row = 0; row < border_image_resolution ; row++) {
+ result[row] = Array(border_image_resolution);
+ }
+
+ for (var x = 0; x < border_image_resolution ; x++) {
+ for (var y = 0; y < border_image_resolution; y++) {
+ if (x == 0 || y == 0 || x >= border_image_resolution - 1 || y >= border_image_resolution - 1) {
+ result[x][y] = grid[x][y];
+ } else {
+ var is_border = (grid[x][y] > 0
+ && (grid[x-1][y-1] != grid[x][y] || grid[x-1][y] != grid[x][y] || grid[x][y-1] != grid[x][y] || grid[x+1][y] != grid[x][y]
+ || grid[x][y+1] != grid[x][y] || grid[x+1][y+1] != grid[x][y] || grid[x-1][y+1] != grid[x][y] || grid[x+1][y-1] != grid[x][y]));
+ if (is_border) {
+ result[x][y] = grid[x][y];
+ } else {
+ result[x][y] = 0;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+
+/****************************************************************************
+ Creates a hash of the map borders.
+****************************************************************************/
+function generate_borders_image_hash() {
+ var hash = 0;
+
+ for (var x = 0; x < border_image_resolution ; x++) {
+ for (var y = 0; y < border_image_resolution; y++) {
+ var gx = Math.floor(map.ysize * x / border_image_resolution);
+ var gy = Math.floor(map.xsize * y / border_image_resolution);
+ hash += border_image_color(gy, gx);
+ }
+ }
+
+ return hash;
+}
+
+
+/****************************************************************************
+ Returns the color of the tile at the given map position.
+****************************************************************************/
+function border_image_color(map_x, map_y)
+{
+ var ptile = map_pos_to_tile(map_x, map_y);
+
+ if (ptile != null && ptile['owner'] != null && ptile['owner'] < 255) {
+ return 1 + ptile['owner'];
+ }
+
+ return 0;
+}
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/camera.js b/freeciv-web/src/main/webapp/javascript/webgl/camera.js
new file mode 100644
index 000000000..bd30b1176
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/camera.js
@@ -0,0 +1,97 @@
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2017 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+var camera;
+
+var camera_dx = 300;
+var camera_dy = 490;
+var camera_dz = 300;
+var camera_current_x = 0;
+var camera_current_y = 0;
+var camera_current_z = 0;
+
+
+/****************************************************************************
+ Point the camera to look at point x, y, z in Three.js coordinates.
+****************************************************************************/
+function camera_look_at(x, y, z)
+{
+ camera_current_x = x;
+ camera_current_y = y;
+ camera_current_z = z;
+
+ if (camera != null) {
+ camera.position.set( x + camera_dx, y + camera_dy, z + camera_dz);
+ camera.lookAt( new THREE.Vector3(x, 0, z));
+ }
+
+ if (directionalLight != null) directionalLight.position.set( x + camera_dx, y + camera_dy, z + camera_dz ).normalize();
+
+}
+
+/**************************************************************************
+ Centers the mapview around the given tile..
+**************************************************************************/
+function center_tile_mapcanvas_3d(ptile)
+{
+ if (ptile != null) {
+ var pos = map_to_scene_coords(ptile['x'], ptile['y']);
+ camera_look_at(pos['x'], 0, pos['y']);
+ }
+
+}
+
+/**************************************************************************
+ Enabled silding of the mapview to the given tile.
+**************************************************************************/
+function enable_mapview_slide_3d(ptile)
+{
+ var pos_dest = map_to_scene_coords(ptile['x'], ptile['y']);
+
+ mapview_slide['dx'] = camera_current_x - pos_dest['x'];
+ mapview_slide['dy'] = camera_current_z - pos_dest['y'];
+ mapview_slide['i'] = mapview_slide['max'];
+ mapview_slide['prev'] = mapview_slide['i'];
+ mapview_slide['start'] = new Date().getTime();
+ mapview_slide['active'] = true;
+
+}
+
+/**************************************************************************
+ Updates mapview slide, called once per frame.
+**************************************************************************/
+function update_map_slide_3d()
+{
+ var elapsed = 1 + new Date().getTime() - mapview_slide['start'];
+ mapview_slide['i'] = Math.floor(mapview_slide['max']
+ * (mapview_slide['slide_time']
+ - elapsed) / mapview_slide['slide_time']);
+
+ if (mapview_slide['i'] <= 0) {
+ mapview_slide['active'] = false;
+ return;
+ }
+
+ var dx = Math.floor(mapview_slide['dx'] * (mapview_slide['i'] - mapview_slide['prev']) / mapview_slide['max']);
+ var dy = Math.floor(mapview_slide['dy'] * (mapview_slide['i'] - mapview_slide['prev']) / mapview_slide['max']);
+
+ camera_look_at(camera_current_x + dx, 0, camera_current_z + dy);
+ mapview_slide['prev'] = mapview_slide['i'];
+
+}
\ No newline at end of file
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/freeciv-webgl.png b/freeciv-web/src/main/webapp/javascript/webgl/freeciv-webgl.png
new file mode 100644
index 000000000..40cf02fa6
Binary files /dev/null and b/freeciv-web/src/main/webapp/javascript/webgl/freeciv-webgl.png differ
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/goto.js b/freeciv-web/src/main/webapp/javascript/webgl/goto.js
new file mode 100644
index 000000000..fbe707a01
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/goto.js
@@ -0,0 +1,77 @@
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2016 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+var goto_lines = [];
+
+/****************************************************************************
+ Renders a goto line by creating many planes along the goto path.
+****************************************************************************/
+function webgl_render_goto_line(start_tile, goto_packet_dir)
+{
+ clear_goto_tiles();
+ var ptile = start_tile;
+
+ var material = new THREE.MeshBasicMaterial( { color: 0x055dff, side:THREE.DoubleSide} );
+ var goto_width = 3;
+
+ for (var i = 0; i < goto_packet_dir.length; i++) {
+ if (ptile == null) break;
+ var dir = goto_packet_dir[i];
+
+ if (dir == -1) {
+ /* Assume that this means refuel. */
+ continue;
+ }
+
+ var nexttile = mapstep(ptile, dir);
+ if (nexttile != null) {
+ var currpos = map_to_scene_coords(ptile['x'], ptile['y']);
+ var nextpos = map_to_scene_coords(nexttile['x'], nexttile['y']);
+ var height = 5 + ptile['height'] * 100;
+ if (ptile['x'] == 0 || ptile['x'] >= map['xsize'] - 1 || nexttile['x'] == 0 || nexttile['x'] >= map['xsize'] - 1) continue;
+
+ var gotoLineGemetry = new THREE.PlaneGeometry(60, 5);
+ gotoLineGemetry.dynamic = true;
+ var delta = 0;
+ if (dir == 1 || dir == 6) delta = 3;
+ gotoLineGemetry.vertices[0].x = 0.0;
+ gotoLineGemetry.vertices[0].y = 0.0;
+ gotoLineGemetry.vertices[0].z = 0.0;
+ gotoLineGemetry.vertices[1].x = nextpos['x'] - currpos['x'];
+ gotoLineGemetry.vertices[1].y = (nexttile['height'] - ptile['height']) * 100 - delta;
+ gotoLineGemetry.vertices[1].z = nextpos['y'] - currpos['y'];
+ gotoLineGemetry.vertices[2].x = 0.0;
+ gotoLineGemetry.vertices[2].y = delta;
+ gotoLineGemetry.vertices[2].z = goto_width;
+ gotoLineGemetry.vertices[3].x = nextpos['x'] - currpos['x'];
+ gotoLineGemetry.vertices[3].y = (nexttile['height'] - ptile['height']) * 100 + delta;
+ gotoLineGemetry.vertices[3].z = nextpos['y'] - currpos['y'] + goto_width;
+ gotoLineGemetry.verticesNeedUpdate = true;
+ var gotoline = new THREE.Mesh(gotoLineGemetry, material);
+
+ gotoline.translateOnAxis(new THREE.Vector3(1,0,0).normalize(), currpos['x'] + 10);
+ gotoline.translateOnAxis(new THREE.Vector3(0,1,0).normalize(), height + 25);
+ gotoline.translateOnAxis(new THREE.Vector3(0,0,1).normalize(), currpos['y'] + 10);
+ scene.add(gotoline);
+ goto_lines.push(gotoline);
+ }
+
+ ptile = mapstep(ptile, dir);
+ }
+}
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/heightmap.js b/freeciv-web/src/main/webapp/javascript/webgl/heightmap.js
new file mode 100644
index 000000000..6952ec807
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/heightmap.js
@@ -0,0 +1,203 @@
+/**********************************************************************
+ Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
+ Copyright (C) 2009-2016 The Freeciv-web project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+***********************************************************************/
+
+
+var heightmap = null;
+
+/****************************************************************************
+ Create heightmap, a 2d array with the height.
+****************************************************************************/
+function create_heightmap()
+{
+ var start_heightmap = new Date().getTime();
+ var heightmap_resolution_x = map.xsize * 4 + 1; // FIXME: in some special-cases, map.xsize is null (not set yet).
+ var heightmap_resolution_y = map.ysize * 4 + 1;
+
+ var heightmap_tiles = new Array(map.xsize);
+ var distance_from_coast_map = new Array(map.xsize);
+ for (var x = 0; x < map.xsize; x++) {
+ heightmap_tiles[x] = new Array(map.ysize);
+ distance_from_coast_map[x] = new Array(map.ysize);
+ }
+
+ /* Here we look at every tile and determine its distance from the sea or river. */
+ /* First of all we set non-sea tiles' distance to be very big. */
+ for (var x = 0; x < map.xsize ; x++) {
+ for (var y = 0; y < map.ysize; y++) {
+ var ptile = map_pos_to_tile(x, y);
+ if (ptile != null && tile_terrain(ptile) != null && !is_ocean_tile(ptile) && !tile_has_extra(ptile, EXTRA_RIVER)) {
+ distance_from_coast_map[x][y] = 100000;
+ } else {
+ distance_from_coast_map[x][y] = 0;
+ }
+ }
+ }
+ /* Then, for each sea or river tile, we propagate the distance information to the inner
+ * land. */
+ for (var x = 0; x < map.xsize; x++) {
+ for (var y = 0; y < map.ysize; y++) {
+ var ptile = map_pos_to_tile(x, y);
+ if (ptile != null && tile_terrain(ptile) != null && (is_ocean_tile(ptile) || tile_has_extra(ptile, EXTRA_RIVER))) {
+ propagate_distance_from_coast(distance_from_coast_map, x, y, 0);
+ }
+ }
+ }
+
+ for (var x = 0; x < map.xsize; x++) {
+ for (var y = 0; y < map.ysize; y++) {
+ var ptile = map_pos_to_tile(x, y);
+ if (ptile != null) {
+ heightmap_tiles[x][y] = 0.5 + 0.4 * map_tile_height(ptile) + 0.09 * distance_from_coast_map[x][y];
+ if (heightmap_tiles[x][y] > ptile['height']) {
+ ptile['height'] = heightmap_tiles[x][y];
+ }
+ }
+ }
+ }
+
+ heightmap = new Array(heightmap_resolution_x);
+ for (var hx = 0; hx < heightmap_resolution_x; hx++) {
+ heightmap[hx] = new Array(heightmap_resolution_y);
+ }
+
+ /* smooth */
+ for (var x = 0; x < heightmap_resolution_x; x++) {
+ for (var y = 0; y < heightmap_resolution_y; y++) {
+ var gx = x / 4 - 0.5;
+ var gy = y / 4 - 0.5;
+
+ if (Math.round(gx) == gx && Math.round(gy) == gy) {
+ /* On tile center */
+ heightmap[x][y] = heightmap_tiles[gx][gy];
+ } else {
+ /* Interpolate between neighbouring tiles, with each tile having weight:
+ * 1 / (distance to the tile)^2.
+ */
+ var neighbours = [
+ { "x": Math.floor(gx), "y": Math.floor(gy) },
+ { "x": Math.floor(gx), "y": Math.ceil(gy) },
+ { "x": Math.ceil(gx), "y": Math.floor(gy) },
+ { "x": Math.ceil(gx), "y": Math.ceil(gy) }];
+
+ var norm = 0;
+ var sum = 0;
+ for (var i = 0; i < 4; i++) {
+ var coords = neighbours[i];
+ if (coords.x < 0 || coords.x >= map.xsize ||
+ coords.y < 0 || coords.y >= map.ysize) {
+ /* Outside of map, don't use in the sum */
+ continue;
+ }
+ var dx = gx - coords.x;
+ var dy = gy - coords.y;
+ var distance = Math.sqrt(dx*dx + dy*dy);
+ sum += heightmap_tiles[coords.x][coords.y] / distance / distance;
+ norm += 1. / distance / distance;
+ }
+ // set final heightmap value, and add some random noise.
+ heightmap[x][y] = (sum / norm);
+ }
+ }
+ }
+
+ for (var x = 0; x < heightmap_resolution_x; x++) {
+ for (var y = 0; y < heightmap_resolution_y; y++) {
+ if (heightmap[x][y] > 100) {
+ heightmap[x][y] = 0.5 + 0.4 * ((Math.random() - 0.5) / 60) + 0.2;
+ }
+ }
+ }
+
+ console.log("create_heightmap took: " + (new Date().getTime() - start_heightmap) + " ms.");
+
+}
+
+/****************************************************************************
+ ...
+****************************************************************************/
+function propagate_distance_from_coast(distance_from_coast_map, x, y, level)
+{
+ var current_distance = distance_from_coast_map[x][y];
+
+ // limit distance from coast to 2. this is a quick-fix for inland tiles
+ // incorrectly assigned as mountains by the fragment shader.
+ if (current_distance >= 2) current_distance = 2;
+
+ for (var dir = 0; dir < DIR8_LAST; dir++) {
+ var dir_x = x + DIR_DX[dir];
+ var dir_y = y + DIR_DY[dir];
+ if (dir_x < 0 || dir_x >= map.xsize || dir_y < 0 || dir_y >= map.ysize) {
+ /* Outside of the map */
+ continue;
+ }
+ if (distance_from_coast_map[dir_x][dir_y] > current_distance + 1) {
+ distance_from_coast_map[dir_x][dir_y] = current_distance + 1;
+ if (level < (map.xsize * 2)) propagate_distance_from_coast(distance_from_coast_map, dir_x, dir_y, level + 1);
+ }
+ }
+}
+
+/****************************************************************************
+ Returns the tile height from -1.0 to 1.0
+****************************************************************************/
+function map_tile_height(ptile)
+{
+ if (ptile != null && tile_terrain(ptile) != null) {
+ if (tile_has_extra(ptile, EXTRA_RIVER)) return -0.01;
+ if (is_ocean_tile(ptile)) return -0.13 + ((Math.random() - 0.5) / 80);
+ if (tile_terrain(ptile)['name'] == "Hills") return 0.25;
+ if (tile_terrain(ptile)['name'] == "Mountains") return 0.65 + ((Math.random() - 0.5) / 10);
+ }
+
+ return 0.0 + ((Math.random() - 0.5) / 60);
+}
+
+/****************************************************************************
+ Returns height offset for units. This will make units higher above cities.
+****************************************************************************/
+function get_unit_height_offset(punit)
+{
+ if (punit == null) return 0;
+ var ptile = index_to_tile(punit['tile']);
+ if (ptile == null) return 0;
+ var pcity = tile_city(ptile);
+
+ if (pcity != null) return 10;
+
+ return 0;
+
+}
+
+/****************************************************************************
+ Returns height offset for cities.
+****************************************************************************/
+function get_city_height_offset(pcity)
+{
+ if (pcity == null) return 0;
+ var ptile = index_to_tile(pcity['tile']);
+ if (ptile == null) return 0;
+
+ if (tile_terrain(ptile) != null) {
+ if (tile_terrain(ptile)['name'] == "Hills") return -6;
+ if (tile_terrain(ptile)['name'] == "Mountains") return -10;
+ }
+
+ return 2;
+
+}
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/libs/AnaglyphEffect.js b/freeciv-web/src/main/webapp/javascript/webgl/libs/AnaglyphEffect.js
new file mode 100644
index 000000000..69e398e24
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/libs/AnaglyphEffect.js
@@ -0,0 +1,151 @@
+THREE.AnaglyphEffect = function ( renderer, width, height ) {
+
+ // Dubois matrices from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.7.6968&rep=rep1&type=pdf#page=4
+
+ this.colorMatrixLeft = new THREE.Matrix3().fromArray( [
+ 0.456100, - 0.0400822, - 0.0152161,
+ 0.500484, - 0.0378246, - 0.0205971,
+ 0.176381, - 0.0157589, - 0.00546856
+ ] );
+
+ this.colorMatrixRight = new THREE.Matrix3().fromArray( [
+ - 0.0434706, 0.378476, - 0.0721527,
+ - 0.0879388, 0.73364, - 0.112961,
+ - 0.00155529, - 0.0184503, 1.2264
+ ] );
+
+ var _camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+
+ var _scene = new THREE.Scene();
+
+ var _stereo = new THREE.StereoCamera();
+
+ var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat };
+
+ if ( width === undefined ) width = 512;
+ if ( height === undefined ) height = 512;
+
+ var _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params );
+ var _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params );
+
+ var _material = new THREE.ShaderMaterial( {
+
+ uniforms: {
+
+ 'mapLeft': { value: _renderTargetL.texture },
+ 'mapRight': { value: _renderTargetR.texture },
+
+ 'colorMatrixLeft': { value: this.colorMatrixLeft },
+ 'colorMatrixRight': { value: this.colorMatrixRight }
+
+ },
+
+ vertexShader: [
+
+ 'varying vec2 vUv;',
+
+ 'void main() {',
+
+ ' vUv = vec2( uv.x, uv.y );',
+ ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+
+ '}'
+
+ ].join( '\n' ),
+
+ fragmentShader: [
+
+ 'uniform sampler2D mapLeft;',
+ 'uniform sampler2D mapRight;',
+ 'varying vec2 vUv;',
+
+ 'uniform mat3 colorMatrixLeft;',
+ 'uniform mat3 colorMatrixRight;',
+
+ // These functions implement sRGB linearization and gamma correction
+
+ 'float lin( float c ) {',
+ ' return c <= 0.04045 ? c * 0.0773993808 :',
+ ' pow( c * 0.9478672986 + 0.0521327014, 2.4 );',
+ '}',
+
+ 'vec4 lin( vec4 c ) {',
+ ' return vec4( lin( c.r ), lin( c.g ), lin( c.b ), c.a );',
+ '}',
+
+ 'float dev( float c ) {',
+ ' return c <= 0.0031308 ? c * 12.92',
+ ' : pow( c, 0.41666 ) * 1.055 - 0.055;',
+ '}',
+
+
+ 'void main() {',
+
+ ' vec2 uv = vUv;',
+
+ ' vec4 colorL = lin( texture2D( mapLeft, uv ) );',
+ ' vec4 colorR = lin( texture2D( mapRight, uv ) );',
+
+ ' vec3 color = clamp(',
+ ' colorMatrixLeft * colorL.rgb +',
+ ' colorMatrixRight * colorR.rgb, 0., 1. );',
+
+ ' gl_FragColor = vec4(',
+ ' dev( color.r ), dev( color.g ), dev( color.b ),',
+ ' max( colorL.a, colorR.a ) );',
+
+ '}'
+
+ ].join( '\n' )
+
+ } );
+
+ var _mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), _material );
+ _scene.add( _mesh );
+
+ this.setSize = function ( width, height ) {
+
+ renderer.setSize( width, height );
+
+ var pixelRatio = renderer.getPixelRatio();
+
+ _renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
+ _renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
+
+ };
+
+ this.render = function ( scene, camera ) {
+
+ var currentRenderTarget = renderer.getRenderTarget();
+
+ scene.updateMatrixWorld();
+
+ if ( camera.parent === null ) camera.updateMatrixWorld();
+
+ _stereo.update( camera );
+
+ renderer.setRenderTarget( _renderTargetL );
+ renderer.clear();
+ renderer.render( scene, _stereo.cameraL );
+
+ renderer.setRenderTarget( _renderTargetR );
+ renderer.clear();
+ renderer.render( scene, _stereo.cameraR );
+
+ renderer.setRenderTarget( null );
+ renderer.render( _scene, _camera );
+
+ renderer.setRenderTarget( currentRenderTarget );
+
+ };
+
+ this.dispose = function () {
+
+ if ( _renderTargetL ) _renderTargetL.dispose();
+ if ( _renderTargetR ) _renderTargetR.dispose();
+ if ( _mesh ) _mesh.geometry.dispose();
+ if ( _material ) _material.dispose();
+
+ };
+
+};
diff --git a/freeciv-web/src/main/webapp/javascript/webgl/libs/GLTFLoader.js b/freeciv-web/src/main/webapp/javascript/webgl/libs/GLTFLoader.js
new file mode 100644
index 000000000..f2369ee8d
--- /dev/null
+++ b/freeciv-web/src/main/webapp/javascript/webgl/libs/GLTFLoader.js
@@ -0,0 +1,3872 @@
+THREE.GLTFLoader = ( function () {
+
+ function GLTFLoader( manager ) {
+
+ THREE.Loader.call( this, manager );
+
+ this.dracoLoader = null;
+ this.ddsLoader = null;
+ this.ktx2Loader = null;
+ this.meshoptDecoder = null;
+
+ this.pluginCallbacks = [];
+
+ this.register( function ( parser ) {
+
+ return new GLTFMaterialsClearcoatExtension( parser );
+
+ } );
+
+ this.register( function ( parser ) {
+
+ return new GLTFTextureBasisUExtension( parser );
+
+ } );
+
+ this.register( function ( parser ) {
+
+ return new GLTFTextureWebPExtension( parser );
+
+ } );
+
+ this.register( function ( parser ) {
+
+ return new GLTFMaterialsTransmissionExtension( parser );
+
+ } );
+
+ this.register( function ( parser ) {
+
+ return new GLTFLightsExtension( parser );
+
+ } );
+
+ this.register( function ( parser ) {
+
+ return new GLTFMeshoptCompression( parser );
+
+ } );
+
+ }
+
+ GLTFLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
+
+ constructor: GLTFLoader,
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var resourcePath;
+
+ if ( this.resourcePath !== '' ) {
+
+ resourcePath = this.resourcePath;
+
+ } else if ( this.path !== '' ) {
+
+ resourcePath = this.path;
+
+ } else {
+
+ resourcePath = THREE.LoaderUtils.extractUrlBase( url );
+
+ }
+
+ // Tells the LoadingManager to track an extra item, which resolves after
+ // the model is fully loaded. This means the count of items loaded will
+ // be incorrect, but ensures manager.onLoad() does not fire early.
+ this.manager.itemStart( url );
+
+ var _onError = function ( e ) {
+
+ if ( onError ) {
+
+ onError( e );
+
+ } else {
+
+ console.error( e );
+
+ }
+
+ scope.manager.itemError( url );
+ scope.manager.itemEnd( url );
+
+ };
+
+ var loader = new THREE.FileLoader( this.manager );
+
+ loader.setPath( this.path );
+ loader.setResponseType( 'arraybuffer' );
+ loader.setRequestHeader( this.requestHeader );
+ loader.setWithCredentials( this.withCredentials );
+
+ loader.load( url, function ( data ) {
+
+ try {
+
+ scope.parse( data, resourcePath, function ( gltf ) {
+
+ onLoad( gltf );
+
+ scope.manager.itemEnd( url );
+
+ }, _onError );
+
+ } catch ( e ) {
+
+ _onError( e );
+
+ }
+
+ }, onProgress, _onError );
+
+ },
+
+ setDRACOLoader: function ( dracoLoader ) {
+
+ this.dracoLoader = dracoLoader;
+ return this;
+
+ },
+
+ setDDSLoader: function ( ddsLoader ) {
+
+ this.ddsLoader = ddsLoader;
+ return this;
+
+ },
+
+ setKTX2Loader: function ( ktx2Loader ) {
+
+ this.ktx2Loader = ktx2Loader;
+ return this;
+
+ },
+
+ setMeshoptDecoder: function ( meshoptDecoder ) {
+
+ this.meshoptDecoder = meshoptDecoder;
+ return this;
+
+ },
+
+ register: function ( callback ) {
+
+ if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) {
+
+ this.pluginCallbacks.push( callback );
+
+ }
+
+ return this;
+
+ },
+
+ unregister: function ( callback ) {
+
+ if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) {
+
+ this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 );
+
+ }
+
+ return this;
+
+ },
+
+ parse: function ( data, path, onLoad, onError ) {
+
+ var content;
+ var extensions = {};
+ var plugins = {};
+
+ if ( typeof data === 'string' ) {
+
+ content = data;
+
+ } else {
+
+ var magic = THREE.LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) );
+
+ if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) {
+
+ try {
+
+ extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data );
+
+ } catch ( error ) {
+
+ if ( onError ) onError( error );
+ return;
+
+ }
+
+ content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content;
+
+ } else {
+
+ content = THREE.LoaderUtils.decodeText( new Uint8Array( data ) );
+
+ }
+
+ }
+
+ var json = JSON.parse( content );
+
+ if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) {
+
+ if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) );
+ return;
+
+ }
+
+ var parser = new GLTFParser( json, {
+
+ path: path || this.resourcePath || '',
+ crossOrigin: this.crossOrigin,
+ manager: this.manager,
+ ktx2Loader: this.ktx2Loader,
+ meshoptDecoder: this.meshoptDecoder
+
+ } );
+
+ parser.fileLoader.setRequestHeader( this.requestHeader );
+
+ for ( var i = 0; i < this.pluginCallbacks.length; i ++ ) {
+
+ var plugin = this.pluginCallbacks[ i ]( parser );
+ plugins[ plugin.name ] = plugin;
+
+ // Workaround to avoid determining as unknown extension
+ // in addUnknownExtensionsToUserData().
+ // Remove this workaround if we move all the existing
+ // extension handlers to plugin system
+ extensions[ plugin.name ] = true;
+
+ }
+
+ if ( json.extensionsUsed ) {
+
+ for ( var i = 0; i < json.extensionsUsed.length; ++ i ) {
+
+ var extensionName = json.extensionsUsed[ i ];
+ var extensionsRequired = json.extensionsRequired || [];
+
+ switch ( extensionName ) {
+
+ case EXTENSIONS.KHR_MATERIALS_UNLIT:
+ extensions[ extensionName ] = new GLTFMaterialsUnlitExtension();
+ break;
+
+ case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
+ extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
+ break;
+
+ case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
+ extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader );
+ break;
+
+ case EXTENSIONS.MSFT_TEXTURE_DDS:
+ extensions[ extensionName ] = new GLTFTextureDDSExtension( this.ddsLoader );
+ break;
+
+ case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
+ extensions[ extensionName ] = new GLTFTextureTransformExtension();
+ break;
+
+ case EXTENSIONS.KHR_MESH_QUANTIZATION:
+ extensions[ extensionName ] = new GLTFMeshQuantizationExtension();
+ break;
+
+ default:
+
+ if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) {
+
+ console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ parser.setExtensions( extensions );
+ parser.setPlugins( plugins );
+ parser.parse( onLoad, onError );
+
+ }
+
+ } );
+
+ /* GLTFREGISTRY */
+
+ function GLTFRegistry() {
+
+ var objects = {};
+
+ return {
+
+ get: function ( key ) {
+
+ return objects[ key ];
+
+ },
+
+ add: function ( key, object ) {
+
+ objects[ key ] = object;
+
+ },
+
+ remove: function ( key ) {
+
+ delete objects[ key ];
+
+ },
+
+ removeAll: function () {
+
+ objects = {};
+
+ }
+
+ };
+
+ }
+
+ /*********************************/
+ /********** EXTENSIONS ***********/
+ /*********************************/
+
+ var EXTENSIONS = {
+ KHR_BINARY_GLTF: 'KHR_binary_glTF',
+ KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
+ KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
+ KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
+ KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
+ KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
+ KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
+ KHR_TEXTURE_BASISU: 'KHR_texture_basisu',
+ KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
+ KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization',
+ EXT_TEXTURE_WEBP: 'EXT_texture_webp',
+ EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
+ MSFT_TEXTURE_DDS: 'MSFT_texture_dds'
+ };
+
+ /**
+ * DDS Texture Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
+ *
+ */
+ function GLTFTextureDDSExtension( ddsLoader ) {
+
+ if ( ! ddsLoader ) {
+
+ throw new Error( 'THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader' );
+
+ }
+
+ this.name = EXTENSIONS.MSFT_TEXTURE_DDS;
+ this.ddsLoader = ddsLoader;
+
+ }
+
+ /**
+ * Punctual Lights Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
+ */
+ function GLTFLightsExtension( parser ) {
+
+ this.parser = parser;
+ this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL;
+
+ // Object3D instance caches
+ this.cache = { refs: {}, uses: {} };
+
+ }
+
+ GLTFLightsExtension.prototype._markDefs = function () {
+
+ var parser = this.parser;
+ var nodeDefs = this.parser.json.nodes || [];
+
+ for ( var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) {
+
+ var nodeDef = nodeDefs[ nodeIndex ];
+
+ if ( nodeDef.extensions
+ && nodeDef.extensions[ this.name ]
+ && nodeDef.extensions[ this.name ].light !== undefined ) {
+
+ parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light );
+
+ }
+
+ }
+
+ };
+
+ GLTFLightsExtension.prototype._loadLight = function ( lightIndex ) {
+
+ var parser = this.parser;
+ var cacheKey = 'light:' + lightIndex;
+ var dependency = parser.cache.get( cacheKey );
+
+ if ( dependency ) return dependency;
+
+ var json = parser.json;
+ var extensions = ( json.extensions && json.extensions[ this.name ] ) || {};
+ var lightDefs = extensions.lights || [];
+ var lightDef = lightDefs[ lightIndex ];
+ var lightNode;
+
+ var color = new THREE.Color( 0xffffff );
+
+ if ( lightDef.color !== undefined ) color.fromArray( lightDef.color );
+
+ var range = lightDef.range !== undefined ? lightDef.range : 0;
+
+ switch ( lightDef.type ) {
+
+ case 'directional':
+ lightNode = new THREE.DirectionalLight( color );
+ lightNode.target.position.set( 0, 0, - 1 );
+ lightNode.add( lightNode.target );
+ break;
+
+ case 'point':
+ lightNode = new THREE.PointLight( color );
+ lightNode.distance = range;
+ break;
+
+ case 'spot':
+ lightNode = new THREE.SpotLight( color );
+ lightNode.distance = range;
+ // Handle spotlight properties.
+ lightDef.spot = lightDef.spot || {};
+ lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0;
+ lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
+ lightNode.angle = lightDef.spot.outerConeAngle;
+ lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
+ lightNode.target.position.set( 0, 0, - 1 );
+ lightNode.add( lightNode.target );
+ break;
+
+ default:
+ throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type );
+
+ }
+
+ // Some lights (e.g. spot) default to a position other than the origin. Reset the position
+ // here, because node-level parsing will only override position if explicitly specified.
+ lightNode.position.set( 0, 0, 0 );
+
+ lightNode.decay = 2;
+
+ if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity;
+
+ lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) );
+
+ dependency = Promise.resolve( lightNode );
+
+ parser.cache.add( cacheKey, dependency );
+
+ return dependency;
+
+ };
+
+ GLTFLightsExtension.prototype.createNodeAttachment = function ( nodeIndex ) {
+
+ var self = this;
+ var parser = this.parser;
+ var json = parser.json;
+ var nodeDef = json.nodes[ nodeIndex ];
+ var lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {};
+ var lightIndex = lightDef.light;
+
+ if ( lightIndex === undefined ) return null;
+
+ return this._loadLight( lightIndex ).then( function ( light ) {
+
+ return parser._getNodeRef( self.cache, lightIndex, light );
+
+ } );
+
+ };
+
+ /**
+ * Unlit Materials Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
+ */
+ function GLTFMaterialsUnlitExtension() {
+
+ this.name = EXTENSIONS.KHR_MATERIALS_UNLIT;
+
+ }
+
+ GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () {
+
+ return THREE.MeshBasicMaterial;
+
+ };
+
+ GLTFMaterialsUnlitExtension.prototype.extendParams = function ( materialParams, materialDef, parser ) {
+
+ var pending = [];
+
+ materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 );
+ materialParams.opacity = 1.0;
+
+ var metallicRoughness = materialDef.pbrMetallicRoughness;
+
+ if ( metallicRoughness ) {
+
+ if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
+
+ var array = metallicRoughness.baseColorFactor;
+
+ materialParams.color.fromArray( array );
+ materialParams.opacity = array[ 3 ];
+
+ }
+
+ if ( metallicRoughness.baseColorTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) );
+
+ }
+
+ }
+
+ return Promise.all( pending );
+
+ };
+
+ /**
+ * Clearcoat Materials Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
+ */
+ function GLTFMaterialsClearcoatExtension( parser ) {
+
+ this.parser = parser;
+ this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT;
+
+ }
+
+ GLTFMaterialsClearcoatExtension.prototype.getMaterialType = function ( materialIndex ) {
+
+ var parser = this.parser;
+ var materialDef = parser.json.materials[ materialIndex ];
+
+ if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
+
+ return THREE.MeshPhysicalMaterial;
+
+ };
+
+ GLTFMaterialsClearcoatExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) {
+
+ var parser = this.parser;
+ var materialDef = parser.json.materials[ materialIndex ];
+
+ if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
+
+ return Promise.resolve();
+
+ }
+
+ var pending = [];
+
+ var extension = materialDef.extensions[ this.name ];
+
+ if ( extension.clearcoatFactor !== undefined ) {
+
+ materialParams.clearcoat = extension.clearcoatFactor;
+
+ }
+
+ if ( extension.clearcoatTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) );
+
+ }
+
+ if ( extension.clearcoatRoughnessFactor !== undefined ) {
+
+ materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor;
+
+ }
+
+ if ( extension.clearcoatRoughnessTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) );
+
+ }
+
+ if ( extension.clearcoatNormalTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) );
+
+ if ( extension.clearcoatNormalTexture.scale !== undefined ) {
+
+ var scale = extension.clearcoatNormalTexture.scale;
+
+ materialParams.clearcoatNormalScale = new THREE.Vector2( scale, scale );
+
+ }
+
+ }
+
+ return Promise.all( pending );
+
+ };
+
+ /**
+ * Transmission Materials Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission
+ * Draft: https://github.com/KhronosGroup/glTF/pull/1698
+ */
+ function GLTFMaterialsTransmissionExtension( parser ) {
+
+ this.parser = parser;
+ this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION;
+
+ }
+
+ GLTFMaterialsTransmissionExtension.prototype.getMaterialType = function ( materialIndex ) {
+
+ var parser = this.parser;
+ var materialDef = parser.json.materials[ materialIndex ];
+
+ if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
+
+ return THREE.MeshPhysicalMaterial;
+
+ };
+
+ GLTFMaterialsTransmissionExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) {
+
+ var parser = this.parser;
+ var materialDef = parser.json.materials[ materialIndex ];
+
+ if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
+
+ return Promise.resolve();
+
+ }
+
+ var pending = [];
+
+ var extension = materialDef.extensions[ this.name ];
+
+ if ( extension.transmissionFactor !== undefined ) {
+
+ materialParams.transmission = extension.transmissionFactor;
+
+ }
+
+ if ( extension.transmissionTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) );
+
+ }
+
+ return Promise.all( pending );
+
+ };
+
+ /**
+ * BasisU Texture Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
+ */
+ function GLTFTextureBasisUExtension( parser ) {
+
+ this.parser = parser;
+ this.name = EXTENSIONS.KHR_TEXTURE_BASISU;
+
+ }
+
+ GLTFTextureBasisUExtension.prototype.loadTexture = function ( textureIndex ) {
+
+ var parser = this.parser;
+ var json = parser.json;
+
+ var textureDef = json.textures[ textureIndex ];
+
+ if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) {
+
+ return null;
+
+ }
+
+ var extension = textureDef.extensions[ this.name ];
+ var source = json.images[ extension.source ];
+ var loader = parser.options.ktx2Loader;
+
+ if ( ! loader ) {
+
+ if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) {
+
+ throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' );
+
+ } else {
+
+ // Assumes that the extension is optional and that a fallback texture is present
+ return null;
+
+ }
+
+ }
+
+ return parser.loadTextureImage( textureIndex, source, loader );
+
+ };
+
+ /**
+ * WebP Texture Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp
+ */
+ function GLTFTextureWebPExtension( parser ) {
+
+ this.parser = parser;
+ this.name = EXTENSIONS.EXT_TEXTURE_WEBP;
+ this.isSupported = null;
+
+ }
+
+ GLTFTextureWebPExtension.prototype.loadTexture = function ( textureIndex ) {
+
+ var name = this.name;
+ var parser = this.parser;
+ var json = parser.json;
+
+ var textureDef = json.textures[ textureIndex ];
+
+ if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) {
+
+ return null;
+
+ }
+
+ var extension = textureDef.extensions[ name ];
+ var source = json.images[ extension.source ];
+ var loader = source.uri ? parser.options.manager.getHandler( source.uri ) : parser.textureLoader;
+
+ return this.detectSupport().then( function ( isSupported ) {
+
+ if ( isSupported ) return parser.loadTextureImage( textureIndex, source, loader );
+
+ if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) {
+
+ throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' );
+
+ }
+
+ // Fall back to PNG or JPEG.
+ return parser.loadTexture( textureIndex );
+
+ } );
+
+ };
+
+ GLTFTextureWebPExtension.prototype.detectSupport = function () {
+
+ if ( ! this.isSupported ) {
+
+ this.isSupported = new Promise( function ( resolve ) {
+
+ var image = new Image();
+
+ // Lossy test image. Support for lossy images doesn't guarantee support for all
+ // WebP images, unfortunately.
+ image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
+
+ image.onload = image.onerror = function () {
+
+ resolve( image.height === 1 );
+
+ };
+
+ } );
+
+ }
+
+ return this.isSupported;
+
+ };
+
+ /**
+ * meshopt BufferView Compression Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression
+ */
+ function GLTFMeshoptCompression( parser ) {
+
+ this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION;
+ this.parser = parser;
+
+ }
+
+ GLTFMeshoptCompression.prototype.loadBufferView = function ( index ) {
+
+ var json = this.parser.json;
+ var bufferView = json.bufferViews[ index ];
+
+ if ( bufferView.extensions && bufferView.extensions[ this.name ] ) {
+
+ var extensionDef = bufferView.extensions[ this.name ];
+
+ var buffer = this.parser.getDependency( 'buffer', extensionDef.buffer );
+ var decoder = this.parser.options.meshoptDecoder;
+
+ if ( ! decoder || ! decoder.supported ) {
+
+ if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) {
+
+ throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' );
+
+ } else {
+
+ // Assumes that the extension is optional and that fallback buffer data is present
+ return null;
+
+ }
+
+ }
+
+ return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) {
+
+ var byteOffset = extensionDef.byteOffset || 0;
+ var byteLength = extensionDef.byteLength || 0;
+
+ var count = extensionDef.count;
+ var stride = extensionDef.byteStride;
+
+ var result = new ArrayBuffer( count * stride );
+ var source = new Uint8Array( res[ 0 ], byteOffset, byteLength );
+
+ decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter );
+ return result;
+
+ } );
+
+ } else {
+
+ return null;
+
+ }
+
+ };
+
+ /* BINARY EXTENSION */
+ var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
+ var BINARY_EXTENSION_HEADER_LENGTH = 12;
+ var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
+
+ function GLTFBinaryExtension( data ) {
+
+ this.name = EXTENSIONS.KHR_BINARY_GLTF;
+ this.content = null;
+ this.body = null;
+
+ var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH );
+
+ this.header = {
+ magic: THREE.LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ),
+ version: headerView.getUint32( 4, true ),
+ length: headerView.getUint32( 8, true )
+ };
+
+ if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) {
+
+ throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' );
+
+ } else if ( this.header.version < 2.0 ) {
+
+ throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' );
+
+ }
+
+ var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH );
+ var chunkIndex = 0;
+
+ while ( chunkIndex < chunkView.byteLength ) {
+
+ var chunkLength = chunkView.getUint32( chunkIndex, true );
+ chunkIndex += 4;
+
+ var chunkType = chunkView.getUint32( chunkIndex, true );
+ chunkIndex += 4;
+
+ if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) {
+
+ var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength );
+ this.content = THREE.LoaderUtils.decodeText( contentArray );
+
+ } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) {
+
+ var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
+ this.body = data.slice( byteOffset, byteOffset + chunkLength );
+
+ }
+
+ // Clients must ignore chunks with unknown types.
+
+ chunkIndex += chunkLength;
+
+ }
+
+ if ( this.content === null ) {
+
+ throw new Error( 'THREE.GLTFLoader: JSON content not found.' );
+
+ }
+
+ }
+
+ /**
+ * DRACO Mesh Compression Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
+ */
+ function GLTFDracoMeshCompressionExtension( json, dracoLoader ) {
+
+ if ( ! dracoLoader ) {
+
+ throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' );
+
+ }
+
+ this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
+ this.json = json;
+ this.dracoLoader = dracoLoader;
+ this.dracoLoader.preload();
+
+ }
+
+ GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) {
+
+ var json = this.json;
+ var dracoLoader = this.dracoLoader;
+ var bufferViewIndex = primitive.extensions[ this.name ].bufferView;
+ var gltfAttributeMap = primitive.extensions[ this.name ].attributes;
+ var threeAttributeMap = {};
+ var attributeNormalizedMap = {};
+ var attributeTypeMap = {};
+
+ for ( var attributeName in gltfAttributeMap ) {
+
+ var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
+
+ threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ];
+
+ }
+
+ for ( attributeName in primitive.attributes ) {
+
+ var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
+
+ if ( gltfAttributeMap[ attributeName ] !== undefined ) {
+
+ var accessorDef = json.accessors[ primitive.attributes[ attributeName ] ];
+ var componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
+
+ attributeTypeMap[ threeAttributeName ] = componentType;
+ attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true;
+
+ }
+
+ }
+
+ return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
+
+ return new Promise( function ( resolve ) {
+
+ dracoLoader.decodeDracoFile( bufferView, function ( geometry ) {
+
+ for ( var attributeName in geometry.attributes ) {
+
+ var attribute = geometry.attributes[ attributeName ];
+ var normalized = attributeNormalizedMap[ attributeName ];
+
+ if ( normalized !== undefined ) attribute.normalized = normalized;
+
+ }
+
+ resolve( geometry );
+
+ }, threeAttributeMap, attributeTypeMap );
+
+ } );
+
+ } );
+
+ };
+
+ /**
+ * Texture Transform Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
+ */
+ function GLTFTextureTransformExtension() {
+
+ this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM;
+
+ }
+
+ GLTFTextureTransformExtension.prototype.extendTexture = function ( texture, transform ) {
+
+ texture = texture.clone();
+
+ if ( transform.offset !== undefined ) {
+
+ texture.offset.fromArray( transform.offset );
+
+ }
+
+ if ( transform.rotation !== undefined ) {
+
+ texture.rotation = transform.rotation;
+
+ }
+
+ if ( transform.scale !== undefined ) {
+
+ texture.repeat.fromArray( transform.scale );
+
+ }
+
+ if ( transform.texCoord !== undefined ) {
+
+ console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' );
+
+ }
+
+ texture.needsUpdate = true;
+
+ return texture;
+
+ };
+
+ /**
+ * Specular-Glossiness Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
+ */
+
+ /**
+ * A sub class of THREE.StandardMaterial with some of the functionality
+ * changed via the `onBeforeCompile` callback
+ * @pailhead
+ */
+
+ function GLTFMeshStandardSGMaterial( params ) {
+
+ THREE.MeshStandardMaterial.call( this );
+
+ this.isGLTFSpecularGlossinessMaterial = true;
+
+ //various chunks that need replacing
+ var specularMapParsFragmentChunk = [
+ '#ifdef USE_SPECULARMAP',
+ ' uniform sampler2D specularMap;',
+ '#endif'
+ ].join( '\n' );
+
+ var glossinessMapParsFragmentChunk = [
+ '#ifdef USE_GLOSSINESSMAP',
+ ' uniform sampler2D glossinessMap;',
+ '#endif'
+ ].join( '\n' );
+
+ var specularMapFragmentChunk = [
+ 'vec3 specularFactor = specular;',
+ '#ifdef USE_SPECULARMAP',
+ ' vec4 texelSpecular = texture2D( specularMap, vUv );',
+ ' texelSpecular = sRGBToLinear( texelSpecular );',
+ ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture',
+ ' specularFactor *= texelSpecular.rgb;',
+ '#endif'
+ ].join( '\n' );
+
+ var glossinessMapFragmentChunk = [
+ 'float glossinessFactor = glossiness;',
+ '#ifdef USE_GLOSSINESSMAP',
+ ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );',
+ ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture',
+ ' glossinessFactor *= texelGlossiness.a;',
+ '#endif'
+ ].join( '\n' );
+
+ var lightPhysicalFragmentChunk = [
+ 'PhysicalMaterial material;',
+ 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );',
+ 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );',
+ 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );',
+ 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.',
+ 'material.specularRoughness += geometryRoughness;',
+ 'material.specularRoughness = min( material.specularRoughness, 1.0 );',
+ 'material.specularColor = specularFactor;',
+ ].join( '\n' );
+
+ var uniforms = {
+ specular: { value: new THREE.Color().setHex( 0xffffff ) },
+ glossiness: { value: 1 },
+ specularMap: { value: null },
+ glossinessMap: { value: null }
+ };
+
+ this._extraUniforms = uniforms;
+
+ this.onBeforeCompile = function ( shader ) {
+
+ for ( var uniformName in uniforms ) {
+
+ shader.uniforms[ uniformName ] = uniforms[ uniformName ];
+
+ }
+
+ shader.fragmentShader = shader.fragmentShader
+ .replace( 'uniform float roughness;', 'uniform vec3 specular;' )
+ .replace( 'uniform float metalness;', 'uniform float glossiness;' )
+ .replace( '#include ', specularMapParsFragmentChunk )
+ .replace( '#include ', glossinessMapParsFragmentChunk )
+ .replace( '#include ', specularMapFragmentChunk )
+ .replace( '#include ', glossinessMapFragmentChunk )
+ .replace( '#include ', lightPhysicalFragmentChunk );
+
+ };
+
+ Object.defineProperties( this, {
+
+ specular: {
+ get: function () {
+
+ return uniforms.specular.value;
+
+ },
+ set: function ( v ) {
+
+ uniforms.specular.value = v;
+
+ }
+ },
+
+ specularMap: {
+ get: function () {
+
+ return uniforms.specularMap.value;
+
+ },
+ set: function ( v ) {
+
+ uniforms.specularMap.value = v;
+
+ if ( v ) {
+
+ this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps
+
+ } else {
+
+ delete this.defines.USE_SPECULARMAP;
+
+ }
+
+ }
+ },
+
+ glossiness: {
+ get: function () {
+
+ return uniforms.glossiness.value;
+
+ },
+ set: function ( v ) {
+
+ uniforms.glossiness.value = v;
+
+ }
+ },
+
+ glossinessMap: {
+ get: function () {
+
+ return uniforms.glossinessMap.value;
+
+ },
+ set: function ( v ) {
+
+ uniforms.glossinessMap.value = v;
+
+ if ( v ) {
+
+ this.defines.USE_GLOSSINESSMAP = '';
+ this.defines.USE_UV = '';
+
+ } else {
+
+ delete this.defines.USE_GLOSSINESSMAP;
+ delete this.defines.USE_UV;
+
+ }
+
+ }
+ }
+
+ } );
+
+ delete this.metalness;
+ delete this.roughness;
+ delete this.metalnessMap;
+ delete this.roughnessMap;
+
+ this.setValues( params );
+
+ }
+
+ GLTFMeshStandardSGMaterial.prototype = Object.create( THREE.MeshStandardMaterial.prototype );
+ GLTFMeshStandardSGMaterial.prototype.constructor = GLTFMeshStandardSGMaterial;
+
+ GLTFMeshStandardSGMaterial.prototype.copy = function ( source ) {
+
+ THREE.MeshStandardMaterial.prototype.copy.call( this, source );
+ this.specularMap = source.specularMap;
+ this.specular.copy( source.specular );
+ this.glossinessMap = source.glossinessMap;
+ this.glossiness = source.glossiness;
+ delete this.metalness;
+ delete this.roughness;
+ delete this.metalnessMap;
+ delete this.roughnessMap;
+ return this;
+
+ };
+
+ function GLTFMaterialsPbrSpecularGlossinessExtension() {
+
+ return {
+
+ name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS,
+
+ specularGlossinessParams: [
+ 'color',
+ 'map',
+ 'lightMap',
+ 'lightMapIntensity',
+ 'aoMap',
+ 'aoMapIntensity',
+ 'emissive',
+ 'emissiveIntensity',
+ 'emissiveMap',
+ 'bumpMap',
+ 'bumpScale',
+ 'normalMap',
+ 'normalMapType',
+ 'displacementMap',
+ 'displacementScale',
+ 'displacementBias',
+ 'specularMap',
+ 'specular',
+ 'glossinessMap',
+ 'glossiness',
+ 'alphaMap',
+ 'envMap',
+ 'envMapIntensity',
+ 'refractionRatio',
+ ],
+
+ getMaterialType: function () {
+
+ return GLTFMeshStandardSGMaterial;
+
+ },
+
+ extendParams: function ( materialParams, materialDef, parser ) {
+
+ var pbrSpecularGlossiness = materialDef.extensions[ this.name ];
+
+ materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 );
+ materialParams.opacity = 1.0;
+
+ var pending = [];
+
+ if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) {
+
+ var array = pbrSpecularGlossiness.diffuseFactor;
+
+ materialParams.color.fromArray( array );
+ materialParams.opacity = array[ 3 ];
+
+ }
+
+ if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) );
+
+ }
+
+ materialParams.emissive = new THREE.Color( 0.0, 0.0, 0.0 );
+ materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
+ materialParams.specular = new THREE.Color( 1.0, 1.0, 1.0 );
+
+ if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) {
+
+ materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor );
+
+ }
+
+ if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) {
+
+ var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture;
+ pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) );
+ pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) );
+
+ }
+
+ return Promise.all( pending );
+
+ },
+
+ createMaterial: function ( materialParams ) {
+
+ var material = new GLTFMeshStandardSGMaterial( materialParams );
+ material.fog = true;
+
+ material.color = materialParams.color;
+
+ material.map = materialParams.map === undefined ? null : materialParams.map;
+
+ material.lightMap = null;
+ material.lightMapIntensity = 1.0;
+
+ material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap;
+ material.aoMapIntensity = 1.0;
+
+ material.emissive = materialParams.emissive;
+ material.emissiveIntensity = 1.0;
+ material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap;
+
+ material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap;
+ material.bumpScale = 1;
+
+ material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap;
+ material.normalMapType = THREE.TangentSpaceNormalMap;
+
+ if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale;
+
+ material.displacementMap = null;
+ material.displacementScale = 1;
+ material.displacementBias = 0;
+
+ material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap;
+ material.specular = materialParams.specular;
+
+ material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap;
+ material.glossiness = materialParams.glossiness;
+
+ material.alphaMap = null;
+
+ material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap;
+ material.envMapIntensity = 1.0;
+
+ material.refractionRatio = 0.98;
+
+ return material;
+
+ },
+
+ };
+
+ }
+
+ /**
+ * Mesh Quantization Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization
+ */
+ function GLTFMeshQuantizationExtension() {
+
+ this.name = EXTENSIONS.KHR_MESH_QUANTIZATION;
+
+ }
+
+ /*********************************/
+ /********** INTERPOLATION ********/
+ /*********************************/
+
+ // Spline Interpolation
+ // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
+ function GLTFCubicSplineInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+
+ THREE.Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
+
+ }
+
+ GLTFCubicSplineInterpolant.prototype = Object.create( THREE.Interpolant.prototype );
+ GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant;
+
+ GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function ( index ) {
+
+ // Copies a sample value to the result buffer. See description of glTF
+ // CUBICSPLINE values layout in interpolate_() function below.
+
+ var result = this.resultBuffer,
+ values = this.sampleValues,
+ valueSize = this.valueSize,
+ offset = index * valueSize * 3 + valueSize;
+
+ for ( var i = 0; i !== valueSize; i ++ ) {
+
+ result[ i ] = values[ offset + i ];
+
+ }
+
+ return result;
+
+ };
+
+ GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
+
+ GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
+
+ GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) {
+
+ var result = this.resultBuffer;
+ var values = this.sampleValues;
+ var stride = this.valueSize;
+
+ var stride2 = stride * 2;
+ var stride3 = stride * 3;
+
+ var td = t1 - t0;
+
+ var p = ( t - t0 ) / td;
+ var pp = p * p;
+ var ppp = pp * p;
+
+ var offset1 = i1 * stride3;
+ var offset0 = offset1 - stride3;
+
+ var s2 = - 2 * ppp + 3 * pp;
+ var s3 = ppp - pp;
+ var s0 = 1 - s2;
+ var s1 = s3 - pp + p;
+
+ // Layout of keyframe output values for CUBICSPLINE animations:
+ // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
+ for ( var i = 0; i !== stride; i ++ ) {
+
+ var p0 = values[ offset0 + i + stride ]; // splineVertex_k
+ var m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k)
+ var p1 = values[ offset1 + i + stride ]; // splineVertex_k+1
+ var m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k)
+
+ result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1;
+
+ }
+
+ return result;
+
+ };
+
+ /*********************************/
+ /********** INTERNALS ************/
+ /*********************************/
+
+ /* CONSTANTS */
+
+ var WEBGL_CONSTANTS = {
+ FLOAT: 5126,
+ //FLOAT_MAT2: 35674,
+ FLOAT_MAT3: 35675,
+ FLOAT_MAT4: 35676,
+ FLOAT_VEC2: 35664,
+ FLOAT_VEC3: 35665,
+ FLOAT_VEC4: 35666,
+ LINEAR: 9729,
+ REPEAT: 10497,
+ SAMPLER_2D: 35678,
+ POINTS: 0,
+ LINES: 1,
+ LINE_LOOP: 2,
+ LINE_STRIP: 3,
+ TRIANGLES: 4,
+ TRIANGLE_STRIP: 5,
+ TRIANGLE_FAN: 6,
+ UNSIGNED_BYTE: 5121,
+ UNSIGNED_SHORT: 5123
+ };
+
+ var WEBGL_COMPONENT_TYPES = {
+ 5120: Int8Array,
+ 5121: Uint8Array,
+ 5122: Int16Array,
+ 5123: Uint16Array,
+ 5125: Uint32Array,
+ 5126: Float32Array
+ };
+
+ var WEBGL_FILTERS = {
+ 9728: THREE.NearestFilter,
+ 9729: THREE.LinearFilter,
+ 9984: THREE.NearestMipmapNearestFilter,
+ 9985: THREE.LinearMipmapNearestFilter,
+ 9986: THREE.NearestMipmapLinearFilter,
+ 9987: THREE.LinearMipmapLinearFilter
+ };
+
+ var WEBGL_WRAPPINGS = {
+ 33071: THREE.ClampToEdgeWrapping,
+ 33648: THREE.MirroredRepeatWrapping,
+ 10497: THREE.RepeatWrapping
+ };
+
+ var WEBGL_TYPE_SIZES = {
+ 'SCALAR': 1,
+ 'VEC2': 2,
+ 'VEC3': 3,
+ 'VEC4': 4,
+ 'MAT2': 4,
+ 'MAT3': 9,
+ 'MAT4': 16
+ };
+
+ var ATTRIBUTES = {
+ POSITION: 'position',
+ NORMAL: 'normal',
+ TANGENT: 'tangent',
+ TEXCOORD_0: 'uv',
+ TEXCOORD_1: 'uv2',
+ COLOR_0: 'color',
+ WEIGHTS_0: 'skinWeight',
+ JOINTS_0: 'skinIndex',
+ };
+
+ var PATH_PROPERTIES = {
+ scale: 'scale',
+ translation: 'position',
+ rotation: 'quaternion',
+ weights: 'morphTargetInfluences'
+ };
+
+ var INTERPOLATION = {
+ CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each
+ // keyframe track will be initialized with a default interpolation type, then modified.
+ LINEAR: THREE.InterpolateLinear,
+ STEP: THREE.InterpolateDiscrete
+ };
+
+ var ALPHA_MODES = {
+ OPAQUE: 'OPAQUE',
+ MASK: 'MASK',
+ BLEND: 'BLEND'
+ };
+
+ /* UTILITY FUNCTIONS */
+
+ function resolveURL( url, path ) {
+
+ // Invalid URL
+ if ( typeof url !== 'string' || url === '' ) return '';
+
+ // Host Relative URL
+ if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) {
+
+ path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' );
+
+ }
+
+ // Absolute URL http://,https://,//
+ if ( /^(https?:)?\/\//i.test( url ) ) return url;
+
+ // Data URI
+ if ( /^data:.*,.*$/i.test( url ) ) return url;
+
+ // Blob URL
+ if ( /^blob:.*$/i.test( url ) ) return url;
+
+ // Relative URL
+ return path + url;
+
+ }
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
+ */
+ function createDefaultMaterial( cache ) {
+
+ if ( cache[ 'DefaultMaterial' ] === undefined ) {
+
+ cache[ 'DefaultMaterial' ] = new THREE.MeshStandardMaterial( {
+ color: 0xFFFFFF,
+ emissive: 0x000000,
+ metalness: 1,
+ roughness: 1,
+ transparent: false,
+ depthTest: true,
+ side: THREE.FrontSide
+ } );
+
+ }
+
+ return cache[ 'DefaultMaterial' ];
+
+ }
+
+ function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) {
+
+ // Add unknown glTF extensions to an object's userData.
+
+ for ( var name in objectDef.extensions ) {
+
+ if ( knownExtensions[ name ] === undefined ) {
+
+ object.userData.gltfExtensions = object.userData.gltfExtensions || {};
+ object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ];
+
+ }
+
+ }
+
+ }
+
+ /**
+ * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry} object
+ * @param {GLTF.definition} gltfDef
+ */
+ function assignExtrasToUserData( object, gltfDef ) {
+
+ if ( gltfDef.extras !== undefined ) {
+
+ if ( typeof gltfDef.extras === 'object' ) {
+
+ Object.assign( object.userData, gltfDef.extras );
+
+ } else {
+
+ console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras );
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
+ *
+ * @param {THREE.BufferGeometry} geometry
+ * @param {Array} targets
+ * @param {GLTFParser} parser
+ * @return {Promise}
+ */
+ function addMorphTargets( geometry, targets, parser ) {
+
+ var hasMorphPosition = false;
+ var hasMorphNormal = false;
+
+ for ( var i = 0, il = targets.length; i < il; i ++ ) {
+
+ var target = targets[ i ];
+
+ if ( target.POSITION !== undefined ) hasMorphPosition = true;
+ if ( target.NORMAL !== undefined ) hasMorphNormal = true;
+
+ if ( hasMorphPosition && hasMorphNormal ) break;
+
+ }
+
+ if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry );
+
+ var pendingPositionAccessors = [];
+ var pendingNormalAccessors = [];
+
+ for ( var i = 0, il = targets.length; i < il; i ++ ) {
+
+ var target = targets[ i ];
+
+ if ( hasMorphPosition ) {
+
+ var pendingAccessor = target.POSITION !== undefined
+ ? parser.getDependency( 'accessor', target.POSITION )
+ : geometry.attributes.position;
+
+ pendingPositionAccessors.push( pendingAccessor );
+
+ }
+
+ if ( hasMorphNormal ) {
+
+ var pendingAccessor = target.NORMAL !== undefined
+ ? parser.getDependency( 'accessor', target.NORMAL )
+ : geometry.attributes.normal;
+
+ pendingNormalAccessors.push( pendingAccessor );
+
+ }
+
+ }
+
+ return Promise.all( [
+ Promise.all( pendingPositionAccessors ),
+ Promise.all( pendingNormalAccessors )
+ ] ).then( function ( accessors ) {
+
+ var morphPositions = accessors[ 0 ];
+ var morphNormals = accessors[ 1 ];
+
+ if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions;
+ if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals;
+ geometry.morphTargetsRelative = true;
+
+ return geometry;
+
+ } );
+
+ }
+
+ /**
+ * @param {THREE.Mesh} mesh
+ * @param {GLTF.Mesh} meshDef
+ */
+ function updateMorphTargets( mesh, meshDef ) {
+
+ mesh.updateMorphTargets();
+
+ if ( meshDef.weights !== undefined ) {
+
+ for ( var i = 0, il = meshDef.weights.length; i < il; i ++ ) {
+
+ mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ];
+
+ }
+
+ }
+
+ // .extras has user-defined data, so check that .extras.targetNames is an array.
+ if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) {
+
+ var targetNames = meshDef.extras.targetNames;
+
+ if ( mesh.morphTargetInfluences.length === targetNames.length ) {
+
+ mesh.morphTargetDictionary = {};
+
+ for ( var i = 0, il = targetNames.length; i < il; i ++ ) {
+
+ mesh.morphTargetDictionary[ targetNames[ i ] ] = i;
+
+ }
+
+ } else {
+
+ console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' );
+
+ }
+
+ }
+
+ }
+
+ function createPrimitiveKey( primitiveDef ) {
+
+ var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ];
+ var geometryKey;
+
+ if ( dracoExtension ) {
+
+ geometryKey = 'draco:' + dracoExtension.bufferView
+ + ':' + dracoExtension.indices
+ + ':' + createAttributesKey( dracoExtension.attributes );
+
+ } else {
+
+ geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode;
+
+ }
+
+ return geometryKey;
+
+ }
+
+ function createAttributesKey( attributes ) {
+
+ var attributesKey = '';
+
+ var keys = Object.keys( attributes ).sort();
+
+ for ( var i = 0, il = keys.length; i < il; i ++ ) {
+
+ attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';';
+
+ }
+
+ return attributesKey;
+
+ }
+
+ /* GLTF PARSER */
+
+ function GLTFParser( json, options ) {
+
+ this.json = json || {};
+ this.extensions = {};
+ this.plugins = {};
+ this.options = options || {};
+
+ // loader object cache
+ this.cache = new GLTFRegistry();
+
+ // associations between Three.js objects and glTF elements
+ this.associations = new Map();
+
+ // BufferGeometry caching
+ this.primitiveCache = {};
+
+ // Object3D instance caches
+ this.meshCache = { refs: {}, uses: {} };
+ this.cameraCache = { refs: {}, uses: {} };
+ this.lightCache = { refs: {}, uses: {} };
+
+ // Track node names, to ensure no duplicates
+ this.nodeNamesUsed = {};
+
+ // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the
+ // expensive work of uploading a texture to the GPU off the main thread.
+ if ( typeof createImageBitmap !== 'undefined' && /Firefox/.test( navigator.userAgent ) === false ) {
+
+ this.textureLoader = new THREE.ImageBitmapLoader( this.options.manager );
+
+ } else {
+
+ this.textureLoader = new THREE.TextureLoader( this.options.manager );
+
+ }
+
+ this.textureLoader.setCrossOrigin( this.options.crossOrigin );
+
+ this.fileLoader = new THREE.FileLoader( this.options.manager );
+ this.fileLoader.setResponseType( 'arraybuffer' );
+
+ if ( this.options.crossOrigin === 'use-credentials' ) {
+
+ this.fileLoader.setWithCredentials( true );
+
+ }
+
+ }
+
+ GLTFParser.prototype.setExtensions = function ( extensions ) {
+
+ this.extensions = extensions;
+
+ };
+
+ GLTFParser.prototype.setPlugins = function ( plugins ) {
+
+ this.plugins = plugins;
+
+ };
+
+ GLTFParser.prototype.parse = function ( onLoad, onError ) {
+
+ var parser = this;
+ var json = this.json;
+ var extensions = this.extensions;
+
+ // Clear the loader cache
+ this.cache.removeAll();
+
+ // Mark the special nodes/meshes in json for efficient parse
+ this._invokeAll( function ( ext ) {
+
+ return ext._markDefs && ext._markDefs();
+
+ } );
+
+ Promise.all( [
+
+ this.getDependencies( 'scene' ),
+ this.getDependencies( 'animation' ),
+ this.getDependencies( 'camera' ),
+
+ ] ).then( function ( dependencies ) {
+
+ var result = {
+ scene: dependencies[ 0 ][ json.scene || 0 ],
+ scenes: dependencies[ 0 ],
+ animations: dependencies[ 1 ],
+ cameras: dependencies[ 2 ],
+ asset: json.asset,
+ parser: parser,
+ userData: {}
+ };
+
+ addUnknownExtensionsToUserData( extensions, result, json );
+
+ assignExtrasToUserData( result, json );
+
+ onLoad( result );
+
+ } ).catch( onError );
+
+ };
+
+ /**
+ * Marks the special nodes/meshes in json for efficient parse.
+ */
+ GLTFParser.prototype._markDefs = function () {
+
+ var nodeDefs = this.json.nodes || [];
+ var skinDefs = this.json.skins || [];
+ var meshDefs = this.json.meshes || [];
+
+ // Nothing in the node definition indicates whether it is a Bone or an
+ // Object3D. Use the skins' joint references to mark bones.
+ for ( var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) {
+
+ var joints = skinDefs[ skinIndex ].joints;
+
+ for ( var i = 0, il = joints.length; i < il; i ++ ) {
+
+ nodeDefs[ joints[ i ] ].isBone = true;
+
+ }
+
+ }
+
+ // Iterate over all nodes, marking references to shared resources,
+ // as well as skeleton joints.
+ for ( var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) {
+
+ var nodeDef = nodeDefs[ nodeIndex ];
+
+ if ( nodeDef.mesh !== undefined ) {
+
+ this._addNodeRef( this.meshCache, nodeDef.mesh );
+
+ // Nothing in the mesh definition indicates whether it is
+ // a SkinnedMesh or Mesh. Use the node's mesh reference
+ // to mark SkinnedMesh if node has skin.
+ if ( nodeDef.skin !== undefined ) {
+
+ meshDefs[ nodeDef.mesh ].isSkinnedMesh = true;
+
+ }
+
+ }
+
+ if ( nodeDef.camera !== undefined ) {
+
+ this._addNodeRef( this.cameraCache, nodeDef.camera );
+
+ }
+
+ }
+
+ };
+
+ /**
+ * Counts references to shared node / Object3D resources. These resources
+ * can be reused, or "instantiated", at multiple nodes in the scene
+ * hierarchy. Mesh, Camera, and Light instances are instantiated and must
+ * be marked. Non-scenegraph resources (like Materials, Geometries, and
+ * Textures) can be reused directly and are not marked here.
+ *
+ * Example: CesiumMilkTruck sample model reuses "Wheel" meshes.
+ */
+ GLTFParser.prototype._addNodeRef = function ( cache, index ) {
+
+ if ( index === undefined ) return;
+
+ if ( cache.refs[ index ] === undefined ) {
+
+ cache.refs[ index ] = cache.uses[ index ] = 0;
+
+ }
+
+ cache.refs[ index ] ++;
+
+ };
+
+ /** Returns a reference to a shared resource, cloning it if necessary. */
+ GLTFParser.prototype._getNodeRef = function ( cache, index, object ) {
+
+ if ( cache.refs[ index ] <= 1 ) return object;
+
+ var ref = object.clone();
+
+ ref.name += '_instance_' + ( cache.uses[ index ] ++ );
+
+ return ref;
+
+ };
+
+ GLTFParser.prototype._invokeOne = function ( func ) {
+
+ var extensions = Object.values( this.plugins );
+ extensions.push( this );
+
+ for ( var i = 0; i < extensions.length; i ++ ) {
+
+ var result = func( extensions[ i ] );
+
+ if ( result ) return result;
+
+ }
+
+ };
+
+ GLTFParser.prototype._invokeAll = function ( func ) {
+
+ var extensions = Object.values( this.plugins );
+ extensions.unshift( this );
+
+ var pending = [];
+
+ for ( var i = 0; i < extensions.length; i ++ ) {
+
+ var result = func( extensions[ i ] );
+
+ if ( result ) pending.push( result );
+
+ }
+
+ return pending;
+
+ };
+
+ /**
+ * Requests the specified dependency asynchronously, with caching.
+ * @param {string} type
+ * @param {number} index
+ * @return {Promise}
+ */
+ GLTFParser.prototype.getDependency = function ( type, index ) {
+
+ var cacheKey = type + ':' + index;
+ var dependency = this.cache.get( cacheKey );
+
+ if ( ! dependency ) {
+
+ switch ( type ) {
+
+ case 'scene':
+ dependency = this.loadScene( index );
+ break;
+
+ case 'node':
+ dependency = this.loadNode( index );
+ break;
+
+ case 'mesh':
+ dependency = this._invokeOne( function ( ext ) {
+
+ return ext.loadMesh && ext.loadMesh( index );
+
+ } );
+ break;
+
+ case 'accessor':
+ dependency = this.loadAccessor( index );
+ break;
+
+ case 'bufferView':
+ dependency = this._invokeOne( function ( ext ) {
+
+ return ext.loadBufferView && ext.loadBufferView( index );
+
+ } );
+ break;
+
+ case 'buffer':
+ dependency = this.loadBuffer( index );
+ break;
+
+ case 'material':
+ dependency = this._invokeOne( function ( ext ) {
+
+ return ext.loadMaterial && ext.loadMaterial( index );
+
+ } );
+ break;
+
+ case 'texture':
+ dependency = this._invokeOne( function ( ext ) {
+
+ return ext.loadTexture && ext.loadTexture( index );
+
+ } );
+ break;
+
+ case 'skin':
+ dependency = this.loadSkin( index );
+ break;
+
+ case 'animation':
+ dependency = this.loadAnimation( index );
+ break;
+
+ case 'camera':
+ dependency = this.loadCamera( index );
+ break;
+
+ default:
+ throw new Error( 'Unknown type: ' + type );
+
+ }
+
+ this.cache.add( cacheKey, dependency );
+
+ }
+
+ return dependency;
+
+ };
+
+ /**
+ * Requests all dependencies of the specified type asynchronously, with caching.
+ * @param {string} type
+ * @return {Promise>}
+ */
+ GLTFParser.prototype.getDependencies = function ( type ) {
+
+ var dependencies = this.cache.get( type );
+
+ if ( ! dependencies ) {
+
+ var parser = this;
+ var defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || [];
+
+ dependencies = Promise.all( defs.map( function ( def, index ) {
+
+ return parser.getDependency( type, index );
+
+ } ) );
+
+ this.cache.add( type, dependencies );
+
+ }
+
+ return dependencies;
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
+ * @param {number} bufferIndex
+ * @return {Promise}
+ */
+ GLTFParser.prototype.loadBuffer = function ( bufferIndex ) {
+
+ var bufferDef = this.json.buffers[ bufferIndex ];
+ var loader = this.fileLoader;
+
+ if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) {
+
+ throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' );
+
+ }
+
+ // If present, GLB container is required to be the first buffer.
+ if ( bufferDef.uri === undefined && bufferIndex === 0 ) {
+
+ return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body );
+
+ }
+
+ var options = this.options;
+
+ return new Promise( function ( resolve, reject ) {
+
+ loader.load( resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () {
+
+ reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) );
+
+ } );
+
+ } );
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
+ * @param {number} bufferViewIndex
+ * @return {Promise}
+ */
+ GLTFParser.prototype.loadBufferView = function ( bufferViewIndex ) {
+
+ var bufferViewDef = this.json.bufferViews[ bufferViewIndex ];
+
+ return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) {
+
+ var byteLength = bufferViewDef.byteLength || 0;
+ var byteOffset = bufferViewDef.byteOffset || 0;
+ return buffer.slice( byteOffset, byteOffset + byteLength );
+
+ } );
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors
+ * @param {number} accessorIndex
+ * @return {Promise}
+ */
+ GLTFParser.prototype.loadAccessor = function ( accessorIndex ) {
+
+ var parser = this;
+ var json = this.json;
+
+ var accessorDef = this.json.accessors[ accessorIndex ];
+
+ if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) {
+
+ // Ignore empty accessors, which may be used to declare runtime
+ // information about attributes coming from another source (e.g. Draco
+ // compression extension).
+ return Promise.resolve( null );
+
+ }
+
+ var pendingBufferViews = [];
+
+ if ( accessorDef.bufferView !== undefined ) {
+
+ pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) );
+
+ } else {
+
+ pendingBufferViews.push( null );
+
+ }
+
+ if ( accessorDef.sparse !== undefined ) {
+
+ pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) );
+ pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) );
+
+ }
+
+ return Promise.all( pendingBufferViews ).then( function ( bufferViews ) {
+
+ var bufferView = bufferViews[ 0 ];
+
+ var itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ];
+ var TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
+
+ // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
+ var elementBytes = TypedArray.BYTES_PER_ELEMENT;
+ var itemBytes = elementBytes * itemSize;
+ var byteOffset = accessorDef.byteOffset || 0;
+ var byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined;
+ var normalized = accessorDef.normalized === true;
+ var array, bufferAttribute;
+
+ // The buffer is not interleaved if the stride is the item size in bytes.
+ if ( byteStride && byteStride !== itemBytes ) {
+
+ // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer
+ // This makes sure that IBA.count reflects accessor.count properly
+ var ibSlice = Math.floor( byteOffset / byteStride );
+ var ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count;
+ var ib = parser.cache.get( ibCacheKey );
+
+ if ( ! ib ) {
+
+ array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes );
+
+ // Integer parameters to IB/IBA are in array elements, not bytes.
+ ib = new THREE.InterleavedBuffer( array, byteStride / elementBytes );
+
+ parser.cache.add( ibCacheKey, ib );
+
+ }
+
+ bufferAttribute = new THREE.InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized );
+
+ } else {
+
+ if ( bufferView === null ) {
+
+ array = new TypedArray( accessorDef.count * itemSize );
+
+ } else {
+
+ array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize );
+
+ }
+
+ bufferAttribute = new THREE.BufferAttribute( array, itemSize, normalized );
+
+ }
+
+ // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
+ if ( accessorDef.sparse !== undefined ) {
+
+ var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR;
+ var TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ];
+
+ var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0;
+ var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0;
+
+ var sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices );
+ var sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize );
+
+ if ( bufferView !== null ) {
+
+ // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
+ bufferAttribute = new THREE.BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized );
+
+ }
+
+ for ( var i = 0, il = sparseIndices.length; i < il; i ++ ) {
+
+ var index = sparseIndices[ i ];
+
+ bufferAttribute.setX( index, sparseValues[ i * itemSize ] );
+ if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] );
+ if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] );
+ if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] );
+ if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' );
+
+ }
+
+ }
+
+ return bufferAttribute;
+
+ } );
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures
+ * @param {number} textureIndex
+ * @return {Promise}
+ */
+ GLTFParser.prototype.loadTexture = function ( textureIndex ) {
+
+ var parser = this;
+ var json = this.json;
+ var options = this.options;
+
+ var textureDef = json.textures[ textureIndex ];
+
+ var textureExtensions = textureDef.extensions || {};
+
+ var source;
+
+ if ( textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] ) {
+
+ source = json.images[ textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].source ];
+
+ } else {
+
+ source = json.images[ textureDef.source ];
+
+ }
+
+ var loader;
+
+ if ( source.uri ) {
+
+ loader = options.manager.getHandler( source.uri );
+
+ }
+
+ if ( ! loader ) {
+
+ loader = textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ]
+ ? parser.extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].ddsLoader
+ : this.textureLoader;
+
+ }
+
+ return this.loadTextureImage( textureIndex, source, loader );
+
+ };
+
+ GLTFParser.prototype.loadTextureImage = function ( textureIndex, source, loader ) {
+
+ var parser = this;
+ var json = this.json;
+ var options = this.options;
+
+ var textureDef = json.textures[ textureIndex ];
+
+ var URL = self.URL || self.webkitURL;
+
+ var sourceURI = source.uri;
+ var isObjectURL = false;
+ var hasAlpha = true;
+
+ if ( source.mimeType === 'image/jpeg' ) hasAlpha = false;
+
+ if ( source.bufferView !== undefined ) {
+
+ // Load binary image data from bufferView, if provided.
+
+ sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) {
+
+ if ( source.mimeType === 'image/png' ) {
+
+ // Inspect the PNG 'IHDR' chunk to determine whether the image could have an
+ // alpha channel. This check is conservative — the image could have an alpha
+ // channel with all values == 1, and the indexed type (colorType == 3) only
+ // sometimes contains alpha.
+ //
+ // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header
+ var colorType = new DataView( bufferView, 25, 1 ).getUint8( 0, false );
+ hasAlpha = colorType === 6 || colorType === 4 || colorType === 3;
+
+ }
+
+ isObjectURL = true;
+ var blob = new Blob( [ bufferView ], { type: source.mimeType } );
+ sourceURI = URL.createObjectURL( blob );
+ return sourceURI;
+
+ } );
+
+ }
+
+ return Promise.resolve( sourceURI ).then( function ( sourceURI ) {
+
+ return new Promise( function ( resolve, reject ) {
+
+ var onLoad = resolve;
+
+ if ( loader.isImageBitmapLoader === true ) {
+
+ onLoad = function ( imageBitmap ) {
+
+ resolve( new THREE.CanvasTexture( imageBitmap ) );
+
+ };
+
+ }
+
+ loader.load( resolveURL( sourceURI, options.path ), onLoad, undefined, reject );
+
+ } );
+
+ } ).then( function ( texture ) {
+
+ // Clean up resources and configure Texture.
+
+ if ( isObjectURL === true ) {
+
+ URL.revokeObjectURL( sourceURI );
+
+ }
+
+ texture.flipY = false;
+
+ if ( textureDef.name ) texture.name = textureDef.name;
+
+ // When there is definitely no alpha channel in the texture, set RGBFormat to save space.
+ if ( ! hasAlpha ) texture.format = THREE.RGBFormat;
+
+ var samplers = json.samplers || {};
+ var sampler = samplers[ textureDef.sampler ] || {};
+
+ texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter;
+ texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipmapLinearFilter;
+ texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping;
+ texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping;
+
+ parser.associations.set( texture, {
+ type: 'textures',
+ index: textureIndex
+ } );
+
+ return texture;
+
+ } );
+
+ };
+
+ /**
+ * Asynchronously assigns a texture to the given material parameters.
+ * @param {Object} materialParams
+ * @param {string} mapName
+ * @param {Object} mapDef
+ * @return {Promise}
+ */
+ GLTFParser.prototype.assignTexture = function ( materialParams, mapName, mapDef ) {
+
+ var parser = this;
+
+ return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) {
+
+ // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured
+ // However, we will copy UV set 0 to UV set 1 on demand for aoMap
+ if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) {
+
+ console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' );
+
+ }
+
+ if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) {
+
+ var transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined;
+
+ if ( transform ) {
+
+ var gltfReference = parser.associations.get( texture );
+ texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform );
+ parser.associations.set( texture, gltfReference );
+
+ }
+
+ }
+
+ materialParams[ mapName ] = texture;
+
+ } );
+
+ };
+
+ /**
+ * Assigns final material to a Mesh, Line, or Points instance. The instance
+ * already has a material (generated from the glTF material options alone)
+ * but reuse of the same glTF material may require multiple threejs materials
+ * to accomodate different primitive types, defines, etc. New materials will
+ * be created if necessary, and reused from a cache.
+ * @param {THREE.Object3D} mesh Mesh, Line, or Points instance.
+ */
+ GLTFParser.prototype.assignFinalMaterial = function ( mesh ) {
+
+ var geometry = mesh.geometry;
+ var material = mesh.material;
+
+ var useVertexTangents = geometry.attributes.tangent !== undefined;
+ var useVertexColors = geometry.attributes.color !== undefined;
+ var useFlatShading = geometry.attributes.normal === undefined;
+ var useSkinning = mesh.isSkinnedMesh === true;
+ var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0;
+ var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined;
+
+ if ( mesh.isPoints ) {
+
+ var cacheKey = 'PointsMaterial:' + material.uuid;
+
+ var pointsMaterial = this.cache.get( cacheKey );
+
+ if ( ! pointsMaterial ) {
+
+ pointsMaterial = new THREE.PointsMaterial();
+ THREE.Material.prototype.copy.call( pointsMaterial, material );
+ pointsMaterial.color.copy( material.color );
+ pointsMaterial.map = material.map;
+ pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px
+
+ this.cache.add( cacheKey, pointsMaterial );
+
+ }
+
+ material = pointsMaterial;
+
+ } else if ( mesh.isLine ) {
+
+ var cacheKey = 'LineBasicMaterial:' + material.uuid;
+
+ var lineMaterial = this.cache.get( cacheKey );
+
+ if ( ! lineMaterial ) {
+
+ lineMaterial = new THREE.LineBasicMaterial();
+ THREE.Material.prototype.copy.call( lineMaterial, material );
+ lineMaterial.color.copy( material.color );
+
+ this.cache.add( cacheKey, lineMaterial );
+
+ }
+
+ material = lineMaterial;
+
+ }
+
+ // Clone the material if it will be modified
+ if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
+
+ var cacheKey = 'ClonedMaterial:' + material.uuid + ':';
+
+ if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
+ if ( useSkinning ) cacheKey += 'skinning:';
+ if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
+ if ( useVertexColors ) cacheKey += 'vertex-colors:';
+ if ( useFlatShading ) cacheKey += 'flat-shading:';
+ if ( useMorphTargets ) cacheKey += 'morph-targets:';
+ if ( useMorphNormals ) cacheKey += 'morph-normals:';
+
+ var cachedMaterial = this.cache.get( cacheKey );
+
+ if ( ! cachedMaterial ) {
+
+ cachedMaterial = material.clone();
+
+ if ( useSkinning ) cachedMaterial.skinning = true;
+ if ( useVertexTangents ) cachedMaterial.vertexTangents = true;
+ if ( useVertexColors ) cachedMaterial.vertexColors = true;
+ if ( useFlatShading ) cachedMaterial.flatShading = true;
+ if ( useMorphTargets ) cachedMaterial.morphTargets = true;
+ if ( useMorphNormals ) cachedMaterial.morphNormals = true;
+
+ this.cache.add( cacheKey, cachedMaterial );
+
+ this.associations.set( cachedMaterial, this.associations.get( material ) );
+
+ }
+
+ material = cachedMaterial;
+
+ }
+
+ // workarounds for mesh and geometry
+
+ if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
+
+ geometry.setAttribute( 'uv2', geometry.attributes.uv );
+
+ }
+
+ // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
+ if ( material.normalScale && ! useVertexTangents ) {
+
+ material.normalScale.y = - material.normalScale.y;
+
+ }
+
+ if ( material.clearcoatNormalScale && ! useVertexTangents ) {
+
+ material.clearcoatNormalScale.y = - material.clearcoatNormalScale.y;
+
+ }
+
+ mesh.material = material;
+
+ };
+
+ GLTFParser.prototype.getMaterialType = function ( /* materialIndex */ ) {
+
+ return THREE.MeshStandardMaterial;
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
+ * @param {number} materialIndex
+ * @return {Promise}
+ */
+ GLTFParser.prototype.loadMaterial = function ( materialIndex ) {
+
+ var parser = this;
+ var json = this.json;
+ var extensions = this.extensions;
+ var materialDef = json.materials[ materialIndex ];
+
+ var materialType;
+ var materialParams = {};
+ var materialExtensions = materialDef.extensions || {};
+
+ var pending = [];
+
+ if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) {
+
+ var sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ];
+ materialType = sgExtension.getMaterialType();
+ pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) );
+
+ } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) {
+
+ var kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ];
+ materialType = kmuExtension.getMaterialType();
+ pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) );
+
+ } else {
+
+ // Specification:
+ // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
+
+ var metallicRoughness = materialDef.pbrMetallicRoughness || {};
+
+ materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 );
+ materialParams.opacity = 1.0;
+
+ if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
+
+ var array = metallicRoughness.baseColorFactor;
+
+ materialParams.color.fromArray( array );
+ materialParams.opacity = array[ 3 ];
+
+ }
+
+ if ( metallicRoughness.baseColorTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) );
+
+ }
+
+ materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0;
+ materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0;
+
+ if ( metallicRoughness.metallicRoughnessTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) );
+ pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) );
+
+ }
+
+ materialType = this._invokeOne( function ( ext ) {
+
+ return ext.getMaterialType && ext.getMaterialType( materialIndex );
+
+ } );
+
+ pending.push( Promise.all( this._invokeAll( function ( ext ) {
+
+ return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams );
+
+ } ) ) );
+
+ }
+
+ if ( materialDef.doubleSided === true ) {
+
+ materialParams.side = THREE.DoubleSide;
+
+ }
+
+ var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE;
+
+ if ( alphaMode === ALPHA_MODES.BLEND ) {
+
+ materialParams.transparent = true;
+
+ // See: https://github.com/mrdoob/three.js/issues/17706
+ materialParams.depthWrite = false;
+
+ } else {
+
+ materialParams.transparent = false;
+
+ if ( alphaMode === ALPHA_MODES.MASK ) {
+
+ materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5;
+
+ }
+
+ }
+
+ if ( materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
+
+ pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );
+
+ materialParams.normalScale = new THREE.Vector2( 1, 1 );
+
+ if ( materialDef.normalTexture.scale !== undefined ) {
+
+ materialParams.normalScale.set( materialDef.normalTexture.scale, materialDef.normalTexture.scale );
+
+ }
+
+ }
+
+ if ( materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
+
+ pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) );
+
+ if ( materialDef.occlusionTexture.strength !== undefined ) {
+
+ materialParams.aoMapIntensity = materialDef.occlusionTexture.strength;
+
+ }
+
+ }
+
+ if ( materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial ) {
+
+ materialParams.emissive = new THREE.Color().fromArray( materialDef.emissiveFactor );
+
+ }
+
+ if ( materialDef.emissiveTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
+
+ pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) );
+
+ }
+
+ return Promise.all( pending ).then( function () {
+
+ var material;
+
+ if ( materialType === GLTFMeshStandardSGMaterial ) {
+
+ material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams );
+
+ } else {
+
+ material = new materialType( materialParams );
+
+ }
+
+ if ( materialDef.name ) material.name = materialDef.name;
+
+ // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding.
+ if ( material.map ) material.map.encoding = THREE.sRGBEncoding;
+ if ( material.emissiveMap ) material.emissiveMap.encoding = THREE.sRGBEncoding;
+
+ assignExtrasToUserData( material, materialDef );
+
+ parser.associations.set( material, { type: 'materials', index: materialIndex } );
+
+ if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef );
+
+ return material;
+
+ } );
+
+ };
+
+ /** When Object3D instances are targeted by animation, they need unique names. */
+ GLTFParser.prototype.createUniqueName = function ( originalName ) {
+
+ var sanitizedName = THREE.PropertyBinding.sanitizeNodeName( originalName || '' );
+
+ var name = sanitizedName;
+
+ for ( var i = 1; this.nodeNamesUsed[ name ]; ++ i ) {
+
+ name = sanitizedName + '_' + i;
+
+ }
+
+ this.nodeNamesUsed[ name ] = true;
+
+ return name;
+
+ };
+
+ /**
+ * @param {THREE.BufferGeometry} geometry
+ * @param {GLTF.Primitive} primitiveDef
+ * @param {GLTFParser} parser
+ */
+ function computeBounds( geometry, primitiveDef, parser ) {
+
+ var attributes = primitiveDef.attributes;
+
+ var box = new THREE.Box3();
+
+ if ( attributes.POSITION !== undefined ) {
+
+ var accessor = parser.json.accessors[ attributes.POSITION ];
+
+ var min = accessor.min;
+ var max = accessor.max;
+
+ // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
+
+ if ( min !== undefined && max !== undefined ) {
+
+ box.set(
+ new THREE.Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ),
+ new THREE.Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) );
+
+ } else {
+
+ console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' );
+
+ return;
+
+ }
+
+ } else {
+
+ return;
+
+ }
+
+ var targets = primitiveDef.targets;
+
+ if ( targets !== undefined ) {
+
+ var maxDisplacement = new THREE.Vector3();
+ var vector = new THREE.Vector3();
+
+ for ( var i = 0, il = targets.length; i < il; i ++ ) {
+
+ var target = targets[ i ];
+
+ if ( target.POSITION !== undefined ) {
+
+ var accessor = parser.json.accessors[ target.POSITION ];
+ var min = accessor.min;
+ var max = accessor.max;
+
+ // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
+
+ if ( min !== undefined && max !== undefined ) {
+
+ // we need to get max of absolute components because target weight is [-1,1]
+ vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) );
+ vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) );
+ vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) );
+
+ // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative
+ // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets
+ // are used to implement key-frame animations and as such only two are active at a time - this results in very large
+ // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size.
+ maxDisplacement.max( vector );
+
+ } else {
+
+ console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' );
+
+ }
+
+ }
+
+ }
+
+ // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets.
+ box.expandByVector( maxDisplacement );
+
+ }
+
+ geometry.boundingBox = box;
+
+ var sphere = new THREE.Sphere();
+
+ box.getCenter( sphere.center );
+ sphere.radius = box.min.distanceTo( box.max ) / 2;
+
+ geometry.boundingSphere = sphere;
+
+ }
+
+ /**
+ * @param {THREE.BufferGeometry} geometry
+ * @param {GLTF.Primitive} primitiveDef
+ * @param {GLTFParser} parser
+ * @return {Promise}
+ */
+ function addPrimitiveAttributes( geometry, primitiveDef, parser ) {
+
+ var attributes = primitiveDef.attributes;
+
+ var pending = [];
+
+ function assignAttributeAccessor( accessorIndex, attributeName ) {
+
+ return parser.getDependency( 'accessor', accessorIndex )
+ .then( function ( accessor ) {
+
+ geometry.setAttribute( attributeName, accessor );
+
+ } );
+
+ }
+
+ for ( var gltfAttributeName in attributes ) {
+
+ var threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase();
+
+ // Skip attributes already provided by e.g. Draco extension.
+ if ( threeAttributeName in geometry.attributes ) continue;
+
+ pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) );
+
+ }
+
+ if ( primitiveDef.indices !== undefined && ! geometry.index ) {
+
+ var accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) {
+
+ geometry.setIndex( accessor );
+
+ } );
+
+ pending.push( accessor );
+
+ }
+
+ assignExtrasToUserData( geometry, primitiveDef );
+
+ computeBounds( geometry, primitiveDef, parser );
+
+ return Promise.all( pending ).then( function () {
+
+ return primitiveDef.targets !== undefined
+ ? addMorphTargets( geometry, primitiveDef.targets, parser )
+ : geometry;
+
+ } );
+
+ }
+
+ /**
+ * @param {THREE.BufferGeometry} geometry
+ * @param {Number} drawMode
+ * @return {THREE.BufferGeometry}
+ */
+ function toTrianglesDrawMode( geometry, drawMode ) {
+
+ var index = geometry.getIndex();
+
+ // generate index if not present
+
+ if ( index === null ) {
+
+ var indices = [];
+
+ var position = geometry.getAttribute( 'position' );
+
+ if ( position !== undefined ) {
+
+ for ( var i = 0; i < position.count; i ++ ) {
+
+ indices.push( i );
+
+ }
+
+ geometry.setIndex( indices );
+ index = geometry.getIndex();
+
+ } else {
+
+ console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
+ return geometry;
+
+ }
+
+ }
+
+ //
+
+ var numberOfTriangles = index.count - 2;
+ var newIndices = [];
+
+ if ( drawMode === THREE.TriangleFanDrawMode ) {
+
+ // gl.TRIANGLE_FAN
+
+ for ( var i = 1; i <= numberOfTriangles; i ++ ) {
+
+ newIndices.push( index.getX( 0 ) );
+ newIndices.push( index.getX( i ) );
+ newIndices.push( index.getX( i + 1 ) );
+
+ }
+
+ } else {
+
+ // gl.TRIANGLE_STRIP
+
+ for ( var i = 0; i < numberOfTriangles; i ++ ) {
+
+ if ( i % 2 === 0 ) {
+
+ newIndices.push( index.getX( i ) );
+ newIndices.push( index.getX( i + 1 ) );
+ newIndices.push( index.getX( i + 2 ) );
+
+
+ } else {
+
+ newIndices.push( index.getX( i + 2 ) );
+ newIndices.push( index.getX( i + 1 ) );
+ newIndices.push( index.getX( i ) );
+
+ }
+
+ }
+
+ }
+
+ if ( ( newIndices.length / 3 ) !== numberOfTriangles ) {
+
+ console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' );
+
+ }
+
+ // build final geometry
+
+ var newGeometry = geometry.clone();
+ newGeometry.setIndex( newIndices );
+
+ return newGeometry;
+
+ }
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
+ *
+ * Creates BufferGeometries from primitives.
+ *
+ * @param {Array} primitives
+ * @return {Promise>}
+ */
+ GLTFParser.prototype.loadGeometries = function ( primitives ) {
+
+ var parser = this;
+ var extensions = this.extensions;
+ var cache = this.primitiveCache;
+
+ function createDracoPrimitive( primitive ) {
+
+ return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]
+ .decodePrimitive( primitive, parser )
+ .then( function ( geometry ) {
+
+ return addPrimitiveAttributes( geometry, primitive, parser );
+
+ } );
+
+ }
+
+ var pending = [];
+
+ for ( var i = 0, il = primitives.length; i < il; i ++ ) {
+
+ var primitive = primitives[ i ];
+ var cacheKey = createPrimitiveKey( primitive );
+
+ // See if we've already created this geometry
+ var cached = cache[ cacheKey ];
+
+ if ( cached ) {
+
+ // Use the cached geometry if it exists
+ pending.push( cached.promise );
+
+ } else {
+
+ var geometryPromise;
+
+ if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) {
+
+ // Use DRACO geometry if available
+ geometryPromise = createDracoPrimitive( primitive );
+
+ } else {
+
+ // Otherwise create a new geometry
+ geometryPromise = addPrimitiveAttributes( new THREE.BufferGeometry(), primitive, parser );
+
+ }
+
+ // Cache this geometry
+ cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise };
+
+ pending.push( geometryPromise );
+
+ }
+
+ }
+
+ return Promise.all( pending );
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes
+ * @param {number} meshIndex
+ * @return {Promise}
+ */
+ GLTFParser.prototype.loadMesh = function ( meshIndex ) {
+
+ var parser = this;
+ var json = this.json;
+ var extensions = this.extensions;
+
+ var meshDef = json.meshes[ meshIndex ];
+ var primitives = meshDef.primitives;
+
+ var pending = [];
+
+ for ( var i = 0, il = primitives.length; i < il; i ++ ) {
+
+ var material = primitives[ i ].material === undefined
+ ? createDefaultMaterial( this.cache )
+ : this.getDependency( 'material', primitives[ i ].material );
+
+ pending.push( material );
+
+ }
+
+ pending.push( parser.loadGeometries( primitives ) );
+
+ return Promise.all( pending ).then( function ( results ) {
+
+ var materials = results.slice( 0, results.length - 1 );
+ var geometries = results[ results.length - 1 ];
+
+ var meshes = [];
+
+ for ( var i = 0, il = geometries.length; i < il; i ++ ) {
+
+ var geometry = geometries[ i ];
+ var primitive = primitives[ i ];
+
+ // 1. create Mesh
+
+ var mesh;
+
+ var material = materials[ i ];
+
+ if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
+ primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
+ primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
+ primitive.mode === undefined ) {
+
+ // .isSkinnedMesh isn't in glTF spec. See ._markDefs()
+ mesh = meshDef.isSkinnedMesh === true
+ ? new THREE.SkinnedMesh( geometry, material )
+ : new THREE.Mesh( geometry, material );
+
+ if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) {
+
+ // we normalize floating point skin weight array to fix malformed assets (see #15319)
+ // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
+ mesh.normalizeSkinWeights();
+
+ }
+
+ if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
+
+ mesh.geometry = toTrianglesDrawMode( mesh.geometry, THREE.TriangleStripDrawMode );
+
+ } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
+
+ mesh.geometry = toTrianglesDrawMode( mesh.geometry, THREE.TriangleFanDrawMode );
+
+ }
+
+ } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
+
+ mesh = new THREE.LineSegments( geometry, material );
+
+ } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
+
+ mesh = new THREE.Line( geometry, material );
+
+ } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
+
+ mesh = new THREE.LineLoop( geometry, material );
+
+ } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
+
+ mesh = new THREE.Points( geometry, material );
+
+ } else {
+
+ throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
+
+ }
+
+ if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) {
+
+ updateMorphTargets( mesh, meshDef );
+
+ }
+
+ mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) );
+
+ assignExtrasToUserData( mesh, meshDef );
+
+ if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive );
+
+ parser.assignFinalMaterial( mesh );
+
+ meshes.push( mesh );
+
+ }
+
+ if ( meshes.length === 1 ) {
+
+ return meshes[ 0 ];
+
+ }
+
+ var group = new THREE.Group();
+
+ for ( var i = 0, il = meshes.length; i < il; i ++ ) {
+
+ group.add( meshes[ i ] );
+
+ }
+
+ return group;
+
+ } );
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
+ * @param {number} cameraIndex
+ * @return {Promise}
+ */
+ GLTFParser.prototype.loadCamera = function ( cameraIndex ) {
+
+ var camera;
+ var cameraDef = this.json.cameras[ cameraIndex ];
+ var params = cameraDef[ cameraDef.type ];
+
+ if ( ! params ) {
+
+ console.warn( 'THREE.GLTFLoader: Missing camera parameters.' );
+ return;
+
+ }
+
+ if ( cameraDef.type === 'perspective' ) {
+
+ camera = new THREE.PerspectiveCamera( THREE.MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 );
+
+ } else if ( cameraDef.type === 'orthographic' ) {
+
+ camera = new THREE.OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar );
+
+ }
+
+ if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name );
+
+ assignExtrasToUserData( camera, cameraDef );
+
+ return Promise.resolve( camera );
+
+ };
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins
+ * @param {number} skinIndex
+ * @return {Promise