From 852bde0bad87b0aab2f8e70688989c76a4c34480 Mon Sep 17 00:00:00 2001 From: rubidium Date: Sat, 28 Aug 2010 18:49:39 +0000 Subject: [PATCH] (svn r20660) -Codechange: implement (most) of action2 support for objects --- src/newgrf_object.cpp | 398 ++++++++++++++++++++++++++++++++++++++- src/newgrf_object.h | 7 + src/newgrf_spritegroup.h | 4 + src/object_type.h | 2 + 4 files changed, 410 insertions(+), 1 deletion(-) diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index dc1ac84f86..02e33bc20c 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -10,13 +10,23 @@ /** @file newgrf_object.cpp Handling of object NewGRFs. */ #include "stdafx.h" +#include "company_base.h" +#include "company_func.h" #include "core/mem_func.hpp" #include "date_func.h" +#include "debug.h" +#include "landscape.h" #include "newgrf.h" #include "newgrf_class_func.h" #include "newgrf_object.h" +#include "newgrf_spritegroup.h" +#include "newgrf_town.h" +#include "object_base.h" #include "object_map.h" -#include "openttd.h" +#include "sprite.h" +#include "town.h" +#include "viewport_func.h" +#include "water.h" /** The override manager for our objects. */ ObjectOverrideManager _object_mngr(NEW_OBJECT_OFFSET, NUM_OBJECTS, INVALID_OBJECT_TYPE); @@ -79,3 +89,389 @@ template } INSTANTIATE_NEWGRF_CLASS_METHODS(ObjectClass, ObjectSpec, ObjectClassID, OBJECT_CLASS_MAX) + + +static uint32 ObjectGetRandomBits(const ResolverObject *object) +{ + return IsTileType(object->u.object.tile, MP_OBJECT) ? GetObjectRandomBits(object->u.object.tile) : 0; +} + +static uint32 ObjectGetTriggers(const ResolverObject *object) +{ + return 0; +} + +static void ObjectSetTriggers(const ResolverObject *object, int triggers) +{ +} + + +/** + * Make an analysis of a tile and check for its belonging to the same + * object, and/or the same grf file + * @param tile TileIndex of the tile to query + * @param index Object to which to compare the tile to + * @param cur_grfid GRFID of the current callback chain + * @return value encoded as per NFO specs + */ +static uint32 GetObjectIDAtOffset(TileIndex tile, ObjectID oid, uint32 cur_grfid) +{ + if (!IsTileType(tile, MP_OBJECT) || GetObjectIndex(tile) != oid) { + /* No object and/or the tile does not have the same object as the one we match it with */ + return 0xFFFF; + } + + const ObjectSpec *spec = ObjectSpec::GetByTile(tile); + + if (spec->grf_prop.grffile->grfid == cur_grfid) { // same object, same grf ? + return spec->grf_prop.local_id; + } + + return 0xFFFE; // Defined in another grf file +} + +/** + * Based on newhouses equivalent, but adapted for newobjects + * @param parameter from callback. It's in fact a pair of coordinates + * @param tile TileIndex from which the callback was initiated + * @param index of the object been queried for + * @return a construction of bits obeying the newgrf format + */ +static uint32 GetNearbyObjectTileInformation(byte parameter, TileIndex tile, ObjectID index) +{ + if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required + bool is_same_object = (IsTileType(tile, MP_OBJECT) && GetObjectIndex(tile) == index); + + return GetNearbyTileInformation(tile) | (is_same_object ? 1 : 0) << 8; +} + +/** + * Get the object's animation counter data. + * @param tile The tile to query. + * @return The object's data. + */ +static uint32 GetObjectAnimationCounter(TileIndex tile) +{ + return Object::GetByTile(tile)->colour << 8 | GetAnimationFrame(tile); +} + +/** + * Get the closest object of a given type. + * @param tile The tile to start searching from. + * @param type The type of the object to search for. + * @param current The current object (to ignore). + * @return The distance to the closest object. + */ +static uint32 GetClosestObject(TileIndex tile, ObjectType type, const Object *current) +{ + uint32 best_dist = UINT32_MAX; + const Object *o; + FOR_ALL_OBJECTS(o) { + if (GetObjectType(o->location.tile) != type || o == current) continue; + + best_dist = min(best_dist, DistanceManhattan(tile, o->location.tile)); + } + + return best_dist; +} + +/** + * Implementation of var 65 + * @param local_id Parameter given to the callback, which is the set id, or the local id, in our terminology. + * @param grfid The object's GRFID. + * @param tile The tile to look from. + * @param current Object for which the inquiry is made + * @return The formatted answer to the callback : rr(reserved) cc(count) dddd(manhattan distance of closest sister) + */ +static uint32 GetCountAndDistanceOfClosestInstance(byte local_id, uint32 grfid, TileIndex tile, const Object *current) +{ + uint32 grf_id = GetRegister(0x100); // Get the GRFID of the definition to look for in register 100h + uint32 idx; + + /* Determine what will be the object type to look for */ + switch (grf_id) { + case 0: // this is a default object type + idx = local_id; + break; + + case 0xFFFFFFFF: // current grf + grf_id = grfid; + /* FALL THROUGH */ + + default: // use the grfid specified in register 100h + idx = _object_mngr.GetID(local_id, grf_id); + break; + } + + /* If the object type is invalid, there is none and the closest is far away. */ + if (idx >= NUM_OBJECTS) return 0 | 0xFFFF; + + return Object::GetTypeCount(idx) << 16 | min(GetClosestObject(tile, idx, current), 0xFFFF); +} + +/** Used by the resolver to get values for feature 0F deterministic spritegroups. */ +static uint32 ObjectGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) +{ + const Object *o = object->u.object.o; + TileIndex tile = object->u.object.tile; + + if (object->scope == VSG_SCOPE_PARENT) { + /* Pass the request on to the town of the object */ + return TownGetVariable(variable, parameter, available, (o == NULL) ? ClosestTownFromTile(tile, UINT_MAX) : o->town); + } + + /* We get the town from the object, or we calculate the closest + * town if we need to when there's no object. */ + const Town *t = NULL; + + if (o == NULL) { + switch (variable) { + /* Allow these when there's no object. */ + case 0x41: + case 0x60: + case 0x61: + case 0x62: + case 0x64: + case 0x65: + break; + + /* Allow these, but find the closest town. */ + case 0x45: + case 0x46: + t = ClosestTownFromTile(tile, UINT_MAX); + break; + + /* Construction date */ + case 0x42: return _date; + + /* Object founder information */ + case 0x44: return _current_company; + + /* + * Disallow the rest: + * 0x40: Relative position is passed as parameter during construction. + * 0x43: Animation counter is only for actual tiles. + * 0x63: Animation counter of nearby tile, see above. + */ + default: + DEBUG(grf, 1, "Unhandled object property 0x%X", variable); + + *available = false; + return UINT_MAX; + } + } else { + t = o->town; + } + + switch (variable) { + /* Relative position. */ + case 0x40: { + uint offset = tile - o->location.tile; + uint offset_x = TileX(offset); + uint offset_y = TileY(offset); + return offset_y << 20 | offset_x << 16 | offset_y << 8 | offset_x; + } + + /* Tile information. */ + case 0x41: return GetTileSlope(tile, NULL) << 8 | GetTerrainType(tile); + + /* Construction date */ + case 0x42: return o->build_date; + + /* Animation counter */ + case 0x43: return GetObjectAnimationCounter(tile); + + /* Object founder information */ + case 0x44: return GetTileOwner(tile); + + /* Get town zone and Manhattan distance of closest town */ + case 0x45: return GetTownRadiusGroup(t, tile) << 16 | min(DistanceManhattan(tile, t->xy), 0xFFFF); + + /* Get square of Euclidian distance of closes town */ + case 0x46: return GetTownRadiusGroup(t, tile) << 16 | min(DistanceSquare(tile, t->xy), 0xFFFF); + + /* Get object ID at offset param */ + case 0x60: return GetObjectIDAtOffset(GetNearbyTile(parameter, tile), o == NULL ? INVALID_OBJECT : o->index, object->grffile->grfid); + + /* Get random tile bits at offset param */ + case 0x61: + tile = GetNearbyTile(parameter, tile); + return (IsTileType(tile, MP_OBJECT) && Object::GetByTile(tile) == o) ? GetObjectRandomBits(tile) : 0; + + /* Land info of nearby tiles */ + case 0x62: return GetNearbyObjectTileInformation(parameter, tile, o == NULL ? INVALID_OBJECT : o->index); + + /* Animation counter of nearby tile */ + case 0x63: + tile = GetNearbyTile(parameter, tile); + return (IsTileType(tile, MP_OBJECT) && Object::GetByTile(tile) == o) ? GetObjectAnimationCounter(tile) : 0; + + /* Distance of nearest object of given type */ + case 0x64: return GetClosestObject(tile, GetObjectType(tile), o); + + /* Count of object, distance of closest instance */ + case 0x65: return GetCountAndDistanceOfClosestInstance(parameter, object->grffile->grfid, tile, o); + } + + DEBUG(grf, 1, "Unhandled object property 0x%X", variable); + + *available = false; + return UINT_MAX; +} + +static const SpriteGroup *ObjectResolveReal(const ResolverObject *object, const RealSpriteGroup *group) +{ + /* Objects do not have 'real' groups */ + return NULL; +} + +/** + * Get the object's sprite group. + * @param spec The specification to get the sprite group from. + * @param o The object to get he sprite group for. + * @return The resolved sprite group. + */ +static const SpriteGroup *GetObjectSpriteGroup(const ObjectSpec *spec, const Object *o) +{ + const SpriteGroup *group = NULL; + + if (o == NULL) group = spec->grf_prop.spritegroup[CT_PURCHASE_OBJECT]; + if (group != NULL) return group; + + /* Fall back to the default set if the selected cargo type is not defined */ + return spec->grf_prop.spritegroup[0]; + +} + +/** + * Returns a resolver object to be used with feature 0F spritegroups. + */ +static void NewObjectResolver(ResolverObject *res, const ObjectSpec *spec, const Object *o, TileIndex tile) +{ + res->GetRandomBits = ObjectGetRandomBits; + res->GetTriggers = ObjectGetTriggers; + res->SetTriggers = ObjectSetTriggers; + res->GetVariable = ObjectGetVariable; + res->ResolveReal = ObjectResolveReal; + + res->u.object.o = o; + res->u.object.tile = tile; + + res->callback = CBID_NO_CALLBACK; + res->callback_param1 = 0; + res->callback_param2 = 0; + res->last_value = 0; + res->trigger = 0; + res->reseed = 0; + res->count = 0; + + res->grffile = spec->grf_prop.grffile; +} + +/** + * Perform a callback for an object. + * @param callback The callback to perform. + * @param param1 The first parameter to pass to the NewGRF. + * @param param2 The second parameter to pass to the NewGRF. + * @param spec The specification of the object / the entry point. + * @param o The object to call the callback for. + * @param tile The tile the callback is called for. + * @return The result of the callback. + */ +uint16 GetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, const ObjectSpec *spec, const Object *o, TileIndex tile) +{ + ResolverObject object; + NewObjectResolver(&object, spec, o, tile); + object.callback = callback; + object.callback_param1 = param1; + object.callback_param2 = param2; + + const SpriteGroup *group = SpriteGroup::Resolve(GetObjectSpriteGroup(spec, o), &object); + if (group == NULL) return CALLBACK_FAILED; + + return group->GetCallbackResult(); +} + +/** + * Draw an group of sprites on the map. + * @param ti Information about the tile to draw on. + * @param group The group of sprites to draw. + * @param spec Object spec to draw. + */ +static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, const ObjectSpec *spec) +{ + const DrawTileSprites *dts = group->dts; + PaletteID palette = ((spec->flags & OBJECT_FLAG_2CC_COLOUR) ? SPR_2CCMAP_BASE : PALETTE_RECOLOUR_START) + Object::GetByTile(ti->tile)->colour; + + SpriteID image = dts->ground.sprite; + PaletteID pal = dts->ground.pal; + + if (GB(image, 0, SPRITE_WIDTH) != 0) { + /* If the ground sprite is the default flat water sprite, draw also canal/river borders + * Do not do this if the tile's WaterClass is 'land'. */ + if ((image == SPR_FLAT_WATER_TILE || spec->flags & OBJECT_FLAG_DRAW_WATER) && IsTileOnWater(ti->tile)) { + DrawWaterClassGround(ti); + } else { + DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette)); + } + } + + DrawNewGRFTileSeq(ti, dts, TO_STRUCTURES, 0, palette); +} + +/** + * Draw an object on the map. + * @param ti Information about the tile to draw on. + * @param spec Object spec to draw. + */ +void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec) +{ + ResolverObject object; + const Object *o = Object::GetByTile(ti->tile); + NewObjectResolver(&object, spec, o, ti->tile); + + const SpriteGroup *group = SpriteGroup::Resolve(GetObjectSpriteGroup(spec, o), &object); + if (group == NULL || group->type != SGT_TILELAYOUT) return; + + DrawTileLayout(ti, (const TileLayoutSpriteGroup *)group, spec); +} + +/** + * Draw representation of an object (tile) for GUI purposes. + * @param x Position x of image. + * @param y Position y of image. + * @param spec Object spec to draw. + */ +void DrawNewObjectTileInGUI(int x, int y, const ObjectSpec *spec) +{ + ResolverObject object; + NewObjectResolver(&object, spec, NULL, INVALID_TILE); + + const SpriteGroup *group = SpriteGroup::Resolve(GetObjectSpriteGroup(spec, NULL), &object); + if (group == NULL || group->type != SGT_TILELAYOUT) return; + + const DrawTileSprites *dts = ((const TileLayoutSpriteGroup *)group)->dts; + + PaletteID palette; + if (Company::IsValidID(_local_company)) { + /* Get the colours of our company! */ + if (spec->flags & OBJECT_FLAG_2CC_COLOUR) { + const Livery *l = Company::Get(_local_company)->livery; + palette = SPR_2CCMAP_BASE + l->colour1 + l->colour2 * 16; + } else { + palette = COMPANY_SPRITE_COLOUR(_local_company); + } + } else { + /* There's no company, so just take the base palette. */ + palette = (spec->flags & OBJECT_FLAG_2CC_COLOUR) ? SPR_2CCMAP_BASE : PALETTE_RECOLOUR_START; + } + + SpriteID image = dts->ground.sprite; + PaletteID pal = dts->ground.pal; + + if (GB(image, 0, SPRITE_WIDTH) != 0) { + DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y); + } + + DrawNewGRFTileSeqInGUI(x, y, dts, 0, palette); +} diff --git a/src/newgrf_object.h b/src/newgrf_object.h index 09d3df2ef3..0035ede93a 100644 --- a/src/newgrf_object.h +++ b/src/newgrf_object.h @@ -12,9 +12,11 @@ #ifndef NEWGRF_OBJECT_H #define NEWGRF_OBJECT_H +#include "newgrf_callbacks.h" #include "date_type.h" #include "economy_func.h" #include "strings_type.h" +#include "tile_cmd.h" #include "object_type.h" #include "newgrf_animation_type.h" #include "newgrf_class.h" @@ -114,4 +116,9 @@ typedef NewGRFClass ObjectClass; /** Mapping of purchase for objects. */ static const CargoID CT_PURCHASE_OBJECT = 1; +uint16 GetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, const ObjectSpec *spec, const Object *o, TileIndex tile); + +void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec); +void DrawNewObjectTileInGUI(int x, int y, const ObjectSpec *spec); + #endif /* NEWGRF_OBJECT_H */ diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index b9393dc2e4..80d4588a20 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -354,6 +354,10 @@ struct ResolverObject { byte layout; ///< Layout of the airport to build. TileIndex tile; ///< Tile for the callback, only valid for airporttile callbacks. } airport; + struct { + const struct Object *o; ///< The object the callback is ran for. + TileIndex tile; ///< The tile related to the object. + } object; } u; uint32 (*GetRandomBits)(const struct ResolverObject*); diff --git a/src/object_type.h b/src/object_type.h index db06669dd2..7987c6481e 100644 --- a/src/object_type.h +++ b/src/object_type.h @@ -31,4 +31,6 @@ typedef uint16 ObjectID; struct Object; struct ObjectSpec; +static const ObjectID INVALID_OBJECT = 0xFFFF; ///< An invalid object + #endif /* OBJECT_TYPE_H */