|
|
/* $Id$ */
|
|
|
|
|
|
/** @file gfx.cpp */
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
#include "openttd.h"
|
|
|
#include "functions.h"
|
|
|
#include "macros.h"
|
|
|
#include "spritecache.h"
|
|
|
#include "strings.h"
|
|
|
#include "string.h"
|
|
|
#include "gfx.h"
|
|
|
#include "table/palettes.h"
|
|
|
#include "table/sprites.h"
|
|
|
#include "hal.h"
|
|
|
#include "variables.h"
|
|
|
#include "table/control_codes.h"
|
|
|
#include "fontcache.h"
|
|
|
#include "genworld.h"
|
|
|
#include "debug.h"
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
bool _dbg_screen_rect;
|
|
|
#endif
|
|
|
|
|
|
byte _dirkeys; // 1 = left, 2 = up, 4 = right, 8 = down
|
|
|
byte _dirkeys; ///< 1 = left, 2 = up, 4 = right, 8 = down
|
|
|
bool _fullscreen;
|
|
|
CursorVars _cursor;
|
|
|
bool _ctrl_pressed; // Is Ctrl pressed?
|
|
|
bool _shift_pressed; // Is Shift pressed?
|
|
|
bool _ctrl_pressed; ///< Is Ctrl pressed?
|
|
|
bool _shift_pressed; ///< Is Shift pressed?
|
|
|
byte _fast_forward;
|
|
|
bool _left_button_down;
|
|
|
bool _left_button_clicked;
|
|
|
bool _right_button_down;
|
|
|
bool _right_button_clicked;
|
|
|
DrawPixelInfo _screen;
|
|
|
bool _exit_game;
|
|
|
bool _networking; ///< are we in networking mode?
|
|
|
byte _game_mode;
|
|
|
byte _pause;
|
|
|
int _pal_first_dirty;
|
|
|
int _pal_last_dirty;
|
|
@@ -79,76 +81,76 @@ void GfxScroll(int left, int top, int wi
|
|
|
Pixel *dst;
|
|
|
int p;
|
|
|
int ht;
|
|
|
|
|
|
if (xo == 0 && yo == 0) return;
|
|
|
|
|
|
if (_cursor.visible) UndrawMouseCursor();
|
|
|
UndrawTextMessage();
|
|
|
|
|
|
p = _screen.pitch;
|
|
|
|
|
|
if (yo > 0) {
|
|
|
// Calculate pointers
|
|
|
/*Calculate pointers */
|
|
|
dst = _screen.dst_ptr + (top + height - 1) * p + left;
|
|
|
src = dst - yo * p;
|
|
|
|
|
|
// Decrease height and increase top
|
|
|
/* Decrease height and increase top */
|
|
|
top += yo;
|
|
|
height -= yo;
|
|
|
assert(height > 0);
|
|
|
|
|
|
// Adjust left & width
|
|
|
/* Adjust left & width */
|
|
|
if (xo >= 0) {
|
|
|
dst += xo;
|
|
|
left += xo;
|
|
|
width -= xo;
|
|
|
} else {
|
|
|
src -= xo;
|
|
|
width += xo;
|
|
|
}
|
|
|
|
|
|
for (ht = height; ht > 0; --ht) {
|
|
|
memcpy(dst, src, width);
|
|
|
src -= p;
|
|
|
dst -= p;
|
|
|
}
|
|
|
} else {
|
|
|
// Calculate pointers
|
|
|
/* Calculate pointers */
|
|
|
dst = _screen.dst_ptr + top * p + left;
|
|
|
src = dst - yo * p;
|
|
|
|
|
|
// Decrese height. (yo is <=0).
|
|
|
/* Decrese height. (yo is <=0). */
|
|
|
height += yo;
|
|
|
assert(height > 0);
|
|
|
|
|
|
// Adjust left & width
|
|
|
/* Adjust left & width */
|
|
|
if (xo >= 0) {
|
|
|
dst += xo;
|
|
|
left += xo;
|
|
|
width -= xo;
|
|
|
} else {
|
|
|
src -= xo;
|
|
|
width += xo;
|
|
|
}
|
|
|
|
|
|
// the y-displacement may be 0 therefore we have to use memmove,
|
|
|
// because source and destination may overlap
|
|
|
/* the y-displacement may be 0 therefore we have to use memmove,
|
|
|
* because source and destination may overlap */
|
|
|
for (ht = height; ht > 0; --ht) {
|
|
|
memmove(dst, src, width);
|
|
|
src += p;
|
|
|
dst += p;
|
|
|
}
|
|
|
}
|
|
|
// This part of the screen is now dirty.
|
|
|
/* This part of the screen is now dirty. */
|
|
|
_video_driver->make_dirty(left, top, width, height);
|
|
|
}
|
|
|
|
|
|
|
|
|
void GfxFillRect(int left, int top, int right, int bottom, int color)
|
|
|
{
|
|
|
const DrawPixelInfo* dpi = _cur_dpi;
|
|
|
Pixel *dst;
|
|
|
const int otop = top;
|
|
|
const int oleft = left;
|
|
|
|
|
|
if (dpi->zoom != 0) return;
|
|
@@ -203,25 +205,25 @@ static void GfxSetPixel(int x, int y, in
|
|
|
return;
|
|
|
dpi->dst_ptr[y * dpi->pitch + x] = color;
|
|
|
}
|
|
|
|
|
|
void GfxDrawLine(int x, int y, int x2, int y2, int color)
|
|
|
{
|
|
|
int dy;
|
|
|
int dx;
|
|
|
int stepx;
|
|
|
int stepy;
|
|
|
int frac;
|
|
|
|
|
|
// Check clipping first
|
|
|
/* Check clipping first */
|
|
|
{
|
|
|
DrawPixelInfo *dpi = _cur_dpi;
|
|
|
int t;
|
|
|
|
|
|
if (x < dpi->left && x2 < dpi->left) return;
|
|
|
|
|
|
if (y < dpi->top && y2 < dpi->top) return;
|
|
|
|
|
|
t = dpi->left + dpi->width;
|
|
|
if (x > t && x2 > t) return;
|
|
|
|
|
|
t = dpi->top + dpi->height;
|
|
@@ -283,42 +285,42 @@ static int TruncateString(char *str, int
|
|
|
int ddd, ddd_w;
|
|
|
|
|
|
WChar c;
|
|
|
char *ddd_pos;
|
|
|
|
|
|
ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
|
|
|
|
|
|
for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
|
|
|
if (IsPrintable(c)) {
|
|
|
w += GetCharacterWidth(size, c);
|
|
|
|
|
|
if (w >= maxw) {
|
|
|
// string got too big... insert dotdotdot
|
|
|
/* string got too big... insert dotdotdot */
|
|
|
ddd_pos[0] = ddd_pos[1] = ddd_pos[2] = '.';
|
|
|
ddd_pos[3] = 0;
|
|
|
return ddd_w;
|
|
|
}
|
|
|
} else {
|
|
|
if (c == SCC_SETX) str++;
|
|
|
else if (c == SCC_SETXY) str += 2;
|
|
|
else if (c == SCC_TINYFONT) {
|
|
|
size = FS_SMALL;
|
|
|
ddd = GetCharacterWidth(size, '.') * 3;
|
|
|
} else if (c == SCC_BIGFONT) {
|
|
|
size = FS_LARGE;
|
|
|
ddd = GetCharacterWidth(size, '.') * 3;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Remember the last position where three dots fit.
|
|
|
/* Remember the last position where three dots fit. */
|
|
|
if (w + ddd < maxw) {
|
|
|
ddd_w = w + ddd;
|
|
|
ddd_pos = str;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return w;
|
|
|
}
|
|
|
|
|
|
static inline int TruncateStringID(StringID src, char *dest, int maxw, const char* last)
|
|
|
{
|
|
|
GetString(dest, src, last);
|
|
@@ -1535,103 +1537,103 @@ void DoPaletteAnimations(void)
|
|
|
/* Amount of colors to be rotated.
|
|
|
* A few more for the DOS palette, because the water colors are
|
|
|
* 245-254 for DOS and 217-226 for Windows. */
|
|
|
const ExtraPaletteValues *ev = &_extra_palette_values;
|
|
|
int c = _use_dos_palette ? 38 : 28;
|
|
|
Colour old_val[38]; // max(38, 28)
|
|
|
uint i;
|
|
|
uint j;
|
|
|
|
|
|
d = &_cur_palette[217];
|
|
|
memcpy(old_val, d, c * sizeof(*old_val));
|
|
|
|
|
|
// Dark blue water
|
|
|
/* Dark blue water */
|
|
|
s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a;
|
|
|
j = EXTR(320, 5);
|
|
|
for (i = 0; i != 5; i++) {
|
|
|
*d++ = s[j];
|
|
|
j++;
|
|
|
if (j == 5) j = 0;
|
|
|
}
|
|
|
|
|
|
// Glittery water
|
|
|
/* Glittery water */
|
|
|
s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b;
|
|
|
j = EXTR(128, 15);
|
|
|
for (i = 0; i != 5; i++) {
|
|
|
*d++ = s[j];
|
|
|
j += 3;
|
|
|
if (j >= 15) j -= 15;
|
|
|
}
|
|
|
|
|
|
s = ev->e;
|
|
|
j = EXTR2(512, 5);
|
|
|
for (i = 0; i != 5; i++) {
|
|
|
*d++ = s[j];
|
|
|
j++;
|
|
|
if (j == 5) j = 0;
|
|
|
}
|
|
|
|
|
|
// Oil refinery fire animation
|
|
|
/* Oil refinery fire animation */
|
|
|
s = ev->oil_ref;
|
|
|
j = EXTR2(512, 7);
|
|
|
for (i = 0; i != 7; i++) {
|
|
|
*d++ = s[j];
|
|
|
j++;
|
|
|
if (j == 7) j = 0;
|
|
|
}
|
|
|
|
|
|
// Radio tower blinking
|
|
|
/* Radio tower blinking */
|
|
|
{
|
|
|
byte i = (_timer_counter >> 1) & 0x7F;
|
|
|
byte v;
|
|
|
|
|
|
(v = 255, i < 0x3f) ||
|
|
|
(v = 128, i < 0x4A || i >= 0x75) ||
|
|
|
(v = 20);
|
|
|
d->r = v;
|
|
|
d->g = 0;
|
|
|
d->b = 0;
|
|
|
d++;
|
|
|
|
|
|
i ^= 0x40;
|
|
|
(v = 255, i < 0x3f) ||
|
|
|
(v = 128, i < 0x4A || i >= 0x75) ||
|
|
|
(v = 20);
|
|
|
d->r = v;
|
|
|
d->g = 0;
|
|
|
d->b = 0;
|
|
|
d++;
|
|
|
}
|
|
|
|
|
|
// Handle lighthouse and stadium animation
|
|
|
/* Handle lighthouse and stadium animation */
|
|
|
s = ev->lighthouse;
|
|
|
j = EXTR(256, 4);
|
|
|
for (i = 0; i != 4; i++) {
|
|
|
*d++ = s[j];
|
|
|
j++;
|
|
|
if (j == 4) j = 0;
|
|
|
}
|
|
|
|
|
|
// Animate water for old DOS graphics
|
|
|
/* Animate water for old DOS graphics */
|
|
|
if (_use_dos_palette) {
|
|
|
// Dark blue water DOS
|
|
|
/* Dark blue water DOS */
|
|
|
s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a;
|
|
|
j = EXTR(320, 5);
|
|
|
for (i = 0; i != 5; i++) {
|
|
|
*d++ = s[j];
|
|
|
j++;
|
|
|
if (j == 5) j = 0;
|
|
|
}
|
|
|
|
|
|
// Glittery water DOS
|
|
|
/* Glittery water DOS */
|
|
|
s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b;
|
|
|
j = EXTR(128, 15);
|
|
|
for (i = 0; i != 5; i++) {
|
|
|
*d++ = s[j];
|
|
|
j += 3;
|
|
|
if (j >= 15) j -= 15;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (memcmp(old_val, &_cur_palette[217], c * sizeof(*old_val)) != 0) {
|
|
|
if (_pal_first_dirty > 217) _pal_first_dirty = 217;
|
|
|
if (_pal_last_dirty < 217 + c) _pal_last_dirty = 217 + c;
|
|
@@ -1661,29 +1663,29 @@ void LoadStringWidthTable(void)
|
|
|
|
|
|
|
|
|
byte GetCharacterWidth(FontSize size, WChar key)
|
|
|
{
|
|
|
if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
|
|
|
|
|
|
return GetGlyphWidth(size, key);
|
|
|
}
|
|
|
|
|
|
|
|
|
void ScreenSizeChanged(void)
|
|
|
{
|
|
|
// check the dirty rect
|
|
|
/* check the dirty rect */
|
|
|
if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
|
|
|
if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
|
|
|
|
|
|
// screen size changed and the old bitmap is invalid now, so we don't want to undraw it
|
|
|
/* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
|
|
|
_cursor.visible = false;
|
|
|
}
|
|
|
|
|
|
void UndrawMouseCursor(void)
|
|
|
{
|
|
|
if (_cursor.visible) {
|
|
|
_cursor.visible = false;
|
|
|
memcpy_pitch(
|
|
|
_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
|
|
|
_cursor_backup,
|
|
|
_cursor.draw_size.x, _cursor.draw_size.y, _cursor.draw_size.x, _screen.pitch);
|
|
|
|
|
@@ -1692,25 +1694,25 @@ void UndrawMouseCursor(void)
|
|
|
}
|
|
|
|
|
|
void DrawMouseCursor(void)
|
|
|
{
|
|
|
int x;
|
|
|
int y;
|
|
|
int w;
|
|
|
int h;
|
|
|
|
|
|
/* Redraw mouse cursor but only when it's inside the window */
|
|
|
if (!_cursor.in_window) return;
|
|
|
|
|
|
// Don't draw the mouse cursor if it's already drawn
|
|
|
/* Don't draw the mouse cursor if it's already drawn */
|
|
|
if (_cursor.visible) {
|
|
|
if (!_cursor.dirty) return;
|
|
|
UndrawMouseCursor();
|
|
|
}
|
|
|
|
|
|
w = _cursor.size.x;
|
|
|
x = _cursor.pos.x + _cursor.offs.x;
|
|
|
if (x < 0) {
|
|
|
w += x;
|
|
|
x = 0;
|
|
|
}
|
|
|
if (w > _screen.width - x) w = _screen.width - x;
|
|
@@ -1722,31 +1724,31 @@ void DrawMouseCursor(void)
|
|
|
y = _cursor.pos.y + _cursor.offs.y;
|
|
|
if (y < 0) {
|
|
|
h += y;
|
|
|
y = 0;
|
|
|
}
|
|
|
if (h > _screen.height - y) h = _screen.height - y;
|
|
|
if (h <= 0) return;
|
|
|
_cursor.draw_pos.y = y;
|
|
|
_cursor.draw_size.y = h;
|
|
|
|
|
|
assert(w * h < (int)sizeof(_cursor_backup));
|
|
|
|
|
|
// Make backup of stuff below cursor
|
|
|
/* Make backup of stuff below cursor */
|
|
|
memcpy_pitch(
|
|
|
_cursor_backup,
|
|
|
_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
|
|
|
_cursor.draw_size.x, _cursor.draw_size.y, _screen.pitch, _cursor.draw_size.x);
|
|
|
|
|
|
// Draw cursor on screen
|
|
|
/* Draw cursor on screen */
|
|
|
_cur_dpi = &_screen;
|
|
|
DrawSprite(_cursor.sprite, _cursor.pal, _cursor.pos.x, _cursor.pos.y);
|
|
|
|
|
|
_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
|
|
|
|
|
|
_cursor.visible = true;
|
|
|
_cursor.dirty = false;
|
|
|
}
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
static void DbgScreenRect(int left, int top, int right, int bottom)
|
|
|
{
|
|
@@ -1797,47 +1799,47 @@ void DrawDirtyBlocks(void)
|
|
|
y = 0;
|
|
|
do {
|
|
|
x = 0;
|
|
|
do {
|
|
|
if (*b != 0) {
|
|
|
int left;
|
|
|
int top;
|
|
|
int right = x + 64;
|
|
|
int bottom = y;
|
|
|
byte *p = b;
|
|
|
int h2;
|
|
|
|
|
|
// First try coalescing downwards
|
|
|
/* First try coalescing downwards */
|
|
|
do {
|
|
|
*p = 0;
|
|
|
p += DIRTY_BYTES_PER_LINE;
|
|
|
bottom += 8;
|
|
|
} while (bottom != h && *p != 0);
|
|
|
|
|
|
// Try coalescing to the right too.
|
|
|
/* Try coalescing to the right too. */
|
|
|
h2 = (bottom - y) >> 3;
|
|
|
assert(h2 > 0);
|
|
|
p = b;
|
|
|
|
|
|
while (right != w) {
|
|
|
byte *p2 = ++p;
|
|
|
int h = h2;
|
|
|
// Check if a full line of dirty flags is set.
|
|
|
/* Check if a full line of dirty flags is set. */
|
|
|
do {
|
|
|
if (!*p2) goto no_more_coalesc;
|
|
|
p2 += DIRTY_BYTES_PER_LINE;
|
|
|
} while (--h != 0);
|
|
|
|
|
|
// Wohoo, can combine it one step to the right!
|
|
|
// Do that, and clear the bits.
|
|
|
/* Wohoo, can combine it one step to the right!
|
|
|
* Do that, and clear the bits. */
|
|
|
right += 64;
|
|
|
|
|
|
h = h2;
|
|
|
p2 = p;
|
|
|
do {
|
|
|
*p2 = 0;
|
|
|
p2 += DIRTY_BYTES_PER_LINE;
|
|
|
} while (--h != 0);
|
|
|
}
|
|
|
no_more_coalesc:
|
|
|
|
|
|
left = x;
|
|
@@ -1996,27 +1998,27 @@ static void SwitchAnimatedCursor(void)
|
|
|
_cursor.animate_timeout = cur->display_time;
|
|
|
_cursor.animate_cur = cur + 1;
|
|
|
}
|
|
|
|
|
|
void CursorTick(void)
|
|
|
{
|
|
|
if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0)
|
|
|
SwitchAnimatedCursor();
|
|
|
}
|
|
|
|
|
|
void SetMouseCursor(SpriteID sprite, SpriteID pal)
|
|
|
{
|
|
|
// Turn off animation
|
|
|
/* Turn off animation */
|
|
|
_cursor.animate_timeout = 0;
|
|
|
// Set cursor
|
|
|
/* Set cursor */
|
|
|
SetCursorSprite(sprite, pal);
|
|
|
}
|
|
|
|
|
|
void SetAnimatedMouseCursor(const AnimCursor *table)
|
|
|
{
|
|
|
_cursor.animate_list = table;
|
|
|
_cursor.animate_cur = NULL;
|
|
|
_cursor.pal = PAL_NONE;
|
|
|
SwitchAnimatedCursor();
|
|
|
}
|
|
|
|
|
|
bool ChangeResInGame(int w, int h)
|