tron@2186: /* $Id$ */ tron@2186: belugas@6918: /** @file texteff.cpp */ belugas@6918: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" rubidium@8615: #include "tile_cmd.h" maedhros@6949: #include "landscape.h" truelight@0: #include "viewport.h" truelight@0: #include "saveload.h" truelight@543: #include "console.h" truelight@1595: #include "string.h" tron@2153: #include "variables.h" celestar@2218: #include "table/sprites.h" truelight@7433: #include "blitter/factory.hpp" truelight@543: #include /* va_list */ truelight@7494: #include "texteff.hpp" peter1138@7666: #include "video/video_driver.hpp" belugas@8345: #include "transparency.h" rubidium@8610: #include "strings_func.h" rubidium@8626: #include "core/alloc_func.hpp" rubidium@8636: #include "date_func.h" rubidium@8627: #include "functions.h" truelight@0: rubidium@5679: enum { Darkvater@6541: MAX_TEXTMESSAGE_LENGTH = 200, truelight@7494: INIT_NUM_TEXT_MESSAGES = 20, rubidium@5679: MAX_CHAT_MESSAGES = 10, rubidium@5679: }; rubidium@5679: rubidium@6574: struct TextEffect { truelight@0: StringID string_id; tron@849: int32 x; tron@849: int32 y; tron@849: int32 right; tron@849: int32 bottom; truelight@0: uint16 duration; rubidium@7502: uint64 params_1; rubidium@7502: uint64 params_2; truelight@7494: TextEffectMode mode; rubidium@6574: }; truelight@0: truelight@543: rubidium@7950: struct ChatMessage { truelight@543: char message[MAX_TEXTMESSAGE_LENGTH]; truelight@543: uint16 color; truelight@4363: Date end_date; rubidium@6574: }; truelight@543: rubidium@7950: /* used for text effects */ truelight@7494: static TextEffect *_text_effect_list = NULL; rubidium@7950: static uint16 _num_text_effects = INIT_NUM_TEXT_MESSAGES; truelight@0: rubidium@7950: /* used for chat window */ rubidium@7950: static ChatMessage _chatmsg_list[MAX_CHAT_MESSAGES]; rubidium@7950: static bool _chatmessage_dirty = false; rubidium@7950: static bool _chatmessage_visible = false; truelight@543: Darkvater@4956: /* The chatbox grows from the bottom so the coordinates are pixels from Darkvater@4956: * the left and pixels from the bottom. The height is the maximum height */ rubidium@8617: static const PointDimension _chatmsg_box = {10, 30, 500, 150}; rubidium@7950: static uint8 _chatmessage_backup[150 * 500 * 6]; // (height * width) truelight@543: rubidium@7950: static inline uint GetChatMessageCount() Darkvater@4957: { Darkvater@4959: uint i; Darkvater@4957: Darkvater@4959: for (i = 0; i < MAX_CHAT_MESSAGES; i++) { rubidium@7950: if (_chatmsg_list[i].message[0] == '\0') break; Darkvater@4957: } Darkvater@4957: Darkvater@4957: return i; Darkvater@4957: } Darkvater@4957: Darkvater@4957: /* Add a text message to the 'chat window' to be shown Darkvater@4957: * @param color The colour this message is to be shown in Darkvater@4957: * @param duration The duration of the chat message in game-days Darkvater@4957: * @param message message itself in printf() style */ rubidium@7950: void CDECL AddChatMessage(uint16 color, uint8 duration, const char *message, ...) truelight@543: { tron@2455: char buf[MAX_TEXTMESSAGE_LENGTH]; Darkvater@4957: const char *bufp; truelight@543: va_list va; Darkvater@4957: uint msg_count; Darkvater@4957: uint16 lines; truelight@543: truelight@543: va_start(va, message); tron@2373: vsnprintf(buf, lengthof(buf), message, va); truelight@543: va_end(va); truelight@543: Darkvater@6541: Darkvater@6541: Utf8TrimString(buf, MAX_TEXTMESSAGE_LENGTH); Darkvater@6541: Darkvater@4957: /* Force linebreaks for strings that are too long */ rubidium@7950: lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1; Darkvater@4957: if (lines >= MAX_CHAT_MESSAGES) return; truelight@543: rubidium@7950: msg_count = GetChatMessageCount(); Darkvater@4957: /* We want to add more chat messages than there is free space for, remove 'old' */ Darkvater@4957: if (lines > MAX_CHAT_MESSAGES - msg_count) { Darkvater@4957: int i = lines - (MAX_CHAT_MESSAGES - msg_count); rubidium@7950: memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i)); Darkvater@4957: msg_count = MAX_CHAT_MESSAGES - lines; truelight@543: } truelight@543: Darkvater@4957: for (bufp = buf; lines != 0; lines--) { rubidium@7950: ChatMessage *cmsg = &_chatmsg_list[msg_count++]; rubidium@7950: ttd_strlcpy(cmsg->message, bufp, sizeof(cmsg->message)); Darkvater@4957: Darkvater@4957: /* The default colour for a message is player colour. Replace this with Darkvater@4957: * white for any additional lines */ rubidium@7950: cmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR; rubidium@7950: cmsg->end_date = _date + duration; Darkvater@4957: Darkvater@4957: bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string Darkvater@4957: } truelight@543: rubidium@7950: _chatmessage_dirty = true; truelight@543: } truelight@543: rubidium@7950: void InitChatMessage() truelight@543: { tron@2639: uint i; tron@2639: tron@2639: for (i = 0; i < MAX_CHAT_MESSAGES; i++) { rubidium@7950: _chatmsg_list[i].message[0] = '\0'; tron@2639: } truelight@543: } truelight@543: rubidium@7950: /** Hide the chatbox */ rubidium@7950: void UndrawChatMessage() truelight@543: { rubidium@7950: if (_chatmessage_visible) { truelight@7433: Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter(); rubidium@5679: /* Sometimes we also need to hide the cursor rubidium@5679: * This is because both textmessage and the cursor take a shot of the rubidium@5679: * screen before drawing. rubidium@5679: * Now the textmessage takes his shot and paints his data before the cursor rubidium@5679: * does, so in the shot of the cursor is the screen-data of the textmessage rubidium@5679: * included when the cursor hangs somewhere over the textmessage. To rubidium@5679: * avoid wrong repaints, we undraw the cursor in that case, and everything rubidium@5679: * looks nicely ;) rubidium@5679: * (and now hope this story above makes sense to you ;)) rubidium@5679: */ truelight@543: truelight@543: if (_cursor.visible) { rubidium@7950: if (_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x && rubidium@7950: _cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width && rubidium@7950: _cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height && rubidium@7950: _cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) { truelight@543: UndrawMouseCursor(); truelight@543: } truelight@543: } truelight@543: rubidium@7950: int x = _chatmsg_box.x; rubidium@7950: int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height; rubidium@7950: int width = _chatmsg_box.width; rubidium@7950: int height = _chatmsg_box.height; rubidium@6692: if (y < 0) { rubidium@7950: height = max(height + y, min(_chatmsg_box.height, _screen.height)); rubidium@6692: y = 0; rubidium@6692: } rubidium@6692: if (x + width >= _screen.width) { rubidium@6692: width = _screen.width - x; rubidium@6692: } rubidium@6700: if (width <= 0 || height <= 0) return; rubidium@6692: rubidium@7950: _chatmessage_visible = false; rubidium@5679: /* Put our 'shot' back to the screen */ rubidium@7950: blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height); rubidium@5679: /* And make sure it is updated next time */ peter1138@7666: _video_driver->MakeDirty(x, y, width, height); truelight@543: rubidium@7950: _chatmessage_dirty = true; truelight@543: } truelight@543: } truelight@543: belugas@6918: /** Check if a message is expired every day */ rubidium@7950: void ChatMessageDailyLoop() truelight@543: { tron@2639: uint i; tron@2639: truelight@1595: for (i = 0; i < MAX_CHAT_MESSAGES; i++) { rubidium@7950: ChatMessage *cmsg = &_chatmsg_list[i]; rubidium@7950: if (cmsg->message[0] == '\0') continue; truelight@1595: Darkvater@4957: /* Message has expired, remove from the list */ rubidium@7950: if (cmsg->end_date < _date) { truelight@1595: /* Move the remaining messages over the current message */ rubidium@7950: if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1)); truelight@1595: truelight@1595: /* Mark the last item as empty */ rubidium@7950: _chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0'; rubidium@7950: _chatmessage_dirty = true; truelight@1595: truelight@1595: /* Go one item back, because we moved the array 1 to the left */ truelight@543: i--; truelight@543: } truelight@543: } truelight@543: } truelight@543: rubidium@7950: /** Draw the chat message-box */ rubidium@7950: void DrawChatMessage() truelight@543: { truelight@7433: Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter(); rubidium@7950: if (!_chatmessage_dirty) return; truelight@543: rubidium@5679: /* First undraw if needed */ rubidium@7950: UndrawChatMessage(); truelight@543: Darkvater@4959: if (_iconsole_mode == ICONSOLE_FULL) return; truelight@543: truelight@1595: /* Check if we have anything to draw at all */ rubidium@7950: uint count = GetChatMessageCount(); Darkvater@4960: if (count == 0) return; truelight@543: rubidium@7950: int x = _chatmsg_box.x; rubidium@7950: int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height; rubidium@7950: int width = _chatmsg_box.width; rubidium@7950: int height = _chatmsg_box.height; rubidium@6692: if (y < 0) { rubidium@7950: height = max(height + y, min(_chatmsg_box.height, _screen.height)); rubidium@6692: y = 0; rubidium@6692: } rubidium@6692: if (x + width >= _screen.width) { rubidium@6692: width = _screen.width - x; rubidium@6692: } rubidium@6700: if (width <= 0 || height <= 0) return; rubidium@6700: rubidium@7950: assert(blitter->BufferSize(width, height) < (int)sizeof(_chatmessage_backup)); truelight@7481: rubidium@5679: /* Make a copy of the screen as it is before painting (for undraw) */ rubidium@7950: blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height); truelight@543: Darkvater@4960: _cur_dpi = &_screen; // switch to _screen painting truelight@543: rubidium@7950: /* Paint a half-transparent box behind the chat messages */ Darkvater@4960: GfxFillRect( rubidium@7950: _chatmsg_box.x, rubidium@7950: _screen.height - _chatmsg_box.y - count * 13 - 2, rubidium@7950: _chatmsg_box.x + _chatmsg_box.width - 1, rubidium@7950: _screen.height - _chatmsg_box.y - 2, peter1138@5919: PALETTE_TO_TRANSPARENT | (1 << USE_COLORTABLE) // black, but with some alpha for background Darkvater@4960: ); truelight@1595: rubidium@7950: /* Paint the chat messages starting with the lowest at the bottom */ rubidium@6692: for (uint y = 13; count-- != 0; y += 13) { rubidium@7950: DoDrawString(_chatmsg_list[count].message, _chatmsg_box.x + 3, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].color); rubidium@6988: } truelight@543: rubidium@5679: /* Make sure the data is updated next flush */ peter1138@7666: _video_driver->MakeDirty(x, y, width, height); truelight@543: rubidium@7950: _chatmessage_visible = true; rubidium@7950: _chatmessage_dirty = false; truelight@543: } truelight@543: rubidium@8041: /* Text Effects */ rubidium@8041: /** rubidium@8041: * Mark the area of the text effect as dirty. rubidium@8041: * rubidium@8041: * This function marks the area of a text effect as dirty for repaint. rubidium@8041: * rubidium@8041: * @param te The TextEffect to mark the area dirty rubidium@8041: * @ingroup dirty rubidium@8041: */ truelight@0: static void MarkTextEffectAreaDirty(TextEffect *te) truelight@0: { rubidium@8031: /* Width and height of the text effect are doubled, so they are correct in both zoom out levels 1x and 2x. */ truelight@0: MarkAllViewportsDirty( truelight@0: te->x, truelight@0: te->y - 1, truelight@0: (te->right - te->x)*2 + te->x + 1, truelight@0: (te->bottom - (te->y - 1)) * 2 + (te->y - 1) + 1 truelight@0: ); truelight@0: } truelight@0: truelight@7494: TextEffectID AddTextEffect(StringID msg, int x, int y, uint16 duration, TextEffectMode mode) truelight@0: { truelight@0: TextEffect *te; truelight@0: int w; truelight@0: char buffer[100]; truelight@7494: TextEffectID i; truelight@0: truelight@7494: if (_game_mode == GM_MENU) return INVALID_TE_ID; truelight@193: truelight@7494: /* Look for a free spot in the text effect array */ truelight@7494: for (i = 0; i < _num_text_effects; i++) { truelight@7494: if (_text_effect_list[i].string_id == INVALID_STRING_ID) break; truelight@0: } truelight@0: truelight@7494: /* If there is none found, we grow the array */ truelight@7494: if (i == _num_text_effects) { truelight@7494: _num_text_effects += 25; rubidium@8533: _text_effect_list = ReallocT(_text_effect_list, _num_text_effects); truelight@7494: for (; i < _num_text_effects; i++) _text_effect_list[i].string_id = INVALID_STRING_ID; truelight@7494: i = _num_text_effects - 1; truelight@7494: } truelight@7494: truelight@7494: te = &_text_effect_list[i]; truelight@7494: truelight@7494: /* Start defining this object */ truelight@0: te->string_id = msg; truelight@0: te->duration = duration; truelight@0: te->y = y - 5; truelight@0: te->bottom = y + 5; tron@534: te->params_1 = GetDParam(0); tron@534: te->params_2 = GetDParam(4); truelight@7494: te->mode = mode; truelight@0: Darkvater@4912: GetString(buffer, msg, lastof(buffer)); Darkvater@4609: w = GetStringBoundingBox(buffer).width; truelight@0: truelight@0: te->x = x - (w >> 1); truelight@0: te->right = x + (w >> 1) - 1; truelight@0: MarkTextEffectAreaDirty(te); truelight@7494: truelight@7494: return i; truelight@7494: } truelight@7494: truelight@7494: void UpdateTextEffect(TextEffectID te_id, StringID msg) truelight@7494: { truelight@7494: assert(te_id < _num_text_effects); truelight@7494: TextEffect *te; truelight@7494: truelight@7494: /* Update details */ truelight@7494: te = &_text_effect_list[te_id]; truelight@7494: te->string_id = msg; truelight@7494: te->params_1 = GetDParam(0); truelight@7494: te->params_2 = GetDParam(4); truelight@7494: rubidium@8031: /* Update width of text effect */ rubidium@8031: char buffer[100]; rubidium@8031: GetString(buffer, msg, lastof(buffer)); rubidium@8031: int w = GetStringBoundingBox(buffer).width; rubidium@8031: rubidium@8031: /* Only allow to make it broader, so it completely covers the old text. That avoids remnants of the old text. */ rubidium@8031: int right_new = te->x + w; rubidium@8031: if (te->right < right_new) te->right = right_new; rubidium@8031: truelight@7494: MarkTextEffectAreaDirty(te); truelight@7494: } truelight@7494: truelight@7494: void RemoveTextEffect(TextEffectID te_id) truelight@7494: { truelight@7494: assert(te_id < _num_text_effects); truelight@7494: TextEffect *te; truelight@7494: truelight@7494: te = &_text_effect_list[te_id]; truelight@7494: MarkTextEffectAreaDirty(te); truelight@7494: te->string_id = INVALID_STRING_ID; truelight@0: } truelight@0: truelight@0: static void MoveTextEffect(TextEffect *te) truelight@0: { truelight@7494: /* Never expire for duration of 0xFFFF */ truelight@7494: if (te->duration == 0xFFFF) return; truelight@0: if (te->duration < 8) { tron@2470: te->string_id = INVALID_STRING_ID; truelight@0: } else { tron@2549: te->duration -= 8; truelight@0: te->y--; truelight@0: te->bottom--; truelight@0: } truelight@0: MarkTextEffectAreaDirty(te); truelight@0: } truelight@0: rubidium@6573: void MoveAllTextEffects() truelight@0: { truelight@7494: for (TextEffectID i = 0; i < _num_text_effects; i++) { truelight@7494: TextEffect *te = &_text_effect_list[i]; truelight@7494: if (te->string_id != INVALID_STRING_ID && te->mode == TE_RISING) MoveTextEffect(te); truelight@0: } truelight@0: } truelight@0: rubidium@6573: void InitTextEffects() truelight@0: { truelight@7494: if (_text_effect_list == NULL) _text_effect_list = MallocT(_num_text_effects); truelight@0: truelight@7494: for (TextEffectID i = 0; i < _num_text_effects; i++) _text_effect_list[i].string_id = INVALID_STRING_ID; truelight@0: } truelight@0: truelight@0: void DrawTextEffects(DrawPixelInfo *dpi) truelight@0: { tron@4469: switch (dpi->zoom) { truelight@7120: case ZOOM_LVL_NORMAL: truelight@7494: for (TextEffectID i = 0; i < _num_text_effects; i++) { truelight@7494: TextEffect *te = &_text_effect_list[i]; tron@4469: if (te->string_id != INVALID_STRING_ID && tron@4469: dpi->left <= te->right && tron@4469: dpi->top <= te->bottom && tron@4469: dpi->left + dpi->width > te->x && tron@4469: dpi->top + dpi->height > te->y) { belugas@8345: if (te->mode == TE_RISING || (_patches.loading_indicators && !IsTransparencySet(TO_LOADING))) { truelight@7494: AddStringToDraw(te->x, te->y, te->string_id, te->params_1, te->params_2); truelight@7494: } tron@4469: } tron@4469: } tron@4469: break; truelight@0: truelight@7120: case ZOOM_LVL_OUT_2X: truelight@7494: for (TextEffectID i = 0; i < _num_text_effects; i++) { truelight@7494: TextEffect *te = &_text_effect_list[i]; tron@4469: if (te->string_id != INVALID_STRING_ID && tron@4469: dpi->left <= te->right * 2 - te->x && tron@4469: dpi->top <= te->bottom * 2 - te->y && tron@4469: dpi->left + dpi->width > te->x && tron@4469: dpi->top + dpi->height > te->y) { belugas@8345: if (te->mode == TE_RISING || (_patches.loading_indicators && !IsTransparencySet(TO_LOADING))) { truelight@7494: AddStringToDraw(te->x, te->y, (StringID)(te->string_id - 1), te->params_1, te->params_2); truelight@7494: } tron@4469: } tron@4469: } tron@4469: break; truelight@7120: truelight@7122: case ZOOM_LVL_OUT_4X: truelight@7149: case ZOOM_LVL_OUT_8X: truelight@7120: break; truelight@7122: truelight@7122: default: NOT_REACHED(); truelight@0: } truelight@0: } truelight@0: rubidium@8190: /** The table/list with animated tiles. */ rubidium@8190: TileIndex *_animated_tile_list = NULL; rubidium@8190: /** The number of animated tiles in the current state. */ rubidium@8190: uint _animated_tile_count = 0; rubidium@8190: /** The number of slots for animated tiles allocated currently. */ rubidium@8190: static uint _animated_tile_allocated = 0; rubidium@8190: rubidium@8190: /** rubidium@8190: * Removes the given tile from the animated tile table. rubidium@8190: * @param tile the tile to remove rubidium@8190: */ tron@1977: void DeleteAnimatedTile(TileIndex tile) truelight@0: { rubidium@8190: for (TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) { tron@1977: if (tile == *ti) { rubidium@8245: /* Remove the hole rubidium@8245: * The order of the remaining elements must stay the same, otherwise the animation loop rubidium@8245: * may miss a tile; that's why we must use memmove instead of just moving the last element. rubidium@8245: */ rubidium@8245: memmove(ti, ti + 1, (_animated_tile_list + _animated_tile_count - (ti + 1)) * sizeof(*ti)); rubidium@8190: _animated_tile_count--; truelight@0: MarkTileDirtyByTile(tile); truelight@0: return; truelight@193: } truelight@0: } truelight@0: } truelight@0: rubidium@8190: /** rubidium@8190: * Add the given tile to the animated tile table (if it does not exist rubidium@8190: * on that table yet). Also increases the size of the table if necessary. rubidium@8190: * @param tile the tile to make animated rubidium@8190: */ rubidium@8190: void AddAnimatedTile(TileIndex tile) truelight@0: { rubidium@8190: MarkTileDirtyByTile(tile); truelight@0: rubidium@8190: for (const TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) { rubidium@8190: if (tile == *ti) return; truelight@193: } truelight@0: rubidium@8190: /* Table not large enough, so make it larger */ rubidium@8190: if (_animated_tile_count == _animated_tile_allocated) { rubidium@8190: _animated_tile_allocated *= 2; rubidium@8190: _animated_tile_list = ReallocT(_animated_tile_list, _animated_tile_allocated); rubidium@8190: } rubidium@8190: rubidium@8190: _animated_tile_list[_animated_tile_count] = tile; rubidium@8190: _animated_tile_count++; truelight@0: } truelight@0: rubidium@8190: /** rubidium@8190: * Animate all tiles in the animated tile list, i.e.\ call AnimateTile on them. rubidium@8190: */ rubidium@6573: void AnimateAnimatedTiles() truelight@0: { rubidium@8245: const TileIndex *ti = _animated_tile_list; rubidium@8245: while (ti < _animated_tile_list + _animated_tile_count) { rubidium@8245: const TileIndex curr = *ti; rubidium@8245: AnimateTile(curr); rubidium@8245: /* During the AnimateTile call, DeleteAnimatedTile could have been called, rubidium@8245: * deleting an element we've already processed and pushing the rest one rubidium@8245: * slot to the left. We can detect this by checking whether the index rubidium@8245: * in the current slot has changed - if it has, an element has been deleted, rubidium@8245: * and we should process the current slot again instead of going forward. rubidium@8245: * NOTE: this will still break if more than one animated tile is being rubidium@8245: * deleted during the same AnimateTile call, but no code seems to rubidium@8245: * be doing this anyway. rubidium@8245: */ rubidium@8245: if (*ti == curr) ++ti; truelight@0: } truelight@0: } truelight@0: rubidium@8190: /** rubidium@8190: * Initialize all animated tile variables to some known begin point rubidium@8190: */ rubidium@6573: void InitializeAnimatedTiles() truelight@0: { rubidium@8190: _animated_tile_list = ReallocT(_animated_tile_list, 256); rubidium@8190: _animated_tile_count = 0; rubidium@8190: _animated_tile_allocated = 256; truelight@0: } truelight@0: rubidium@8190: /** rubidium@8190: * Save the ANIT chunk. rubidium@8190: */ rubidium@8190: static void Save_ANIT() truelight@0: { rubidium@8190: SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list)); rubidium@8190: SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32); truelight@0: } truelight@0: rubidium@8190: /** rubidium@8190: * Load the ANIT chunk; the chunk containing the animated tiles. rubidium@8190: */ rubidium@8190: static void Load_ANIT() rubidium@8190: { rubidium@8190: /* Before version 80 we did NOT have a variable length animated tile table */ rubidium@8190: if (CheckSavegameVersion(80)) { rubidium@8190: /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */ rubidium@8190: SlArray(_animated_tile_list, 256, CheckSavegameVersion(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32); truelight@0: rubidium@8190: for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) { rubidium@8190: if (_animated_tile_list[_animated_tile_count] == 0) break; rubidium@8190: } rubidium@8190: return; rubidium@8190: } rubidium@8190: rubidium@8190: _animated_tile_count = SlGetFieldLength() / sizeof(*_animated_tile_list); rubidium@8190: rubidium@8190: /* Determine a nice rounded size for the amount of allocated tiles */ rubidium@8190: _animated_tile_allocated = 256; rubidium@8190: while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2; rubidium@8190: rubidium@8190: _animated_tile_list = ReallocT(_animated_tile_list, _animated_tile_allocated); rubidium@8190: SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32); rubidium@8190: } rubidium@8190: rubidium@8190: /** rubidium@8190: * "Definition" imported by the saveload code to be able to load and save rubidium@8190: * the animated tile table. rubidium@8190: */ rubidium@5838: extern const ChunkHandler _animated_tile_chunk_handlers[] = { rubidium@8190: { 'ANIT', Save_ANIT, Load_ANIT, CH_RIFF | CH_LAST}, truelight@0: };