~ubuntu-branches/debian/squeeze/openttd/squeeze

« back to all changes in this revision

Viewing changes to src/texteff.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jordi Mallach, Matthijs Kooijman, Jordi Mallach
  • Date: 2009-04-15 18:22:10 UTC
  • mfrom: (1.1.6 upstream) (2.1.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090415182210-22ktb8kdbp2tf3bm
[ Matthijs Kooijman ]
* New upstream release.
* Remove Debian specific desktop file, upstream provides one now. 
* Add debian/watch file.

[ Jordi Mallach ]
* Bump Standards-Version to 3.8.1, with no changes required.
* Move to debhelper compat 7. Bump Build-Depends accordingly.
* Use dh_prep.
* Add "set -e" to config script.
* Remove a few extra doc files that get installed by upstream Makefile.
* Add more complete copyright information.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: texteff.cpp 11834 2008-01-13 14:37:30Z rubidium $ */
 
1
/* $Id: texteff.cpp 15299 2009-01-31 20:16:06Z smatz $ */
2
2
 
3
 
/** @file texteff.cpp */
 
3
/** @file texteff.cpp Handling of text effects. */
4
4
 
5
5
#include "stdafx.h"
6
6
#include "openttd.h"
7
 
#include "tile_cmd.h"
8
 
#include "landscape.h"
9
 
#include "gfx_func.h"
10
 
#include "saveload.h"
11
 
#include "console.h"
12
 
#include "variables.h"
13
 
#include "blitter/factory.hpp"
 
7
#include "strings_type.h"
14
8
#include "texteff.hpp"
15
 
#include "video/video_driver.hpp"
 
9
#include "core/bitmath_func.hpp"
16
10
#include "transparency.h"
17
11
#include "strings_func.h"
18
12
#include "core/alloc_func.hpp"
19
 
#include "date_func.h"
20
13
#include "functions.h"
21
14
#include "viewport_func.h"
22
15
#include "settings_type.h"
23
16
 
24
 
#include "table/sprites.h"
25
 
 
26
 
#include <stdarg.h> /* va_list */
27
 
 
28
17
enum {
29
 
        MAX_TEXTMESSAGE_LENGTH = 200,
30
 
        INIT_NUM_TEXT_MESSAGES =  20,
31
 
        MAX_CHAT_MESSAGES      =  10,
 
18
        INIT_NUM_TEXT_EFFECTS  =  20,
32
19
};
33
20
 
34
21
struct TextEffect {
43
30
        TextEffectMode mode;
44
31
};
45
32
 
46
 
 
47
 
struct ChatMessage {
48
 
        char message[MAX_TEXTMESSAGE_LENGTH];
49
 
        uint16 color;
50
 
        Date end_date;
51
 
};
52
 
 
53
33
/* used for text effects */
54
34
static TextEffect *_text_effect_list = NULL;
55
 
static uint16 _num_text_effects = INIT_NUM_TEXT_MESSAGES;
56
 
 
57
 
/* used for chat window */
58
 
static ChatMessage _chatmsg_list[MAX_CHAT_MESSAGES];
59
 
static bool _chatmessage_dirty = false;
60
 
static bool _chatmessage_visible = false;
61
 
 
62
 
/* The chatbox grows from the bottom so the coordinates are pixels from
63
 
 * the left and pixels from the bottom. The height is the maximum height */
64
 
static const PointDimension _chatmsg_box = {10, 30, 500, 150};
65
 
static uint8 _chatmessage_backup[150 * 500 * 6]; // (height * width)
66
 
 
67
 
static inline uint GetChatMessageCount()
68
 
{
69
 
        uint i;
70
 
 
71
 
        for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
72
 
                if (_chatmsg_list[i].message[0] == '\0') break;
73
 
        }
74
 
 
75
 
        return i;
76
 
}
77
 
 
78
 
/* Add a text message to the 'chat window' to be shown
79
 
 * @param color The colour this message is to be shown in
80
 
 * @param duration The duration of the chat message in game-days
81
 
 * @param message message itself in printf() style */
82
 
void CDECL AddChatMessage(uint16 color, uint8 duration, const char *message, ...)
83
 
{
84
 
        char buf[MAX_TEXTMESSAGE_LENGTH];
85
 
        const char *bufp;
86
 
        va_list va;
87
 
        uint msg_count;
88
 
        uint16 lines;
89
 
 
90
 
        va_start(va, message);
91
 
        vsnprintf(buf, lengthof(buf), message, va);
92
 
        va_end(va);
93
 
 
94
 
 
95
 
        Utf8TrimString(buf, MAX_TEXTMESSAGE_LENGTH);
96
 
 
97
 
        /* Force linebreaks for strings that are too long */
98
 
        lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1;
99
 
        if (lines >= MAX_CHAT_MESSAGES) return;
100
 
 
101
 
        msg_count = GetChatMessageCount();
102
 
        /* We want to add more chat messages than there is free space for, remove 'old' */
103
 
        if (lines > MAX_CHAT_MESSAGES - msg_count) {
104
 
                int i = lines - (MAX_CHAT_MESSAGES - msg_count);
105
 
                memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i));
106
 
                msg_count = MAX_CHAT_MESSAGES - lines;
107
 
        }
108
 
 
109
 
        for (bufp = buf; lines != 0; lines--) {
110
 
                ChatMessage *cmsg = &_chatmsg_list[msg_count++];
111
 
                ttd_strlcpy(cmsg->message, bufp, sizeof(cmsg->message));
112
 
 
113
 
                /* The default colour for a message is player colour. Replace this with
114
 
                 * white for any additional lines */
115
 
                cmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR;
116
 
                cmsg->end_date = _date + duration;
117
 
 
118
 
                bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string
119
 
        }
120
 
 
121
 
        _chatmessage_dirty = true;
122
 
}
123
 
 
124
 
void InitChatMessage()
125
 
{
126
 
        uint i;
127
 
 
128
 
        for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
129
 
                _chatmsg_list[i].message[0] = '\0';
130
 
        }
131
 
}
132
 
 
133
 
/** Hide the chatbox */
134
 
void UndrawChatMessage()
135
 
{
136
 
        if (_chatmessage_visible) {
137
 
                Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
138
 
                /* Sometimes we also need to hide the cursor
139
 
                 *   This is because both textmessage and the cursor take a shot of the
140
 
                 *   screen before drawing.
141
 
                 *   Now the textmessage takes his shot and paints his data before the cursor
142
 
                 *   does, so in the shot of the cursor is the screen-data of the textmessage
143
 
                 *   included when the cursor hangs somewhere over the textmessage. To
144
 
                 *   avoid wrong repaints, we undraw the cursor in that case, and everything
145
 
                 *   looks nicely ;)
146
 
                 * (and now hope this story above makes sense to you ;))
147
 
                 */
148
 
 
149
 
                if (_cursor.visible) {
150
 
                        if (_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x &&
151
 
                                _cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width &&
152
 
                                _cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height &&
153
 
                                _cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) {
154
 
                                UndrawMouseCursor();
155
 
                        }
156
 
                }
157
 
 
158
 
                int x      = _chatmsg_box.x;
159
 
                int y      = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
160
 
                int width  = _chatmsg_box.width;
161
 
                int height = _chatmsg_box.height;
162
 
                if (y < 0) {
163
 
                        height = max(height + y, min(_chatmsg_box.height, _screen.height));
164
 
                        y = 0;
165
 
                }
166
 
                if (x + width >= _screen.width) {
167
 
                        width = _screen.width - x;
168
 
                }
169
 
                if (width <= 0 || height <= 0) return;
170
 
 
171
 
                _chatmessage_visible = false;
172
 
                /* Put our 'shot' back to the screen */
173
 
                blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
174
 
                /* And make sure it is updated next time */
175
 
                _video_driver->MakeDirty(x, y, width, height);
176
 
 
177
 
                _chatmessage_dirty = true;
178
 
        }
179
 
}
180
 
 
181
 
/** Check if a message is expired every day */
182
 
void ChatMessageDailyLoop()
183
 
{
184
 
        uint i;
185
 
 
186
 
        for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
187
 
                ChatMessage *cmsg = &_chatmsg_list[i];
188
 
                if (cmsg->message[0] == '\0') continue;
189
 
 
190
 
                /* Message has expired, remove from the list */
191
 
                if (cmsg->end_date < _date) {
192
 
                        /* Move the remaining messages over the current message */
193
 
                        if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1));
194
 
 
195
 
                        /* Mark the last item as empty */
196
 
                        _chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
197
 
                        _chatmessage_dirty = true;
198
 
 
199
 
                        /* Go one item back, because we moved the array 1 to the left */
200
 
                        i--;
201
 
                }
202
 
        }
203
 
}
204
 
 
205
 
/** Draw the chat message-box */
206
 
void DrawChatMessage()
207
 
{
208
 
        Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
209
 
        if (!_chatmessage_dirty) return;
210
 
 
211
 
        /* First undraw if needed */
212
 
        UndrawChatMessage();
213
 
 
214
 
        if (_iconsole_mode == ICONSOLE_FULL) return;
215
 
 
216
 
        /* Check if we have anything to draw at all */
217
 
        uint count = GetChatMessageCount();
218
 
        if (count == 0) return;
219
 
 
220
 
        int x      = _chatmsg_box.x;
221
 
        int y      = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
222
 
        int width  = _chatmsg_box.width;
223
 
        int height = _chatmsg_box.height;
224
 
        if (y < 0) {
225
 
                height = max(height + y, min(_chatmsg_box.height, _screen.height));
226
 
                y = 0;
227
 
        }
228
 
        if (x + width >= _screen.width) {
229
 
                width = _screen.width - x;
230
 
        }
231
 
        if (width <= 0 || height <= 0) return;
232
 
 
233
 
        assert(blitter->BufferSize(width, height) < (int)sizeof(_chatmessage_backup));
234
 
 
235
 
        /* Make a copy of the screen as it is before painting (for undraw) */
236
 
        blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
237
 
 
238
 
        _cur_dpi = &_screen; // switch to _screen painting
239
 
 
240
 
        /* Paint a half-transparent box behind the chat messages */
241
 
        GfxFillRect(
242
 
                        _chatmsg_box.x,
243
 
                        _screen.height - _chatmsg_box.y - count * 13 - 2,
244
 
                        _chatmsg_box.x + _chatmsg_box.width - 1,
245
 
                        _screen.height - _chatmsg_box.y - 2,
246
 
                        PALETTE_TO_TRANSPARENT | (1 << USE_COLORTABLE) // black, but with some alpha for background
247
 
                );
248
 
 
249
 
        /* Paint the chat messages starting with the lowest at the bottom */
250
 
        for (uint y = 13; count-- != 0; y += 13) {
251
 
                DoDrawString(_chatmsg_list[count].message, _chatmsg_box.x + 3, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].color);
252
 
        }
253
 
 
254
 
        /* Make sure the data is updated next flush */
255
 
        _video_driver->MakeDirty(x, y, width, height);
256
 
 
257
 
        _chatmessage_visible = true;
258
 
        _chatmessage_dirty = false;
259
 
}
 
35
static uint16 _num_text_effects = INIT_NUM_TEXT_EFFECTS;
260
36
 
261
37
/* Text Effects */
262
38
/**
394
170
                                                dpi->top  <= te->bottom &&
395
171
                                                dpi->left + dpi->width  > te->x &&
396
172
                                                dpi->top  + dpi->height > te->y) {
397
 
                                        if (te->mode == TE_RISING || (_patches.loading_indicators && !IsTransparencySet(TO_LOADING))) {
 
173
                                        if (te->mode == TE_RISING || (_settings_client.gui.loading_indicators && !IsTransparencySet(TO_LOADING))) {
398
174
                                                AddStringToDraw(te->x, te->y, te->string_id, te->params_1, te->params_2);
399
175
                                        }
400
176
                                }
409
185
                                                dpi->top  <= te->bottom * 2 - te->y &&
410
186
                                                dpi->left + dpi->width  > te->x &&
411
187
                                                dpi->top  + dpi->height > te->y) {
412
 
                                        if (te->mode == TE_RISING || (_patches.loading_indicators && !IsTransparencySet(TO_LOADING))) {
 
188
                                        if (te->mode == TE_RISING || (_settings_client.gui.loading_indicators && !IsTransparencySet(TO_LOADING))) {
413
189
                                                AddStringToDraw(te->x, te->y, (StringID)(te->string_id - 1), te->params_1, te->params_2);
414
190
                                        }
415
191
                                }
423
199
                default: NOT_REACHED();
424
200
        }
425
201
}
426
 
 
427
 
/** The table/list with animated tiles. */
428
 
TileIndex *_animated_tile_list = NULL;
429
 
/** The number of animated tiles in the current state. */
430
 
uint _animated_tile_count = 0;
431
 
/** The number of slots for animated tiles allocated currently. */
432
 
static uint _animated_tile_allocated = 0;
433
 
 
434
 
/**
435
 
 * Removes the given tile from the animated tile table.
436
 
 * @param tile the tile to remove
437
 
 */
438
 
void DeleteAnimatedTile(TileIndex tile)
439
 
{
440
 
        for (TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
441
 
                if (tile == *ti) {
442
 
                        /* Remove the hole
443
 
                         * The order of the remaining elements must stay the same, otherwise the animation loop
444
 
                         * may miss a tile; that's why we must use memmove instead of just moving the last element.
445
 
                         */
446
 
                        memmove(ti, ti + 1, (_animated_tile_list + _animated_tile_count - (ti + 1)) * sizeof(*ti));
447
 
                        _animated_tile_count--;
448
 
                        MarkTileDirtyByTile(tile);
449
 
                        return;
450
 
                }
451
 
        }
452
 
}
453
 
 
454
 
/**
455
 
 * Add the given tile to the animated tile table (if it does not exist
456
 
 * on that table yet). Also increases the size of the table if necessary.
457
 
 * @param tile the tile to make animated
458
 
 */
459
 
void AddAnimatedTile(TileIndex tile)
460
 
{
461
 
        MarkTileDirtyByTile(tile);
462
 
 
463
 
        for (const TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
464
 
                if (tile == *ti) return;
465
 
        }
466
 
 
467
 
        /* Table not large enough, so make it larger */
468
 
        if (_animated_tile_count == _animated_tile_allocated) {
469
 
                _animated_tile_allocated *= 2;
470
 
                _animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
471
 
        }
472
 
 
473
 
        _animated_tile_list[_animated_tile_count] = tile;
474
 
        _animated_tile_count++;
475
 
}
476
 
 
477
 
/**
478
 
 * Animate all tiles in the animated tile list, i.e.\ call AnimateTile on them.
479
 
 */
480
 
void AnimateAnimatedTiles()
481
 
{
482
 
        const TileIndex *ti = _animated_tile_list;
483
 
        while (ti < _animated_tile_list + _animated_tile_count) {
484
 
                const TileIndex curr = *ti;
485
 
                AnimateTile(curr);
486
 
                /* During the AnimateTile call, DeleteAnimatedTile could have been called,
487
 
                 * deleting an element we've already processed and pushing the rest one
488
 
                 * slot to the left. We can detect this by checking whether the index
489
 
                 * in the current slot has changed - if it has, an element has been deleted,
490
 
                 * and we should process the current slot again instead of going forward.
491
 
                 * NOTE: this will still break if more than one animated tile is being
492
 
                 *       deleted during the same AnimateTile call, but no code seems to
493
 
                 *       be doing this anyway.
494
 
                 */
495
 
                if (*ti == curr) ++ti;
496
 
        }
497
 
}
498
 
 
499
 
/**
500
 
 * Initialize all animated tile variables to some known begin point
501
 
 */
502
 
void InitializeAnimatedTiles()
503
 
{
504
 
        _animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, 256);
505
 
        _animated_tile_count = 0;
506
 
        _animated_tile_allocated = 256;
507
 
}
508
 
 
509
 
/**
510
 
 * Save the ANIT chunk.
511
 
 */
512
 
static void Save_ANIT()
513
 
{
514
 
        SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list));
515
 
        SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
516
 
}
517
 
 
518
 
/**
519
 
 * Load the ANIT chunk; the chunk containing the animated tiles.
520
 
 */
521
 
static void Load_ANIT()
522
 
{
523
 
        /* Before version 80 we did NOT have a variable length animated tile table */
524
 
        if (CheckSavegameVersion(80)) {
525
 
                /* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */
526
 
                SlArray(_animated_tile_list, 256, CheckSavegameVersion(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32);
527
 
 
528
 
                for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) {
529
 
                        if (_animated_tile_list[_animated_tile_count] == 0) break;
530
 
                }
531
 
                return;
532
 
        }
533
 
 
534
 
        _animated_tile_count = SlGetFieldLength() / sizeof(*_animated_tile_list);
535
 
 
536
 
        /* Determine a nice rounded size for the amount of allocated tiles */
537
 
        _animated_tile_allocated = 256;
538
 
        while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2;
539
 
 
540
 
        _animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
541
 
        SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
542
 
}
543
 
 
544
 
/**
545
 
 * "Definition" imported by the saveload code to be able to load and save
546
 
 * the animated tile table.
547
 
 */
548
 
extern const ChunkHandler _animated_tile_chunk_handlers[] = {
549
 
        { 'ANIT', Save_ANIT, Load_ANIT, CH_RIFF | CH_LAST},
550
 
};