src/texteff.cpp
branchNewGRF_ports
changeset 6720 35756db7e577
parent 6719 4cc327ad39d5
child 6868 7eb395287b3d
equal deleted inserted replaced
6719:4cc327ad39d5 6720:35756db7e577
     9 #include "strings.h"
     9 #include "strings.h"
    10 #include "gfx.h"
    10 #include "gfx.h"
    11 #include "landscape.h"
    11 #include "landscape.h"
    12 #include "viewport.h"
    12 #include "viewport.h"
    13 #include "saveload.h"
    13 #include "saveload.h"
    14 #include "hal.h"
       
    15 #include "console.h"
    14 #include "console.h"
    16 #include "string.h"
    15 #include "string.h"
    17 #include "variables.h"
    16 #include "variables.h"
    18 #include "table/sprites.h"
    17 #include "table/sprites.h"
       
    18 #include "blitter/factory.hpp"
    19 #include <stdarg.h> /* va_list */
    19 #include <stdarg.h> /* va_list */
    20 #include "date.h"
    20 #include "date.h"
       
    21 #include "texteff.hpp"
       
    22 #include "video/video_driver.hpp"
    21 
    23 
    22 enum {
    24 enum {
    23 	MAX_TEXTMESSAGE_LENGTH = 200,
    25 	MAX_TEXTMESSAGE_LENGTH = 200,
    24 	MAX_TEXT_MESSAGES      =  30,
    26 	INIT_NUM_TEXT_MESSAGES =  20,
    25 	MAX_CHAT_MESSAGES      =  10,
    27 	MAX_CHAT_MESSAGES      =  10,
    26 	MAX_ANIMATED_TILES     = 256,
    28 	MAX_ANIMATED_TILES     = 256,
    27 };
    29 };
    28 
    30 
    29 struct TextEffect {
    31 struct TextEffect {
    31 	int32 x;
    33 	int32 x;
    32 	int32 y;
    34 	int32 y;
    33 	int32 right;
    35 	int32 right;
    34 	int32 bottom;
    36 	int32 bottom;
    35 	uint16 duration;
    37 	uint16 duration;
    36 	uint32 params_1;
    38 	uint64 params_1;
    37 	uint32 params_2;
    39 	uint64 params_2;
       
    40 	TextEffectMode mode;
    38 };
    41 };
    39 
    42 
    40 
    43 
    41 struct TextMessage {
    44 struct TextMessage {
    42 	char message[MAX_TEXTMESSAGE_LENGTH];
    45 	char message[MAX_TEXTMESSAGE_LENGTH];
    43 	uint16 color;
    46 	uint16 color;
    44 	Date end_date;
    47 	Date end_date;
    45 };
    48 };
    46 
    49 
    47 static TextEffect _text_effect_list[MAX_TEXT_MESSAGES];
    50 static TextEffect *_text_effect_list = NULL;
    48 static TextMessage _textmsg_list[MAX_CHAT_MESSAGES];
    51 static TextMessage _textmsg_list[MAX_CHAT_MESSAGES];
    49 TileIndex _animated_tile_list[MAX_ANIMATED_TILES];
    52 TileIndex _animated_tile_list[MAX_ANIMATED_TILES];
    50 
    53 
    51 static bool _textmessage_dirty = false;
    54 static bool _textmessage_dirty = false;
    52 static bool _textmessage_visible = false;
    55 static bool _textmessage_visible = false;
       
    56 static uint16 _num_text_effects = INIT_NUM_TEXT_MESSAGES;
    53 
    57 
    54 /* The chatbox grows from the bottom so the coordinates are pixels from
    58 /* The chatbox grows from the bottom so the coordinates are pixels from
    55  * the left and pixels from the bottom. The height is the maximum height */
    59  * the left and pixels from the bottom. The height is the maximum height */
    56 static const Oblong _textmsg_box = {10, 30, 500, 150};
    60 static const Oblong _textmsg_box = {10, 30, 500, 150};
    57 static Pixel _textmessage_backup[150 * 500]; // (height * width)
    61 static uint8 _textmessage_backup[150 * 500 * 6]; // (height * width)
    58 
       
    59 extern void memcpy_pitch(void *dst, void *src, int w, int h, int srcpitch, int dstpitch);
       
    60 
    62 
    61 static inline uint GetTextMessageCount()
    63 static inline uint GetTextMessageCount()
    62 {
    64 {
    63 	uint i;
    65 	uint i;
    64 
    66 
   126 
   128 
   127 /** Hide the textbox */
   129 /** Hide the textbox */
   128 void UndrawTextMessage()
   130 void UndrawTextMessage()
   129 {
   131 {
   130 	if (_textmessage_visible) {
   132 	if (_textmessage_visible) {
       
   133 		Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
   131 		/* Sometimes we also need to hide the cursor
   134 		/* Sometimes we also need to hide the cursor
   132 		 *   This is because both textmessage and the cursor take a shot of the
   135 		 *   This is because both textmessage and the cursor take a shot of the
   133 		 *   screen before drawing.
   136 		 *   screen before drawing.
   134 		 *   Now the textmessage takes his shot and paints his data before the cursor
   137 		 *   Now the textmessage takes his shot and paints his data before the cursor
   135 		 *   does, so in the shot of the cursor is the screen-data of the textmessage
   138 		 *   does, so in the shot of the cursor is the screen-data of the textmessage
   161 		}
   164 		}
   162 		if (width <= 0 || height <= 0) return;
   165 		if (width <= 0 || height <= 0) return;
   163 
   166 
   164 		_textmessage_visible = false;
   167 		_textmessage_visible = false;
   165 		/* Put our 'shot' back to the screen */
   168 		/* Put our 'shot' back to the screen */
   166 		memcpy_pitch(
   169 		blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _textmessage_backup, width, height);
   167 			_screen.dst_ptr + x + y * _screen.pitch,
       
   168 			_textmessage_backup,
       
   169 			width, height, _textmsg_box.width, _screen.pitch);
       
   170 
       
   171 		/* And make sure it is updated next time */
   170 		/* And make sure it is updated next time */
   172 		_video_driver->make_dirty(x, y, width, height);
   171 		_video_driver->MakeDirty(x, y, width, height);
   173 
   172 
   174 		_textmessage_dirty = true;
   173 		_textmessage_dirty = true;
   175 	}
   174 	}
   176 }
   175 }
   177 
   176 
   200 }
   199 }
   201 
   200 
   202 /** Draw the textmessage-box */
   201 /** Draw the textmessage-box */
   203 void DrawTextMessage()
   202 void DrawTextMessage()
   204 {
   203 {
       
   204 	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
   205 	if (!_textmessage_dirty) return;
   205 	if (!_textmessage_dirty) return;
   206 
   206 
   207 	/* First undraw if needed */
   207 	/* First undraw if needed */
   208 	UndrawTextMessage();
   208 	UndrawTextMessage();
   209 
   209 
   224 	if (x + width >= _screen.width) {
   224 	if (x + width >= _screen.width) {
   225 		width = _screen.width - x;
   225 		width = _screen.width - x;
   226 	}
   226 	}
   227 	if (width <= 0 || height <= 0) return;
   227 	if (width <= 0 || height <= 0) return;
   228 
   228 
       
   229 	assert(blitter->BufferSize(width, height) < (int)sizeof(_textmessage_backup));
       
   230 
   229 	/* Make a copy of the screen as it is before painting (for undraw) */
   231 	/* Make a copy of the screen as it is before painting (for undraw) */
   230 	memcpy_pitch(
   232 	blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _textmessage_backup, width, height);
   231 		_textmessage_backup,
       
   232 		_screen.dst_ptr + x + y * _screen.pitch,
       
   233 		width, height, _screen.pitch, _textmsg_box.width);
       
   234 
   233 
   235 	_cur_dpi = &_screen; // switch to _screen painting
   234 	_cur_dpi = &_screen; // switch to _screen painting
   236 
   235 
   237 	/* Paint a half-transparent box behind the text messages */
   236 	/* Paint a half-transparent box behind the text messages */
   238 	GfxFillRect(
   237 	GfxFillRect(
   247 	for (uint y = 13; count-- != 0; y += 13) {
   246 	for (uint y = 13; count-- != 0; y += 13) {
   248 		DoDrawString(_textmsg_list[count].message, _textmsg_box.x + 3, _screen.height - _textmsg_box.y - y + 1, _textmsg_list[count].color);
   247 		DoDrawString(_textmsg_list[count].message, _textmsg_box.x + 3, _screen.height - _textmsg_box.y - y + 1, _textmsg_list[count].color);
   249 	}
   248 	}
   250 
   249 
   251 	/* Make sure the data is updated next flush */
   250 	/* Make sure the data is updated next flush */
   252 	_video_driver->make_dirty(x, y, width, height);
   251 	_video_driver->MakeDirty(x, y, width, height);
   253 
   252 
   254 	_textmessage_visible = true;
   253 	_textmessage_visible = true;
   255 	_textmessage_dirty = false;
   254 	_textmessage_dirty = false;
   256 }
   255 }
   257 
   256 
   263 		(te->right - te->x)*2 + te->x + 1,
   262 		(te->right - te->x)*2 + te->x + 1,
   264 		(te->bottom - (te->y - 1)) * 2 + (te->y - 1) + 1
   263 		(te->bottom - (te->y - 1)) * 2 + (te->y - 1) + 1
   265 	);
   264 	);
   266 }
   265 }
   267 
   266 
   268 void AddTextEffect(StringID msg, int x, int y, uint16 duration)
   267 TextEffectID AddTextEffect(StringID msg, int x, int y, uint16 duration, TextEffectMode mode)
   269 {
   268 {
   270 	TextEffect *te;
   269 	TextEffect *te;
   271 	int w;
   270 	int w;
   272 	char buffer[100];
   271 	char buffer[100];
   273 
   272 	TextEffectID i;
   274 	if (_game_mode == GM_MENU) return;
   273 
   275 
   274 	if (_game_mode == GM_MENU) return INVALID_TE_ID;
   276 	for (te = _text_effect_list; te->string_id != INVALID_STRING_ID; ) {
   275 
   277 		if (++te == endof(_text_effect_list)) return;
   276 	/* Look for a free spot in the text effect array */
   278 	}
   277 	for (i = 0; i < _num_text_effects; i++) {
   279 
   278 		if (_text_effect_list[i].string_id == INVALID_STRING_ID) break;
       
   279 	}
       
   280 
       
   281 	/* If there is none found, we grow the array */
       
   282 	if (i == _num_text_effects) {
       
   283 		_num_text_effects += 25;
       
   284 		_text_effect_list = (TextEffect*) realloc(_text_effect_list, _num_text_effects * sizeof(TextEffect));
       
   285 		for (; i < _num_text_effects; i++) _text_effect_list[i].string_id = INVALID_STRING_ID;
       
   286 		i = _num_text_effects - 1;
       
   287 	}
       
   288 
       
   289 	te = &_text_effect_list[i];
       
   290 
       
   291 	/* Start defining this object */
   280 	te->string_id = msg;
   292 	te->string_id = msg;
   281 	te->duration = duration;
   293 	te->duration = duration;
   282 	te->y = y - 5;
   294 	te->y = y - 5;
   283 	te->bottom = y + 5;
   295 	te->bottom = y + 5;
   284 	te->params_1 = GetDParam(0);
   296 	te->params_1 = GetDParam(0);
   285 	te->params_2 = GetDParam(4);
   297 	te->params_2 = GetDParam(4);
       
   298 	te->mode = mode;
   286 
   299 
   287 	GetString(buffer, msg, lastof(buffer));
   300 	GetString(buffer, msg, lastof(buffer));
   288 	w = GetStringBoundingBox(buffer).width;
   301 	w = GetStringBoundingBox(buffer).width;
   289 
   302 
   290 	te->x = x - (w >> 1);
   303 	te->x = x - (w >> 1);
   291 	te->right = x + (w >> 1) - 1;
   304 	te->right = x + (w >> 1) - 1;
   292 	MarkTextEffectAreaDirty(te);
   305 	MarkTextEffectAreaDirty(te);
       
   306 
       
   307 	return i;
       
   308 }
       
   309 
       
   310 void UpdateTextEffect(TextEffectID te_id, StringID msg)
       
   311 {
       
   312 	assert(te_id < _num_text_effects);
       
   313 	TextEffect *te;
       
   314 
       
   315 	/* Update details */
       
   316 	te = &_text_effect_list[te_id];
       
   317 	te->string_id = msg;
       
   318 	te->params_1 = GetDParam(0);
       
   319 	te->params_2 = GetDParam(4);
       
   320 
       
   321 	MarkTextEffectAreaDirty(te);
       
   322 }
       
   323 
       
   324 void RemoveTextEffect(TextEffectID te_id)
       
   325 {
       
   326 	assert(te_id < _num_text_effects);
       
   327 	TextEffect *te;
       
   328 
       
   329 	te = &_text_effect_list[te_id];
       
   330 	MarkTextEffectAreaDirty(te);
       
   331 	te->string_id = INVALID_STRING_ID;
   293 }
   332 }
   294 
   333 
   295 static void MoveTextEffect(TextEffect *te)
   334 static void MoveTextEffect(TextEffect *te)
   296 {
   335 {
       
   336 	/* Never expire for duration of 0xFFFF */
       
   337 	if (te->duration == 0xFFFF) return;
   297 	if (te->duration < 8) {
   338 	if (te->duration < 8) {
   298 		te->string_id = INVALID_STRING_ID;
   339 		te->string_id = INVALID_STRING_ID;
   299 	} else {
   340 	} else {
   300 		te->duration -= 8;
   341 		te->duration -= 8;
   301 		te->y--;
   342 		te->y--;
   304 	MarkTextEffectAreaDirty(te);
   345 	MarkTextEffectAreaDirty(te);
   305 }
   346 }
   306 
   347 
   307 void MoveAllTextEffects()
   348 void MoveAllTextEffects()
   308 {
   349 {
   309 	TextEffect *te;
   350 	for (TextEffectID i = 0; i < _num_text_effects; i++) {
   310 
   351 		TextEffect *te = &_text_effect_list[i];
   311 	for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
   352 		if (te->string_id != INVALID_STRING_ID && te->mode == TE_RISING) MoveTextEffect(te);
   312 		if (te->string_id != INVALID_STRING_ID) MoveTextEffect(te);
       
   313 	}
   353 	}
   314 }
   354 }
   315 
   355 
   316 void InitTextEffects()
   356 void InitTextEffects()
   317 {
   357 {
   318 	TextEffect *te;
   358 	if (_text_effect_list == NULL) _text_effect_list = MallocT<TextEffect>(_num_text_effects);
   319 
   359 
   320 	for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
   360 	for (TextEffectID i = 0; i < _num_text_effects; i++) _text_effect_list[i].string_id = INVALID_STRING_ID;
   321 		te->string_id = INVALID_STRING_ID;
       
   322 	}
       
   323 }
   361 }
   324 
   362 
   325 void DrawTextEffects(DrawPixelInfo *dpi)
   363 void DrawTextEffects(DrawPixelInfo *dpi)
   326 {
   364 {
   327 	const TextEffect* te;
       
   328 
       
   329 	switch (dpi->zoom) {
   365 	switch (dpi->zoom) {
   330 		case ZOOM_LVL_NORMAL:
   366 		case ZOOM_LVL_NORMAL:
   331 			for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
   367 			for (TextEffectID i = 0; i < _num_text_effects; i++) {
       
   368 				TextEffect *te = &_text_effect_list[i];
   332 				if (te->string_id != INVALID_STRING_ID &&
   369 				if (te->string_id != INVALID_STRING_ID &&
   333 						dpi->left <= te->right &&
   370 						dpi->left <= te->right &&
   334 						dpi->top  <= te->bottom &&
   371 						dpi->top  <= te->bottom &&
   335 						dpi->left + dpi->width  > te->x &&
   372 						dpi->left + dpi->width  > te->x &&
   336 						dpi->top  + dpi->height > te->y) {
   373 						dpi->top  + dpi->height > te->y) {
   337 					AddStringToDraw(te->x, te->y, te->string_id, te->params_1, te->params_2);
   374 					if (te->mode == TE_RISING || (_patches.loading_indicators && !HASBIT(_transparent_opt, TO_LOADING))) {
       
   375 						AddStringToDraw(te->x, te->y, te->string_id, te->params_1, te->params_2);
       
   376 					}
   338 				}
   377 				}
   339 			}
   378 			}
   340 			break;
   379 			break;
   341 
   380 
   342 		case ZOOM_LVL_OUT_2X:
   381 		case ZOOM_LVL_OUT_2X:
   343 			for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
   382 			for (TextEffectID i = 0; i < _num_text_effects; i++) {
       
   383 				TextEffect *te = &_text_effect_list[i];
   344 				if (te->string_id != INVALID_STRING_ID &&
   384 				if (te->string_id != INVALID_STRING_ID &&
   345 						dpi->left <= te->right  * 2 - te->x &&
   385 						dpi->left <= te->right  * 2 - te->x &&
   346 						dpi->top  <= te->bottom * 2 - te->y &&
   386 						dpi->top  <= te->bottom * 2 - te->y &&
   347 						dpi->left + dpi->width  > te->x &&
   387 						dpi->left + dpi->width  > te->x &&
   348 						dpi->top  + dpi->height > te->y) {
   388 						dpi->top  + dpi->height > te->y) {
   349 					AddStringToDraw(te->x, te->y, (StringID)(te->string_id-1), te->params_1, te->params_2);
   389 					if (te->mode == TE_RISING || (_patches.loading_indicators && !HASBIT(_transparent_opt, TO_LOADING))) {
       
   390 						AddStringToDraw(te->x, te->y, (StringID)(te->string_id - 1), te->params_1, te->params_2);
       
   391 					}
   350 				}
   392 				}
   351 			}
   393 			}
   352 			break;
   394 			break;
   353 
   395 
   354 		case ZOOM_LVL_OUT_4X:
   396 		case ZOOM_LVL_OUT_4X:
   355 		case ZOOM_LVL_OUT_8X:
   397 		case ZOOM_LVL_OUT_8X:
   356 		case ZOOM_LVL_OUT_16X:
       
   357 			break;
   398 			break;
   358 
   399 
   359 		default: NOT_REACHED();
   400 		default: NOT_REACHED();
   360 	}
   401 	}
   361 }
   402 }