src/texteff.cpp
changeset 9898 75347c78b276
parent 9607 5a5728fb702a
equal deleted inserted replaced
9897:4d9a6ff6703e 9898:75347c78b276
     4 
     4 
     5 #include "stdafx.h"
     5 #include "stdafx.h"
     6 #include "openttd.h"
     6 #include "openttd.h"
     7 #include "landscape.h"
     7 #include "landscape.h"
     8 #include "gfx_func.h"
     8 #include "gfx_func.h"
     9 #include "console_func.h"
       
    10 #include "variables.h"
     9 #include "variables.h"
    11 #include "blitter/factory.hpp"
       
    12 #include "texteff.hpp"
    10 #include "texteff.hpp"
    13 #include "video/video_driver.hpp"
    11 #include "core/bitmath_func.hpp"
    14 #include "transparency.h"
    12 #include "transparency.h"
    15 #include "strings_func.h"
    13 #include "strings_func.h"
    16 #include "core/alloc_func.hpp"
    14 #include "core/alloc_func.hpp"
    17 #include "date_func.h"
       
    18 #include "functions.h"
    15 #include "functions.h"
    19 #include "viewport_func.h"
    16 #include "viewport_func.h"
    20 #include "settings_type.h"
    17 #include "settings_type.h"
    21 
    18 
    22 #include "table/sprites.h"
       
    23 
       
    24 #include <stdarg.h> /* va_list */
       
    25 
       
    26 enum {
    19 enum {
    27 	MAX_TEXTMESSAGE_LENGTH = 200,
    20 	INIT_NUM_TEXT_EFFECTS  =  20,
    28 	INIT_NUM_TEXT_MESSAGES =  20,
       
    29 	MAX_CHAT_MESSAGES      =  10,
       
    30 };
    21 };
    31 
    22 
    32 struct TextEffect {
    23 struct TextEffect {
    33 	StringID string_id;
    24 	StringID string_id;
    34 	int32 x;
    25 	int32 x;
    39 	uint64 params_1;
    30 	uint64 params_1;
    40 	uint64 params_2;
    31 	uint64 params_2;
    41 	TextEffectMode mode;
    32 	TextEffectMode mode;
    42 };
    33 };
    43 
    34 
    44 
       
    45 struct ChatMessage {
       
    46 	char message[MAX_TEXTMESSAGE_LENGTH];
       
    47 	uint16 color;
       
    48 	Date end_date;
       
    49 };
       
    50 
       
    51 /* used for text effects */
    35 /* used for text effects */
    52 static TextEffect *_text_effect_list = NULL;
    36 static TextEffect *_text_effect_list = NULL;
    53 static uint16 _num_text_effects = INIT_NUM_TEXT_MESSAGES;
    37 static uint16 _num_text_effects = INIT_NUM_TEXT_EFFECTS;
    54 
       
    55 /* used for chat window */
       
    56 static ChatMessage _chatmsg_list[MAX_CHAT_MESSAGES];
       
    57 static bool _chatmessage_dirty = false;
       
    58 static bool _chatmessage_visible = false;
       
    59 
       
    60 /* The chatbox grows from the bottom so the coordinates are pixels from
       
    61  * the left and pixels from the bottom. The height is the maximum height */
       
    62 static const PointDimension _chatmsg_box = {10, 30, 500, 150};
       
    63 static uint8 _chatmessage_backup[150 * 500 * 6]; // (height * width)
       
    64 
       
    65 static inline uint GetChatMessageCount()
       
    66 {
       
    67 	uint i;
       
    68 
       
    69 	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
       
    70 		if (_chatmsg_list[i].message[0] == '\0') break;
       
    71 	}
       
    72 
       
    73 	return i;
       
    74 }
       
    75 
       
    76 /* Add a text message to the 'chat window' to be shown
       
    77  * @param color The colour this message is to be shown in
       
    78  * @param duration The duration of the chat message in game-days
       
    79  * @param message message itself in printf() style */
       
    80 void CDECL AddChatMessage(uint16 color, uint8 duration, const char *message, ...)
       
    81 {
       
    82 	char buf[MAX_TEXTMESSAGE_LENGTH];
       
    83 	const char *bufp;
       
    84 	va_list va;
       
    85 	uint msg_count;
       
    86 	uint16 lines;
       
    87 
       
    88 	va_start(va, message);
       
    89 	vsnprintf(buf, lengthof(buf), message, va);
       
    90 	va_end(va);
       
    91 
       
    92 
       
    93 	Utf8TrimString(buf, MAX_TEXTMESSAGE_LENGTH);
       
    94 
       
    95 	/* Force linebreaks for strings that are too long */
       
    96 	lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1;
       
    97 	if (lines >= MAX_CHAT_MESSAGES) return;
       
    98 
       
    99 	msg_count = GetChatMessageCount();
       
   100 	/* We want to add more chat messages than there is free space for, remove 'old' */
       
   101 	if (lines > MAX_CHAT_MESSAGES - msg_count) {
       
   102 		int i = lines - (MAX_CHAT_MESSAGES - msg_count);
       
   103 		memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i));
       
   104 		msg_count = MAX_CHAT_MESSAGES - lines;
       
   105 	}
       
   106 
       
   107 	for (bufp = buf; lines != 0; lines--) {
       
   108 		ChatMessage *cmsg = &_chatmsg_list[msg_count++];
       
   109 		ttd_strlcpy(cmsg->message, bufp, sizeof(cmsg->message));
       
   110 
       
   111 		/* The default colour for a message is player colour. Replace this with
       
   112 		 * white for any additional lines */
       
   113 		cmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR;
       
   114 		cmsg->end_date = _date + duration;
       
   115 
       
   116 		bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string
       
   117 	}
       
   118 
       
   119 	_chatmessage_dirty = true;
       
   120 }
       
   121 
       
   122 void InitChatMessage()
       
   123 {
       
   124 	uint i;
       
   125 
       
   126 	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
       
   127 		_chatmsg_list[i].message[0] = '\0';
       
   128 	}
       
   129 }
       
   130 
       
   131 /** Hide the chatbox */
       
   132 void UndrawChatMessage()
       
   133 {
       
   134 	if (_chatmessage_visible) {
       
   135 		Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
       
   136 		/* Sometimes we also need to hide the cursor
       
   137 		 *   This is because both textmessage and the cursor take a shot of the
       
   138 		 *   screen before drawing.
       
   139 		 *   Now the textmessage takes his shot and paints his data before the cursor
       
   140 		 *   does, so in the shot of the cursor is the screen-data of the textmessage
       
   141 		 *   included when the cursor hangs somewhere over the textmessage. To
       
   142 		 *   avoid wrong repaints, we undraw the cursor in that case, and everything
       
   143 		 *   looks nicely ;)
       
   144 		 * (and now hope this story above makes sense to you ;))
       
   145 		 */
       
   146 
       
   147 		if (_cursor.visible) {
       
   148 			if (_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x &&
       
   149 				_cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width &&
       
   150 				_cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height &&
       
   151 				_cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) {
       
   152 				UndrawMouseCursor();
       
   153 			}
       
   154 		}
       
   155 
       
   156 		int x      = _chatmsg_box.x;
       
   157 		int y      = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
       
   158 		int width  = _chatmsg_box.width;
       
   159 		int height = _chatmsg_box.height;
       
   160 		if (y < 0) {
       
   161 			height = max(height + y, min(_chatmsg_box.height, _screen.height));
       
   162 			y = 0;
       
   163 		}
       
   164 		if (x + width >= _screen.width) {
       
   165 			width = _screen.width - x;
       
   166 		}
       
   167 		if (width <= 0 || height <= 0) return;
       
   168 
       
   169 		_chatmessage_visible = false;
       
   170 		/* Put our 'shot' back to the screen */
       
   171 		blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
       
   172 		/* And make sure it is updated next time */
       
   173 		_video_driver->MakeDirty(x, y, width, height);
       
   174 
       
   175 		_chatmessage_dirty = true;
       
   176 	}
       
   177 }
       
   178 
       
   179 /** Check if a message is expired every day */
       
   180 void ChatMessageDailyLoop()
       
   181 {
       
   182 	uint i;
       
   183 
       
   184 	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
       
   185 		ChatMessage *cmsg = &_chatmsg_list[i];
       
   186 		if (cmsg->message[0] == '\0') continue;
       
   187 
       
   188 		/* Message has expired, remove from the list */
       
   189 		if (cmsg->end_date < _date) {
       
   190 			/* Move the remaining messages over the current message */
       
   191 			if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1));
       
   192 
       
   193 			/* Mark the last item as empty */
       
   194 			_chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
       
   195 			_chatmessage_dirty = true;
       
   196 
       
   197 			/* Go one item back, because we moved the array 1 to the left */
       
   198 			i--;
       
   199 		}
       
   200 	}
       
   201 }
       
   202 
       
   203 /** Draw the chat message-box */
       
   204 void DrawChatMessage()
       
   205 {
       
   206 	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
       
   207 	if (!_chatmessage_dirty) return;
       
   208 
       
   209 	/* First undraw if needed */
       
   210 	UndrawChatMessage();
       
   211 
       
   212 	if (_iconsole_mode == ICONSOLE_FULL) return;
       
   213 
       
   214 	/* Check if we have anything to draw at all */
       
   215 	uint count = GetChatMessageCount();
       
   216 	if (count == 0) return;
       
   217 
       
   218 	int x      = _chatmsg_box.x;
       
   219 	int y      = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
       
   220 	int width  = _chatmsg_box.width;
       
   221 	int height = _chatmsg_box.height;
       
   222 	if (y < 0) {
       
   223 		height = max(height + y, min(_chatmsg_box.height, _screen.height));
       
   224 		y = 0;
       
   225 	}
       
   226 	if (x + width >= _screen.width) {
       
   227 		width = _screen.width - x;
       
   228 	}
       
   229 	if (width <= 0 || height <= 0) return;
       
   230 
       
   231 	assert(blitter->BufferSize(width, height) < (int)sizeof(_chatmessage_backup));
       
   232 
       
   233 	/* Make a copy of the screen as it is before painting (for undraw) */
       
   234 	blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
       
   235 
       
   236 	_cur_dpi = &_screen; // switch to _screen painting
       
   237 
       
   238 	/* Paint a half-transparent box behind the chat messages */
       
   239 	GfxFillRect(
       
   240 			_chatmsg_box.x,
       
   241 			_screen.height - _chatmsg_box.y - count * 13 - 2,
       
   242 			_chatmsg_box.x + _chatmsg_box.width - 1,
       
   243 			_screen.height - _chatmsg_box.y - 2,
       
   244 			PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOR // black, but with some alpha for background
       
   245 		);
       
   246 
       
   247 	/* Paint the chat messages starting with the lowest at the bottom */
       
   248 	for (uint y = 13; count-- != 0; y += 13) {
       
   249 		DoDrawString(_chatmsg_list[count].message, _chatmsg_box.x + 3, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].color);
       
   250 	}
       
   251 
       
   252 	/* Make sure the data is updated next flush */
       
   253 	_video_driver->MakeDirty(x, y, width, height);
       
   254 
       
   255 	_chatmessage_visible = true;
       
   256 	_chatmessage_dirty = false;
       
   257 }
       
   258 
    38 
   259 /* Text Effects */
    39 /* Text Effects */
   260 /**
    40 /**
   261  * Mark the area of the text effect as dirty.
    41  * Mark the area of the text effect as dirty.
   262  *
    42  *