summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lang/english.txt1
-rw-r--r--src/misc.cpp2
-rw-r--r--src/object_base.h44
-rw-r--r--src/object_cmd.cpp64
-rw-r--r--src/object_map.h59
-rw-r--r--src/object_type.h4
-rw-r--r--src/saveload/afterload.cpp42
-rw-r--r--src/saveload/object_sl.cpp57
-rw-r--r--src/saveload/saveload.cpp2
-rw-r--r--src/town_cmd.cpp33
10 files changed, 236 insertions, 72 deletions
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 61094ee21..8be19adcc 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -3630,6 +3630,7 @@ STR_ERROR_TUNNEL_THROUGH_MAP_BORDER :{WHITE}Tunnel w
STR_ERROR_UNABLE_TO_EXCAVATE_LAND :{WHITE}Unable to excavate land for other end of tunnel
# Object related errors
+STR_ERROR_TOO_MANY_OBJECTS :{WHITE}... too many objects
STR_ERROR_CAN_T_BUILD_OBJECT :{WHITE}Can't build object...
STR_ERROR_OBJECT_IN_THE_WAY :{WHITE}Object in the way
STR_ERROR_COMPANY_HEADQUARTERS_IN :{WHITE}... company headquarters in the way
diff --git a/src/misc.cpp b/src/misc.cpp
index 34d46fbca..b0f2dd35d 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -42,6 +42,7 @@ void InitializeRoadGui();
void InitializeAirportGui();
void InitializeDockGui();
void InitializeIndustries();
+void InitializeObjects();
void InitializeTowns();
void InitializeSubsidies();
void InitializeTrees();
@@ -100,6 +101,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
InitializeRoadStops();
InitializeCargoPackets();
InitializeIndustries();
+ InitializeObjects();
InitializeBuildingCounts();
InitializeNPF();
diff --git a/src/object_base.h b/src/object_base.h
new file mode 100644
index 000000000..2aaf8aa0c
--- /dev/null
+++ b/src/object_base.h
@@ -0,0 +1,44 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD 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 General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file object_base.h Base for all objects. */
+
+#ifndef OBJECT_BASE_H
+#define OBJECT_BASE_H
+
+#include "core/pool_type.hpp"
+#include "object_type.h"
+#include "tilearea_type.h"
+#include "town_type.h"
+#include "date_type.h"
+
+typedef Pool<Object, ObjectID, 64, 64000> ObjectPool;
+extern ObjectPool _object_pool;
+
+/** An object, such as transmitter, on the map. */
+struct Object : ObjectPool::PoolItem<&_object_pool> {
+ Town *town; ///< Town the object is built in
+ TileArea location; ///< Location of the object
+ Date build_date; ///< Date of construction
+
+ /** Make sure the object isn't zeroed. */
+ Object() {}
+
+ /**
+ * Get the object associated with a tile.
+ * @param tile The tile to fetch the object for.
+ * @return The object.
+ */
+ static Object *GetByTile(TileIndex tile);
+};
+
+#define FOR_ALL_OBJECTS_FROM(var, start) FOR_ALL_ITEMS_FROM(Object, object_index, var, start)
+#define FOR_ALL_OBJECTS(var) FOR_ALL_OBJECTS_FROM(var, 0)
+
+#endif /* OBJECT_BASE_H */
diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp
index 3b15a95e0..1db6ed1f9 100644
--- a/src/object_cmd.cpp
+++ b/src/object_cmd.cpp
@@ -29,12 +29,29 @@
#include "cargopacket.h"
#include "sprite.h"
#include "core/random_func.hpp"
+#include "core/pool_func.hpp"
#include "object_map.h"
+#include "object_base.h"
+#include "date_func.h"
#include "table/strings.h"
#include "table/sprites.h"
#include "table/object_land.h"
+ObjectPool _object_pool("Object");
+INSTANTIATE_POOL_METHODS(Object)
+
+/* static */ Object *Object::GetByTile(TileIndex tile)
+{
+ return Object::Get(GetObjectIndex(tile));
+}
+
+/** Initialize/reset the objects. */
+void InitializeObjects()
+{
+ _object_pool.CleanPool();
+}
+
/* static */ const ObjectSpec *ObjectSpec::Get(ObjectType index)
{
assert(index < OBJECT_MAX);
@@ -51,24 +68,26 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town)
const ObjectSpec *spec = ObjectSpec::Get(type);
TileArea ta(tile, GB(spec->size, 0, 4), GB(spec->size, 4, 4));
+ Object *o = new Object();
+ o->location = ta;
+ o->town = town == NULL ? CalcClosestTownFromTile(tile) : town;
+ o->build_date = _date;
+
+ assert(o->town != NULL);
+
TILE_AREA_LOOP(t, ta) {
- TileIndex offset = t - tile;
- MakeObject(t, type, owner, TileY(offset) << 4 | TileX(offset), town == NULL ? 0 : town->index, WATER_CLASS_INVALID);
+ MakeObject(t, type, owner, o->index, WATER_CLASS_INVALID);
MarkTileDirtyByTile(t);
}
}
/**
* Increase the animation stage of a whole structure.
- * @param northern The northern tile of the structure.
- * @pre GetObjectOffset(northern) == 0
+ * @param tile The tile of the structure.
*/
-void IncreaseAnimationStage(TileIndex northern)
+static void IncreaseAnimationStage(TileIndex tile)
{
- assert(GetObjectOffset(northern) == 0);
- const ObjectSpec *spec = ObjectSpec::GetByTile(northern);
-
- TileArea ta(northern, GB(spec->size, 0, 4), GB(spec->size, 4, 4));
+ TileArea ta = Object::GetByTile(tile)->location;
TILE_AREA_LOOP(t, ta) {
SetObjectAnimationStage(t, GetObjectAnimationStage(t) + 1);
MarkTileDirtyByTile(t);
@@ -119,6 +138,9 @@ CommandCost CmdBuildObject(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (spec->flags & OBJECT_FLAG_ONLY_IN_SCENEDIT && (_game_mode != GM_EDITOR || _current_company != OWNER_NONE)) return CMD_ERROR;
if (spec->flags & OBJECT_FLAG_ONLY_IN_GAME && (_game_mode != GM_NORMAL || _current_company > MAX_COMPANIES)) return CMD_ERROR;
+ if (!Object::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_OBJECTS);
+ if (Town::GetNumItems() == 0) return_cmd_error(STR_ERROR_MUST_FOUND_TOWN_FIRST);
+
int size_x = GB(spec->size, 0, 4);
int size_y = GB(spec->size, 4, 4);
TileArea ta(tile, size_x, size_y);
@@ -195,8 +217,8 @@ static void DrawTile_Object(TileInfo *ti)
PaletteID palette = to == OWNER_NONE ? PAL_NONE : COMPANY_SPRITE_COLOUR(to);
if (type == OBJECT_HQ) {
- uint8 offset = GetObjectOffset(ti->tile);
- dts = &_object_hq[GetCompanyHQSize(ti->tile) << 2 | GB(offset, 4, 1) << 1 | GB(offset, 0, 1)];
+ TileIndex diff = ti->tile - Object::GetByTile(ti->tile)->location.tile;
+ dts = &_object_hq[GetCompanyHQSize(ti->tile) << 2 | TileY(diff) << 1 | TileX(diff)];
} else {
dts = &_objects[type];
}
@@ -254,8 +276,8 @@ static CommandCost ClearTile_Object(TileIndex tile, DoCommandFlag flags)
const ObjectSpec *spec = ObjectSpec::Get(type);
/* Get to the northern most tile. */
- byte tile_offset = GetObjectOffset(tile);
- tile -= TileXY(GB(tile_offset, 0, 4), GB(tile_offset, 4, 4));
+ Object *o = Object::GetByTile(tile);
+ TileArea ta = o->location;
/* Water can remove everything! */
if (_current_company != OWNER_WATER) {
@@ -276,11 +298,7 @@ static CommandCost ClearTile_Object(TileIndex tile, DoCommandFlag flags)
}
}
- int size_x = GB(spec->size, 0, 4);
- int size_y = GB(spec->size, 4, 4);
- TileArea ta(tile, size_x, size_y);
-
- CommandCost cost(EXPENSES_CONSTRUCTION, spec->GetClearCost() * size_x * size_y);
+ CommandCost cost(EXPENSES_CONSTRUCTION, spec->GetClearCost() * ta.w * ta.h);
if (spec->flags & OBJECT_FLAG_CLEAR_INCOME) cost.MultiplyCost(-1); // They get an income!
switch (type) {
@@ -299,9 +317,9 @@ static CommandCost ClearTile_Object(TileIndex tile, DoCommandFlag flags)
case OBJECT_STATUE:
if (flags & DC_EXEC) {
- Town *t = Town::Get(GetStatueTownID(tile));
- ClrBit(t->statues, GetTileOwner(tile));
- SetWindowDirty(WC_TOWN_AUTHORITY, t->index);
+ Town *town = o->town;
+ ClrBit(town->statues, GetTileOwner(tile));
+ SetWindowDirty(WC_TOWN_AUTHORITY, town->index);
}
break;
@@ -311,6 +329,7 @@ static CommandCost ClearTile_Object(TileIndex tile, DoCommandFlag flags)
if (flags & DC_EXEC) {
TILE_AREA_LOOP(tile_cur, ta) DoClearSquare(tile_cur);
+ delete o;
}
return cost;
@@ -344,6 +363,7 @@ static void GetTileDesc_Object(TileIndex tile, TileDesc *td)
{
td->str = ObjectSpec::GetByTile(tile)->name;
td->owner[0] = GetTileOwner(tile);
+ td->build_date = Object::GetByTile(tile)->build_date;
}
static void TileLoop_Object(TileIndex tile)
@@ -492,7 +512,7 @@ static void ChangeTileOwner_Object(TileIndex tile, Owner old_owner, Owner new_ow
if (IsOwnedLand(tile) && new_owner != INVALID_OWNER) {
SetTileOwner(tile, new_owner);
} else if (IsStatueTile(tile)) {
- Town *t = Town::Get(GetStatueTownID(tile));
+ Town *t = Object::GetByTile(tile)->town;
ClrBit(t->statues, old_owner);
if (new_owner != INVALID_OWNER && !HasBit(t->statues, new_owner)) {
/* Transfer ownership to the new company */
diff --git a/src/object_map.h b/src/object_map.h
index 0f4de9915..d26279f88 100644
--- a/src/object_map.h
+++ b/src/object_map.h
@@ -29,6 +29,18 @@ static inline ObjectType GetObjectType(TileIndex t)
}
/**
+ * Get the index of which object this tile is attached to.
+ * @param t the tile
+ * @pre IsTileType(t, MP_OBJECT)
+ * @return The ObjectID of the object.
+ */
+static inline ObjectID GetObjectIndex(TileIndex t)
+{
+ assert(IsTileType(t, MP_OBJECT));
+ return _m[t].m2;
+}
+
+/**
* Does the given tile have a transmitter?
* @param t the tile to inspect.
* @return true if and only if the tile has a transmitter.
@@ -95,18 +107,6 @@ static inline bool IsStatueTile(TileIndex t)
}
/**
- * Get the town of the given statue tile.
- * @param t the tile of the statue.
- * @pre IsStatueTile(t)
- * @return the town the given statue is in.
- */
-static inline TownID GetStatueTownID(TileIndex t)
-{
- assert(IsStatueTile(t));
- return _m[t].m2;
-}
-
-/**
* Get animation stage/counter of this tile.
* @param t The tile to query.
* @pre IsTileType(t, MP_OBJECT)
@@ -115,7 +115,7 @@ static inline TownID GetStatueTownID(TileIndex t)
static inline byte GetObjectAnimationStage(TileIndex t)
{
assert(IsTileType(t, MP_OBJECT));
- return GB(_m[t].m6, 2, 4);
+ return _m[t].m3;
}
/**
@@ -127,31 +127,7 @@ static inline byte GetObjectAnimationStage(TileIndex t)
static inline void SetObjectAnimationStage(TileIndex t, uint8 stage)
{
assert(IsTileType(t, MP_OBJECT));
- SB(_m[t].m6, 2, 4, stage);
-}
-
-/**
- * Get offset to the northern most tile.
- * @param t The tile to get the offset from.
- * @return The offset to the northern most tile of this structure.
- * @pre IsTileType(t, MP_OBJECT)
- */
-static inline byte GetObjectOffset(TileIndex t)
-{
- assert(IsTileType(t, MP_OBJECT));
- return _m[t].m3;
-}
-
-/**
- * Set offset to the northern most tile.
- * @param t The tile to set the offset of.
- * @param offset The offset to the northern most tile of this structure.
- * @pre IsTileType(t, MP_OBJECT)
- */
-static inline void SetObjectOffset(TileIndex t, uint8 offset)
-{
- assert(IsTileType(t, MP_OBJECT));
- _m[t].m3 = offset;
+ _m[t].m3 = stage;
}
@@ -161,17 +137,16 @@ static inline void SetObjectOffset(TileIndex t, uint8 offset)
* @param t The tile to make and object tile.
* @param u The object type of the tile.
* @param o The new owner of the tile.
- * @param offset The offset to the northern tile of this object.
- * @param index Generic index associated with the object type.
+ * @param index Index to the object.
* @param wc Water class for this obect.
*/
-static inline void MakeObject(TileIndex t, ObjectType u, Owner o, uint8 offset, uint index, WaterClass wc)
+static inline void MakeObject(TileIndex t, ObjectType u, Owner o, ObjectID index, WaterClass wc)
{
SetTileType(t, MP_OBJECT);
SetTileOwner(t, o);
SetWaterClass(t, wc);
_m[t].m2 = index;
- _m[t].m3 = offset;
+ _m[t].m3 = 0;
_m[t].m4 = 0;
_m[t].m5 = u;
SB(_m[t].m6, 2, 4, 0);
diff --git a/src/object_type.h b/src/object_type.h
index 3a801960d..d074878b2 100644
--- a/src/object_type.h
+++ b/src/object_type.h
@@ -22,6 +22,10 @@ enum ObjectType {
OBJECT_MAX,
};
+/** Unique identifier for an object. */
+typedef uint16 ObjectID;
+
+struct Object;
struct ObjectSpec;
#endif /* OBJECT_TYPE_H */
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index 12cbf2e3e..1a2d07cca 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -37,6 +37,7 @@
#include "../signs_func.h"
#include "../aircraft.h"
#include "../object_map.h"
+#include "../object_base.h"
#include "../tree_map.h"
#include "../company_func.h"
#include "../road_cmd.h"
@@ -1838,8 +1839,8 @@ bool AfterLoadGame()
/* Reordering/generalisation of the object bits. */
ObjectType type = GetObjectType(t);
- SetObjectAnimationStage(t, type == OBJECT_HQ ? GB(_m[t].m3, 2, 3) : 0);
- SetObjectOffset(t, type == OBJECT_HQ ? GB(_m[t].m3, 1, 1) | GB(_m[t].m3, 0, 1) << 4 : 0);
+ SB(_m[t].m6, 2, 4, type == OBJECT_HQ ? GB(_m[t].m3, 2, 3) : 0);
+ _m[t].m3 = type == OBJECT_HQ ? GB(_m[t].m3, 1, 1) | GB(_m[t].m3, 0, 1) << 4 : 0;
/* Make sure those bits are clear as well! */
_m[t].m4 = 0;
@@ -1847,6 +1848,43 @@ bool AfterLoadGame()
}
}
+ if (CheckSavegameVersion(147) && Object::GetNumItems() == 0) {
+ /* Make real objects for object tiles. */
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (!IsTileType(t, MP_OBJECT)) continue;
+
+ if (Town::GetNumItems() == 0) {
+ /* No towns, so remove all objects! */
+ DoClearSquare(t);
+ } else {
+ uint offset = _m[t].m3;
+
+ /* Also move the animation state. */
+ _m[t].m3 = GB(_m[t].m6, 2, 4);
+ SB(_m[t].m6, 2, 4, 0);
+
+ if (offset == 0) {
+ /* No offset, so make the object. */
+ ObjectType type = GetObjectType(t);
+ int size = type == OBJECT_HQ ? 2 : 1;
+
+ Object *o = new Object();
+ o->location.tile = t;
+ o->location.w = size;
+ o->location.h = size;
+ o->build_date = _date;
+ o->town = type == OBJECT_STATUE ? Town::Get(_m[t].m2) : CalcClosestTownFromTile(t, UINT_MAX);
+ _m[t].m2 = o->index;
+ } else {
+ /* We're at an offset, so get the ID from our "root". */
+ TileIndex northern_tile = t - TileXY(GB(offset, 0, 4), GB(offset, 4, 4));
+ assert(IsTileType(northern_tile, MP_OBJECT));
+ _m[t].m2 = _m[northern_tile].m2;
+ }
+ }
+ }
+ }
+
if (CheckSavegameVersion(113)) {
/* allow_town_roads is added, set it if town_layout wasn't TL_NO_ROADS */
if (_settings_game.economy.town_layout == 0) { // was TL_NO_ROADS
diff --git a/src/saveload/object_sl.cpp b/src/saveload/object_sl.cpp
new file mode 100644
index 000000000..db1ab1e41
--- /dev/null
+++ b/src/saveload/object_sl.cpp
@@ -0,0 +1,57 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD 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 General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file object_sl.cpp Code handling saving and loading of objects */
+
+#include "../stdafx.h"
+#include "../object_base.h"
+
+#include "saveload.h"
+
+static const SaveLoad _object_desc[] = {
+ SLE_VAR(Object, location.tile, SLE_UINT32),
+ SLE_VAR(Object, location.w, SLE_FILE_U8 | SLE_VAR_U16),
+ SLE_VAR(Object, location.h, SLE_FILE_U8 | SLE_VAR_U16),
+ SLE_REF(Object, town, REF_TOWN),
+ SLE_VAR(Object, build_date, SLE_UINT32),
+
+ SLE_END()
+};
+
+static void Save_OBJS()
+{
+ Object *o;
+
+ /* Write the objects */
+ FOR_ALL_OBJECTS(o) {
+ SlSetArrayIndex(o->index);
+ SlObject(o, _object_desc);
+ }
+}
+
+static void Load_OBJS()
+{
+ int index;
+ while ((index = SlIterateArray()) != -1) {
+ Object *o = new (index) Object();
+ SlObject(o, _object_desc);
+ }
+}
+
+static void Ptrs_OBJS()
+{
+ Object *o;
+ FOR_ALL_OBJECTS(o) {
+ SlObject(o, _object_desc);
+ }
+}
+
+extern const ChunkHandler _object_chunk_handlers[] = {
+ { 'OBJS', Save_OBJS, Load_OBJS, Ptrs_OBJS, NULL, CH_ARRAY | CH_LAST},
+};
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index 7a9cd9eee..935ebe61d 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -296,6 +296,7 @@ extern const ChunkHandler _cargopacket_chunk_handlers[];
extern const ChunkHandler _autoreplace_chunk_handlers[];
extern const ChunkHandler _labelmaps_chunk_handlers[];
extern const ChunkHandler _airport_chunk_handlers[];
+extern const ChunkHandler _object_chunk_handlers[];
static const ChunkHandler * const _chunk_handlers[] = {
_gamelog_chunk_handlers,
@@ -324,6 +325,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
_autoreplace_chunk_handlers,
_labelmaps_chunk_handlers,
_airport_chunk_handlers,
+ _object_chunk_handlers,
NULL,
};
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index 85e797215..20b922ccf 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -48,6 +48,7 @@
#include "core/backup_type.hpp"
#include "depot_base.h"
#include "object_map.h"
+#include "object_base.h"
#include "table/strings.h"
#include "table/town_land.h"
@@ -71,9 +72,13 @@ Town::~Town()
DeleteWindowById(WC_TOWN_VIEW, this->index);
/* Check no industry is related to us. */
- Industry *i;
+ const Industry *i;
FOR_ALL_INDUSTRIES(i) assert(i->town != this);
+ /* ... and no object is related to us. */
+ const Object *o;
+ FOR_ALL_OBJECTS(o) assert(o->town != this);
+
/* Check no tile is related to us. */
for (TileIndex tile = 0; tile < MapSize(); ++tile) {
switch (GetTileType(tile)) {
@@ -89,10 +94,6 @@ Town::~Town()
assert(!IsTileOwner(tile, OWNER_TOWN) || ClosestTownFromTile(tile, UINT_MAX) != this);
break;
- case MP_OBJECT:
- assert(GetObjectType(tile) != OBJECT_STATUE || GetStatueTownID(tile) != this->index);
- break;
-
default:
break;
}
@@ -114,6 +115,12 @@ void Town::PostDestructor(size_t index)
{
InvalidateWindowData(WC_TOWN_DIRECTORY, 0, 0);
UpdateNearestTownForRoadTiles(false);
+
+ /* Give objects a new home! */
+ Object *o;
+ FOR_ALL_OBJECTS(o) {
+ if (o->town == NULL) o->town = CalcClosestTownFromTile(o->location.tile, UINT_MAX);
+ }
}
/**
@@ -2403,7 +2410,21 @@ CommandCost CmdDeleteTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
break;
case MP_OBJECT:
- try_clear = GetObjectType(tile) == OBJECT_STATUE && GetStatueTownID(tile) == t->index;
+ if (Town::GetNumItems() == 1) {
+ /* No towns will be left, remove it! */
+ try_clear = true;
+ } else {
+ Object *o = Object::GetByTile(tile);
+ if (o->town == t) {
+ if (GetObjectType(tile) == OBJECT_STATUE) {
+ /* Statue... always remove. */
+ try_clear = true;
+ } else {
+ /* Tell to find a new town. */
+ o->town = NULL;
+ }
+ }
+ }
break;
default: