6880 lines
197 KiB
C++
6880 lines
197 KiB
C++
#include "interpreter_extra.h"
|
|
|
|
#include "actions.h"
|
|
#include "animation.h"
|
|
#include "art.h"
|
|
#include "color.h"
|
|
#include "combat.h"
|
|
#include "combat_ai.h"
|
|
#include "core.h"
|
|
#include "critter.h"
|
|
#include "debug.h"
|
|
#include "dialog.h"
|
|
#include "display_monitor.h"
|
|
#include "endgame.h"
|
|
#include "game.h"
|
|
#include "game_config.h"
|
|
#include "game_dialog.h"
|
|
#include "game_movie.h"
|
|
#include "game_sound.h"
|
|
#include "interface.h"
|
|
#include "item.h"
|
|
#include "light.h"
|
|
#include "loadsave.h"
|
|
#include "map.h"
|
|
#include "object.h"
|
|
#include "palette.h"
|
|
#include "party_member.h"
|
|
#include "perk.h"
|
|
#include "proto.h"
|
|
#include "proto_instance.h"
|
|
#include "queue.h"
|
|
#include "random.h"
|
|
#include "reaction.h"
|
|
#include "scripts.h"
|
|
#include "skill.h"
|
|
#include "stat.h"
|
|
#include "text_object.h"
|
|
#include "tile.h"
|
|
#include "trait.h"
|
|
#include "world_map.h"
|
|
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
// 0x504B04
|
|
char _Error_0[] = "Error";
|
|
|
|
// 0x504B0C
|
|
char _aCritter[] = "<Critter>";
|
|
|
|
// Maps light level to light intensity.
|
|
//
|
|
// Middle value is mapped one-to-one which corresponds to 50% light level
|
|
// (cavern lighting). Light levels above (51-100%) and below (0-49) is
|
|
// calculated as percentage from two adjacent light values.
|
|
//
|
|
// See [opSetLightLevel] for math.
|
|
//
|
|
// 0x453F90
|
|
const int dword_453F90[3] = {
|
|
0x4000,
|
|
0xA000,
|
|
0x10000,
|
|
};
|
|
|
|
// 0x453F9C
|
|
const unsigned short word_453F9C[MOVIE_COUNT] = {
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
|
};
|
|
|
|
// 0x453FC0
|
|
Rect stru_453FC0 = { 0, 0, 640, 480 };
|
|
|
|
// 0x518EC0
|
|
const char* _dbg_error_strs[SCRIPT_ERROR_COUNT] = {
|
|
"unimped",
|
|
"obj is NULL",
|
|
"can't match program to sid",
|
|
"follows",
|
|
};
|
|
|
|
// 0x518ED0
|
|
const int _ftList[11] = {
|
|
ANIM_FALL_BACK_BLOOD_SF,
|
|
ANIM_BIG_HOLE_SF,
|
|
ANIM_CHARRED_BODY_SF,
|
|
ANIM_CHUNKS_OF_FLESH_SF,
|
|
ANIM_FALL_FRONT_BLOOD_SF,
|
|
ANIM_FALL_BACK_BLOOD_SF,
|
|
ANIM_DANCING_AUTOFIRE_SF,
|
|
ANIM_SLICED_IN_HALF_SF,
|
|
ANIM_EXPLODED_TO_NOTHING_SF,
|
|
ANIM_FALL_BACK_BLOOD_SF,
|
|
ANIM_FALL_FRONT_BLOOD_SF,
|
|
};
|
|
|
|
// 0x518EFC
|
|
char* _errStr = _Error_0;
|
|
|
|
// Last message type during op_float_msg sequential.
|
|
//
|
|
// 0x518F00
|
|
int _last_color = 1;
|
|
|
|
// 0x518F04
|
|
char* _strName = _aCritter;
|
|
|
|
// NOTE: This value is a little bit odd. It's used to handle 2 operations:
|
|
// [opStartGameDialog] and [opGameDialogReaction]. It's not used outside those
|
|
// functions.
|
|
//
|
|
// When used inside [opStartGameDialog] this value stores [Fidget] constant
|
|
// (1 - Good, 4 - Neutral, 7 - Bad).
|
|
//
|
|
// When used inside [opGameDialogReaction] this value contains specified
|
|
// reaction (-1 - Good, 0 - Neutral, 1 - Bad).
|
|
//
|
|
// 0x5970D0
|
|
int gGameDialogReactionOrFidget;
|
|
|
|
// 0x453FD0
|
|
void scriptPredefinedError(Program* program, const char* name, int error)
|
|
{
|
|
char string[260];
|
|
|
|
sprintf(string, "Script Error: %s: op_%s: %s", program->name, name, _dbg_error_strs[error]);
|
|
|
|
debugPrint(string);
|
|
}
|
|
|
|
// 0x45400C
|
|
void scriptError(const char* format, ...)
|
|
{
|
|
char string[260];
|
|
|
|
va_list argptr;
|
|
va_start(argptr, format);
|
|
vsprintf(string, format, argptr);
|
|
va_end(argptr);
|
|
|
|
debugPrint(string);
|
|
}
|
|
|
|
// 0x45404C
|
|
int tileIsVisible(int tile)
|
|
{
|
|
if (abs(gCenterTile - tile) % 200 < 5) {
|
|
return 1;
|
|
}
|
|
|
|
if (abs(gCenterTile - tile) / 200 < 5) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// 0x45409C
|
|
int _correctFidForRemovedItem(Object* a1, Object* a2, int flags)
|
|
{
|
|
if (a1 == gDude) {
|
|
bool animated = !gameUiIsDisabled();
|
|
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
}
|
|
|
|
int fid = a1->fid;
|
|
int v8 = (fid & 0xF000) >> 12;
|
|
int newFid = -1;
|
|
|
|
if ((flags & 0x03000000) != 0) {
|
|
if (a1 == gDude) {
|
|
if (interfaceGetCurrentHand()) {
|
|
if ((flags & 0x02000000) != 0) {
|
|
v8 = 0;
|
|
}
|
|
} else {
|
|
if ((flags & 0x01000000) != 0) {
|
|
v8 = 0;
|
|
}
|
|
}
|
|
} else {
|
|
if ((flags & 0x02000000) != 0) {
|
|
v8 = 0;
|
|
}
|
|
}
|
|
|
|
if (v8 == 0) {
|
|
newFid = buildFid((fid & 0xF000000) >> 24, fid & 0xFFF, (fid & 0xFF0000) >> 16, 0, (fid & 0x70000000) >> 28);
|
|
}
|
|
} else {
|
|
if (a1 == gDude) {
|
|
newFid = buildFid((fid & 0xF000000) >> 24, _art_vault_guy_num, (fid & 0xFF0000) >> 16, v8, (fid & 0x70000000) >> 28);
|
|
}
|
|
|
|
_adjust_ac(a1, a2, NULL);
|
|
}
|
|
|
|
if (newFid != -1) {
|
|
Rect rect;
|
|
objectSetFid(a1, newFid, &rect);
|
|
tileWindowRefreshRect(&rect, gElevation);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// give_exp_points
|
|
// 0x4541C8
|
|
void opGiveExpPoints(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to give_exp_points", program->name);
|
|
}
|
|
|
|
if (pcAddExperience(data) != 0) {
|
|
scriptError("\nScript Error: %s: op_give_exp_points: stat_pc_set failed");
|
|
}
|
|
}
|
|
|
|
// scr_return
|
|
// 0x454238
|
|
void opScrReturn(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to scr_return", program->name);
|
|
}
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
script->field_28 = data;
|
|
}
|
|
}
|
|
|
|
// play_sfx
|
|
// 0x4542AC
|
|
void opPlaySfx(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_STRING) {
|
|
programFatalError("script error: %s: invalid arg to play_sfx", program->name);
|
|
}
|
|
|
|
char* name = programGetString(program, opcode, data);
|
|
soundPlayFile(name);
|
|
}
|
|
|
|
// set_map_start
|
|
// 0x454314
|
|
void opSetMapStart(Program* program)
|
|
{
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to set_map_start", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int x = data[3];
|
|
int y = data[2];
|
|
int elevation = data[1];
|
|
int rotation = data[0];
|
|
|
|
if (mapSetElevation(elevation) != 0) {
|
|
scriptError("\nScript Error: %s: op_set_map_start: map_set_elevation failed", program->name);
|
|
return;
|
|
}
|
|
|
|
int tile = 200 * y + x;
|
|
if (tileSetCenter(tile, TILE_SET_CENTER_FLAG_0x01 | TILE_SET_CENTER_FLAG_0x02) != 0) {
|
|
scriptError("\nScript Error: %s: op_set_map_start: tile_set_center failed", program->name);
|
|
return;
|
|
}
|
|
|
|
mapSetStart(tile, elevation, rotation);
|
|
}
|
|
|
|
// override_map_start
|
|
// 0x4543F4
|
|
void opOverrideMapStart(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to override_map_start", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int x = data[3];
|
|
int y = data[2];
|
|
int elevation = data[1];
|
|
int rotation = data[0];
|
|
|
|
char text[60];
|
|
sprintf(text, "OVERRIDE_MAP_START: x: %d, y: %d", x, y);
|
|
debugPrint(text);
|
|
|
|
int tile = 200 * y + x;
|
|
int previousTile = gCenterTile;
|
|
if (tile != -1) {
|
|
if (objectSetRotation(gDude, rotation, NULL) != 0) {
|
|
scriptError("\nError: %s: obj_set_rotation failed in override_map_start!", program->name);
|
|
}
|
|
|
|
if (objectSetLocation(gDude, tile, elevation, NULL) != 0) {
|
|
scriptError("\nError: %s: obj_move_to_tile failed in override_map_start!", program->name);
|
|
|
|
if (objectSetLocation(gDude, previousTile, elevation, NULL) != 0) {
|
|
scriptError("\nError: %s: obj_move_to_tile RECOVERY Also failed!");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
tileSetCenter(tile, TILE_SET_CENTER_FLAG_0x01);
|
|
tileWindowRefresh();
|
|
}
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// has_skill
|
|
// 0x454568
|
|
void opHasSkill(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to has_skill", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int skill = data[0];
|
|
|
|
int result = 0;
|
|
if (object != NULL) {
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
result = skillGetValue(object, skill);
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "has_skill", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// using_skill
|
|
// 0x454634
|
|
void opUsingSkill(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to using_skill", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int skill = data[0];
|
|
|
|
// NOTE: In the original source code this value is left uninitialized, that
|
|
// explains why garbage is returned when using something else than dude and
|
|
// SKILL_SNEAK as arguments.
|
|
int result = 0;
|
|
|
|
if (skill == SKILL_SNEAK && object == gDude) {
|
|
result = dudeHasState(DUDE_STATE_SNEAKING);
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// roll_vs_skill
|
|
// 0x4546E8
|
|
void opRollVsSkill(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to roll_vs_skill", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int skill = data[1];
|
|
int modifier = data[0];
|
|
|
|
int roll = ROLL_CRITICAL_FAILURE;
|
|
if (object != NULL) {
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
roll = skillRoll(object, skill, modifier, &(script->howMuch));
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "roll_vs_skill", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, roll);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// skill_contest
|
|
// 0x4547D4
|
|
void opSkillContest(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to skill_contest", program->name, arg);
|
|
}
|
|
}
|
|
|
|
scriptPredefinedError(program, "skill_contest", SCRIPT_ERROR_NOT_IMPLEMENTED);
|
|
programStackPushInt32(program, 0);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// do_check
|
|
// 0x454890
|
|
void opDoCheck(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to do_check", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int stat = data[1];
|
|
int mod = data[0];
|
|
|
|
int roll = 0;
|
|
if (object != NULL) {
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
switch (stat) {
|
|
case STAT_STRENGTH:
|
|
case STAT_PERCEPTION:
|
|
case STAT_ENDURANCE:
|
|
case STAT_CHARISMA:
|
|
case STAT_INTELLIGENCE:
|
|
case STAT_AGILITY:
|
|
case STAT_LUCK:
|
|
roll = statRoll(object, stat, mod, &(script->howMuch));
|
|
break;
|
|
default:
|
|
scriptError("\nScript Error: %s: op_do_check: Stat out of range", program->name);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "do_check", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, roll);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// success
|
|
// 0x4549A8
|
|
void opSuccess(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to success", program->name);
|
|
}
|
|
|
|
int result = -1;
|
|
|
|
switch (data) {
|
|
case ROLL_CRITICAL_FAILURE:
|
|
case ROLL_FAILURE:
|
|
result = 0;
|
|
break;
|
|
case ROLL_SUCCESS:
|
|
case ROLL_CRITICAL_SUCCESS:
|
|
result = 1;
|
|
break;
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// critical
|
|
// 0x454A44
|
|
void opCritical(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to critical", program->name);
|
|
}
|
|
|
|
int result = -1;
|
|
|
|
switch (data) {
|
|
case ROLL_CRITICAL_FAILURE:
|
|
case ROLL_CRITICAL_SUCCESS:
|
|
result = 1;
|
|
break;
|
|
case ROLL_FAILURE:
|
|
case ROLL_SUCCESS:
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// how_much
|
|
// 0x454AD0
|
|
void opHowMuch(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to how_much", program->name);
|
|
}
|
|
|
|
int result = 0;
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
result = script->howMuch;
|
|
} else {
|
|
scriptPredefinedError(program, "how_much", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// mark_area_known
|
|
// 0x454B6C
|
|
void opMarkAreaKnown(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to mark_area_known", program->name, arg);
|
|
}
|
|
}
|
|
|
|
// TODO: Provide meaningful names.
|
|
if (data[2] == 0) {
|
|
if (data[0] == CITY_STATE_INVISIBLE) {
|
|
_wmAreaSetVisibleState(data[1], 0, 1);
|
|
} else {
|
|
_wmAreaSetVisibleState(data[1], 1, 1);
|
|
_wmAreaMarkVisitedState(data[1], data[0]);
|
|
}
|
|
} else if (data[2] == 1) {
|
|
_wmMapMarkVisited(data[1]);
|
|
}
|
|
}
|
|
|
|
// reaction_influence
|
|
// 0x454C34
|
|
void opReactionInfluence(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reaction_influence", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int result = _reaction_influence_();
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// random
|
|
// 0x454CD4
|
|
void opRandom(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to random", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int result;
|
|
if (_vcr_status() == 2) {
|
|
result = randomBetween(data[1], data[0]);
|
|
} else {
|
|
result = (data[0] - data[1]) / 2;
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// roll_dice
|
|
// 0x454D88
|
|
void opRollDice(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to roll_dice", program->name, arg);
|
|
}
|
|
}
|
|
|
|
scriptPredefinedError(program, "roll_dice", SCRIPT_ERROR_NOT_IMPLEMENTED);
|
|
|
|
programStackPushInt32(program, 0);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// move_to
|
|
// 0x454E28
|
|
void opMoveTo(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to move_to", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int tile = data[1];
|
|
int elevation = data[0];
|
|
|
|
int newTile;
|
|
|
|
if (object != NULL) {
|
|
if (object == gDude) {
|
|
bool tileLimitingEnabled = tileScrollLimitingIsEnabled();
|
|
bool tileBlockingEnabled = tileScrollBlockingIsEnabled();
|
|
|
|
if (tileLimitingEnabled) {
|
|
tileScrollLimitingDisable();
|
|
}
|
|
|
|
if (tileBlockingEnabled) {
|
|
tileScrollBlockingDisable();
|
|
}
|
|
|
|
Rect rect;
|
|
newTile = objectSetLocation(object, tile, elevation, &rect);
|
|
if (newTile != -1) {
|
|
tileSetCenter(object->tile, TILE_SET_CENTER_FLAG_0x01);
|
|
}
|
|
|
|
if (tileLimitingEnabled) {
|
|
tileScrollLimitingEnable();
|
|
}
|
|
|
|
if (tileBlockingEnabled) {
|
|
tileScrollBlockingEnable();
|
|
}
|
|
} else {
|
|
Rect before;
|
|
objectGetRect(object, &before);
|
|
|
|
if (object->elevation != elevation && (object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
_combat_delete_critter(object);
|
|
}
|
|
|
|
Rect after;
|
|
newTile = objectSetLocation(object, tile, elevation, &after);
|
|
if (newTile != -1) {
|
|
rectUnion(&before, &after, &before);
|
|
tileWindowRefreshRect(&before, gElevation);
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "move_to", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
newTile = -1;
|
|
}
|
|
|
|
programStackPushInt32(program, newTile);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// create_object_sid
|
|
// 0x454FA8
|
|
void opCreateObject(Program* program)
|
|
{
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to create_object", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int pid = data[3];
|
|
int tile = data[2];
|
|
int elevation = data[1];
|
|
int sid = data[0];
|
|
|
|
Object* object = NULL;
|
|
|
|
if (_isLoadingGame() != 0) {
|
|
debugPrint("\nError: attempt to Create critter in load/save-game: %s!", program->name);
|
|
goto out;
|
|
}
|
|
|
|
if (pid == 0) {
|
|
debugPrint("\nError: attempt to Create critter With PID of 0: %s!", program->name);
|
|
goto out;
|
|
}
|
|
|
|
Proto* proto;
|
|
if (protoGetProto(pid, &proto) != -1) {
|
|
if (objectCreateWithFidPid(&object, proto->fid, pid) != -1) {
|
|
if (tile == -1) {
|
|
tile = 0;
|
|
}
|
|
|
|
Rect rect;
|
|
if (objectSetLocation(object, tile, elevation, &rect) != -1) {
|
|
tileWindowRefreshRect(&rect, object->elevation);
|
|
}
|
|
}
|
|
} else {
|
|
// SFALL: Prevent a crash when the proto is missing.
|
|
goto out;
|
|
}
|
|
|
|
if (sid != -1) {
|
|
int scriptType = 0;
|
|
switch (object->pid >> 24) {
|
|
case OBJ_TYPE_CRITTER:
|
|
scriptType = SCRIPT_TYPE_CRITTER;
|
|
break;
|
|
case OBJ_TYPE_ITEM:
|
|
case OBJ_TYPE_SCENERY:
|
|
scriptType = SCRIPT_TYPE_ITEM;
|
|
break;
|
|
}
|
|
|
|
if (object->sid != -1) {
|
|
scriptRemove(object->sid);
|
|
object->sid = -1;
|
|
}
|
|
|
|
if (scriptAdd(&(object->sid), scriptType) == -1) {
|
|
goto out;
|
|
}
|
|
|
|
Script* script;
|
|
if (scriptGetScript(object->sid, &script) == -1) {
|
|
goto out;
|
|
}
|
|
|
|
script->field_14 = sid - 1;
|
|
|
|
if (scriptType == SCRIPT_TYPE_SPATIAL) {
|
|
script->sp.built_tile = ((object->elevation << 29) & 0xE0000000) | object->tile;
|
|
script->sp.radius = 3;
|
|
}
|
|
|
|
object->id = scriptsNewObjectId();
|
|
script->field_1C = object->id;
|
|
script->owner = object;
|
|
_scr_find_str_run_info(sid - 1, &(script->field_50), object->sid);
|
|
};
|
|
|
|
out:
|
|
|
|
programStackPushInt32(program, (int)object);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// destroy_object
|
|
// 0x4551E4
|
|
void opDestroyObject(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to destroy_object", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "destroy_object", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
if (_isLoadingGame()) {
|
|
debugPrint("\nError: attempt to destroy critter in load/save-game: %s!", program->name);
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool isSelf = object == scriptGetSelf(program);
|
|
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
_combat_delete_critter(object);
|
|
}
|
|
|
|
Object* owner = objectGetOwner(object);
|
|
if (owner != NULL) {
|
|
int quantity = _item_count(owner, object);
|
|
itemRemove(owner, object, quantity);
|
|
|
|
if (owner == gDude) {
|
|
bool animated = !gameUiIsDisabled();
|
|
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
}
|
|
|
|
_obj_connect(object, 1, 0, NULL);
|
|
|
|
if (isSelf) {
|
|
object->sid = -1;
|
|
object->flags |= (OBJECT_HIDDEN | OBJECT_TEMPORARY);
|
|
} else {
|
|
reg_anim_clear(object);
|
|
objectDestroy(object, NULL);
|
|
}
|
|
} else {
|
|
reg_anim_clear(object);
|
|
|
|
Rect rect;
|
|
objectDestroy(object, &rect);
|
|
tileWindowRefreshRect(&rect, gElevation);
|
|
}
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
|
|
if (isSelf) {
|
|
program->flags |= PROGRAM_FLAG_0x0100;
|
|
}
|
|
}
|
|
|
|
// display_msg
|
|
// 0x455388
|
|
void opDisplayMsg(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & VALUE_TYPE_MASK) != VALUE_TYPE_STRING) {
|
|
programFatalError("script error: %s: invalid arg to display_msg", program->name);
|
|
}
|
|
|
|
char* string = programGetString(program, opcode, data);
|
|
displayMonitorAddMessage(string);
|
|
|
|
bool showScriptMessages = false;
|
|
configGetBool(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, &showScriptMessages);
|
|
|
|
if (showScriptMessages) {
|
|
debugPrint("\n");
|
|
debugPrint(string);
|
|
}
|
|
}
|
|
|
|
// script_overrides
|
|
// 0x455430
|
|
void opScriptOverrides(Program* program)
|
|
{
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
script->scriptOverrides = 1;
|
|
} else {
|
|
scriptPredefinedError(program, "script_overrides", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
}
|
|
|
|
// obj_is_carrying_obj_pid
|
|
// 0x455470
|
|
void opObjectIsCarryingObjectWithPid(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to obj_is_carrying_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[1];
|
|
int pid = data[0];
|
|
|
|
int result = 0;
|
|
if (obj != NULL) {
|
|
result = objectGetCarriedQuantityByPid(obj, pid);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_is_carrying_obj_pid", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// tile_contains_obj_pid
|
|
// 0x455534
|
|
void opTileContainsObjectWithPid(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to tile_contains_obj_pid", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int tile = data[2];
|
|
int elevation = data[1];
|
|
int pid = data[0];
|
|
|
|
int result = 0;
|
|
|
|
Object* object = objectFindFirstAtLocation(elevation, tile);
|
|
while (object) {
|
|
if (object->pid == pid) {
|
|
result = 1;
|
|
break;
|
|
}
|
|
object = objectFindNextAtLocation();
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// self_obj
|
|
// 0x455600
|
|
void opGetSelf(Program* program)
|
|
{
|
|
Object* object = scriptGetSelf(program);
|
|
programStackPushInt32(program, (int)object);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// source_obj
|
|
// 0x455624
|
|
void opGetSource(Program* program)
|
|
{
|
|
Object* object = NULL;
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
object = script->source;
|
|
} else {
|
|
scriptPredefinedError(program, "source_obj", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
|
|
programStackPushInt32(program, (int)object);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// target_obj
|
|
// 0x455678
|
|
void opGetTarget(Program* program)
|
|
{
|
|
Object* object = NULL;
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
object = script->target;
|
|
} else {
|
|
scriptPredefinedError(program, "target_obj", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
|
|
programStackPushInt32(program, (int)object);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// dude_obj
|
|
// 0x4556CC
|
|
void opGetDude(Program* program)
|
|
{
|
|
programStackPushInt32(program, (int)gDude);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// NOTE: The implementation is the same as in [opGetTarget].
|
|
//
|
|
// obj_being_used_with
|
|
// 0x4556EC
|
|
void opGetObjectBeingUsed(Program* program)
|
|
{
|
|
Object* object = NULL;
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
object = script->target;
|
|
} else {
|
|
scriptPredefinedError(program, "obj_being_used_with", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
|
|
programStackPushInt32(program, (int)object);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// local_var
|
|
// 0x455740
|
|
void opGetLocalVar(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
// FIXME: The error message is wrong.
|
|
programFatalError("script error: %s: invalid arg to op_global_var", program->name);
|
|
}
|
|
|
|
int value = -1;
|
|
|
|
int sid = scriptGetSid(program);
|
|
scriptGetLocalVar(sid, data, &value);
|
|
|
|
programStackPushInt32(program, value);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// set_local_var
|
|
// 0x4557C8
|
|
void opSetLocalVar(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to set_local_var", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int variable = data[1];
|
|
int value = data[0];
|
|
|
|
int sid = scriptGetSid(program);
|
|
scriptSetLocalVar(sid, variable, value);
|
|
}
|
|
|
|
// map_var
|
|
// 0x455858
|
|
void opGetMapVar(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to op_map_var", program->name);
|
|
}
|
|
|
|
int value = mapGetGlobalVar(data);
|
|
|
|
programStackPushInt32(program, value);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// set_map_var
|
|
// 0x4558C8
|
|
void opSetMapVar(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to set_map_var", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int variable = data[1];
|
|
int value = data[0];
|
|
|
|
mapSetGlobalVar(variable, value);
|
|
}
|
|
|
|
// global_var
|
|
// 0x455950
|
|
void opGetGlobalVar(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to op_global_var", program->name);
|
|
}
|
|
|
|
int value = -1;
|
|
if (gGameGlobalVarsLength != 0) {
|
|
value = gameGetGlobalVar(data);
|
|
} else {
|
|
scriptError("\nScript Error: %s: op_global_var: no global vars found!", program->name);
|
|
}
|
|
|
|
programStackPushInt32(program, value);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// set_global_var
|
|
// 0x4559EC
|
|
// 0x80C6
|
|
void opSetGlobalVar(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to set_global_var", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int variable = data[1];
|
|
int value = data[0];
|
|
|
|
if (gGameGlobalVarsLength != 0) {
|
|
gameSetGlobalVar(variable, value);
|
|
} else {
|
|
scriptError("\nScript Error: %s: op_set_global_var: no global vars found!", program->name);
|
|
}
|
|
}
|
|
|
|
// script_action
|
|
// 0x455A90
|
|
void opGetScriptAction(Program* program)
|
|
{
|
|
int action = 0;
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
action = script->action;
|
|
} else {
|
|
scriptPredefinedError(program, "script_action", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
|
|
programStackPushInt32(program, action);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_type
|
|
// 0x455AE4
|
|
void opGetObjectType(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to op_obj_type", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
int objectType = -1;
|
|
if (object != NULL) {
|
|
objectType = (object->fid & 0xF000000) >> 24;
|
|
}
|
|
|
|
programStackPushInt32(program, objectType);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_item_subtype
|
|
// 0x455B6C
|
|
void opGetItemType(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to op_item_subtype", program->name);
|
|
}
|
|
|
|
Object* obj = (Object*)data;
|
|
|
|
int itemType = -1;
|
|
if (obj != NULL) {
|
|
if ((obj->pid >> 24) == OBJ_TYPE_ITEM) {
|
|
Proto* proto;
|
|
if (protoGetProto(obj->pid, &proto) != -1) {
|
|
itemType = itemGetType(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
programStackPushInt32(program, itemType);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// get_critter_stat
|
|
// 0x455C10
|
|
void opGetCritterStat(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to get_critter_stat", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int stat = data[0];
|
|
|
|
int value = -1;
|
|
if (object != NULL) {
|
|
value = critterGetStat(object, stat);
|
|
} else {
|
|
scriptPredefinedError(program, "get_critter_stat", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, value);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// NOTE: Despite it's name it does not actually "set" stat, but "adjust". So
|
|
// it's last argument is amount of adjustment, not it's final value.
|
|
//
|
|
// set_critter_stat
|
|
// 0x455CCC
|
|
void opSetCritterStat(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to set_critter_stat", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int stat = data[1];
|
|
int value = data[0];
|
|
|
|
int result = 0;
|
|
if (object != NULL) {
|
|
if (object == gDude) {
|
|
int currentValue = critterGetBaseStatWithTraitModifier(object, stat);
|
|
critterSetBaseStat(object, stat, currentValue + value);
|
|
} else {
|
|
scriptPredefinedError(program, "set_critter_stat", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Can't modify anyone except obj_dude!");
|
|
result = -1;
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "set_critter_stat", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
result = -1;
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// animate_stand_obj
|
|
// 0x455DC8
|
|
void opAnimateStand(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to animate_stand_obj", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
if (object == NULL) {
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) == -1) {
|
|
scriptPredefinedError(program, "animate_stand_obj", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
return;
|
|
}
|
|
|
|
object = scriptGetSelf(program);
|
|
}
|
|
|
|
if (!isInCombat()) {
|
|
reg_anim_begin(1);
|
|
reg_anim_animate(object, ANIM_STAND, 0);
|
|
reg_anim_end();
|
|
}
|
|
}
|
|
|
|
// animate_stand_reverse_obj
|
|
// 0x455E7C
|
|
void opAnimateStandReverse(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
// FIXME: typo in message, should be animate_stand_reverse_obj.
|
|
programFatalError("script error: %s: invalid arg to animate_stand_obj", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
if (object == NULL) {
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) == -1) {
|
|
scriptPredefinedError(program, "animate_stand_reverse_obj", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
return;
|
|
}
|
|
|
|
object = scriptGetSelf(program);
|
|
}
|
|
|
|
if (!isInCombat()) {
|
|
reg_anim_begin(0x01);
|
|
reg_anim_animate_reverse(object, ANIM_STAND, 0);
|
|
reg_anim_end();
|
|
}
|
|
}
|
|
|
|
// animate_move_obj_to_tile
|
|
// 0x455F30
|
|
void opAnimateMoveObjectToTile(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to animate_move_obj_to_tile", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int tile = data[1];
|
|
int flags = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "animate_move_obj_to_tile", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (tile <= -1) {
|
|
return;
|
|
}
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) == -1) {
|
|
scriptPredefinedError(program, "animate_move_obj_to_tile", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
return;
|
|
}
|
|
|
|
if (!critterIsActive(object)) {
|
|
return;
|
|
}
|
|
|
|
if (isInCombat()) {
|
|
return;
|
|
}
|
|
|
|
if ((flags & 0x10) != 0) {
|
|
reg_anim_clear(object);
|
|
flags &= ~0x10;
|
|
}
|
|
|
|
reg_anim_begin(1);
|
|
|
|
if (flags == 0) {
|
|
reg_anim_obj_move_to_tile(object, tile, object->elevation, -1, 0);
|
|
} else {
|
|
reg_anim_obj_run_to_tile(object, tile, object->elevation, -1, 0);
|
|
}
|
|
|
|
reg_anim_end();
|
|
}
|
|
|
|
// tile_in_tile_rect
|
|
// 0x45607C
|
|
void opTileInTileRect(Program* program)
|
|
{
|
|
opcode_t opcode[5];
|
|
int data[5];
|
|
Point points[5];
|
|
|
|
for (int arg = 0; arg < 5; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to tile_in_tile_rect", program->name, arg);
|
|
}
|
|
|
|
points[arg].x = data[arg] % 200;
|
|
points[arg].y = data[arg] / 200;
|
|
}
|
|
|
|
int x = points[0].x;
|
|
int y = points[0].y;
|
|
|
|
int minX = points[1].x;
|
|
int maxX = points[4].x;
|
|
|
|
int minY = points[4].y;
|
|
int maxY = points[1].y;
|
|
|
|
int result = 0;
|
|
if (x >= minX && x <= maxX && y >= minY && y <= maxY) {
|
|
result = 1;
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// make_daytime
|
|
// 0x456170
|
|
void opMakeDayTime(Program* program)
|
|
{
|
|
}
|
|
|
|
// tile_distance
|
|
// 0x456174
|
|
void opTileDistanceBetween(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to tile_distance", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int tile1 = data[1];
|
|
int tile2 = data[0];
|
|
|
|
int distance;
|
|
|
|
if (tile1 != -1 && tile2 != -1) {
|
|
distance = tileDistanceBetween(tile1, tile2);
|
|
} else {
|
|
distance = 9999;
|
|
}
|
|
|
|
programStackPushInt32(program, distance);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// tile_distance_objs
|
|
// 0x456228
|
|
void opTileDistanceBetweenObjects(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to tile_distance_objs", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object1 = (Object*)data[1];
|
|
Object* object2 = (Object*)data[0];
|
|
|
|
int distance = 9999;
|
|
if (object1 != NULL && object2 != NULL) {
|
|
if (data[1] >= HEX_GRID_SIZE && data[0] >= HEX_GRID_SIZE) {
|
|
if (object1->elevation == object2->elevation) {
|
|
if (object1->tile != -1 && object2->tile != -1) {
|
|
distance = tileDistanceBetween(object1->tile, object2->tile);
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "tile_distance_objs", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Passed a tile # instead of an object!!!BADBADBAD!");
|
|
}
|
|
}
|
|
|
|
programStackPushInt32(program, distance);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// tile_num
|
|
// 0x456324
|
|
void opGetObjectTile(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to tile_num", program->name);
|
|
}
|
|
|
|
Object* obj = (Object*)data;
|
|
|
|
int tile = -1;
|
|
if (obj != NULL) {
|
|
tile = obj->tile;
|
|
} else {
|
|
scriptPredefinedError(program, "tile_num", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, tile);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// tile_num_in_direction
|
|
// 0x4563B4
|
|
void opGetTileInDirection(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to tile_num_in_direction", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int origin = data[2];
|
|
int rotation = data[1];
|
|
int distance = data[0];
|
|
|
|
int tile = -1;
|
|
|
|
if (origin != -1) {
|
|
if (rotation < ROTATION_COUNT) {
|
|
if (distance != 0) {
|
|
tile = tileGetTileInDirection(origin, rotation, distance);
|
|
if (tile < -1) {
|
|
debugPrint("\nError: %s: op_tile_num_in_direction got #: %d", program->name, tile);
|
|
tile = -1;
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "tile_num_in_direction", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" rotation out of Range!");
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "tile_num_in_direction", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" tileNum is -1!");
|
|
}
|
|
|
|
programStackPushInt32(program, tile);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// pickup_obj
|
|
// 0x4564D4
|
|
void opPickup(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to pickup_obj", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object == NULL) {
|
|
return;
|
|
}
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) == 1) {
|
|
scriptPredefinedError(program, "pickup_obj", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
return;
|
|
}
|
|
|
|
if (script->target == NULL) {
|
|
scriptPredefinedError(program, "pickup_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
actionPickUp(script->target, object);
|
|
}
|
|
|
|
// drop_obj
|
|
// 0x456580
|
|
void opDrop(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to drop_obj", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object == NULL) {
|
|
return;
|
|
}
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) == -1) {
|
|
// FIXME: Should be SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID.
|
|
scriptPredefinedError(program, "drop_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (script->target == NULL) {
|
|
// FIXME: Should be SCRIPT_ERROR_OBJECT_IS_NULL.
|
|
scriptPredefinedError(program, "drop_obj", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
return;
|
|
}
|
|
|
|
_obj_drop(script->target, object);
|
|
}
|
|
|
|
// add_obj_to_inven
|
|
// 0x45662C
|
|
void opAddObjectToInventory(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to add_obj_to_inven", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* owner = (Object*)data[1];
|
|
Object* item = (Object*)data[0];
|
|
|
|
if (owner == NULL || item == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (item->owner == NULL) {
|
|
if (itemAdd(owner, item, 1) == 0) {
|
|
Rect rect;
|
|
_obj_disconnect(item, &rect);
|
|
tileWindowRefreshRect(&rect, item->elevation);
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "add_obj_to_inven", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Item was already attached to something else!");
|
|
}
|
|
}
|
|
|
|
// rm_obj_from_inven
|
|
// 0x456708
|
|
void opRemoveObjectFromInventory(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to rm_obj_from_inven", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* owner = (Object*)data[1];
|
|
Object* item = (Object*)data[0];
|
|
|
|
if (owner == NULL || item == NULL) {
|
|
return;
|
|
}
|
|
|
|
bool updateFlags = false;
|
|
int flags = 0;
|
|
|
|
if ((item->flags & OBJECT_EQUIPPED) != 0) {
|
|
if ((item->flags & OBJECT_IN_LEFT_HAND) != 0) {
|
|
flags |= OBJECT_IN_LEFT_HAND;
|
|
}
|
|
|
|
if ((item->flags & OBJECT_IN_RIGHT_HAND) != 0) {
|
|
flags |= OBJECT_IN_RIGHT_HAND;
|
|
}
|
|
|
|
if ((item->flags & OBJECT_WORN) != 0) {
|
|
flags |= OBJECT_WORN;
|
|
}
|
|
|
|
updateFlags = true;
|
|
}
|
|
|
|
if (itemRemove(owner, item, 1) == 0) {
|
|
Rect rect;
|
|
_obj_connect(item, 1, 0, &rect);
|
|
tileWindowRefreshRect(&rect, item->elevation);
|
|
|
|
if (updateFlags) {
|
|
_correctFidForRemovedItem(owner, item, flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
// wield_obj_critter
|
|
// 0x45681C
|
|
void opWieldItem(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to wield_obj_critter", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* critter = (Object*)data[1];
|
|
Object* item = (Object*)data[0];
|
|
|
|
if (critter == NULL) {
|
|
scriptPredefinedError(program, "wield_obj_critter", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (item == NULL) {
|
|
scriptPredefinedError(program, "wield_obj_critter", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
|
|
scriptPredefinedError(program, "wield_obj_critter", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Only works for critters! ERROR ERROR ERROR!");
|
|
return;
|
|
}
|
|
|
|
int hand = HAND_RIGHT;
|
|
|
|
bool v1 = false;
|
|
Object* oldArmor = NULL;
|
|
Object* newArmor = NULL;
|
|
if (critter == gDude) {
|
|
if (interfaceGetCurrentHand() == HAND_LEFT) {
|
|
hand = HAND_LEFT;
|
|
}
|
|
|
|
if (itemGetType(item) == ITEM_TYPE_ARMOR) {
|
|
oldArmor = critterGetArmor(gDude);
|
|
}
|
|
|
|
v1 = true;
|
|
newArmor = item;
|
|
}
|
|
|
|
if (_inven_wield(critter, item, hand) == -1) {
|
|
scriptPredefinedError(program, "wield_obj_critter", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" inven_wield failed! ERROR ERROR ERROR!");
|
|
return;
|
|
}
|
|
|
|
if (critter == gDude) {
|
|
if (v1) {
|
|
_adjust_ac(critter, oldArmor, newArmor);
|
|
}
|
|
|
|
bool animated = !gameUiIsDisabled();
|
|
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
}
|
|
}
|
|
|
|
// use_obj
|
|
// 0x4569D0
|
|
void opUseObject(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to use_obj", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "use_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) == -1) {
|
|
// FIXME: Should be SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID.
|
|
scriptPredefinedError(program, "use_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (script->target == NULL) {
|
|
scriptPredefinedError(program, "use_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
Object* self = scriptGetSelf(program);
|
|
if ((self->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
_action_use_an_object(script->target, object);
|
|
} else {
|
|
_obj_use(self, object);
|
|
}
|
|
}
|
|
|
|
// obj_can_see_obj
|
|
// 0x456AC4
|
|
void opObjectCanSeeObject(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to obj_can_see_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object1 = (Object*)data[1];
|
|
Object* object2 = (Object*)data[0];
|
|
|
|
int result = 0;
|
|
|
|
if (object1 != NULL && object2 != NULL) {
|
|
if (object2->tile != -1) {
|
|
// NOTE: Looks like dead code, I guess these checks were incorporated
|
|
// into higher level functions, but this code left intact.
|
|
if (object2 == gDude) {
|
|
dudeHasState(0);
|
|
}
|
|
|
|
critterGetStat(object1, STAT_PERCEPTION);
|
|
|
|
if (objectCanHearObject(object1, object2)) {
|
|
Object* a5;
|
|
_make_straight_path(object1, object1->tile, object2->tile, NULL, &a5, 16);
|
|
if (a5 == object2) {
|
|
result = 1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "obj_can_see_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// attack_complex
|
|
// 0x456C00
|
|
void opAttackComplex(Program* program)
|
|
{
|
|
opcode_t opcode[8];
|
|
int data[8];
|
|
|
|
for (int arg = 0; arg < 8; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to attack", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* target = (Object*)data[7];
|
|
if (target == NULL) {
|
|
scriptPredefinedError(program, "attack", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
Object* self = scriptGetSelf(program);
|
|
if (self == NULL) {
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if (!critterIsActive(self) || (self->flags & OBJECT_HIDDEN) != 0) {
|
|
debugPrint("\n But is already Inactive (Dead/Stunned/Invisible)");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if (!critterIsActive(target) || (target->flags & OBJECT_HIDDEN) != 0) {
|
|
debugPrint("\n But target is already dead or invisible");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if ((target->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) != 0) {
|
|
debugPrint("\n But target is AFRAID");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if (_gdialogActive()) {
|
|
// TODO: Might be an error, program flag is not removed.
|
|
return;
|
|
}
|
|
|
|
if (isInCombat()) {
|
|
CritterCombatData* combatData = &(self->data.critter.combat);
|
|
if ((combatData->maneuver & CRITTER_MANEUVER_0x01) == 0) {
|
|
combatData->maneuver |= CRITTER_MANEUVER_0x01;
|
|
combatData->whoHitMe = target;
|
|
}
|
|
} else {
|
|
STRUCT_664980 attack;
|
|
attack.attacker = self;
|
|
attack.defender = target;
|
|
attack.actionPointsBonus = 0;
|
|
attack.accuracyBonus = data[4];
|
|
attack.damageBonus = 0;
|
|
attack.minDamage = data[3];
|
|
attack.maxDamage = data[2];
|
|
|
|
// TODO: Something is probably broken here, why it wants
|
|
// flags to be the same? Maybe because both of them
|
|
// are applied to defender because of the bug in 0x422F3C?
|
|
if (data[1] == data[0]) {
|
|
attack.field_1C = 1;
|
|
attack.field_24 = data[0];
|
|
attack.field_20 = data[1];
|
|
} else {
|
|
attack.field_1C = 0;
|
|
}
|
|
|
|
scriptsRequestCombat(&attack);
|
|
}
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// start_gdialog
|
|
// 0x456DF0
|
|
void opStartGameDialog(Program* program)
|
|
{
|
|
opcode_t opcode[5];
|
|
int data[5];
|
|
|
|
for (int arg = 0; arg < 5; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to start_gdialog", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[3];
|
|
int reactionLevel = data[2];
|
|
int headId = data[1];
|
|
int backgroundId = data[0];
|
|
|
|
if (isInCombat()) {
|
|
return;
|
|
}
|
|
|
|
if (obj == NULL) {
|
|
scriptPredefinedError(program, "start_gdialog", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
gGameDialogHeadFid = -1;
|
|
if ((obj->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
Proto* proto;
|
|
if (protoGetProto(obj->pid, &proto) == -1) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (headId != -1) {
|
|
gGameDialogHeadFid = buildFid(OBJ_TYPE_HEAD, headId, 0, 0, 0);
|
|
}
|
|
|
|
gameDialogSetBackground(backgroundId);
|
|
gGameDialogReactionOrFidget = reactionLevel;
|
|
|
|
if (gGameDialogHeadFid != -1) {
|
|
int npcReactionValue = reactionGetValue(gGameDialogSpeaker);
|
|
int npcReactionType = reactionTranslateValue(npcReactionValue);
|
|
switch (npcReactionType) {
|
|
case NPC_REACTION_BAD:
|
|
gGameDialogReactionOrFidget = FIDGET_BAD;
|
|
break;
|
|
case NPC_REACTION_NEUTRAL:
|
|
gGameDialogReactionOrFidget = FIDGET_NEUTRAL;
|
|
break;
|
|
case NPC_REACTION_GOOD:
|
|
gGameDialogReactionOrFidget = FIDGET_GOOD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gGameDialogSid = scriptGetSid(program);
|
|
gGameDialogSpeaker = scriptGetSelf(program);
|
|
_gdialogInitFromScript(gGameDialogHeadFid, gGameDialogReactionOrFidget);
|
|
}
|
|
|
|
// end_dialogue
|
|
// 0x456F80
|
|
void opEndGameDialog(Program* program)
|
|
{
|
|
if (_gdialogExitFromScript() != -1) {
|
|
gGameDialogSpeaker = NULL;
|
|
gGameDialogSid = -1;
|
|
}
|
|
}
|
|
|
|
// dialogue_reaction
|
|
// 0x456FA4
|
|
void opGameDialogReaction(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int value = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, value);
|
|
}
|
|
|
|
if ((opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to dialogue_reaction", program->name);
|
|
}
|
|
|
|
gGameDialogReactionOrFidget = value;
|
|
_talk_to_critter_reacts(value);
|
|
}
|
|
|
|
// metarule3
|
|
// 0x457110
|
|
void opMetarule3(Program* program)
|
|
{
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to metarule3", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int rule = data[3];
|
|
int result = 0;
|
|
|
|
switch (rule) {
|
|
case METARULE3_CLR_FIXED_TIMED_EVENTS:
|
|
if (1) {
|
|
_scrSetQueueTestVals((Object*)data[2], data[1]);
|
|
_queue_clear_type(EVENT_TYPE_SCRIPT, _scrQueueRemoveFixed);
|
|
}
|
|
break;
|
|
case METARULE3_MARK_SUBTILE:
|
|
result = _wmSubTileMarkRadiusVisited(data[2], data[1], data[0]);
|
|
break;
|
|
case METARULE3_GET_KILL_COUNT:
|
|
result = killsGetByType(data[2]);
|
|
break;
|
|
case METARULE3_MARK_MAP_ENTRANCE:
|
|
result = _wmMapMarkMapEntranceState(data[2], data[1], data[0]);
|
|
break;
|
|
case METARULE3_WM_SUBTILE_STATE:
|
|
if (1) {
|
|
int state;
|
|
if (_wmSubTileGetVisitedState(data[2], data[1], &state) == 0) {
|
|
result = state;
|
|
}
|
|
}
|
|
break;
|
|
case METARULE3_TILE_GET_NEXT_CRITTER:
|
|
if (1) {
|
|
int tile = data[2];
|
|
int elevation = data[1];
|
|
Object* previousCritter = (Object*)data[0];
|
|
|
|
bool critterFound = previousCritter == NULL;
|
|
|
|
Object* object = objectFindFirstAtLocation(elevation, tile);
|
|
while (object != NULL) {
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
if (critterFound) {
|
|
result = (int)object;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (object == previousCritter) {
|
|
critterFound = true;
|
|
}
|
|
|
|
object = objectFindNextAtLocation();
|
|
}
|
|
}
|
|
break;
|
|
case METARULE3_ART_SET_BASE_FID_NUM:
|
|
if (1) {
|
|
Object* obj = (Object*)data[2];
|
|
int frmId = data[1];
|
|
|
|
int fid = buildFid((obj->fid & 0xF000000) >> 24,
|
|
frmId,
|
|
(obj->fid & 0xFF0000) >> 16,
|
|
(obj->fid & 0xF000) >> 12,
|
|
(obj->fid & 0x70000000) >> 28);
|
|
|
|
Rect updatedRect;
|
|
objectSetFid(obj, fid, &updatedRect);
|
|
tileWindowRefreshRect(&updatedRect, gElevation);
|
|
}
|
|
break;
|
|
case METARULE3_TILE_SET_CENTER:
|
|
result = tileSetCenter(data[2], TILE_SET_CENTER_FLAG_0x01);
|
|
break;
|
|
case METARULE3_109:
|
|
result = aiGetChemUse((Object*)data[2]);
|
|
break;
|
|
case METARULE3_110:
|
|
result = carIsEmpty() ? 1 : 0;
|
|
break;
|
|
case METARULE3_111:
|
|
result = _map_target_load_area();
|
|
break;
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// set_map_music
|
|
// 0x45734C
|
|
void opSetMapMusic(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
}
|
|
|
|
if ((opcode[1] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
// FIXME: argument is wrong, should be 1.
|
|
programFatalError("script error: %s: invalid arg %d to set_map_music", program->name, 2);
|
|
}
|
|
|
|
int mapIndex = data[1];
|
|
|
|
char* string = NULL;
|
|
if ((opcode[0] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
string = programGetString(program, opcode[0], data[0]);
|
|
} else {
|
|
// FIXME: argument is wrong, should be 0.
|
|
programFatalError("script error: %s: invalid arg %d to set_map_music", program->name, 2);
|
|
}
|
|
|
|
debugPrint("\nset_map_music: %d, %s", mapIndex, string);
|
|
worldmapSetMapMusic(mapIndex, string);
|
|
}
|
|
|
|
// NOTE: Function name is a bit misleading. Last parameter is a boolean value
|
|
// where 1 or true makes object invisible, and value 0 (false) makes it visible
|
|
// again. So a better name for this function is opSetObjectInvisible.
|
|
//
|
|
//
|
|
// set_obj_visibility
|
|
// 0x45741C
|
|
void opSetObjectVisibility(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to set_obj_visibility", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[1];
|
|
int invisible = data[0];
|
|
|
|
if (obj == NULL) {
|
|
scriptPredefinedError(program, "set_obj_visibility", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (_isLoadingGame()) {
|
|
debugPrint("Error: attempt to set_obj_visibility in load/save-game: %s!", program->name);
|
|
return;
|
|
}
|
|
|
|
if (invisible != 0) {
|
|
if ((obj->flags & OBJECT_HIDDEN) == 0) {
|
|
if (isInCombat()) {
|
|
objectDisableOutline(obj, NULL);
|
|
objectClearOutline(obj, NULL);
|
|
}
|
|
|
|
Rect rect;
|
|
if (objectHide(obj, &rect) != -1) {
|
|
if ((obj->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
obj->flags |= OBJECT_NO_BLOCK;
|
|
}
|
|
|
|
tileWindowRefreshRect(&rect, obj->elevation);
|
|
}
|
|
}
|
|
} else {
|
|
if ((obj->flags & OBJECT_HIDDEN) != 0) {
|
|
if ((obj->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
obj->flags &= ~OBJECT_NO_BLOCK;
|
|
}
|
|
|
|
Rect rect;
|
|
if (objectShow(obj, &rect) != -1) {
|
|
tileWindowRefreshRect(&rect, obj->elevation);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// load_map
|
|
// 0x45755C
|
|
void opLoadMap(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
opcode[0] = programStackPopInt16(program);
|
|
data[0] = programStackPopInt32(program);
|
|
|
|
if (opcode[0] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[0], data[0]);
|
|
}
|
|
|
|
if ((opcode[0] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg 0 to load_map", program->name);
|
|
}
|
|
|
|
opcode[1] = programStackPopInt16(program);
|
|
data[1] = programStackPopInt32(program);
|
|
|
|
if (opcode[1] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[1], data[1]);
|
|
}
|
|
|
|
int param = data[0];
|
|
int mapIndexOrName = data[1];
|
|
|
|
char* mapName = NULL;
|
|
|
|
if ((opcode[1] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
if ((opcode[1] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
mapName = programGetString(program, opcode[1], mapIndexOrName);
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg 1 to load_map", program->name);
|
|
}
|
|
}
|
|
|
|
int mapIndex = -1;
|
|
|
|
if (mapName != NULL) {
|
|
gGameGlobalVars[GVAR_LOAD_MAP_INDEX] = param;
|
|
mapIndex = mapGetIndexByFileName(mapName);
|
|
} else {
|
|
if (mapIndexOrName >= 0) {
|
|
gGameGlobalVars[GVAR_LOAD_MAP_INDEX] = param;
|
|
mapIndex = mapIndexOrName;
|
|
}
|
|
}
|
|
|
|
if (mapIndex != -1) {
|
|
MapTransition transition;
|
|
transition.map = mapIndex;
|
|
transition.elevation = -1;
|
|
transition.tile = -1;
|
|
transition.rotation = -1;
|
|
mapSetTransition(&transition);
|
|
}
|
|
}
|
|
|
|
// wm_area_set_pos
|
|
// 0x457680
|
|
void opWorldmapCitySetPos(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to wm_area_set_pos", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int city = data[2];
|
|
int x = data[1];
|
|
int y = data[0];
|
|
|
|
if (worldmapCitySetPos(city, x, y) == -1) {
|
|
scriptPredefinedError(program, "wm_area_set_pos", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint("Invalid Parameter!");
|
|
}
|
|
}
|
|
|
|
// set_exit_grids
|
|
// 0x457730
|
|
void opSetExitGrids(Program* program)
|
|
{
|
|
opcode_t opcode[5];
|
|
int data[5];
|
|
|
|
for (int arg = 0; arg < 5; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to set_exit_grids", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int elevation = data[4];
|
|
int destinationMap = data[3];
|
|
int destinationElevation = data[2];
|
|
int destinationTile = data[1];
|
|
int destinationRotation = data[0];
|
|
|
|
Object* object = objectFindFirstAtElevation(elevation);
|
|
while (object != NULL) {
|
|
if (object->pid >= PROTO_ID_0x5000010 && object->pid <= PROTO_ID_0x5000017) {
|
|
object->data.misc.map = destinationMap;
|
|
object->data.misc.tile = destinationTile;
|
|
object->data.misc.elevation = destinationElevation;
|
|
}
|
|
object = objectFindNextAtElevation();
|
|
}
|
|
}
|
|
|
|
// anim_busy
|
|
// 0x4577EC
|
|
void opAnimBusy(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to anim_busy", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
int rc = 0;
|
|
if (object != NULL) {
|
|
rc = animationIsBusy(object);
|
|
} else {
|
|
scriptPredefinedError(program, "anim_busy", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, rc);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// critter_heal
|
|
// 0x457880
|
|
void opCritterHeal(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_heal", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* critter = (Object*)data[1];
|
|
int amount = data[0];
|
|
|
|
int rc = critterAdjustHitPoints(critter, amount);
|
|
|
|
if (critter == gDude) {
|
|
interfaceRenderHitPoints(true);
|
|
}
|
|
|
|
programStackPushInt32(program, rc);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// set_light_level
|
|
// 0x457934
|
|
void opSetLightLevel(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to set_light_level", program->name);
|
|
}
|
|
|
|
int lightLevel = data;
|
|
|
|
if (data == 50) {
|
|
lightSetLightLevel(dword_453F90[1], true);
|
|
return;
|
|
}
|
|
|
|
int lightIntensity;
|
|
if (data > 50) {
|
|
lightIntensity = dword_453F90[1] + data * (dword_453F90[2] - dword_453F90[1]) / 100;
|
|
} else {
|
|
lightIntensity = dword_453F90[0] + data * (dword_453F90[1] - dword_453F90[0]) / 100;
|
|
}
|
|
|
|
lightSetLightLevel(lightIntensity, true);
|
|
}
|
|
|
|
// game_time
|
|
// 0x4579F4
|
|
void opGetGameTime(Program* program)
|
|
{
|
|
int time = gameTimeGetTime();
|
|
programStackPushInt32(program, time);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// game_time_in_seconds
|
|
// 0x457A18
|
|
void opGetGameTimeInSeconds(Program* program)
|
|
{
|
|
int time = gameTimeGetTime();
|
|
programStackPushInt32(program, time / 10);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// elevation
|
|
// 0x457A44
|
|
void opGetObjectElevation(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to elevation", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
int elevation = 0;
|
|
if (object != NULL) {
|
|
elevation = object->elevation;
|
|
} else {
|
|
scriptPredefinedError(program, "elevation", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, elevation);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// kill_critter
|
|
// 0x457AD4
|
|
void opKillCritter(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to kill_critter", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int deathFrame = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "kill_critter", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (_isLoadingGame()) {
|
|
debugPrint("\nError: attempt to destroy critter in load/save-game: %s!", program->name);
|
|
}
|
|
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
Object* self = scriptGetSelf(program);
|
|
bool isSelf = self == object;
|
|
|
|
reg_anim_clear(object);
|
|
_combat_delete_critter(object);
|
|
critterKill(object, deathFrame, 1);
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
|
|
if (isSelf) {
|
|
program->flags |= PROGRAM_FLAG_0x0100;
|
|
}
|
|
}
|
|
|
|
// [forceBack] is to force fall back animation, otherwise it's fall front if it's present
|
|
int _correctDeath(Object* critter, int anim, bool forceBack)
|
|
{
|
|
if (anim >= ANIM_BIG_HOLE_SF && anim <= ANIM_FALL_FRONT_BLOOD_SF) {
|
|
int violenceLevel = VIOLENCE_LEVEL_MAXIMUM_BLOOD;
|
|
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &violenceLevel);
|
|
|
|
bool useStandardDeath = false;
|
|
if (violenceLevel < VIOLENCE_LEVEL_MAXIMUM_BLOOD) {
|
|
useStandardDeath = true;
|
|
} else {
|
|
int fid = buildFid(1, critter->fid & 0xFFF, anim, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
|
|
if (!artExists(fid)) {
|
|
useStandardDeath = true;
|
|
}
|
|
}
|
|
|
|
if (useStandardDeath) {
|
|
if (forceBack) {
|
|
anim = ANIM_FALL_BACK;
|
|
} else {
|
|
int fid = buildFid(1, critter->fid & 0xFFF, ANIM_FALL_FRONT, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
|
|
if (artExists(fid)) {
|
|
anim = ANIM_FALL_FRONT;
|
|
} else {
|
|
anim = ANIM_FALL_BACK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return anim;
|
|
}
|
|
|
|
// kill_critter_type
|
|
// 0x457CB4
|
|
void opKillCritterType(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to kill_critter", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int pid = data[1];
|
|
int deathFrame = data[0];
|
|
|
|
if (_isLoadingGame()) {
|
|
debugPrint("\nError: attempt to destroy critter in load/save-game: %s!", program->name);
|
|
return;
|
|
}
|
|
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
Object* previousObj = NULL;
|
|
int count = 0;
|
|
int v3 = 0;
|
|
|
|
Object* obj = objectFindFirst();
|
|
while (obj != NULL) {
|
|
if (((obj->fid & 0xFF0000) >> 16) >= ANIM_FALL_BACK_SF) {
|
|
obj = objectFindNext();
|
|
continue;
|
|
}
|
|
|
|
if ((obj->flags & OBJECT_HIDDEN) == 0 && obj->pid == pid && !critterIsDead(obj)) {
|
|
if (obj == previousObj || count > 200) {
|
|
scriptPredefinedError(program, "kill_critter_type", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Infinite loop destroying critters!");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
reg_anim_clear(obj);
|
|
|
|
if (deathFrame != 0) {
|
|
_combat_delete_critter(obj);
|
|
if (deathFrame == 1) {
|
|
int anim = _correctDeath(obj, _ftList[v3], 1);
|
|
critterKill(obj, anim, 1);
|
|
v3 += 1;
|
|
if (v3 >= 11) {
|
|
v3 = 0;
|
|
}
|
|
} else {
|
|
critterKill(obj, ANIM_FALL_BACK_SF, 1);
|
|
}
|
|
} else {
|
|
reg_anim_clear(obj);
|
|
|
|
Rect rect;
|
|
objectDestroy(obj, &rect);
|
|
tileWindowRefreshRect(&rect, gElevation);
|
|
}
|
|
|
|
previousObj = obj;
|
|
count += 1;
|
|
|
|
objectFindFirst();
|
|
|
|
gMapHeader.field_38 = gameTimeGetTime();
|
|
}
|
|
|
|
obj = objectFindNext();
|
|
}
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// critter_dmg
|
|
// 0x457EB4
|
|
void opCritterDamage(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_damage", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int amount = data[1];
|
|
int damageTypeWithFlags = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "critter_damage", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if ((object->pid >> 24) != OBJ_TYPE_CRITTER) {
|
|
scriptPredefinedError(program, "critter_damage", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Can't call on non-critters!");
|
|
return;
|
|
}
|
|
|
|
Object* self = scriptGetSelf(program);
|
|
if (object->data.critter.combat.whoHitMeCid == -1) {
|
|
object->data.critter.combat.whoHitMe = NULL;
|
|
}
|
|
|
|
bool animate = (damageTypeWithFlags & 0x200) == 0;
|
|
bool bypassArmor = (damageTypeWithFlags & 0x100) != 0;
|
|
int damageType = damageTypeWithFlags & ~(0x100 | 0x200);
|
|
_action_dmg(object->tile, object->elevation, amount, amount, damageType, animate, bypassArmor);
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
|
|
if (self == object) {
|
|
program->flags |= PROGRAM_FLAG_0x0100;
|
|
}
|
|
}
|
|
|
|
// add_timer_event
|
|
// 0x457FF0
|
|
void opAddTimerEvent(Program* s)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(s);
|
|
data[arg] = programStackPopInt32(s);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(s, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to add_timer_event", s->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int delay = data[1];
|
|
int param = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptError("\nScript Error: %s: op_add_timer_event: pobj is NULL!", s->name);
|
|
return;
|
|
}
|
|
|
|
scriptAddTimerEvent(object->sid, delay, param);
|
|
}
|
|
|
|
// rm_timer_event
|
|
// 0x458094
|
|
void opRemoveTimerEvent(Program* program)
|
|
{
|
|
int elevation;
|
|
|
|
elevation = 0;
|
|
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to rm_timer_event", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object == NULL) {
|
|
// FIXME: Should be op_rm_timer_event.
|
|
scriptError("\nScript Error: %s: op_add_timer_event: pobj is NULL!");
|
|
return;
|
|
}
|
|
|
|
queueRemoveEvents(object);
|
|
}
|
|
|
|
// Converts seconds into game ticks.
|
|
//
|
|
// game_ticks
|
|
// 0x458108
|
|
void opGameTicks(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to game_ticks", program->name);
|
|
}
|
|
|
|
int ticks = data;
|
|
|
|
if (ticks < 0) {
|
|
ticks = 0;
|
|
}
|
|
|
|
programStackPushInt32(program, ticks * 10);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// NOTE: The name of this function is misleading. It has (almost) nothing to do
|
|
// with player's "Traits" as a feature. Instead it's used to query many
|
|
// information of the critters using passed parameters. It's like "metarule" but
|
|
// for critters.
|
|
//
|
|
// 0x458180
|
|
// has_trait
|
|
void opHasTrait(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to has_trait", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int type = data[2];
|
|
Object* object = (Object*)data[1];
|
|
int param = data[0];
|
|
|
|
int result = 0;
|
|
|
|
if (object != NULL) {
|
|
switch (type) {
|
|
case CRITTER_TRAIT_PERK:
|
|
if (param < PERK_COUNT) {
|
|
result = perkGetRank(object, param);
|
|
} else {
|
|
scriptError("\nScript Error: %s: op_has_trait: Perk out of range", program->name);
|
|
}
|
|
break;
|
|
case CRITTER_TRAIT_OBJECT:
|
|
switch (param) {
|
|
case CRITTER_TRAIT_OBJECT_AI_PACKET:
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
result = object->data.critter.combat.aiPacket;
|
|
}
|
|
break;
|
|
case CRITTER_TRAIT_OBJECT_TEAM:
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
result = object->data.critter.combat.team;
|
|
}
|
|
break;
|
|
case CRITTER_TRAIT_OBJECT_ROTATION:
|
|
result = object->rotation;
|
|
break;
|
|
case CRITTER_TRAIT_OBJECT_IS_INVISIBLE:
|
|
result = (object->flags & OBJECT_HIDDEN) == 0;
|
|
break;
|
|
case CRITTER_TRAIT_OBJECT_GET_INVENTORY_WEIGHT:
|
|
result = objectGetInventoryWeight(object);
|
|
break;
|
|
}
|
|
break;
|
|
case CRITTER_TRAIT_TRAIT:
|
|
if (param < TRAIT_COUNT) {
|
|
result = traitIsSelected(param);
|
|
} else {
|
|
scriptError("\nScript Error: %s: op_has_trait: Trait out of range", program->name);
|
|
}
|
|
break;
|
|
default:
|
|
scriptError("\nScript Error: %s: op_has_trait: Trait out of range", program->name);
|
|
break;
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "has_trait", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_can_hear_obj
|
|
// 0x45835C
|
|
void opObjectCanHearObject(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d, to obj_can_hear_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object1 = (Object*)data[1];
|
|
Object* object2 = (Object*)data[0];
|
|
|
|
bool canHear = false;
|
|
|
|
// FIXME: This is clearly an error. If any of the object is NULL
|
|
// dereferencing will crash the game.
|
|
if (object2 == NULL || object1 == NULL) {
|
|
if (object2->elevation == object1->elevation) {
|
|
if (object2->tile != -1 && object1->tile != -1) {
|
|
if (objectCanHearObject(object2, object1)) {
|
|
canHear = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
programStackPushInt32(program, canHear);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// game_time_hour
|
|
// 0x458438
|
|
void opGameTimeHour(Program* program)
|
|
{
|
|
int value = gameTimeGetHour();
|
|
programStackPushInt32(program, value);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// fixed_param
|
|
// 0x45845C
|
|
void opGetFixedParam(Program* program)
|
|
{
|
|
int fixedParam = 0;
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
fixedParam = script->fixedParam;
|
|
} else {
|
|
scriptPredefinedError(program, "fixed_param", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
|
|
programStackPushInt32(program, fixedParam);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// tile_is_visible
|
|
// 0x4584B0
|
|
void opTileIsVisible(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to tile_is_visible", program->name);
|
|
}
|
|
|
|
int isVisible = 0;
|
|
if (tileIsVisible(data)) {
|
|
isVisible = 1;
|
|
}
|
|
|
|
programStackPushInt32(program, isVisible);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// dialogue_system_enter
|
|
// 0x458534
|
|
void opGameDialogSystemEnter(Program* program)
|
|
{
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) == -1) {
|
|
return;
|
|
}
|
|
|
|
Object* self = scriptGetSelf(program);
|
|
if ((self->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
if (!critterIsActive(self)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (isInCombat()) {
|
|
return;
|
|
}
|
|
|
|
if (_game_state_request(4) == -1) {
|
|
return;
|
|
}
|
|
|
|
gGameDialogSpeaker = scriptGetSelf(program);
|
|
}
|
|
|
|
// action_being_used
|
|
// 0x458594
|
|
void opGetActionBeingUsed(Program* program)
|
|
{
|
|
int action = -1;
|
|
|
|
int sid = scriptGetSid(program);
|
|
|
|
Script* script;
|
|
if (scriptGetScript(sid, &script) != -1) {
|
|
action = script->actionBeingUsed;
|
|
} else {
|
|
scriptPredefinedError(program, "action_being_used", SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID);
|
|
}
|
|
|
|
programStackPushInt32(program, action);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// critter_state
|
|
// 0x4585E8
|
|
void opGetCritterState(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to critter_state", program->name);
|
|
}
|
|
|
|
Object* critter = (Object*)data;
|
|
|
|
int state = CRITTER_STATE_DEAD;
|
|
if (critter != NULL && (critter->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
if (critterIsActive(critter)) {
|
|
state = CRITTER_STATE_NORMAL;
|
|
|
|
int anim = (critter->fid & 0xFF0000) >> 16;
|
|
if (anim >= ANIM_FALL_BACK_SF && anim <= ANIM_FALL_FRONT_SF) {
|
|
state = CRITTER_STATE_PRONE;
|
|
}
|
|
|
|
state |= (critter->data.critter.combat.results & DAM_CRIP);
|
|
} else {
|
|
if (!critterIsDead(critter)) {
|
|
state = CRITTER_STATE_PRONE;
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "critter_state", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, state);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// game_time_advance
|
|
// 0x4586C8
|
|
void opGameTimeAdvance(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to game_time_advance", program->name);
|
|
}
|
|
|
|
int days = data / GAME_TIME_TICKS_PER_DAY;
|
|
int remainder = data % GAME_TIME_TICKS_PER_DAY;
|
|
|
|
for (int day = 0; day < days; day++) {
|
|
gameTimeAddTicks(GAME_TIME_TICKS_PER_DAY);
|
|
queueProcessEvents();
|
|
}
|
|
|
|
gameTimeAddTicks(remainder);
|
|
queueProcessEvents();
|
|
}
|
|
|
|
// radiation_inc
|
|
// 0x458760
|
|
void opRadiationIncrease(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to radiation_inc", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int amount = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "radiation_inc", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
critterAdjustRadiation(object, amount);
|
|
}
|
|
|
|
// radiation_dec
|
|
// 0x458800
|
|
void opRadiationDecrease(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to radiation_dec", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int amount = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "radiation_dec", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
int radiation = critterGetRadiation(object);
|
|
int adjustment = radiation >= 0 ? -amount : 0;
|
|
|
|
critterAdjustRadiation(object, adjustment);
|
|
}
|
|
|
|
// critter_attempt_placement
|
|
// 0x4588B4
|
|
void opCritterAttemptPlacement(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_attempt_placement", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* critter = (Object*)data[2];
|
|
int tile = data[1];
|
|
int elevation = data[0];
|
|
|
|
if (critter == NULL) {
|
|
scriptPredefinedError(program, "critter_attempt_placement", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (elevation != critter->elevation && critter->pid >> 24 == OBJ_TYPE_CRITTER) {
|
|
_combat_delete_critter(critter);
|
|
}
|
|
|
|
objectSetLocation(critter, 0, elevation, NULL);
|
|
|
|
int rc = _obj_attempt_placement(critter, tile, elevation, 1);
|
|
programStackPushInt32(program, rc);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_pid
|
|
// 0x4589A0
|
|
void opGetObjectPid(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_pid", program->name);
|
|
}
|
|
|
|
Object* obj = (Object*)data;
|
|
|
|
int pid = -1;
|
|
if (obj) {
|
|
pid = obj->pid;
|
|
} else {
|
|
scriptPredefinedError(program, "obj_pid", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, pid);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// cur_map_index
|
|
// 0x458A30
|
|
void opGetCurrentMap(Program* program)
|
|
{
|
|
int mapIndex = mapGetCurrentMap();
|
|
programStackPushInt32(program, mapIndex);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// critter_add_trait
|
|
// 0x458A54
|
|
void opCritterAddTrait(Program* program)
|
|
{
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_add_trait", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[3];
|
|
int kind = data[2];
|
|
int param = data[1];
|
|
int value = data[0];
|
|
|
|
if (object != NULL) {
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
switch (kind) {
|
|
case CRITTER_TRAIT_PERK:
|
|
if (1) {
|
|
char* critterName = critterGetName(object);
|
|
char* perkName = perkGetName(param);
|
|
debugPrint("\nintextra::critter_add_trait: Adding Perk %s to %s", perkName, critterName);
|
|
|
|
if (value > 0) {
|
|
if (perkAddForce(object, param) != 0) {
|
|
scriptError("\nScript Error: %s: op_critter_add_trait: perk_add_force failed", program->name);
|
|
debugPrint("Perk: %d", param);
|
|
}
|
|
} else {
|
|
if (perkRemove(object, param) != 0) {
|
|
// FIXME: typo in debug message, should be perk_sub
|
|
scriptError("\nScript Error: %s: op_critter_add_trait: per_sub failed", program->name);
|
|
debugPrint("Perk: %d", param);
|
|
}
|
|
}
|
|
|
|
if (object == gDude) {
|
|
interfaceRenderHitPoints(true);
|
|
}
|
|
}
|
|
break;
|
|
case CRITTER_TRAIT_OBJECT:
|
|
switch (param) {
|
|
case CRITTER_TRAIT_OBJECT_AI_PACKET:
|
|
critterSetAiPacket(object, value);
|
|
break;
|
|
case CRITTER_TRAIT_OBJECT_TEAM:
|
|
if (objectIsPartyMember(object)) {
|
|
break;
|
|
}
|
|
|
|
if (object->data.critter.combat.team == value) {
|
|
break;
|
|
}
|
|
|
|
if (_isLoadingGame()) {
|
|
break;
|
|
}
|
|
|
|
critterSetTeam(object, value);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
scriptError("\nScript Error: %s: op_critter_add_trait: Trait out of range", program->name);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "critter_add_trait", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, -1);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// critter_rm_trait
|
|
// 0x458C2C
|
|
void opCritterRemoveTrait(Program* program)
|
|
{
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_rm_trait", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[3];
|
|
int kind = data[2];
|
|
int param = data[1];
|
|
int value = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "critter_rm_trait", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
// FIXME: Ruins stack.
|
|
return;
|
|
}
|
|
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
switch (kind) {
|
|
case CRITTER_TRAIT_PERK:
|
|
while (perkGetRank(object, param) > 0) {
|
|
if (perkRemove(object, param) != 0) {
|
|
scriptError("\nScript Error: op_critter_rm_trait: perk_sub failed");
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
scriptError("\nScript Error: %s: op_critter_rm_trait: Trait out of range", program->name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
programStackPushInt32(program, -1);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// proto_data
|
|
// 0x458D38
|
|
void opGetProtoData(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to proto_data", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int pid = data[1];
|
|
int member = data[0];
|
|
|
|
ProtoDataMemberValue value;
|
|
value.integerValue = 0;
|
|
int valueType = protoGetDataMember(pid, member, &value);
|
|
switch (valueType) {
|
|
case PROTO_DATA_MEMBER_TYPE_INT:
|
|
programStackPushInt32(program, value.integerValue);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
break;
|
|
case PROTO_DATA_MEMBER_TYPE_STRING:
|
|
programStackPushInt32(program, programPushString(program, value.stringValue));
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
break;
|
|
default:
|
|
programStackPushInt32(program, 0);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// message_str
|
|
// 0x458E10
|
|
void opGetMessageString(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to message_str", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int messageListIndex = data[1];
|
|
int messageIndex = data[0];
|
|
|
|
char* string;
|
|
if (messageIndex >= 1) {
|
|
string = _scr_get_msg_str_speech(messageListIndex, messageIndex, 1);
|
|
if (string == NULL) {
|
|
debugPrint("\nError: No message file EXISTS!: index %d, line %d", messageListIndex, messageIndex);
|
|
string = _errStr;
|
|
}
|
|
} else {
|
|
string = _errStr;
|
|
}
|
|
|
|
programStackPushInt32(program, programPushString(program, string));
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// critter_inven_obj
|
|
// 0x458F00
|
|
void opCritterGetInventoryObject(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_inven_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* critter = (Object*)data[1];
|
|
int type = data[0];
|
|
|
|
int result = 0;
|
|
|
|
if ((critter->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
switch (type) {
|
|
case INVEN_TYPE_WORN:
|
|
result = (int)critterGetArmor(critter);
|
|
break;
|
|
case INVEN_TYPE_RIGHT_HAND:
|
|
if (critter == gDude) {
|
|
if (interfaceGetCurrentHand() != HAND_LEFT) {
|
|
result = (int)critterGetItem2(critter);
|
|
}
|
|
} else {
|
|
result = (int)critterGetItem2(critter);
|
|
}
|
|
break;
|
|
case INVEN_TYPE_LEFT_HAND:
|
|
if (critter == gDude) {
|
|
if (interfaceGetCurrentHand() == HAND_LEFT) {
|
|
result = (int)critterGetItem1(critter);
|
|
}
|
|
} else {
|
|
result = (int)critterGetItem1(critter);
|
|
}
|
|
break;
|
|
case INVEN_TYPE_INV_COUNT:
|
|
result = critter->data.inventory.length;
|
|
break;
|
|
default:
|
|
scriptError("script error: %s: Error in critter_inven_obj -- wrong type!", program->name);
|
|
break;
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "critter_inven_obj", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Not a critter!");
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_set_light_level
|
|
// 0x459088
|
|
void opSetObjectLightLevel(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to obj_set_light_level", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int lightIntensity = data[1];
|
|
int lightDistance = data[0];
|
|
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "obj_set_light_level", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
Rect rect;
|
|
if (lightIntensity != 0) {
|
|
if (objectSetLight(object, lightDistance, (lightIntensity * 65636) / 100, &rect) == -1) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (objectSetLight(object, lightDistance, 0, &rect) == -1) {
|
|
return;
|
|
}
|
|
}
|
|
tileWindowRefreshRect(&rect, object->elevation);
|
|
}
|
|
|
|
// 0x459170
|
|
void opWorldmap(Program* program)
|
|
{
|
|
scriptsRequestWorldMap();
|
|
}
|
|
|
|
// inven_cmds
|
|
// 0x459178
|
|
void _op_inven_cmds(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to inven_cmds", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[2];
|
|
int cmd = data[1];
|
|
int index = data[0];
|
|
|
|
Object* item = NULL;
|
|
|
|
if (obj != NULL) {
|
|
switch (cmd) {
|
|
case 13:
|
|
item = _inven_index_ptr(obj, index);
|
|
break;
|
|
}
|
|
} else {
|
|
// FIXME: Should be inven_cmds.
|
|
scriptPredefinedError(program, "anim", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, (int)item);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// float_msg
|
|
// 0x459280
|
|
void opFloatMessage(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
char* string = NULL;
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if (arg == 1) {
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
string = programGetString(program, opcode[arg], data[arg]);
|
|
}
|
|
} else {
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to float_msg", program->name, arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[2];
|
|
int floatingMessageType = data[0];
|
|
|
|
int color = _colorTable[32747];
|
|
int a5 = _colorTable[0];
|
|
int font = 101;
|
|
|
|
if (obj == NULL) {
|
|
scriptPredefinedError(program, "float_msg", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (string == NULL || *string == '\0') {
|
|
textObjectsRemoveByOwner(obj);
|
|
tileWindowRefresh();
|
|
return;
|
|
}
|
|
|
|
if (obj->elevation != gElevation) {
|
|
return;
|
|
}
|
|
|
|
if (floatingMessageType == FLOATING_MESSAGE_TYPE_COLOR_SEQUENCE) {
|
|
floatingMessageType = _last_color + 1;
|
|
if (floatingMessageType >= FLOATING_MESSAGE_TYPE_COUNT) {
|
|
floatingMessageType = FLOATING_MESSAGE_TYPE_BLACK;
|
|
}
|
|
_last_color = floatingMessageType;
|
|
}
|
|
|
|
switch (floatingMessageType) {
|
|
case FLOATING_MESSAGE_TYPE_WARNING:
|
|
color = _colorTable[31744];
|
|
a5 = _colorTable[0];
|
|
font = 103;
|
|
tileSetCenter(gDude->tile, TILE_SET_CENTER_FLAG_0x01);
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_NORMAL:
|
|
case FLOATING_MESSAGE_TYPE_YELLOW:
|
|
color = _colorTable[32747];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_BLACK:
|
|
case FLOATING_MESSAGE_TYPE_PURPLE:
|
|
case FLOATING_MESSAGE_TYPE_GREY:
|
|
color = _colorTable[10570];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_RED:
|
|
color = _colorTable[31744];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_GREEN:
|
|
color = _colorTable[992];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_BLUE:
|
|
color = _colorTable[31];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_NEAR_WHITE:
|
|
color = _colorTable[21140];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_LIGHT_RED:
|
|
color = _colorTable[32074];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_WHITE:
|
|
color = _colorTable[32767];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_DARK_GREY:
|
|
color = _colorTable[8456];
|
|
break;
|
|
case FLOATING_MESSAGE_TYPE_LIGHT_GREY:
|
|
color = _colorTable[15855];
|
|
break;
|
|
}
|
|
|
|
Rect rect;
|
|
if (textObjectAdd(obj, string, font, color, a5, &rect) != -1) {
|
|
tileWindowRefreshRect(&rect, obj->elevation);
|
|
}
|
|
}
|
|
|
|
// metarule
|
|
// 0x4594A0
|
|
void opMetarule(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to metarule", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int rule = data[1];
|
|
int param = data[0];
|
|
|
|
int result = 0;
|
|
|
|
switch (rule) {
|
|
case METARULE_SIGNAL_END_GAME:
|
|
result = 0;
|
|
_game_user_wants_to_quit = 2;
|
|
break;
|
|
case METARULE_FIRST_RUN:
|
|
result = (gMapHeader.flags & MAP_SAVED) == 0;
|
|
break;
|
|
case METARULE_ELEVATOR:
|
|
scriptsRequestElevator(scriptGetSelf(program), param);
|
|
result = 0;
|
|
break;
|
|
case METARULE_PARTY_COUNT:
|
|
result = _getPartyMemberCount();
|
|
break;
|
|
case METARULE_AREA_KNOWN:
|
|
result = _wmAreaVisitedState(param);
|
|
break;
|
|
case METARULE_WHO_ON_DRUGS:
|
|
result = queueHasEvent((Object*)param, EVENT_TYPE_DRUG);
|
|
break;
|
|
case METARULE_MAP_KNOWN:
|
|
result = _wmMapIsKnown(param);
|
|
break;
|
|
case METARULE_IS_LOADGAME:
|
|
result = _isLoadingGame();
|
|
break;
|
|
case METARULE_CAR_CURRENT_TOWN:
|
|
result = carGetCity();
|
|
break;
|
|
case METARULE_GIVE_CAR_TO_PARTY:
|
|
result = _wmCarGiveToParty();
|
|
break;
|
|
case METARULE_GIVE_CAR_GAS:
|
|
result = carAddFuel(param);
|
|
break;
|
|
case METARULE_SKILL_CHECK_TAG:
|
|
result = skillIsTagged(param);
|
|
break;
|
|
case METARULE_DROP_ALL_INVEN:
|
|
if (1) {
|
|
Object* object = (Object*)param;
|
|
result = _item_drop_all(object, object->tile);
|
|
if (gDude == object) {
|
|
interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
interfaceRenderArmorClass(false);
|
|
}
|
|
}
|
|
break;
|
|
case METARULE_INVEN_UNWIELD_WHO:
|
|
if (1) {
|
|
Object* object = (Object*)param;
|
|
|
|
int hand = HAND_RIGHT;
|
|
if (object == gDude) {
|
|
if (interfaceGetCurrentHand() == HAND_LEFT) {
|
|
hand = HAND_LEFT;
|
|
}
|
|
}
|
|
|
|
result = _invenUnwieldFunc(object, hand, 0);
|
|
|
|
if (object == gDude) {
|
|
bool animated = !gameUiIsDisabled();
|
|
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
} else {
|
|
Object* item = critterGetItem1(object);
|
|
if (itemGetType(item) == ITEM_TYPE_WEAPON) {
|
|
item->flags &= ~OBJECT_IN_LEFT_HAND;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case METARULE_GET_WORLDMAP_XPOS:
|
|
_wmGetPartyWorldPos(&result, NULL);
|
|
break;
|
|
case METARULE_GET_WORLDMAP_YPOS:
|
|
_wmGetPartyWorldPos(NULL, &result);
|
|
break;
|
|
case METARULE_CURRENT_TOWN:
|
|
if (_wmGetPartyCurArea(&result) == -1) {
|
|
debugPrint("\nIntextra: Error: metarule: current_town");
|
|
}
|
|
break;
|
|
case METARULE_LANGUAGE_FILTER:
|
|
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_LANGUAGE_FILTER_KEY, &result);
|
|
break;
|
|
case METARULE_VIOLENCE_FILTER:
|
|
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &result);
|
|
break;
|
|
case METARULE_WEAPON_DAMAGE_TYPE:
|
|
if (1) {
|
|
Object* object = (Object*)param;
|
|
if ((object->pid >> 24) == OBJ_TYPE_ITEM) {
|
|
if (itemGetType(object) == ITEM_TYPE_WEAPON) {
|
|
result = weaponGetDamageType(NULL, object);
|
|
break;
|
|
}
|
|
} else {
|
|
if (buildFid(5, 10, 0, 0, 0) == object->fid) {
|
|
result = DAMAGE_TYPE_EXPLOSION;
|
|
break;
|
|
}
|
|
}
|
|
|
|
scriptPredefinedError(program, "metarule:w_damage_type", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint("Not a weapon!");
|
|
}
|
|
break;
|
|
case METARULE_CRITTER_BARTERS:
|
|
if (1) {
|
|
Object* object = (Object*)param;
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
Proto* proto;
|
|
protoGetProto(object->pid, &proto);
|
|
if ((proto->critter.data.flags & 0x02) != 0) {
|
|
result = 1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case METARULE_CRITTER_KILL_TYPE:
|
|
result = critterGetKillType((Object*)param);
|
|
break;
|
|
case METARULE_SET_CAR_CARRY_AMOUNT:
|
|
if (1) {
|
|
Proto* proto;
|
|
if (protoGetProto(PROTO_ID_CAR_TRUNK, &proto) != -1) {
|
|
proto->item.data.container.maxSize = param;
|
|
result = 1;
|
|
}
|
|
}
|
|
break;
|
|
case METARULE_GET_CAR_CARRY_AMOUNT:
|
|
if (1) {
|
|
Proto* proto;
|
|
if (protoGetProto(PROTO_ID_CAR_TRUNK, &proto) != -1) {
|
|
result = proto->item.data.container.maxSize;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// anim
|
|
// 0x4598BC
|
|
void opAnim(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to anim", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[2];
|
|
int anim = data[1];
|
|
int frame = data[0];
|
|
|
|
if (obj == NULL) {
|
|
scriptPredefinedError(program, "anim", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (anim < ANIM_COUNT) {
|
|
CritterCombatData* combatData = NULL;
|
|
if ((obj->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
combatData = &(obj->data.critter.combat);
|
|
}
|
|
|
|
anim = _correctDeath(obj, anim, true);
|
|
|
|
reg_anim_begin(1);
|
|
|
|
// TODO: Not sure about the purpose, why it handles knock down flag?
|
|
if (frame == 0) {
|
|
reg_anim_animate(obj, anim, 0);
|
|
if (anim >= ANIM_FALL_BACK && anim <= ANIM_FALL_FRONT_BLOOD) {
|
|
int fid = buildFid(1, obj->fid & 0xFFF, anim + 28, (obj->fid & 0xF000) >> 12, (obj->fid & 0x70000000) >> 28);
|
|
reg_anim_17(obj, fid, -1);
|
|
}
|
|
|
|
if (combatData != NULL) {
|
|
combatData->results &= DAM_KNOCKED_DOWN;
|
|
}
|
|
} else {
|
|
int fid = buildFid((obj->fid & 0xF000000) >> 24, obj->fid & 0xFFF, anim, (obj->fid & 0xF000) >> 12, (obj->fid & 0x70000000) >> 24);
|
|
reg_anim_animate_reverse(obj, anim, 0);
|
|
|
|
if (anim == ANIM_PRONE_TO_STANDING) {
|
|
fid = buildFid((obj->fid & 0xF000000) >> 24, obj->fid & 0xFFF, ANIM_FALL_FRONT_SF, (obj->fid & 0xF000) >> 12, (obj->fid & 0x70000000) >> 24);
|
|
} else if (anim == ANIM_BACK_TO_STANDING) {
|
|
fid = buildFid((obj->fid & 0xF000000) >> 24, obj->fid & 0xFFF, ANIM_FALL_BACK_SF, (obj->fid & 0xF000) >> 12, (obj->fid & 0x70000000) >> 24);
|
|
}
|
|
|
|
if (combatData != NULL) {
|
|
combatData->results |= DAM_KNOCKED_DOWN;
|
|
}
|
|
|
|
reg_anim_17(obj, fid, -1);
|
|
}
|
|
|
|
reg_anim_end();
|
|
} else if (anim == 1000) {
|
|
if (frame < ROTATION_COUNT) {
|
|
Rect rect;
|
|
objectSetRotation(obj, frame, &rect);
|
|
tileWindowRefreshRect(&rect, gElevation);
|
|
}
|
|
} else if (anim == 1010) {
|
|
Rect rect;
|
|
objectSetFrame(obj, frame, &rect);
|
|
tileWindowRefreshRect(&rect, gElevation);
|
|
} else {
|
|
scriptError("\nScript Error: %s: op_anim: anim out of range", program->name);
|
|
}
|
|
}
|
|
|
|
// obj_carrying_pid_obj
|
|
// 0x459B5C
|
|
void opObjectCarryingObjectByPid(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to obj_carrying_pid_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int pid = data[0];
|
|
|
|
Object* result = NULL;
|
|
if (object != NULL) {
|
|
result = objectGetCarriedObjectByPid(object, pid);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_carrying_pid_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, (int)result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// reg_anim_func
|
|
// 0x459C20
|
|
void opRegAnimFunc(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_func", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int cmd = data[1];
|
|
int param = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
switch (cmd) {
|
|
case OP_REG_ANIM_FUNC_BEGIN:
|
|
reg_anim_begin(param);
|
|
break;
|
|
case OP_REG_ANIM_FUNC_CLEAR:
|
|
reg_anim_clear((Object*)param);
|
|
break;
|
|
case OP_REG_ANIM_FUNC_END:
|
|
reg_anim_end();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// reg_anim_animate
|
|
// 0x459CD4
|
|
void opRegAnimAnimate(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_animate", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int anim = data[1];
|
|
int delay = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
int violenceLevel = VIOLENCE_LEVEL_NONE;
|
|
if (anim != 20 || object == NULL || object->pid != 0x100002F || (configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &violenceLevel) && violenceLevel >= 2)) {
|
|
if (object != NULL) {
|
|
reg_anim_animate(object, anim, delay);
|
|
} else {
|
|
scriptPredefinedError(program, "reg_anim_animate", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// reg_anim_animate_reverse
|
|
// 0x459DC4
|
|
void opRegAnimAnimateReverse(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_animate_reverse", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int anim = data[1];
|
|
int delay = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
if (object != NULL) {
|
|
reg_anim_animate_reverse(object, anim, delay);
|
|
} else {
|
|
scriptPredefinedError(program, "reg_anim_animate_reverse", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// reg_anim_obj_move_to_obj
|
|
// 0x459E74
|
|
void opRegAnimObjectMoveToObject(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_obj_move_to_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
Object* dest = (Object*)data[1];
|
|
int delay = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
if (object != NULL) {
|
|
reg_anim_obj_move_to_obj(object, dest, -1, delay);
|
|
} else {
|
|
scriptPredefinedError(program, "reg_anim_obj_move_to_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// reg_anim_obj_run_to_obj
|
|
// 0x459F28
|
|
void opRegAnimObjectRunToObject(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_obj_run_to_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
Object* dest = (Object*)data[1];
|
|
int delay = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
if (object != NULL) {
|
|
reg_anim_obj_run_to_obj(object, dest, -1, delay);
|
|
} else {
|
|
scriptPredefinedError(program, "reg_anim_obj_run_to_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// reg_anim_obj_move_to_tile
|
|
// 0x459FDC
|
|
void opRegAnimObjectMoveToTile(Program* prg)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(prg);
|
|
data[arg] = programStackPopInt32(prg);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(prg, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_obj_move_to_tile", prg->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int tile = data[1];
|
|
int delay = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
if (object != NULL) {
|
|
reg_anim_obj_move_to_tile(object, tile, object->elevation, -1, delay);
|
|
} else {
|
|
scriptPredefinedError(prg, "reg_anim_obj_move_to_tile", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// reg_anim_obj_run_to_tile
|
|
// 0x45A094
|
|
void opRegAnimObjectRunToTile(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_obj_run_to_tile", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
int tile = data[1];
|
|
int delay = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
if (object != NULL) {
|
|
reg_anim_obj_run_to_tile(object, tile, object->elevation, -1, delay);
|
|
} else {
|
|
scriptPredefinedError(program, "reg_anim_obj_run_to_tile", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// play_gmovie
|
|
// 0x45A14C
|
|
void opPlayGameMovie(Program* program)
|
|
{
|
|
unsigned short flags[MOVIE_COUNT];
|
|
memcpy(flags, word_453F9C, sizeof(word_453F9C));
|
|
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to play_gmovie", program->name);
|
|
}
|
|
|
|
gameDialogDisable();
|
|
|
|
if (gameMoviePlay(data, word_453F9C[data]) == -1) {
|
|
debugPrint("\nError playing movie %d!", data);
|
|
}
|
|
|
|
gameDialogEnable();
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// add_mult_objs_to_inven
|
|
// 0x45A200
|
|
void opAddMultipleObjectsToInventory(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to add_mult_objs_to_inven", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[2];
|
|
Object* item = (Object*)data[1];
|
|
int quantity = data[0];
|
|
|
|
if (object == NULL || item == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (quantity < 0) {
|
|
quantity = 1;
|
|
} else if (quantity > 99999) {
|
|
quantity = 500;
|
|
}
|
|
|
|
if (itemAdd(object, item, quantity) == 0) {
|
|
Rect rect;
|
|
_obj_disconnect(item, &rect);
|
|
tileWindowRefreshRect(&rect, item->elevation);
|
|
}
|
|
}
|
|
|
|
// rm_mult_objs_from_inven
|
|
// 0x45A2D4
|
|
void opRemoveMultipleObjectsFromInventory(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to rm_mult_objs_from_inven", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* owner = (Object*)data[2];
|
|
Object* item = (Object*)data[1];
|
|
int quantityToRemove = data[0];
|
|
|
|
if (owner == NULL || item == NULL) {
|
|
// FIXME: Ruined stack.
|
|
return;
|
|
}
|
|
|
|
bool itemWasEquipped = (item->flags & OBJECT_EQUIPPED) != 0;
|
|
|
|
int quantity = _item_count(owner, item);
|
|
if (quantity > quantityToRemove) {
|
|
quantity = quantityToRemove;
|
|
}
|
|
|
|
if (quantity != 0) {
|
|
if (itemRemove(owner, item, quantity) == 0) {
|
|
Rect updatedRect;
|
|
_obj_connect(item, 1, 0, &updatedRect);
|
|
if (itemWasEquipped) {
|
|
if (owner == gDude) {
|
|
bool animated = !gameUiIsDisabled();
|
|
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
programStackPushInt32(program, quantity);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// get_month
|
|
// 0x45A40C
|
|
void opGetMonth(Program* program)
|
|
{
|
|
int month;
|
|
gameTimeGetDate(&month, NULL, NULL);
|
|
|
|
programStackPushInt32(program, month);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// get_day
|
|
// 0x45A43C
|
|
void opGetDay(Program* program)
|
|
{
|
|
int day;
|
|
gameTimeGetDate(NULL, &day, NULL);
|
|
|
|
programStackPushInt32(program, day);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// explosion
|
|
// 0x45A46C
|
|
void opExplosion(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to explosion", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int tile = data[2];
|
|
int elevation = data[1];
|
|
int maxDamage = data[0];
|
|
|
|
if (tile == -1) {
|
|
debugPrint("\nError: explosion: bad tile_num!");
|
|
return;
|
|
}
|
|
|
|
int minDamage = 1;
|
|
if (maxDamage == 0) {
|
|
minDamage = 0;
|
|
}
|
|
|
|
scriptsRequestExplosion(tile, elevation, minDamage, maxDamage);
|
|
}
|
|
|
|
// days_since_visited
|
|
// 0x45A528
|
|
void opGetDaysSinceLastVisit(Program* program)
|
|
{
|
|
int days;
|
|
|
|
if (gMapHeader.field_38 != 0) {
|
|
days = (gameTimeGetTime() - gMapHeader.field_38) / GAME_TIME_TICKS_PER_DAY;
|
|
} else {
|
|
days = -1;
|
|
}
|
|
|
|
programStackPushInt32(program, days);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// gsay_start
|
|
// 0x45A56C
|
|
void _op_gsay_start(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
if (_gdialogStart() != 0) {
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
programFatalError("Error starting dialog.");
|
|
}
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// gsay_end
|
|
// 0x45A5B0
|
|
void _op_gsay_end(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
_gdialogGo();
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// gsay_reply
|
|
// 0x45A5D4
|
|
void _op_gsay_reply(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
char* string = NULL;
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
if (arg == 0) {
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
string = programGetString(program, opcode[arg], data[arg]);
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to gsay_reply", program->name, arg);
|
|
}
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to gsay_reply", program->name, arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
int messageListId = data[1];
|
|
int messageId = data[0];
|
|
|
|
if (string != NULL) {
|
|
gameDialogSetTextReply(program, messageListId, string);
|
|
} else {
|
|
gameDialogSetMessageReply(program, messageListId, messageId);
|
|
}
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// gsay_option
|
|
// 0x45A6C4
|
|
void _op_gsay_option(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
// TODO: Original code is slightly different, does not use loop for first
|
|
// two args, but uses loop for two last args.
|
|
char* string = NULL;
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
if (arg == 2) {
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
string = programGetString(program, opcode[arg], data[arg]);
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to gsay_option", program->name, arg);
|
|
}
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to gsay_option", program->name, arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
int messageListId = data[3];
|
|
int messageId = data[2];
|
|
int proc = data[1];
|
|
int reaction = data[0];
|
|
|
|
// TODO: Not sure about this, needs testing.
|
|
if ((opcode[1] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
char* procName = programGetString(program, opcode[1], data[1]);
|
|
if (string != NULL) {
|
|
gameDialogAddTextOptionWithProcIdentifier(data[3], string, procName, reaction);
|
|
} else {
|
|
gameDialogAddMessageOptionWithProcIdentifier(data[3], data[2], procName, reaction);
|
|
}
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if ((opcode[1] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("Invalid arg 3 to sayOption");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if (string != NULL) {
|
|
gameDialogAddTextOptionWithProc(data[3], string, proc, reaction);
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
} else {
|
|
gameDialogAddMessageOptionWithProc(data[3], data[2], proc, reaction);
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
}
|
|
|
|
// gsay_message
|
|
// 0x45A8AC
|
|
void _op_gsay_message(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
char* string = NULL;
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
if (arg == 1) {
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
string = programGetString(program, opcode[arg], data[arg]);
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to gsay_message", program->name, arg);
|
|
}
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to gsay_message", program->name, arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
int messageListId = data[2];
|
|
int messageId = data[1];
|
|
int reaction = data[0];
|
|
|
|
if (string != NULL) {
|
|
gameDialogSetTextReply(program, messageListId, string);
|
|
} else {
|
|
gameDialogSetMessageReply(program, messageListId, messageId);
|
|
}
|
|
|
|
gameDialogAddMessageOptionWithProcIdentifier(-2, -2, NULL, 50);
|
|
_gdialogSayMessage();
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// giq_option
|
|
// 0x45A9B4
|
|
void _op_giq_option(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode[5];
|
|
int data[5];
|
|
|
|
char* string = NULL;
|
|
|
|
for (int arg = 0; arg < 5; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
if (arg == 2) {
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
string = programGetString(program, opcode[arg], data[arg]);
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to giq_option", program->name, arg);
|
|
}
|
|
} else {
|
|
programFatalError("script error: %s: invalid arg %d to giq_option", program->name, arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
int iq = data[4];
|
|
int messageListId = data[3];
|
|
int messageId = data[2];
|
|
int proc = data[1];
|
|
int reaction = data[0];
|
|
|
|
int intelligence = critterGetStat(gDude, STAT_INTELLIGENCE);
|
|
intelligence += perkGetRank(gDude, PERK_SMOOTH_TALKER);
|
|
|
|
if (iq < 0) {
|
|
if (-intelligence < iq) {
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
} else {
|
|
if (intelligence < iq) {
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((opcode[1] & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
|
char* procName = programGetString(program, opcode[1], data[1]);
|
|
if (string != NULL) {
|
|
gameDialogAddTextOptionWithProcIdentifier(messageListId, string, procName, reaction);
|
|
} else {
|
|
gameDialogAddMessageOptionWithProcIdentifier(messageListId, messageId, procName, reaction);
|
|
}
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if ((opcode[1] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("Invalid arg 4 to sayOption");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if (string != NULL) {
|
|
gameDialogAddTextOptionWithProc(messageListId, string, proc, reaction);
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
} else {
|
|
gameDialogAddMessageOptionWithProc(messageListId, messageId, proc, reaction);
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
}
|
|
|
|
// poison
|
|
// 0x45AB90
|
|
void opPoison(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to poison", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[1];
|
|
int amount = data[0];
|
|
|
|
if (obj == NULL) {
|
|
scriptPredefinedError(program, "poison", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (critterAdjustPoison(obj, amount) != 0) {
|
|
debugPrint("\nScript Error: poison: adjust failed!");
|
|
}
|
|
}
|
|
|
|
// get_poison
|
|
// 0x45AC44
|
|
void opGetPoison(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to get_poison", program->name);
|
|
}
|
|
|
|
Object* obj = (Object*)data;
|
|
|
|
int poison = 0;
|
|
if (obj != NULL) {
|
|
if (obj->pid >> 24 == 1) {
|
|
poison = critterGetPoison(obj);
|
|
} else {
|
|
debugPrint("\nScript Error: get_poison: who is not a critter!");
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "get_poison", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, poison);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// party_add
|
|
// 0x45ACF4
|
|
void opPartyAdd(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to party_add", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "party_add", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
partyMemberAdd(object);
|
|
}
|
|
|
|
// party_remove
|
|
// 0x45AD68
|
|
void opPartyRemove(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to party_remove", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
if (object == NULL) {
|
|
scriptPredefinedError(program, "party_remove", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
partyMemberRemove(object);
|
|
}
|
|
|
|
// reg_anim_animate_forever
|
|
// 0x45ADDC
|
|
void opRegAnimAnimateForever(Program* prg)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(prg);
|
|
data[arg] = programStackPopInt32(prg);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(prg, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_animate_forever", prg->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[1];
|
|
int anim = data[0];
|
|
|
|
if (!isInCombat()) {
|
|
if (obj != NULL) {
|
|
reg_anim_animate_forever(obj, anim, -1);
|
|
} else {
|
|
scriptPredefinedError(prg, "reg_anim_animate_forever", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// critter_injure
|
|
// 0x45AE8C
|
|
void opCritterInjure(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_injure", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* critter = (Object*)data[1];
|
|
int flags = data[0];
|
|
|
|
if (critter == NULL) {
|
|
scriptPredefinedError(program, "critter_injure", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
bool reverse = (flags & DAM_PERFORM_REVERSE) != 0;
|
|
|
|
flags &= DAM_CRIP;
|
|
|
|
if (reverse) {
|
|
critter->data.critter.combat.results &= ~flags;
|
|
} else {
|
|
critter->data.critter.combat.results |= flags;
|
|
}
|
|
|
|
if (critter == gDude) {
|
|
if ((flags & (DAM_CRIP_ARM_LEFT | DAM_CRIP_ARM_RIGHT)) != 0) {
|
|
int leftItemAction;
|
|
int rightItemAction;
|
|
interfaceGetItemActions(&leftItemAction, &rightItemAction);
|
|
interfaceUpdateItems(true, leftItemAction, rightItemAction);
|
|
}
|
|
}
|
|
}
|
|
|
|
// combat_is_initialized
|
|
// 0x45AF7C
|
|
void opCombatIsInitialized(Program* program)
|
|
{
|
|
programStackPushInt32(program, isInCombat());
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// gdialog_barter
|
|
// 0x45AFA0
|
|
void _op_gdialog_barter(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to gdialog_barter", program->name);
|
|
}
|
|
|
|
if (gameDialogBarter(data) == -1) {
|
|
debugPrint("\nScript Error: gdialog_barter: failed");
|
|
}
|
|
}
|
|
|
|
// difficulty_level
|
|
// 0x45B010
|
|
void opGetGameDifficulty(Program* program)
|
|
{
|
|
int gameDifficulty;
|
|
if (!configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_GAME_DIFFICULTY_KEY, &gameDifficulty)) {
|
|
gameDifficulty = GAME_DIFFICULTY_NORMAL;
|
|
}
|
|
|
|
programStackPushInt32(program, gameDifficulty);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// running_burning_guy
|
|
// 0x45B05C
|
|
void opGetRunningBurningGuy(Program* program)
|
|
{
|
|
int runningBurningGuy;
|
|
if (!configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_BURNING_GUY_KEY, &runningBurningGuy)) {
|
|
runningBurningGuy = 1;
|
|
}
|
|
|
|
programStackPushInt32(program, runningBurningGuy);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// inven_unwield
|
|
void _op_inven_unwield(Program* program)
|
|
{
|
|
Object* obj;
|
|
int v1;
|
|
|
|
obj = scriptGetSelf(program);
|
|
v1 = 1;
|
|
|
|
if (obj == gDude && !interfaceGetCurrentHand()) {
|
|
v1 = 0;
|
|
}
|
|
|
|
_inven_unwield(obj, v1);
|
|
}
|
|
|
|
// obj_is_locked
|
|
// 0x45B0D8
|
|
void opObjectIsLocked(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_is_locked", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
bool locked = false;
|
|
if (object != NULL) {
|
|
locked = objectIsLocked(object);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_is_locked", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, locked);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_lock
|
|
// 0x45B16C
|
|
void opObjectLock(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_lock", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object != NULL) {
|
|
objectLock(object);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_lock", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// obj_unlock
|
|
// 0x45B1E0
|
|
void opObjectUnlock(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_unlock", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object != NULL) {
|
|
objectUnlock(object);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_unlock", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// obj_is_open
|
|
// 0x45B254
|
|
void opObjectIsOpen(Program* s)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(s);
|
|
int data = programStackPopInt32(s);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(s, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_is_open", s->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
bool isOpen = false;
|
|
if (object != NULL) {
|
|
isOpen = objectIsOpen(object);
|
|
} else {
|
|
scriptPredefinedError(s, "obj_is_open", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(s, isOpen);
|
|
programStackPushInt16(s, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_open
|
|
// 0x45B2E8
|
|
void opObjectOpen(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_open", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object != NULL) {
|
|
objectOpen(object);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_open", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// obj_close
|
|
// 0x45B35C
|
|
void opObjectClose(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_close", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
if (object != NULL) {
|
|
objectClose(object);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_close", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// game_ui_disable
|
|
// 0x45B3D0
|
|
void opGameUiDisable(Program* program)
|
|
{
|
|
gameUiDisable(0);
|
|
}
|
|
|
|
// game_ui_enable
|
|
// 0x45B3D8
|
|
void opGameUiEnable(Program* program)
|
|
{
|
|
gameUiEnable();
|
|
}
|
|
|
|
// game_ui_is_disabled
|
|
// 0x45B3E0
|
|
void opGameUiIsDisabled(Program* program)
|
|
{
|
|
programStackPushInt32(program, gameUiIsDisabled());
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// gfade_out
|
|
// 0x45B404
|
|
void opFadeOut(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to gfade_out", program->name);
|
|
}
|
|
|
|
if (data != 0) {
|
|
paletteFadeTo(gPaletteBlack);
|
|
} else {
|
|
scriptPredefinedError(program, "gfade_out", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// gfade_in
|
|
// 0x45B47C
|
|
void opFadeIn(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to gfade_in", program->name);
|
|
}
|
|
|
|
if (data != 0) {
|
|
paletteFadeTo(_cmap);
|
|
} else {
|
|
scriptPredefinedError(program, "gfade_in", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// item_caps_total
|
|
// 0x45B4F4
|
|
void opItemCapsTotal(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to item_caps_total", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
int amount = 0;
|
|
if (object != NULL) {
|
|
amount = itemGetTotalCaps(object);
|
|
} else {
|
|
scriptPredefinedError(program, "item_caps_total", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, amount);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// item_caps_adjust
|
|
// 0x45B588
|
|
void opItemCapsAdjust(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to item_caps_adjust", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int amount = data[0];
|
|
|
|
int rc = -1;
|
|
|
|
if (object != NULL) {
|
|
rc = itemCapsAdjust(object, amount);
|
|
} else {
|
|
scriptPredefinedError(program, "item_caps_adjust", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, rc);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// anim_action_frame
|
|
void _op_anim_action_frame(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to anim_action_frame", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int anim = data[0];
|
|
|
|
int actionFrame = 0;
|
|
|
|
if (object != NULL) {
|
|
int fid = buildFid((object->fid & 0xF000000) >> 24, object->fid & 0xFFF, anim, 0, object->rotation);
|
|
CacheEntry* frmHandle;
|
|
Art* frm = artLock(fid, &frmHandle);
|
|
if (frm != NULL) {
|
|
actionFrame = artGetActionFrame(frm);
|
|
artUnlock(frmHandle);
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "anim_action_frame", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, actionFrame);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// reg_anim_play_sfx
|
|
// 0x45B740
|
|
void opRegAnimPlaySfx(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if (arg == 1) {
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_STRING) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_play_sfx", program->name, arg);
|
|
}
|
|
} else {
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to reg_anim_play_sfx", program->name, arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[2];
|
|
int name = data[1];
|
|
int delay = data[0];
|
|
|
|
char* soundEffectName = programGetString(program, opcode[1], name);
|
|
if (soundEffectName == NULL) {
|
|
scriptPredefinedError(program, "reg_anim_play_sfx", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Can't match string!");
|
|
}
|
|
|
|
if (obj != NULL) {
|
|
reg_anim_play_sfx(obj, soundEffectName, delay);
|
|
} else {
|
|
scriptPredefinedError(program, "reg_anim_play_sfx", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// critter_mod_skill
|
|
// 0x45B840
|
|
void opCritterModifySkill(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_mod_skill", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* critter = (Object*)data[2];
|
|
int skill = data[1];
|
|
int points = data[0];
|
|
|
|
if (critter != NULL && points != 0) {
|
|
if (critter->pid >> 24 == OBJ_TYPE_CRITTER) {
|
|
if (critter == gDude) {
|
|
int normalizedPoints = abs(points);
|
|
if (skillIsTagged(skill)) {
|
|
// Halve number of skill points. Increment/decrement skill
|
|
// points routines handle that.
|
|
normalizedPoints /= 2;
|
|
}
|
|
|
|
if (points > 0) {
|
|
// Increment skill points one by one.
|
|
for (int it = 0; it < normalizedPoints; it++) {
|
|
skillAddForce(gDude, skill);
|
|
}
|
|
} else {
|
|
// Decrement skill points one by one.
|
|
for (int it = 0; it < normalizedPoints; it++) {
|
|
skillSubForce(gDude, skill);
|
|
}
|
|
}
|
|
|
|
// TODO: Checking for critter is dude twice probably means this
|
|
// is inlined function.
|
|
if (critter == gDude) {
|
|
int leftItemAction;
|
|
int rightItemAction;
|
|
interfaceGetItemActions(&leftItemAction, &rightItemAction);
|
|
interfaceUpdateItems(false, leftItemAction, rightItemAction);
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "critter_mod_skill", SCRIPT_ERROR_FOLLOWS);
|
|
debugPrint(" Can't modify anyone except obj_dude!");
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "critter_mod_skill", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, 0);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// sfx_build_char_name
|
|
// 0x45B9C4
|
|
void opSfxBuildCharName(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to sfx_build_char_name", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* obj = (Object*)data[2];
|
|
int anim = data[1];
|
|
int extra = data[0];
|
|
|
|
int stringOffset = 0;
|
|
|
|
if (obj != NULL) {
|
|
char soundEffectName[16];
|
|
strcpy(soundEffectName, sfxBuildCharName(obj, anim, extra));
|
|
stringOffset = programPushString(program, soundEffectName);
|
|
} else {
|
|
scriptPredefinedError(program, "sfx_build_char_name", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// sfx_build_ambient_name
|
|
// 0x45BAA8
|
|
void opSfxBuildAmbientName(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to sfx_build_ambient_name", program->name);
|
|
}
|
|
|
|
char* baseName = programGetString(program, opcode, data);
|
|
|
|
char soundEffectName[16];
|
|
strcpy(soundEffectName, gameSoundBuildAmbientSoundEffectName(baseName));
|
|
|
|
int stringOffset = programPushString(program, soundEffectName);
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// sfx_build_interface_name
|
|
// 0x45BB54
|
|
void opSfxBuildInterfaceName(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to sfx_build_interface_name", program->name);
|
|
}
|
|
|
|
char* baseName = programGetString(program, opcode, data);
|
|
|
|
char soundEffectName[16];
|
|
strcpy(soundEffectName, gameSoundBuildInterfaceName(baseName));
|
|
|
|
int stringOffset = programPushString(program, soundEffectName);
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// sfx_build_item_name
|
|
// 0x45BC00
|
|
void opSfxBuildItemName(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to sfx_build_item_name", program->name);
|
|
}
|
|
|
|
const char* baseName = programGetString(program, opcode, data);
|
|
|
|
char soundEffectName[16];
|
|
strcpy(soundEffectName, gameSoundBuildInterfaceName(baseName));
|
|
|
|
int stringOffset = programPushString(program, soundEffectName);
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// sfx_build_weapon_name
|
|
// 0x45BCAC
|
|
void opSfxBuildWeaponName(Program* program)
|
|
{
|
|
opcode_t opcode[4];
|
|
int data[4];
|
|
|
|
for (int arg = 0; arg < 4; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to sfx_build_weapon_name", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int weaponSfxType = data[3];
|
|
Object* weapon = (Object*)data[2];
|
|
int hitMode = data[1];
|
|
Object* target = (Object*)data[0];
|
|
|
|
char soundEffectName[16];
|
|
strcpy(soundEffectName, sfxBuildWeaponName(weaponSfxType, weapon, hitMode, target));
|
|
|
|
int stringOffset = programPushString(program, soundEffectName);
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// sfx_build_scenery_name
|
|
// 0x45BD7C
|
|
void opSfxBuildSceneryName(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to sfx_build_scenery_name", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int action = data[1];
|
|
int actionType = data[0];
|
|
|
|
char* baseName = programGetString(program, opcode[2], data[2]);
|
|
|
|
char soundEffectName[16];
|
|
strcpy(soundEffectName, sfxBuildSceneryName(actionType, action, baseName));
|
|
|
|
int stringOffset = programPushString(program, soundEffectName);
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// sfx_build_open_name
|
|
// 0x45BE58
|
|
void opSfxBuildOpenName(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to sfx_build_open_name", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int action = data[0];
|
|
|
|
int stringOffset = 0;
|
|
|
|
if (object != NULL) {
|
|
char soundEffectName[16];
|
|
strcpy(soundEffectName, sfxBuildOpenName(object, action));
|
|
|
|
stringOffset = programPushString(program, soundEffectName);
|
|
} else {
|
|
scriptPredefinedError(program, "sfx_build_open_name", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// attack_setup
|
|
// 0x45BF38
|
|
void opAttackSetup(Program* program)
|
|
{
|
|
opcode_t opcodes[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcodes[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcodes[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcodes[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcodes[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to attack_setup", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* attacker = (Object*)data[1];
|
|
Object* defender = (Object*)data[0];
|
|
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
if (attacker != NULL) {
|
|
if (!critterIsActive(attacker) || (attacker->flags & OBJECT_HIDDEN) != 0) {
|
|
debugPrint("\n But is already dead or invisible");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if (!critterIsActive(defender) || (defender->flags & OBJECT_HIDDEN) != 0) {
|
|
debugPrint("\n But target is already dead or invisible");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if ((defender->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) != 0) {
|
|
debugPrint("\n But target is AFRAID");
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
return;
|
|
}
|
|
|
|
if (isInCombat()) {
|
|
if ((attacker->data.critter.combat.maneuver & CRITTER_MANEUVER_0x01) == 0) {
|
|
attacker->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01;
|
|
attacker->data.critter.combat.whoHitMe = defender;
|
|
}
|
|
} else {
|
|
STRUCT_664980 attack;
|
|
attack.attacker = attacker;
|
|
attack.defender = defender;
|
|
attack.actionPointsBonus = 0;
|
|
attack.accuracyBonus = 0;
|
|
attack.damageBonus = 0;
|
|
attack.minDamage = 0;
|
|
attack.maxDamage = INT_MAX;
|
|
|
|
// FIXME: Something bad here, when attacker and defender are
|
|
// the same object, these objects are used as flags, which
|
|
// are later used in 0x422F3C as flags of defender.
|
|
if (data[1] == data[0]) {
|
|
attack.field_1C = 1;
|
|
attack.field_20 = data[1];
|
|
attack.field_24 = data[0];
|
|
} else {
|
|
attack.field_1C = 0;
|
|
}
|
|
|
|
scriptsRequestCombat(&attack);
|
|
}
|
|
}
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// destroy_mult_objs
|
|
// 0x45C0E8
|
|
void opDestroyMultipleObjects(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to destroy_mult_objs", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int quantity = data[0];
|
|
|
|
Object* self = scriptGetSelf(program);
|
|
bool isSelf = self == object;
|
|
|
|
int result = 0;
|
|
|
|
if ((object->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
_combat_delete_critter(object);
|
|
}
|
|
|
|
Object* owner = objectGetOwner(object);
|
|
if (owner != NULL) {
|
|
int quantityToDestroy = _item_count(owner, object);
|
|
if (quantityToDestroy > quantity) {
|
|
quantityToDestroy = quantity;
|
|
}
|
|
|
|
itemRemove(owner, object, quantityToDestroy);
|
|
|
|
if (owner == gDude) {
|
|
bool animated = !gameUiIsDisabled();
|
|
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
}
|
|
|
|
_obj_connect(object, 1, 0, NULL);
|
|
|
|
if (isSelf) {
|
|
object->sid = -1;
|
|
object->flags |= (OBJECT_HIDDEN | OBJECT_TEMPORARY);
|
|
} else {
|
|
reg_anim_clear(object);
|
|
objectDestroy(object, NULL);
|
|
}
|
|
|
|
result = quantityToDestroy;
|
|
} else {
|
|
reg_anim_clear(object);
|
|
|
|
Rect rect;
|
|
objectDestroy(object, &rect);
|
|
tileWindowRefreshRect(&rect, gElevation);
|
|
}
|
|
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
|
|
if (isSelf) {
|
|
program->flags |= PROGRAM_FLAG_0x0100;
|
|
}
|
|
}
|
|
|
|
// use_obj_on_obj
|
|
// 0x45C290
|
|
void opUseObjectOnObject(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to use_obj_on_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* item = (Object*)data[1];
|
|
Object* target = (Object*)data[0];
|
|
|
|
if (item == NULL) {
|
|
scriptPredefinedError(program, "use_obj_on_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (target == NULL) {
|
|
scriptPredefinedError(program, "use_obj_on_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
Script* script;
|
|
int sid = scriptGetSid(program);
|
|
if (scriptGetScript(sid, &script) == -1) {
|
|
// FIXME: Should be SCRIPT_ERROR_CANT_MATCH_PROGRAM_TO_SID.
|
|
scriptPredefinedError(program, "use_obj_on_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
Object* self = scriptGetSelf(program);
|
|
if ((self->pid >> 24) == OBJ_TYPE_CRITTER) {
|
|
_action_use_an_item_on_object(self, target, item);
|
|
} else {
|
|
_obj_use_item_on(self, target, item);
|
|
}
|
|
}
|
|
|
|
// endgame_slideshow
|
|
// 0x45C3B0
|
|
void opEndgameSlideshow(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
scriptsRequestEndgame();
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// move_obj_inven_to_obj
|
|
// 0x45C3D0
|
|
void opMoveObjectInventoryToObject(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to move_obj_inven_to_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object1 = (Object*)data[1];
|
|
Object* object2 = (Object*)data[0];
|
|
|
|
if (object1 == NULL) {
|
|
scriptPredefinedError(program, "move_obj_inven_to_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
if (object2 == NULL) {
|
|
scriptPredefinedError(program, "move_obj_inven_to_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
return;
|
|
}
|
|
|
|
Object* oldArmor = NULL;
|
|
Object* item2 = NULL;
|
|
if (object1 == gDude) {
|
|
oldArmor = critterGetArmor(object1);
|
|
} else {
|
|
item2 = critterGetItem2(object1);
|
|
}
|
|
|
|
if (object1 != gDude && item2 != NULL) {
|
|
int flags = 0;
|
|
if ((item2->flags & 0x01000000) != 0) {
|
|
flags |= 0x01000000;
|
|
}
|
|
|
|
if ((item2->flags & 0x02000000) != 0) {
|
|
flags |= 0x02000000;
|
|
}
|
|
|
|
_correctFidForRemovedItem(object1, item2, flags);
|
|
}
|
|
|
|
_item_move_all(object1, object2);
|
|
|
|
if (object1 == gDude) {
|
|
if (oldArmor != NULL) {
|
|
_adjust_ac(gDude, oldArmor, NULL);
|
|
}
|
|
|
|
_proto_dude_update_gender();
|
|
|
|
bool animated = !gameUiIsDisabled();
|
|
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
|
}
|
|
}
|
|
|
|
// endgame_movie
|
|
// 0x45C54C
|
|
void opEndgameMovie(Program* program)
|
|
{
|
|
program->flags |= PROGRAM_FLAG_0x20;
|
|
endgamePlayMovie();
|
|
program->flags &= ~PROGRAM_FLAG_0x20;
|
|
}
|
|
|
|
// obj_art_fid
|
|
// 0x45C56C
|
|
void opGetObjectFid(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_art_fid", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
int fid = 0;
|
|
if (object != NULL) {
|
|
fid = object->fid;
|
|
} else {
|
|
scriptPredefinedError(program, "obj_art_fid", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, fid);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// art_anim
|
|
// 0x45C5F8
|
|
void opGetFidAnim(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to art_anim", program->name);
|
|
}
|
|
|
|
programStackPushInt32(program, (data & 0xFF0000) >> 16);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// party_member_obj
|
|
// 0x45C66C
|
|
void opGetPartyMember(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to party_member_obj", program->name);
|
|
}
|
|
|
|
Object* object = partyMemberFindByPid(data);
|
|
programStackPushInt32(program, (int)object);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// rotation_to_tile
|
|
// 0x45C6DC
|
|
void opGetRotationToTile(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to rotation_to_tile", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int tile1 = data[1];
|
|
int tile2 = data[0];
|
|
|
|
int rotation = tileGetRotationTo(tile1, tile2);
|
|
programStackPushInt32(program, rotation);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// jam_lock
|
|
// 0x45C778
|
|
void opJamLock(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to jam_lock", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
objectJamLock(object);
|
|
}
|
|
|
|
// gdialog_set_barter_mod
|
|
// 0x45C7D4
|
|
void opGameDialogSetBarterMod(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to gdialog_set_barter_mod", program->name);
|
|
}
|
|
|
|
gameDialogSetBarterModifier(data);
|
|
}
|
|
|
|
// combat_difficulty
|
|
// 0x45C830
|
|
void opGetCombatDifficulty(Program* program)
|
|
{
|
|
int combatDifficulty;
|
|
if (!configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, &combatDifficulty)) {
|
|
combatDifficulty = 0;
|
|
}
|
|
|
|
programStackPushInt32(program, combatDifficulty);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_on_screen
|
|
// 0x45C878
|
|
void opObjectOnScreen(Program* program)
|
|
{
|
|
Rect rect;
|
|
rectCopy(&rect, &stru_453FC0);
|
|
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_on_screen", program->name);
|
|
}
|
|
|
|
Object* object = (Object*)data;
|
|
|
|
int result = 0;
|
|
|
|
if (object != NULL) {
|
|
if (gElevation == object->elevation) {
|
|
Rect objectRect;
|
|
objectGetRect(object, &objectRect);
|
|
|
|
if (rectIntersection(&objectRect, &rect, &objectRect) == 0) {
|
|
result = 1;
|
|
}
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "obj_on_screen", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
//debugPrint("ObjOnScreen: %d\n", result);
|
|
programStackPushInt32(program, result);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// critter_is_fleeing
|
|
// 0x45C93C
|
|
void opCritterIsFleeing(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to critter_is_fleeing", program->name);
|
|
}
|
|
|
|
Object* obj = (Object*)data;
|
|
|
|
bool fleeing = false;
|
|
if (obj != NULL) {
|
|
fleeing = (obj->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) != 0;
|
|
} else {
|
|
scriptPredefinedError(program, "critter_is_fleeing", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
programStackPushInt32(program, fleeing);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// critter_set_flee_state
|
|
// 0x45C9DC
|
|
void opCritterSetFleeState(Program* program)
|
|
{
|
|
opcode_t opcode[2];
|
|
int data[2];
|
|
|
|
for (int arg = 0; arg < 2; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to critter_set_flee_state", program->name, arg);
|
|
}
|
|
}
|
|
|
|
Object* object = (Object*)data[1];
|
|
int fleeing = data[0];
|
|
|
|
if (object != NULL) {
|
|
if (fleeing != 0) {
|
|
object->data.critter.combat.maneuver |= CRITTER_MANUEVER_FLEEING;
|
|
} else {
|
|
object->data.critter.combat.maneuver &= ~CRITTER_MANUEVER_FLEEING;
|
|
}
|
|
} else {
|
|
scriptPredefinedError(program, "critter_set_flee_state", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// terminate_combat
|
|
// 0x45CA84
|
|
void opTerminateCombat(Program* program)
|
|
{
|
|
if (isInCombat()) {
|
|
_game_user_wants_to_quit = 1;
|
|
Object* self = scriptGetSelf(program);
|
|
if (self != NULL) {
|
|
if ((self->pid >> 24) == 1) {
|
|
self->data.critter.combat.maneuver |= CRITTER_MANEUVER_STOP_ATTACKING;
|
|
self->data.critter.combat.whoHitMe = NULL;
|
|
_combatAIInfoSetLastTarget(self, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// debug_msg
|
|
// 0x45CAC8
|
|
void opDebugMessage(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_STRING) {
|
|
programFatalError("script error: %s: invalid arg to debug_msg", program->name);
|
|
}
|
|
|
|
char* string = programGetString(program, opcode, data);
|
|
|
|
if (string != NULL) {
|
|
bool showScriptMessages = false;
|
|
configGetBool(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, &showScriptMessages);
|
|
if (showScriptMessages) {
|
|
debugPrint("\n");
|
|
debugPrint(string);
|
|
}
|
|
}
|
|
}
|
|
|
|
// critter_stop_attacking
|
|
// 0x45CB70
|
|
void opCritterStopAttacking(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to critter_stop_attacking", program->name);
|
|
}
|
|
|
|
Object* obj = (Object*)data;
|
|
|
|
if (obj != NULL) {
|
|
obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_STOP_ATTACKING;
|
|
obj->data.critter.combat.whoHitMe = NULL;
|
|
_combatAIInfoSetLastTarget(obj, NULL);
|
|
} else {
|
|
scriptPredefinedError(program, "critter_stop_attacking", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
}
|
|
|
|
// tile_contains_pid_obj
|
|
// 0x45CBF8
|
|
void opTileGetObjectWithPid(Program* program)
|
|
{
|
|
opcode_t opcode[3];
|
|
int data[3];
|
|
|
|
for (int arg = 0; arg < 3; arg++) {
|
|
opcode[arg] = programStackPopInt16(program);
|
|
data[arg] = programStackPopInt32(program);
|
|
|
|
if (opcode[arg] == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode[arg], data[arg]);
|
|
}
|
|
|
|
if ((opcode[arg] & VALUE_TYPE_MASK) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg %d to tile_contains_pid_obj", program->name, arg);
|
|
}
|
|
}
|
|
|
|
int tile = data[2];
|
|
int elevation = data[1];
|
|
int pid = data[0];
|
|
Object* found = NULL;
|
|
|
|
if (tile != -1) {
|
|
Object* object = objectFindFirstAtLocation(elevation, tile);
|
|
while (object != NULL) {
|
|
if (object->pid == pid) {
|
|
found = object;
|
|
break;
|
|
}
|
|
object = objectFindNextAtLocation();
|
|
}
|
|
}
|
|
|
|
programStackPushInt32(program, (int)found);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// obj_name
|
|
// 0x45CCC8
|
|
void opGetObjectName(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to obj_name", program->name);
|
|
}
|
|
|
|
Object* obj = (Object*)data;
|
|
if (obj != NULL) {
|
|
_strName = objectGetName(obj);
|
|
} else {
|
|
scriptPredefinedError(program, "obj_name", SCRIPT_ERROR_OBJECT_IS_NULL);
|
|
}
|
|
|
|
int stringOffset = programPushString(program, _strName);
|
|
|
|
programStackPushInt32(program, stringOffset);
|
|
programStackPushInt16(program, VALUE_TYPE_DYNAMIC_STRING);
|
|
}
|
|
|
|
// get_pc_stat
|
|
// 0x45CD64
|
|
void opGetPcStat(Program* program)
|
|
{
|
|
opcode_t opcode = programStackPopInt16(program);
|
|
int data = programStackPopInt32(program);
|
|
|
|
if (opcode == VALUE_TYPE_DYNAMIC_STRING) {
|
|
programPopString(program, opcode, data);
|
|
}
|
|
|
|
if ((opcode & 0xF7FF) != VALUE_TYPE_INT) {
|
|
programFatalError("script error: %s: invalid arg to get_pc_stat", program->name);
|
|
}
|
|
|
|
int value = pcGetStat(data);
|
|
programStackPushInt32(program, value);
|
|
programStackPushInt16(program, VALUE_TYPE_INT);
|
|
}
|
|
|
|
// 0x45CDD4
|
|
void _intExtraClose_()
|
|
{
|
|
}
|
|
|
|
// 0x45CDD8
|
|
void _initIntExtra()
|
|
{
|
|
interpreterRegisterOpcode(0x80A1, opGiveExpPoints); // op_give_exp_points
|
|
interpreterRegisterOpcode(0x80A2, opScrReturn); // op_scr_return
|
|
interpreterRegisterOpcode(0x80A3, opPlaySfx); // op_play_sfx
|
|
interpreterRegisterOpcode(0x80A4, opGetObjectName); // op_obj_name
|
|
interpreterRegisterOpcode(0x80A5, opSfxBuildOpenName); // op_sfx_build_open_name
|
|
interpreterRegisterOpcode(0x80A6, opGetPcStat); // op_get_pc_stat
|
|
interpreterRegisterOpcode(0x80A7, opTileGetObjectWithPid); // op_tile_contains_pid_obj
|
|
interpreterRegisterOpcode(0x80A8, opSetMapStart); // op_set_map_start
|
|
interpreterRegisterOpcode(0x80A9, opOverrideMapStart); // op_override_map_start
|
|
interpreterRegisterOpcode(0x80AA, opHasSkill); // op_has_skill
|
|
interpreterRegisterOpcode(0x80AB, opUsingSkill); // op_using_skill
|
|
interpreterRegisterOpcode(0x80AC, opRollVsSkill); // op_roll_vs_skill
|
|
interpreterRegisterOpcode(0x80AD, opSkillContest); // op_skill_contest
|
|
interpreterRegisterOpcode(0x80AE, opDoCheck); // op_do_check
|
|
interpreterRegisterOpcode(0x80AF, opSuccess); // op_success
|
|
interpreterRegisterOpcode(0x80B0, opCritical); // op_critical
|
|
interpreterRegisterOpcode(0x80B1, opHowMuch); // op_how_much
|
|
interpreterRegisterOpcode(0x80B2, opMarkAreaKnown); // op_mark_area_known
|
|
interpreterRegisterOpcode(0x80B3, opReactionInfluence); // op_reaction_influence
|
|
interpreterRegisterOpcode(0x80B4, opRandom); // op_random
|
|
interpreterRegisterOpcode(0x80B5, opRollDice); // op_roll_dice
|
|
interpreterRegisterOpcode(0x80B6, opMoveTo); // op_move_to
|
|
interpreterRegisterOpcode(0x80B7, opCreateObject); // op_create_object
|
|
interpreterRegisterOpcode(0x80B8, opDisplayMsg); // op_display_msg
|
|
interpreterRegisterOpcode(0x80B9, opScriptOverrides); // op_script_overrides
|
|
interpreterRegisterOpcode(0x80BA, opObjectIsCarryingObjectWithPid); // op_obj_is_carrying_obj
|
|
interpreterRegisterOpcode(0x80BB, opTileContainsObjectWithPid); // op_tile_contains_obj_pid
|
|
interpreterRegisterOpcode(0x80BC, opGetSelf); // op_self_obj
|
|
interpreterRegisterOpcode(0x80BD, opGetSource); // op_source_obj
|
|
interpreterRegisterOpcode(0x80BE, opGetTarget); // op_target_obj
|
|
interpreterRegisterOpcode(0x80BF, opGetDude);
|
|
interpreterRegisterOpcode(0x80C0, opGetObjectBeingUsed); // op_obj_being_used_with
|
|
interpreterRegisterOpcode(0x80C1, opGetLocalVar); // op_get_local_var
|
|
interpreterRegisterOpcode(0x80C2, opSetLocalVar); // op_set_local_var
|
|
interpreterRegisterOpcode(0x80C3, opGetMapVar); // op_get_map_var
|
|
interpreterRegisterOpcode(0x80C4, opSetMapVar); // op_set_map_var
|
|
interpreterRegisterOpcode(0x80C5, opGetGlobalVar); // op_get_global_var
|
|
interpreterRegisterOpcode(0x80C6, opSetGlobalVar); // op_set_global_var
|
|
interpreterRegisterOpcode(0x80C7, opGetScriptAction); // op_script_action
|
|
interpreterRegisterOpcode(0x80C8, opGetObjectType); // op_obj_type
|
|
interpreterRegisterOpcode(0x80C9, opGetItemType); // op_item_subtype
|
|
interpreterRegisterOpcode(0x80CA, opGetCritterStat); // op_get_critter_stat
|
|
interpreterRegisterOpcode(0x80CB, opSetCritterStat); // op_set_critter_stat
|
|
interpreterRegisterOpcode(0x80CC, opAnimateStand); // op_animate_stand_obj
|
|
interpreterRegisterOpcode(0x80CD, opAnimateStandReverse); // animate_stand_reverse_obj
|
|
interpreterRegisterOpcode(0x80CE, opAnimateMoveObjectToTile); // animate_move_obj_to_tile
|
|
interpreterRegisterOpcode(0x80CF, opTileInTileRect); // tile_in_tile_rect
|
|
interpreterRegisterOpcode(0x80D0, opAttackComplex); // op_attack
|
|
interpreterRegisterOpcode(0x80D1, opMakeDayTime); // op_make_daytime
|
|
interpreterRegisterOpcode(0x80D2, opTileDistanceBetween); // op_tile_distance
|
|
interpreterRegisterOpcode(0x80D3, opTileDistanceBetweenObjects); // op_tile_distance_objs
|
|
interpreterRegisterOpcode(0x80D4, opGetObjectTile); // op_tile_num
|
|
interpreterRegisterOpcode(0x80D5, opGetTileInDirection); // op_tile_num_in_direction
|
|
interpreterRegisterOpcode(0x80D6, opPickup); // op_pickup_obj
|
|
interpreterRegisterOpcode(0x80D7, opDrop); // op_drop_obj
|
|
interpreterRegisterOpcode(0x80D8, opAddObjectToInventory); // op_add_obj_to_inven
|
|
interpreterRegisterOpcode(0x80D9, opRemoveObjectFromInventory); // op_rm_obj_from_inven
|
|
interpreterRegisterOpcode(0x80DA, opWieldItem); // op_wield_obj_critter
|
|
interpreterRegisterOpcode(0x80DB, opUseObject); // op_use_obj
|
|
interpreterRegisterOpcode(0x80DC, opObjectCanSeeObject); // op_obj_can_see_obj
|
|
interpreterRegisterOpcode(0x80DD, opAttackComplex); // op_attack
|
|
interpreterRegisterOpcode(0x80DE, opStartGameDialog); // op_start_gdialog
|
|
interpreterRegisterOpcode(0x80DF, opEndGameDialog); // op_end_gdialog
|
|
interpreterRegisterOpcode(0x80E0, opGameDialogReaction); // op_dialogue_reaction
|
|
interpreterRegisterOpcode(0x80E1, opMetarule3); // op_metarule3
|
|
interpreterRegisterOpcode(0x80E2, opSetMapMusic); // op_set_map_music
|
|
interpreterRegisterOpcode(0x80E3, opSetObjectVisibility); // op_set_obj_visibility
|
|
interpreterRegisterOpcode(0x80E4, opLoadMap); // op_load_map
|
|
interpreterRegisterOpcode(0x80E5, opWorldmapCitySetPos); // op_wm_area_set_pos
|
|
interpreterRegisterOpcode(0x80E6, opSetExitGrids); // op_set_exit_grids
|
|
interpreterRegisterOpcode(0x80E7, opAnimBusy); // op_anim_busy
|
|
interpreterRegisterOpcode(0x80E8, opCritterHeal); // op_critter_heal
|
|
interpreterRegisterOpcode(0x80E9, opSetLightLevel); // op_set_light_level
|
|
interpreterRegisterOpcode(0x80EA, opGetGameTime); // op_game_time
|
|
interpreterRegisterOpcode(0x80EB, opGetGameTimeInSeconds); // op_game_time / 10
|
|
interpreterRegisterOpcode(0x80EC, opGetObjectElevation); // op_elevation
|
|
interpreterRegisterOpcode(0x80ED, opKillCritter); // op_kill_critter
|
|
interpreterRegisterOpcode(0x80EE, opKillCritterType); // op_kill_critter_type
|
|
interpreterRegisterOpcode(0x80EF, opCritterDamage); // op_critter_damage
|
|
interpreterRegisterOpcode(0x80F0, opAddTimerEvent); // op_add_timer_event
|
|
interpreterRegisterOpcode(0x80F1, opRemoveTimerEvent); // op_rm_timer_event
|
|
interpreterRegisterOpcode(0x80F2, opGameTicks); // op_game_ticks
|
|
interpreterRegisterOpcode(0x80F3, opHasTrait); // op_has_trait
|
|
interpreterRegisterOpcode(0x80F4, opDestroyObject); // op_destroy_object
|
|
interpreterRegisterOpcode(0x80F5, opObjectCanHearObject); // op_obj_can_hear_obj
|
|
interpreterRegisterOpcode(0x80F6, opGameTimeHour); // op_game_time_hour
|
|
interpreterRegisterOpcode(0x80F7, opGetFixedParam); // op_fixed_param
|
|
interpreterRegisterOpcode(0x80F8, opTileIsVisible); // op_tile_is_visible
|
|
interpreterRegisterOpcode(0x80F9, opGameDialogSystemEnter); // op_dialogue_system_enter
|
|
interpreterRegisterOpcode(0x80FA, opGetActionBeingUsed); // op_action_being_used
|
|
interpreterRegisterOpcode(0x80FB, opGetCritterState); // critter_state
|
|
interpreterRegisterOpcode(0x80FC, opGameTimeAdvance); // op_game_time_advance
|
|
interpreterRegisterOpcode(0x80FD, opRadiationIncrease); // op_radiation_inc
|
|
interpreterRegisterOpcode(0x80FE, opRadiationDecrease); // op_radiation_dec
|
|
interpreterRegisterOpcode(0x80FF, opCritterAttemptPlacement); // critter_attempt_placement
|
|
interpreterRegisterOpcode(0x8100, opGetObjectPid); // op_obj_pid
|
|
interpreterRegisterOpcode(0x8101, opGetCurrentMap); // op_cur_map_index
|
|
interpreterRegisterOpcode(0x8102, opCritterAddTrait); // op_critter_add_trait
|
|
interpreterRegisterOpcode(0x8103, opCritterRemoveTrait); // critter_rm_trait
|
|
interpreterRegisterOpcode(0x8104, opGetProtoData); // op_proto_data
|
|
interpreterRegisterOpcode(0x8105, opGetMessageString); // op_message_str
|
|
interpreterRegisterOpcode(0x8106, opCritterGetInventoryObject); // op_critter_inven_obj
|
|
interpreterRegisterOpcode(0x8107, opSetObjectLightLevel); // op_obj_set_light_level
|
|
interpreterRegisterOpcode(0x8108, opWorldmap); // op_scripts_request_world_map
|
|
interpreterRegisterOpcode(0x8109, _op_inven_cmds); // op_inven_cmds
|
|
interpreterRegisterOpcode(0x810A, opFloatMessage); // op_float_msg
|
|
interpreterRegisterOpcode(0x810B, opMetarule); // op_metarule
|
|
interpreterRegisterOpcode(0x810C, opAnim); // op_anim
|
|
interpreterRegisterOpcode(0x810D, opObjectCarryingObjectByPid); // op_obj_carrying_pid_obj
|
|
interpreterRegisterOpcode(0x810E, opRegAnimFunc); // op_reg_anim_func
|
|
interpreterRegisterOpcode(0x810F, opRegAnimAnimate); // op_reg_anim_animate
|
|
interpreterRegisterOpcode(0x8110, opRegAnimAnimateReverse); // op_reg_anim_animate_reverse
|
|
interpreterRegisterOpcode(0x8111, opRegAnimObjectMoveToObject); // op_reg_anim_obj_move_to_obj
|
|
interpreterRegisterOpcode(0x8112, opRegAnimObjectRunToObject); // op_reg_anim_obj_run_to_obj
|
|
interpreterRegisterOpcode(0x8113, opRegAnimObjectMoveToTile); // op_reg_anim_obj_move_to_tile
|
|
interpreterRegisterOpcode(0x8114, opRegAnimObjectRunToTile); // op_reg_anim_obj_run_to_tile
|
|
interpreterRegisterOpcode(0x8115, opPlayGameMovie); // op_play_gmovie
|
|
interpreterRegisterOpcode(0x8116, opAddMultipleObjectsToInventory); // op_add_mult_objs_to_inven
|
|
interpreterRegisterOpcode(0x8117, opRemoveMultipleObjectsFromInventory); // rm_mult_objs_from_inven
|
|
interpreterRegisterOpcode(0x8118, opGetMonth); // op_month
|
|
interpreterRegisterOpcode(0x8119, opGetDay); // op_day
|
|
interpreterRegisterOpcode(0x811A, opExplosion); // op_explosion
|
|
interpreterRegisterOpcode(0x811B, opGetDaysSinceLastVisit); // op_days_since_visited
|
|
interpreterRegisterOpcode(0x811C, _op_gsay_start);
|
|
interpreterRegisterOpcode(0x811D, _op_gsay_end);
|
|
interpreterRegisterOpcode(0x811E, _op_gsay_reply); // op_gsay_reply
|
|
interpreterRegisterOpcode(0x811F, _op_gsay_option); // op_gsay_option
|
|
interpreterRegisterOpcode(0x8120, _op_gsay_message); // op_gsay_message
|
|
interpreterRegisterOpcode(0x8121, _op_giq_option); // op_giq_option
|
|
interpreterRegisterOpcode(0x8122, opPoison); // op_poison
|
|
interpreterRegisterOpcode(0x8123, opGetPoison); // op_get_poison
|
|
interpreterRegisterOpcode(0x8124, opPartyAdd); // op_party_add
|
|
interpreterRegisterOpcode(0x8125, opPartyRemove); // op_party_remove
|
|
interpreterRegisterOpcode(0x8126, opRegAnimAnimateForever); // op_reg_anim_animate_forever
|
|
interpreterRegisterOpcode(0x8127, opCritterInjure); // op_critter_injure
|
|
interpreterRegisterOpcode(0x8128, opCombatIsInitialized); // op_is_in_combat
|
|
interpreterRegisterOpcode(0x8129, _op_gdialog_barter); // op_gdialog_barter
|
|
interpreterRegisterOpcode(0x812A, opGetGameDifficulty); // op_game_difficulty
|
|
interpreterRegisterOpcode(0x812B, opGetRunningBurningGuy); // op_running_burning_guy
|
|
interpreterRegisterOpcode(0x812C, _op_inven_unwield); // op_inven_unwield
|
|
interpreterRegisterOpcode(0x812D, opObjectIsLocked); // op_obj_is_locked
|
|
interpreterRegisterOpcode(0x812E, opObjectLock); // op_obj_lock
|
|
interpreterRegisterOpcode(0x812F, opObjectUnlock); // op_obj_unlock
|
|
interpreterRegisterOpcode(0x8131, opObjectOpen); // op_obj_open
|
|
interpreterRegisterOpcode(0x8130, opObjectIsOpen); // op_obj_is_open
|
|
interpreterRegisterOpcode(0x8132, opObjectClose); // op_obj_close
|
|
interpreterRegisterOpcode(0x8133, opGameUiDisable); // op_game_ui_disable
|
|
interpreterRegisterOpcode(0x8134, opGameUiEnable); // op_game_ui_enable
|
|
interpreterRegisterOpcode(0x8135, opGameUiIsDisabled); // op_game_ui_is_disabled
|
|
interpreterRegisterOpcode(0x8136, opFadeOut); // op_gfade_out
|
|
interpreterRegisterOpcode(0x8137, opFadeIn); // op_gfade_in
|
|
interpreterRegisterOpcode(0x8138, opItemCapsTotal); // op_item_caps_total
|
|
interpreterRegisterOpcode(0x8139, opItemCapsAdjust); // op_item_caps_adjust
|
|
interpreterRegisterOpcode(0x813A, _op_anim_action_frame); // op_anim_action_frame
|
|
interpreterRegisterOpcode(0x813B, opRegAnimPlaySfx); // op_reg_anim_play_sfx
|
|
interpreterRegisterOpcode(0x813C, opCritterModifySkill); // op_critter_mod_skill
|
|
interpreterRegisterOpcode(0x813D, opSfxBuildCharName); // op_sfx_build_char_name
|
|
interpreterRegisterOpcode(0x813E, opSfxBuildAmbientName); // op_sfx_build_ambient_name
|
|
interpreterRegisterOpcode(0x813F, opSfxBuildInterfaceName); // op_sfx_build_interface_name
|
|
interpreterRegisterOpcode(0x8140, opSfxBuildItemName); // op_sfx_build_item_name
|
|
interpreterRegisterOpcode(0x8141, opSfxBuildWeaponName); // op_sfx_build_weapon_name
|
|
interpreterRegisterOpcode(0x8142, opSfxBuildSceneryName); // op_sfx_build_scenery_name
|
|
interpreterRegisterOpcode(0x8143, opAttackSetup); // op_attack_setup
|
|
interpreterRegisterOpcode(0x8144, opDestroyMultipleObjects); // op_destroy_mult_objs
|
|
interpreterRegisterOpcode(0x8145, opUseObjectOnObject); // op_use_obj_on_obj
|
|
interpreterRegisterOpcode(0x8146, opEndgameSlideshow); // op_endgame_slideshow
|
|
interpreterRegisterOpcode(0x8147, opMoveObjectInventoryToObject); // op_move_obj_inven_to_obj
|
|
interpreterRegisterOpcode(0x8148, opEndgameMovie); // op_endgame_movie
|
|
interpreterRegisterOpcode(0x8149, opGetObjectFid); // op_obj_art_fid
|
|
interpreterRegisterOpcode(0x814A, opGetFidAnim); // op_art_anim
|
|
interpreterRegisterOpcode(0x814B, opGetPartyMember); // op_party_member_obj
|
|
interpreterRegisterOpcode(0x814C, opGetRotationToTile); // op_rotation_to_tile
|
|
interpreterRegisterOpcode(0x814D, opJamLock); // op_jam_lock
|
|
interpreterRegisterOpcode(0x814E, opGameDialogSetBarterMod); // op_gdialog_set_barter_mod
|
|
interpreterRegisterOpcode(0x814F, opGetCombatDifficulty); // op_combat_difficulty
|
|
interpreterRegisterOpcode(0x8150, opObjectOnScreen); // op_obj_on_screen
|
|
interpreterRegisterOpcode(0x8151, opCritterIsFleeing); // op_critter_is_fleeing
|
|
interpreterRegisterOpcode(0x8152, opCritterSetFleeState); // op_critter_set_flee_state
|
|
interpreterRegisterOpcode(0x8153, opTerminateCombat); // op_terminate_combat
|
|
interpreterRegisterOpcode(0x8154, opDebugMessage); // op_debug_msg
|
|
interpreterRegisterOpcode(0x8155, opCritterStopAttacking); // op_critter_stop_attacking
|
|
}
|
|
|
|
// 0x45D878
|
|
void _intExtraRemoveProgramReferences_()
|
|
{
|
|
}
|