From f694bb330b79b0b10288c2d45bab5a02214fe5ff Mon Sep 17 00:00:00 2001
From: Kalle Olavi Niemitalo <kon@iki.fi>
Date: Mon, 1 Mar 2010 02:01:31 +0200
Subject: [PATCHv3 4/6] Add RandomPosition property of unit types

Make it possible to slightly randomize the positions of individual
trees in forests.  The amount of randomness is controlled separately
for each unit type, and it defaults to zero.

When a unit dies, preserve CUnit::IX and CUnit::IY for the corpse.
---
 doc/scripts/unittype.html       |   10 ++++++++++
 engine/include/unit.h           |   20 ++++++++++++++++----
 engine/include/unittype.h       |   12 ++++++++++++
 engine/unit/script_unittype.cpp |   20 ++++++++++++++++++++
 engine/unit/unit.cpp            |    7 +++++--
 engine/unit/unittype.cpp        |    1 +
 6 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/doc/scripts/unittype.html b/doc/scripts/unittype.html
index 88d700f..90563a9 100644
--- a/doc/scripts/unittype.html
+++ b/doc/scripts/unittype.html
@@ -233,6 +233,16 @@ If the name is too long, it is split at space.
 <dt>"offset", {x, y}</dt>
 <dd>Defines the offset of the graphics in pixels used to display the shadow of the unit-type. Note that this is relative to the unit graphics including its own offset</dd>
 </dl></dd>
+<dt>RandomPosition = {xmin, ymin, xmax, ymax}</dt>
+<dd>When a unit of this type is created, move it by a random number of pixels.
+For example, if ymin is -3 and ymax is +4, then the unit can appear at most
+three pixels above or at most four pixels below its standard position.
+This is primarily intended to make forests look more natural.
+You must always have xmin &lt;= xmax and ymin &lt;= ymax.
+Typically, xmin = -xmax and ymin = -ymax, but that is not required.
+The default is {0, 0, 0, 0}, i.e. no randomness in any direction.
+You should leave this zero for types of units that can move.
+</dd>
 <dt>DrawLevel = number</dt>
 <dd>This is used when sorting units and missiles for drawing. Units with a higher draw
 order will always be on top of units with lower draw order. Units are also sorted from
diff --git a/engine/include/unit.h b/engine/include/unit.h
index 113466c..75239f6 100644
--- a/engine/include/unit.h
+++ b/engine/include/unit.h
@@ -162,10 +162,22 @@
 **  CUnit::IX CUnit::IY
 **
 **  Coordinate displacement in pixels or coordinates inside a tile.
-**  Currently only !=0, if the unit is moving from one tile to
-**  an adjacent one.  CUnit::IX is always between -::TileSizeX and
-**  +::TileSizeX, and counts towards 0 during horizontal motion.
-**  CUnit::IY is similar.
+**  Can be !=0 for two reasons:
+**
+**  (1) if the unit is moving from one tile to an adjacent one.
+**      CUnit::IX is then between -::TileSizeX and +::TileSizeX,
+**      and counts towards 0 during horizontal motion.
+**      CUnit::IY is similar.
+**
+**  (2) if the unit is immobile (e.g. a tree) and has been placed
+**      at a somewhat random position to make it look more natural.
+**      See CUnit::Init(CUnitType *) and CUnitType::RandomIXMin.
+**      The map editor does not save these random positions; they
+**      get assigned when the map is loaded.  Saved games however
+**      do include them.
+**
+**  The movement code is unaware of the latter use of IX and IY,
+**  so each unit type should pick just one of these schemes.
 **
 **  CUnit::Frame
 **
diff --git a/engine/include/unittype.h b/engine/include/unittype.h
index c24114c..43ecebf 100644
--- a/engine/include/unittype.h
+++ b/engine/include/unittype.h
@@ -86,6 +86,14 @@
 **
 **    Vertical offset to draw the shadow in pixels.
 **
+**  CUnitType::RandomIXMin CUnitType::RandomIYMin
+**  CUnitType::RandomIXMax CUnitType::RandomIYMax
+**
+**    When the unit is first created, assign random values in these
+**    ranges to CUnit::IX and CUnit::IY.  This is intended to make
+**    forests look more natural.  Moving unit types should have all
+**    of these zero.  
+**
 **  CUnitType::Animations
 **
 **    Animation scripts for the different actions. Currently the
@@ -597,6 +605,10 @@ public:
 	int ShadowHeight;               /// Shadow sprite height
 	int ShadowOffsetX;              /// Shadow horizontal offset
 	int ShadowOffsetY;              /// Shadow vertical offset
+	int RandomIXMin;                /// Minimum random CUnit::IX
+	int RandomIYMin;                /// Minimum random CUnit::IY
+	int RandomIXMax;                /// Maximum random CUnit::IX
+	int RandomIYMax;                /// Maximum random CUnit::IY
 
 	CAnimations *Animations;        /// Animation scripts
 	int StillFrame;                 /// Still frame
diff --git a/engine/unit/script_unittype.cpp b/engine/unit/script_unittype.cpp
index ccb0a5b..629029b 100644
--- a/engine/unit/script_unittype.cpp
+++ b/engine/unit/script_unittype.cpp
@@ -332,6 +332,26 @@ static int CclDefineUnitType(lua_State *l)
 			lua_rawgeti(l, -1, 2);
 			type->OffsetY = LuaToNumber(l, -1);
 			lua_pop(l, 1);
+		} else if (!strcmp(value, "RandomPosition")) {
+			if (!lua_istable(l, -1) || lua_objlen(l, -1) != 4) {
+				LuaError(l, "incorrect argument");
+			}
+			lua_rawgeti(l, -1, 1);
+			type->RandomIXMin = LuaToNumber(l, -1);
+			lua_pop(l, 1);
+			lua_rawgeti(l, -1, 2);
+			type->RandomIYMin = LuaToNumber(l, -1);
+			lua_pop(l, 1);
+			lua_rawgeti(l, -1, 3);
+			type->RandomIXMax = LuaToNumber(l, -1);
+			lua_pop(l, 1);
+			lua_rawgeti(l, -1, 4);
+			type->RandomIYMax = LuaToNumber(l, -1);
+			lua_pop(l, 1);
+			if (type->RandomIXMin > type->RandomIXMax
+			    || type->RandomIYMin > type->RandomIYMax) {
+				LuaError(l, "RandomPosition has min > max");
+			}
 		} else if (!strcmp(value, "Flip")) {
 			type->Flip = LuaToBoolean(l, -1);
 		} else if (!strcmp(value, "Animations")) {
diff --git a/engine/unit/unit.cpp b/engine/unit/unit.cpp
index 91d69f7..56bc860 100644
--- a/engine/unit/unit.cpp
+++ b/engine/unit/unit.cpp
@@ -210,6 +210,9 @@ void CUnit::Init(CUnitType *type)
 			UnitTypeVar.NumberVariable * sizeof(*Variable));
 	}
 
+	IX = type->RandomIXMin + SyncRand(type->RandomIXMax - type->RandomIXMin + 1);
+	IY = type->RandomIYMin + SyncRand(type->RandomIYMax - type->RandomIYMin + 1);
+
 	// Set a heading for the unit if it Handles Directions
 	// Don't set a building heading, as only 1 construction direction
 	//   is allowed.
@@ -2002,8 +2005,8 @@ void LetUnitDie(CUnit *unit)
 	unit->State = 0;
 	unit->Orders[0]->Action = UnitActionDie;
 	if (type->CorpseType) {
-		unit->IX = (type->CorpseType->Width - type->CorpseType->Sprite->Width) / 2;
-		unit->IY = (type->CorpseType->Height - type->CorpseType->Sprite->Height) / 2;
+		unit->IX += (type->CorpseType->Width - type->CorpseType->Sprite->Width) / 2;
+		unit->IY += (type->CorpseType->Height - type->CorpseType->Sprite->Height) / 2;
 
 		unit->CurrentSightRange = type->CorpseType->Stats[unit->Player->Index].Variables[SIGHTRANGE_INDEX].Max;
 	} else {
diff --git a/engine/unit/unittype.cpp b/engine/unit/unittype.cpp
index 94abc0d..5320f95 100644
--- a/engine/unit/unittype.cpp
+++ b/engine/unit/unittype.cpp
@@ -74,6 +74,7 @@ std::string DefaultResourceNames[MaxCosts];
 CUnitType::CUnitType() :
 	Slot(0), Width(0), Height(0), OffsetX(0), OffsetY(0), DrawLevel(0),
 	ShadowWidth(0), ShadowHeight(0), ShadowOffsetX(0), ShadowOffsetY(0),
+	RandomIXMin(0), RandomIYMin(0), RandomIXMax(0), RandomIYMax(0),
 	Animations(NULL), StillFrame(0),
 	DeathExplosion(NULL), CorpseType(NULL),
 	Construction(NULL),  RepairHP(0), TileWidth(0), TileHeight(0),
-- 
1.6.6.1

