2005-07-24 15:12:37 +01:00
/* $Id$ */
2004-08-09 18:04:08 +01:00
# include "stdafx.h"
2005-06-02 20:30:21 +01:00
# include "openttd.h"
2005-07-22 08:02:20 +01:00
# include "functions.h"
2006-10-27 12:08:17 +01:00
# include "macros.h"
2005-02-06 08:18:00 +00:00
# include "strings.h"
2004-08-09 18:04:08 +01: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 13:38:02 +01:00
# include "string.h"
2005-07-21 19:44:27 +01:00
# include "variables.h"
2005-07-28 10:17:32 +01:00
# include "table/sprites.h"
2004-12-04 17:54:56 +00:00
# include <stdarg.h> /* va_list */
2006-08-14 15:21:15 +01:00
# include "date.h"
2004-08-09 18:04:08 +01: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 18:04:08 +01:00
uint16 duration ;
uint32 params_1 ;
uint32 params_2 ;
} TextEffect ;
2006-10-27 12:08:17 +01:00
# define MAX_TEXTMESSAGE_LENGTH 150
2004-12-04 17:54:56 +00:00
typedef struct TextMessage {
char message [ MAX_TEXTMESSAGE_LENGTH ] ;
uint16 color ;
2006-08-23 21:46:54 +01:00
Date end_date ;
2004-12-04 17:54:56 +00:00
} TextMessage ;
# define MAX_CHAT_MESSAGES 10
2004-08-09 18:04:08 +01:00
static TextEffect _text_effect_list [ 30 ] ;
2006-10-27 11:27:38 +01:00
static TextMessage _textmsg_list [ MAX_CHAT_MESSAGES ] ;
2004-08-09 18:04:08 +01:00
TileIndex _animated_tile_list [ 256 ] ;
2006-10-27 11:27:38 +01:00
static bool _textmessage_dirty = false ;
2005-10-22 07:39:32 +01:00
static bool _textmessage_visible = false ;
2004-12-04 17:54:56 +00:00
2006-10-27 11:27:38 +01:00
/* The chatbox grows from the bottom so the coordinates are pixels from
* the left and pixels from the bottom . The height is the maximum height */
2006-10-27 12:08:17 +01:00
static const Oblong _textmsg_box = { 10 , 30 , 500 , 150 } ;
static Pixel _textmessage_backup [ 150 * 500 ] ; // (height * width)
2004-12-04 17:54:56 +00:00
2006-10-27 12:09:24 +01:00
extern void memcpy_pitch ( void * dst , void * src , int w , int h , int srcpitch , int dstpitch ) ;
2004-12-04 17:54:56 +00:00
2006-10-27 12:08:17 +01:00
static inline uint GetTextMessageCount ( void )
{
uint i = 0 ;
for ( i ; i < MAX_CHAT_MESSAGES ; i + + ) {
if ( _textmsg_list [ i ] . message [ 0 ] = = ' \0 ' ) break ;
}
return i ;
}
/* Add a text message to the 'chat window' to be shown
* @ param color The colour this message is to be shown in
* @ param duration The duration of the chat message in game - days
* @ param message message itself in printf ( ) style */
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 07:43:26 +01:00
char buf [ MAX_TEXTMESSAGE_LENGTH ] ;
2006-10-27 12:08:17 +01:00
const char * bufp ;
2004-12-04 17:54:56 +00:00
va_list va ;
2006-10-27 12:08:17 +01:00
uint msg_count ;
uint16 lines ;
2004-12-04 17:54:56 +00:00
va_start ( va , message ) ;
2005-08-28 13:24:57 +01:00
vsnprintf ( buf , lengthof ( buf ) , message , va ) ;
2004-12-04 17:54:56 +00:00
va_end ( va ) ;
2006-10-27 12:08:17 +01:00
/* Force linebreaks for strings that are too long */
lines = GB ( FormatStringLinebreaks ( buf , _textmsg_box . width - 8 ) , 0 , 16 ) + 1 ;
if ( lines > = MAX_CHAT_MESSAGES ) return ;
2004-12-04 17:54:56 +00:00
2006-10-27 12:08:17 +01:00
msg_count = GetTextMessageCount ( ) ;
/* We want to add more chat messages than there is free space for, remove 'old' */
if ( lines > MAX_CHAT_MESSAGES - msg_count ) {
int i = lines - ( MAX_CHAT_MESSAGES - msg_count ) ;
memmove ( & _textmsg_list [ 0 ] , & _textmsg_list [ i ] , sizeof ( _textmsg_list [ 0 ] ) * ( msg_count - i ) ) ;
msg_count = MAX_CHAT_MESSAGES - lines ;
}
2004-12-04 17:54:56 +00:00
2006-10-27 12:08:17 +01:00
for ( bufp = buf ; lines ! = 0 ; lines - - ) {
TextMessage * tmsg = & _textmsg_list [ msg_count + + ] ;
ttd_strlcpy ( tmsg - > message , bufp , sizeof ( tmsg - > message ) ) ;
2004-12-04 17:54:56 +00:00
2006-10-27 12:08:17 +01:00
/* The default colour for a message is player colour. Replace this with
* white for any additional lines */
tmsg - > color = ( bufp = = buf & & color & IS_PALETTE_COLOR ) ? color : ( 0x1D - 15 ) | IS_PALETTE_COLOR ;
tmsg - > end_date = _date + duration ;
2004-12-04 17:54:56 +00:00
2006-10-27 12:08:17 +01:00
bufp + = strlen ( bufp ) + 1 ; // jump to 'next line' in the formatted string
}
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 + + ) {
2006-10-27 11:27:38 +01:00
_textmsg_list [ i ] . message [ 0 ] = ' \0 ' ;
2005-11-14 19:48:04 +00:00
}
2004-12-04 17:54:56 +00:00
}
// 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 ) {
2006-10-27 11:27:38 +01:00
if ( _cursor . draw_pos . x + _cursor . draw_size . x > = _textmsg_box . x & &
_cursor . draw_pos . x < = _textmsg_box . x + _textmsg_box . width & &
_cursor . draw_pos . y + _cursor . draw_size . y > = _screen . height - _textmsg_box . y - _textmsg_box . height & &
_cursor . draw_pos . y < = _screen . height - _textmsg_box . y ) {
2004-12-04 17:54:56 +00:00
UndrawMouseCursor ( ) ;
}
}
_textmessage_visible = false ;
// Put our 'shot' back to the screen
memcpy_pitch (
2006-10-27 11:27:38 +01:00
_screen . dst_ptr + _textmsg_box . x + ( _screen . height - _textmsg_box . y - _textmsg_box . height ) * _screen . pitch ,
2004-12-04 17:54:56 +00:00
_textmessage_backup ,
2006-10-27 11:27:38 +01:00
_textmsg_box . width , _textmsg_box . height , _textmsg_box . width , _screen . pitch ) ;
2004-12-04 17:54:56 +00:00
// And make sure it is updated next time
2006-10-27 11:27:38 +01:00
_video_driver - > make_dirty ( _textmsg_box . x , _screen . height - _textmsg_box . y - _textmsg_box . height , _textmsg_box . width , _textmsg_box . height ) ;
2004-12-04 17:54:56 +00:00
_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 13:38:02 +01:00
for ( i = 0 ; i < MAX_CHAT_MESSAGES ; i + + ) {
2006-10-27 12:08:17 +01:00
TextMessage * tmsg = & _textmsg_list [ i ] ;
if ( tmsg - > message [ 0 ] = = ' \0 ' ) continue ;
2005-03-28 13:38:02 +01:00
2006-10-27 12:08:17 +01:00
/* Message has expired, remove from the list */
if ( tmsg - > end_date < _date ) {
2005-03-28 13:38:02 +01:00
/* Move the remaining messages over the current message */
2006-10-27 12:08:17 +01:00
if ( i ! = MAX_CHAT_MESSAGES - 1 ) memmove ( tmsg , tmsg + 1 , sizeof ( * tmsg ) * ( MAX_CHAT_MESSAGES - i - 1 ) ) ;
2005-03-28 13:38:02 +01:00
/* Mark the last item as empty */
2006-10-27 11:27:38 +01:00
_textmsg_list [ MAX_CHAT_MESSAGES - 1 ] . message [ 0 ] = ' \0 ' ;
2004-12-04 17:54:56 +00:00
_textmessage_dirty = true ;
2005-03-28 13:38:02 +01: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 13:38:02 +01: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 + + ) {
2006-10-27 11:27:38 +01:00
if ( _textmsg_list [ i ] . message [ 0 ] = = ' \0 ' ) break ;
2005-03-28 13:38:02 +01: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 ,
2006-10-27 11:27:38 +01:00
_screen . dst_ptr + _textmsg_box . x + ( _screen . height - _textmsg_box . y - _textmsg_box . height ) * _screen . pitch ,
_textmsg_box . width , _textmsg_box . height , _screen . pitch , _textmsg_box . width ) ;
2004-12-04 17:54:56 +00:00
// Switch to _screen painting
_cur_dpi = & _screen ;
j = 0 ;
// Paint the messages
for ( i = MAX_CHAT_MESSAGES - 1 ; i > = 0 ; i - - ) {
2006-10-27 11:27:38 +01:00
if ( _textmsg_list [ i ] . message [ 0 ] = = ' \0 ' ) continue ;
2005-03-28 13:38:02 +01:00
2004-12-04 17:54:56 +00:00
j + + ;
2006-10-27 11:27:38 +01:00
GfxFillRect ( _textmsg_box . x , _screen . height - _textmsg_box . y - j * 13 - 2 , _textmsg_box . x + _textmsg_box . width - 1 , _screen . height - _textmsg_box . y - j * 13 + 10 , /* black, but with some alpha */ 0x322 | USE_COLORTABLE ) ;
2004-12-04 17:54:56 +00:00
2006-10-27 11:27:38 +01:00
DoDrawString ( _textmsg_list [ i ] . message , _textmsg_box . x + 3 , _screen . height - _textmsg_box . y - j * 13 , _textmsg_list [ i ] . color ) ;
2004-12-04 17:54:56 +00:00
}
// Make sure the data is updated next flush
2006-10-27 11:27:38 +01:00
_video_driver - > make_dirty ( _textmsg_box . x , _screen . height - _textmsg_box . y - _textmsg_box . height , _textmsg_box . width , _textmsg_box . height ) ;
2004-12-04 17:54:56 +00:00
_textmessage_visible = true ;
_textmessage_dirty = false ;
}
2004-08-09 18:04:08 +01: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 ] ;
2006-10-27 12:09:24 +01:00
if ( _game_mode = = GM_MENU ) return ;
2004-09-10 20:02:27 +01:00
2005-09-28 22:49:55 +01: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 18:04:08 +01: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 18:04:08 +01:00
2006-10-22 00:31:34 +01:00
GetString ( buffer , msg , lastof ( buffer ) ) ;
2006-09-16 14:20:14 +01:00
w = GetStringBoundingBox ( buffer ) . width ;
2004-08-09 18:04:08 +01:00
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 22:49:55 +01:00
te - > string_id = INVALID_STRING_ID ;
2004-08-09 18:04:08 +01:00
} else {
2005-10-23 14:04:44 +01:00
te - > duration - = 8 ;
2004-08-09 18:04:08 +01:00
te - > y - - ;
te - > bottom - - ;
}
MarkTextEffectAreaDirty ( te ) ;
}
2005-01-22 20:23:18 +00:00
void MoveAllTextEffects ( void )
2004-08-09 18:04:08 +01:00
{
TextEffect * te ;
2005-10-23 14:04:44 +01:00
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
if ( te - > string_id ! = INVALID_STRING_ID ) MoveTextEffect ( te ) ;
2004-08-09 18:04:08 +01:00
}
}
2005-01-22 20:23:18 +00:00
void InitTextEffects ( void )
2004-08-09 18:04:08 +01:00
{
TextEffect * te ;
2005-10-23 14:04:44 +01:00
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
2005-09-28 22:49:55 +01:00
te - > string_id = INVALID_STRING_ID ;
2004-08-09 18:04:08 +01:00
}
}
void DrawTextEffects ( DrawPixelInfo * dpi )
{
2006-08-31 08:13:36 +01:00
const TextEffect * te ;
switch ( dpi - > zoom ) {
case 0 :
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
if ( te - > string_id ! = INVALID_STRING_ID & &
dpi - > left < = te - > right & &
dpi - > top < = te - > bottom & &
dpi - > left + dpi - > width > te - > x & &
dpi - > top + dpi - > height > te - > y ) {
AddStringToDraw ( te - > x , te - > y , te - > string_id , te - > params_1 , te - > params_2 , 0 ) ;
}
}
break ;
case 1 :
for ( te = _text_effect_list ; te ! = endof ( _text_effect_list ) ; te + + ) {
if ( te - > string_id ! = INVALID_STRING_ID & &
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 ) {
AddStringToDraw ( te - > x , te - > y , ( StringID ) ( te - > string_id - 1 ) , te - > params_1 , te - > params_2 , 0 ) ;
}
}
break ;
2004-08-09 18:04:08 +01:00
}
}
2005-06-24 13:38:35 +01:00
void DeleteAnimatedTile ( TileIndex tile )
2004-08-09 18:04:08 +01:00
{
2005-10-23 14:04:44 +01:00
TileIndex * ti ;
2004-08-09 18:04:08 +01:00
2005-10-23 14:04:44 +01:00
for ( ti = _animated_tile_list ; ti ! = endof ( _animated_tile_list ) ; ti + + ) {
2005-06-24 13:38:35 +01:00
if ( tile = = * ti ) {
2004-08-09 18:04:08 +01:00
/* remove the hole */
2005-10-23 14:04:44 +01:00
memmove ( ti , ti + 1 , endof ( _animated_tile_list ) - 1 - ti ) ;
2004-08-09 18:04:08 +01:00
/* and clear last item */
endof ( _animated_tile_list ) [ - 1 ] = 0 ;
MarkTileDirtyByTile ( tile ) ;
return ;
2004-09-10 20:02:27 +01:00
}
2004-08-09 18:04:08 +01:00
}
}
2005-06-24 13:38:35 +01:00
bool AddAnimatedTile ( TileIndex tile )
2004-08-09 18:04:08 +01:00
{
2005-10-23 14:04:44 +01:00
TileIndex * ti ;
2004-08-09 18:04:08 +01:00
2005-10-23 14:04:44 +01:00
for ( ti = _animated_tile_list ; ti ! = endof ( _animated_tile_list ) ; ti + + ) {
2005-06-24 13:38:35 +01:00
if ( tile = = * ti | | * ti = = 0 ) {
2004-08-09 18:04:08 +01:00
* ti = tile ;
MarkTileDirtyByTile ( tile ) ;
return true ;
}
2004-09-10 20:02:27 +01:00
}
2004-08-09 18:04:08 +01:00
return false ;
}
2005-01-22 20:23:18 +00:00
void AnimateAnimatedTiles ( void )
2004-08-09 18:04:08 +01:00
{
2005-10-23 14:04:44 +01:00
const TileIndex * ti ;
2004-08-09 18:04:08 +01:00
2005-10-23 14:04:44 +01:00
for ( ti = _animated_tile_list ; ti ! = endof ( _animated_tile_list ) & & * ti ! = 0 ; ti + + ) {
AnimateTile ( * ti ) ;
2004-08-09 18:04:08 +01:00
}
}
2005-01-22 20:23:18 +00:00
void InitializeAnimatedTiles ( void )
2004-08-09 18:04:08 +01: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 18:04:08 +01: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 23:16:05 +01:00
SlArray ( _animated_tile_list , lengthof ( _animated_tile_list ) , SLE_FILE_U16 | SLE_VAR_U32 ) ;
2005-10-23 14:04:44 +01:00
} else {
2005-01-25 21:43:57 +00:00
SlArray ( _animated_tile_list , lengthof ( _animated_tile_list ) , SLE_UINT32 ) ;
2005-10-23 14:04:44 +01:00
}
2004-08-09 18:04:08 +01:00
}
const ChunkHandler _animated_tile_chunk_handlers [ ] = {
{ ' ANIT ' , SaveLoad_ANIT , SaveLoad_ANIT , CH_RIFF | CH_LAST } ,
} ;