mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-12 12:09:15 +00:00
377 lines
9.2 KiB
C++
377 lines
9.2 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "chamber/chamber.h"
|
|
#include "chamber/common.h"
|
|
#include "chamber/portrait.h"
|
|
#include "chamber/resdata.h"
|
|
#include "chamber/room.h"
|
|
#include "chamber/cga.h"
|
|
#include "chamber/script.h"
|
|
#include "chamber/dialog.h"
|
|
#include "chamber/input.h"
|
|
#include "chamber/sound.h"
|
|
#include "chamber/ifgm.h"
|
|
|
|
namespace Chamber {
|
|
|
|
|
|
extern uint16 cpu_speed_delay;
|
|
|
|
byte *cur_image_pixels;
|
|
byte cur_image_size_w;
|
|
byte cur_image_size_h;
|
|
byte cur_image_coords_x;
|
|
byte cur_image_coords_y;
|
|
uint16 cur_image_offs;
|
|
uint16 cur_image_end;
|
|
byte cur_image_idx;
|
|
byte cur_image_anim1;
|
|
byte cur_image_anim2;
|
|
uint16 cur_frame_width;
|
|
|
|
typedef struct persframe_t {
|
|
byte height;
|
|
byte width;
|
|
byte topbot; /*border and fill colors*/
|
|
byte fill;
|
|
byte left;
|
|
byte right;
|
|
} persframe_t;
|
|
|
|
persframe_t pers_frames[] = {
|
|
{65, 16, 0xFF, 0xAA, 0xEA, 0xAB},
|
|
{70, 16, 0xFF, 0xAA, 0xEA, 0xAB},
|
|
{65, 17, 0xFF, 0xAA, 0xEA, 0xAB},
|
|
{75, 17, 0xFF, 0xAA, 0xEA, 0xAB},
|
|
{85, 16, 0xFF, 0xAA, 0xEA, 0xAB},
|
|
{47, 13, 0xFF, 0, 0xC0, 3},
|
|
{65, 18, 0xFF, 0xAA, 0xEA, 0xAB},
|
|
{38, 11, 0xFF, 0, 0xC0, 3},
|
|
{27, 34, 0, 0, 0, 0}
|
|
};
|
|
|
|
void makePortraitFrame(byte index, byte *target) {
|
|
uint16 i;
|
|
persframe_t *pframe = &pers_frames[index];
|
|
*target++ = pframe->height;
|
|
*target++ = pframe->width;
|
|
cur_frame_width = pframe->width;
|
|
memset(target, pframe->topbot, pframe->width);
|
|
target += pframe->width;
|
|
for (i = 0; i < pframe->height - 2; i++) {
|
|
*target++ = pframe->left;
|
|
memset(target, pframe->fill, pframe->width - 2);
|
|
target += pframe->width - 2;
|
|
*target++ = pframe->right;
|
|
}
|
|
memset(target, pframe->topbot, pframe->width);
|
|
}
|
|
|
|
/*TODO: move this to CGA ?*/
|
|
/*
|
|
Superimpose source sprite data over target image data
|
|
*/
|
|
void mergeImageAndSpriteData(byte *target, int16 pitch, byte *source, uint16 w, uint16 h) {
|
|
uint16 x;
|
|
while (h--) {
|
|
for (x = 0; x < w; x++) {
|
|
byte m = *source++;
|
|
*target &= m;
|
|
*target++ |= *source++;
|
|
}
|
|
target -= w;
|
|
target += pitch;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Superimpose horizontally-flipped source sprite data over target image data
|
|
*/
|
|
void mergeImageAndSpriteDataFlip(byte *target, int16 pitch, byte *source, uint16 w, uint16 h) {
|
|
uint16 x;
|
|
target += w - 1;
|
|
while (h--) {
|
|
for (x = 0; x < w; x++) {
|
|
byte m = cga_pixel_flip[*source++];
|
|
*target &= m;
|
|
*target |= cga_pixel_flip[*source++];
|
|
target -= 1;
|
|
}
|
|
target += w;
|
|
target += pitch;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Build portrait from multiple pers sprites
|
|
*/
|
|
byte *loadPortrait(byte **pinfo, byte *end) {
|
|
while (*pinfo != end) {
|
|
byte index;
|
|
uint16 flags;
|
|
int16 pitch;
|
|
byte *buffer, *sprite;
|
|
byte sprw, sprh;
|
|
|
|
index = *((*pinfo)++);
|
|
flags = *((*pinfo)++);
|
|
flags |= (*((*pinfo)++)) << 8;
|
|
buffer = sprit_load_buffer + 2 + 2 + (flags & 0x3FFF);
|
|
pitch = cur_frame_width;
|
|
|
|
sprite = loadPersSprit(index);
|
|
sprw = *sprite++;
|
|
sprh = *sprite++;
|
|
|
|
if (flags & 0x8000) { /*vertical flip*/
|
|
buffer += pitch * (sprh - 1);
|
|
pitch = -pitch;
|
|
}
|
|
if (flags & 0x4000) /*horizontal flip*/
|
|
mergeImageAndSpriteDataFlip(buffer, pitch, sprite, sprw, sprh);
|
|
else
|
|
mergeImageAndSpriteData(buffer, pitch, sprite, sprw, sprh);
|
|
}
|
|
return sprit_load_buffer + 2;
|
|
}
|
|
|
|
byte *loadPortraitWithFrame(byte index) {
|
|
byte *pinfo, *end;
|
|
pinfo = seekToEntry(icone_data, index, &end);
|
|
makePortraitFrame(*pinfo++, sprit_load_buffer + 2);
|
|
return loadPortrait(&pinfo, end);
|
|
}
|
|
|
|
|
|
#define STATIC_ANIMS_MAX 24
|
|
|
|
struct {
|
|
byte index;
|
|
byte image;
|
|
byte x;
|
|
byte y;
|
|
byte anim1;
|
|
byte anim2;
|
|
} static_anims[] = {
|
|
{ 24, 13, 35, 10, 4, 5},
|
|
{ 88, 42, 35, 10, 11, 12},
|
|
{152, 50, 35, 10, 13, 14},
|
|
{216, 58, 35, 10, 15, 16},
|
|
{ 40, 9, 30, 20, 3, 3},
|
|
{ 48, 1, 35, 20, 1, 2},
|
|
{ 32, 66, 35, 20, 17, 18},
|
|
{128, 21, 20, 10, 6, 6},
|
|
{192, 25, 2, 70, 7, 7},
|
|
{ 56, 85, 25, 20, 26, 27},
|
|
{ 64, 74, 56, 85, 23, 23},
|
|
{ 72, 74, 56, 85, 23, 23},
|
|
{ 80, 78, 27, 20, 24, 24},
|
|
{144, 80, 27, 20, 25, 25},
|
|
{ 96, 100, 27, 20, 29, 29},
|
|
{104, 92, 27, 20, 28, 28},
|
|
{112, 100, 27, 20, 29, 53},
|
|
{224, 96, 27, 20, 48, 48},
|
|
{232, 92, 27, 20, 47, 47},
|
|
{184, 160, 27, 20, 50, 52},
|
|
{200, 78, 27, 20, 24, 24},
|
|
{160, 106, 33, 2, 49, 49},
|
|
{168, 147, 16, 2, 32, 32},
|
|
{248, 117, 16, 2, 33, 33}
|
|
};
|
|
|
|
byte selectCurrentAnim(byte *x, byte *y, byte *index) {
|
|
int16 i;
|
|
byte aniidx = ((pers_t *)(script_vars[kScrPool8_CurrentPers]))->index & ~7;
|
|
for (i = 0; i < STATIC_ANIMS_MAX; i++) {
|
|
if (static_anims[i].index == aniidx) {
|
|
*x = static_anims[i].x;
|
|
*y = static_anims[i].y;
|
|
*index = static_anims[i].image;
|
|
cur_image_anim1 = static_anims[i].anim1;
|
|
cur_image_anim2 = static_anims[i].anim2;
|
|
return 1;
|
|
}
|
|
}
|
|
warning("SelectCurrentAnim: not found for %d", aniidx);
|
|
return 0;
|
|
}
|
|
|
|
void drawBoxAroundSpot(void) {
|
|
byte *buffer;
|
|
uint16 w, h;
|
|
uint16 ofs;
|
|
uint16 x, y;
|
|
|
|
if (*spot_sprite == 0)
|
|
return;
|
|
zone_spots_cur = found_spot;
|
|
zone_spr_index = script_byte_vars.cur_spot_idx - 1;
|
|
|
|
buffer = *spot_sprite;
|
|
|
|
h = *(byte *)(buffer + 0);
|
|
w = *(byte *)(buffer + 1);
|
|
ofs = *(uint16 *)(buffer + 2);
|
|
|
|
/*decode ofs back to x:y*/
|
|
/*TODO: this is CGA-only!*/
|
|
y = (ofs & CGA_ODD_LINES_OFS) ? 1 : 0;
|
|
ofs &= ~CGA_ODD_LINES_OFS;
|
|
x = (ofs % CGA_BYTES_PER_LINE) * CGA_PIXELS_PER_BYTE;
|
|
y += (ofs / CGA_BYTES_PER_LINE) * 2;
|
|
w *= CGA_PIXELS_PER_BYTE; /*TODO: this will overflow on large sprite*/
|
|
|
|
cga_DrawVLine(x, y, h - 1, 0, CGA_SCREENBUFFER);
|
|
cga_DrawHLine(x, y, w - 1, 0, CGA_SCREENBUFFER);
|
|
cga_DrawVLine(x + w - 1, y, h - 1, 0, CGA_SCREENBUFFER);
|
|
cga_DrawHLine(x, y + h - 1, w - 1, 0, CGA_SCREENBUFFER);
|
|
|
|
cga_RefreshImageData(*spot_sprite);
|
|
}
|
|
|
|
/*Get on-screen image as specified by script to temp buffer and register it with dirty rect of kind 2
|
|
If rmb is pressed, draw it immediately and return 0
|
|
*/
|
|
int16 drawPortrait(byte **desc, byte *x, byte *y, byte *width, byte *height) {
|
|
byte index;
|
|
byte xx, yy;
|
|
byte *image;
|
|
|
|
index = *((*desc)++);
|
|
if (index == 0xFF) {
|
|
if (script_byte_vars.dirty_rect_kind != 0)
|
|
return 0;
|
|
drawBoxAroundSpot();
|
|
if (!selectCurrentAnim(&xx, &yy, &index))
|
|
return 0;
|
|
} else {
|
|
xx = *((*desc)++);
|
|
yy = *((*desc)++);
|
|
}
|
|
cur_image_coords_x = xx;
|
|
cur_image_coords_y = yy;
|
|
cur_image_idx = index;
|
|
|
|
image = loadPortraitWithFrame(index - 1);
|
|
cur_image_size_h = *image++;
|
|
cur_image_size_w = *image++;
|
|
cur_image_pixels = image;
|
|
cur_image_offs = cga_CalcXY_p(cur_image_coords_x, cur_image_coords_y);
|
|
addDirtyRect(DirtyRectSprite, cur_image_coords_x, cur_image_coords_y, cur_image_size_w, cur_image_size_h, cur_image_offs);
|
|
|
|
/*TODO: remove and use only globals?*/
|
|
*x = cur_image_coords_x;
|
|
*y = cur_image_coords_y;
|
|
*width = cur_image_size_w;
|
|
*height = cur_image_size_h;
|
|
|
|
if (right_button) {
|
|
cga_BlitAndWait(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void playHurtSound() {
|
|
if (!ifgm_loaded)
|
|
playSound(144);
|
|
else
|
|
playSound(144 + (getRand() / 4) % 4);
|
|
}
|
|
|
|
void blinkWithSound(byte color) {
|
|
cga_ColorSelect(color);
|
|
playHurtSound();
|
|
selectPalette();
|
|
}
|
|
|
|
void blinkToRed(void) {
|
|
blinkWithSound(0x3C);
|
|
}
|
|
|
|
void blinkToWhite(void) {
|
|
if (g_vm->getLanguage() == Common::EN_USA)
|
|
playHurtSound(); /*TODO: play here and later? looks like a bug, original code will trash palette selection if pcspeaker is used*/
|
|
|
|
blinkWithSound(0x3F);
|
|
}
|
|
|
|
volatile byte vblank_ticks;
|
|
|
|
void waitVBlankTimer(void) {
|
|
if (g_vm->getLanguage() == Common::EN_USA) {
|
|
/*A crude attempt to fix the animation speed...*/
|
|
while (vblank_ticks < 3) ;
|
|
vblank_ticks = 0;
|
|
}
|
|
waitVBlank();
|
|
}
|
|
|
|
void animPortrait(byte layer, byte index, byte delay) {
|
|
byte *ani, *ani_end;
|
|
byte temp;
|
|
|
|
selectCurrentAnim(&temp, &temp, &temp);
|
|
|
|
if (index == 0xFF)
|
|
index = cur_image_anim1;
|
|
if (index == 0xFE)
|
|
index = cur_image_anim2;
|
|
|
|
IFGM_PlaySfx(index);
|
|
|
|
ani = seekToEntry(anico_data, index - 1, &ani_end);
|
|
cur_image_pixels = sprit_load_buffer + 2 + 2;
|
|
|
|
while (ani != ani_end) {
|
|
byte kind;
|
|
byte x, y;
|
|
byte width, height;
|
|
uint16 offs;
|
|
|
|
byte portrait = *ani++;
|
|
loadPortraitWithFrame(portrait - 1);
|
|
if (*ani == 0xFF) {
|
|
ani++;
|
|
loadPortrait(&ani, ani + 3);
|
|
}
|
|
getDirtyRectAndSetSprite(layer, &kind, &x, &y, &width, &height, &offs);
|
|
waitVBlank();
|
|
cga_BlitAndWait(cur_image_pixels, width, width, height, CGA_SCREENBUFFER, offs);
|
|
waitVBlankTimer();
|
|
if (delay) {
|
|
if (ani[-1] == 37) { /*TODO: what is it?*/
|
|
if (script_byte_vars.extreme_violence)
|
|
blinkToRed();
|
|
else
|
|
blinkToWhite();
|
|
} else {
|
|
int16 i;
|
|
while (delay--) for (i = 0; i < cpu_speed_delay; i++) ; /*TODO: FIXME weak delay*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // End of namespace Chamber
|