scummvm/engines/chamber/script.cpp

4452 lines
89 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 "common/system.h"
#include "common/debug.h"
#include "chamber/chamber.h"
#include "chamber/common.h"
#include "chamber/script.h"
#include "chamber/enums.h"
#include "chamber/resdata.h"
#include "chamber/cga.h"
#include "chamber/cursor.h"
#include "chamber/portrait.h"
#include "chamber/input.h"
#include "chamber/menu.h"
#include "chamber/room.h"
#include "chamber/dialog.h"
#include "chamber/print.h"
#include "chamber/anim.h"
#include "chamber/invent.h"
#include "chamber/sound.h"
#include "chamber/savegame.h"
#include "chamber/ifgm.h"
#if 0
#define DEBUG_SCRIPT
char DEBUG_SCRIPT_LOG[] = "!script.log";
#endif
#include "chamber/scrvars.h"
namespace Chamber {
byte rand_seed;
uint16 the_command;
uint16 script_res;
byte *script_ptr, *script_end_ptr;
byte *script_stack[5 * 2];
byte **script_stack_ptr = script_stack;
void *script_vars[kScrPools_MAX] = {
&script_word_vars,
&script_word_vars,
&script_byte_vars,
inventory_items,
zones_data,
pers_list,
inventory_items,
inventory_items + kItemZapstik1 - 1,
pers_list
};
extern void askDisk2(void);
/*
Get next random byte value
*/
byte getRand(void) {
script_byte_vars.rand_value = aleat_data[++rand_seed];
return script_byte_vars.rand_value;
}
/*
Get next random word value
*/
uint16 getRandW(void) {
uint16 r = getRand() << 8;
return r | getRand();
}
uint16 Swap16(uint16 x) {
return (x << 8) | (x >> 8);
}
/*
Script handlers exit codes
*/
enum CommandStatus {
ScriptContinue = 0, /*run next script command normally*/
ScriptRerun = 1, /*abort current script, execute new command*/
/*TODO: maybe define ScriptRestartGame to support game restart?*/
};
uint16 CMD_TRAP(void) {
warning("CMD TRAP");
promptWait();
for (;;) ;
return 0;
}
uint16 SCR_TRAP(void) {
warning("SCR TRAP 0x%02X @ 0x%lX", *script_ptr, script_ptr - templ_data);
promptWait();
for (;;) ;
return 0;
}
/*
Remove "not interested in that" tag from owned items
*/
void reclaimRefusedItems(void) {
int16 i;
for (i = 0; i < MAX_INV_ITEMS; i++) {
if (inventory_items[i].flags == (ITEMFLG_OWNED | ITEMFLG_DONTWANT))
inventory_items[i].flags = ITEMFLG_OWNED;
}
}
/*
Trade with a fellow Aspirant (the one that offers to swap an item)
*/
uint16 SCR_1_AspirantItemTrade(void) {
byte *old_script, *old_script_end = script_end_ptr;
item_t *item1, *item2;
script_ptr++;
old_script = script_ptr;
for (;;) {
inv_bgcolor = 0xFF;
openInventory(0xFE, ITEMFLG_OWNED);
if (inv_count == 0) {
the_command = 0xC1BC;
runCommand();
break;
}
if (the_command == 0) {
the_command = 0xC1C0;
runCommand();
break;
}
the_command = 0x9140;
if (aspirant_ptr->item == 0)
break;
item1 = &inventory_items[aspirant_ptr->item - 1]; /*aspirant's item*/
item2 = (item_t *)(script_vars[kScrPool3_CurrentItem]); /*our offer*/
if (item2->flags == (ITEMFLG_OWNED | ITEMFLG_DONTWANT) || item1->name == item2->name) {
the_command = 0xC1C0;
runCommand();
break;
}
if (item2->name == 109 /*SKULL*/
|| item2->name == 132 /*ZAPSTIK*/
|| item2->name == 108 /*DAGGER*/
|| script_byte_vars.rand_value < 154) {
/*accept*/
item2->flags = ITEMFLG_ASPIR;
item1->flags = ITEMFLG_OWNED;
aspirant_ptr->item = script_byte_vars.inv_item_index;
switch (item2->name) {
case 132: /*ZAPSTIK*/
script_byte_vars.zapstiks_owned--;
the_command = 0xC04B;
break;
case 104: /*ROPE*/
the_command = 0xC1BA;
break;
case 107: /*GOBLET*/
the_command = 0xC1BB;
break;
default: /*STONE FLY*/
the_command = 0xC1B9;
}
runCommand();
break;
} else {
/*not interested*/
item2->flags = ITEMFLG_OWNED | ITEMFLG_DONTWANT;
the_command = 0xC1BD;
runCommand();
continue;
}
}
reclaimRefusedItems();
script_ptr = old_script;
script_end_ptr = old_script_end;
return 0;
}
/*
Trade with a rude/passive Aspirant (the one that says nasty words about you)
*/
uint16 SCR_2_RudeAspirantTrade(void) {
byte *old_script, *old_script_end = script_end_ptr;
item_t *item1, *item2;
script_ptr++;
old_script = script_ptr;
the_command = 0x9099; /*WHAT DO YOU WANT TO EXCHANGE?*/
runCommand();
for (;;) {
inv_bgcolor = 0xFF;
openInventory(0xFE, ITEMFLG_OWNED);
if (inv_count == 0) {
the_command = 0xC1C5;
runCommand();
break;
}
if (the_command == 0)
break;
the_command = 0x9140; /*NOTHING ON HIM*/
if (aspirant_ptr->item == 0) {
runCommand();
break;
}
item1 = &inventory_items[aspirant_ptr->item - 1]; /*aspirant's item*/
item2 = (item_t *)(script_vars[kScrPool3_CurrentItem]); /*our offer*/
if (item2->flags == (ITEMFLG_OWNED | ITEMFLG_DONTWANT)) {
the_command = 0xC1C0;
runCommand();
break;
}
/*only trade for ROPE, LANTERN, STONE FLY, GOBLET*/
if (item1->name < 104 || item1->name >= 108) {
runCommand();
break;
}
if ((item1->name != item2->name)
&& (item2->name == 109 /*SKULL*/
|| item2->name == 132 /*ZAPSTIK*/
|| item2->name == 108 /*DAGGER*/
|| script_byte_vars.rand_value < 154)) {
/*show confirmation menu*/
script_byte_vars.trade_accepted = 0;
the_command = 0xC1C6;
runCommand();
if (script_byte_vars.trade_accepted == 0)
break;
item2->flags = ITEMFLG_ASPIR;
item1->flags = ITEMFLG_OWNED;
aspirant_ptr->item = script_byte_vars.inv_item_index;
switch (item2->name) {
case 132: /*ZAPSTIK*/
script_byte_vars.zapstiks_owned--;
the_command = 0xC04B;
break;
case 104: /*ROPE*/
the_command = 0xC1BA;
break;
case 107: /*GOBLET*/
the_command = 0xC1BB;
break;
default: /*STONE FLY*/
the_command = 0xC1B9;
}
runCommand();
break;
} else {
/*not interested*/
item2->flags = ITEMFLG_OWNED | ITEMFLG_DONTWANT;
the_command = 0xC1BD;
runCommand();
continue;
}
}
reclaimRefusedItems();
script_ptr = old_script;
script_end_ptr = old_script_end;
return 0;
}
/*
Steal a Zapstik form Protozorq
*/
uint16 SCR_4_StealZapstik(void) {
byte *old_script;
pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]);
script_ptr++;
old_script = script_ptr;
if ((pers->index & ~7) != 0x30) {
the_command = 0x9148; /*YOU`VE ALREADY GOT IT*/
runCommand();
} else {
pers->index &= ~0x18;
script_vars[kScrPool3_CurrentItem] = &inventory_items[kItemZapstik1 - 1 + (script_byte_vars.cur_pers - 1) - kPersProtozorq1];
script_byte_vars.steals_count++;
bounceCurrentItem(ITEMFLG_OWNED, 85); /*bounce to inventory*/
the_command = 0x9147; /*YOU GET HIS ZAPSTIK*/
if (script_byte_vars.zapstik_stolen == 0) {
runCommand();
script_byte_vars.zapstik_stolen = 1;
the_command = 0x9032; /*THIS SHOULD GIVE YOU THE EDGE IN MOST COMBATS!*/
}
runCommand();
}
script_ptr = old_script;
return 0;
}
byte wait_delta = 0;
/*
Wait for a specified number of seconds (real time) or a keypress
*/
void wait(byte seconds) {
warning("STUB: Wait(%d)", seconds);
#if 0
struct time t;
uint16 endtime;
seconds += wait_delta;
if (seconds > 127) /*TODO: is this a check for a negative value?*/
seconds = 0;
gettime(&t);
endtime = t.ti_sec * 100 + t.ti_hund + seconds * 100;
while (buttons == 0) {
uint16 current;
gettime(&t);
current = t.ti_sec * 100 + t.ti_hund;
if (endtime >= 6000 && current < 2048) /*TODO: some kind of overflow check???*/
current += 6000;
if (current >= endtime)
break;
}
#endif
}
/*
Wait for a 4 seconds or a keypress
*/
uint16 SCR_2C_Wait4(void) {
script_ptr++;
wait(4);
return 0;
}
/*
Wait for a specified number of seconds or a keypress
TODO: Always waits for a 4 seconds due to a bug?
*/
uint16 SCR_2D_Wait(void) {
byte seconds;
script_ptr++;
seconds = *script_ptr++;
(void)seconds;
wait(4); /*TODO: looks like a bug?*/
return 0;
}
/*
Show blinking prompt indicator and wait for a keypress
*/
uint16 SCR_2E_promptWait(void) {
script_ptr++;
promptWait();
return 0;
}
#define VARTYPE_VAR 0x80
#define VARTYPE_BLOCK 0x40
#define VARTYPE_WORD 0x20
#define VARTYPE_KIND 0x1F
#define VARSIZE_BYTE 0
#define VARSIZE_WORD 1
byte var_size;
/*
Fetch variable's value and address
*/
uint16 loadVar(byte **ptr, byte **varptr) {
byte vartype;
byte *varbase;
uint16 value = 0;
var_size = VARSIZE_BYTE;
vartype = *((*ptr)++);
if (vartype & VARTYPE_VAR) {
/*variable*/
byte varoffs;
varbase = (byte *)script_vars[vartype & VARTYPE_KIND];
if (vartype & VARTYPE_BLOCK) {
byte *end;
byte index = *((*ptr)++);
varbase = seekToEntryW(varbase, index, &end);
}
varoffs = *((*ptr)++);
#if 1
{
int16 maxoffs = 0;
switch (vartype & VARTYPE_KIND) {
case kScrPool0_WordVars0:
case kScrPool1_WordVars1:
maxoffs = sizeof(script_word_vars);
break;
case kScrPool2_ByteVars:
maxoffs = sizeof(script_byte_vars);
break;
case kScrPool3_CurrentItem:
maxoffs = sizeof(item_t);
break;
case kScrPool4_ZoneSpots:
maxoffs = RES_ZONES_MAX;
break;
case kScrPool5_Persons:
maxoffs = sizeof(pers_list);
break;
case kScrPool6_Inventory:
maxoffs = sizeof(inventory_items);
break;
case kScrPool7_Zapstiks:
maxoffs = sizeof(inventory_items) - sizeof(item_t) * (kItemZapstik1 - 1);
break;
case kScrPool8_CurrentPers:
maxoffs = sizeof(pers_t);
break;
}
if (varoffs >= maxoffs) {
warning("Scr var out of bounds @ %X (pool %d, ofs 0x%X, max 0x%X)", (uint16)(script_ptr - templ_data), vartype & VARTYPE_KIND, varoffs, maxoffs);
promptWait();
}
}
#endif
value = varbase[varoffs];
if (vartype & VARTYPE_WORD) {
value = (value << 8) | varbase[varoffs + 1];
var_size = VARSIZE_WORD;
}
*varptr = &varbase[varoffs];
#if 0
/*TODO: debug stuff, remove me*/
if (varoffs == 0x48)
warning("Var 2.%X = %X", varoffs, value);
#endif
} else {
/*immediate value*/
value = *((*ptr)++);
if (vartype & VARTYPE_WORD) {
value = (value << 8) | *((*ptr)++);
var_size = VARSIZE_WORD;
}
*varptr = 0;
}
return value;
}
#define MATHOP_END 0x80
#define MATHOP_CMP 0x40
#define MATHOP_ADD 0x20
#define MATHOP_SUB 0x10
#define MATHOP_AND 0x08
#define MATHOP_OR 0x04
#define MATHOP_XOR 0x02
#define MATHOP_EQ 0x20
#define MATHOP_B 0x10
#define MATHOP_A 0x08
#define MATHOP_NEQ 0x04
#define MATHOP_LE 0x02
#define MATHOP_GE 0x01
/*
Perform math/logic operation on two operands
*/
uint16 mathOp(byte op, uint16 op1, uint16 op2) {
if (op & MATHOP_CMP) {
if (op & MATHOP_EQ)
if (op1 == op2) return 0xffff;
if (op & MATHOP_B)
if (op1 < op2) return 0xffff;
if (op & MATHOP_A)
if (op1 > op2) return 0xffff;
if (op & MATHOP_NEQ)
if (op1 != op2) return 0xffff;
if (op & MATHOP_LE)
if ((int16)op1 <= (int16)op2) return 0xffff;
if (op & MATHOP_GE)
if ((int16)op1 >= (int16)op2) return 0xffff;
return 0;
} else {
if (op & MATHOP_ADD)
op1 += op2;
if (op & MATHOP_SUB)
op1 -= op2;
if (op & MATHOP_AND)
op1 &= op2;
if (op & MATHOP_OR)
op1 |= op2;
if (op & MATHOP_XOR)
op1 ^= op2;
return op1;
}
}
/*
Evaluate an expression
*/
uint16 mathExpr(byte **ptr) {
byte op;
uint16 op1, op2;
byte *opptr;
op1 = loadVar(ptr, &opptr);
while (((op = *((*ptr)++)) & MATHOP_END) == 0) {
op2 = loadVar(ptr, &opptr);
op1 = mathOp(op, op1, op2);
}
return op1;
}
/*
Evaluate an expression and assign result to a variable
*/
uint16 SCR_3B_MathExpr(void) {
uint16 op1, op2;
byte *opptr;
script_ptr++;
/*get result variable pointer*/
op1 = loadVar(&script_ptr, &opptr);
/*evaluate*/
op2 = mathExpr(&script_ptr);
/*store result*/
/*TODO: original bug? MathExpr may overwrite global var_size, so mixed-size expressions will produce errorneous results*/
if (var_size == VARSIZE_BYTE)
*opptr = op2 & 255;
else {
opptr[0] = op2 >> 8; /*store in big-endian*/
opptr[1] = op2 & 255;
}
(void)op1;
/*return op1;*/ /*previous value, never used?*/
return 0;
}
/*
Discard current callchain (the real one) and execute command
*/
uint16 SCR_4D_PriorityCommand(void) {
script_ptr++;
the_command = *script_ptr++; /*little-endian*/
the_command |= (*script_ptr++) << 8;
the_command |= 0xF000;
g_vm->_prioritycommand_1 = true;
return ScriptRerun;
}
/*
Jump to routine
*/
uint16 SCR_12_Chain(void) {
script_ptr++;
the_command = *script_ptr++; /*little-endian*/
the_command |= (*script_ptr++) << 8;
script_ptr = getScriptSubroutine(the_command - 1);
return 0;
}
/*
Absolute jump
Jumping past current routine ends the script
*/
uint16 SCR_33_Jump(void) {
uint16 offs;
script_ptr++;
offs = *script_ptr++; /*little-endian*/
offs |= (*script_ptr++) << 8;
script_ptr = templ_data + offs;
return 0;
}
/*
Conditional jump (IF/ELSE block)
*/
uint16 SCR_3C_CondExpr(void) {
script_ptr++;
if (mathExpr(&script_ptr)) {
/*fall to IF block*/
script_ptr += 2;
} else {
/*branch to ELSE block*/
script_ptr -= 1; /*simulate opcode byte for Jump handler*/
return SCR_33_Jump();
}
return 0;
}
/*
Absolute subroutine call
*/
uint16 SCR_34_Call(void) {
uint16 offs;
script_ptr++;
offs = *script_ptr++; /*little-endian*/
offs |= (*script_ptr++) << 8;
*script_stack_ptr++ = script_ptr;
*script_stack_ptr++ = script_end_ptr;
script_ptr = templ_data + offs;
return 0;
}
/*
Return from script subroutine
*/
uint16 SCR_35_Ret(void) {
script_end_ptr = *(--script_stack_ptr);
script_ptr = *(--script_stack_ptr);
return 0;
}
/*
Draw portrait, pushing it from left to right
*/
uint16 SCR_5_DrawPortraitLiftRight(void) {
byte x, y, width, height;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
/*TODO: use local args instead of globals*/
cga_AnimLiftToRight(width, cur_image_pixels + width - 1, width, 1, height, CGA_SCREENBUFFER, cga_CalcXY_p(x, y));
return 0;
}
/*
Draw portrait, pushing it from right to left
*/
uint16 SCR_6_DrawPortraitLiftLeft(void) {
byte x, y, width, height;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
/*TODO: use local args instead of globals*/
cga_AnimLiftToLeft(width, cur_image_pixels, width, 1, height, CGA_SCREENBUFFER, cga_CalcXY_p(x + width - 1, y));
return 0;
}
/*
Draw portrait, pushing it from top to bottom
*/
uint16 SCR_7_DrawPortraitLiftDown(void) {
byte x, y, width, height;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
/*TODO: use local args instead of globals*/
cga_AnimLiftToDown(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs);
return 0;
}
/*
Draw portrait, pushing it from bottom to top
*/
uint16 SCR_8_DrawPortraitLiftUp(void) {
byte x, y, width, height;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
/*TODO: use local args instead of globals*/
cga_AnimLiftToUp(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, x, y + height - 1);
return 0;
}
/*
Draw portrait, no special effects
*/
uint16 SCR_9_DrawPortrait(void) {
byte x, y, width, height;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
cga_BlitAndWait(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs);
return 0;
}
/*
Draw portrait, no special effects
*/
uint16 SCR_A_DrawPortrait(void) {
return SCR_9_DrawPortrait();
}
/*
Draw screen pixels using 2-phase clockwise twist
*/
void twistDraw(byte x, byte y, byte width, byte height, byte *source, byte *target) {
int16 i;
uint16 sx, ex, sy, ey, t;
sx = x * 4;
ex = x * 4 + width * 4 - 1;
sy = y;
ey = y + height - 1;
for (i = 0; i < width * 4; i++) {
cga_TraceLine(sx, ex, sy, ey, source, target);
waitVBlank();
sx += 1;
ex -= 1;
}
t = sx;
sx = ex + 1;
ex = t - 1;
t = sy;
sy = ey;
ey = t;
for (i = 0; i < height; i++) {
cga_TraceLine(sx, ex, sy, ey, source, target);
waitVBlank();
sy -= 1;
ey += 1;
}
}
/*
Draw image with twist-effect
*/
uint16 SCR_B_DrawPortraitTwistEffect(void) {
byte x, y, width, height;
uint16 offs;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
offs = cga_CalcXY_p(x, y);
cga_SwapScreenRect(cur_image_pixels, width, height, backbuffer, offs);
twistDraw(x, y, width, height, backbuffer, frontbuffer);
cga_BlitAndWait(scratch_mem2, width, width, height, backbuffer, offs);
return 0;
}
/*
Draw screen pixels using arc-like sweep
*/
void arcDraw(byte x, byte y, byte width, byte height, byte *source, byte *target) {
int16 i;
uint16 sx, ex, sy, ey;
sx = x * 4;
ex = x * 4 + width * 2 - 1;
sy = y + height - 1;
ey = y + height - 1;
for (i = 0; i < height; i++) {
cga_TraceLine(sx, ex, sy, ey, source, target);
waitVBlank();
sy -= 1;
}
for (i = 0; i < width * 4; i++) {
cga_TraceLine(sx, ex, sy, ey, source, target);
waitVBlank();
sx += 1;
}
for (i = 0; i < height + 1; i++) {
cga_TraceLine(sx, ex, sy, ey, source, target);
waitVBlank();
sy += 1;
}
}
/*
Draw image with arc-effect
*/
uint16 SCR_C_DrawPortraitArcEffect(void) {
byte x, y, width, height;
uint16 offs;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
offs = cga_CalcXY_p(x, y);
cga_SwapScreenRect(cur_image_pixels, width, height, backbuffer, offs);
arcDraw(x, y, width, height, backbuffer, frontbuffer);
cga_BlitAndWait(scratch_mem2, width, width, height, backbuffer, offs);
return 0;
}
/*
Draw image with slow top-to-down reveal effect by repeatedly draw its every 17th pixel
*/
uint16 SCR_D_DrawPortraitDotEffect(void) {
//int16 i;
byte x, y, width, height;
uint16 offs, step = 17;
byte *target = CGA_SCREENBUFFER;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
cur_image_end = width * height;
int16 count = 0;
for (offs = 0; offs != cur_image_end;) {
target[cga_CalcXY_p(x + offs % cur_image_size_w, y + offs / cur_image_size_w)] = cur_image_pixels[offs];
if (count % 5 == 0)
cga_blitToScreen(offs, 4, 1);
offs += step;
if (offs > cur_image_end)
offs -= cur_image_end;
count++;
}
return 0;
}
/*
Draw image with slow zoom-in reveal effect
*/
uint16 SCR_E_DrawPortraitZoomIn(void) {
byte x, y, width, height;
script_ptr++;
if (!drawPortrait(&script_ptr, &x, &y, &width, &height))
return 0;
cga_AnimZoomIn(cur_image_pixels, cur_image_size_w, cur_image_size_h, frontbuffer, cur_image_offs);
return 0;
}
uint16 drawPortraitZoomed(byte **params) {
byte x, y, width, height;
byte zwidth, zheight;
right_button = 0; /*prevent cancel or zoom parameters won't be consumed*/
if (!drawPortrait(params, &x, &y, &width, &height))
return 0; /*TODO: maybe just remove the if/return instead?*/
zwidth = *((*params)++);
zheight = *((*params)++);
/*adjust the rect for new size*/
last_dirty_rect->width = zwidth + 2;
last_dirty_rect->height = zheight;
cga_ZoomImage(cur_image_pixels, cur_image_size_w, cur_image_size_h, zwidth, zheight, frontbuffer, cur_image_offs);
return 0;
}
/*
Draw image with specified w/h zoom
*/
uint16 SCR_10_DrawPortraitZoomed(void) {
script_ptr++;
drawPortraitZoomed(&script_ptr);
#if 0
/*TODO: debug wait*/
promptWait();
#endif
return 0;
}
/*
Hide portrait, pushing it from right to left
*/
uint16 SCR_19_HidePortraitLiftLeft(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
/*TODO: This originally was done by reusing door sliding routine*/
/*offs = cga_CalcXY_p(x + 1, y);*/
offs++;
while (--width) {
cga_HideScreenBlockLiftToLeft(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs);
}
offs--;
/*hide leftmost line*/
/*TODO: move this to CGA?*/
uint16 ooffs = offs;
byte oh = height;
while (height--) {
memcpy(frontbuffer + offs, backbuffer + offs, 1);
offs ^= CGA_ODD_LINES_OFS;
if ((offs & CGA_ODD_LINES_OFS) == 0)
offs += CGA_BYTES_PER_LINE;
}
cga_blitToScreen(ooffs, 1, oh);
return 0;
}
/*
Hide portrait, pushing it from left to right
*/
uint16 SCR_1A_HidePortraitLiftRight(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
/*TODO: This originally was done by reusing door sliding routine*/
offs = cga_CalcXY_p(x + width - 2, y);
while (--width) {
cga_HideScreenBlockLiftToRight(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs);
}
offs++;
/*hide leftmost line*/
/*TODO: move this to CGA?*/
uint16 ooffs = offs;
byte oh = height;
while (height--) {
memcpy(frontbuffer + offs, backbuffer + offs, 1);
offs ^= CGA_ODD_LINES_OFS;
if ((offs & CGA_ODD_LINES_OFS) == 0)
offs += CGA_BYTES_PER_LINE;
}
cga_blitToScreen(ooffs, 1, oh);
return 0;
}
/*
Hide portrait, pushing it from bottom to top
*/
uint16 SCR_1B_HidePortraitLiftUp(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
offs = cga_CalcXY_p(x, y + 1);
while (--height) {
cga_HideScreenBlockLiftToUp(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs);
}
/*hide topmost line*/
/*TODO: move this to CGA?*/
offs ^= CGA_ODD_LINES_OFS;
if ((offs & CGA_ODD_LINES_OFS) != 0)
offs -= CGA_BYTES_PER_LINE;
memcpy(CGA_SCREENBUFFER + offs, backbuffer + offs, width);
cga_blitToScreen(offs, width, 1);
return 0;
}
/*
Hide portrait, pushing it from top to bottom
*/
uint16 SCR_1C_HidePortraitLiftDown(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
offs = cga_CalcXY_p(x, y + height - 2);
while (--height) {
cga_HideScreenBlockLiftToDown(1, CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs);
}
/*hide bottommost line*/
/*TODO: move this to CGA?*/
offs ^= CGA_ODD_LINES_OFS;
if ((offs & CGA_ODD_LINES_OFS) == 0)
offs += CGA_BYTES_PER_LINE;
memcpy(CGA_SCREENBUFFER + offs, backbuffer + offs, width);
cga_blitToScreen(offs, width, 1);
return 0;
}
/*
Hide portrait with twist effect
*/
uint16 SCR_1E_HidePortraitTwist(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
twistDraw(x, y, width, height, backbuffer, frontbuffer);
return 0;
}
/*
Hide portrait with arc effect
*/
uint16 SCR_1F_HidePortraitArc(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
arcDraw(x, y, width, height, backbuffer, frontbuffer);
return 0;
}
/*
Hide portrait with dots effect
*/
uint16 SCR_20_HidePortraitDots(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
dot_effect_step = 17;
dot_effect_delay = 100;
copyScreenBlockWithDotEffect(backbuffer, x, y, width, height, frontbuffer);
return 0;
}
/*
Play room's door open animation
*/
uint16 SCR_39_AnimRoomDoorOpen(void) {
byte door;
script_ptr++;
door = *script_ptr++;
animRoomDoorOpen(door);
return 0;
}
/*
Play room's door close animation
*/
uint16 SCR_3A_AnimRoomDoorClose(void) {
byte door;
script_ptr++;
door = *script_ptr++;
animRoomDoorClose(door);
return 0;
}
uint16 SCR_25_ChangeZoneOnly(void) {
byte index;
byte old = script_byte_vars.zone_room;
script_ptr++;
index = *script_ptr++;
changeZone(index);
script_byte_vars.zone_room = old;
return 0;
}
#define JCOUNT 16
typedef struct jpoint_t {
signed short x;
signed short y;
} jpoint_t;
static jpoint_t jdeltas[JCOUNT] = {
{0, -2},
{1, -2},
{2, -2},
{2, -1},
{2, 0},
{2, 1},
{2, 2},
{1, 2},
{0, 2},
{ -1, 2},
{ -2, 2},
{ -2, 1},
{ -2, 0},
{ -2, -1},
{ -2, -2},
{ -1, -2}
};
/*
Play exploding zoom animation
*/
void jaggedZoom(byte *source, byte *target) {
int16 i;
jpoint_t points[JCOUNT + 1];
uint16 outside = 0;
uint16 cycle = 0;
uint16 choices = 0;
for (i = 0; i < JCOUNT; i++) {
points[i].x = 320;
points[i].y = 200;
}
points[i].x = 0;
points[i].y = 0;
for (;;) {
cycle++;
if (cycle % 8 == 0)
choices = getRandW();
for (i = 0; i < JCOUNT; i++) {
signed short t;
if (choices & (1 << i)) {
t = points[i].x + jdeltas[i].x;
if (t < 0 || t >= 600) { /*TODO: 640?*/
outside |= 0x8000; /*TODO: should this mask be rotated?*/
t = points[i].x;
}
points[i].x = t;
t = points[i].y + jdeltas[i].y;
if (t < 0 || t >= 400) {
outside |= 0x8000;
t = points[i].y;
}
points[i].y = t;
}
}
if (outside)
break;
for (i = 0; i < JCOUNT; i++) {
uint16 sx = points[i].x;
uint16 sy = points[i].y;
uint16 ex = points[i + 1].x;
uint16 ey = points[i + 1].y;
if (ex == 0 && ey == 0) {
ex = points[0].x;
ey = points[0].y;
}
cga_TraceLine(sx / 2, ex / 2, sy / 2, ey / 2, source, target);
/*TODO: waitVBlank(); maybe?*/
}
}
}
typedef struct star_t {
uint16 ofs;
byte pixel;
byte mask;
signed short x;
signed short y;
uint16 z;
} star_t;
/*
Generate random star
*/
void randomStar(star_t *star) {
star->x = getRandW();
star->y = getRandW();
star->z = getRandW() & 0xFFF;
}
/*
Generate a bunch of random stars
*/
star_t *initStarfield(void) {
int16 i;
star_t *stars = (star_t *)scratch_mem2;
for (i = 0; i < 300; i++) {
stars[i].ofs = 0;
stars[i].pixel = 0;
stars[i].mask = 0;
randomStar(&stars[i]);
}
return stars;
}
/*
Draw a frame of starfield animation and update stars
*/
void drawStars(star_t *stars, int16 iter, byte *target) {
int16 i;
/*TODO: bug? initialized 300 stars, but animated only 256?*/
for (i = 0; i < 256; i++, stars++) {
short z, x, y;
byte pixel, mask;
target[stars->ofs] &= stars->mask;
if (stars->z < 328) {
if (iter >= 30) {
randomStar(stars);
stars->z |= 0x1800;
}
continue;
}
stars->z -= 328;
z = 0xCFFFFul / (stars->z + 16);
x = ((long)z * stars->x) >> 16;
y = ((long)z * stars->y) >> 16;
x += 320 / 2;
y += 200 / 2;
if (x < 0 || x >= 320 || y < 0 || y >= 200) {
stars->z = 0;
continue;
}
stars->ofs = cga_CalcXY(x, y);
pixel = (stars->z < 0xE00) ? 0xC0 : 0x40;
pixel >>= (x % 4) * 2;
mask = 0xC0;
mask = ~(mask >> (x % 4) * 2);
stars->pixel = pixel;
stars->mask = mask;
target[stars->ofs] &= mask;
target[stars->ofs] |= pixel;
}
}
/*
Play starfield animation
*/
void animStarfield(star_t *stars, byte *target) {
int16 i;
for (i = 100; i; i--)
drawStars(stars, i, target);
}
/*
Play Game Over sequence and restart the game
*/
uint16 SCR_26_GameOver(void) {
IFGM_PlaySample(160);
in_de_profundis = 0;
script_byte_vars.game_paused = 1;
memset(backbuffer, 0, sizeof(backbuffer) - 2); /*TODO: original bug?*/
jaggedZoom(backbuffer, frontbuffer);
cga_BackBufferToRealFull();
cga_ColorSelect(0x30);
animStarfield(initStarfield(), frontbuffer);
playAnim(44, 156 / 4, 95);
script_byte_vars.zone_index = 135;
/*reload background*/
while (!loadFond())
askDisk2();
jaggedZoom(backbuffer, frontbuffer);
cga_BackBufferToRealFull();
restartGame();
return 0;
}
/*
Draw all active room's persons
*/
uint16 SCR_4C_DrawPersons(void) {
script_ptr++;
drawPersons();
return 0;
}
/*
Redraw all room's static objects
*/
uint16 SCR_13_RedrawRoomStatics(void) {
byte index;
script_ptr++;
index = *script_ptr++;
redrawRoomStatics(index, 0);
return 0;
}
/*
Go to a new zone
If go through a door, play door's opening animation
*/
uint16 SCR_42_LoadZone(void) {
byte index;
script_ptr++;
index = *script_ptr++;
skip_zone_transition = 0;
if (right_button)
script_byte_vars.last_door = 0;
else {
if ((script_byte_vars.cur_spot_flags & (SPOTFLG_20 | SPOTFLG_10 | SPOTFLG_8)) == 0)
script_byte_vars.last_door = script_byte_vars.cur_spot_flags & 7;
else if ((script_byte_vars.cur_spot_flags & ((SPOTFLG_20 | SPOTFLG_10 | SPOTFLG_8))) == SPOTFLG_8) {
skip_zone_transition = 1;
animRoomDoorOpen(script_byte_vars.cur_spot_idx);
script_byte_vars.last_door = script_byte_vars.cur_spot_flags & 7;
} else
script_byte_vars.last_door = 0;
}
beforeChangeZone(index);
changeZone(index);
script_byte_vars.zone_area_copy = script_byte_vars.zone_area;
script_byte_vars.cur_spot_idx = findInitialSpot();
skip_zone_transition |= script_byte_vars.cur_spot_idx;
drawRoomStatics();
if (script_byte_vars.bvar_5F != 0) {
redrawRoomStatics(script_byte_vars.bvar_5F, 0);
script_byte_vars.bvar_5F = 0;
}
backupSpotsImages();
prepareVorts();
prepareTurkey();
prepareAspirant();
drawPersons();
script_byte_vars.cur_spot_flags = 0;
return 0;
}
/*
Draw current sprites
*/
uint16 SCR_59_blitSpritesToBackBuffer(void) {
script_ptr++;
blitSpritesToBackBuffer();
return 0;
}
/*
Apply current palette
*/
uint16 SCR_5A_SelectPalette(void) {
script_ptr++;
selectPalette();
return 0;
}
/*
Apply specific palette
*/
uint16 SCR_5E_SelectTempPalette(void) {
byte index;
script_ptr++;
index = *script_ptr++;
selectSpecificPalette(index);
return 0;
}
/*
Draw new zone
*/
uint16 SCR_43_RefreshZone(void) {
script_ptr++;
refreshZone();
return 0;
}
/*
Go to new zone and draw it
*/
uint16 SCR_36_ChangeZone(void) {
SCR_42_LoadZone();
refreshZone();
return 0;
}
/*
Draw a static sprite in the room
*/
void SCR_DrawRoomObjectBack(byte *x, byte *y, byte *w, byte *h) {
byte obj[3];
script_ptr++;
obj[0] = *script_ptr++; /*spr*/
obj[1] = *script_ptr++; /*x*/
obj[2] = *script_ptr++; /*y*/
drawRoomStaticObject(obj, x, y, w, h);
}
/*
Draw a static sprite in the room (to backbuffer)
*/
uint16 SCR_5F_DrawRoomObjectBack(void) {
byte x, y, w, h;
SCR_DrawRoomObjectBack(&x, &y, &w, &h);
return 0;
}
/*
Display a static sprite in the room (to screen)
*/
uint16 SCR_11_DrawRoomObject(void) {
byte x, y, w, h;
SCR_DrawRoomObjectBack(&x, &y, &w, &h);
cga_CopyScreenBlock(backbuffer, w, h, frontbuffer, cga_CalcXY_p(x, y));
return 0;
}
/*
Draw box with item sprite and its name
*/
uint16 SCR_3_DrawItemBox(void) {
byte current;
item_t *item;
byte x, y;
byte *msg;
script_ptr++;
current = *script_ptr++;
if (current)
item = (item_t *)script_vars[kScrPool3_CurrentItem];
else
item = &inventory_items[aspirant_ptr->item - 1];
x = dirty_rects[0].x;
y = dirty_rects[0].y + 70;
msg = seekToString(desci_data, 274 + item->name);
desciTextBox(x, y, 18, msg);
drawSpriteN(item->sprite, x, y + 1, frontbuffer);
return 0;
}
/*
Draw simple bubble with text
*/
uint16 SCR_37_desciTextBox(void) {
byte x, y, w;
byte *msg;
script_ptr++;
msg = seekToStringScr(desci_data, *script_ptr, &script_ptr);
script_ptr++;
x = *script_ptr++;
y = *script_ptr++;
w = *script_ptr++;
desciTextBox(x, y, w, msg);
return 0;
}
/*
Play portrait animation
*/
uint16 SCR_18_AnimPortrait(void) {
byte layer, index, delay;
script_ptr++;
layer = *script_ptr++;
index = *script_ptr++;
delay = *script_ptr++;
animPortrait(layer, index, delay);
return 0;
}
/*
Play animation
*/
uint16 SCR_38_PlayAnim(void) {
byte index, x, y;
script_ptr++;
index = *script_ptr++;
x = *script_ptr++;
y = *script_ptr++;
playAnim(index, x, y);
return 0;
}
/*
Pop up the actions menu and handle its commands
*/
uint16 SCR_3D_ActionsMenu(void) {
uint16 cmd;
byte *old_script = script_ptr;
byte *old_script_end = script_end_ptr;
act_menu_x = 0xFF;
for (;;) {
script_ptr++;
actionsMenu(&script_ptr);
if (the_command == 0xFFFF)
break;
cmd = the_command & 0xF000;
if (cmd == 0xC000 || cmd == 0xA000) {
return ScriptRerun;
}
runCommand();
// For properly returning to RunScript() during the 2nd Call from SCR_4D_PriorityCommand() to runCommand()
if (g_vm->_prioritycommand_1) {
g_vm->_prioritycommand_2 = true;
break;
}
script_byte_vars.used_commands++;
if (script_byte_vars.bvar_43 == 0 && script_byte_vars.used_commands > script_byte_vars.check_used_commands) {
the_command = Swap16(script_word_vars.next_aspirant_cmd);
if (the_command)
return ScriptRerun;
}
script_ptr = old_script;
if (--script_byte_vars.tries_left == 0)
resetAllPersons();
}
script_end_ptr = old_script_end;
return ScriptContinue;
}
/*
The Wall room puzzle
*/
uint16 SCR_3E_TheWallAdvance(void) {
script_ptr++;
IFGM_PlaySample(29);
script_byte_vars.the_wall_phase = (script_byte_vars.the_wall_phase + 1) % 4;
switch (script_byte_vars.the_wall_phase) {
default:
theWallPhase3_DoorOpen1();
break;
case 0:
theWallPhase0_DoorOpen2();
break;
case 1:
theWallPhase1_DoorClose1();
break;
case 2:
theWallPhase2_DoorClose2();
break;
}
return 0;
}
/*
When playing cups with proto
*/
uint16 SCR_28_MenuLoop(void) {
byte cursor;
byte mask, value;
script_ptr++;
cursor = *script_ptr++;
mask = *script_ptr++;
value = *script_ptr++;
selectCursor(cursor);
menuLoop(mask, value);
return 0;
}
/*
Restore screen data from back buffer as specified by dirty rects of specified index
*/
uint16 SCR_2A_PopDialogRect(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs); /*TODO: implicit target*/
cga_CopyScreenBlock(backbuffer, 2, 21, CGA_SCREENBUFFER, offs = (x << 8) | y); /*TODO: implicit target*/
cur_dlg_index = 0;
return 0;
}
/*
Restore screen data from back buffer as specified by dirty rect of kind dialog bubble
*/
uint16 SCR_2B_PopAllBubbles(void) {
script_ptr++;
popDirtyRects(DirtyRectBubble);
return 0;
}
/*
Hide a portrait, with shatter effect
*/
uint16 SCR_22_HidePortraitShatter(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
cga_HideShatterFall(CGA_SCREENBUFFER, backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
/*
Hide a portrait, no special effects
*/
uint16 SCR_23_HidePortrait(void) {
byte index;
byte kind;
byte x, y;
byte width, height;
uint16 offs;
script_ptr++;
index = *script_ptr++;
getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
if (right_button) {
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
return 0;
}
/*
Hide a portrait, no special effects
*/
uint16 SCR_1D_HidePortrait(void) {
return SCR_23_HidePortrait();
}
/*
Hide a portrait, no special effects
*/
uint16 SCR_21_HidePortrait(void) {
return SCR_23_HidePortrait();
}
/*
Hide a portrait, no special effects
*/
uint16 SCR_3F_HidePortrait(void) {
return SCR_23_HidePortrait();
}
/*
Restore screen data from back buffer for all portraits
*/
uint16 SCR_24_PopAllPortraits(void) {
script_ptr++;
popDirtyRects(DirtyRectSprite);
return 0;
}
/*
Restore screen data from back buffer for all text bubbles
*/
uint16 SCR_40_PopAllTextBoxes() {
script_ptr++;
popDirtyRects(DirtyRectText);
return 0;
}
/*
Draw updated Hands in Who Will Be Saved
*/
uint16 SCR_41_LiftHand(void) {
script_ptr++;
redrawRoomStatics(92, script_byte_vars.hands);
cga_BackBufferToRealFull();
playSound(31);
return 0;
}
byte fight_mode = 0;
uint16 SCR_30_Fight(void) {
static byte player_image[] = {26, 0, 0};
byte *image = player_image;
byte x, y, width, height, kind;
uint16 offs;
byte *old_script, *old_script_end = script_end_ptr;
pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]);
byte strenght, win, rnd;
script_ptr++;
old_script = script_ptr;
x = 140 / 4;
y = 20;
fight_mode = 1;
if (pers->name != 44) { /*VORT*/
if (next_vorts_cmd == 0xA015) {
the_command = 0xA015;
runCommand();
selectPerson(PersonOffset(pers - pers_list));
}
if (Swap16(script_word_vars.next_aspirant_cmd) == 0xC357) {
the_command = 0xC357;
runCommand();
}
pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]);
if (pers->name != 56 && pers->name != 51) { /*MONKEY, TURKEY*/
x = dirty_rects[0].x + 64 / 4;
y = dirty_rects[0].y;
fight_mode = 0;
}
}
/*draw player portrait*/
player_image[1] = x;
player_image[2] = y;
if (drawPortrait(&image, &x, &y, &width, &height))
cga_AnimLiftToLeft(width, cur_image_pixels, width, 1, height, CGA_SCREENBUFFER, cga_CalcXY_p(x + width - 1, y));
blinkToWhite();
if (pers->name != 44 && pers->name != 56 && pers->name != 51) { /*VORT, MONKEY, TURKEY*/
getDirtyRectAndFree(1, &kind, &x, &y, &width, &height, &offs);
cga_CopyScreenBlock(backbuffer, width, height, CGA_SCREENBUFFER, offs);
}
/*check fight outcome*/
strenght = 0;
script_byte_vars.fight_status = 0;
if (script_byte_vars.extreme_violence == 0) {
static byte character_strenght[] = {
1, /*THE MASTER OF ORDEALS*/
3, /*PROTOZORQ*/
1, /*VORT*/
1, /*THE POORMOUTH*/
1, /*KHELE*/
1, /*THE MISTRESS*/
5, /*DEILOS*/
3, /*ASPIRANT*/
2, /*DIVO*/
1, /*TURKEY*/
1, /*PRIESTESS*/
1, /*SCI FI*/
1, /*NORMAJEEN*/
1, /*ASH*/
1, /*MONKEY*/
1, /*HARSSK*/
1 /*ZORQ*/
};
strenght = character_strenght[pers->name - 42];
/*check if can decrease*/
if (strenght != 1 && (pers->flags & PERSFLG_80))
strenght--;
if (script_byte_vars.zapstiks_owned != 0 || script_byte_vars.bvar_66 != 0)
strenght--;
}
/*check if can increase*/
if (strenght != 5) {
if ((pers->item >= kItemDagger1 && pers->item <= kItemDagger4)
|| (pers->item >= kItemZapstik1 && pers->item <= kItemZapstik13) /*TODO: ignore kItemZapstik14?*/
|| pers->item == kItemBlade || pers->item == kItemChopper
|| ((pers->index >> 3) == 6))
strenght++;
}
/*
win flags:
1 - player win
2 - player lose
4 - "YOU RUN..."
8 - "THE ASPIRANT STEALS EVERYTHING YOU HAVE!"
0x20 - "TU NUH RAY VUN IN FAY VRABLE SIT YOU AISHUN."
0x40 - "OUT KUM UNSER TUN."
0x80 - "SIT YOU ASHUN KRITI KAL FOR TOONUH RAY VUN."
*/
win = 1;
rnd = script_byte_vars.rand_value;
#ifdef CHEAT
strenght = 1;
#endif
if (strenght >= 2) {
if (strenght == 2) {
if (rnd >= 205)
win = getRand() < 128 ? (0x40 | 0x10 | 1) : (0x40 | 0x10 | 2);
} else if (strenght == 4 && rnd < 100) {
win = getRand() < 128 ? (0x40 | 0x10 | 1) : (0x40 | 0x10 | 2);
} else {
win = 2;
if (strenght == 3) {
if (rnd < 128) /*TODO: check me, maybe original bug (checks against wrong reg?)*/
win = getRand() < 51 ? (0x80 | 0x10 | 1) : (0x80 | 0x10 | 2);
else
win = getRand() < 205 ? (0x20 | 0x10 | 1) : (0x20 | 0x10 | 2);
}
}
}
script_byte_vars.fight_status = win;
script_ptr = old_script;
script_end_ptr = old_script_end;
return 0;
}
byte prev_fight_mode = 0;
uint16 fight_pers_ofs = 0;
typedef struct fightentry_t {
byte room;
animdesc_t anim;
} fightentry_t;
fightentry_t fightlist1[] = {
{50, {47, {{36, 153}}}},
{51, {47, {{36, 153}}}},
{53, {47, {{37, 160}}}},
{54, {47, {{36, 153}}}},
{56, {47, {{31, 128}}}},
{57, {47, {{27, 161}}}},
{58, {47, {{28, 152}}}},
{59, {47, {{25, 153}}}},
{60, {47, {{22, 155}}}},
{61, {47, {{27, 160}}}}
};
fightentry_t fightlist2[] = {
{ 1, {24, {{42, 128}}}},
{ 2, {24, {{44, 126}}}},
{ 3, {24, {{47, 126}}}},
{ 4, {24, {{44, 126}}}},
{ 5, {24, {{47, 126}}}},
{ 6, {24, {{28, 126}}}},
{ 7, {24, {{55, 126}}}},
{ 8, {24, {{49, 126}}}},
{10, {24, {{41, 147}}}},
{11, {24, {{41, 147}}}},
{18, {24, {{41, 147}}}},
{19, {24, {{41, 147}}}},
{90, {24, {{44, 121}}}},
{91, {28, {{24, 123}}}},
{12, {24, {{41, 147}}}},
{13, {24, {{41, 147}}}},
{35, {24, {{39, 147}}}},
{42, {24, {{39, 147}}}},
{50, {55, {{46, 130}}}},
{52, {24, {{42, 121}}}},
{54, {55, {{46, 130}}}},
{61, {67, {{37, 125}}}},
{62, {55, {{32, 133}}}},
{63, {55, {{32, 133}}}},
{64, {55, {{32, 133}}}},
{65, {55, {{32, 133}}}}
};
fightentry_t fightlist3[] = {
{ 2, {25, {{35, 144}}}},
{ 3, {25, {{38, 144}}}},
{ 4, {25, {{35, 144}}}},
{ 5, {25, {{38, 144}}}},
{ 6, {25, {{19, 144}}}},
{ 7, {25, {{46, 144}}}},
{ 8, {26, {{64, 132}}}},
{10, {25, {{32, 165}}}},
{11, {25, {{32, 165}}}},
{12, {25, {{32, 165}}}},
{13, {25, {{32, 165}}}},
{18, {25, {{32, 165}}}},
{19, {25, {{32, 165}}}},
{90, {36, {{27, 127}}}},
{91, {27, {{44, 123}}}},
{35, {25, {{30, 165}}}},
{42, {25, {{30, 165}}}},
{50, {56, {{36, 153}}}},
{54, {56, {{36, 153}}}},
{62, {56, {{22, 156}}}},
{63, {56, {{22, 156}}}},
{64, {56, {{22, 156}}}},
{65, {56, {{22, 156}}}}
};
/*Draw defeated enemy*/
uint16 SCR_31_Fight2(void) {
script_ptr++;
if (script_byte_vars.bvar_43 != 18) {
pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]);
fight_pers_ofs = (byte *)pers - (byte *)pers_list; /*TODO check size*/
pers->flags |= PERSFLG_40;
pers->area = 0;
found_spot->flags &= ~SPOTFLG_80;
if (pers->index == 16) { /*Vort trio*/
pers_list[kPersVort2].area = script_byte_vars.zone_area;
pers_list[kPersVort2].flags = pers->flags;
if (script_byte_vars.zapstiks_owned == 0) {
static const animdesc_t anim19 = {ANIMFLG_USESPOT | 19, { { 0, 0 } }};
animateSpot(&anim19);
}
the_command = next_vorts_cmd;
runCommand();
} else if (pers->index == 8) { /*Vort duo*/
pers_list[kPersVort3].area = script_byte_vars.zone_area;
pers_list[kPersVort3].flags = pers->flags;
if (script_byte_vars.zapstiks_owned == 0) {
static const animdesc_t anim20 = {ANIMFLG_USESPOT | 20, { { 0, 0 } }};
animateSpot(&anim20);
}
the_command = next_vorts_cmd;
runCommand();
} else {
if (prev_fight_mode == 0
&& script_byte_vars.zapstiks_owned != 0
&& fight_mode == 0) {
script_byte_vars.fight_status &= ~1;
} else {
uint16 i;
fightentry_t *fightlist;
uint16 fightlistsize;
byte animidx;
prev_fight_mode = 0;
switch (pers->name) {
case 56: /*MONKEY*/
animidx = 47;
fightlist = fightlist1;
fightlistsize = 10;
break;
case 51: /*TURKEY*/
next_turkey_cmd = 0;
animidx = 66;
fightlist = fightlist1;
fightlistsize = 10;
break;
default:
animidx = 0;
fightlist = fightlist2;
fightlistsize = 26;
}
for (i = 0; i < fightlistsize; i++) {
if (fightlist[i].room == script_byte_vars.zone_room) {
if (animidx != 0) {
fightlist[i].anim.index = animidx;
IFGM_PlaySample(150);
}
if (fightlist[i].anim.index == 55)
playSound(151);
playAnim(fightlist[i].anim.index, fightlist[i].anim.params.coords.x, fightlist[i].anim.params.coords.y);
break;
}
}
}
}
}
return 0;
}
void FightWin(void) {
script_byte_vars.bvar_67 = 0;
if (script_byte_vars.bvar_43 != 18 && *spot_sprite != 0) {
cga_RestoreImage(*spot_sprite, frontbuffer);
cga_RestoreImage(*spot_sprite, backbuffer);
if (script_byte_vars.extreme_violence == 0
&& script_byte_vars.bvar_60 == 0
&& script_byte_vars.zapstiks_owned != 0
&& fight_mode == 0) {
script_byte_vars.bvar_67 = 1;
playSound(149);
playAnim(40, found_spot->sx, found_spot->sy);
}
}
prev_fight_mode = script_byte_vars.extreme_violence;
script_byte_vars.extreme_violence = 0;
}
uint16 SCR_32_FightWin(void) {
script_ptr++;
FightWin();
return 0;
}
void DrawDeathAnim(void) {
int16 i;
/*remove existing cadaver if any*/
if (selectPerson(PersonOffset(kPersCadaver))) {
found_spot->flags &= ~SPOTFLG_80;
cga_RestoreImage(*spot_sprite, backbuffer);
}
for (i = 0; i < 23; i++) {
if (fightlist3[i].room == script_byte_vars.zone_room) {
playAnim(fightlist3[i].anim.index, fightlist3[i].anim.params.coords.x, fightlist3[i].anim.params.coords.y);
break;
}
}
}
uint16 SCR_16_DrawDeathAnim(void) {
script_ptr++;
DrawDeathAnim();
return 0;
}
uint16 SCR_60_ReviveCadaver(void) {
pers_t *pers;
script_ptr++;
blitSpritesToBackBuffer();
selectPerson(PersonOffset(kPersCadaver));
script_byte_vars.bvar_60 = 1;
FightWin();
script_byte_vars.bvar_60 = 0;
pers_list[kPersCadaver].area = 0;
selectPerson(fight_pers_ofs);
zone_spots[5].flags = SPOTFLG_40 | SPOTFLG_10 | SPOTFLG_2 | SPOTFLG_1;
found_spot->flags |= SPOTFLG_80;
pers = (pers_t *)script_vars[kScrPool8_CurrentPers];
pers->flags &= ~PERSFLG_40;
pers->area = script_byte_vars.zone_area;
drawPersons();
cga_BackBufferToRealFull();
return 0;
}
uint16 SCR_57_ShowCharacterSprite(void) {
byte index, x, y;
script_ptr++;
index = *script_ptr++;
x = *script_ptr++;
y = *script_ptr++;
drawCharacterSprite(index, x, y, frontbuffer);
return 0;
}
uint16 SCR_58_DrawCharacterSprite(void) {
byte index, x, y;
script_ptr++;
index = *script_ptr++;
x = *script_ptr++;
y = *script_ptr++;
drawCharacterSprite(index, x, y, backbuffer);
return 0;
}
extern void exitGame(void);
uint16 SCR_15_SelectSpot(void) {
byte mask, index;
script_ptr++;
mask = *script_ptr++;
index = *script_ptr++;
if (mask != 0) {
index = findSpotByFlags(mask, index); /*TODO: return 0 if not found?*/
if (index == 0xFF) {
TODO("ERROR: SelectSpot: spot not found");
exitGame(); /*hard abort*/
}
}
found_spot = &zone_spots[index - 1];
script_byte_vars.cur_spot_idx = index;
spot_sprite = &sprites_list[index - 1];
findPerson();
if (script_byte_vars.cur_pers == 0)
script_vars[kScrPool8_CurrentPers] = &pers_list[kPersProtozorq12];
return 0;
}
uint16 SCR_44_BackBufferToScreen(void) {
script_ptr++;
cga_BackBufferToRealFull();
return 0;
}
/*
Animate De Profundis room on entry
*/
uint16 SCR_45_DeProfundisRoomEntry(void) {
uint16 w, h;
uint16 sprofs, ofs;
script_ptr++;
/*draw Platform*/
sprofs = getPuzzlSprite(3, 140 / 4, 174, &w, &h, &ofs);
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
/*draw Granite Monster*/
sprofs = getPuzzlSprite(119, 128 / 4, 94, &w, &h, &ofs);
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
promptWait();
for (; h; h--) {
waitVBlank();
waitVBlank();
cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs);
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) == 0)
ofs += CGA_BYTES_PER_LINE;
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
}
cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs);
return 0;
}
/*
Animate De Profundis hook (lower)
*/
uint16 SCR_46_DeProfundisLowerHook(void) {
byte y;
uint16 w, h;
uint16 sprofs, ofs;
script_ptr++;
/*draw Hook*/
sprofs = getPuzzlSprite(96, 140 / 4, 18, &w, &h, &ofs);
h = 1;
y = 15;
sprofs = y * 20 / 4 * 2; /*TODO: 20 is the sprite width. replace with w?*/
for (; y; y--) {
waitVBlank();
cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs);
h++;
sprofs -= 20 / 4 * 2;
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
}
return 0;
}
/*
Animate De Profundis monster (rise)
*/
uint16 SCR_47_DeProfundisRiseMonster(void) {
byte y;
uint16 w, h;
uint16 sprofs, ofs;
script_ptr++;
/*draw Granite Monster head*/
sprofs = getPuzzlSprite(118, 112 / 4, 174, &w, &h, &ofs);
h = 1;
y = 68;
for (; y; y--) {
waitVBlank();
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) != 0)
ofs -= CGA_BYTES_PER_LINE;
h++;
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
}
return 0;
}
/*
Animate De Profundis monster (lower)
*/
uint16 SCR_48_DeProfundisLowerMonster(void) {
byte y;
uint16 w, h;
uint16 sprofs, ofs;
script_ptr++;
/*draw Hook*/
sprofs = getPuzzlSprite(118, 112 / 4, 106, &w, &h, &ofs);
y = 34;
for (; y; y--) {
waitVBlank();
cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs);
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) == 0)
ofs += CGA_BYTES_PER_LINE;
h--;
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
}
return 0;
}
/*
Animate De Profundis hook (rise)
*/
uint16 SCR_49_DeProfundisRiseHook(void) {
byte y;
uint16 w, h;
uint16 sprofs, ofs;
script_ptr++;
/*draw Hook*/
sprofs = getPuzzlSprite(96, 140 / 4, 18, &w, &h, &ofs);
h = 16;
y = 15;
for (; y; y--) {
waitVBlank();
cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs);
h--;
sprofs += 20 / 4 * 2;
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
}
cga_BlitFromBackBuffer(w, 1, CGA_SCREENBUFFER, ofs);
return 0;
}
/*
Animate De Profundis platform
*/
uint16 SCR_65_DeProfundisMovePlatform(void) {
byte state;
byte x, y;
uint16 w, h;
uint16 sprofs, ofs;
script_ptr++;
state = *script_ptr++;
x = 140 / 4;
y = 174;
if (state != 0)
y += 4;
/*draw Platform*/
sprofs = getPuzzlSprite(3, x, y, &w, &h, &ofs);
y = 4;
if (state) {
h -= 4;
y--;
}
for (; y; y--) {
waitVBlank();
cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs);
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) == 0)
ofs += CGA_BYTES_PER_LINE;
h--;
cga_BlitScratchBackSprite(sprofs, w, h, CGA_SCREENBUFFER, ofs);
}
if (state)
cga_BlitFromBackBuffer(w, h, CGA_SCREENBUFFER, ofs);
return 0;
}
/*
Animate De Profundis monster ride to exit door
*/
uint16 SCR_66_DeProfundisRideToExit(void) {
uint16 w, h;
uint16 sprofs, ofs;
script_ptr++;
/*draw Granite Monster*/
sprofs = getPuzzlSprite(119, 128 / 4, 139, &w, &h, &ofs);
cga_BlitScratchBackSprite(sprofs, w, 20, backbuffer, ofs);
dot_effect_delay = 1;
dot_effect_step = 17;
copyScreenBlockWithDotEffect(backbuffer, 112 / 4, 139, 72 / 4, 40, frontbuffer);
return 0;
}
/*
Draw item bounce to room objects animation
*/
uint16 SCR_4E_BounceCurrentItemToRoom(void) {
script_ptr++;
bounceCurrentItem(ITEMFLG_ROOM, 43);
return 0;
}
/*
Draw item bounce to inventory animation
*/
uint16 SCR_4F_BounceCurrentItemToInventory(void) {
script_ptr++;
bounceCurrentItem(ITEMFLG_OWNED, 85);
return 0;
}
/*
Draw item bounce to inventory animation
*/
uint16 SCR_50_BounceItemToInventory(void) {
byte itemidx;
script_ptr++;
itemidx = *script_ptr++;
script_vars[kScrPool3_CurrentItem] = &inventory_items[itemidx - 1];
bounceCurrentItem(ITEMFLG_OWNED, 85);
return 0;
}
/*
Take away Protozorq's zapstik and bounce it to room
*/
uint16 SCR_4B_ProtoDropZapstik(void) {
pers_t *pers = (pers_t *)(script_vars[kScrPool8_CurrentPers]);
script_ptr++;
if ((pers->index & 0x38) != 0x30)
return 0;
pers->index &= ~0x18;
script_vars[kScrPool3_CurrentItem] = &inventory_items[kItemZapstik1 - 1 + (script_byte_vars.cur_pers - 1) - kPersProtozorq1];
bounceCurrentItem(ITEMFLG_ROOM, 43);
return 0;
}
/*
Take away Aspirant's item and bounce it to the inventory
*/
void LootAspirantsItem(void) {
if (aspirant_ptr->item != 0) {
item_t *item = &inventory_items[aspirant_ptr->item - 1];
aspirant_ptr->item = 0;
script_vars[kScrPool3_CurrentItem] = item;
script_byte_vars.steals_count++;
script_byte_vars.bvar_6D[aspirant_ptr->index >> 6] = item->name; /*TODO: check these index bits*/
bounceCurrentItem(ITEMFLG_OWNED, 85);
the_command = 0x90AA; /*OK*/
} else
the_command = 0x9140; /*NOTHING ON HIM*/
}
/*
Take away Aspirant's item and bounce it to the inventory
*/
uint16 SCR_2F_LootAspirantsItem() {
script_ptr++;
LootAspirantsItem();
return ScriptRerun;
}
/*
Trade with Skull Trader
*/
uint16 SCR_51_SkullTraderItemTrade(void) {
byte *old_script, *old_script_end = script_end_ptr;
byte status;
if (script_byte_vars.bvar_26 >= 63) /*TODO: hang?*/
return 0;
script_ptr++;
old_script = script_ptr;
inv_bgcolor = 0xFF;
openInventory(0xFF, ITEMFLG_OWNED);
status = 1;
if (inv_count != 0) {
status = 2;
if (the_command != 0) {
status = 3;
if (script_byte_vars.inv_item_index >= kItemRope1 && script_byte_vars.inv_item_index <= kItemLantern4) {
the_command = 0xC204; /*WHICH ONE DO YOU WANT?*/
runCommand();
((item_t *)(script_vars[kScrPool3_CurrentItem]))->flags = 0;
openInventory(0xFF, ITEMFLG_TRADER);
status = 4;
if (the_command != 0) {
/*50% chance to win the item*/
status = 5; /*lose*/
#ifdef CHEAT_TRADER
{ /*always win at the Skull Trader*/
#else
if (script_byte_vars.rand_value < 128) {
#endif
status = 6; /*win*/
((item_t *)(script_vars[kScrPool3_CurrentItem]))[-1].flags = ITEMFLG_TRADER; /*offer previous item copy for next trade*/
((item_t *)(script_vars[kScrPool3_CurrentItem]))->flags = 0; /*consume selected item*/
}
}
}
}
}
script_byte_vars.skull_trader_status = status;
script_ptr = old_script;
script_end_ptr = old_script_end;
return 0;
}
uint16 SCR_52_RefreshSpritesData(void) {
script_ptr++;
refreshSpritesData();
return 0;
}
uint16 SCR_53_FindInvItem(void) {
byte first, count, flags, i;
item_t *item;
script_ptr++;
first = *script_ptr++;
count = *script_ptr++;
flags = *script_ptr++;
item = &inventory_items[first - 1];
for (i = 0; i < count; i++) {
if (item[i].flags == flags) {
script_vars[kScrPool3_CurrentItem] = &item[i];
return 0;
}
}
script_vars[kScrPool3_CurrentItem] = &item[count - 1];
return 0;
}
/*
Restore whole room from backbuffer, with dot effect
*/
uint16 SCR_54_DotFadeRoom(void) {
script_ptr++;
dot_effect_delay = 1;
dot_effect_step = 17;
copyScreenBlockWithDotEffect(backbuffer, room_bounds_rect.sx, room_bounds_rect.sy, room_bounds_rect.ex - room_bounds_rect.sx, room_bounds_rect.ey - room_bounds_rect.sy, frontbuffer);
return 0;
}
uint16 SCR_55_DrawRoomItemsIndicator(void) {
script_ptr++;
drawRoomItemsIndicator();
return 0;
}
/*
TODO: check and rename me
*/
uint16 SCR_56_MorphRoom98(void) {
int16 h;
uint16 ofs;
script_ptr++;
IFGM_PlaySample(242);
redrawRoomStatics(98, 0);
ofs = cga_CalcXY(0, 136);
for (h = 60; h; h--) {
memcpy(frontbuffer + ofs, backbuffer + ofs, CGA_BYTES_PER_LINE);
waitVBlank();
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) != 0)
ofs -= CGA_BYTES_PER_LINE;
}
backupSpotImage(&zone_spots[3], &sprites_list[3], sprites_list[3]);
IFGM_StopSample();
return 0;
}
/*
Copy backbuffer to screen, with added vertical mirror
*/
void ShowMirrored(uint16 h, uint16 ofs) {
uint16 x, ofs2 = ofs;
/*move 1 line up*/
ofs2 ^= CGA_ODD_LINES_OFS;
if ((ofs2 & CGA_ODD_LINES_OFS) != 0)
ofs2 -= CGA_BYTES_PER_LINE;
while (h--) {
for (x = 0; x < CGA_BYTES_PER_LINE; x++) {
frontbuffer[ofs2 + x] = frontbuffer[ofs + x] = backbuffer[ofs + x];
backbuffer[ofs + x] = 0;
}
/*move 1 line down*/
ofs += CGA_BYTES_PER_LINE;
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) != 0)
ofs -= CGA_BYTES_PER_LINE;
/*move 1 line up*/
ofs2 ^= CGA_ODD_LINES_OFS;
if ((ofs2 & CGA_ODD_LINES_OFS) != 0)
ofs2 -= CGA_BYTES_PER_LINE;
}
}
void LiftLines(int16 n, byte *source, uint16 sofs, byte *target, uint16 tofs) {
while (n--) {
memcpy(target + tofs, source + sofs, CGA_BYTES_PER_LINE);
sofs += CGA_BYTES_PER_LINE;
sofs ^= CGA_ODD_LINES_OFS;
if ((sofs & CGA_ODD_LINES_OFS) != 0)
sofs -= CGA_BYTES_PER_LINE;
tofs += CGA_BYTES_PER_LINE;
tofs ^= CGA_ODD_LINES_OFS;
if ((tofs & CGA_ODD_LINES_OFS) != 0)
tofs -= CGA_BYTES_PER_LINE;
}
}
#define kSaucerAnimFrames 53
static void AnimSaucer(void) {
static byte image1[] = {167, 0, 146};
byte *pimage1 = image1;
byte *sequence = souco_data;
byte x, y, width, height;
uint16 xx, yy, ww, hh;
byte height_new, height_prev;
uint16 delay;
byte scroll_done = 0;
memset(backbuffer, 0, sizeof(backbuffer) - 2); /*TODO: original bug?*/
cga_BackBufferToRealFull();
cga_ColorSelect(0x30);
right_button = 0;
if (!drawPortrait(&pimage1, &x, &y, &width, &height))
return;
height_prev = 200 - 1;
delay = 10000;
xx = x; /*TODO: is it ok? maybe need *4*/
yy = y;
ww = 254;
hh = 107;
for (; sequence < souco_data + kSaucerAnimFrames * 8; sequence += 8) {
uint16 i, ofs, ofs2, baseofs;
if (sequence != souco_data) {
/*reuse portrait's params for initial state*/
xx = (sequence[0] << 8) | sequence[1];
yy = (sequence[2] << 8) | sequence[3];
ww = (sequence[4] << 8) | sequence[5];
hh = (sequence[6] << 8) | sequence[7];
}
hh >>= 1;
height_new = yy + hh;
height_prev -= (yy - 1);
/*scale the saucer*/
cga_ZoomInplaceXY(cur_image_pixels, width, height, ww, hh, xx, yy, backbuffer);
baseofs = cga_CalcXY(0, yy);
if (!scroll_done) {
/*scroll the saucer*/
scroll_done = 1;
ofs2 = ofs = baseofs;
/*previous line*/
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) != 0)
ofs -= CGA_BYTES_PER_LINE;
for (i = 0; i < 55; i++) {
memcpy(backbuffer + ofs, backbuffer + ofs2, CGA_BYTES_PER_LINE);
/*next line*/
ofs2 += CGA_BYTES_PER_LINE;
ofs2 ^= CGA_ODD_LINES_OFS;
if ((ofs2 & CGA_ODD_LINES_OFS) != 0)
ofs2 -= CGA_BYTES_PER_LINE;
/*previous line line*/
ofs ^= CGA_ODD_LINES_OFS;
if ((ofs & CGA_ODD_LINES_OFS) != 0)
ofs -= CGA_BYTES_PER_LINE;
}
ofs2 = cga_CalcXY(0, 200 - 1);
for (i = 0; i < 108; i++) {
LiftLines(i + 1, backbuffer, ofs, frontbuffer, ofs2);
ofs2 ^= CGA_ODD_LINES_OFS;
if ((ofs2 & CGA_ODD_LINES_OFS) != 0)
ofs2 -= CGA_BYTES_PER_LINE;
waitVBlank();
waitVBlank();
}
/*wipe 56 lines*/
memset(backbuffer + ofs2, 0, 56 / 2 * CGA_BYTES_PER_LINE);
ofs2 ^= CGA_ODD_LINES_OFS;
if ((ofs2 & CGA_ODD_LINES_OFS) == 0)
ofs2 += CGA_BYTES_PER_LINE;
memset(backbuffer + ofs2, 0, 54 / 2 * CGA_BYTES_PER_LINE);
for (i = 0xFFFF; i--;) ; /*TODO: weak delay*/
IFGM_PlaySample(240);
}
/*draw the full saucer on screen*/
ShowMirrored(height_prev + 1, baseofs);
height_prev = height_new;
waitVBlank();
for (i = delay; i--;) ; /*TODO: weak delay*/
delay += 500;
}
}
extern int16 loadSplash(const char *filename);
/*
TODO: check me
*/
void theEnd(void) {
static byte image2[] = {168, 28, 85, 22, 15};
byte *pimage2 = image2;
AnimSaucer();
if (g_vm->getLanguage() == Common::EN_USA) {
drawPortraitZoomed(&pimage2);
script_byte_vars.zone_index = 135;
do {
pollInputButtonsOnly();
}
while(buttons == 0);
while (!loadFond())
askDisk2();
jaggedZoom(backbuffer, frontbuffer);
cga_BackBufferToRealFull();
} else {
while (!loadSplash("PRES.BIN"))
askDisk2();
cga_BackBufferToRealFull();
}
}
uint16 SCR_5B_TheEnd(void) {
script_ptr++; /*Useless since this handler never returns*/
script_byte_vars.game_paused = 5;
theEnd();
if (g_vm->getLanguage() == Common::EN_USA)
restartGame();
else
for (;;) ; /*HANG*/
return 0;
}
/*
Discard all inventory items
*/
uint16 SCR_5C_ClearInventory(void) {
int16 i;
script_ptr++;
for (i = 0; i < MAX_INV_ITEMS; i++) {
if (inventory_items[i].flags == ITEMFLG_OWNED)
inventory_items[i].flags = 0;
}
script_byte_vars.zapstiks_owned = 0;
return 0;
}
/*
Drop group of items from inventory to room
*/
void DropItems(int16 first, int16 count) {
int16 i;
for (i = 0; i < count; i++) {
if (inventory_items[first + i].flags == ITEMFLG_OWNED) {
inventory_items[first + i].flags = ITEMFLG_ROOM;
inventory_items[first + i].area = script_byte_vars.zone_area;
}
}
}
/*
Drop weapon-like items from inventory to room
*/
uint16 SCR_5D_DropWeapons(void) {
script_ptr++;
DropItems(kItemDagger1 - 1, 4); /*DAGGER*/
DropItems(kItemZapstik1 - 1, 14); /*ZAPSTIK*/
DropItems(kItemBlade - 1, 2); /*SACRIFICIAL BLADE , CHOPPER*/
script_byte_vars.zapstiks_owned = 0;
return 0;
}
/*
React to Psi power
*/
uint16 SCR_62_PsiReaction(void) {
byte power;
uint16 cmd;
script_ptr++;
power = *script_ptr++;
cmd = script_word_vars.zone_obj_cmds[(script_byte_vars.cur_spot_idx - 1) * 5 + power];
if (cmd == 0)
cmd = script_word_vars.psi_cmds[power]; /*TODO: is this consistent with zone_obj_cmds?*/
the_command = Swap16(cmd);
return 0;
}
/*TODO: rename me*/
uint16 SCR_63_LiftSpot6(void) {
script_ptr++;
blitSpritesToBackBuffer();
zone_spots[6].sy -= 5;
zone_spots[6].ey -= 5;
backupSpotsImages();
drawPersons();
cga_BackBufferToRealFull();
return 0;
}
uint16 SCR_64_DrawBoxAroundSpot(void) {
script_ptr++;
drawBoxAroundSpot();
return 0;
}
/*
Draw text box
*/
uint16 SCR_14_DrawDesc(void) {
byte *msg;
script_ptr++;
msg = seekToStringScr(desci_data, *script_ptr, &script_ptr);
script_ptr++;
drawMessage(msg, CGA_SCREENBUFFER);
return 0;
}
/*
Draw dialog bubble with text for a person, wait for a key, then hide. Auto find bubble location
Use "thought" spike
*/
uint16 SCR_17_DrawPersonThoughtBubbleDialog(void) {
byte x, y;
byte *msg;
script_ptr++;
msg = seekToStringScr(diali_data, *script_ptr, &script_ptr);
script_ptr++;
x = found_spot->sx;
y = found_spot->sy;
if (x < 140 / 4)
drawPersonBubble(found_spot->ex, y - 40, SPIKE_BUBLEFT | 20, msg);
else
drawPersonBubble(x - 80 / 4, y - 40, SPIKE_BUBRIGHT | 20, msg);
promptWait();
popDirtyRects(DirtyRectBubble);
return 0;
}
/*
Draw dialog bubble with text for a person, wait for a key, then hide. Auto find bubble location
Use normal spike
*/
uint16 SCR_61_drawPersonBubbleDialog(void) {
byte x, y;
byte *msg;
script_ptr++;
msg = seekToStringScr(diali_data, *script_ptr, &script_ptr);
script_ptr++;
x = found_spot->sx;
y = found_spot->sy;
if (x < 140 / 4)
drawPersonBubble(found_spot->ex, y - 40, SPIKE_DNLEFT | 20, msg);
else
drawPersonBubble(x - 80 / 4, y - 40, SPIKE_DNRIGHT | 20, msg);
promptWait();
popDirtyRects(DirtyRectBubble);
return 0;
}
#if 1
byte *DebugString(char *msg, ...) {
int16 i;
byte c;
static byte m[256];
va_list ap;
va_start(ap, msg);
vsnprintf((char *)m, 256, msg, ap);
va_end(ap);
for (i = 0; m[i]; i++) {
c = m[i];
if (c >= 'A' && c <= 'Z')
c = 0x21 + (c - 'A');
else if (c >= 'a' && c <= 'z')
c = 0x21 + (c - 'a');
else if (c >= '0' && c <= '9')
c = 0x10 + (c - '0');
else if (c == ' ')
c = 0x20;
else if (c == '!')
c = 0x01;
else if (c == ',')
c = 0x0C;
else if (c == '.')
c = 0x0E;
else if (c == '\n')
c = 0x00;
else
c = 0x1F;
m[i] = c;
}
cur_str_end = m + i;
return m;
}
#endif
/*
Draw dialog bubble with text for gauss
*/
uint16 SCR_27_DrawGaussBubble(void) {
byte *msg;
script_ptr++;
msg = seekToStringScr(diali_data, *script_ptr, &script_ptr);
script_ptr++;
drawPersonBubble(32 / 4, 20, 15, msg);
return 0;
}
/*
Draw dialog bubble with text
*/
uint16 SCR_29_DialiTextBox(void) {
byte x, y, f;
byte *msg;
script_ptr++;
msg = seekToStringScr(diali_data, *script_ptr, &script_ptr);
cur_dlg_index = cur_str_index; /*TODO: useless?*/
script_ptr++;
x = *script_ptr++;
y = *script_ptr++;
f = *script_ptr++;
drawPersonBubble(x, y, f, msg);
return 0;
}
/*
Do nothing in PC/CGA version
*/
uint16 SCR_F_Unused(void) {
script_ptr++;
script_ptr++;
script_ptr++;
script_ptr++;
return 0;
}
/*
Do nothing in PC/CGA version
*/
uint16 SCR_4A_Unused(void) {
script_ptr++;
return 0;
}
/*
Do nothing in PC/CGA version
*/
uint16 SCR_67_Unused(void) {
script_ptr++;
script_ptr++;
return 0;
}
/*
Play Sfx
NB! Do nothing in EU PC/CGA version
*/
uint16 SCR_68_PlaySfx(void) {
byte index;
script_ptr++;
index = *script_ptr++;
script_ptr++;
IFGM_PlaySfx(index);
return 0;
}
/*
Play sound
*/
uint16 SCR_69_playSound(void) {
byte index;
script_ptr++;
index = *script_ptr++;
script_ptr++;
playSound(index);
return 0;
}
/*
Do nothing in PC/CGA version
*/
uint16 SCR_6A_Unused(void) {
script_ptr++;
return 0;
}
/*
Open room's items inventory
*/
uint16 CMD_1_RoomObjects(void) {
updateUndrawCursor(CGA_SCREENBUFFER);
inv_bgcolor = 0xAA;
openInventory((0xFF << 8) | ITEMFLG_ROOM, (script_byte_vars.zone_area << 8) | ITEMFLG_ROOM);
return ScriptRerun;
}
/*
Open Psi Powers menu
*/
uint16 CMD_2_PsiPowers(void) {
/*Psi powers bar*/
backupAndShowSprite(3, 280 / 4, 40);
processInput();
do {
pollInput();
selectCursor(CURSOR_FINGER);
checkPsiCommandHover();
if (command_hint != 100)
command_hint += 109;
if (command_hint != last_command_hint)
drawCommandHint();
drawHintsAndCursor(CGA_SCREENBUFFER);
} while (buttons == 0);
undrawCursor(CGA_SCREENBUFFER);
cga_RestoreBackupImage(CGA_SCREENBUFFER);
return ScriptRerun;
}
/*
Open normal inventory box
*/
uint16 CMD_3_Posessions(void) {
updateUndrawCursor(CGA_SCREENBUFFER);
inv_bgcolor = 0x55;
openInventory(ITEMFLG_OWNED, ITEMFLG_OWNED);
return ScriptRerun;
}
/*
Show energy level
*/
uint16 CMD_4_EnergyLevel(void) {
static byte energy_image[] = {130, 236 / 4, 71};
byte x, y, width, height;
byte *image = energy_image;
byte anim = 40;
popDirtyRects(DirtyRectSprite);
popDirtyRects(DirtyRectBubble);
cur_dlg_index = 0;
ifgm_flag2 = 0xff;
if (script_byte_vars.psy_energy != 0)
anim = 41 + (script_byte_vars.psy_energy / 16);
if (drawPortrait(&image, &x, &y, &width, &height)) {
cga_BlitAndWait(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs);
}
do {
IFGM_PlaySample(28);
animPortrait(1, anim, 10);
animPortrait(1, anim + 14, 10);
pollInputButtonsOnly();
} while (buttons == 0);
popDirtyRects(DirtyRectSprite);
ifgm_flag2 = 0;
IFGM_StopSample();
return 0;
}
/*
Advance time
*/
uint16 CMD_5_Wait(void) {
script_byte_vars.bvar_25++;
script_word_vars.timer_ticks2 = Swap16(Swap16(script_word_vars.timer_ticks2) + 300);
the_command = next_vorts_cmd;
runCommand();
the_command = next_turkey_cmd;
runCommand();
script_byte_vars.used_commands = script_byte_vars.check_used_commands;
the_command = Swap16(script_word_vars.wvar_0E);
if (the_command == 0) {
if (script_word_vars.next_aspirant_cmd == 0) {
the_command = 0x9005;
runCommand();
}
} else {
if (script_byte_vars.bvar_26 >= 63 && script_byte_vars.zone_area < 22 && script_byte_vars.zone_area != 1)
the_command = 0x9005;
return ScriptRerun;
}
return 0;
}
/*
Load game (menu)
*/
uint16 CMD_6_Load(void) {
the_command = 0xC35C;
return ScriptRerun;
}
/*
Save game (menu)
*/
uint16 CMD_7_Save(void) {
the_command = 0xC35D;
return ScriptRerun;
}
/*
Show timer
*/
uint16 CMD_8_Timer(void) {
static byte timer_image[] = {163, 244 / 4, 104};
byte x, y, width, height;
byte *image = timer_image;
if (drawPortrait(&image, &x, &y, &width, &height)) {
cga_BlitAndWait(cur_image_pixels, cur_image_size_w, cur_image_size_w, cur_image_size_h, CGA_SCREENBUFFER, cur_image_offs);
}
do {
uint16 timer = Swap16(script_word_vars.timer_ticks2);
uint16 minutes = timer % (60 * 60);
char_draw_coords_x = 260 / 4;
char_draw_coords_y = 120;
waitVBlank();
cga_PrintChar(timer / (60 * 60) + 16, CGA_SCREENBUFFER);
cga_PrintChar((minutes & 1) ? 26 : 0, CGA_SCREENBUFFER); /*colon*/
cga_PrintChar(minutes / (60 * 10) + 16, CGA_SCREENBUFFER);
cga_PrintChar(minutes / 60 + 16, CGA_SCREENBUFFER);
pollInputButtonsOnly();
} while (buttons == 0);
popDirtyRects(DirtyRectSprite);
return 0;
}
int16 ConsumePsiEnergy(byte amount) {
byte current = script_byte_vars.psy_energy;
if (current < amount) {
/*no energy left*/
playAnim(68, 296 / 4, 71);
return 0;
}
script_byte_vars.psy_energy = current - amount;
/*significantly changed?*/
if ((current & 0xF0) != (script_byte_vars.psy_energy & 0xF0))
playAnim(68, 296 / 4, 71);
return 1;
}
uint16 CMD_A_PsiSolarEyes(void) {
if (!ConsumePsiEnergy(2))
return 0;
if (zone_palette == 14) {
redrawRoomStatics(script_byte_vars.zone_room, zone_palette);
zone_palette = 0;
cga_BackBufferToRealFull();
}
the_command = Swap16(script_word_vars.wvar_AA);
runCommand();
script_byte_vars.cur_spot_flags = 0xFF;
return 0;
}
uint16 GetZoneObjCommand(uint16 offs) {
/*TODO: fix me: change offs/2 to index*/
the_command = Swap16(script_word_vars.zone_obj_cmds[(script_byte_vars.cur_spot_idx - 1) * 5 + offs / 2]);
return the_command;
}
void DrawStickyNet(void) {
byte x, y, w, h;
uint16 ofs;
byte *sprite = loadPuzzlToScratch(80);
x = room_bounds_rect.sx;
y = room_bounds_rect.sy;
w = room_bounds_rect.ex - x;
h = room_bounds_rect.ey - y;
ofs = cga_CalcXY_p(x, y);
/*16x30 is the net sprite size*/
for (; h; h -= 30) {
int16 i;
for (i = 0; i < w; i += 16 / 4)
drawSprite(sprite, frontbuffer, ofs + i);
ofs += CGA_BYTES_PER_LINE * 30 / 2;
}
}
uint16 CMD_B_PsiStickyFingers(void) {
if (!ConsumePsiEnergy(3))
return 0;
if (script_byte_vars.bvar_43 != 0) {
the_command = Swap16(script_word_vars.wvar_AC);
return ScriptRerun;
}
backupScreenOfSpecialRoom();
DrawStickyNet();
selectCursor(CURSOR_FLY);
menuLoop(0, 0);
playSound(224);
cga_BackBufferToRealFull();
restoreScreenOfSpecialRoom();
if (script_byte_vars.cur_spot_idx == 0 || GetZoneObjCommand(0 * 2) == 0)
the_command = Swap16(script_word_vars.psi_cmds[0]);
if (script_byte_vars.bvar_26 >= 63
&& script_byte_vars.zone_area < 22
&& script_byte_vars.zone_area != 1)
the_command = 0x9005;
return ScriptRerun;
}
uint16 CMD_C_PsiKnowMind(void) {
if (!ConsumePsiEnergy(1))
return 0;
if (script_byte_vars.bvar_43 != 0) {
the_command = Swap16(script_word_vars.wvar_AE);
return ScriptRerun;
}
processMenu();
if (script_byte_vars.cur_spot_idx == 0 || GetZoneObjCommand(2 * 2) == 0)
the_command = Swap16(script_word_vars.psi_cmds[1]);
return ScriptRerun;
}
uint16 CMD_D_PsiBrainwarp(void) {
if (!ConsumePsiEnergy(2))
return 0;
if (script_byte_vars.bvar_43 == 0) {
backupScreenOfSpecialRoom();
processMenu();
if (script_byte_vars.cur_spot_idx == 0) {
the_command = Swap16(script_word_vars.wvar_0C);
script_byte_vars.dead_flag = 0;
return ScriptRerun;
}
if (GetZoneObjCommand(1 * 2) != 0) {
playAnim(39, found_spot->sx + 8 / 4, found_spot->sy - 10);
restoreScreenOfSpecialRoom();
return ScriptRerun;
}
}
if (script_byte_vars.bvar_43 == 18) {
script_byte_vars.dead_flag = 1;
script_byte_vars.tries_left = 2;
return 0;
}
((pers_t *)script_vars[kScrPool8_CurrentPers])->flags |= PERSFLG_80;
script_byte_vars.dead_flag = script_byte_vars.cur_spot_idx;
script_byte_vars.tries_left = 2;
the_command = 0;
if (script_byte_vars.bvar_43 == 0) {
playAnim(39, found_spot->sx + 8 / 4, found_spot->sy - 10);
restoreScreenOfSpecialRoom();
return ScriptRerun;
}
the_command = 0x90AA;
return ScriptRerun;
}
uint16 CMD_E_PsiZoneScan(void) {
byte x, y, w, h;
uint16 offs;
if (!ConsumePsiEnergy(1))
return 0;
if (script_byte_vars.bvar_43 != 0) {
the_command = Swap16(script_word_vars.wvar_B8);
return ScriptRerun;
}
backupScreenOfSpecialRoom();
IFGM_PlaySample(26);
offs = cga_CalcXY_p(room_bounds_rect.sx, room_bounds_rect.sy);
w = room_bounds_rect.ex - room_bounds_rect.sx;
h = room_bounds_rect.ey - room_bounds_rect.sy;
for (y = room_bounds_rect.sy; h; y++, h--) {
spot_t *spot;
for (x = 0; x < w; x++) frontbuffer[offs + x] = ~frontbuffer[offs + x];
cga_blitToScreen(offs, w, 1);
waitVBlank();
for (x = 0; x < w; x++) frontbuffer[offs + x] = ~frontbuffer[offs + x];
cga_blitToScreen(offs, w, 1);
for (spot = zone_spots; spot != zone_spots_end; spot++) {
if ((spot->flags & ~(SPOTFLG_40 | 7)) == (SPOTFLG_20 | SPOTFLG_8) && spot->sy == y) {
playSound(27);
spot->flags |= SPOTFLG_80;
playAnim(38, spot->sx, spot->sy);
break;
}
}
offs ^= CGA_ODD_LINES_OFS;
if ((offs & CGA_ODD_LINES_OFS) == 0)
offs += CGA_BYTES_PER_LINE;
}
restoreScreenOfSpecialRoom();
IFGM_StopSample();
the_command = Swap16(script_word_vars.psi_cmds[2]);
return ScriptRerun;
}
uint16 CMD_F_PsiPsiShift(void) {
if (!ConsumePsiEnergy(3))
return 0;
if (script_byte_vars.bvar_43 != 0) {
the_command = Swap16(script_word_vars.wvar_B0);
return ScriptRerun;
}
selectCursor(CURSOR_GRAB);
menuLoop(0, 0);
backupScreenOfSpecialRoom();
playSound(25);
playAnim(39, cursor_x / 4, cursor_y);
restoreScreenOfSpecialRoom();
if (script_byte_vars.cur_spot_idx == 0 || GetZoneObjCommand(3 * 2) == 0)
the_command = Swap16(script_word_vars.psi_cmds[5]);
return ScriptRerun;
}
uint16 CMD_10_PsiExtremeViolence(void) {
uint16 command;
if (!ConsumePsiEnergy(8))
return 0;
script_byte_vars.extreme_violence = 1;
if (script_byte_vars.bvar_43 != 0) {
the_command = Swap16(script_word_vars.wvar_B2);
return ScriptRerun;
}
processMenu();
if (script_byte_vars.cur_spot_idx == 0) {
the_command = Swap16(script_word_vars.psi_cmds[4]);
script_byte_vars.extreme_violence = 0;
return ScriptRerun;
}
command = GetZoneObjCommand(4 * 2);
if ((command & 0xF000) == 0x9000)
script_byte_vars.extreme_violence = 0;
else if (command == 0) {
the_command = Swap16(script_word_vars.psi_cmds[4]);
script_byte_vars.extreme_violence = 0;
}
return ScriptRerun;
}
uint16 CMD_11_PsiTuneIn(void) {
uint16 command;
byte *msg;
if (!ConsumePsiEnergy(4))
return 0;
if (script_byte_vars.bvar_43 != 0)
command = Swap16(script_word_vars.wvar_B4);
else {
if (script_byte_vars.bvar_26 < 63 || script_byte_vars.zone_area >= 22)
command = Swap16(script_word_vars.psi_cmds[3]);
else
command = 275;
}
/*TODO: is this really neccessary? Maybe it's always set when loaded from script vars?*/
if (command & 0x8000) {
the_command = command;
return ScriptRerun;
}
msg = seekToString(diali_data, command);
cur_dlg_index = cur_str_index; /*TODO: useless?*/
drawPersonBubble(32 / 4, 20, 15, msg);
promptWait();
popDirtyRects(DirtyRectBubble);
return 0;
}
void ActionForPersonChoice(uint16 *actions) {
processMenu();
the_command = 0x9183; /*THERE`S NOBODY.*/
if (script_byte_vars.cur_spot_idx != 0 && script_byte_vars.cur_pers != 0) {
pers_t *pers = (pers_t *)script_vars[kScrPool8_CurrentPers];
byte index = pers->name;
if (index == 93) /*CADAVER*/
index = 19 + 42;
else if (index == 133) /*SCI FI*/
index = 18 + 42;
index -= 42; /*Person names: THE MASTER OF ORDEALS, etc*/
the_command = actions[index];
playSound(22);
}
}
/*TODO: ensure these are never accessed/modified from the scripts*/
uint16 menu_commands_12[] = {
0xC0F0,
0xC0D7,
0x9019,
0xC0DA,
0x9019,
0xC0F1,
0x9007,
0xC0D8,
0x9007,
0x9019,
0x9007,
0x9007,
0xC34E,
0xC34F,
0x9019,
0x9019,
0x9019,
0x9019,
0xC319,
0x9007
};
uint16 menu_commands_22[] = {
0xC325,
0xC326,
0xC31B,
0xC31D,
0xC31F,
0xC31E,
0xC320,
0xC327,
0x90EC,
0xC31C,
0xC328,
0,
0xC077,
0xC077,
0xC321,
0xC322,
0x9007,
0xC323,
0xC2A7,
0xC324
};
uint16 menu_commands_24[] = {
0xC344,
0xC34A,
0xC343,
0xC34D,
0x9019,
0xC0F1,
0x9007,
0xC354,
0x90EC,
0xC343,
0xC345,
0,
0xC343,
0xC343,
0xC343,
0x9019,
0x9019,
0x9019,
0xC343,
0xC343
};
uint16 menu_commands_23[] = {
0xC002,
0xC32A,
0x9019,
0xC325,
0xC114,
0xC32B,
0xC32C,
0xC355,
0x90EC,
0x9019,
0xC328,
0,
0xC077,
0xC077,
0x9019,
0x9113,
0,
0xC323,
0xC2A7,
0xC32D
};
uint16 CMD_12_(void) {
warning("cmd 12");
ActionForPersonChoice(menu_commands_12);
return ScriptRerun;
}
uint16 CMD_13_ActivateFountain(void) {
static byte water1[] = {125, 156 / 4, 58};
static byte water2[] = {126, 156 / 4, 58};
static byte headl[] = {88, 152 / 4, 52};
static byte headr[] = {88, (160 / 4) | 0x80, 52};
byte x, y, w, h;
uint16 i, j;
script_byte_vars.bvar_6A = 1;
for (i = 0; i < 10; i++) {
drawRoomStaticObject(water1, &x, &y, &w, &h);
waitVBlank();
cga_BackBufferToRealFull();
for (j = 0; j < 0x1FFF; j++) ; /*TODO: weak delay*/
drawRoomStaticObject(water2, &x, &y, &w, &h);
waitVBlank();
cga_BackBufferToRealFull();
for (j = 0; j < 0x1FFF; j++) ; /*TODO: weak delay*/
}
drawRoomStaticObject(headl, &x, &y, &w, &h);
drawRoomStaticObject(headr, &x, &y, &w, &h);
cga_BackBufferToRealFull();
return 0;
}
/*
Vorts walking into the room
*/
uint16 CMD_14_VortAppear(void) {
/*TODO: check me*/
pers_list[kPersVort].area = script_byte_vars.zone_area;
selectPerson(0);
animateSpot(&vortanims_ptr->field_1);
IFGM_StopSample();
next_vorts_cmd = 0xA015;
blitSpritesToBackBuffer();
drawPersons();
cga_BackBufferToRealFull();
next_vorts_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
return 0;
}
pers_t *vort_ptr;
#define ADJACENT_AREAS_MAX 19
struct {
byte zone; /* current zone */
byte area; /* area accessible from this zone */
} adjacent_areas[ADJACENT_AREAS_MAX] = {
{ 2, 5},
{ 3, 8},
{ 4, 8},
{ 5, 2},
{120, 3},
{121, 3},
{ 8, 10},
{ 10, 8},
{ 60, 62},
{ 62, 66},
{ 68, 66},
{ 69, 66},
{ 67, 65},
{ 65, 66},
{ 70, 71},
{ 71, 70},
{ 59, 60},
{ 60, 62},
{ 63, 65}
};
/*
Vorts walking out of the room
*/
uint16 CMD_15_VortLeave(void) {
/*TODO: check me*/
uint16 i;
animdesc_t *anim;
pers_t *pers;
if (pers_list[kPersVort].area != 0) {
pers = &pers_list[kPersVort];
anim = &vortanims_ptr->field_4;
} else if (pers_list[kPersVort2].area != 0) {
pers = &pers_list[kPersVort2];
anim = &vortanims_ptr->field_7;
} else {
script_byte_vars.bvar_36 |= 0x80;
pers_list[kPersVort3].area = 0;
pers_list[kPersVort].flags = pers_list[kPersVort3].flags;
pers = &pers_list[kPersVort];
anim = &vortanims_ptr->field_A;
}
pers->area = 0;
next_vorts_cmd = 0;
for (i = 0; i < ADJACENT_AREAS_MAX; i++) {
if (adjacent_areas[i].zone == script_byte_vars.zone_index) {
next_vorts_cmd = 0xA016;
next_vorts_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
pers->area = adjacent_areas[i].area;
}
}
vort_ptr = pers;
zone_spots[(pers->flags & 15) - 1].flags &= ~SPOTFLG_80;
selectPerson(0);
animateSpot(anim);
IFGM_StopSample();
script_byte_vars.bvar_36 &= 0x80;
return 0;
}
/*
Vorts left the room
*/
uint16 CMD_16_VortGone(void) {
vort_ptr->area = 0;
next_vorts_cmd = 0;
return 0;
}
/*
Take away Aspirant's item and bounce it to the inventory
*/
uint16 CMD_17_LootAspirantsItem() {
LootAspirantsItem();
return ScriptRerun;
}
/*
Aspirant walking out of the room
*/
uint16 CMD_18_AspirantLeave(void) {
/*TODO: check me*/
static const animdesc_t anim33 = {ANIMFLG_USESPOT | 33, { { 0, 0 } }};
popDirtyRects(DirtyRectSprite);
popDirtyRects(DirtyRectText);
aspirant_ptr->area = 0;
script_word_vars.next_aspirant_cmd = BE(0);
if ((aspirant_ptr->flags & PERSFLG_40) == 0) {
aspirant_spot->flags &= ~SPOTFLG_80;
selectPerson(script_byte_vars.aspirant_pers_ofs);
script_byte_vars.aspirant_flags = 0;
animateSpot(&anim33);
}
return 0;
}
/*
Aspirant walking into the room
*/
uint16 CMD_19_AspirantAppear(void) {
/*TODO: check me*/
static const animdesc_t anim23 = {ANIMFLG_USESPOT | 23, { { 0, 0 } }};
popDirtyRects(DirtyRectSprite);
aspirant_ptr->area = script_byte_vars.zone_area;
script_word_vars.next_aspirant_cmd = BE(0xA018); /*leave*/
script_byte_vars.check_used_commands = 3;
script_byte_vars.used_commands = 0;
selectPerson(script_byte_vars.aspirant_pers_ofs);
animateSpot(&anim23);
blitSpritesToBackBuffer();
drawPersons();
cga_BackBufferToRealFull();
if (script_byte_vars.aspirant_flags == 5) {
the_command = 0xC029;
script_byte_vars.aspirant_flags = 0;
return ScriptRerun;
}
if (script_byte_vars.aspirant_flags == 6) {
the_command = 0xC165;
script_byte_vars.aspirant_flags = 0;
return ScriptRerun;
}
return 0;
}
/*
Aspirant is dead
*/
uint16 CMD_1A_AspirantDie(void) {
/*TODO: check me, unused in game?*/
script_byte_vars.bvar_45 = 0;
zone_spots[5].flags = SPOTFLG_40 | SPOTFLG_10 | SPOTFLG_2 | SPOTFLG_1;
script_word_vars.next_aspirant_cmd = 0;
DrawDeathAnim();
return 0;
}
/*
Show Holo screen anim and speech
*/
uint16 CMD_1B_Holo(void) {
byte x, y;
uint16 num;
byte *msg;
IFGM_PlaySample(225);
x = found_spot->sx;
y = found_spot->sy;
playAnim(42, x + 4 / 4, y + 6);
num = 321 + ((Swap16(script_word_vars.timer_ticks2) < 60 * 60) ? 0 : 4) + (script_byte_vars.rand_value % 4);
msg = seekToString(diali_data, num);
cur_dlg_index = cur_str_index; /*TODO: useless?*/
if (x < 140 / 4)
drawPersonBubble(x + 32 / 4, y - 40, SPIKE_DNLEFT | 20, msg);
else
drawPersonBubble(x - 92 / 4, y - 40, SPIKE_DNRIGHT | 20, msg);
IFGM_PlaySfx(0);
playAnim(43, x, y);
promptWait();
popDirtyRects(DirtyRectBubble);
IFGM_PlaySample(225);
playAnim(45, x, y);
return 0;
}
/*
Turkey walking into the room
*/
uint16 CMD_1E_TurkeyAppear(void) {
/*TODO: check me*/
pers_list[kPersTurkey].area = script_byte_vars.zone_area;
selectPerson(PersonOffset(kPersTurkey));
animateSpot(&turkeyanims_ptr->field_1);
next_turkey_cmd = 0xA01F;
blitSpritesToBackBuffer();
drawPersons();
cga_BackBufferToRealFull();
next_turkey_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
return 0;
}
/*
Turkey leaving the room
*/
uint16 CMD_1F_TurkeyLeave(void) {
uint16 i;
animdesc_t *anim;
pers_t *pers;
pers = &pers_list[kPersTurkey];
anim = &turkeyanims_ptr->field_4;
pers->area = 0;
next_turkey_cmd = 0;
for (i = 0; i < ADJACENT_AREAS_MAX; i++) {
if (adjacent_areas[i].zone == script_byte_vars.zone_index) {
next_turkey_cmd = 0xA020;
next_turkey_ticks = Swap16(script_word_vars.timer_ticks2) + 5;
pers->area = adjacent_areas[i].area;
}
}
zone_spots[(pers->flags & 15) - 1].flags &= ~SPOTFLG_80;
selectPerson(PersonOffset(kPersTurkey));
animateSpot(anim);
return 0;
}
/*
Turkey left the room
*/
uint16 CMD_20_TurkeyGone(void) {
pers_list[kPersTurkey].area = 0;
next_turkey_cmd = 0;
return 0;
}
/*
Talk to Vorts
*/
uint16 CMD_21_VortTalk(void) {
byte x, y;
uint16 num;
byte *msg;
if (script_byte_vars.rand_value >= 85)
num = 6;
else if (script_byte_vars.rand_value >= 170)
num = 7;
else
num = 35;
msg = seekToString(diali_data, num);
cur_dlg_index = cur_str_index; /*TODO: useless?*/
x = found_spot->sx;
y = found_spot->sy;
if (x < 140 / 4)
drawPersonBubble(found_spot->ex, y - 40, SPIKE_DNLEFT | 20, msg);
else
drawPersonBubble(x - 80 / 4, y - 40, SPIKE_DNRIGHT | 20, msg);
IFGM_PlaySfx(0);
promptWait();
popDirtyRects(DirtyRectBubble);
return 0;
}
uint16 CMD_22_(void) {
ActionForPersonChoice(menu_commands_22);
return ScriptRerun;
}
uint16 CMD_23_(void) {
ActionForPersonChoice(menu_commands_23);
return ScriptRerun;
}
uint16 CMD_24_(void) {
ActionForPersonChoice(menu_commands_24);
return ScriptRerun;
}
/*
Load save file
*/
uint16 CMD_25_LoadGame(void) {
IFGM_StopSample();
if (loadScena())
the_command = 0x918F; /*error loading*/
else
the_command = 0x90AA;
return ScriptRerun;
}
/*
Write save file
*/
uint16 CMD_26_SaveGame(void) {
if (saveScena())
the_command = 0x9190; /*error saving*/
else
the_command = 0x90AA;
return ScriptRerun;
}
typedef uint16 (*cmdhandler_t)(void);
cmdhandler_t command_handlers[] = {
0,
CMD_1_RoomObjects,
CMD_2_PsiPowers,
CMD_3_Posessions,
CMD_4_EnergyLevel,
CMD_5_Wait,
CMD_6_Load,
CMD_7_Save,
CMD_8_Timer,
CMD_TRAP,
CMD_A_PsiSolarEyes,
CMD_B_PsiStickyFingers,
CMD_C_PsiKnowMind,
CMD_D_PsiBrainwarp,
CMD_E_PsiZoneScan,
CMD_F_PsiPsiShift,
CMD_10_PsiExtremeViolence, /*10*/
CMD_11_PsiTuneIn,
CMD_12_,
CMD_13_ActivateFountain,
CMD_14_VortAppear,
CMD_15_VortLeave,
CMD_16_VortGone,
CMD_17_LootAspirantsItem,
CMD_18_AspirantLeave,
CMD_19_AspirantAppear,
CMD_1A_AspirantDie,
CMD_1B_Holo,
CMD_TRAP,
CMD_TRAP,
CMD_1E_TurkeyAppear,
CMD_1F_TurkeyLeave,
CMD_20_TurkeyGone, /*20*/
CMD_21_VortTalk,
CMD_22_,
CMD_23_,
CMD_24_,
CMD_25_LoadGame,
CMD_26_SaveGame
};
#define MAX_CMD_HANDLERS (sizeof(command_handlers) / sizeof(command_handlers[0]))
cmdhandler_t script_handlers[] = {
0,
SCR_1_AspirantItemTrade,
SCR_2_RudeAspirantTrade,
SCR_3_DrawItemBox,
SCR_4_StealZapstik,
SCR_5_DrawPortraitLiftRight,
SCR_6_DrawPortraitLiftLeft,
SCR_7_DrawPortraitLiftDown,
SCR_8_DrawPortraitLiftUp,
SCR_9_DrawPortrait,
SCR_A_DrawPortrait, /*TODO: same as SCR_9_DrawPortrait , unused*/
SCR_B_DrawPortraitTwistEffect,
SCR_C_DrawPortraitArcEffect,
SCR_D_DrawPortraitDotEffect,
SCR_E_DrawPortraitZoomIn,
SCR_F_Unused,
SCR_10_DrawPortraitZoomed, /*10*/
SCR_11_DrawRoomObject,
SCR_12_Chain,
SCR_13_RedrawRoomStatics,
SCR_14_DrawDesc,
SCR_15_SelectSpot,
SCR_16_DrawDeathAnim,
SCR_17_DrawPersonThoughtBubbleDialog,
SCR_18_AnimPortrait,
SCR_19_HidePortraitLiftLeft,
SCR_1A_HidePortraitLiftRight,
SCR_1B_HidePortraitLiftUp,
SCR_1C_HidePortraitLiftDown,
SCR_1D_HidePortrait, /*TODO: same as SCR_23_HidePortrait , unused*/
SCR_1E_HidePortraitTwist,
SCR_1F_HidePortraitArc,
SCR_20_HidePortraitDots, /*20*/
SCR_21_HidePortrait, /*TODO: same as SCR_23_HidePortrait , unused*/
SCR_22_HidePortraitShatter,
SCR_23_HidePortrait,
SCR_24_PopAllPortraits,
SCR_25_ChangeZoneOnly,
SCR_26_GameOver,
SCR_27_DrawGaussBubble,
SCR_28_MenuLoop,
SCR_29_DialiTextBox,
SCR_2A_PopDialogRect,
SCR_2B_PopAllBubbles,
SCR_2C_Wait4,
SCR_2D_Wait,
SCR_2E_promptWait,
SCR_2F_LootAspirantsItem,
SCR_30_Fight, /*30*/
SCR_31_Fight2,
SCR_32_FightWin,
SCR_33_Jump,
SCR_34_Call,
SCR_35_Ret,
SCR_36_ChangeZone,
SCR_37_desciTextBox,
SCR_38_PlayAnim,
SCR_39_AnimRoomDoorOpen,
SCR_3A_AnimRoomDoorClose,
SCR_3B_MathExpr,
SCR_3C_CondExpr,
SCR_3D_ActionsMenu,
SCR_3E_TheWallAdvance,
SCR_3F_HidePortrait, /*TODO: same as SCR_23_HidePortrait , unused*/
SCR_40_PopAllTextBoxes, /*40*/
SCR_41_LiftHand,
SCR_42_LoadZone,
SCR_43_RefreshZone,
SCR_44_BackBufferToScreen,
SCR_45_DeProfundisRoomEntry,
SCR_46_DeProfundisLowerHook,
SCR_47_DeProfundisRiseMonster,
SCR_48_DeProfundisLowerMonster,
SCR_49_DeProfundisRiseHook,
SCR_4A_Unused,
SCR_4B_ProtoDropZapstik,
SCR_4C_DrawPersons,
SCR_4D_PriorityCommand,
SCR_4E_BounceCurrentItemToRoom,
SCR_4F_BounceCurrentItemToInventory,
SCR_50_BounceItemToInventory, /*50*/
SCR_51_SkullTraderItemTrade,
SCR_52_RefreshSpritesData,
SCR_53_FindInvItem,
SCR_54_DotFadeRoom,
SCR_55_DrawRoomItemsIndicator,
SCR_56_MorphRoom98,
SCR_57_ShowCharacterSprite,
SCR_58_DrawCharacterSprite,
SCR_59_blitSpritesToBackBuffer,
SCR_5A_SelectPalette,
SCR_5B_TheEnd,
SCR_5C_ClearInventory,
SCR_5D_DropWeapons,
SCR_5E_SelectTempPalette,
SCR_5F_DrawRoomObjectBack,
SCR_60_ReviveCadaver, /*60*/
SCR_61_drawPersonBubbleDialog,
SCR_62_PsiReaction,
SCR_63_LiftSpot6,
SCR_64_DrawBoxAroundSpot,
SCR_65_DeProfundisMovePlatform,
SCR_66_DeProfundisRideToExit,
SCR_67_Unused,
SCR_68_PlaySfx,
SCR_69_playSound,
SCR_6A_Unused,
};
#define MAX_SCR_HANDLERS (sizeof(script_handlers) / sizeof(script_handlers[0]))
#ifdef DEBUG_SCRIPT
int16 runscr_reentr = 0;
int16 runcmd_reentr = 0;
#endif
/*
Run script routine
*/
uint16 RunScript(byte *code) {
uint16 status = ScriptContinue;
#ifdef DEBUG_SCRIPT
runscr_reentr += 1;
#endif
script_ptr = code;
while (script_ptr != script_end_ptr) {
byte opcode = *script_ptr;
#ifdef DEBUG_SCRIPT
{
FILE *f = fopen(DEBUG_SCRIPT_LOG, "at");
if (f) {
uint16 offs = (script_ptr - templ_data) & 0xFFFF;
fprintf(f, "%04X: %02X\n", offs, opcode);
fclose(f);
}
}
#endif
#ifdef DEBUG_QUEST
if (script_ptr - templ_data == 0x4F) {
/*manipulate rand_value to get a quest item we need*/
script_byte_vars.rand_value = DEBUG_QUEST;
}
#endif
if (opcode == 0 || opcode >= 107)
break;
status = script_handlers[opcode]();
if (g_vm->_shouldRestart)
return status;
if (g_vm->_prioritycommand_1)
return status;
if (status != ScriptContinue || g_vm->_shouldQuit)
break;
}
#ifdef DEBUG_SCRIPT
runscr_reentr -= 1;
#endif
return status;
}
/*
Get script routine
*/
byte *getScriptSubroutine(uint16 index) {
return seekToEntry(templ_data, index, &script_end_ptr);
}
/*
Run script command
*/
uint16 runCommand(void) {
uint16 res;
uint16 cmd;
again:;
res = 0;
if (the_command == 0)
return 0;
#ifdef DEBUG_SCRIPT
{
FILE *f = fopen(DEBUG_SCRIPT_LOG, "at");
if (f) {
fprintf(f, "\nrunCommand 0x%04X rc: %d rs: %d\n", the_command, runcmd_reentr, runscr_reentr);
fclose(f);
}
}
runcmd_reentr += 1;
#endif
cmd = the_command & 0x3FF;
switch (the_command & 0xF000) {
case 0: /*TODO what kind of call is this?*/
res = RunScript(templ_data + the_command);
break;
case 0x9000:
drawMessage(seekToString(desci_data, cmd), CGA_SCREENBUFFER);
break;
case 0xA000:
case 0xB000:
debug("Command: $%X 0x%X", the_command, cmd);
res = command_handlers[cmd]();
break;
case 0xF000:
/*restore sp from keep_sp then run script*/
/*currently only supposed to work correctly from the SCR_4D_PriorityCommand handler*/
debug("Restore: $%X 0x%X", the_command, cmd);
/*TODO("SCR_RESTORE\n");*/
/*fall through*/
default:
res = RunScript(getScriptSubroutine(cmd - 1));
}
#ifdef DEBUG_SCRIPT
runcmd_reentr -= 1;
#endif
#ifdef DEBUG_SCRIPT
{
FILE *f = fopen(DEBUG_SCRIPT_LOG, "at");
if (f) {
fprintf(f, "\n");
fclose(f);
}
}
#endif
if (g_vm->_shouldRestart)
return runCommandKeepSp();
if (g_vm->_prioritycommand_1 && !(g_vm->_prioritycommand_2))
return res;
if (g_vm->_prioritycommand_1 && g_vm->_prioritycommand_2)
return runCommandKeepSp();
/*TODO: this is pretty hacky, original code manipulates the stack to discard old script invocation*/
if (res == ScriptRerun)
goto again;
return res;
}
uint16 runCommandKeepSp(void) {
/*keep_sp = sp;*/
g_vm->_prioritycommand_1 = false;
g_vm->_prioritycommand_2 = false;
if (g_vm->_shouldRestart)
return RUNCOMMAND_RESTART;
return runCommand();
}
} // End of namespace Chamber