mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
608 lines
18 KiB
C++
608 lines
18 KiB
C++
/*
|
|
* sfall
|
|
* Copyright (C) 2008-2016 The sfall team
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "..\..\..\FalloutEngine\Fallout2.h"
|
|
#include "..\..\Combat.h"
|
|
#include "..\..\CritterStats.h"
|
|
#include "..\..\Drugs.h"
|
|
#include "..\..\Explosions.h"
|
|
#include "..\..\LoadGameHook.h"
|
|
#include "..\..\Objects.h"
|
|
#include "..\..\PartyControl.h"
|
|
#include "..\..\ScriptExtender.h"
|
|
#include "..\Arrays.h"
|
|
|
|
#include "Objects.h"
|
|
|
|
namespace sfall
|
|
{
|
|
namespace script
|
|
{
|
|
|
|
static const char* protoFailedLoad = "%s() - failed to load a prototype ID: %d";
|
|
|
|
static const char* nameNPCToInc;
|
|
static long pidNPCToInc;
|
|
static bool onceNpcLoop;
|
|
|
|
static void __cdecl IncNPCLevel(const char* fmt, const char* name) {
|
|
fo::GameObject* mObj;
|
|
__asm {
|
|
push edx;
|
|
mov eax, [ebp + 0x150 - 0x1C + 16]; // ebp <- esp
|
|
mov edx, [eax];
|
|
mov mObj, edx;
|
|
}
|
|
|
|
if ((pidNPCToInc && (mObj && mObj->protoId == pidNPCToInc)) || (!pidNPCToInc && !_stricmp(name, nameNPCToInc))) {
|
|
fo::func::debug_printf(fmt, name);
|
|
|
|
SafeWrite32(0x495C50, 0x01FB840F); // Want to keep this check intact. (restore)
|
|
|
|
SafeMemSet(0x495C77, CodeType::Nop, 6); // Check that the player is high enough for the npc to consider this level
|
|
//SafeMemSet(0x495C8C, CodeType::Nop, 6); // Check that the npc isn't already at its maximum level
|
|
SafeMemSet(0x495CEC, CodeType::Nop, 6); // Check that the npc hasn't already levelled up recently
|
|
if (!npcAutoLevelEnabled) {
|
|
SafeWrite8(0x495CFB, CodeType::JumpShort); // Disable random element
|
|
}
|
|
__asm mov [ebp + 0x150 - 0x28 + 16], 255; // set counter for exit loop
|
|
} else {
|
|
if (!onceNpcLoop) {
|
|
SafeWrite32(0x495C50, 0x01FCE9); // set goto next member
|
|
onceNpcLoop = true;
|
|
}
|
|
}
|
|
__asm pop edx;
|
|
}
|
|
|
|
void op_inc_npc_level(OpcodeContext& ctx) {
|
|
nameNPCToInc = ctx.arg(0).asString();
|
|
pidNPCToInc = ctx.arg(0).asInt(); // set to 0 if passing npc name
|
|
if (pidNPCToInc == 0 && nameNPCToInc[0] == 0) return;
|
|
|
|
MakeCall(0x495BF1, IncNPCLevel); // Replace the debug output
|
|
__asm call fo::funcoffs::partyMemberIncLevels_;
|
|
onceNpcLoop = false;
|
|
|
|
// restore code
|
|
SafeWrite32(0x495C50, 0x01FB840F);
|
|
__int64 data = 0x01D48C0F;
|
|
SafeWriteBytes(0x495C77, (BYTE*)&data, 6);
|
|
//SafeWrite16(0x495C8C, 0x8D0F);
|
|
//SafeWrite32(0x495C8E, 0x000001BF);
|
|
data = 0x0130850F;
|
|
SafeWriteBytes(0x495CEC, (BYTE*)&data, 6);
|
|
if (!npcAutoLevelEnabled) {
|
|
SafeWrite8(0x495CFB, CodeType::JumpZ);
|
|
}
|
|
}
|
|
|
|
void op_get_npc_level(OpcodeContext& ctx) {
|
|
int level = -1;
|
|
DWORD findPid = ctx.arg(0).asInt(); // set to 0 if passing npc name
|
|
const char *critterName, *name = ctx.arg(0).asString();
|
|
|
|
if (findPid || name[0] != 0) {
|
|
DWORD pid = 0;
|
|
auto members = fo::var::partyMemberList;
|
|
for (DWORD i = 0; i < fo::var::partyMemberCount; i++) {
|
|
if (!findPid) {
|
|
__asm {
|
|
mov eax, members;
|
|
mov eax, [eax];
|
|
call fo::funcoffs::critter_name_;
|
|
mov critterName, eax;
|
|
}
|
|
if (!_stricmp(name, critterName)) { // found npc
|
|
pid = members[i].object->protoId;
|
|
break;
|
|
}
|
|
} else {
|
|
DWORD _pid = members[i].object->protoId;
|
|
if (findPid == _pid) {
|
|
pid = _pid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pid) {
|
|
DWORD* pidList = fo::var::partyMemberPidList;
|
|
for (DWORD j = 0; j < fo::var::partyMemberMaxCount; j++) {
|
|
if (pidList[j] == pid) {
|
|
level = fo::var::partyMemberLevelUpInfoList[j * 3];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ctx.setReturn(level);
|
|
}
|
|
|
|
void op_remove_script(OpcodeContext& ctx) {
|
|
auto object = ctx.arg(0).object();
|
|
if (object->scriptId != 0xFFFFFFFF) {
|
|
fo::func::scr_remove(object->scriptId);
|
|
object->scriptId = 0xFFFFFFFF;
|
|
}
|
|
}
|
|
|
|
#define exec_script_proc(script, proc) __asm { \
|
|
__asm mov eax, script \
|
|
__asm mov edx, proc \
|
|
__asm call fo::funcoffs::exec_script_proc_ \
|
|
}
|
|
|
|
void op_set_script(OpcodeContext& ctx) {
|
|
using fo::Scripts::start;
|
|
using fo::Scripts::map_enter_p_proc;
|
|
|
|
long scriptType;
|
|
auto object = ctx.arg(0).object();
|
|
unsigned long valArg = ctx.arg(1).rawValue();
|
|
|
|
long scriptIndex = valArg & ~0xF0000000;
|
|
if (scriptIndex == 0 || valArg > 0x8FFFFFFF) { // negative values are not allowed
|
|
ctx.printOpcodeError("%s() - the script index number is incorrect.", ctx.getOpcodeName());
|
|
return;
|
|
}
|
|
scriptIndex--;
|
|
|
|
if (object->scriptId != 0xFFFFFFFF) {
|
|
fo::func::scr_remove(object->scriptId);
|
|
object->scriptId = 0xFFFFFFFF;
|
|
}
|
|
if (object->IsCritter()) {
|
|
scriptType = fo::Scripts::ScriptTypes::SCRIPT_CRITTER;
|
|
} else {
|
|
scriptType = fo::Scripts::ScriptTypes::SCRIPT_ITEM;
|
|
}
|
|
fo::func::obj_new_sid_inst(object, scriptType, scriptIndex);
|
|
|
|
long scriptId = object->scriptId;
|
|
exec_script_proc(scriptId, start);
|
|
if ((valArg & 0x80000000) == 0) exec_script_proc(scriptId, map_enter_p_proc);
|
|
}
|
|
|
|
void op_create_spatial(OpcodeContext& ctx) {
|
|
using fo::Scripts::start;
|
|
|
|
DWORD scriptIndex = ctx.arg(0).rawValue(),
|
|
tile = ctx.arg(1).rawValue(),
|
|
elevation = ctx.arg(2).rawValue(),
|
|
radius = ctx.arg(3).rawValue();
|
|
|
|
long scriptId;
|
|
fo::ScriptInstance* scriptPtr;
|
|
if (fo::func::scr_new(&scriptId, fo::Scripts::ScriptTypes::SCRIPT_SPATIAL) == -1 || fo::func::scr_ptr(scriptId, &scriptPtr) == -1) return;
|
|
|
|
// set spatial script properties:
|
|
scriptPtr->scriptIdx = scriptIndex - 1;
|
|
scriptPtr->elevationAndTile = (elevation << 29) & 0xE0000000 | tile;
|
|
scriptPtr->spatialRadius = radius;
|
|
|
|
// this will load appropriate script program and link it to the script instance we just created:
|
|
exec_script_proc(scriptId, start);
|
|
|
|
ctx.setReturn(fo::func::scr_find_obj_from_program(scriptPtr->program));
|
|
}
|
|
|
|
#undef exec_script_proc
|
|
|
|
void mf_spatial_radius(OpcodeContext& ctx) {
|
|
auto spatialObj = ctx.arg(0).object();
|
|
fo::ScriptInstance* script;
|
|
if (fo::func::scr_ptr(spatialObj->scriptId, &script) != -1) {
|
|
ctx.setReturn(script->spatialRadius);
|
|
}
|
|
}
|
|
|
|
void op_get_script(OpcodeContext& ctx) {
|
|
auto scriptIndex = ctx.arg(0).object()->scriptIndex;
|
|
ctx.setReturn((scriptIndex >= 0) ? ++scriptIndex : 0);
|
|
}
|
|
|
|
void op_set_critter_burst_disable(OpcodeContext& ctx) {
|
|
SetNoBurstMode(ctx.arg(0).object(), ctx.arg(1).asBool());
|
|
}
|
|
|
|
void op_get_weapon_ammo_pid(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
long pid = -1;
|
|
fo::Proto* proto;
|
|
if (obj->IsItem() && GetProto(obj->protoId, &proto)) {
|
|
long type = proto->item.type;
|
|
if (type == fo::ItemType::item_type_weapon || type == fo::ItemType::item_type_misc_item) {
|
|
pid = obj->item.ammoPid;
|
|
}
|
|
}
|
|
ctx.setReturn(pid);
|
|
}
|
|
|
|
void op_set_weapon_ammo_pid(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
if (obj->IsNotItem()) return;
|
|
|
|
fo::Proto* proto;
|
|
if (GetProto(obj->protoId, &proto)) {
|
|
long type = proto->item.type;
|
|
if (type == fo::ItemType::item_type_weapon || type == fo::ItemType::item_type_misc_item) {
|
|
obj->item.ammoPid = ctx.arg(1).rawValue();
|
|
}
|
|
} else {
|
|
ctx.printOpcodeError(protoFailedLoad, ctx.getOpcodeName(), obj->protoId);
|
|
}
|
|
}
|
|
|
|
void op_get_weapon_ammo_count(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
ctx.setReturn((obj->IsItem()) ? obj->item.charges : 0);
|
|
}
|
|
|
|
void op_set_weapon_ammo_count(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
if (obj->IsItem()) obj->item.charges = ctx.arg(1).rawValue();
|
|
}
|
|
|
|
enum class BlockType {
|
|
BLOCKING_TYPE_BLOCK = 0,
|
|
BLOCKING_TYPE_SHOOT = 1,
|
|
BLOCKING_TYPE_AI = 2,
|
|
BLOCKING_TYPE_SIGHT = 3,
|
|
BLOCKING_TYPE_SCROLL = 4
|
|
};
|
|
|
|
static DWORD getBlockingFunc(BlockType type) {
|
|
switch (type) {
|
|
case BlockType::BLOCKING_TYPE_BLOCK: default:
|
|
return fo::funcoffs::obj_blocking_at_; // with calling hook
|
|
case BlockType::BLOCKING_TYPE_SHOOT:
|
|
return fo::funcoffs::obj_shoot_blocking_at_; // w/o calling hook
|
|
case BlockType::BLOCKING_TYPE_AI:
|
|
return fo::funcoffs::obj_ai_blocking_at_; // w/o calling hook
|
|
case BlockType::BLOCKING_TYPE_SIGHT:
|
|
return fo::funcoffs::obj_sight_blocking_at_; // w/o calling hook
|
|
//case 4:
|
|
// return obj_scroll_blocking_at_;
|
|
}
|
|
}
|
|
|
|
void op_make_straight_path(OpcodeContext& ctx) {
|
|
auto objFrom = ctx.arg(0).object();
|
|
DWORD tileTo = ctx.arg(1).rawValue();
|
|
BlockType type = (BlockType)ctx.arg(2).rawValue();
|
|
|
|
long flag = (type == BlockType::BLOCKING_TYPE_SHOOT) ? 32 : 0;
|
|
fo::GameObject* resultObj = nullptr;
|
|
fo::func::make_straight_path_func(objFrom, objFrom->tile, tileTo, 0, (DWORD*)&resultObj, flag, (void*)getBlockingFunc(type));
|
|
ctx.setReturn(resultObj);
|
|
}
|
|
|
|
void op_make_path(OpcodeContext& ctx) {
|
|
auto objFrom = ctx.arg(0).object();
|
|
auto tileTo = ctx.arg(1).rawValue();
|
|
auto func = getBlockingFunc((BlockType)ctx.arg(2).rawValue());
|
|
|
|
// if the object is not a critter, then there is no need to check tile (tileTo) for blocking
|
|
long checkFlag = (objFrom->IsCritter());
|
|
|
|
char pathData[800];
|
|
long pathLength = fo::func::make_path_func(objFrom, objFrom->tile, tileTo, pathData, checkFlag, (void*)func);
|
|
auto arrayId = CreateTempArray(pathLength, 0);
|
|
for (int i = 0; i < pathLength; i++) {
|
|
arrays[arrayId].val[i].set((long)pathData[i]);
|
|
}
|
|
ctx.setReturn(arrayId);
|
|
}
|
|
|
|
void op_obj_blocking_at(OpcodeContext& ctx) {
|
|
DWORD tile = ctx.arg(0).rawValue(),
|
|
elevation = ctx.arg(1).rawValue();
|
|
BlockType type = (BlockType)ctx.arg(2).rawValue();
|
|
|
|
fo::GameObject* resultObj = fo::func::obj_blocking_at_wrapper(0, tile, elevation, (void*)getBlockingFunc(type));
|
|
if (resultObj && type == BlockType::BLOCKING_TYPE_SHOOT && (resultObj->flags & fo::ObjectFlag::ShootThru)) { // don't know what this flag means, copy-pasted from the engine code
|
|
// this check was added because the engine always does exactly this when using shoot blocking checks
|
|
resultObj = nullptr;
|
|
}
|
|
ctx.setReturn(resultObj);
|
|
}
|
|
|
|
void op_tile_get_objects(OpcodeContext& ctx) {
|
|
DWORD tile = ctx.arg(0).rawValue(),
|
|
elevation = ctx.arg(1).rawValue();
|
|
DWORD arrayId = CreateTempArray(0, 4);
|
|
auto obj = fo::func::obj_find_first_at_tile(elevation, tile);
|
|
while (obj) {
|
|
arrays[arrayId].push_back(reinterpret_cast<long>(obj));
|
|
obj = fo::func::obj_find_next_at_tile();
|
|
}
|
|
ctx.setReturn(arrayId);
|
|
}
|
|
|
|
void op_get_party_members(OpcodeContext& ctx) {
|
|
auto includeHidden = ctx.arg(0).rawValue();
|
|
int actualCount = fo::var::partyMemberCount;
|
|
DWORD arrayId = CreateTempArray(0, 4);
|
|
for (int i = 0; i < actualCount; i++) {
|
|
fo::GameObject* obj = fo::var::partyMemberList[i].object;
|
|
if (includeHidden || (obj->IsCritter() && !fo::func::critter_is_dead(obj) && !(obj->flags & fo::ObjectFlag::Mouse_3d))) {
|
|
arrays[arrayId].push_back((long)obj);
|
|
}
|
|
}
|
|
ctx.setReturn(arrayId);
|
|
}
|
|
|
|
void mf_set_outline(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
int color = ctx.arg(1).rawValue();
|
|
obj->outline = color;
|
|
}
|
|
|
|
void mf_get_outline(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
ctx.setReturn(obj->outline);
|
|
}
|
|
|
|
void mf_set_flags(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
int flags = ctx.arg(1).rawValue();
|
|
obj->flags = flags;
|
|
}
|
|
|
|
void mf_get_flags(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
ctx.setReturn(obj->flags);
|
|
}
|
|
|
|
void mf_outlined_object(OpcodeContext& ctx) {
|
|
ctx.setReturn(fo::var::outlined_object);
|
|
}
|
|
|
|
void mf_set_dude_obj(OpcodeContext& ctx) {
|
|
auto obj = ctx.arg(0).object();
|
|
if (obj == nullptr || obj->IsCritter()) {
|
|
//if (!InCombat && obj && obj != PartyControl::RealDudeObject()) {
|
|
// ctx.printOpcodeError("%s() - controlling of the critter is only allowed in combat mode.", ctx.getMetaruleName());
|
|
//} else {
|
|
PartyControl::SwitchToCritter(obj);
|
|
//}
|
|
} else {
|
|
ctx.printOpcodeError("%s() - the object is not a critter.", ctx.getMetaruleName());
|
|
ctx.setReturn(-1);
|
|
}
|
|
}
|
|
|
|
void mf_real_dude_obj(OpcodeContext& ctx) {
|
|
ctx.setReturn(PartyControl::RealDudeObject());
|
|
}
|
|
|
|
void mf_car_gas_amount(OpcodeContext& ctx) {
|
|
ctx.setReturn(fo::var::carGasAmount);
|
|
}
|
|
|
|
void mf_lock_is_jammed(OpcodeContext& ctx) {
|
|
ctx.setReturn(fo::func::obj_lock_is_jammed(ctx.arg(0).object()));
|
|
}
|
|
|
|
void mf_unjam_lock(OpcodeContext& ctx) {
|
|
fo::func::obj_unjam_lock(ctx.arg(0).object());
|
|
}
|
|
|
|
void mf_set_unjam_locks_time(OpcodeContext& ctx) {
|
|
int time = ctx.arg(0).rawValue();
|
|
if (time < 0 || time > 127) {
|
|
ctx.printOpcodeError("%s() - time argument must be in the range of 0 to 127.", ctx.getMetaruleName());
|
|
ctx.setReturn(-1);
|
|
} else {
|
|
Objects::SetAutoUnjamLockTime(time);
|
|
}
|
|
}
|
|
|
|
void mf_item_make_explosive(OpcodeContext& ctx) {
|
|
DWORD pid = ctx.arg(0).rawValue();
|
|
DWORD pidActive = ctx.arg(1).rawValue();
|
|
DWORD min = ctx.arg(2).rawValue();
|
|
DWORD max = (ctx.numArgs() == 4) ? ctx.arg(3).rawValue() : min;
|
|
|
|
if (min > max) {
|
|
max = min;
|
|
ctx.printOpcodeError("%s() - Warning: value of max argument is less than the min argument.", ctx.getMetaruleName());
|
|
}
|
|
|
|
if (pid > 0 && pidActive > 0) {
|
|
Explosions::AddToExplosives(pid, pidActive, min, max);
|
|
} else {
|
|
ctx.printOpcodeError("%s() - invalid PID number, must be greater than 0.", ctx.getMetaruleName());
|
|
ctx.setReturn(-1);
|
|
}
|
|
}
|
|
|
|
void mf_get_dialog_object(OpcodeContext& ctx) {
|
|
ctx.setReturn(InDialog() ? fo::var::dialog_target : 0);
|
|
}
|
|
|
|
void mf_obj_under_cursor(OpcodeContext& ctx) {
|
|
ctx.setReturn(
|
|
fo::func::object_under_mouse(ctx.arg(0).asBool() ? 1 : -1, ctx.arg(1).rawValue(), fo::var::map_elevation)
|
|
);
|
|
}
|
|
|
|
void mf_get_loot_object(OpcodeContext& ctx) {
|
|
ctx.setReturn((GetLoopFlags() & INTFACELOOT) ? fo::var::target_stack[fo::var::target_curr_stack] : 0);
|
|
}
|
|
|
|
static bool protoMaxLimitPatch = false;
|
|
|
|
void op_get_proto_data(OpcodeContext& ctx) {
|
|
long result = -1;
|
|
fo::Proto* protoPtr;
|
|
int pid = ctx.arg(0).rawValue();
|
|
if (fo::CheckProtoID(pid) && fo::func::proto_ptr(pid, &protoPtr) != result) {
|
|
result = *(long*)((BYTE*)protoPtr + ctx.arg(1).rawValue());
|
|
} else {
|
|
ctx.printOpcodeError(protoFailedLoad, ctx.getOpcodeName(), pid);
|
|
}
|
|
ctx.setReturn(result);
|
|
}
|
|
|
|
void op_set_proto_data(OpcodeContext& ctx) {
|
|
int pid = ctx.arg(0).rawValue();
|
|
if (fo::CheckProtoID(pid) && CritterStats::SetProtoData(pid, ctx.arg(1).rawValue(), ctx.arg(2).rawValue()) != -1) {
|
|
if (!protoMaxLimitPatch) {
|
|
Objects::LoadProtoAutoMaxLimit();
|
|
protoMaxLimitPatch = true;
|
|
}
|
|
} else {
|
|
ctx.printOpcodeError(protoFailedLoad, ctx.getOpcodeName(), pid);
|
|
}
|
|
}
|
|
|
|
void mf_get_object_data(OpcodeContext& ctx) {
|
|
DWORD* object_ptr = (DWORD*)ctx.arg(0).rawValue();
|
|
ctx.setReturn(*(long*)((BYTE*)object_ptr + ctx.arg(1).rawValue()));
|
|
}
|
|
|
|
void mf_set_object_data(OpcodeContext& ctx) {
|
|
DWORD* object_ptr = (DWORD*)ctx.arg(0).rawValue();
|
|
*(long*)((BYTE*)object_ptr + ctx.arg(1).rawValue()) = ctx.arg(2).rawValue();
|
|
}
|
|
|
|
void mf_get_object_ai_data(OpcodeContext& ctx) {
|
|
fo::AIcap* cap = fo::func::ai_cap(ctx.arg(0).object());
|
|
DWORD arrayId, value = -1;
|
|
switch (ctx.arg(1).rawValue()) {
|
|
case 0:
|
|
value = cap->aggression;
|
|
break;
|
|
case 1:
|
|
value = cap->area_attack_mode;
|
|
break;
|
|
case 2:
|
|
value = cap->attack_who;
|
|
break;
|
|
case 3:
|
|
value = cap->best_weapon;
|
|
break;
|
|
case 4:
|
|
value = cap->chem_use;
|
|
break;
|
|
case 5:
|
|
value = cap->disposition;
|
|
break;
|
|
case 6:
|
|
value = cap->distance;
|
|
break;
|
|
case 7:
|
|
value = cap->max_dist;
|
|
break;
|
|
case 8:
|
|
value = cap->min_hp;
|
|
break;
|
|
case 9:
|
|
value = cap->min_to_hit;
|
|
break;
|
|
case 10:
|
|
value = cap->hurt_too_much; // DAM_BLIND/DAM_CRIP_* flags
|
|
break;
|
|
case 11:
|
|
value = cap->run_away_mode;
|
|
break;
|
|
case 12:
|
|
value = cap->secondary_freq;
|
|
break;
|
|
case 13:
|
|
value = cap->called_freq;
|
|
break;
|
|
case 14:
|
|
arrayId = CreateTempArray(3, 0);
|
|
arrays[arrayId].val[0].set(cap->chem_primary_desire);
|
|
arrays[arrayId].val[1].set(cap->chem_primary_desire1);
|
|
arrays[arrayId].val[2].set(cap->chem_primary_desire2);
|
|
value = arrayId;
|
|
break;
|
|
default:
|
|
ctx.printOpcodeError("%s() - invalid value for AI argument.", ctx.getMetaruleName());
|
|
}
|
|
ctx.setReturn(value);
|
|
}
|
|
|
|
void mf_set_drugs_data(OpcodeContext& ctx) {
|
|
int type = ctx.arg(0).rawValue();
|
|
int pid = ctx.arg(1).rawValue();
|
|
int val = ctx.arg(2).rawValue();
|
|
int result;
|
|
switch (type) {
|
|
case 0:
|
|
result = Drugs::SetDrugNumEffect(pid, val);
|
|
break;
|
|
case 1:
|
|
result = Drugs::SetDrugAddictTimeOff(pid, val);
|
|
break;
|
|
default:
|
|
ctx.printOpcodeError("%s() - invalid value for type argument.", ctx.getMetaruleName());
|
|
return;
|
|
}
|
|
if (result) {
|
|
ctx.printOpcodeError("%s() - drug PID not found in the configuration file.", ctx.getMetaruleName());
|
|
ctx.setReturn(-1);
|
|
}
|
|
}
|
|
|
|
void mf_set_unique_id(OpcodeContext& ctx) {
|
|
fo::GameObject* obj = ctx.arg(0).object();
|
|
long id;
|
|
if (ctx.arg(1).rawValue() == -1) {
|
|
id = fo::func::new_obj_id();
|
|
obj->id = id;
|
|
} else {
|
|
id = Objects::SetObjectUniqueID(obj);
|
|
}
|
|
ctx.setReturn(id);
|
|
}
|
|
|
|
void mf_objects_in_radius(OpcodeContext& ctx) {
|
|
long radius = ctx.arg(1).rawValue();
|
|
if (radius <= 0) radius = 1; else if (radius > 50) radius = 50;
|
|
long elev = ctx.arg(2).rawValue();
|
|
if (elev < 0) elev = 0; else if (elev > 2) elev = 2;
|
|
long type = (ctx.numArgs() > 3) ? ctx.arg(3).rawValue() : -1;
|
|
|
|
std::vector<fo::GameObject*> objects;
|
|
objects.reserve(25);
|
|
fo::GetObjectsTileRadius(objects, ctx.arg(0).rawValue(), radius, elev, type);
|
|
size_t sz = objects.size();
|
|
DWORD id = CreateTempArray(sz, 0);
|
|
for (size_t i = 0; i < sz; i++) {
|
|
arrays[id].val[i].set((long)objects[i]);
|
|
}
|
|
ctx.setReturn(id);
|
|
}
|
|
|
|
void mf_npc_engine_level_up(OpcodeContext& ctx) {
|
|
if (ctx.arg(0).asBool()) {
|
|
if (!npcEngineLevelUp) SafeWrite16(0x4AFC1C, 0x840F); // enable
|
|
npcEngineLevelUp = true;
|
|
} else {
|
|
if (npcEngineLevelUp) SafeWrite16(0x4AFC1C, 0xE990);
|
|
npcEngineLevelUp = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|