diff --git a/engine.c b/engine.c --- a/engine.c +++ b/engine.c @@ -1051,6 +1051,195 @@ int32 CmdRenameEngine(int x, int y, uint } +/* + * returns true if an engine is valid, of the specified type, and buildable by + * the current player, false otherwise + * + * engine = index of the engine to check + * type = the type the engine should be of (VEH_xxx) + */ +bool IsEngineBuildable(uint engine, byte type) +{ + const Engine *e; + + // check if it's an engine that is in the engine array + if (!IsEngineIndex(engine)) return false; + + e = GetEngine(engine); + + // check if it's an engine of specified type + if (e->type != type) return false; + + // check if it's available + if (!HASBIT(e->player_avail, _current_player)) return false; + + return true; +} + +/************************************************************************ + * Engine Replacement stuff + ************************************************************************/ + +static void EngineRenewPoolNewBlock(uint start_item); /* Forward declare for initializer of _engine_renew_pool */ +enum { + ENGINE_RENEW_POOL_BLOCK_SIZE_BITS = 3, + ENGINE_RENEW_POOL_MAX_BLOCKS = 8000, +}; + +MemoryPool _engine_renew_pool = { "EngineRe", ENGINE_RENEW_POOL_MAX_BLOCKS, ENGINE_RENEW_POOL_BLOCK_SIZE_BITS, sizeof(EngineRenew), &EngineRenewPoolNewBlock, 0, 0, NULL }; + +static inline uint16 GetEngineRenewPoolSize(void) +{ + return _engine_renew_pool.total_items; +} + +#define FOR_ALL_ENGINE_RENEWS_FROM(er, start) for (er = GetEngineRenew(start); er != NULL; er = (er->index + 1 < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1) : NULL) +#define FOR_ALL_ENGINE_RENEWS(er) FOR_ALL_ENGINE_RENEWS_FROM(er, 0) + +static void EngineRenewPoolNewBlock(uint start_item) +{ + EngineRenew *er; + + FOR_ALL_ENGINE_RENEWS_FROM(er, start_item) { + er->index = start_item++; + er->from = INVALID_ENGINE; + } +} + + +static EngineRenew *AllocateEngineRenew(void) +{ + EngineRenew *er; + + FOR_ALL_ENGINE_RENEWS(er) { + if (er->from == INVALID_ENGINE) { + er->to = INVALID_ENGINE; + er->next = NULL; + return er; + } + } + + /* Check if we can add a block to the pool */ + if (AddBlockToPool(&_engine_renew_pool)) return AllocateEngineRenew(); + + return NULL; +} + +/** + * Retrieves the EngineRenew that specifies the replacement of the given + * engine type from the given renewlist */ +static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine) +{ + EngineRenew* er = (EngineRenew*)erl; /* Fetch first element */ + while (er) { + if (er->from == engine) return er; + er = er->next; + } + return NULL; +} + +void RemoveAllEngineReplacement(EngineRenewList* erl) +{ + EngineRenew* er = (EngineRenew*)(*erl); /* Fetch first element */ + while (er) { + er->from = INVALID_ENGINE; /* "Deallocate" all elements */ + er = er->next; + } + *erl = NULL; /* Empty list */ +} + +EngineID EngineReplacement(EngineRenewList erl, EngineID engine) +{ + const EngineRenew *er = GetEngineReplacement(erl, engine); + return er == NULL ? INVALID_ENGINE : er->to; +} + +int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, uint32 flags) +{ + EngineRenew *er; + + // Check if the old vehicle is already in the list + er = GetEngineReplacement(*erl, old_engine); + if (er != NULL) { + if (flags & DC_EXEC) er->to = new_engine; + return 0; + } + + er = AllocateEngineRenew(); + if (er == NULL) return CMD_ERROR; + + if (flags & DC_EXEC) { + er->from = old_engine; + er->to = new_engine; + er->next = (EngineRenew*)(*erl); /* Resolve the first element in the list */ + + *erl = (EngineRenewList)er; /* Insert before the first element */ + } + + return 0; +} + +int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, uint32 flags) +{ + EngineRenew* er = (EngineRenew*)(*erl); /* Start at the first element */ + EngineRenew* prev = NULL; + + while (er) + { + if (er->from == engine) { + if (flags & DC_EXEC) { + if (prev == NULL) { /* First element */ + (*erl) = (EngineRenewList)er->next; /* The second becomes the new first element */ + } else { + prev->next = er->next; /* Cut this element out */ + } + er->from = INVALID_ENGINE; /* Deallocate */ + } + return 0; + } + prev = er; + er = er->next; /* Look at next element */ + } + + return CMD_ERROR; /* Not found? */ +} + +static const SaveLoad _engine_renew_desc[] = { + SLE_VAR(EngineRenew, from, SLE_UINT16), + SLE_VAR(EngineRenew, to, SLE_UINT16), + + SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS), + + SLE_END() +}; + +static void Save_ERNW(void) +{ + EngineRenew *er; + + FOR_ALL_ENGINE_RENEWS(er) { + if (er->from != INVALID_ENGINE) { + SlSetArrayIndex(er->index); + SlObject(er, _engine_renew_desc); + } + } +} + +static void Load_ERNW(void) +{ + int index; + + while ((index = SlIterateArray()) != -1) { + EngineRenew *er; + + if (!AddBlockIfNeeded(&_engine_renew_pool, index)) + error("EngineRenews: failed loading savegame: too many EngineRenews"); + + er = GetEngineRenew(index); + SlObject(er, _engine_renew_desc); + } +} + static const SaveLoad _engine_desc[] = { SLE_VAR(Engine,intro_date, SLE_UINT16), SLE_VAR(Engine,age, SLE_UINT16), @@ -1100,32 +1289,14 @@ static void LoadSave_ENGS(void) } const ChunkHandler _engine_chunk_handlers[] = { - { 'ENGN', Save_ENGN, Load_ENGN, CH_ARRAY}, - { 'ENGS', LoadSave_ENGS, LoadSave_ENGS, CH_RIFF | CH_LAST}, + { 'ENGN', Save_ENGN, Load_ENGN, CH_ARRAY }, + { 'ENGS', LoadSave_ENGS, LoadSave_ENGS, CH_RIFF }, + { 'ERNW', Save_ERNW, Load_ERNW, CH_ARRAY | CH_LAST}, }; - -/* - * returns true if an engine is valid, of the specified type, and buildable by - * the current player, false otherwise - * - * engine = index of the engine to check - * type = the type the engine should be of (VEH_xxx) - */ -bool IsEngineBuildable(uint engine, byte type) +void InitializeEngines(void) { - const Engine *e; - - // check if it's an engine that is in the engine array - if (!IsEngineIndex(engine)) return false; - - e = GetEngine(engine); - - // check if it's an engine of specified type - if (e->type != type) return false; - - // check if it's available - if (!HASBIT(e->player_avail, _current_player)) return false; - - return true; + /* Clean the engine renew pool and create 1 block in it */ + CleanPool(&_engine_renew_pool); + AddBlockToPool(&_engine_renew_pool); }