diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lang/english.txt | 1 | ||||
-rw-r--r-- | src/misc.cpp | 2 | ||||
-rw-r--r-- | src/object_base.h | 44 | ||||
-rw-r--r-- | src/object_cmd.cpp | 64 | ||||
-rw-r--r-- | src/object_map.h | 59 | ||||
-rw-r--r-- | src/object_type.h | 4 | ||||
-rw-r--r-- | src/saveload/afterload.cpp | 42 | ||||
-rw-r--r-- | src/saveload/object_sl.cpp | 57 | ||||
-rw-r--r-- | src/saveload/saveload.cpp | 2 | ||||
-rw-r--r-- | src/town_cmd.cpp | 33 |
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: |