diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index e8caff18f2..564688bbe0 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -1071,6 +1071,10 @@ RelativePath=".\..\src\newgrf_engine.h" > + + @@ -1947,6 +1951,10 @@ RelativePath=".\..\src\newgrf_engine.cpp" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index fbb3a4f6b8..08089e90a3 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -1068,6 +1068,10 @@ RelativePath=".\..\src\newgrf_engine.h" > + + @@ -1944,6 +1948,10 @@ RelativePath=".\..\src\newgrf_engine.cpp" > + + diff --git a/source.list b/source.list index be268645a1..4a736e9d0c 100644 --- a/source.list +++ b/source.list @@ -176,6 +176,7 @@ newgrf_cargo.h newgrf_commons.h newgrf_config.h newgrf_engine.h +newgrf_generic.h newgrf_house.h newgrf_industries.h newgrf_industrytiles.h @@ -405,6 +406,7 @@ newgrf_cargo.cpp newgrf_commons.cpp newgrf_config.cpp newgrf_engine.cpp +newgrf_generic.cpp newgrf_house.cpp newgrf_industries.cpp newgrf_industrytiles.cpp diff --git a/src/newgrf.cpp b/src/newgrf.cpp index d9130fec49..3e37d18936 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -3089,6 +3089,11 @@ static void FeatureMapSpriteGroup(byte *buf, int len) * W cid cargo ID (sprite group ID) for this type of cargo * W def-cid default cargo ID (sprite group ID) */ + if (_cur_grffile->spritegroups == 0) { + grfmsg(1, "FeatureMapSpriteGroup: No sprite groups to work on! Skipping"); + return; + } + if (!check_length(len, 6, "FeatureMapSpriteGroup")) return; uint8 feature = buf[1]; @@ -3099,7 +3104,12 @@ static void FeatureMapSpriteGroup(byte *buf, int len) /* If idcount is zero, this is a feature callback */ if (idcount == 0) { - grfmsg(2, "FeatureMapSpriteGroup: Feature callbacks not implemented yet"); + byte *bp = &buf[4]; + uint16 groupid = grf_load_word(&bp); + + grfmsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature %d", feature); + + AddGenericCallback(feature, _cur_grffile, _cur_grffile->spritegroups[groupid]); return; } @@ -3109,11 +3119,6 @@ static void FeatureMapSpriteGroup(byte *buf, int len) grfmsg(6, "FeatureMapSpriteGroup: Feature %d, %d ids, %d cids, wagon override %d", feature, idcount, cidcount, wagover); - if (_cur_grffile->spritegroups == 0) { - grfmsg(1, "FeatureMapSpriteGroup: No sprite groups to work on! Skipping"); - return; - } - switch (feature) { case GSF_TRAIN: case GSF_ROAD: @@ -5101,6 +5106,9 @@ static void ResetNewGRFData() UnloadCustomEngineSprites(); ResetEngineListOrder(); + /* Reset generic feature callback lists */ + ResetGenericCallbacks(); + /* Reset price base data */ ResetPriceBaseMultipliers(); diff --git a/src/newgrf_generic.cpp b/src/newgrf_generic.cpp new file mode 100644 index 0000000000..8d515d7137 --- /dev/null +++ b/src/newgrf_generic.cpp @@ -0,0 +1,179 @@ +/* $Id$ */ + +#include "stdafx.h" +#include "openttd.h" +#include "variables.h" +#include "landscape.h" +#include "debug.h" +#include "newgrf.h" +#include "newgrf_callbacks.h" +#include "newgrf_commons.h" +#include "newgrf_spritegroup.h" +#include "newgrf_generic.h" +#include "tile_map.h" +#include + + +struct GenericCallback { + const GRFFile *file; + const SpriteGroup *group; + + GenericCallback(const GRFFile *file, const SpriteGroup *group) : + file(file), + group(group) + { } +}; + +typedef std::list GenericCallbackList; + +static GenericCallbackList _gcl[GSF_END]; + + +/** + * Reset all generic feature callback sprite groups. + */ +void ResetGenericCallbacks() +{ + for (uint8 feature = 0; feature < lengthof(_gcl); feature++) { + _gcl[feature].clear(); + } +} + + +/** + * Add a generic feature callback sprite group to the appropriate feature list. + * @param feature + * @param file + * @param group + */ +void AddGenericCallback(uint8 feature, const GRFFile *file, const SpriteGroup *group) +{ + if (feature >= lengthof(_gcl)) { + grfmsg(5, "AddGenericCallback: Unsupported feature 0x%02X", feature); + return; + } + + /* Generic feature callbacks are evaluated in reverse (i.e. the last group + * to be added is evaluated first, etc) thus we push the group to the + * beginning of the list so a standard iterator will do the right thing. */ + _gcl[feature].push_front(GenericCallback(file, group)); +} + + +static uint32 GenericCallbackGetRandomBits(const ResolverObject *object) +{ + return 0; +} + + +static uint32 GenericCallbackGetTriggers(const ResolverObject *object) +{ + return 0; +} + + +static void GenericCallbackSetTriggers(const ResolverObject *object, int triggers) +{ + return; +} + + +static uint32 GenericCallbackGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) +{ + switch (variable) { + case 0x40: return object->u.generic.cargo_type; + + case 0x80: return object->u.generic.cargo_type; + case 0x81: return object->u.generic.cargo_type; + case 0x82: return object->u.generic.default_selection; + case 0x83: return object->u.generic.src_industry; + case 0x84: return object->u.generic.dst_industry; + case 0x85: return object->u.generic.distance; + case 0x86: return object->u.generic.event; + case 0x87: return object->u.generic.count; + case 0x88: return object->u.generic.station_size; + + default: break; + } + + DEBUG(grf, 1, "Unhandled generic feature property 0x%02X", variable); + + *available = false; + return 0; +} + + +static const SpriteGroup *GenericCallbackResolveReal(const ResolverObject *object, const SpriteGroup *group) +{ + if (group->g.real.num_loaded == 0) return NULL; + + return group->g.real.loaded[0]; +} + + +static inline void NewGenericResolver(ResolverObject *res) +{ + res->GetRandomBits = &GenericCallbackGetRandomBits; + res->GetTriggers = &GenericCallbackGetTriggers; + res->SetTriggers = &GenericCallbackSetTriggers; + res->GetVariable = &GenericCallbackGetVariable; + res->ResolveReal = &GenericCallbackResolveReal; + + res->callback = CBID_NO_CALLBACK; + res->callback_param1 = 0; + res->callback_param2 = 0; + res->last_value = 0; + res->trigger = 0; + res->reseed = 0; +} + + +/** Follow a generic feature callback list and return the first successful + * answer + * @param feature GRF Feature of callback + * @param object pre-populated resolver object + * @param file address of GRFFile object if file reference is needed, NULL is valid + * @return callback value if successful or CALLBACK_FAILED + */ +static uint16 GetGenericCallbackResult(uint8 feature, ResolverObject *object, const GRFFile **file) +{ + assert(feature < lengthof(_gcl)); + + /* Test each feature callback sprite group. */ + for (GenericCallbackList::const_iterator it = _gcl[feature].begin(); it != _gcl[feature].end(); ++it) { + const SpriteGroup *group = it->group; + group = Resolve(group, object); + if (group == NULL || group->type != SGT_CALLBACK) continue; + + /* Return NewGRF file if necessary */ + if (file != NULL) *file = it->file; + + return group->g.callback.result; + } + + /* No callback returned a valid result, so we've failed. */ + return CALLBACK_FAILED; +} + + +/** + * 'Execute' an AI purchase selection callback + */ +uint16 GetAiPurchaseCallbackResult(uint8 feature, CargoID cargo_type, uint8 default_selection, IndustryType src_industry, IndustryType dst_industry, uint8 distance, AIConstructionEvent event, uint8 count, uint8 station_size, const GRFFile **file) +{ + ResolverObject object; + + NewGenericResolver(&object); + + object.callback = CBID_GENERIC_AI_PURCHASE_SELECTION; + object.u.generic.cargo_type = cargo_type; + object.u.generic.default_selection = default_selection; + object.u.generic.src_industry = src_industry; + object.u.generic.dst_industry = dst_industry; + object.u.generic.distance = distance; + object.u.generic.event = event; + object.u.generic.count = count; + object.u.generic.station_size = station_size; + + return GetGenericCallbackResult(feature, &object, file); +} diff --git a/src/newgrf_generic.h b/src/newgrf_generic.h new file mode 100644 index 0000000000..0c24f88fcd --- /dev/null +++ b/src/newgrf_generic.h @@ -0,0 +1,35 @@ +/* $Id$ */ + +#ifndef NEWGRF_GENERIC_H +#define NEWGRF_GENERIC_H + +enum AIConstructionEvent { + AICE_TRAIN_CHECK_RAIL_ENGINE = 0x00, ///< Check if we should build an engine + AICE_TRAIN_CHECK_ELRAIL_ENGINE = 0x01, + AICE_TRAIN_CHECK_MONORAIL_ENGINE = 0x02, + AICE_TRAIN_CHECK_MAGLEV_ENGINE = 0x03, + AICE_TRAIN_GET_RAIL_WAGON = 0x08, + AICE_TRAIN_GET_ELRAIL_WAGON = 0x09, + AICE_TRAIN_GET_MONORAIL_WAGON = 0x0A, + AICE_TRAIN_GET_MAGLEV_WAGON = 0x0B, + AICE_TRAIN_GET_RAILTYPE = 0x0F, + + AICE_ROAD_CHECK_ENGINE = 0x00, ///< Check if we should build an engine + AICE_ROAD_GET_FIRST_ENGINE = 0x01, ///< Unused, we check all + AICE_ROAD_GET_NUMBER_ENGINES = 0x02, ///< Unused, we check all + + AICE_SHIP_CHECK_ENGINE = 0x00, ///< Check if we should build an engine + AICE_SHIP_GET_FIRST_ENGINE = 0x01, ///< Unused, we check all + AICE_SHIP_GET_NUMBER_ENGINES = 0x02, ///< Unused, we check all + + AICE_AIRCRAFT_CHECK_ENGINE = 0x00, ///< Check if we should build an engine + + AICE_STATION_GET_STATION_ID = 0x00, ///< Get a station ID to build +}; + +void ResetGenericCallbacks(); +void AddGenericCallback(uint8 feature, const struct GRFFile *file, const struct SpriteGroup *group); + +uint16 GetAiPurchaseCallbackResult(uint8 feature, CargoID cargo_type, uint8 default_selection, IndustryType src_industry, IndustryType dst_industry, uint8 distance, AIConstructionEvent event, uint8 count, uint8 station_size, const struct GRFFile **file); + +#endif /* NEWGRF_GENERIC_H */ diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 13199e0d1d..f6507b4d28 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -10,6 +10,7 @@ #include "newgrf_storage.h" #include "core/bitmath_func.hpp" #include "gfx_type.h" +#include "newgrf_generic.h" /** * Gets the value of a so-called newgrf "register". @@ -236,6 +237,16 @@ struct ResolverObject { struct { const struct CargoSpec *cs; } cargo; + struct { + CargoID cargo_type; + uint8 default_selection; + IndustryType src_industry; + IndustryType dst_industry; + uint8 distance; + AIConstructionEvent event; + uint8 count; + uint8 station_size; + } generic; } u; uint32 (*GetRandomBits)(const struct ResolverObject*);