mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
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.
1918 lines
52 KiB
C++
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
|