mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-10 03:40:25 +00:00
4452 lines
89 KiB
C++
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
|