2005-07-24 14:12:37 +00:00
/* $Id$ */
2004-08-09 17:04:08 +00:00
# include "stdafx.h"
2005-06-02 19:30:21 +00:00
# include "openttd.h"
2005-07-22 07:02:20 +00:00
# include "functions.h"
2005-02-06 08:18:00 +00:00
# include "strings.h"
2004-08-09 17:04:08 +00:00
# include "gfx.h"
# include "viewport.h"
# include "saveload.h"
2004-12-04 17:54:56 +00:00
# include "hal.h"
# include "console.h"
2005-03-28 12:38:02 +00:00
# include "string.h"
2005-07-21 18:44:27 +00:00
# include "variables.h"
2005-07-28 09:17:32 +00:00
# include "table/sprites.h"
2004-12-04 17:54:56 +00:00
# include <stdarg.h> /* va_list */
2006-08-14 14:21:15 +00:00
# include "date.h"
2004-08-09 17:04:08 +00:00
typedef struct TextEffect {
StringID string_id ;
2005-01-03 08:50:44 +00:00
int32 x ;
int32 y ;
int32 right ;
int32 bottom ;
2004-08-09 17:04:08 +00:00
uint16 duration ;
uint32 params_1 ;
uint32 params_2 ;
} TextEffect ;
2004-12-04 17:54:56 +00:00
# define MAX_TEXTMESSAGE_LENGTH 250
typedef struct TextMessage {
char message [ MAX_TEXTMESSAGE_LENGTH ] ;
uint16 color ;
2006-08-23 20:46:54 +00:00
Date end_date ;
2004-12-04 17:54:56 +00:00
} TextMessage ;
# define MAX_CHAT_MESSAGES 10
2004-08-09 17:04:08 +00:00
static TextEffect _text_effect_list [ 30 ] ;
2004-12-04 17:54:56 +00:00
static TextMessage _text_message_list [ MAX_CHAT_MESSAGES ] ;
2004-08-09 17:04:08 +00:00
TileIndex _animated_tile_list [ 256 ] ;
2004-12-04 17:54:56 +00:00
2005-10-22 06:39:32 +00:00
static int _textmessage_width = 0 ;
static bool _textmessage_dirty = true ;
static bool _textmessage_visible = false ;
2004-12-04 17:54:56 +00:00
2005-10-22 06:39:32 +00:00
static const int _textmessage_box_left = 10 ; // Pixels from left
static const int _textmessage_box_y = 150 ; // Height of box
static const int _textmessage_box_bottom = 30 ; // Pixels from bottom
static const int _textmessage_box_max_width = 400 ; // Max width of box
2004-12-04 17:54:56 +00:00
2005-07-15 14:16:14 +00:00
static Pixel _textmessage_backup [ 150 * 400 ] ; // (y * max_width)
2004-12-04 17:54:56 +00:00
extern void memcpy_pitch ( void * d , void * s , int w , int h , int spitch , int dpitch ) ;
// Duration is in game-days
2005-01-15 15:48:05 +00:00
void CDECL AddTextMessage ( uint16 color , uint8 duration , const char * message , . . . )
2004-12-04 17:54:56 +00:00
{
2005-09-24 06:43:26 +00:00
char buf [ MAX_TEXTMESSAGE_LENGTH ] ;
2004-12-04 17:54:56 +00:00
va_list va ;
2005-09-25 09:04:59 +00:00
size_t length ;
uint i ;
2004-12-04 17:54:56 +00:00
va_start ( va , message ) ;
2005-08-28 12:24:57 +00:00
vsnprintf ( buf , lengthof ( buf ) , message , va ) ;
2004-12-04 17:54:56 +00:00
va_end ( va ) ;
2005-03-28 12:38:02 +00:00
/* Special color magic */
2005-09-14 17:21:30 +00:00
if ( ( color & 0xFF ) = = 0xC9 ) color = 0x1CA ;
2004-12-04 17:54:56 +00:00
2005-03-28 12:38:02 +00:00
/* Cut the message till it fits inside the chatbox */
2005-09-24 06:43:26 +00:00
length = strlen ( buf ) ;
while ( GetStringWidth ( buf ) > _textmessage_width - 9 ) buf [ - - length ] = ' \0 ' ;
2004-12-04 17:54:56 +00:00
2005-03-28 12:38:02 +00:00
/* Find an empty spot and put the message there */
2004-12-04 17:54:56 +00:00
for ( i = 0 ; i < MAX_CHAT_MESSAGES ; i + + ) {
if ( _text_message_list [ i ] . message [ 0 ] = = ' \0 ' ) {
// Empty spot
2005-09-24 06:43:26 +00:00
ttd_strlcpy ( _text_message_list [ i ] . message , buf , sizeof ( _text_message_list [ i ] . message ) ) ;
2004-12-04 17:54:56 +00:00
_text_message_list [ i ] . color = color ;
_text_message_list [ i ] . end_date = _date + duration ;
_textmessage_dirty = true ;
return ;
}
}
// We did not found a free spot, trash the first one, and add to the end
2005-03-28 12:38:02 +00:00
memmove ( & _text_message_list [ 0 ] , & _text_message_list [ 1 ] , sizeof ( _text_message_list [ 0 ] ) * ( MAX_CHAT_MESSAGES - 1 ) ) ;
2005-09-24 06:43:26 +00:00
ttd_strlcpy ( _text_message_list [ MAX_CHAT_MESSAGES - 1 ] . message , buf , sizeof ( _text_message_list [ MAX_CHAT_MESSAGES - 1 ] . message ) ) ;
2004-12-04 17:54:56 +00:00
_text_message_list [ MAX_CHAT_MESSAGES - 1 ] . color = color ;
2005-03-26 04:02:50 +00:00
_text_message_list [ MAX_CHAT_MESSAGES - 1 ] . end_date = _date + duration ;
2004-12-04 17:54:56 +00:00
_textmessage_dirty = true ;
}
2005-01-22 20:23:18 +00:00
void InitTextMessage ( void )
2004-12-04 17:54:56 +00:00
{
2005-11-14 19:48:04 +00:00
uint i ;
for ( i = 0 ; i < MAX_CHAT_MESSAGES ; i + + ) {
2004-12-04 17:54:56 +00:00
_text_message_list [ i ] . message [ 0 ] = ' \0 ' ;
2005-11-14 19:48:04 +00:00
}
2004-12-04 17:54:56 +00:00
_textmessage_width = _textmessage_box_max_width ;
}
// Hide the textbox
2005-01-22 20:23:18 +00:00
void UndrawTextMessage ( void )
2004-12-04 17:54:56 +00:00
{
if ( _textmessage_visible ) {
// Sometimes we also need to hide the cursor
// This is because both textmessage and the cursor take a shot of the
// screen before drawing.
// Now the textmessage takes his shot and paints his data before the cursor
// does, so in the shot of the cursor is the screen-data of the textmessage
// included when the cursor hangs somewhere over the textmessage. To
// avoid wrong repaints, we undraw the cursor in that case, and everything
// looks nicely ;)
// (and now hope this story above makes sense to you ;))
if ( _cursor . visible ) {
if ( _cursor . draw_pos . x + _cursor . draw_size . x > = _textmessage_box_left & &
_cursor . draw_pos . x < = _textmessage_box_left + _textmessage_width & &
_cursor . draw_pos . y + _cursor . draw_size . y > = _screen . height - _textmessage_box_bottom - _textmessage_box_y & &
_cursor . draw_pos . y < = _screen . height - _textmessage_box_bottom ) {
UndrawMouseCursor ( ) ;
}
}
_textmessage_visible = false ;
// Put our 'shot' back to the screen
memcpy_pitch (
_screen . dst_ptr + _textmessage_box_left + ( _screen . height - _textmessage_box_bottom - _textmessage_box_y ) * _screen . pitch ,
_textmessage_backup ,
_textmessage_width , _textmessage_box_y , _textmessage_width , _screen . pitch ) ;
// And make sure it is updated next time
_video_driver - > make_dirty ( _textmessage_box_left , _screen . height - _textmessage_box_bottom - _textmessage_box_y , _textmessage_width , _textmessage_box_y ) ;
_textmessage_dirty = true ;
}
}
// Check if a message is expired every day
2005-01-22 20:23:18 +00:00
void TextMessageDailyLoop ( void )
2004-12-04 17:54:56 +00:00
{
2005-11-14 19:48:04 +00:00
uint i ;
2005-03-28 12:38:02 +00:00
for ( i = 0 ; i < MAX_CHAT_MESSAGES ; i + + ) {
2005-11-14 19:48:04 +00:00
if ( _text_message_list [ i ] . message [ 0 ] = = ' \0 ' ) continue ;
2005-03-28 12:38:02 +00:00
2004-12-04 17:54:56 +00:00
if ( _date > _text_message_list [ i ] . end_date ) {
2005-03-28 12:38:02 +00:00
/* Move the remaining messages over the current message */
if ( i ! = MAX_CHAT_MESSAGES - 1 )
memmove ( & _text_message_list [ i ] , & _text_message_list [ i + 1 ] , sizeof ( _text_message_list [ i ] ) * ( MAX_CHAT_MESSAGES - i - 1 ) ) ;
/* Mark the last item as empty */
2004-12-04 17:54:56 +00:00
_text_message_list [ MAX_CHAT_MESSAGES - 1 ] . message [ 0 ] = ' \0 ' ;
_textmessage_dirty = true ;
2005-03-28 12:38:02 +00:00
/* Go one item back, because we moved the array 1 to the left */
i - - ;
2004-12-04 17:54:56 +00:00
}
}
}
// Draw the textmessage-box
2005-01-22 20:23:18 +00:00
void DrawTextMessage ( void )
2004-12-04 17:54:56 +00:00
{
int i , j ;
bool has_message ;
2005-11-14 19:48:04 +00:00
if ( ! _textmessage_dirty ) return ;
2004-12-04 17:54:56 +00:00
// First undraw if needed
UndrawTextMessage ( ) ;
if ( _iconsole_mode = = ICONSOLE_FULL )
return ;
2005-03-28 12:38:02 +00:00
/* Check if we have anything to draw at all */
2004-12-04 17:54:56 +00:00
has_message = false ;
for ( i = 0 ; i < MAX_CHAT_MESSAGES ; i + + ) {
2005-11-14 19:48:04 +00:00
if ( _text_message_list [ i ] . message [ 0 ] = = ' \0 ' ) break ;
2005-03-28 12:38:02 +00:00
2004-12-04 17:54:56 +00:00
has_message = true ;
}
2005-11-14 19:48:04 +00:00
if ( ! has_message ) return ;
2004-12-04 17:54:56 +00:00
// Make a copy of the screen as it is before painting (for undraw)
memcpy_pitch (
_textmessage_backup ,
_screen . dst_ptr + _textmessage_box_left + ( _screen . height - _textmessage_box_bottom - _textmessage_box_y ) * _screen . pitch ,
_textmessage_width , _textmessage_box_y , _screen . pitch , _textmessage_width ) ;
// Switch to _screen painting
_cur_dpi = & _screen ;
j = 0 ;
// Paint the messages
for ( i = MAX_CHAT_MESSAGES - 1 ; i > = 0 ; i - - ) {
2005-11-14 19:48:04 +00:00
if ( _text_message_list [ i ] . message [ 0 ] = = ' \0 ' ) continue ;
2005-03-28 12:38:02 +00:00
2004-12-04 17:54:56 +00:00
j + + ;
2005-07-28 09:17:32 +00:00
GfxFillRect ( _textmessage_box_left , _screen . height - _textmessage_box_bottom - j * 13 - 2 , _textmessage_box_left + _textmessage_width - 1 , _screen . height - _textmessage_box_bottom - j * 13 + 10 , /* black, but with some alpha */ 0x322 | USE_COLORTABLE ) ;
2004-12-04 17:54:56 +00:00
DoDrawString ( _text_message_list [ i ] . message , _textmessage_box_left + 2 , _screen . height - _textmessage_box_bottom - j * 13 - 1 , 0x10 ) ;
DoDrawString ( _text_message_list [ i ] . message , _textmessage_box_left + 3 , _screen . height - _textmessage_box_bottom - j * 13 , _text_message_list [ i ] . color ) ;
}
// Make sure the data is updated next flush
_video_driver - > make_dirty ( _textmessage_box_left , _screen . height - _textmessage_box_bottom - _textmessage_box_y , _textmessage_width , _textmessage_box_y ) ;
_textmessage_visible = true ;
_textmessage_dirty = false ;
}
2004-08-09 17:04:08 +00:00
static void MarkTextEffectAreaDirty ( TextEffect * te )
{
MarkAllViewportsDirty (
te - > x ,
te - > y - 1 ,
( te - > right - te - > x ) * 2 + te - > x + 1 ,
( te - > bottom - ( te - > y - 1 ) ) * 2 + ( te - > y - 1 ) + 1
) ;
}
void AddTextEffect ( StringID msg , int x , int y , uint16 duration )
{
TextEffect * te ;
int w ;
char buffer [ 100 ] ;
if ( _game_mode = = GM_MENU )
return ;
2004-09-10 19:02:27 +00:00
2005-09-28 21:49:55 +00:00
for ( te = _text_effect_list ; te - > string_id ! = INVALID_STRING_ID ; ) {
2005-11-14 19:48:04 +00:00
if ( + + te = = endof ( _text_effect_list ) ) return ;
2004-08-09 17:04:08 +00:00
}
te - > string_id = msg ;
te - > duration = duration ;
te - > y = y - 5 ;
te - > bottom = y + 5 ;
2004-12-02 22:53:07 +00:00
te - > params_1 = GetDParam ( 0 ) ;
te - > params_2 = GetDParam ( 4 ) ;
2004-08-09 17:04:08 +00:00
GetString ( buffer , msg ) ;
w = GetStringWidth ( buffer ) ;
te - > x = x - ( w > > 1 ) ;
te - > right = x + ( w > > 1 ) - 1 ;
MarkTextEffectAreaDirty ( te ) ;
}
static void MoveTextEffect ( TextEffect * te )
{
if ( te - > duration < 8 ) {
2005-09-28 21:49:55 +00:00
te - > string_id = INVALID_STRING_ID ;
2004-08-09 17:04:08 +00:00
} else {
2005-10-23 13:04:44 +00:00
te - > duration - = 8 ;
2004-08-09 17:04:08 +00:00
te - > y - - ;
te - > bottom - - ;
}
MarkTextEffectAreaDirty ( te ) ;
}
2005-01-22 20:23:18 +00:00
void MoveAllTextEffects ( void )
2004-08-09 17:04:08 +00:00
{
TextEffect * te ;
2005-10-23 13:04:44 +00:00
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
if ( te - > string_id ! = INVALID_STRING_ID ) MoveTextEffect ( te ) ;
2004-08-09 17:04:08 +00:00
}
}
2005-01-22 20:23:18 +00:00
void InitTextEffects ( void )
2004-08-09 17:04:08 +00:00
{
TextEffect * te ;
2005-10-23 13:04:44 +00:00
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
2005-09-28 21:49:55 +00:00
te - > string_id = INVALID_STRING_ID ;
2004-08-09 17:04:08 +00:00
}
}
void DrawTextEffects ( DrawPixelInfo * dpi )
{
TextEffect * te ;
if ( dpi - > zoom < 1 ) {
2005-10-23 13:04:44 +00:00
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
2005-09-28 21:49:55 +00:00
if ( te - > string_id = = INVALID_STRING_ID )
2004-08-09 17:04:08 +00:00
continue ;
/* intersection? */
2005-01-03 08:50:44 +00:00
if ( dpi - > left > te - > right | |
dpi - > top > te - > bottom | |
dpi - > left + dpi - > width < = te - > x | |
dpi - > top + dpi - > height < = te - > y )
2004-08-09 17:04:08 +00:00
continue ;
2004-12-31 18:57:24 +00:00
AddStringToDraw ( te - > x , te - > y , te - > string_id , te - > params_1 , te - > params_2 , 0 ) ;
2004-08-09 17:04:08 +00:00
}
} else if ( dpi - > zoom = = 1 ) {
2005-10-23 13:04:44 +00:00
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
2005-09-28 21:49:55 +00:00
if ( te - > string_id = = INVALID_STRING_ID )
2004-08-09 17:04:08 +00:00
continue ;
/* intersection? */
if ( dpi - > left > te - > right * 2 - te - > x | |
dpi - > top > te - > bottom * 2 - te - > y | |
( dpi - > left + dpi - > width ) < = te - > x | |
( dpi - > top + dpi - > height ) < = te - > y )
continue ;
2004-12-31 18:57:24 +00:00
AddStringToDraw ( te - > x , te - > y , ( StringID ) ( te - > string_id - 1 ) , te - > params_1 , te - > params_2 , 0 ) ;
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
}
}
2005-06-24 12:38:35 +00:00
void DeleteAnimatedTile ( TileIndex tile )
2004-08-09 17:04:08 +00:00
{
2005-10-23 13:04:44 +00:00
TileIndex * ti ;
2004-08-09 17:04:08 +00:00
2005-10-23 13:04:44 +00:00
for ( ti = _animated_tile_list ; ti ! = endof ( _animated_tile_list ) ; ti + + ) {
2005-06-24 12:38:35 +00:00
if ( tile = = * ti ) {
2004-08-09 17:04:08 +00:00
/* remove the hole */
2005-10-23 13:04:44 +00:00
memmove ( ti , ti + 1 , endof ( _animated_tile_list ) - 1 - ti ) ;
2004-08-09 17:04:08 +00:00
/* and clear last item */
endof ( _animated_tile_list ) [ - 1 ] = 0 ;
MarkTileDirtyByTile ( tile ) ;
return ;
2004-09-10 19:02:27 +00:00
}
2004-08-09 17:04:08 +00:00
}
}
2005-06-24 12:38:35 +00:00
bool AddAnimatedTile ( TileIndex tile )
2004-08-09 17:04:08 +00:00
{
2005-10-23 13:04:44 +00:00
TileIndex * ti ;
2004-08-09 17:04:08 +00:00
2005-10-23 13:04:44 +00:00
for ( ti = _animated_tile_list ; ti ! = endof ( _animated_tile_list ) ; ti + + ) {
2005-06-24 12:38:35 +00:00
if ( tile = = * ti | | * ti = = 0 ) {
2004-08-09 17:04:08 +00:00
* ti = tile ;
MarkTileDirtyByTile ( tile ) ;
return true ;
}
2004-09-10 19:02:27 +00:00
}
2004-08-09 17:04:08 +00:00
return false ;
}
2005-01-22 20:23:18 +00:00
void AnimateAnimatedTiles ( void )
2004-08-09 17:04:08 +00:00
{
2005-10-23 13:04:44 +00:00
const TileIndex * ti ;
2004-08-09 17:04:08 +00:00
2005-10-23 13:04:44 +00:00
for ( ti = _animated_tile_list ; ti ! = endof ( _animated_tile_list ) & & * ti ! = 0 ; ti + + ) {
AnimateTile ( * ti ) ;
2004-08-09 17:04:08 +00:00
}
}
2005-01-22 20:23:18 +00:00
void InitializeAnimatedTiles ( void )
2004-08-09 17:04:08 +00:00
{
memset ( _animated_tile_list , 0 , sizeof ( _animated_tile_list ) ) ;
}
2005-01-22 20:23:18 +00:00
static void SaveLoad_ANIT ( void )
2004-08-09 17:04:08 +00:00
{
2005-11-22 19:33:29 +00:00
// In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;)
if ( CheckSavegameVersion ( 6 ) ) {
2005-05-30 22:16:05 +00:00
SlArray ( _animated_tile_list , lengthof ( _animated_tile_list ) , SLE_FILE_U16 | SLE_VAR_U32 ) ;
2005-10-23 13:04:44 +00:00
} else {
2005-01-25 21:43:57 +00:00
SlArray ( _animated_tile_list , lengthof ( _animated_tile_list ) , SLE_UINT32 ) ;
2005-10-23 13:04:44 +00:00
}
2004-08-09 17:04:08 +00:00
}
const ChunkHandler _animated_tile_chunk_handlers [ ] = {
{ ' ANIT ' , SaveLoad_ANIT , SaveLoad_ANIT , CH_RIFF | CH_LAST } ,
} ;