00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "tile_cmd.h"
00008 #include "landscape.h"
00009 #include "gfx_func.h"
00010 #include "saveload.h"
00011 #include "console.h"
00012 #include "variables.h"
00013 #include "blitter/factory.hpp"
00014 #include "texteff.hpp"
00015 #include "video/video_driver.hpp"
00016 #include "transparency.h"
00017 #include "strings_func.h"
00018 #include "core/alloc_func.hpp"
00019 #include "date_func.h"
00020 #include "functions.h"
00021 #include "viewport_func.h"
00022 #include "settings_type.h"
00023
00024 #include "table/sprites.h"
00025
00026 #include <stdarg.h>
00027
00028 enum {
00029 MAX_TEXTMESSAGE_LENGTH = 200,
00030 INIT_NUM_TEXT_MESSAGES = 20,
00031 MAX_CHAT_MESSAGES = 10,
00032 };
00033
00034 struct TextEffect {
00035 StringID string_id;
00036 int32 x;
00037 int32 y;
00038 int32 right;
00039 int32 bottom;
00040 uint16 duration;
00041 uint64 params_1;
00042 uint64 params_2;
00043 TextEffectMode mode;
00044 };
00045
00046
00047 struct ChatMessage {
00048 char message[MAX_TEXTMESSAGE_LENGTH];
00049 uint16 color;
00050 Date end_date;
00051 };
00052
00053
00054 static TextEffect *_text_effect_list = NULL;
00055 static uint16 _num_text_effects = INIT_NUM_TEXT_MESSAGES;
00056
00057
00058 static ChatMessage _chatmsg_list[MAX_CHAT_MESSAGES];
00059 static bool _chatmessage_dirty = false;
00060 static bool _chatmessage_visible = false;
00061
00062
00063
00064 static const PointDimension _chatmsg_box = {10, 30, 500, 150};
00065 static uint8 _chatmessage_backup[150 * 500 * 6];
00066
00067 static inline uint GetChatMessageCount()
00068 {
00069 uint i;
00070
00071 for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
00072 if (_chatmsg_list[i].message[0] == '\0') break;
00073 }
00074
00075 return i;
00076 }
00077
00078
00079
00080
00081
00082 void CDECL AddChatMessage(uint16 color, uint8 duration, const char *message, ...)
00083 {
00084 char buf[MAX_TEXTMESSAGE_LENGTH];
00085 const char *bufp;
00086 va_list va;
00087 uint msg_count;
00088 uint16 lines;
00089
00090 va_start(va, message);
00091 vsnprintf(buf, lengthof(buf), message, va);
00092 va_end(va);
00093
00094
00095 Utf8TrimString(buf, MAX_TEXTMESSAGE_LENGTH);
00096
00097
00098 lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1;
00099 if (lines >= MAX_CHAT_MESSAGES) return;
00100
00101 msg_count = GetChatMessageCount();
00102
00103 if (lines > MAX_CHAT_MESSAGES - msg_count) {
00104 int i = lines - (MAX_CHAT_MESSAGES - msg_count);
00105 memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i));
00106 msg_count = MAX_CHAT_MESSAGES - lines;
00107 }
00108
00109 for (bufp = buf; lines != 0; lines--) {
00110 ChatMessage *cmsg = &_chatmsg_list[msg_count++];
00111 ttd_strlcpy(cmsg->message, bufp, sizeof(cmsg->message));
00112
00113
00114
00115 cmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR;
00116 cmsg->end_date = _date + duration;
00117
00118 bufp += strlen(bufp) + 1;
00119 }
00120
00121 _chatmessage_dirty = true;
00122 }
00123
00124 void InitChatMessage()
00125 {
00126 uint i;
00127
00128 for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
00129 _chatmsg_list[i].message[0] = '\0';
00130 }
00131 }
00132
00134 void UndrawChatMessage()
00135 {
00136 if (_chatmessage_visible) {
00137 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149 if (_cursor.visible) {
00150 if (_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x &&
00151 _cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width &&
00152 _cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height &&
00153 _cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) {
00154 UndrawMouseCursor();
00155 }
00156 }
00157
00158 int x = _chatmsg_box.x;
00159 int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
00160 int width = _chatmsg_box.width;
00161 int height = _chatmsg_box.height;
00162 if (y < 0) {
00163 height = max(height + y, min(_chatmsg_box.height, _screen.height));
00164 y = 0;
00165 }
00166 if (x + width >= _screen.width) {
00167 width = _screen.width - x;
00168 }
00169 if (width <= 0 || height <= 0) return;
00170
00171 _chatmessage_visible = false;
00172
00173 blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
00174
00175 _video_driver->MakeDirty(x, y, width, height);
00176
00177 _chatmessage_dirty = true;
00178 }
00179 }
00180
00182 void ChatMessageDailyLoop()
00183 {
00184 uint i;
00185
00186 for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
00187 ChatMessage *cmsg = &_chatmsg_list[i];
00188 if (cmsg->message[0] == '\0') continue;
00189
00190
00191 if (cmsg->end_date < _date) {
00192
00193 if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1));
00194
00195
00196 _chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
00197 _chatmessage_dirty = true;
00198
00199
00200 i--;
00201 }
00202 }
00203 }
00204
00206 void DrawChatMessage()
00207 {
00208 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00209 if (!_chatmessage_dirty) return;
00210
00211
00212 UndrawChatMessage();
00213
00214 if (_iconsole_mode == ICONSOLE_FULL) return;
00215
00216
00217 uint count = GetChatMessageCount();
00218 if (count == 0) return;
00219
00220 int x = _chatmsg_box.x;
00221 int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
00222 int width = _chatmsg_box.width;
00223 int height = _chatmsg_box.height;
00224 if (y < 0) {
00225 height = max(height + y, min(_chatmsg_box.height, _screen.height));
00226 y = 0;
00227 }
00228 if (x + width >= _screen.width) {
00229 width = _screen.width - x;
00230 }
00231 if (width <= 0 || height <= 0) return;
00232
00233 assert(blitter->BufferSize(width, height) < (int)sizeof(_chatmessage_backup));
00234
00235
00236 blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
00237
00238 _cur_dpi = &_screen;
00239
00240
00241 GfxFillRect(
00242 _chatmsg_box.x,
00243 _screen.height - _chatmsg_box.y - count * 13 - 2,
00244 _chatmsg_box.x + _chatmsg_box.width - 1,
00245 _screen.height - _chatmsg_box.y - 2,
00246 PALETTE_TO_TRANSPARENT | (1 << USE_COLORTABLE)
00247 );
00248
00249
00250 for (uint y = 13; count-- != 0; y += 13) {
00251 DoDrawString(_chatmsg_list[count].message, _chatmsg_box.x + 3, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].color);
00252 }
00253
00254
00255 _video_driver->MakeDirty(x, y, width, height);
00256
00257 _chatmessage_visible = true;
00258 _chatmessage_dirty = false;
00259 }
00260
00261
00270 static void MarkTextEffectAreaDirty(TextEffect *te)
00271 {
00272
00273 MarkAllViewportsDirty(
00274 te->x,
00275 te->y - 1,
00276 (te->right - te->x)*2 + te->x + 1,
00277 (te->bottom - (te->y - 1)) * 2 + (te->y - 1) + 1
00278 );
00279 }
00280
00281 TextEffectID AddTextEffect(StringID msg, int x, int y, uint16 duration, TextEffectMode mode)
00282 {
00283 TextEffect *te;
00284 int w;
00285 char buffer[100];
00286 TextEffectID i;
00287
00288 if (_game_mode == GM_MENU) return INVALID_TE_ID;
00289
00290
00291 for (i = 0; i < _num_text_effects; i++) {
00292 if (_text_effect_list[i].string_id == INVALID_STRING_ID) break;
00293 }
00294
00295
00296 if (i == _num_text_effects) {
00297 _num_text_effects += 25;
00298 _text_effect_list = ReallocT<TextEffect>(_text_effect_list, _num_text_effects);
00299 for (; i < _num_text_effects; i++) _text_effect_list[i].string_id = INVALID_STRING_ID;
00300 i = _num_text_effects - 1;
00301 }
00302
00303 te = &_text_effect_list[i];
00304
00305
00306 te->string_id = msg;
00307 te->duration = duration;
00308 te->y = y - 5;
00309 te->bottom = y + 5;
00310 te->params_1 = GetDParam(0);
00311 te->params_2 = GetDParam(4);
00312 te->mode = mode;
00313
00314 GetString(buffer, msg, lastof(buffer));
00315 w = GetStringBoundingBox(buffer).width;
00316
00317 te->x = x - (w >> 1);
00318 te->right = x + (w >> 1) - 1;
00319 MarkTextEffectAreaDirty(te);
00320
00321 return i;
00322 }
00323
00324 void UpdateTextEffect(TextEffectID te_id, StringID msg)
00325 {
00326 assert(te_id < _num_text_effects);
00327 TextEffect *te;
00328
00329
00330 te = &_text_effect_list[te_id];
00331 te->string_id = msg;
00332 te->params_1 = GetDParam(0);
00333 te->params_2 = GetDParam(4);
00334
00335
00336 char buffer[100];
00337 GetString(buffer, msg, lastof(buffer));
00338 int w = GetStringBoundingBox(buffer).width;
00339
00340
00341 int right_new = te->x + w;
00342 if (te->right < right_new) te->right = right_new;
00343
00344 MarkTextEffectAreaDirty(te);
00345 }
00346
00347 void RemoveTextEffect(TextEffectID te_id)
00348 {
00349 assert(te_id < _num_text_effects);
00350 TextEffect *te;
00351
00352 te = &_text_effect_list[te_id];
00353 MarkTextEffectAreaDirty(te);
00354 te->string_id = INVALID_STRING_ID;
00355 }
00356
00357 static void MoveTextEffect(TextEffect *te)
00358 {
00359
00360 if (te->duration == 0xFFFF) return;
00361 if (te->duration < 8) {
00362 te->string_id = INVALID_STRING_ID;
00363 } else {
00364 te->duration -= 8;
00365 te->y--;
00366 te->bottom--;
00367 }
00368 MarkTextEffectAreaDirty(te);
00369 }
00370
00371 void MoveAllTextEffects()
00372 {
00373 for (TextEffectID i = 0; i < _num_text_effects; i++) {
00374 TextEffect *te = &_text_effect_list[i];
00375 if (te->string_id != INVALID_STRING_ID && te->mode == TE_RISING) MoveTextEffect(te);
00376 }
00377 }
00378
00379 void InitTextEffects()
00380 {
00381 if (_text_effect_list == NULL) _text_effect_list = MallocT<TextEffect>(_num_text_effects);
00382
00383 for (TextEffectID i = 0; i < _num_text_effects; i++) _text_effect_list[i].string_id = INVALID_STRING_ID;
00384 }
00385
00386 void DrawTextEffects(DrawPixelInfo *dpi)
00387 {
00388 switch (dpi->zoom) {
00389 case ZOOM_LVL_NORMAL:
00390 for (TextEffectID i = 0; i < _num_text_effects; i++) {
00391 TextEffect *te = &_text_effect_list[i];
00392 if (te->string_id != INVALID_STRING_ID &&
00393 dpi->left <= te->right &&
00394 dpi->top <= te->bottom &&
00395 dpi->left + dpi->width > te->x &&
00396 dpi->top + dpi->height > te->y) {
00397 if (te->mode == TE_RISING || (_patches.loading_indicators && !IsTransparencySet(TO_LOADING))) {
00398 AddStringToDraw(te->x, te->y, te->string_id, te->params_1, te->params_2);
00399 }
00400 }
00401 }
00402 break;
00403
00404 case ZOOM_LVL_OUT_2X:
00405 for (TextEffectID i = 0; i < _num_text_effects; i++) {
00406 TextEffect *te = &_text_effect_list[i];
00407 if (te->string_id != INVALID_STRING_ID &&
00408 dpi->left <= te->right * 2 - te->x &&
00409 dpi->top <= te->bottom * 2 - te->y &&
00410 dpi->left + dpi->width > te->x &&
00411 dpi->top + dpi->height > te->y) {
00412 if (te->mode == TE_RISING || (_patches.loading_indicators && !IsTransparencySet(TO_LOADING))) {
00413 AddStringToDraw(te->x, te->y, (StringID)(te->string_id - 1), te->params_1, te->params_2);
00414 }
00415 }
00416 }
00417 break;
00418
00419 case ZOOM_LVL_OUT_4X:
00420 case ZOOM_LVL_OUT_8X:
00421 break;
00422
00423 default: NOT_REACHED();
00424 }
00425 }
00426
00428 TileIndex *_animated_tile_list = NULL;
00430 uint _animated_tile_count = 0;
00432 static uint _animated_tile_allocated = 0;
00433
00438 void DeleteAnimatedTile(TileIndex tile)
00439 {
00440 for (TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
00441 if (tile == *ti) {
00442
00443
00444
00445
00446 memmove(ti, ti + 1, (_animated_tile_list + _animated_tile_count - (ti + 1)) * sizeof(*ti));
00447 _animated_tile_count--;
00448 MarkTileDirtyByTile(tile);
00449 return;
00450 }
00451 }
00452 }
00453
00459 void AddAnimatedTile(TileIndex tile)
00460 {
00461 MarkTileDirtyByTile(tile);
00462
00463 for (const TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
00464 if (tile == *ti) return;
00465 }
00466
00467
00468 if (_animated_tile_count == _animated_tile_allocated) {
00469 _animated_tile_allocated *= 2;
00470 _animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
00471 }
00472
00473 _animated_tile_list[_animated_tile_count] = tile;
00474 _animated_tile_count++;
00475 }
00476
00480 void AnimateAnimatedTiles()
00481 {
00482 const TileIndex *ti = _animated_tile_list;
00483 while (ti < _animated_tile_list + _animated_tile_count) {
00484 const TileIndex curr = *ti;
00485 AnimateTile(curr);
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495 if (*ti == curr) ++ti;
00496 }
00497 }
00498
00502 void InitializeAnimatedTiles()
00503 {
00504 _animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, 256);
00505 _animated_tile_count = 0;
00506 _animated_tile_allocated = 256;
00507 }
00508
00512 static void Save_ANIT()
00513 {
00514 SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list));
00515 SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
00516 }
00517
00521 static void Load_ANIT()
00522 {
00523
00524 if (CheckSavegameVersion(80)) {
00525
00526 SlArray(_animated_tile_list, 256, CheckSavegameVersion(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32);
00527
00528 for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) {
00529 if (_animated_tile_list[_animated_tile_count] == 0) break;
00530 }
00531 return;
00532 }
00533
00534 _animated_tile_count = SlGetFieldLength() / sizeof(*_animated_tile_list);
00535
00536
00537 _animated_tile_allocated = 256;
00538 while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2;
00539
00540 _animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
00541 SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
00542 }
00543
00548 extern const ChunkHandler _animated_tile_chunk_handlers[] = {
00549 { 'ANIT', Save_ANIT, Load_ANIT, CH_RIFF | CH_LAST},
00550 };