mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-23 02:11:38 +00:00
1d3ca2e409
svn-id: r24808
814 lines
18 KiB
C++
814 lines
18 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2006 The ScummVM project
|
|
*
|
|
* Copyright (C) 1999-2003 Sarien Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "agi/agi.h"
|
|
#include "agi/sprite.h"
|
|
#include "agi/graphics.h"
|
|
#include "agi/text.h"
|
|
#include "agi/savegame.h"
|
|
|
|
namespace Agi {
|
|
|
|
/**
|
|
* Sprite structure.
|
|
* This structure holds information on visible and priority data of
|
|
* a rectangular area of the AGI screen. Sprites are chained in two
|
|
* circular lists, one for updating and other for non-updating sprites.
|
|
*/
|
|
struct sprite {
|
|
vt_entry *v; /**< pointer to view table entry */
|
|
int16 x_pos; /**< x coordinate of the sprite */
|
|
int16 y_pos; /**< y coordinate of the sprite */
|
|
int16 x_size; /**< width of the sprite */
|
|
int16 y_size; /**< height of the sprite */
|
|
uint8 *buffer; /**< buffer to store background data */
|
|
uint8 *hires; /**< buffer for hi-res background */
|
|
};
|
|
|
|
/*
|
|
* Sprite pool replaces dynamic allocation
|
|
*/
|
|
#undef ALLOC_DEBUG
|
|
|
|
|
|
#define POOL_SIZE 68000 /* Gold Rush mine room needs > 50000 */
|
|
/* Speeder bike challenge needs > 67000 */
|
|
|
|
void *SpritesMgr::pool_alloc(int size) {
|
|
uint8 *x;
|
|
|
|
/* Adjust size to 32-bit boundary to prevent data misalignment
|
|
* errors.
|
|
*/
|
|
size = (size + 3) & ~3;
|
|
|
|
x = pool_top;
|
|
pool_top += size;
|
|
|
|
if (pool_top >= (uint8 *)sprite_pool + POOL_SIZE) {
|
|
debugC(1, kDebugLevelMain | kDebugLevelResources, "not enough memory");
|
|
pool_top = x;
|
|
return NULL;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
/* Note: it's critical that pool_release() is called in the exact
|
|
reverse order of pool_alloc()
|
|
*/
|
|
void SpritesMgr::pool_release(void *s) {
|
|
pool_top = (uint8 *)s;
|
|
}
|
|
|
|
/*
|
|
* Blitter functions
|
|
*/
|
|
|
|
/* Blit one pixel considering the priorities */
|
|
|
|
void SpritesMgr::blit_pixel(uint8 *p, uint8 *end, uint8 col, int spr, int width, int *hidden) {
|
|
int epr = 0, pr = 0; /* effective and real priorities */
|
|
|
|
/* CM: priority 15 overrides control lines and is ignored when
|
|
* tracking effective priority. This tweak is needed to fix
|
|
* bug #451768, and should not affect Sierra games because
|
|
* sprites shouldn't have priority 15 (like the AGI Mouse
|
|
* demo "mouse pointer")
|
|
*
|
|
* Update: this solution breaks other games, and can't be used.
|
|
*/
|
|
|
|
if (p >= end)
|
|
return;
|
|
|
|
/* Check if we're on a control line */
|
|
if ((pr = *p & 0xf0) < 0x30) {
|
|
uint8 *p1;
|
|
/* Yes, get effective priority going down */
|
|
for (p1 = p; p1 < end && (epr = *p1 & 0xf0) < 0x30;
|
|
p1 += width);
|
|
if (p1 >= end)
|
|
epr = 0x40;
|
|
} else {
|
|
epr = pr;
|
|
}
|
|
|
|
if (spr >= epr) {
|
|
/* Keep control line information visible, but put our
|
|
* priority over water (0x30) surface
|
|
*/
|
|
*p = (pr < 0x30 ? pr : spr) | col;
|
|
*hidden = false;
|
|
|
|
/* Except if our priority is 15, which should never happen
|
|
* (fixes bug #451768)
|
|
*
|
|
* Update: breaks other games, can't be used
|
|
*
|
|
* if (spr == 0xf0)
|
|
* *p = spr | col;
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
#define X_FACT 2 /* Horizontal hires factor */
|
|
|
|
int SpritesMgr::blit_hires_cel(int x, int y, int spr, view_cel *c) {
|
|
uint8 *q = NULL;
|
|
uint8 *h0, *h, *end;
|
|
int i, j, t, m, col;
|
|
int hidden = true;
|
|
|
|
q = c->data;
|
|
t = c->transparency;
|
|
m = c->mirror;
|
|
spr <<= 4;
|
|
h0 = &_vm->game.hires[(x + y * _WIDTH + m * (c->width - 1)) * X_FACT];
|
|
|
|
end = _vm->game.hires + _WIDTH * X_FACT * _HEIGHT;
|
|
|
|
for (i = 0; i < c->height; i++) {
|
|
h = h0;
|
|
while (*q) {
|
|
col = (*q & 0xf0) >> 4;
|
|
for (j = *q & 0x0f; j; j--, h += X_FACT * (1 - 2 * m)) {
|
|
if (col != t) {
|
|
blit_pixel(h, end, col, spr, _WIDTH * X_FACT, &hidden);
|
|
blit_pixel(h + 1, end, col, spr, _WIDTH * X_FACT, &hidden);
|
|
}
|
|
}
|
|
q++;
|
|
}
|
|
h0 += _WIDTH * X_FACT;
|
|
q++;
|
|
}
|
|
return hidden;
|
|
}
|
|
|
|
int SpritesMgr::blit_cel(int x, int y, int spr, view_cel *c) {
|
|
uint8 *p0, *p, *q = NULL, *end;
|
|
int i, j, t, m, col;
|
|
int hidden = true;
|
|
|
|
/* Fixes bug #477841 (crash in PQ1 map C4 when y == -2) */
|
|
if (y < 0)
|
|
y = 0;
|
|
if (x < 0)
|
|
x = 0;
|
|
if (y >= _HEIGHT)
|
|
y = _HEIGHT - 1;
|
|
if (x >= _WIDTH)
|
|
x = _WIDTH - 1;
|
|
|
|
if (_vm->opt.hires)
|
|
blit_hires_cel(x, y, spr, c);
|
|
|
|
q = c->data;
|
|
t = c->transparency;
|
|
m = c->mirror;
|
|
spr <<= 4;
|
|
p0 = &_vm->game.sbuf[x + y * _WIDTH + m * (c->width - 1)];
|
|
|
|
end = _vm->game.sbuf + _WIDTH * _HEIGHT;
|
|
|
|
for (i = 0; i < c->height; i++) {
|
|
p = p0;
|
|
while (*q) {
|
|
col = (*q & 0xf0) >> 4;
|
|
for (j = *q & 0x0f; j; j--, p += 1 - 2 * m) {
|
|
if (col != t) {
|
|
blit_pixel(p, end, col, spr, _WIDTH, &hidden);
|
|
}
|
|
}
|
|
q++;
|
|
}
|
|
p0 += _WIDTH;
|
|
q++;
|
|
}
|
|
|
|
return hidden;
|
|
}
|
|
|
|
void SpritesMgr::objs_savearea(sprite *s) {
|
|
int y;
|
|
int16 x_pos = s->x_pos, y_pos = s->y_pos;
|
|
int16 x_size = s->x_size, y_size = s->y_size;
|
|
uint8 *p0, *q;
|
|
uint8 *h0, *k;
|
|
|
|
if (x_pos + x_size > _WIDTH)
|
|
x_size = _WIDTH - x_pos;
|
|
|
|
if (x_pos < 0) {
|
|
x_size += x_pos;
|
|
x_pos = 0;
|
|
}
|
|
|
|
if (y_pos + y_size > _HEIGHT)
|
|
y_size = _HEIGHT - y_pos;
|
|
|
|
if (y_pos < 0) {
|
|
y_size += y_pos;
|
|
y_pos = 0;
|
|
}
|
|
|
|
if (x_size <= 0 || y_size <= 0)
|
|
return;
|
|
|
|
p0 = &_vm->game.sbuf[x_pos + y_pos * _WIDTH];
|
|
q = s->buffer;
|
|
h0 = &_vm->game.hires[(x_pos + y_pos * _WIDTH) * 2];
|
|
k = s->hires;
|
|
for (y = 0; y < y_size; y++) {
|
|
memcpy(q, p0, x_size);
|
|
q += x_size;
|
|
p0 += _WIDTH;
|
|
memcpy(k, h0, x_size * 2);
|
|
k += x_size * 2;
|
|
h0 += _WIDTH * 2;
|
|
}
|
|
}
|
|
|
|
void SpritesMgr::objs_restorearea(sprite *s) {
|
|
int y, offset;
|
|
int16 x_pos = s->x_pos, y_pos = s->y_pos;
|
|
int16 x_size = s->x_size, y_size = s->y_size;
|
|
uint8 *p0, *q;
|
|
uint8 *h0, *k;
|
|
|
|
if (x_pos + x_size > _WIDTH)
|
|
x_size = _WIDTH - x_pos;
|
|
|
|
if (x_pos < 0) {
|
|
x_size += x_pos;
|
|
x_pos = 0;
|
|
}
|
|
|
|
if (y_pos + y_size > _HEIGHT)
|
|
y_size = _HEIGHT - y_pos;
|
|
|
|
if (y_pos < 0) {
|
|
y_size += y_pos;
|
|
y_pos = 0;
|
|
}
|
|
|
|
if (x_size <= 0 || y_size <= 0)
|
|
return;
|
|
|
|
p0 = &_vm->game.sbuf[x_pos + y_pos * _WIDTH];
|
|
q = s->buffer;
|
|
h0 = &_vm->game.hires[(x_pos + y_pos * _WIDTH) * 2];
|
|
k = s->hires;
|
|
offset = _vm->game.line_min_print * CHAR_LINES;
|
|
for (y = 0; y < y_size; y++) {
|
|
memcpy(p0, q, x_size);
|
|
_gfx->putPixelsA(x_pos, y_pos + y + offset, x_size, p0);
|
|
q += x_size;
|
|
p0 += _WIDTH;
|
|
memcpy(h0, k, x_size * 2);
|
|
if (_vm->opt.hires) {
|
|
_gfx->putPixelsHires(x_pos * 2, y_pos + y + offset, x_size * 2, h0);
|
|
}
|
|
k += x_size * 2;
|
|
h0 += _WIDTH * 2;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Condition to determine whether a sprite will be in the 'updating' list.
|
|
*/
|
|
bool SpritesMgr::test_updating(vt_entry *v, AgiEngine *agi) {
|
|
/* Sanity check (see bug #779302) */
|
|
if (~agi->game.dir_view[v->current_view].flags & RES_LOADED)
|
|
return false;
|
|
|
|
return (v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | UPDATE | DRAWN);
|
|
}
|
|
|
|
/**
|
|
* Condition to determine whether a sprite will be in the 'non-updating' list.
|
|
*/
|
|
bool SpritesMgr::test_not_updating(vt_entry *v, AgiEngine *agi) {
|
|
/* Sanity check (see bug #779302) */
|
|
if (~agi->game.dir_view[v->current_view].flags & RES_LOADED)
|
|
return false;
|
|
|
|
return (v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | DRAWN);
|
|
}
|
|
|
|
/**
|
|
* Convert sprite priority to y value.
|
|
*/
|
|
INLINE int SpritesMgr::prio_to_y(int p) {
|
|
int i;
|
|
|
|
if (p == 0)
|
|
return -1;
|
|
|
|
for (i = 167; i >= 0; i--) {
|
|
if (_vm->game.pri_table[i] < p)
|
|
return i;
|
|
}
|
|
|
|
return -1; /* (p - 5) * 12 + 48; */
|
|
}
|
|
|
|
/**
|
|
* Create and initialize a new sprite structure.
|
|
*/
|
|
sprite *SpritesMgr::new_sprite(vt_entry *v) {
|
|
sprite *s;
|
|
s = (sprite *)pool_alloc(sizeof(sprite));
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
s->v = v; /* link sprite to associated view table entry */
|
|
s->x_pos = v->x_pos;
|
|
s->y_pos = v->y_pos - v->y_size + 1;
|
|
s->x_size = v->x_size;
|
|
s->y_size = v->y_size;
|
|
s->buffer = (uint8 *) pool_alloc(s->x_size * s->y_size);
|
|
s->hires = (uint8 *) pool_alloc(s->x_size * s->y_size * 2);
|
|
v->s = s; /* link view table entry to this sprite */
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Insert sprite in the specified sprite list.
|
|
*/
|
|
void SpritesMgr::spr_addlist(SpriteList& l, vt_entry *v) {
|
|
sprite *s = new_sprite(v);
|
|
l.push_back(s);
|
|
}
|
|
|
|
/**
|
|
* Sort sprites from lower y values to build a sprite list.
|
|
*/
|
|
void SpritesMgr::build_list(SpriteList& l, bool (*test) (vt_entry *, AgiEngine *)) {
|
|
int i, j, k;
|
|
vt_entry *v;
|
|
vt_entry *entry[0x100];
|
|
int y_val[0x100];
|
|
int min_y = 0xff, min_index = 0;
|
|
|
|
/* fill the arrays with all sprites that satisfy the 'test'
|
|
* condition and their y values
|
|
*/
|
|
i = 0;
|
|
for (v = _vm->game.view_table; v < &_vm->game.view_table[MAX_VIEWTABLE]; v++) {
|
|
if ((*test)(v, _vm)) {
|
|
entry[i] = v;
|
|
y_val[i] = v->flags & FIXED_PRIORITY ? prio_to_y(v->priority) : v->y_pos;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/* now look for the smallest y value in the array and put that
|
|
* sprite in the list
|
|
*/
|
|
for (j = 0; j < i; j++) {
|
|
min_y = 0xff;
|
|
for (k = 0; k < i; k++) {
|
|
if (y_val[k] < min_y) {
|
|
min_index = k;
|
|
min_y = y_val[k];
|
|
}
|
|
}
|
|
|
|
y_val[min_index] = 0xff;
|
|
spr_addlist(l, entry[min_index]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build list of updating sprites.
|
|
*/
|
|
void SpritesMgr::build_upd_blitlist() {
|
|
build_list(spr_upd, test_updating);
|
|
}
|
|
|
|
/**
|
|
* Build list of non-updating sprites.
|
|
*/
|
|
void SpritesMgr::build_nonupd_blitlist() {
|
|
build_list(spr_nonupd, test_not_updating);
|
|
}
|
|
|
|
/**
|
|
* Clear the given sprite list.
|
|
*/
|
|
void SpritesMgr::free_list(SpriteList& l) {
|
|
SpriteList::iterator iter;
|
|
for (iter = l.reverse_begin(); iter != l.end(); ) {
|
|
sprite* s = *iter;
|
|
pool_release(s->hires);
|
|
pool_release(s->buffer);
|
|
pool_release(s);
|
|
iter = l.reverse_erase(iter);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy sprites from the pic buffer to the screen buffer, and check if
|
|
* sprites of the given list have moved.
|
|
*/
|
|
void SpritesMgr::commit_sprites(SpriteList& l) {
|
|
SpriteList::iterator iter;
|
|
for (iter = l.begin(); iter != l.end(); ++iter) {
|
|
sprite *s = *iter;
|
|
int x1, y1, x2, y2, w, h;
|
|
|
|
w = (s->v->cel_data->width > s->v->cel_data_2->width) ?
|
|
s->v->cel_data->width : s->v->cel_data_2->width;
|
|
|
|
h = (s->v->cel_data->height >
|
|
s->v->cel_data_2->height) ? s->v->cel_data->
|
|
height : s->v->cel_data_2->height;
|
|
|
|
s->v->cel_data_2 = s->v->cel_data;
|
|
|
|
if (s->v->x_pos < s->v->x_pos2) {
|
|
x1 = s->v->x_pos;
|
|
x2 = s->v->x_pos2 + w - 1;
|
|
} else {
|
|
x1 = s->v->x_pos2;
|
|
x2 = s->v->x_pos + w - 1;
|
|
}
|
|
|
|
if (s->v->y_pos < s->v->y_pos2) {
|
|
y1 = s->v->y_pos - h + 1;
|
|
y2 = s->v->y_pos2;
|
|
} else {
|
|
y1 = s->v->y_pos2 - h + 1;
|
|
y2 = s->v->y_pos;
|
|
}
|
|
|
|
commit_block(x1, y1, x2, y2);
|
|
|
|
if (s->v->step_time_count != s->v->step_time)
|
|
continue;
|
|
|
|
if (s->v->x_pos == s->v->x_pos2 && s->v->y_pos == s->v->y_pos2) {
|
|
s->v->flags |= DIDNT_MOVE;
|
|
continue;
|
|
}
|
|
|
|
s->v->x_pos2 = s->v->x_pos;
|
|
s->v->y_pos2 = s->v->y_pos;
|
|
s->v->flags &= ~DIDNT_MOVE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Erase all sprites in the given list.
|
|
*/
|
|
void SpritesMgr::erase_sprites(SpriteList& l) {
|
|
SpriteList::iterator iter;
|
|
for (iter = l.reverse_begin(); iter != l.end(); --iter) {
|
|
sprite *s = *iter;
|
|
objs_restorearea(s);
|
|
}
|
|
|
|
free_list(l);
|
|
}
|
|
|
|
/**
|
|
* Blit all sprites in the given list.
|
|
*/
|
|
void SpritesMgr::blit_sprites(SpriteList& l) {
|
|
int hidden;
|
|
SpriteList::iterator iter;
|
|
for (iter = l.begin(); iter != l.end(); ++iter) {
|
|
sprite *s = *iter;
|
|
objs_savearea(s);
|
|
debugC(8, kDebugLevelSprites, "s->v->entry = %d (prio %d)", s->v->entry, s->v->priority);
|
|
hidden = blit_cel(s->x_pos, s->y_pos, s->v->priority, s->v->cel_data);
|
|
if (s->v->entry == 0) { /* if ego, update f1 */
|
|
_vm->setflag(F_ego_invisible, hidden);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Public functions
|
|
*/
|
|
|
|
void SpritesMgr::commit_upd_sprites() {
|
|
commit_sprites(spr_upd);
|
|
}
|
|
|
|
void SpritesMgr::commit_nonupd_sprites() {
|
|
commit_sprites(spr_nonupd);
|
|
}
|
|
|
|
/* check moves in both lists */
|
|
void SpritesMgr::commit_both() {
|
|
commit_upd_sprites();
|
|
commit_nonupd_sprites();
|
|
}
|
|
|
|
/**
|
|
* Erase updating sprites.
|
|
* This function follows the list of all updating sprites and restores
|
|
* the visible and priority data of their background buffers back to
|
|
* the AGI screen.
|
|
*
|
|
* @see erase_nonupd_sprites()
|
|
* @see erase_both()
|
|
*/
|
|
void SpritesMgr::erase_upd_sprites() {
|
|
erase_sprites(spr_upd);
|
|
}
|
|
|
|
/**
|
|
* Erase non-updating sprites.
|
|
* This function follows the list of all non-updating sprites and restores
|
|
* the visible and priority data of their background buffers back to
|
|
* the AGI screen.
|
|
*
|
|
* @see erase_upd_sprites()
|
|
* @see erase_both()
|
|
*/
|
|
void SpritesMgr::erase_nonupd_sprites() {
|
|
erase_sprites(spr_nonupd);
|
|
}
|
|
|
|
/**
|
|
* Erase all sprites.
|
|
* This function follows the lists of all updating and non-updating
|
|
* sprites and restores the visible and priority data of their background
|
|
* buffers back to the AGI screen.
|
|
*
|
|
* @see erase_upd_sprites()
|
|
* @see erase_nonupd_sprites()
|
|
*/
|
|
void SpritesMgr::erase_both() {
|
|
erase_upd_sprites();
|
|
erase_nonupd_sprites();
|
|
}
|
|
|
|
/**
|
|
* Blit updating sprites.
|
|
* This function follows the list of all updating sprites and blits
|
|
* them on the AGI screen.
|
|
*
|
|
* @see blit_nonupd_sprites()
|
|
* @see blit_both()
|
|
*/
|
|
void SpritesMgr::blit_upd_sprites() {
|
|
debugC(7, kDebugLevelSprites, "blit updating");
|
|
build_upd_blitlist();
|
|
blit_sprites(spr_upd);
|
|
}
|
|
|
|
/**
|
|
* Blit non-updating sprites.
|
|
* This function follows the list of all non-updating sprites and blits
|
|
* them on the AGI screen.
|
|
*
|
|
* @see blit_upd_sprites()
|
|
* @see blit_both()
|
|
*/
|
|
void SpritesMgr::blit_nonupd_sprites() {
|
|
debugC(7, kDebugLevelSprites, "blit non-updating");
|
|
build_nonupd_blitlist();
|
|
blit_sprites(spr_nonupd);
|
|
}
|
|
|
|
/**
|
|
* Blit all sprites.
|
|
* This function follows the lists of all updating and non-updating
|
|
* sprites and blits them on the AGI screen.
|
|
*
|
|
* @see blit_upd_sprites()
|
|
* @see blit_nonupd_sprites()
|
|
*/
|
|
void SpritesMgr::blit_both() {
|
|
blit_nonupd_sprites();
|
|
blit_upd_sprites();
|
|
}
|
|
|
|
/**
|
|
* Add view to picture.
|
|
* This function is used to implement the add.to.pic AGI command. It
|
|
* copies the specified cel from a view resource on the current picture.
|
|
* This cel is not a sprite, it can't be moved or removed.
|
|
* @param view number of view resource
|
|
* @param loop number of loop in the specified view resource
|
|
* @param cel number of cel in the specified loop
|
|
* @param x x coordinate to place the view
|
|
* @param y y coordinate to place the view
|
|
* @param pri priority to use
|
|
* @param mar if < 4, create a margin around the the base of the cel
|
|
*/
|
|
void SpritesMgr::add_to_pic(int view, int loop, int cel, int x, int y, int pri, int mar) {
|
|
view_cel *c = NULL;
|
|
int x1, y1, x2, y2, y3;
|
|
uint8 *p1, *p2;
|
|
|
|
debugC(3, kDebugLevelSprites, "v=%d, l=%d, c=%d, x=%d, y=%d, p=%d, m=%d", view, loop, cel, x, y, pri, mar);
|
|
|
|
_vm->record_image_stack_call(ADD_VIEW, view, loop, cel, x, y, pri, mar);
|
|
|
|
/*
|
|
* Was hardcoded to 8, changed to pri_table[y] to fix Gold
|
|
* Rush (see bug #587558)
|
|
*/
|
|
if (pri == 0)
|
|
pri = _vm->game.pri_table[y];
|
|
|
|
c = &_vm->game.views[view].loop[loop].cel[cel];
|
|
|
|
x1 = x;
|
|
y1 = y - c->height + 1;
|
|
x2 = x + c->width - 1;
|
|
y2 = y;
|
|
|
|
if (x1 < 0) {
|
|
x2 -= x1;
|
|
x1 = 0;
|
|
}
|
|
if (y1 < 0) {
|
|
y2 -= y1;
|
|
y1 = 0;
|
|
}
|
|
if (x2 >= _WIDTH)
|
|
x2 = _WIDTH - 1;
|
|
if (y2 >= _HEIGHT)
|
|
y2 = _HEIGHT - 1;
|
|
|
|
erase_both();
|
|
|
|
debugC(4, kDebugLevelSprites, "blit_cel (%d, %d, %d, c)", x, y, pri);
|
|
blit_cel(x1, y1, pri, c);
|
|
|
|
/* If margin is 0, 1, 2, or 3, the base of the cel is
|
|
* surrounded with a rectangle of the corresponding priority.
|
|
* If margin >= 4, this extra margin is not shown.
|
|
*/
|
|
if (mar < 4) {
|
|
/* add rectangle around object, don't clobber control
|
|
* info in priority data. The box extends to the end of
|
|
* its priority band!
|
|
*
|
|
* SQ1 needs +1 (see bug #810331)
|
|
*/
|
|
y3 = (y2 / 12) * 12 + 1;
|
|
|
|
// don't let box extend below y.
|
|
if (y3 > y2) y3 = y2;
|
|
|
|
p1 = &_vm->game.sbuf[x1 + y3 * _WIDTH];
|
|
p2 = &_vm->game.sbuf[x2 + y3 * _WIDTH];
|
|
|
|
for (y = y3; y <= y2; y++) {
|
|
if ((*p1 >> 4) >= 4)
|
|
*p1 = (mar << 4) | (*p1 & 0x0f);
|
|
if ((*p2 >> 4) >= 4)
|
|
*p2 = (mar << 4) | (*p2 & 0x0f);
|
|
p1 += _WIDTH;
|
|
p2 += _WIDTH;
|
|
}
|
|
|
|
debugC(4, kDebugLevelSprites, "pri box: %d %d %d %d (%d)", x1, y3, x2, y2, mar);
|
|
p1 = &_vm->game.sbuf[x1 + y3 * _WIDTH];
|
|
p2 = &_vm->game.sbuf[x1 + y2 * _WIDTH];
|
|
for (x = x1; x <= x2; x++) {
|
|
if ((*p1 >> 4) >= 4)
|
|
*p1 = (mar << 4) | (*p1 & 0x0f);
|
|
if ((*p2 >> 4) >= 4)
|
|
*p2 = (mar << 4) | (*p2 & 0x0f);
|
|
p1++;
|
|
p2++;
|
|
}
|
|
}
|
|
|
|
blit_both();
|
|
|
|
debugC(4, kDebugLevelSprites, "commit_block (%d, %d, %d, %d)", x1, y1, x2, y2);
|
|
commit_block(x1, y1, x2, y2);
|
|
}
|
|
|
|
/**
|
|
* Show object and description
|
|
* This function shows an object from the player's inventory, displaying
|
|
* a message box with the object description.
|
|
* @param n Number of the object to show
|
|
*/
|
|
void SpritesMgr::show_obj(int n) {
|
|
view_cel *c;
|
|
sprite s;
|
|
int x1, y1, x2, y2;
|
|
|
|
_vm->agiLoadResource(rVIEW, n);
|
|
if (!(c = &_vm->game.views[n].loop[0].cel[0]))
|
|
return;
|
|
|
|
x1 = (_WIDTH - c->width) / 2;
|
|
y1 = 112;
|
|
x2 = x1 + c->width - 1;
|
|
y2 = y1 + c->height - 1;
|
|
|
|
s.x_pos = x1;
|
|
s.y_pos = y1;
|
|
s.x_size = c->width;
|
|
s.y_size = c->height;
|
|
s.buffer = (uint8 *)malloc(s.x_size * s.y_size);
|
|
s.hires = (uint8 *)malloc(s.x_size * s.y_size * 2);
|
|
|
|
objs_savearea(&s);
|
|
blit_cel(x1, y1, s.x_size, c);
|
|
commit_block(x1, y1, x2, y2);
|
|
_vm->message_box(_vm->game.views[n].descr);
|
|
objs_restorearea(&s);
|
|
commit_block(x1, y1, x2, y2);
|
|
|
|
free(s.buffer);
|
|
|
|
/* Added to fix a memory leak --Vasyl */
|
|
free(s.hires);
|
|
}
|
|
|
|
void SpritesMgr::commit_block(int x1, int y1, int x2, int y2) {
|
|
int i, w, offset;
|
|
uint8 *q;
|
|
uint8 *h;
|
|
|
|
if (!_vm->game.picture_shown)
|
|
return;
|
|
|
|
/* Clipping */
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
if (x2 < 0)
|
|
x2 = 0;
|
|
if (y1 < 0)
|
|
y1 = 0;
|
|
if (y2 < 0)
|
|
y2 = 0;
|
|
if (x1 >= _WIDTH)
|
|
x1 = _WIDTH - 1;
|
|
if (x2 >= _WIDTH)
|
|
x2 = _WIDTH - 1;
|
|
if (y1 >= _HEIGHT)
|
|
y1 = _HEIGHT - 1;
|
|
if (y2 >= _HEIGHT)
|
|
y2 = _HEIGHT - 1;
|
|
|
|
debugC(7, kDebugLevelSprites, "%d, %d, %d, %d", x1, y1, x2, y2);
|
|
|
|
w = x2 - x1 + 1;
|
|
q = &_vm->game.sbuf[x1 + _WIDTH * y1];
|
|
h = &_vm->game.hires[(x1 + _WIDTH * y1) * 2];
|
|
offset = _vm->game.line_min_print * CHAR_LINES;
|
|
for (i = y1; i <= y2; i++) {
|
|
_gfx->putPixelsA(x1, i + offset, w, q);
|
|
q += _WIDTH;
|
|
if (_vm->opt.hires) {
|
|
_gfx->putPixelsHires(x1 * 2, i + offset, w * 2, h);
|
|
}
|
|
h += _WIDTH * 2;
|
|
}
|
|
|
|
_gfx->flushBlockA(x1, y1 + offset, x2, y2 + offset);
|
|
}
|
|
|
|
SpritesMgr::SpritesMgr(AgiEngine *agi, GfxMgr *gfx) {
|
|
_vm = agi;
|
|
_gfx = gfx;
|
|
|
|
sprite_pool = (uint8 *)malloc(POOL_SIZE);
|
|
pool_top = sprite_pool;
|
|
}
|
|
|
|
SpritesMgr::~SpritesMgr() {
|
|
free(sprite_pool);
|
|
}
|
|
|
|
} // End of namespace Agi
|