scummvm/engines/chamber/room.cpp
yigithanyigit 15602e77c7 CHAMBERS: Refactor code for HGA compatibility
Refactor the existing CGA rendering code to make it compatible with HGA rendering. The main changes include the introduction of engine variables and a move away from macros.
2024-04-25 00:42:36 +02:00

1918 lines
52 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/room.h"
#include "chamber/enums.h"
#include "chamber/resdata.h"
#include "chamber/cga.h"
#include "chamber/print.h"
#include "chamber/anim.h"
#include "chamber/cursor.h"
#include "chamber/script.h"
#include "chamber/print.h"
#include "chamber/input.h"
#include "chamber/dialog.h"
#include "chamber/portrait.h"
#include "chamber/sound.h"
#include "chamber/ifgm.h"
namespace Chamber {
byte scratch_mem1[8010];
byte *scratch_mem2 = scratch_mem1 + 1500;
rect_t room_bounds_rect = {0, 0, 0, 0};
byte last_object_hint = 0;
byte object_hint = 0;
byte command_hint = 0;
byte zone_name = 0;
byte room_hint_bar_width = 0;
byte last_command_hint = 0;
byte zone_spr_index = 0;
byte zone_obj_count = 0;
byte cmd_hint_bar_width = 32;
byte cmd_hint_bar_coords_x = 188 / 4;
byte cmd_hint_bar_coords_y = 193;
byte room_hint_bar_coords_x = 0;
byte room_hint_bar_coords_y = 0;
byte *sprites_list[MAX_SPRITES];
spot_t *zone_spots;
spot_t *zone_spots_end;
spot_t *zone_spots_cur;
vortanims_t *vortanims_ptr;
turkeyanims_t *turkeyanims_ptr;
pers_t *aspirant_ptr;
spot_t *aspirant_spot;
spot_t *found_spot;
byte **spot_sprite;
byte zone_palette;
uint16 zsprite_draw_ofs;
byte zsprite_w;
byte zsprite_h;
byte *lutin_mem;
uint16 drops_cleanup_time = 0;
byte in_de_profundis = 0; /*TODO: useless?*/
uint16 next_vorts_cmd = 0;
uint16 next_vorts_ticks = 0;
uint16 next_turkey_cmd = 0;
uint16 next_turkey_ticks = 0;
uint16 next_protozorqs_ticks = 0;
byte skip_zone_transition;
#define VORTANIMS_MAX 25
/*
Vorts room enter/leave animations
*/
vortanims_t vortsanim_list[VORTANIMS_MAX] = {
{ 2, { 3, {{52, 113}}}, { 8, {{43, 113}}}, {12, {{43, 113}}}, {16, {{43, 113}}}},
{ 3, { 6, {{58, 120}}}, { 7, {{33, 120}}}, {11, {{33, 120}}}, {15, {{33, 120}}}},
{ 4, { 2, {{26, 121}}}, { 9, {{43, 121}}}, {13, {{43, 121}}}, {17, {{43, 121}}}},
{ 5, { 1, {{32, 119}}}, {10, {{33, 119}}}, {14, {{33, 119}}}, {18, {{33, 119}}}},
{ 6, { 3, {{36, 115}}}, { 8, {{27, 115}}}, {12, {{27, 115}}}, {16, {{27, 115}}}},
{ 7, { 1, {{40, 123}}}, {10, {{41, 123}}}, {14, {{41, 123}}}, {18, {{41, 123}}}},
{ 8, {21, {{64, 132}}}, { 9, {{33, 132}}}, {13, {{33, 132}}}, {17, {{33, 132}}}},
{90, { 1, {{27, 129}}}, {10, {{28, 129}}}, {14, {{28, 129}}}, {18, {{28, 129}}}},
{91, { 3, {{44, 129}}}, { 8, {{35, 129}}}, {12, {{35, 129}}}, {16, {{35, 129}}}},
{10, {29, {{22, 156}}}, {30, {{22, 156}}}, {31, {{22, 156}}}, {32, {{22, 156}}}},
{11, {29, {{22, 156}}}, {30, {{22, 156}}}, {31, {{22, 156}}}, {32, {{22, 156}}}},
{12, {29, {{22, 156}}}, {30, {{22, 156}}}, {31, {{22, 156}}}, {32, {{22, 156}}}},
{13, {29, {{22, 156}}}, {30, {{22, 156}}}, {31, {{22, 156}}}, {32, {{22, 156}}}},
{18, {29, {{22, 156}}}, {30, {{22, 156}}}, {31, {{22, 156}}}, {32, {{22, 156}}}},
{35, {29, {{20, 156}}}, {30, {{20, 156}}}, {31, {{20, 156}}}, {32, {{20, 156}}}},
{42, {29, {{18, 156}}}, {30, {{18, 156}}}, {31, {{18, 156}}}, {32, {{18, 156}}}},
{51, {29, {{57, 144}}}, {30, {{57, 144}}}, {31, {{57, 144}}}, {32, {{57, 144}}}},
{53, {29, {{48, 156}}}, {30, {{48, 156}}}, {31, {{48, 156}}}, {32, {{48, 156}}}},
{54, {29, {{55, 139}}}, {30, {{55, 139}}}, {31, {{55, 139}}}, {32, {{55, 139}}}},
{56, {29, {{47, 115}}}, {30, {{47, 115}}}, {31, {{47, 115}}}, {32, {{47, 115}}}},
{57, {29, {{38, 136}}}, {30, {{38, 136}}}, {31, {{38, 136}}}, {32, {{38, 136}}}},
{58, {29, {{28, 125}}}, {30, {{28, 125}}}, {31, {{28, 125}}}, {32, {{28, 125}}}},
{59, {29, {{45, 132}}}, {30, {{45, 132}}}, {31, {{45, 132}}}, {32, {{45, 132}}}},
{60, {29, {{21, 150}}}, {30, {{21, 150}}}, {31, {{21, 150}}}, {32, {{21, 150}}}},
{61, {29, {{23, 123}}}, {30, {{23, 123}}}, {31, {{23, 123}}}, {32, {{23, 123}}}}
};
#define TURKEYANIMS_MAX 10
turkeyanims_t turkeyanim_list[TURKEYANIMS_MAX] = {
{50, {61, {{14, 140}}}, {62, {{14, 140}}}},
{51, {61, {{14, 143}}}, {62, {{14, 143}}}},
{53, {61, {{20, 153}}}, {62, {{20, 153}}}},
{54, {61, {{16, 139}}}, {62, {{16, 139}}}},
{56, {61, {{24, 118}}}, {62, {{24, 118}}}},
{57, {61, {{27, 129}}}, {62, {{27, 129}}}},
{58, {61, {{39, 125}}}, {62, {{39, 125}}}},
{59, {61, {{29, 130}}}, {62, {{29, 130}}}},
{60, {61, {{49, 150}}}, {62, {{49, 150}}}},
{61, {61, {{56, 141}}}, {62, {{56, 141}}}}
};
static const byte cga_color_sels[] = {
0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x10, 0x30, 0x10, 0x10, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x10, 0x10, 0x10
};
void selectSpecificPalette(byte index) {
cga_ColorSelect(cga_color_sels[index]);
}
void selectPalette(void) {
cga_ColorSelect(cga_color_sels[script_byte_vars.palette_index]);
}
/*
Blit sprites to backbuffer
*/
void blitSpritesToBackBuffer(void) {
int16 i;
for (i = 0; i < MAX_SPRITES; i++) {
byte *sprite = sprites_list[i];
cga_RestoreImage(sprite, backbuffer);
}
}
/*
Copy data at sprite's rect from screen to backbuffer
*/
void refreshSpritesData(void) {
int16 i;
for (i = 0; i < MAX_SPRITES; i++) {
byte *sprite = sprites_list[i];
cga_RefreshImageData(sprite);
}
}
/*
Check if packed x/y coordinates are in rect
*/
int16 isInRect(byte x, byte y, rect_t *rect) {
if (x < rect->sx) return 0;
if (x >= rect->ex) return 0;
if (y < rect->sy) return 0;
if (y >= rect->ey) return 0;
return 1;
}
/*
Check if cursor is in rect
*/
int16 isCursorInRect(rect_t *rect) {
return isInRect(cursor_x / g_vm->_screenPPB, cursor_y, rect);
}
/*
Find person for a current spot
*/
void findPerson(void) {
int16 i;
pers_t *pers = pers_list;
for (i = 0; i < PERS_MAX; i++, pers++) {
if ((pers->flags & 15) == script_byte_vars.cur_spot_idx) {
script_vars[kScrPool8_CurrentPers] = pers;
script_byte_vars.cur_pers = i + 1;
return;
}
}
script_byte_vars.cur_pers = 0;
}
/*
Select a spot under cursor if its flags are matched given criteria
*/
void checkHotspots(byte m, byte v) {
int16 i;
spot_t *spot = zone_spots;
for (i = 0; spot != zone_spots_end; i++, spot++) {
if (isCursorInRect((rect_t *)spot) && (spot->flags & SPOTFLG_80) && ((spot->flags & m) == v)) {
script_byte_vars.cur_spot_idx = i + 1;
spot_sprite = sprites_list + i;
found_spot = spot;
script_byte_vars.cur_spot_flags = spot->flags;
object_hint = spot->hint;
cursor_color = 0xAA;
the_command = Swap16(spot->command);
findPerson();
return;
}
}
cursor_color = 0xFF;
object_hint = zone_name; /*room name*/
script_byte_vars.cur_spot_idx = 0;
}
/*
Select cursor shape for current spot
*/
void selectSpotCursor(void) {
int16 curs = CURSOR_TARGET;
checkHotspots(script_byte_vars.spot_m, script_byte_vars.spot_v);
if (cursor_color == 0xAA) {
curs = CURSOR_BODY;
if ((script_byte_vars.cur_spot_flags & (SPOTFLG_20 | SPOTFLG_10 | SPOTFLG_8)) != SPOTFLG_10) {
curs = CURSOR_ARROWS;
if ((script_byte_vars.cur_spot_flags & SPOTFLG_20) != 0)
curs = CURSOR_CROSSHAIR;
}
}
cursor_shape = souri_data + curs * CURSOR_WIDTH * CURSOR_HEIGHT * 2 / g_vm->_screenPPB;
}
#define kBgW 8
#define kBgH 30
static const int16 *background_draw_steps;
/*blocks draw order (clockwise inward spiral)*/
static const int16 background_draw_steps_cga[] = {
kBgW, kBgW, kBgW, kBgW, kBgW, kBgW, kBgW,
kBgH / 2 * CGA_BYTES_PER_LINE, kBgH / 2 * CGA_BYTES_PER_LINE, kBgH / 2 * CGA_BYTES_PER_LINE, kBgH / 2 * CGA_BYTES_PER_LINE, kBgH / 2 * CGA_BYTES_PER_LINE,
-kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW,
-kBgH / 2 * CGA_BYTES_PER_LINE, -kBgH / 2 * CGA_BYTES_PER_LINE, -kBgH / 2 * CGA_BYTES_PER_LINE, -kBgH / 2 * CGA_BYTES_PER_LINE,
kBgW, kBgW, kBgW, kBgW, kBgW, kBgW, kBgW,
kBgH / 2 * CGA_BYTES_PER_LINE, kBgH / 2 * CGA_BYTES_PER_LINE, kBgH / 2 * CGA_BYTES_PER_LINE,
-kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW,
-kBgH / 2 * CGA_BYTES_PER_LINE, -kBgH / 2 * CGA_BYTES_PER_LINE,
kBgW, kBgW, kBgW, kBgW, kBgW,
kBgH / 2 * CGA_BYTES_PER_LINE,
-kBgW, -kBgW, -kBgW, -kBgW, 0
};
/*blocks draw order (clockwise inward spiral)*/
static const int16 background_draw_steps_hga[] = {
kBgW, kBgW, kBgW, kBgW, kBgW, kBgW, kBgW,
kBgH / 2 * HGA_BYTES_PER_LINE, kBgH / 2 * HGA_BYTES_PER_LINE, kBgH / 2 * HGA_BYTES_PER_LINE, kBgH / 2 * HGA_BYTES_PER_LINE, kBgH / 2 * HGA_BYTES_PER_LINE,
-kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW,
-kBgH / 2 * HGA_BYTES_PER_LINE, -kBgH / 2 * HGA_BYTES_PER_LINE, -kBgH / 2 * HGA_BYTES_PER_LINE, -kBgH / 2 * HGA_BYTES_PER_LINE,
kBgW, kBgW, kBgW, kBgW, kBgW, kBgW, kBgW,
kBgH / 2 * HGA_BYTES_PER_LINE, kBgH / 2 * HGA_BYTES_PER_LINE, kBgH / 2 * HGA_BYTES_PER_LINE,
-kBgW, -kBgW, -kBgW, -kBgW, -kBgW, -kBgW,
-kBgH / 2 * HGA_BYTES_PER_LINE, -kBgH / 2 * HGA_BYTES_PER_LINE,
kBgW, kBgW, kBgW, kBgW, kBgW,
kBgH / 2 * HGA_BYTES_PER_LINE,
-kBgW, -kBgW, -kBgW, -kBgW, 0
};
/*
Draw main backgound pattern, in spiral-like order
*/
void drawBackground(byte *target, byte vblank) {
int16 i;
uint16 offs = (2 / 2) * CGA_BYTES_PER_LINE + 8; /*TODO: calcxy?*/
byte *pixels = gauss_data + 0x3C8; /*TODO: better const*/
for (i = 0; i < 53; i++) {
/*draw a tile, alternating between two variants*/
cga_Blit(pixels + (i & 1 ? 0 : kBgW * kBgH), kBgW, kBgW, kBgH, target, offs);
if (vblank)
waitVBlank();
if (g_vm->_videoMode == Common::RenderMode::kRenderCGA) {
background_draw_steps = background_draw_steps_cga;
} else {
background_draw_steps = background_draw_steps_hga;
}
offs += background_draw_steps[i];
}
offs = (182 / 2) * CGA_BYTES_PER_LINE; /*TODO: calcxy?*/
for (i = 0; i < 9; i++) {
cga_Blit(pixels, kBgW, kBgW, 9, target, offs);
offs += kBgW;
}
}
/*
Load and initialize zone data
*/
void loadZone(void) {
byte *zptr, *zend;
zptr = seekToEntry(zones_data, script_byte_vars.zone_index - 1, &zend);
script_byte_vars.zone_area = *zptr++;
script_byte_vars.zone_room = *zptr++;
zone_name = object_hint = *zptr++;
zone_palette = script_byte_vars.palette_index = *zptr++;
zone_obj_count = *zptr++;
if (zone_obj_count != 0) {
uint16 i;
uint16 *zcmds = script_word_vars.zone_obj_cmds;
#if 0
memset(script_word_vars.zone_obj_cmds, 0, 15 * 5 * sizeof(uint8)); /*Original code? - half of list: TODO: bug? wipe whole list?*/
#else
memset(script_word_vars.zone_obj_cmds, 0, 15 * 5 * sizeof(uint16)); /* wipe whole list */
#endif
for (i = 0; i < zone_obj_count; i++) {
/*load spot's reactions*/
uint16 flags = (*zptr++) << 8;
flags |= *zptr++;
if (flags & 0x10) {
zcmds[0] = zptr[0] | (zptr[1] << 8); /*TODO: big-endian, but loaded here as le and converted later*/
zptr += 2;
}
if (flags & 8) {
zcmds[1] = zptr[0] | (zptr[1] << 8);
zptr += 2;
}
if (flags & 4) {
zcmds[2] = zptr[0] | (zptr[1] << 8);
zptr += 2;
}
if (flags & 2) {
zcmds[3] = zptr[0] | (zptr[1] << 8);
zptr += 2;
}
if (flags & 1) {
zcmds[4] = zptr[0] | (zptr[1] << 8);
zptr += 2;
}
zcmds += 5;
}
}
zone_spots = (spot_t *)zptr;
script_vars[kScrPool4_ZoneSpots] = (spot_t *)zptr;
zone_spots_end = (spot_t *)zend;
zone_spots_cur = (spot_t *)zptr;
zone_spr_index = 0;
script_byte_vars.dead_flag = 0;
script_byte_vars.bvar_25 = 0;
script_word_vars.next_aspirant_cmd = BE(0);
next_turkey_cmd = 0;
next_vorts_cmd = 0;
script_byte_vars.used_commands = 0;
}
void resetZone(void) {
script_byte_vars.bvar_43 = 0;
script_byte_vars.bvar_4B = 0;
script_byte_vars.bvar_61 = 0;
script_byte_vars.bvar_45 = 0;
script_byte_vars.bvar_0B = 0;
script_byte_vars.bvar_33 = 0;
script_byte_vars.bvar_34 = 0;
script_byte_vars.bvar_08 = 0;
script_byte_vars.aspirant_flags = 0;
script_word_vars.psi_cmds[0] = BE(0x9048);
script_word_vars.psi_cmds[1] = BE(0xA01D);
script_word_vars.psi_cmds[2] = BE(0);
script_word_vars.psi_cmds[3] = BE(0x00F7);
script_word_vars.psi_cmds[4] = BE(0x9048);
script_word_vars.psi_cmds[5] = BE(0x9048);
script_word_vars.wvar_0C = BE(0xA01C);
script_word_vars.wvar_0E = BE(0);
script_word_vars.wvar_AA = BE(0);
}
/*
Load puzzl sprite to buffer, return next free buffer ptr
*/
byte *loadPuzzl(byte index, byte *buffer) {
if (script_byte_vars.palette_index == 14)
return loadSprite(index, puzzl_data + 4, buffer, 1);
else
return loadSprite(index, puzzl_data + 4, buffer, 0);
}
/*
Load puzzl sprite to scratch buffer, return sprite ptr
*/
byte *loadPuzzlToScratch(byte index) {
byte *buffer = scratch_mem2;
loadPuzzl(index, buffer);
return buffer;
}
#define kNumDoorSprites 3
typedef struct doorinfo_t {
byte flipped;
struct {
byte width;
byte height;
byte *pixels;
uint16 offs;
} layer[kNumDoorSprites];
byte width;
byte height;
uint16 offs;
byte sprites[1]; /*variable size*/
} doorinfo_t;
byte *doors_list[MAX_DOORS];
byte arpla_y_step;
/*
Fill in sliding door animation information
*/
void initRoomDoorInfo(byte index) {
int16 i;
byte *aptr;
byte *sprbuf;
doorinfo_t *info = (doorinfo_t *)scratch_mem2;
rect_t bounds = {0xFF, 0, 0xFF, 0};
aptr = doors_list[index - 1];
info->flipped = (aptr[1] & 0x80) ? ~0 : 0;
sprbuf = info->sprites;
for (i = 0; i < kNumDoorSprites; i++) {
byte x, y, w, h, ox;
byte *sprite = sprbuf;
sprbuf = loadPuzzl(aptr[0], sprbuf);
x = aptr[1];
y = aptr[2];
w = sprite[0];
h = sprite[1];
ox = x;
if (x & 0x80) {
/*horizontal flip*/
x = (x + w - 1) & 0x7F;
ox &= 0x7F;
}
y = (y * 2) & 0xFF; /*TODO: disregard vertical flip?*/
if (ox < bounds.sx)
bounds.sx = ox;
if (ox + w >= bounds.ex)
bounds.ex = ox + w;
if (y < bounds.sy)
bounds.sy = y;
if (y + h >= bounds.ey)
bounds.ey = y + h;
info->layer[i].width = w;
info->layer[i].height = h;
info->layer[i].pixels = sprite + 2;
info->layer[i].offs = CalcXY_p(x, y);
aptr += 3;
}
info->width = bounds.ex - bounds.sx;
info->height = bounds.ey - bounds.sy;
info->offs = CalcXY_p(bounds.sx, bounds.sy);
}
/*
Draw sliding door
*/
void drawRoomDoor(void) {
int16 i;
doorinfo_t *info = (doorinfo_t *)scratch_mem2;
for (i = 0; i < kNumDoorSprites; i++) {
byte w = info->layer[i].width;
byte h = info->layer[i].height;
byte *pixels = info->layer[i].pixels;
uint16 offs = info->layer[i].offs;
if (!info->flipped)
cga_BlitSprite(pixels, w * 2, w, h, backbuffer, offs);
else
cga_BlitSpriteFlip(pixels, w * 2, w, h, backbuffer, offs);
}
waitVBlank();
waitVBlank();
cga_CopyScreenBlock(backbuffer, info->width, info->height, frontbuffer, info->offs);
}
/*
Animate sliding door open
*/
void animRoomDoorOpen(byte index) {
int16 i;
byte oldheight;
doorinfo_t *info = (doorinfo_t *)scratch_mem2;
initRoomDoorInfo(index);
IFGM_PlaySample(29);
oldheight = info->layer[1].height;
for (i = 0; i < oldheight / 2; i++) {
#if 1
drawRoomDoor();
#endif
info->layer[1].height -= 2;
info->layer[1].pixels += info->layer[1].width * 2 * 2;
}
playSound(31);
}
/*
Animate sliding door close
*/
void animRoomDoorClose(byte index) {
int16 i;
byte oldheight;
byte *oldpixels;
doorinfo_t *info = (doorinfo_t *)scratch_mem2;
initRoomDoorInfo(index);
IFGM_PlaySample(29);
oldheight = info->layer[1].height;
oldpixels = info->layer[1].pixels;
info->layer[1].pixels += info->layer[1].width * 2 * (info->layer[1].height - 1);
info->layer[1].height = 1;
for (i = 0; i < oldheight / 2; i++) {
#if 1
drawRoomDoor();
#endif
info->layer[1].height += 2;
info->layer[1].pixels -= info->layer[1].width * 2 * 2;
}
info->layer[1].height = oldheight;
info->layer[1].pixels = oldpixels;
drawRoomDoor();
playSound(31);
}
/*Maybe FindRoomDoor?*/
byte findInitialSpot(void) {
spot_t *spot;
byte index;
byte flags = script_byte_vars.last_door;
if (flags == 0)
return 0;
flags |= SPOTFLG_80 | SPOTFLG_8;
for (index = 1, spot = zone_spots; spot != zone_spots_end; index++, spot++) {
if (spot->flags == flags)
return index;
}
return 0;
}
/*
Find first spot index that matches given flags
*/
byte findSpotByFlags(byte mask, byte value) {
spot_t *spot;
byte index;
for (index = 1, spot = zone_spots; spot != zone_spots_end; spot++, index++) {
if ((spot->flags & mask) == value)
return index;
}
return 0xFF;
}
/*
Select person and its spot (if available)
*/
byte selectPerson(byte offset) {
/*TODO: replace offset arg with index?*/
byte index = offset / 5; /* / sizeof(pers_t) */
script_vars[kScrPool8_CurrentPers] = &pers_list[index];
index = findSpotByFlags(0x3F, (pers_list[index].index & 7) | SPOTFLG_10); /*TODO: return 0 if not found?*/
if (index == 0xFF)
return 0;
found_spot = &zone_spots[index - 1];
script_byte_vars.cur_spot_idx = index;
spot_sprite = &sprites_list[index - 1];
return 1;
}
/*
Play animation at the selected spot or specified coordinates
*/
void animateSpot(const animdesc_t *info) {
byte *sprite = *spot_sprite;
cga_RestoreImage(sprite, backbuffer);
if (info->index & ANIMFLG_USESPOT) {
/*at selected spot*/
cursor_x = found_spot->sx * 4;
cursor_y = found_spot->sy;
if (info->params.desc)
drawMessage(seekToString(desci_data, info->params.desc), frontbuffer);
playAnim(info->index & ~ANIMFLG_USESPOT, found_spot->sx, found_spot->sy);
} else {
/*at specified coords*/
playAnim(info->index, info->params.coords.x, info->params.coords.y);
}
}
typedef struct lutinanim_t {
byte phase;
byte sprites[8];
} lutinanim_t;
lutinanim_t lutins_table[] = {
{0, { 55, 55, 55, 55, 55, 55, 55, 55} },
{0, { 41, 41, 41, 41, 41, 41, 41, 41} },
{0, { 6, 6, 6, 6, 6, 6, 6, 6} },
{0, { 33, 34, 33, 35, 33, 36, 33, 35} },
{0, { 10, 11, 10, 13, 10, 12, 10, 14} },
{0, { 15, 16, 17, 18, 19, 15, 20, 21} },
{0, { 80, 81, 80, 83, 80, 82, 80, 84} },
{0, {139, 140, 141, 142, 139, 140, 141, 142} },
{0, {133, 134, 133, 135, 133, 134, 133, 135} },
{0, {136, 137, 136, 138, 136, 137, 136, 138} },
{0, {144, 145, 144, 145, 144, 145, 144, 145} },
{0, { 33, 34, 33, 35, 33, 36, 33, 35} },
{0, {181, 182, 181, 183, 181, 182, 181, 183} },
{0, {178, 179, 178, 180, 178, 179, 178, 180} },
{0, {181, 182, 181, 183, 181, 182, 181, 183} },
{0, {185, 186, 187, 188, 185, 186, 187, 188} },
{0, {185, 186, 187, 188, 185, 186, 187, 188} },
{0, {164, 132, 164, 132, 164, 132, 164, 132} },
{0, {144, 145, 144, 145, 144, 145, 144, 145} },
{0, { 33, 34, 33, 35, 33, 36, 33, 35} },
{0, {199, 200, 201, 202, 199, 200, 201, 202} },
{0, {195, 196, 197, 198, 195, 196, 197, 198} },
{0, {203, 203, 203, 203, 203, 203, 203, 203} },
{0, {148, 148, 148, 148, 148, 148, 148, 148} },
{0, {185, 186, 187, 188, 185, 186, 187, 188} },
{0, {193, 193, 193, 193, 193, 193, 193, 193} },
{0, {165, 166, 167, 168, 165, 166, 167, 168} },
{0, { 33, 34, 33, 35, 33, 36, 33, 35} },
{0, {181, 182, 181, 183, 181, 182, 181, 183} },
{0, {178, 179, 178, 180, 178, 179, 178, 180} },
{0, { 91, 91, 91, 91, 91, 91, 91, 91} },
{0, {221, 221, 221, 221, 221, 221, 221, 221} },
{0, { 0, 0, 0, 0, 0, 0, 0, 0} }
};
void beforeChangeZone(byte index) {
byte oldspot;
static const animdesc_t anim57 = {ANIMFLG_USESPOT | 57, { { 0, 0 } }};
static const animdesc_t anim58 = {ANIMFLG_USESPOT | 58, { { 0, 0 } }};
script_byte_vars.need_draw_spots = 0;
if (pers_list[kPersScifi].area != script_byte_vars.zone_area)
return;
if (index < 59 || index >= 63)
return;
oldspot = script_byte_vars.cur_spot_idx;
script_byte_vars.need_draw_spots = 0xff;
selectPerson(PersonOffset(kPersScifi));
animateSpot(&anim57);
if (pers_list[kPersMonkey].area != 0) {
selectPerson(PersonOffset(kPersMonkey));
animateSpot(&anim58);
}
script_byte_vars.cur_spot_idx = oldspot;
}
void changeZone(byte index) {
byte spridx = 0;
script_byte_vars.prev_zone_index = script_byte_vars.zone_index;
script_byte_vars.zone_index = index;
if (script_byte_vars.bvar_6C != 0)
spridx = 229;
else if (script_byte_vars.zone_index == 129)
spridx = 221;
else if (script_byte_vars.zone_index == 130)
spridx = 222;
if (spridx != 0) {
int16 i;
lutinanim_t *la = &lutins_table[31];
for (i = 0; i < 8; i++)
la->sprites[i] = spridx;
}
loadZone();
resetZone();
}
void drawPersons(void) {
int16 i;
byte index, pidx;
spot_t *spot;
for (spot = zone_spots; spot != zone_spots_end; spot++) {
if ((spot->flags & 0x38) == SPOTFLG_10)
spot->flags &= ~SPOTFLG_80;
}
for (i = 0; i < PERS_MAX; i++) {
pers_list[i].flags &= ~0xF;
if (pers_list[i].area != script_byte_vars.zone_area)
continue;
if (pers_list[i].flags & PERSFLG_40)
continue;
pidx = (pers_list[i].index & 7) | SPOTFLG_10;
for (index = 1, spot = zone_spots; spot != zone_spots_end; spot++, index++) {
if ((spot->flags & ~SPOTFLG_40) == pidx) {
spot->flags |= SPOTFLG_80;
spot->hint = pers_list[i].name;
pers_list[i].flags |= index;
if (spot->flags & SPOTFLG_40)
drawZoneAniSprite((rect_t *)spot, index, backbuffer);
break;
}
}
}
}
/*
Draw room's static object to backbuffer
*/
void drawRoomStaticObject(byte *aptr, byte *rx, byte *ry, byte *rw, byte *rh) {
byte x, y, w, h;
int16 pitch;
byte *sprite = loadPuzzlToScratch(aptr[0]);
x = aptr[1];
y = aptr[2];
w = sprite[0];
h = sprite[1];
sprite += 2;
*rx = x & 0x7F;
*ry = (y & 0x7F) * 2;
*rw = w;
*rh = h;
pitch = w * 2;
if (x & 0x80) {
/*horizontal flip*/
x = (x + w - 1) & 0x7F;
}
if (y & 0x80) {
/*vertical flip*/
sprite += pitch * (h - 1);
pitch = -pitch;
}
y = (y * 2) & 0xFF;
if (aptr[0] == 83) { /*Hand sprite from Who Will Be Saved room*/
if (arpla_y_step & 1)
y -= 8;
arpla_y_step >>= 1;
}
/*TODO: adjust ry accordingly? SCR_11_DrawRoomObject uses offs from adjusted y, but DrawRoomStatics relies on original y*/
/*TODO: check if this results in any glitches in Who Will Be Saved*/
if (aptr[1] & 0x80)
cga_BlitSpriteFlip(sprite, pitch, w, h, backbuffer, CalcXY_p(x, y));
else
cga_BlitSprite(sprite, pitch, w, h, backbuffer, CalcXY_p(x, y));
}
/*
Draw all room's static objects (decorations) to backbuffer
Initialize room bounds rect to room's dimensions
Draw room's name box and text
*/
void drawRoomStatics(void) {
byte *aptr, *aend;
byte doorcount = 0;
byte x, y, w, h;
uint16 xx, ww;
drawBackground(backbuffer, 0);
arpla_y_step = script_byte_vars.hands;
aptr = seekToEntry(arpla_data, script_byte_vars.zone_room - 1, &aend);
room_bounds_rect.sx = 0xFF;
room_bounds_rect.ex = 0;
room_bounds_rect.sy = 0xFF;
room_bounds_rect.ey = 0;
/*load and draw room decor*/
for (; aptr != aend; aptr += 3) {
byte index = *aptr;
/*a door ?*/
if (index >= 50 && index < 61) {
doors_list[doorcount++] = aptr - 3; /*TODO: check for list overflow?*/
if (doorcount == script_byte_vars.cur_spot_idx)
continue;
if (script_byte_vars.zone_room == 32 && index == 91 && (script_byte_vars.bvar_27 & 8))
continue; /*TODO: error? index may never be that high here*/
}
/*draw decor object*/
drawRoomStaticObject(aptr, &x, &y, &w, &h);
/*update room's bounding rect*/
if (x < room_bounds_rect.sx)
room_bounds_rect.sx = x;
if (x + w > room_bounds_rect.ex)
room_bounds_rect.ex = x + w;
if (y < room_bounds_rect.sy)
room_bounds_rect.sy = y;
if (y + h > room_bounds_rect.ey)
room_bounds_rect.ey = y + h;
}
/*place object hint box right under the room*/
room_hint_bar_width = room_bounds_rect.ex - room_bounds_rect.sx - 2;
char_draw_coords_x = room_bounds_rect.sx;
room_hint_bar_coords_x = room_bounds_rect.sx + 1;
char_draw_coords_y = room_bounds_rect.ey + 2;
room_hint_bar_coords_y = room_bounds_rect.ey + 2;
char_xlat_table = chars_color_wonb;
/*print room name*/
cga_PrintChar(0x3B, backbuffer);
drawObjectHint(); /* area name */
cga_PrintChar(0x3C, backbuffer);
/*draw border around hint text*/
xx = (room_hint_bar_coords_x - 1) * 4;
y = room_hint_bar_coords_y;
ww = (room_hint_bar_width + 2) * 4;
cga_DrawHLine(xx, y - 2, ww, 2, backbuffer);
cga_DrawHLine(xx, y - 1, ww, 0, backbuffer);
cga_DrawHLine(xx, y + 6, ww, 2, backbuffer);
cga_DrawVLine(xx, y - 2, 9, 2, backbuffer);
cga_DrawVLine(xx + ww - 1, y - 2, 9, 2, backbuffer);
}
/*
Redraw all room's static objects (decorations) to backbuffer
*/
void redrawRoomStatics(byte index, byte y_step) {
byte *aptr, *aend;
byte x, y, w, h;
arpla_y_step = y_step;
aptr = seekToEntry(arpla_data, index - 1, &aend);
for (; aptr != aend; aptr += 3) {
/*load room's bg objs*/
drawRoomStaticObject(aptr, &x, &y, &w, &h);
}
}
/*
Draw "some item in the room" icon
*/
void drawRoomItemsIndicator(void) {
byte spridx = 172;
int16 i;
for (i = 0; i < MAX_INV_ITEMS; i++) {
if (inventory_items[i].flags == ITEMFLG_ROOM
&& inventory_items[i].area == script_byte_vars.zone_area) {
spridx = 173;
break;
}
}
drawSpriteN(spridx, 296 / g_vm->_screenPPB, 14, CGA_SCREENBUFFER);
drawSpriteN(spridx, 296 / g_vm->_screenPPB, 14, backbuffer);
/*recalculate the number of zapstiks we have*/
script_byte_vars.zapstiks_owned = 0;
for (i = 0; i < 14; i++) {
if (inventory_items[i + kItemZapstik1 - 1].flags == ITEMFLG_OWNED)
script_byte_vars.zapstiks_owned++;
}
}
void drawZoneSpots(void) {
static const animdesc_t anim59 = {ANIMFLG_USESPOT | 59, { { 0, 0 } }};
static const animdesc_t anim60 = {ANIMFLG_USESPOT | 60, { { 0, 0 } }};
byte oldspot = script_byte_vars.cur_spot_idx;
if (!script_byte_vars.need_draw_spots)
return;
selectPerson(PersonOffset(kPersScifi));
animateSpot(&anim59);
pers_list[kPersScifi].area = script_byte_vars.zone_area;
if (pers_list[kPersMonkey].area != 0) {
pers_list[kPersMonkey].area = script_byte_vars.zone_area;
selectPerson(PersonOffset(kPersMonkey));
animateSpot(&anim60);
}
script_byte_vars.cur_spot_idx = oldspot;
drawPersons();
}
void refreshZone(void) {
popDirtyRects(DirtyRectSprite);
popDirtyRects(DirtyRectBubble);
popDirtyRects(DirtyRectText);
if (!skip_zone_transition && !right_button)
drawBackground(CGA_SCREENBUFFER, 1);
cga_BackBufferToRealFull();
in_de_profundis = 0;
IFGM_StopSample();
if (script_byte_vars.zone_area == 22) {
IFGM_PlaySample(131);
in_de_profundis = 1;
}
drawTheWallDoors();
selectPalette();
drawRoomItemsIndicator();
drawZoneSpots();
if (script_byte_vars.cur_spot_idx != 0)
animRoomDoorClose(script_byte_vars.cur_spot_idx);
blitSpritesToBackBuffer();
}
/*
Draw object hint or zone name text to backbuffer
*/
void drawObjectHint(void) {
if (script_byte_vars.zone_index == 135)
return;
char_draw_max_width = room_hint_bar_width;
char_draw_coords_x = room_hint_bar_coords_x;
char_draw_coords_y = room_hint_bar_coords_y;
char_xlat_table = chars_color_wonb;
printStringCentered(seekToString(motsi_data, object_hint), backbuffer);
#ifdef DEBUG_ZONE
cga_PrintChar(0x20, backbuffer);
cga_PrintChar(0x10 + script_byte_vars.zone_index / 100, backbuffer);
cga_PrintChar(0x10 + (script_byte_vars.zone_index / 10) % 10, backbuffer);
cga_PrintChar(0x10 + script_byte_vars.zone_index % 10, backbuffer);
#endif
}
/*
Copy object hint from backbuffer to screen
*/
void showObjectHint(byte *target) {
if (script_byte_vars.zone_index == 135)
return;
cga_CopyScreenBlock(backbuffer, room_hint_bar_width + 2, 9, target, CalcXY_p(room_hint_bar_coords_x - 1, room_hint_bar_coords_y - 2));
}
/*
Draw command hint text to backbuffer
*/
void drawCommandHint(void) {
char_draw_max_width = cmd_hint_bar_width;
char_draw_coords_x = cmd_hint_bar_coords_x;
char_draw_coords_y = cmd_hint_bar_coords_y;
char_xlat_table = chars_color_wonb;
printStringCentered(seekToString(vepci_data, command_hint), backbuffer);
}
/*
Copy command hint from backbuffer to screen
*/
void showCommandHint(byte *target) {
cga_CopyScreenBlock(backbuffer, cmd_hint_bar_width + 2, 9, target, CalcXY_p(cmd_hint_bar_coords_x - 1, cmd_hint_bar_coords_y - 2));
}
void loadLutinSprite(uint16 lutidx) {
byte spridx;
uint16 flags;
byte *lutin_entry, *lutin_entry_end;
byte *buffer;
byte *sprite;
byte sprw, sprh;
uint16 i;
buffer = lutin_mem;
for (i = 0; i < 800; i++) { /*TODO: fix size*/
buffer[i * 2] = 0xFF; /*mask*/
buffer[i * 2 + 1] = 0; /*pixels*/
}
lutin_entry = seekToEntry(lutin_data, lutidx, &lutin_entry_end);
*buffer++ = *lutin_entry++; /*width*/
*buffer++ = *lutin_entry++; /*height*/
for (; lutin_entry != lutin_entry_end;) {
spridx = *lutin_entry++;
flags = *lutin_entry++;
flags |= (*lutin_entry++) << 8;
sprite = loadSprit(spridx);
sprw = *sprite++;
sprh = *sprite++;
buffer = lutin_mem + 2 + (flags & 0x7FFF) * 2;
if (flags & 0x8000)
mergeSpritesDataFlip(buffer, lutin_mem[0] * 2, sprite, sprw, sprh);
else
mergeSpritesData(buffer, lutin_mem[0] * 2, sprite, sprw, sprh);
}
}
/*
Draw specific room's person idle sprite
*/
void drawCharacterSprite(byte spridx, byte x, byte y, byte *target) {
lutin_mem = scratch_mem2;
loadLutinSprite(spridx);
drawSprite(scratch_mem2, target, CalcXY_p(x, y));
}
/*
Draw room's person idle sprite and advance sprite's animation
Return true if a sprite was drawn
*/
char drawZoneAniSprite(rect_t *rect, uint16 index, byte *target) {
int16 i;
byte spridx;
pers_t *pers = pers_list;
for (i = 0; i < PERS_MAX; i++, pers++) {
if ((pers->flags & 15) == index) {
lutinanim_t *la = &lutins_table[pers->index >> 3];
spridx = la->sprites[la->phase];
la->phase = (la->phase + 1) % 8;
lutin_mem = scratch_mem2;
loadLutinSprite(spridx);
zsprite_w = scratch_mem2[0];
zsprite_h = scratch_mem2[1];
zsprite_draw_ofs = CalcXY_p(rect->sx, rect->sy);
drawSprite(scratch_mem2, target, zsprite_draw_ofs);
return ~0;
}
}
return 0;
}
/*
Initialize Aspirant
*/
void prepareAspirant(void) {
byte index;
byte hostility, appearance;
byte flags;
if (script_byte_vars.zone_area == kAreaDreamsOfSlime) {
pers_list[kPersAspirant1].area = kAreaDreamsOfSlime;
pers_list[kPersAspirant2].area = kAreaDreamsOfSlime;
pers_list[kPersAspirant3].area = kAreaDreamsOfSlime;
pers_list[kPersAspirant1].name = 50; /*DIVO*/
pers_list[kPersAspirant2].name = 50;
pers_list[kPersAspirant3].name = 50;
return;
}
pers_list[kPersAspirant1].area = kAreaNone;
pers_list[kPersAspirant2].area = kAreaNone;
pers_list[kPersAspirant3].area = kAreaNone;
pers_list[kPersAspirant4].area = kAreaNone;
if (script_byte_vars.bvar_26 >= 63)
return;
if (script_byte_vars.zone_area >= kAreaPassage1)
return;
index = findSpotByFlags(0x3F, 17);
if (index == 0xFF)
return;
aspirant_spot = &zone_spots[index - 1];
script_byte_vars.aspirant_pers_ofs += 5; /*FIXME: this is sizeof(pers_t), but it's used in scripts, so hardcoded*/
if (script_byte_vars.aspirant_pers_ofs >= PersonOffset(kPersAspirant4 + 1))
script_byte_vars.aspirant_pers_ofs = PersonOffset(kPersAspirant1);
aspirant_ptr = &pers_list[script_byte_vars.aspirant_pers_ofs / 5];
if (aspirant_ptr->flags & PERSFLG_40)
return;
hostility = script_byte_vars.rand_value;
appearance = getRand();
flags = 0;
/*
flags values:
0 - passive
1 - willing to trade
2 - hostile
|4 - ?
*/
if (script_byte_vars.zone_area <= kAreaTheMastersOrbit3) {
/*at The Master's Orbit*/
if (hostility < 90 && aspirant_ptr->item) {
/* ROPE, STONE FLY, GOBLET, LANTERN, but not DAGGER*/
if ((aspirant_ptr->item >= kItemRope1 && aspirant_ptr->item <= kItemLantern4)
&& !(aspirant_ptr->item >= kItemDagger1 && aspirant_ptr->item <= kItemDagger4))
flags = 1; /*willing to trade*/
}
if (appearance < 23) {
/*spawn in this room*/
aspirant_ptr->area = script_byte_vars.zone_area;
script_word_vars.next_aspirant_cmd = BE(0xA018); /*leave*/
script_byte_vars.check_used_commands = 3; /*after 3 actions*/
script_byte_vars.aspirant_flags = flags;
script_vars[kScrPool8_CurrentPers] = aspirant_ptr;
} else if (appearance < 52) {
script_word_vars.next_aspirant_cmd = BE(0xA019);
flags |= 4;
script_byte_vars.check_used_commands = 3;
script_byte_vars.aspirant_flags = flags;
script_vars[kScrPool8_CurrentPers] = aspirant_ptr;
} else {
/*do not spawn*/
script_byte_vars.aspirant_flags = 0;
return;
}
} else {
/*at other places*/
if (hostility < 39 && aspirant_ptr->item)
flags = 1; /*willing to trade*/
if (hostility >= 166)
flags = 2; /*hostile*/
if (appearance < 26) {
/*spawn in this room*/
aspirant_ptr->area = script_byte_vars.zone_area;
script_word_vars.next_aspirant_cmd = BE(0xA018); /*leave*/
script_byte_vars.check_used_commands = 3; /*after 3 actions*/
script_byte_vars.aspirant_flags = flags;
script_vars[kScrPool8_CurrentPers] = aspirant_ptr;
} else if (appearance < 52) {
script_word_vars.next_aspirant_cmd = BE(0xA019);
flags |= 4;
script_byte_vars.check_used_commands = 3;
script_byte_vars.aspirant_flags = flags;
script_vars[kScrPool8_CurrentPers] = aspirant_ptr;
} else {
/*do not spawn*/
script_byte_vars.aspirant_flags = 0;
return;
}
}
}
/*
Initialize Vorts
*/
void prepareVorts(void) {
spot_t *spot;
if ((script_byte_vars.zone_area != kAreaTheReturn) || !(script_byte_vars.bvar_36 & 0x80)) {
pers_list[kPersVort].flags &= ~PERSFLG_40;
pers_list[kPersVort2].flags &= ~PERSFLG_40;
pers_list[kPersVort3].flags &= ~PERSFLG_40;
for (spot = zone_spots; spot != zone_spots_end; spot++) {
if ((spot->flags & ~SPOTFLG_80) == (SPOTFLG_40 | SPOTFLG_10)) {
int16 i;
for (i = 0; i < VORTANIMS_MAX; i++) {
if (vortsanim_list[i].room == script_byte_vars.zone_room) {
vortanims_ptr = &vortsanim_list[i];
if (script_byte_vars.zone_area == pers_list[kPersVort].area
|| script_byte_vars.zone_area == pers_list[kPersVort2].area
|| script_byte_vars.zone_area == pers_list[kPersVort3].area) {
next_vorts_cmd = 0xA015; /*VortLeave*/
} else {
pers_list[kPersVort].area = 0;
pers_list[kPersVort2].area = 0;
pers_list[kPersVort3].area = 0;
script_byte_vars.bvar_36 &= 0x80; /*TODO: is this correct? |= ?*/
if (script_byte_vars.rand_value < 39) {
pers_list[kPersVort].area = script_byte_vars.zone_area;
next_vorts_cmd = 0xA015; /*VortLeave*/
} else if (script_byte_vars.rand_value < 78)
next_vorts_cmd = 0xA014; /*VortAppear*/
else
return;
}
script_byte_vars.bvar_36 |= 1;
next_vorts_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
return;
}
}
break;
}
}
}
pers_list[kPersVort].area = 0;
pers_list[kPersVort2].area = 0;
pers_list[kPersVort3].area = 0;
script_byte_vars.bvar_36 &= 0x80; /*TODO: is this correct? |= ?*/
}
/*
Initialize Turkey
*/
void prepareTurkey(void) {
spot_t *spot;
if (script_byte_vars.zone_area == kAreaPlacatingThePowers && script_byte_vars.bvar_4E == 0) {
pers_list[kPersTurkey].area = kAreaPlacatingThePowers;
return;
}
pers_list[kPersTurkey].flags &= ~PERSFLG_40;
for (spot = zone_spots; spot != zone_spots_end; spot++) {
if ((spot->flags & ~SPOTFLG_80) == (SPOTFLG_40 | SPOTFLG_10 | SPOTFLG_1)) {
int16 i;
for (i = 0; i < TURKEYANIMS_MAX; i++) {
if (turkeyanim_list[i].room == script_byte_vars.zone_room) {
turkeyanims_ptr = &turkeyanim_list[i];
if (script_byte_vars.zone_area == pers_list[kPersTurkey].area) {
next_turkey_cmd = 0xA01F; /*leave*/
next_turkey_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
} else {
pers_list[kPersTurkey].area = 0;
if (script_byte_vars.rand_value >= 217) {
pers_list[kPersTurkey].area = script_byte_vars.zone_area;
next_turkey_cmd = 0xA01F; /*leave*/
next_turkey_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
} else if (script_byte_vars.rand_value >= 178) {
next_turkey_cmd = 0xA01E; /*appear*/
next_turkey_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
}
}
return;
}
}
break;
}
}
pers_list[kPersTurkey].area = 0;
}
/*
Load puzzl sprite to scratch and init draw params
*/
uint16 getPuzzlSprite(byte index, byte x, byte y, uint16 *w, uint16 *h, uint16 *ofs) {
byte *spr = loadPuzzlToScratch(index);
*w = spr[0];
*h = spr[1];
*ofs = CalcXY_p(x, y);
return 0; /*sprite offset in scratch buf*/
}
/*
Save specific fully drawn rooms to backbuffer
*/
void backupScreenOfSpecialRoom(void) {
switch (script_byte_vars.zone_room) {
case 41: /* THE POWERS OF THE ABYSS */
case 22: /* DE PROFUNDIS */
case 23: /* DE PROFUNDIS */
case 24: /* THE WALL */
cga_RealBufferToBackFull();
break;
}
}
/*
Fully redraw specific rooms
*/
void restoreScreenOfSpecialRoom(void) {
switch (script_byte_vars.zone_room) {
case 23: /* DE PROFUNDIS */
case 24: /* DE PROFUNDIS */
redrawRoomStatics(script_byte_vars.zone_room, 0);
break;
}
}
/*
Modify anim sprite 127
*/
void setAnim127Sprite(byte flags, byte spridx) {
byte *lutin_entry, *lutin_entry_end;
lutin_entry = seekToEntry(lutin_data, 127, &lutin_entry_end);
lutin_entry[2] = spridx;
switch (spridx) {
case 37: /*DAGGER*/
case 58: /*CHOPPER*/
case 40: /*SACRIFICIAL BLADE*/
if (flags == ITEMFLG_OWNED)
script_byte_vars.bvar_66 += 1;
else
script_byte_vars.bvar_66 -= 1;
break;
}
}
/*
Bounce current item to the room/inventory
*/
void bounceCurrentItem(byte flags, byte y) {
item_t *item = (item_t *)(script_vars[kScrPool3_CurrentItem]);
setAnim127Sprite(flags, item->sprite);
item->flags = flags;
item->area = script_byte_vars.zone_area;
backupScreenOfSpecialRoom();
playAnim(41, 176 / 4, y);
drawRoomItemsIndicator();
restoreScreenOfSpecialRoom();
}
/*
Load The Wall gate sprites
*/
byte *loadMursmSprite(byte index) {
byte *pinfo, *end;
pinfo = seekToEntry(mursm_data, index, &end);
while (pinfo != end) {
uint16 flags;
int16 pitch;
byte *buffer, *sprite;
byte sprw, sprh;
index = *pinfo++;
flags = *pinfo++;
flags |= (*pinfo++) << 8;
buffer = sprit_load_buffer + (flags & 0x3FFF);
pitch = 20;
sprite = loadPuzzlToScratch(index & 0x7F);
sprw = *sprite++;
sprh = *sprite++;
if (index & 0x80) /*horizontal flip*/
mergeImageAndSpriteDataFlip(buffer, pitch, sprite, sprw, sprh);
else
mergeImageAndSpriteData(buffer, pitch, sprite, sprw, sprh);
}
/*TODO: move this from globals to args/consts?*/
cur_frame_width = 20;
cur_image_coords_y = 32;
cur_image_size_w = 80 / 4;
cur_image_size_h = 59;
return sprit_load_buffer;
}
thewalldoor_t the_wall_doors[2];
/*
Open The Wall's right gate
TODO: move this to CGA?
*/
void theWallOpenRightDoor(byte x, byte y, byte width, byte height, byte limit) {
uint16 offs = CalcXY_p(x + width - 2, y);
while (--width) {
cga_HideScreenBlockLiftToRight(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs);
if (width == limit)
return;
}
offs++;
/*hide remaining column*/
uint16 ooffs = offs;
byte oh = height;
while (height--) {
memcpy(frontbuffer + offs, backbuffer + offs, 1);
offs ^= g_vm->_line_offset;
if ((offs & g_vm->_line_offset) == 0)
offs += CGA_BYTES_PER_LINE;
}
cga_blitToScreen(ooffs, 1, oh);
}
/*
Open The Wall's left gate
TODO: move this to CGA?
*/
void theWallOpenLeftDoor(byte x, byte y, byte width, byte height, byte limit) {
uint16 offs = CalcXY_p(x + 1, y);
while (--width) {
cga_HideScreenBlockLiftToLeft(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs);
if (width == limit)
return;
}
offs--;
/*hide remaining column*/
uint16 ooffs = offs;
byte oh = height;
while (height--) {
memcpy(frontbuffer + offs, backbuffer + offs, 1);
offs ^= g_vm->_line_offset;
if ((offs & g_vm->_line_offset) == 0)
offs += CGA_BYTES_PER_LINE;
}
cga_blitToScreen(ooffs, 1, oh);
}
/*
Animate The Wall doors
Phase 3: Fully closed -> Half opened
*/
void theWallPhase3_DoorOpen1(void) {
script_byte_vars.zone_index = (script_byte_vars.zone_index == 95) ? 9 : 102;
loadZone();
theWallOpenRightDoor(144 / 4, 32, 80 / 4, 59, 40 / 4);
theWallOpenLeftDoor(64 / 4, 32, 80 / 4, 59, 40 / 4);
IFGM_StopSample();
/*TODO: fill in the_wall_door_* structures, as they are used by the original code and appear in savefile*/
}
/*
Animate The Wall doors
Phase 0: Half opened -> Fully opened
*/
void theWallPhase0_DoorOpen2(void) {
script_byte_vars.zone_index = (script_byte_vars.zone_index == 9) ? 24 : 30;
loadZone();
theWallOpenRightDoor((144 + 40) / 4, 32, (80 - 40) / 4, 59, 0);
theWallOpenLeftDoor(64 / 4, 32, (80 - 40) / 4, 59, 0);
IFGM_StopSample();
/*TODO: fill in the_wall_door_* structures, as they are used by the original code and appear in savefile*/
}
/*
Animate The Wall doors
Phase 1: Opened -> Half closed
*/
void theWallPhase1_DoorClose1(void) {
byte *spr;
script_byte_vars.zone_index = (script_byte_vars.zone_index == 24) ? 9 : 102;
loadZone();
spr = loadMursmSprite(0);
spr += cur_frame_width - 1;
cur_image_coords_x = 64 / 4;
cga_AnimLiftToRight(10, spr, cur_frame_width, 1, cur_image_size_h, frontbuffer, CalcXY_p(cur_image_coords_x, cur_image_coords_y));
spr = loadMursmSprite(1);
cur_image_coords_x = 220 / 4;
cga_AnimLiftToLeft(10, spr, cur_frame_width, 1, cur_image_size_h, frontbuffer, CalcXY_p(cur_image_coords_x, cur_image_coords_y));
IFGM_StopSample();
/*TODO: fill in the_wall_door_* structures, as they are used by the original code and appear in savefile*/
}
/*
Animate The Wall doors
Phase 2: Half closed -> Fully closed
*/
void theWallPhase2_DoorClose2(void) {
byte *spr;
script_byte_vars.zone_index = (script_byte_vars.zone_index == 9) ? 95 : 103;
loadZone();
spr = loadMursmSprite(0);
spr += cur_frame_width - 1;
cur_image_coords_x = 64 / 4;
cga_AnimLiftToRight(10, spr - 10, cur_frame_width, 1 + 10, cur_image_size_h, frontbuffer, CalcXY_p(cur_image_coords_x, cur_image_coords_y));
spr = loadMursmSprite(1);
cur_image_coords_x = 220 / 4;
cga_AnimLiftToLeft(10, spr, cur_frame_width, 1 + 10, cur_image_size_h, frontbuffer, CalcXY_p(cur_image_coords_x, cur_image_coords_y) - 10);
IFGM_PlaySample(30);
/*TODO: fill in the_wall_door_* structures, as they are used by the original code and appear in savefile*/
}
/*
Draw default The Wall doors
*/
void drawTheWallDoors(void) {
switch (script_byte_vars.zone_index) {
case 9:
case 102:
cga_Blit(loadMursmSprite(0) + 10, 20, 10, 59, CGA_SCREENBUFFER, CalcXY_p(64 / g_vm->_screenPPB, 32));
if (g_vm->getLanguage() == Common::EN_USA) {
/*This fixes odd black patch on the right gate door*/
cga_Blit(loadMursmSprite(1) , 20, 10, 59, CGA_SCREENBUFFER, CalcXY_p(184 / g_vm->_screenPPB, 32));
} else {
cga_Blit(loadMursmSprite(1) , 20, 10, 59, CGA_SCREENBUFFER, CalcXY_p(180 / g_vm->_screenPPB, 32));
}
break;
case 95:
case 103:
cga_Blit(loadMursmSprite(0), 20, 20, 59, CGA_SCREENBUFFER, CalcXY_p(64 / g_vm->_screenPPB, 32));
cga_Blit(loadMursmSprite(1), 20, 20, 59, CGA_SCREENBUFFER, CalcXY_p(144 / g_vm->_screenPPB, 32));
break;
}
}
/*
Superimpose source sprite data over target sprite data
*/
void mergeSpritesData(byte *target, uint16 pitch, byte *source, uint16 w, uint16 h) {
uint16 x;
while (h--) {
for (x = 0; x < w; x++) {
byte m = *source++;
*target++ &= m;
*target &= m;
*target++ |= *source++;
}
target -= w * 2;
target += pitch;
}
}
/*
Superimpose horizontally-flipped source sprite data over target sprite data
*/
void mergeSpritesDataFlip(byte *target, uint16 pitch, byte *source, uint16 w, uint16 h) {
uint16 x;
target += w * 2 - 2;
while (h--) {
for (x = 0; x < w; x++) {
byte m = cga_pixel_flip[*source++];
*target++ &= m;
*target &= m;
*target |= cga_pixel_flip[*source++];
target -= 3;
}
target += w * 2;
target += pitch;
}
}
/*
Save image at the rect to buffer
Return current and next free buffer ptr
*/
byte *backupSpotImage(spot_t *spot, byte **spotback, byte *buffer) {
*spotback = buffer;
buffer = cga_BackupImage(backbuffer, CalcXY_p(spot->sx, spot->sy), spot->ex - spot->sx, spot->ey - spot->sy, buffer);
return buffer;
}
/*
Save zone spot images to sprites list
*/
void backupSpotsImages(void) {
spot_t *spot = zone_spots;
byte *buffer = scratch_mem1;
int16 i;
for (i = 0; i < MAX_SPRITES; i++)
sprites_list[i] = 0;
for (i = 0; spot != zone_spots_end; spot++, i++) { /*TODO: maybe don't advance it if spot is skipped?*/
if (spot->flags & SPOTFLG_40)
buffer = backupSpotImage(spot, &sprites_list[i], buffer);
}
}
/*
Animate all room's persons, one per call
TODO: rename me
*/
void drawSpots(byte *target) {
spot_t *spot = zone_spots_cur;
byte spridx = zone_spr_index;
if (spot == zone_spots_end) {
spot = zone_spots;
spridx = 0;
}
do {
spridx++;
if ((spot->flags & SPOTFLG_40) && ((spot->flags & ~7) == (SPOTFLG_80 | SPOTFLG_40 | SPOTFLG_10))) {
zone_spots_cur = spot + 1;
zone_spr_index = spridx;
/*TODO: subclass spot_t from rect_t*/
if (drawZoneAniSprite((rect_t *)spot, (spot - zone_spots) + 1, backbuffer)) {
updateCursor();
waitVBlank();
undrawCursor(target);
cga_CopyScreenBlock(backbuffer, zsprite_w, zsprite_h, target, zsprite_draw_ofs);
drawCursor(target);
cga_RestoreImage(sprites_list[zone_spr_index - 1], backbuffer);
return;
}
spridx = zone_spr_index; /*TODO: not neded?*/
}
spot++;
} while (spot != zone_spots_end);
zone_spots_cur = spot;
zone_spr_index = spridx;
}
/*
Animate room's persons at fixed rate
TODO: rename me
*/
void animateSpots(byte *target) {
if (script_byte_vars.timer_ticks % 32 == 31)
drawSpots(target);
}
/*
Draw cursor and hints text on screen
*/
void drawHintsAndCursor(byte *target) {
updateCursor();
waitVBlank();
undrawCursor(target);
if (object_hint != last_object_hint) {
showObjectHint(target);
last_object_hint = object_hint;
}
if (command_hint != last_command_hint) {
showCommandHint(target);
last_command_hint = command_hint;
}
drawCursor(target);
}
/*
Hide a person
*/
void hidePerson(byte offset) {
selectPerson(offset);
found_spot->flags &= ~SPOTFLG_80;
}
const byte patrol_route[] = {
kAreaGuardRoom, kAreaTheConcourse,
kAreaPassage1, kAreaTheRing2,
kAreaTheRing1, kAreaTheRing6,
kAreaTheRing5, kAreaTheRing4,
kAreaTheRing3, kAreaTheRing2,
kAreaPassage1, kAreaTheConcourse};
const byte *timed_seq_ptr = patrol_route;
/*
Update Protozorqs locations
*/
void updateProtozorqs(void) {
uint16 elapsed;
if (script_byte_vars.bvar_45 != 0)
return;
if (script_byte_vars.bvar_26 >= 63)
return;
script_word_vars.next_protozorqs_cmd = BE(0);
if (pers_list[kPersProtozorq5].flags & PERSFLG_40) {
pers_list[kPersProtozorq5].area = kAreaGuardRoom;
pers_list[kPersProtozorq5].flags &= ~(PERSFLG_10 | PERSFLG_20 | PERSFLG_40 | PERSFLG_80);
pers_list[kPersProtozorq5].index = 52;
}
if (pers_list[kPersProtozorq14].flags & PERSFLG_20)
script_byte_vars.bvar_30 |= 4;
elapsed = Swap16(script_word_vars.timer_ticks2);
if (elapsed < next_protozorqs_ticks)
return;
next_protozorqs_ticks = elapsed + 30;
script_byte_vars.bvar_3F = *timed_seq_ptr++;
if (timed_seq_ptr == patrol_route + sizeof(patrol_route))
timed_seq_ptr = patrol_route;
script_byte_vars.bvar_40 = *timed_seq_ptr++;
if (script_byte_vars.bvar_3F == kAreaGuardRoom) {
script_byte_vars.bvar_30 &= ~4;
pers_list[kPersProtozorq14].flags &= ~(PERSFLG_20 | PERSFLG_40 | PERSFLG_80);
pers_list[kPersProtozorq14].index = 55;
if (pers_list[kPersProtozorq5].area == kAreaGuardRoom) {
pers_list[kPersProtozorq5].flags &= ~(PERSFLG_20 | PERSFLG_40 | PERSFLG_80);
script_byte_vars.bvar_41 = 1;
}
}
if (script_byte_vars.bvar_3F == kAreaTheRing2 && script_byte_vars.bvar_40 == kAreaTheRing1) {
script_byte_vars.bvar_41 = 0;
if (pers_list[kPersProtozorq5].area == kAreaGuardRoom) {
script_byte_vars.bvar_40 = kAreaPassage1;
timed_seq_ptr = patrol_route + 4;
}
}
pers_list[kPersProtozorq14].area = script_byte_vars.bvar_40;
if (script_byte_vars.bvar_41 != 0) {
pers_list[kPersProtozorq5].area = script_byte_vars.bvar_40;
pers_list[kPersProtozorq12].area = script_byte_vars.bvar_40;
pers_list[kPersProtozorq13].area = script_byte_vars.bvar_40;
if (script_byte_vars.zone_area == script_byte_vars.bvar_3F) {
static const animdesc_t anim35 = {ANIMFLG_USESPOT | 35, { { 0, 0 } }};
updateUndrawCursor(frontbuffer);
refreshSpritesData();
hidePerson(PersonOffset(kPersProtozorq14));
hidePerson(PersonOffset(kPersProtozorq5));
hidePerson(PersonOffset(kPersProtozorq12));
hidePerson(PersonOffset(kPersProtozorq13));
animateSpot(&anim35);
blitSpritesToBackBuffer();
drawCursor(frontbuffer);
return;
}
if (script_byte_vars.zone_area == script_byte_vars.bvar_40) {
static const animdesc_t anim34 = {ANIMFLG_USESPOT | 34, { { 0, 0 } }};
updateUndrawCursor(frontbuffer);
refreshSpritesData();
selectPerson(PersonOffset(kPersProtozorq14));
animateSpot(&anim34);
if (selectPerson(PersonOffset(kPersProtozorq12))) {
animateSpot(&anim34);
selectPerson(PersonOffset(kPersProtozorq5));
animateSpot(&anim34);
selectPerson(PersonOffset(kPersProtozorq13));
animateSpot(&anim34);
}
drawPersons();
cga_BackBufferToRealFull();
blitSpritesToBackBuffer();
drawCursor(frontbuffer);
if (script_byte_vars.zapstiks_owned != 0)
script_word_vars.next_protozorqs_cmd = BE(0xC1FD);
else if (pers_list[kPersProtozorq14].flags & PERSFLG_20)
script_word_vars.next_protozorqs_cmd = BE(0xC1E5);
else if (script_byte_vars.zone_area == kAreaPassage1)
script_word_vars.next_protozorqs_cmd = BE(0xC060);
return;
}
} else {
if (pers_list[kPersProtozorq14].flags & PERSFLG_40)
return;
if (script_byte_vars.zone_area == script_byte_vars.bvar_3F) {
static const animdesc_t anim35 = {ANIMFLG_USESPOT | 35, { { 0, 0 } }};
updateUndrawCursor(frontbuffer);
refreshSpritesData();
hidePerson(PersonOffset(kPersProtozorq14));
animateSpot(&anim35);
blitSpritesToBackBuffer();
drawCursor(frontbuffer);
return;
}
if (script_byte_vars.zone_area == script_byte_vars.bvar_40) {
static const animdesc_t anim34 = {ANIMFLG_USESPOT | 34, { { 0, 0 } }};
updateUndrawCursor(frontbuffer);
refreshSpritesData();
selectPerson(PersonOffset(kPersProtozorq14));
animateSpot(&anim34);
drawPersons();
cga_BackBufferToRealFull();
blitSpritesToBackBuffer();
drawCursor(frontbuffer);
return;
}
}
}
/*
Check how many time have passed and call the guards
*/
void checkGameTimeLimit(void) {
uint16 elapsed = Swap16(script_word_vars.timer_ticks2);
if (elapsed < 60 * 60)
return;
if (script_byte_vars.bvar_26 < 63) {
script_byte_vars.bvar_37 = 3;
script_byte_vars.bvar_26 = (script_byte_vars.zone_area < kAreaPassage1) ? 255 : 63;
pers_list[kPersProtozorq5].area = 0;
pers_list[kPersProtozorq6].area = 0;
pers_list[kPersProtozorq7].area = 0;
pers_list[kPersProtozorq8].area = 0;
pers_list[kPersProtozorq10].area = 0;
pers_list[kPersProtozorq11].area = kAreaGuardRoom;
pers_list[kPersProtozorq12].area = kAreaGuardRoom;
pers_list[kPersProtozorq13].area = kAreaGuardRoom;
pers_list[kPersProtozorq14].area = kAreaGuardRoom;
if (script_byte_vars.zone_area < kAreaDeProfundis)
script_word_vars.next_protozorqs_cmd = BE(0xC012);
return;
}
if (elapsed < 105 * 60)
return;
if (elapsed < 120 * 60) {
script_byte_vars.bvar_37 = 2;
script_byte_vars.bvar_4E = 1;
if ((script_byte_vars.zone_area != kAreaBirthOfADivineRace)
&& (script_byte_vars.zone_area >= kAreaPlacatingThePowers && script_byte_vars.zone_area <= kAreaCell4))
script_word_vars.next_protozorqs_cmd = BE(0xC012);
return;
}
script_word_vars.next_protozorqs_cmd = BE(0xC318); /*game over*/
}
/*
Clean up dropped items after some time
*/
void cleanupDroppedItems(void) {
int16 i;
if (Swap16(script_word_vars.timer_ticks2) - drops_cleanup_time < 180)
return;
drops_cleanup_time = Swap16(script_word_vars.timer_ticks2);
for (i = 0; i < MAX_INV_ITEMS; i++) {
if (inventory_items[i].flags & ITEMFLG_ROOM) {
if (inventory_items[i].area != script_byte_vars.zone_area && inventory_items[i].area < kAreaPassage4)
inventory_items[i].flags &= ~ITEMFLG_ROOM;
}
}
}
void resetAllPersons(void) {
int16 i;
for (i = 0; i < PERS_MAX; i++)
pers_list[i].flags &= ~PERSFLG_80;
script_byte_vars.dead_flag = 0;
}
} // End of namespace Chamber