Code deobfuscation/decyphering (3) (#374)

This commit is contained in:
Vlad 2025-02-09 17:20:53 +01:00 committed by GitHub
parent 593f80144c
commit 080f999626
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 1075 additions and 1036 deletions

View File

@ -1060,21 +1060,21 @@ int _action_climb_ladder(Object* a1, Object* a2)
} }
// 0x411F2C // 0x411F2C
int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3) int _action_use_an_item_on_object(Object* user, Object* targetObj, Object* item)
{ {
Proto* proto = nullptr; Proto* proto = nullptr;
int type = FID_TYPE(a2->fid); int type = FID_TYPE(targetObj->fid);
int sceneryType = -1; int sceneryType = -1;
if (type == OBJ_TYPE_SCENERY) { if (type == OBJ_TYPE_SCENERY) {
if (protoGetProto(a2->pid, &proto) == -1) { if (protoGetProto(targetObj->pid, &proto) == -1) {
return -1; return -1;
} }
sceneryType = proto->scenery.type; sceneryType = proto->scenery.type;
} }
if (sceneryType != SCENERY_TYPE_LADDER_UP || a3 != nullptr) { if (sceneryType != SCENERY_TYPE_LADDER_UP || item != nullptr) {
if (a1 == gDude) { if (user == gDude) {
int anim = FID_ANIM_TYPE(gDude->fid); int anim = FID_ANIM_TYPE(gDude->fid);
if (anim == ANIM_WALK || anim == ANIM_RUNNING) { if (anim == ANIM_WALK || anim == ANIM_RUNNING) {
reg_anim_clear(gDude); reg_anim_clear(gDude);
@ -1085,40 +1085,40 @@ int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3)
int actionPoints; int actionPoints;
if (isInCombat()) { if (isInCombat()) {
animationRequestOptions = ANIMATION_REQUEST_RESERVED; animationRequestOptions = ANIMATION_REQUEST_RESERVED;
actionPoints = a1->data.critter.combat.ap; actionPoints = user->data.critter.combat.ap;
} else { } else {
animationRequestOptions = ANIMATION_REQUEST_UNRESERVED; animationRequestOptions = ANIMATION_REQUEST_UNRESERVED;
actionPoints = -1; actionPoints = -1;
} }
if (a1 == gDude) { if (user == gDude) {
animationRequestOptions = ANIMATION_REQUEST_RESERVED; animationRequestOptions = ANIMATION_REQUEST_RESERVED;
} }
reg_anim_begin(animationRequestOptions); reg_anim_begin(animationRequestOptions);
if (actionPoints != -1 || objectGetDistanceBetween(a1, a2) < 5) { if (actionPoints != -1 || objectGetDistanceBetween(user, targetObj) < 5) {
animationRegisterMoveToObject(a1, a2, actionPoints, 0); animationRegisterMoveToObject(user, targetObj, actionPoints, 0);
} else { } else {
animationRegisterRunToObject(a1, a2, -1, 0); animationRegisterRunToObject(user, targetObj, -1, 0);
} }
animationRegisterCallbackForced(a1, a2, (AnimationCallback*)_is_next_to, -1); animationRegisterCallbackForced(user, targetObj, (AnimationCallback*)_is_next_to, -1);
if (a3 == nullptr) { if (item == nullptr) {
animationRegisterCallback(a1, a2, (AnimationCallback*)_check_scenery_ap_cost, -1); animationRegisterCallback(user, targetObj, (AnimationCallback*)_check_scenery_ap_cost, -1);
} }
int a2a = (a1->fid & 0xF000) >> 12; int weaponAnimCode = (user->fid & 0xF000) >> 12;
if (a2a != 0) { if (weaponAnimCode != 0) {
const char* sfx = sfxBuildCharName(a1, ANIM_PUT_AWAY, CHARACTER_SOUND_EFFECT_UNUSED); const char* sfx = sfxBuildCharName(user, ANIM_PUT_AWAY, CHARACTER_SOUND_EFFECT_UNUSED);
animationRegisterPlaySoundEffect(a1, sfx, -1); animationRegisterPlaySoundEffect(user, sfx, -1);
animationRegisterAnimate(a1, ANIM_PUT_AWAY, 0); animationRegisterAnimate(user, ANIM_PUT_AWAY, 0);
} }
int anim; int anim;
int objectType = FID_TYPE(a2->fid); int objectType = FID_TYPE(targetObj->fid);
if (objectType == OBJ_TYPE_CRITTER && _critter_is_prone(a2)) { if (objectType == OBJ_TYPE_CRITTER && _critter_is_prone(targetObj)) {
anim = ANIM_MAGIC_HANDS_GROUND; anim = ANIM_MAGIC_HANDS_GROUND;
} else if (objectType == OBJ_TYPE_SCENERY && (proto->scenery.extendedFlags & 0x01) != 0) { } else if (objectType == OBJ_TYPE_SCENERY && (proto->scenery.extendedFlags & 0x01) != 0) {
anim = ANIM_MAGIC_HANDS_GROUND; anim = ANIM_MAGIC_HANDS_GROUND;
@ -1126,31 +1126,31 @@ int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3)
anim = ANIM_MAGIC_HANDS_MIDDLE; anim = ANIM_MAGIC_HANDS_MIDDLE;
} }
if (sceneryType != SCENERY_TYPE_STAIRS && a3 == nullptr) { if (sceneryType != SCENERY_TYPE_STAIRS && item == nullptr) {
animationRegisterAnimate(a1, anim, -1); animationRegisterAnimate(user, anim, -1);
} }
if (a3 != nullptr) { if (item != nullptr) {
// TODO: Get rid of cast. // TODO: Get rid of cast.
animationRegisterCallback3(a1, a2, a3, (AnimationCallback3*)_obj_use_item_on, -1); animationRegisterCallback3(user, targetObj, item, (AnimationCallback3*)_obj_use_item_on, -1);
} else { } else {
animationRegisterCallback(a1, a2, (AnimationCallback*)_obj_use, -1); animationRegisterCallback(user, targetObj, (AnimationCallback*)_obj_use, -1);
} }
if (a2a != 0) { if (weaponAnimCode != 0) {
animationRegisterTakeOutWeapon(a1, a2a, -1); animationRegisterTakeOutWeapon(user, weaponAnimCode, -1);
} }
return reg_anim_end(); return reg_anim_end();
} }
return _action_climb_ladder(a1, a2); return _action_climb_ladder(user, targetObj);
} }
// 0x412114 // 0x412114
int _action_use_an_object(Object* a1, Object* a2) int _action_use_an_object(Object* user, Object* targetObj)
{ {
return _action_use_an_item_on_object(a1, a2, nullptr); return _action_use_an_item_on_object(user, targetObj, nullptr);
} }
// 0x412134 // 0x412134
@ -1322,27 +1322,27 @@ static int _action_use_skill_in_combat_error(Object* critter)
// skill_use // skill_use
// 0x41255C // 0x41255C
int actionUseSkill(Object* a1, Object* a2, int skill) int actionUseSkill(Object* user, Object* target, int skill)
{ {
switch (skill) { switch (skill) {
case SKILL_FIRST_AID: case SKILL_FIRST_AID:
case SKILL_DOCTOR: case SKILL_DOCTOR:
if (isInCombat()) { if (isInCombat()) {
// NOTE: Uninline. // NOTE: Uninline.
return _action_use_skill_in_combat_error(a1); return _action_use_skill_in_combat_error(user);
} }
if (PID_TYPE(a2->pid) != OBJ_TYPE_CRITTER) { if (PID_TYPE(target->pid) != OBJ_TYPE_CRITTER) {
return -1; return -1;
} }
break; break;
case SKILL_LOCKPICK: case SKILL_LOCKPICK:
if (isInCombat()) { if (isInCombat()) {
// NOTE: Uninline. // NOTE: Uninline.
return _action_use_skill_in_combat_error(a1); return _action_use_skill_in_combat_error(user);
} }
if (PID_TYPE(a2->pid) != OBJ_TYPE_ITEM && PID_TYPE(a2->pid) != OBJ_TYPE_SCENERY) { if (PID_TYPE(target->pid) != OBJ_TYPE_ITEM && PID_TYPE(target->pid) != OBJ_TYPE_SCENERY) {
return -1; return -1;
} }
@ -1350,14 +1350,14 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
case SKILL_STEAL: case SKILL_STEAL:
if (isInCombat()) { if (isInCombat()) {
// NOTE: Uninline. // NOTE: Uninline.
return _action_use_skill_in_combat_error(a1); return _action_use_skill_in_combat_error(user);
} }
if (PID_TYPE(a2->pid) != OBJ_TYPE_ITEM && PID_TYPE(a2->pid) != OBJ_TYPE_CRITTER) { if (PID_TYPE(target->pid) != OBJ_TYPE_ITEM && PID_TYPE(target->pid) != OBJ_TYPE_CRITTER) {
return -1; return -1;
} }
if (a2 == a1) { if (target == user) {
return -1; return -1;
} }
@ -1365,10 +1365,10 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
case SKILL_TRAPS: case SKILL_TRAPS:
if (isInCombat()) { if (isInCombat()) {
// NOTE: Uninline. // NOTE: Uninline.
return _action_use_skill_in_combat_error(a1); return _action_use_skill_in_combat_error(user);
} }
if (PID_TYPE(a2->pid) == OBJ_TYPE_CRITTER) { if (PID_TYPE(target->pid) == OBJ_TYPE_CRITTER) {
return -1; return -1;
} }
@ -1377,18 +1377,18 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
case SKILL_REPAIR: case SKILL_REPAIR:
if (isInCombat()) { if (isInCombat()) {
// NOTE: Uninline. // NOTE: Uninline.
return _action_use_skill_in_combat_error(a1); return _action_use_skill_in_combat_error(user);
} }
if (PID_TYPE(a2->pid) != OBJ_TYPE_CRITTER) { if (PID_TYPE(target->pid) != OBJ_TYPE_CRITTER) {
break; break;
} }
if (critterGetKillType(a2) == KILL_TYPE_ROBOT) { if (critterGetKillType(target) == KILL_TYPE_ROBOT) {
break; break;
} }
if (critterGetKillType(a2) == KILL_TYPE_BRAHMIN if (critterGetKillType(target) == KILL_TYPE_BRAHMIN
&& skill == SKILL_SCIENCE) { && skill == SKILL_SCIENCE) {
break; break;
} }
@ -1398,7 +1398,7 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
int targetType = SCIENCE_REPAIR_TARGET_TYPE_DEFAULT; int targetType = SCIENCE_REPAIR_TARGET_TYPE_DEFAULT;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY, &targetType); configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY, &targetType);
if (targetType == SCIENCE_REPAIR_TARGET_TYPE_DUDE) { if (targetType == SCIENCE_REPAIR_TARGET_TYPE_DUDE) {
if (a2 == gDude) { if (target == gDude) {
break; break;
} }
} else if (targetType == SCIENCE_REPAIR_TARGET_TYPE_ANYONE) { } else if (targetType == SCIENCE_REPAIR_TARGET_TYPE_ANYONE) {
@ -1418,7 +1418,7 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
// skill in entire party, and this skill is his/her own best. // skill in entire party, and this skill is his/her own best.
Object* performer = gDude; Object* performer = gDude;
if (a1 == gDude) { if (user == gDude) {
Object* partyMember = partyMemberGetBestInSkill(skill); Object* partyMember = partyMemberGetBestInSkill(skill);
if (partyMember == gDude) { if (partyMember == gDude) {
@ -1451,7 +1451,7 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
if (partyMember != nullptr) { if (partyMember != nullptr) {
bool isDude = false; bool isDude = false;
if (objectGetDistanceBetween(gDude, a2) <= 1) { if (objectGetDistanceBetween(gDude, target) <= 1) {
isDude = true; isDude = true;
} }
@ -1478,21 +1478,21 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
if (isInCombat()) { if (isInCombat()) {
reg_anim_begin(ANIMATION_REQUEST_RESERVED); reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterMoveToObject(performer, a2, performer->data.critter.combat.ap, 0); animationRegisterMoveToObject(performer, target, performer->data.critter.combat.ap, 0);
} else { } else {
reg_anim_begin(a1 == gDude ? ANIMATION_REQUEST_RESERVED : ANIMATION_REQUEST_UNRESERVED); reg_anim_begin(user == gDude ? ANIMATION_REQUEST_RESERVED : ANIMATION_REQUEST_UNRESERVED);
if (a2 != gDude) { if (target != gDude) {
if (objectGetDistanceBetween(performer, a2) >= 5) { if (objectGetDistanceBetween(performer, target) >= 5) {
animationRegisterRunToObject(performer, a2, -1, 0); animationRegisterRunToObject(performer, target, -1, 0);
} else { } else {
animationRegisterMoveToObject(performer, a2, -1, 0); animationRegisterMoveToObject(performer, target, -1, 0);
} }
} }
} }
animationRegisterCallbackForced(performer, a2, (AnimationCallback*)_is_next_to, -1); animationRegisterCallbackForced(performer, target, (AnimationCallback*)_is_next_to, -1);
int anim = (FID_TYPE(a2->fid) == OBJ_TYPE_CRITTER && _critter_is_prone(a2)) ? ANIM_MAGIC_HANDS_GROUND : ANIM_MAGIC_HANDS_MIDDLE; int anim = (FID_TYPE(target->fid) == OBJ_TYPE_CRITTER && _critter_is_prone(target)) ? ANIM_MAGIC_HANDS_GROUND : ANIM_MAGIC_HANDS_MIDDLE;
int fid = buildFid(OBJ_TYPE_CRITTER, performer->fid & 0xFFF, anim, 0, performer->rotation + 1); int fid = buildFid(OBJ_TYPE_CRITTER, performer->fid & 0xFFF, anim, 0, performer->rotation + 1);
CacheEntry* artHandle; CacheEntry* artHandle;
@ -1504,7 +1504,7 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
animationRegisterAnimate(performer, anim, -1); animationRegisterAnimate(performer, anim, -1);
// TODO: Get rid of casts. // TODO: Get rid of casts.
animationRegisterCallback3(performer, a2, (void*)skill, (AnimationCallback3*)_obj_use_skill_on, -1); animationRegisterCallback3(performer, target, (void*)skill, (AnimationCallback3*)_obj_use_skill_on, -1);
return reg_anim_end(); return reg_anim_end();
} }

View File

@ -9,12 +9,12 @@ namespace fallout {
extern int rotation; extern int rotation;
int _action_attack(Attack* attack); int _action_attack(Attack* attack);
int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3); int _action_use_an_item_on_object(Object* user, Object* targetObj, Object* item);
int _action_use_an_object(Object* a1, Object* a2); int _action_use_an_object(Object* user, Object* targetObj);
int actionPickUp(Object* critter, Object* item); int actionPickUp(Object* critter, Object* item);
int _action_loot_container(Object* critter, Object* container); int _action_loot_container(Object* critter, Object* container);
int _action_skill_use(int a1); int _action_skill_use(int skill);
int actionUseSkill(Object* a1, Object* a2, int skill); int actionUseSkill(Object* user, Object* target, int skill);
bool _is_hit_from_front(Object* attacker, Object* defender); bool _is_hit_from_front(Object* attacker, Object* defender);
bool _can_see(Object* a1, Object* a2); bool _can_see(Object* a1, Object* a2);
bool _action_explode_running(); bool _action_explode_running();

View File

@ -34,6 +34,7 @@
#include "tile.h" #include "tile.h"
#include "trait.h" #include "trait.h"
#include "vcr.h" #include "vcr.h"
#include "worldmap.h"
namespace fallout { namespace fallout {
@ -1033,7 +1034,7 @@ int animationRegisterHideObjectForced(Object* object)
} }
// 0x414E98 // 0x414E98
int animationRegisterCallback(void* a1, void* a2, AnimationCallback* proc, int delay) int animationRegisterCallback(void* param1, void* param2, AnimationCallback* proc, int delay)
{ {
if (_check_registry(nullptr) == -1 || proc == nullptr) { if (_check_registry(nullptr) == -1 || proc == nullptr) {
_anim_cleanup(); _anim_cleanup();
@ -1045,8 +1046,8 @@ int animationRegisterCallback(void* a1, void* a2, AnimationCallback* proc, int d
animationDescription->kind = ANIM_KIND_CALLBACK; animationDescription->kind = ANIM_KIND_CALLBACK;
animationDescription->extendedFlags = 0; animationDescription->extendedFlags = 0;
animationDescription->artCacheKey = nullptr; animationDescription->artCacheKey = nullptr;
animationDescription->param2 = a2; animationDescription->param2 = param2;
animationDescription->param1 = a1; animationDescription->param1 = param1;
animationDescription->callback = proc; animationDescription->callback = proc;
animationDescription->delay = delay; animationDescription->delay = delay;
@ -1058,7 +1059,7 @@ int animationRegisterCallback(void* a1, void* a2, AnimationCallback* proc, int d
// Same as `animationRegisterCallback` but accepting 3 parameters. // Same as `animationRegisterCallback` but accepting 3 parameters.
// //
// 0x414F20 // 0x414F20
int animationRegisterCallback3(void* a1, void* a2, void* a3, AnimationCallback3* proc, int delay) int animationRegisterCallback3(void* param1, void* param2, void* param3, AnimationCallback3* proc, int delay)
{ {
if (_check_registry(nullptr) == -1 || proc == nullptr) { if (_check_registry(nullptr) == -1 || proc == nullptr) {
_anim_cleanup(); _anim_cleanup();
@ -1070,10 +1071,10 @@ int animationRegisterCallback3(void* a1, void* a2, void* a3, AnimationCallback3*
animationDescription->kind = ANIM_KIND_CALLBACK3; animationDescription->kind = ANIM_KIND_CALLBACK3;
animationDescription->extendedFlags = 0; animationDescription->extendedFlags = 0;
animationDescription->artCacheKey = nullptr; animationDescription->artCacheKey = nullptr;
animationDescription->param2 = a2; animationDescription->param2 = param2;
animationDescription->param1 = a1; animationDescription->param1 = param1;
animationDescription->callback3 = proc; animationDescription->callback3 = proc;
animationDescription->param3 = a3; animationDescription->param3 = param3;
animationDescription->delay = delay; animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++; gAnimationDescriptionCurrentIndex++;
@ -3062,7 +3063,7 @@ void _dude_fidget()
objectGetRect(object, &rect); objectGetRect(object, &rect);
Rect intersection; Rect intersection;
if (rectIntersection(&rect, &_scr_size, &intersection) == 0 && (gMapHeader.field_34 != 97 || object->pid != 0x10000FA)) { if (rectIntersection(&rect, &_scr_size, &intersection) == 0 && (gMapHeader.index != MAP_SPECIAL_RND_WOODSMAN || object->pid != 0x10000FA)) {
candidates[candidatesLength++] = object; candidates[candidatesLength++] = object;
} }
} }

View File

@ -1006,45 +1006,28 @@ static void artCacheFreeImpl(void* ptr)
internal_free(ptr); internal_free(ptr);
} }
// 0x419C88 static int buildFidInternal(unsigned short frmId, unsigned char weaponCode, unsigned char animType, unsigned char objectType, unsigned char rotation)
int buildFid(int objectType, int frmId, int animType, int a3, int rotation)
{ {
int v7, v8, v9, v10; return ((rotation << 28) & 0x70000000) | (objectType << 24) | ((animType << 16) & 0xFF0000) | ((weaponCode << 12) & 0xF000) | (frmId & 0xFFF);
}
v10 = rotation; // 0x419C88
int buildFid(int objectType, int frmId, int animType, int weaponCode, int rotation)
if (objectType != OBJ_TYPE_CRITTER) { {
goto zero; // Always use rotation 0 (NE) for non-critters, for certain critter animations.
// For other critter animations, check if art for the given rotation exists, if not try rotation 1 (E) and if that also doesn't exist, then default to 0 (NE).
if (objectType != OBJ_TYPE_CRITTER
|| animType == ANIM_FIRE_DANCE
|| animType < ANIM_FALL_BACK
|| animType > ANIM_FALL_FRONT_BLOOD) {
rotation = ROTATION_NE;
} else if (!artExists(buildFidInternal(frmId, weaponCode, animType, OBJ_TYPE_CRITTER, rotation))) {
rotation = rotation != ROTATION_E
&& artExists(buildFidInternal(frmId, weaponCode, animType, OBJ_TYPE_CRITTER, ROTATION_E))
? ROTATION_E
: ROTATION_NE;
} }
return buildFidInternal(frmId, weaponCode, animType, objectType, rotation);
if (animType == ANIM_FIRE_DANCE || animType < ANIM_FALL_BACK || animType > ANIM_FALL_FRONT_BLOOD) {
goto zero;
}
v7 = ((a3 << 12) & 0xF000) | ((animType << 16) & 0xFF0000) | 0x1000000;
v8 = ((rotation << 28) & 0x70000000) | v7;
v9 = frmId & 0xFFF;
if (artExists(v9 | v8) != 0) {
goto out;
}
if (objectType == rotation) {
goto zero;
}
v10 = objectType;
if (artExists(v9 | v7 | 0x10000000) != 0) {
goto out;
}
zero:
v10 = 0;
out:
return ((v10 << 28) & 0x70000000) | (objectType << 24) | ((animType << 16) & 0xFF0000) | ((a3 << 12) & 0xF000) | (frmId & 0xFFF);
} }
// 0x419D60 // 0x419D60

View File

@ -147,7 +147,7 @@ bool _art_fid_valid(int fid);
int _art_alias_num(int a1); int _art_alias_num(int a1);
int artCritterFidShouldRun(int a1); int artCritterFidShouldRun(int a1);
int artAliasFid(int fid); int artAliasFid(int fid);
int buildFid(int objectType, int frmId, int animType, int a4, int rotation); int buildFid(int objectType, int frmId, int animType, int weaponCode, int rotation);
Art* artLoad(const char* path); Art* artLoad(const char* path);
int artRead(const char* path, unsigned char* data); int artRead(const char* path, unsigned char* data);
int artWrite(const char* path, unsigned char* data); int artWrite(const char* path, unsigned char* data);

View File

@ -2620,7 +2620,7 @@ static int _ai_switch_weapons(Object* attacker, int* hitMode, Object** weapon, O
} }
if (*weapon != nullptr) { if (*weapon != nullptr) {
_inven_wield(attacker, *weapon, 1); _inven_wield(attacker, *weapon, HAND_RIGHT);
_combat_turn_run(); _combat_turn_run();
if (weaponGetActionPointCost(attacker, *hitMode, 0) <= attacker->data.critter.combat.ap) { if (weaponGetActionPointCost(attacker, *hitMode, 0) <= attacker->data.critter.combat.ap) {
return 0; return 0;

View File

@ -238,19 +238,19 @@ char* critterGetName(Object* obj)
return gDudeName; return gDudeName;
} }
if (obj->field_80 == -1) { if (obj->scriptIndex == -1) {
if (obj->sid != -1) { if (obj->sid != -1) {
Script* script; Script* script;
if (scriptGetScript(obj->sid, &script) != -1) { if (scriptGetScript(obj->sid, &script) != -1) {
obj->field_80 = script->field_14; obj->scriptIndex = script->index;
} }
} }
} }
char* name = nullptr; char* name = nullptr;
if (obj->field_80 != -1) { if (obj->scriptIndex != -1) {
MessageListItem messageListItem; MessageListItem messageListItem;
messageListItem.num = 101 + obj->field_80; messageListItem.num = 101 + obj->scriptIndex;
if (messageListGetItem(&gCritterMessageList, &messageListItem)) { if (messageListGetItem(&gCritterMessageList, &messageListItem)) {
name = messageListItem.text; name = messageListItem.text;
} }

View File

@ -587,7 +587,10 @@ static void _demo_copy_title(int win);
static void _demo_copy_options(int win); static void _demo_copy_options(int win);
static void _gDialogRefreshOptionsRect(int win, Rect* drawRect); static void _gDialogRefreshOptionsRect(int win, Rect* drawRect);
static void gameDialogTicker(); static void gameDialogTicker();
static void _gdialog_scroll_subwin(int a1, int a2, unsigned char* a3, unsigned char* a4, unsigned char* a5, int a6, int a7); // Animates scroll up or down of a given dialog sub-window.
// If scrolling up - only uses subWindowFrmData to gradually fill the window (must be pre-filled with bg window contents).
// If scroliing down - uses both subWindowFrmData and bgWindowFrmData to fill parts of window buffer.
static void _gdialog_scroll_subwin(int windowIdx, bool scrollUp, unsigned char* subWindowFrmData, unsigned char* windowBuf, unsigned char* bgWindowFrmData, int windowHeight, bool instantScrollUp = false);
static int _text_num_lines(const char* a1, int a2); static int _text_num_lines(const char* a1, int a2);
static int text_to_rect_wrapped(unsigned char* buffer, Rect* rect, char* string, int* a4, int height, int pitch, int color); static int text_to_rect_wrapped(unsigned char* buffer, Rect* rect, char* string, int* a4, int height, int pitch, int color);
static int gameDialogDrawText(unsigned char* buffer, Rect* rect, char* string, int* a4, int height, int pitch, int color, int a7); static int gameDialogDrawText(unsigned char* buffer, Rect* rect, char* string, int* a4, int height, int pitch, int color, int a7);
@ -2932,44 +2935,42 @@ void _talk_to_critter_reacts(int a1)
} }
// 0x447D98 // 0x447D98
void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a4, unsigned char* a5, int a6, int a7) void _gdialog_scroll_subwin(int windowIdx, bool scrollUp, unsigned char* windowFrmData, unsigned char* windowBuf, unsigned char* bgWindowFrmData, int windowHeight, bool instantScrollUp)
{ {
int v7; constexpr int stripHeight = 10;
unsigned char* v9; int height = windowHeight;
unsigned char* dest = windowBuf;
Rect rect; Rect rect;
v7 = a6; if (scrollUp) {
v9 = a4;
if (a2 == 1) {
rect.left = 0; rect.left = 0;
rect.right = GAME_DIALOG_WINDOW_WIDTH - 1; rect.right = GAME_DIALOG_WINDOW_WIDTH - 1;
rect.bottom = a6 - 1; rect.bottom = windowHeight - 1;
int v18 = a6 / 10; int strips = windowHeight / stripHeight;
if (a7 == -1) { if (instantScrollUp) {
rect.top = 10; rect.top = stripHeight;
v18 = 0; strips = 0;
} else { } else {
rect.top = v18 * 10; rect.top = strips * stripHeight;
v7 = a6 % 10; height = windowHeight % stripHeight;
v9 += GAME_DIALOG_WINDOW_WIDTH * rect.top; dest += GAME_DIALOG_WINDOW_WIDTH * rect.top;
} }
for (; v18 >= 0; v18--) { for (; strips >= 0; strips--) {
sharedFpsLimiter.mark(); sharedFpsLimiter.mark();
soundContinueAll(); soundContinueAll();
blitBufferToBuffer(a3, blitBufferToBuffer(windowFrmData,
GAME_DIALOG_WINDOW_WIDTH, GAME_DIALOG_WINDOW_WIDTH,
v7, height,
GAME_DIALOG_WINDOW_WIDTH, GAME_DIALOG_WINDOW_WIDTH,
v9, dest,
GAME_DIALOG_WINDOW_WIDTH); GAME_DIALOG_WINDOW_WIDTH);
rect.top -= 10; rect.top -= stripHeight;
windowRefreshRect(win, &rect); windowRefreshRect(windowIdx, &rect);
v7 += 10; height += stripHeight;
v9 -= 10 * (GAME_DIALOG_WINDOW_WIDTH); dest -= stripHeight * (GAME_DIALOG_WINDOW_WIDTH);
delay_ms(33); delay_ms(33);
@ -2978,36 +2979,36 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
} }
} else { } else {
rect.right = GAME_DIALOG_WINDOW_WIDTH - 1; rect.right = GAME_DIALOG_WINDOW_WIDTH - 1;
rect.bottom = a6 - 1; rect.bottom = windowHeight - 1;
rect.left = 0; rect.left = 0;
rect.top = 0; rect.top = 0;
for (int index = a6 / 10; index > 0; index--) { for (int strips = windowHeight / stripHeight; strips > 0; strips--) {
sharedFpsLimiter.mark(); sharedFpsLimiter.mark();
soundContinueAll(); soundContinueAll();
blitBufferToBuffer(a5, blitBufferToBuffer(bgWindowFrmData,
GAME_DIALOG_WINDOW_WIDTH, GAME_DIALOG_WINDOW_WIDTH,
10, stripHeight,
GAME_DIALOG_WINDOW_WIDTH, GAME_DIALOG_WINDOW_WIDTH,
v9, dest,
GAME_DIALOG_WINDOW_WIDTH); GAME_DIALOG_WINDOW_WIDTH);
v9 += 10 * (GAME_DIALOG_WINDOW_WIDTH); dest += stripHeight * (GAME_DIALOG_WINDOW_WIDTH);
v7 -= 10; height -= stripHeight;
a5 += 10 * (GAME_DIALOG_WINDOW_WIDTH); bgWindowFrmData += stripHeight * (GAME_DIALOG_WINDOW_WIDTH);
blitBufferToBuffer(a3, blitBufferToBuffer(windowFrmData,
GAME_DIALOG_WINDOW_WIDTH, GAME_DIALOG_WINDOW_WIDTH,
v7, height,
GAME_DIALOG_WINDOW_WIDTH, GAME_DIALOG_WINDOW_WIDTH,
v9, dest,
GAME_DIALOG_WINDOW_WIDTH); GAME_DIALOG_WINDOW_WIDTH);
windowRefreshRect(win, &rect); windowRefreshRect(windowIdx, &rect);
rect.top += 10; rect.top += stripHeight;
delay_ms(33); delay_ms(33);
@ -3228,7 +3229,7 @@ int _gdialog_barter_create_win()
unsigned char* backgroundWindowBuffer = windowGetBuffer(gGameDialogBackgroundWindow); unsigned char* backgroundWindowBuffer = windowGetBuffer(gGameDialogBackgroundWindow);
blitBufferToBuffer(backgroundWindowBuffer + width * (480 - _dialogue_subwin_len), width, _dialogue_subwin_len, width, windowBuffer, width); blitBufferToBuffer(backgroundWindowBuffer + width * (480 - _dialogue_subwin_len), width, _dialogue_subwin_len, width, windowBuffer, width);
_gdialog_scroll_subwin(gGameDialogWindow, 1, backgroundData, windowBuffer, nullptr, _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, true, backgroundData, windowBuffer, nullptr, _dialogue_subwin_len);
backgroundFrmImage.unlock(); backgroundFrmImage.unlock();
@ -3306,7 +3307,7 @@ void _gdialog_barter_destroy_win()
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, frmId, 0, 0, 0); int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, frmId, 0, 0, 0);
if (backgroundFrmImage.lock(backgroundFid)) { if (backgroundFrmImage.lock(backgroundFid)) {
unsigned char* windowBuffer = windowGetBuffer(gGameDialogWindow); unsigned char* windowBuffer = windowGetBuffer(gGameDialogWindow);
_gdialog_scroll_subwin(gGameDialogWindow, 0, backgroundFrmImage.getData(), windowBuffer, backgroundWindowBuffer, _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, false, backgroundFrmImage.getData(), windowBuffer, backgroundWindowBuffer, _dialogue_subwin_len);
} }
windowDestroy(gGameDialogWindow); windowDestroy(gGameDialogWindow);
@ -3380,7 +3381,7 @@ int partyMemberControlWindowInit()
unsigned char* windowBuffer = windowGetBuffer(gGameDialogWindow); unsigned char* windowBuffer = windowGetBuffer(gGameDialogWindow);
unsigned char* src = windowGetBuffer(gGameDialogBackgroundWindow); unsigned char* src = windowGetBuffer(gGameDialogBackgroundWindow);
blitBufferToBuffer(src + (GAME_DIALOG_WINDOW_WIDTH) * (GAME_DIALOG_WINDOW_HEIGHT - _dialogue_subwin_len), GAME_DIALOG_WINDOW_WIDTH, _dialogue_subwin_len, GAME_DIALOG_WINDOW_WIDTH, windowBuffer, GAME_DIALOG_WINDOW_WIDTH); blitBufferToBuffer(src + (GAME_DIALOG_WINDOW_WIDTH) * (GAME_DIALOG_WINDOW_HEIGHT - _dialogue_subwin_len), GAME_DIALOG_WINDOW_WIDTH, _dialogue_subwin_len, GAME_DIALOG_WINDOW_WIDTH, windowBuffer, GAME_DIALOG_WINDOW_WIDTH);
_gdialog_scroll_subwin(gGameDialogWindow, 1, backgroundData, windowBuffer, nullptr, _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, true, backgroundData, windowBuffer, nullptr, _dialogue_subwin_len);
backgroundFrmImage.unlock(); backgroundFrmImage.unlock();
// TALK // TALK
@ -3529,7 +3530,7 @@ void partyMemberControlWindowFree()
FrmImage backgroundFrmImage; FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 390, 0, 0, 0); int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 390, 0, 0, 0);
if (backgroundFrmImage.lock(backgroundFid)) { if (backgroundFrmImage.lock(backgroundFid)) {
_gdialog_scroll_subwin(gGameDialogWindow, 0, backgroundFrmImage.getData(), windowGetBuffer(gGameDialogWindow), windowGetBuffer(gGameDialogBackgroundWindow) + (GAME_DIALOG_WINDOW_WIDTH) * (480 - _dialogue_subwin_len), _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, false, backgroundFrmImage.getData(), windowGetBuffer(gGameDialogWindow), windowGetBuffer(gGameDialogBackgroundWindow) + (GAME_DIALOG_WINDOW_WIDTH) * (480 - _dialogue_subwin_len), _dialogue_subwin_len);
} }
windowDestroy(gGameDialogWindow); windowDestroy(gGameDialogWindow);
@ -3715,7 +3716,7 @@ void partyMemberControlWindowHandleEvents()
Object* weapon = _ai_search_inven_weap(gGameDialogSpeaker, 0, nullptr); Object* weapon = _ai_search_inven_weap(gGameDialogSpeaker, 0, nullptr);
if (weapon != nullptr) { if (weapon != nullptr) {
_inven_wield(gGameDialogSpeaker, weapon, 1); _inven_wield(gGameDialogSpeaker, weapon, HAND_RIGHT);
aiAttemptWeaponReload(gGameDialogSpeaker, 0); aiAttemptWeaponReload(gGameDialogSpeaker, 0);
int num = _gdPickAIUpdateMsg(gGameDialogSpeaker); int num = _gdPickAIUpdateMsg(gGameDialogSpeaker);
@ -3826,7 +3827,7 @@ int partyMemberCustomizationWindowInit()
windowBuffer, windowBuffer,
GAME_DIALOG_WINDOW_WIDTH); GAME_DIALOG_WINDOW_WIDTH);
_gdialog_scroll_subwin(gGameDialogWindow, 1, backgroundFrmData, windowBuffer, nullptr, _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, true, backgroundFrmData, windowBuffer, nullptr, _dialogue_subwin_len);
backgroundFrmImage.unlock(); backgroundFrmImage.unlock();
_gdialog_buttons[0] = buttonCreate(gGameDialogWindow, 593, 101, 14, 14, -1, -1, -1, 13, _redButtonNormalFrmImage.getData(), _redButtonPressedFrmImage.getData(), nullptr, BUTTON_FLAG_TRANSPARENT); _gdialog_buttons[0] = buttonCreate(gGameDialogWindow, 593, 101, 14, 14, -1, -1, -1, 13, _redButtonNormalFrmImage.getData(), _redButtonPressedFrmImage.getData(), nullptr, BUTTON_FLAG_TRANSPARENT);
@ -3934,7 +3935,7 @@ void partyMemberCustomizationWindowFree()
// custom.frm - party member control interface // custom.frm - party member control interface
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 391, 0, 0, 0); int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 391, 0, 0, 0);
if (backgroundFrmImage.lock(backgroundFid)) { if (backgroundFrmImage.lock(backgroundFid)) {
_gdialog_scroll_subwin(gGameDialogWindow, 0, backgroundFrmImage.getData(), windowGetBuffer(gGameDialogWindow), windowGetBuffer(gGameDialogBackgroundWindow) + (GAME_DIALOG_WINDOW_WIDTH) * (480 - _dialogue_subwin_len), _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, false, backgroundFrmImage.getData(), windowGetBuffer(gGameDialogWindow), windowGetBuffer(gGameDialogBackgroundWindow) + (GAME_DIALOG_WINDOW_WIDTH) * (480 - _dialogue_subwin_len), _dialogue_subwin_len);
} }
windowDestroy(gGameDialogWindow); windowDestroy(gGameDialogWindow);
@ -4339,17 +4340,17 @@ int _gdialog_window_create()
gGameDialogWindow = windowCreate(dialogSubwindowX, dialogSubwindowY, screenWidth, _dialogue_subwin_len, 256, WINDOW_DONT_MOVE_TOP); gGameDialogWindow = windowCreate(dialogSubwindowX, dialogSubwindowY, screenWidth, _dialogue_subwin_len, 256, WINDOW_DONT_MOVE_TOP);
if (gGameDialogWindow != -1) { if (gGameDialogWindow != -1) {
unsigned char* v10 = windowGetBuffer(gGameDialogWindow); unsigned char* windowBuf = windowGetBuffer(gGameDialogWindow);
unsigned char* v14 = windowGetBuffer(gGameDialogBackgroundWindow); unsigned char* bgWindowBuf = windowGetBuffer(gGameDialogBackgroundWindow);
// TODO: Not sure about offsets. // TODO: Not sure about offsets.
blitBufferToBuffer(v14 + screenWidth * (GAME_DIALOG_WINDOW_HEIGHT - _dialogue_subwin_len), screenWidth, _dialogue_subwin_len, screenWidth, v10, screenWidth); blitBufferToBuffer(bgWindowBuf + screenWidth * (GAME_DIALOG_WINDOW_HEIGHT - _dialogue_subwin_len), screenWidth, _dialogue_subwin_len, screenWidth, windowBuf, screenWidth);
if (_dialogue_just_started) { if (_dialogue_just_started) {
windowRefresh(gGameDialogBackgroundWindow); windowRefresh(gGameDialogBackgroundWindow);
_gdialog_scroll_subwin(gGameDialogWindow, 1, backgroundFrmData, v10, nullptr, _dialogue_subwin_len, -1); _gdialog_scroll_subwin(gGameDialogWindow, true, backgroundFrmData, windowBuf, nullptr, _dialogue_subwin_len, true);
_dialogue_just_started = 0; _dialogue_just_started = 0;
} else { } else {
_gdialog_scroll_subwin(gGameDialogWindow, 1, backgroundFrmData, v10, nullptr, _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, true, backgroundFrmData, windowBuf, nullptr, _dialogue_subwin_len, false);
} }
// BARTER/TRADE // BARTER/TRADE
@ -4438,7 +4439,7 @@ void _gdialog_window_destroy()
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, frmId, 0, 0, 0); int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, frmId, 0, 0, 0);
if (backgroundFrmImage.lock(backgroundFid)) { if (backgroundFrmImage.lock(backgroundFid)) {
unsigned char* windowBuffer = windowGetBuffer(gGameDialogWindow); unsigned char* windowBuffer = windowGetBuffer(gGameDialogWindow);
_gdialog_scroll_subwin(gGameDialogWindow, 0, backgroundFrmImage.getData(), windowBuffer, backgroundWindowBuffer, _dialogue_subwin_len, 0); _gdialog_scroll_subwin(gGameDialogWindow, false, backgroundFrmImage.getData(), windowBuffer, backgroundWindowBuffer, _dialogue_subwin_len);
windowDestroy(gGameDialogWindow); windowDestroy(gGameDialogWindow);
_gdialog_window_created = 0; _gdialog_window_created = 0;
gGameDialogWindow = -1; gGameDialogWindow = -1;

View File

@ -318,7 +318,7 @@ static int gameMouseActionMenuInit();
static void gameMouseActionMenuFree(); static void gameMouseActionMenuFree();
static int gmouse_3d_set_flat_fid(int fid, Rect* rect); static int gmouse_3d_set_flat_fid(int fid, Rect* rect);
static int gameMouseUpdateHexCursorFid(Rect* rect); static int gameMouseUpdateHexCursorFid(Rect* rect);
static int _gmouse_3d_move_to(int x, int y, int elevation, Rect* a4); static int _gmouse_3d_move_to(int x, int y, int elevation, Rect* rect);
static int gameMouseHandleScrolling(int x, int y, int cursor); static int gameMouseHandleScrolling(int x, int y, int cursor);
static int objectIsDoor(Object* object); static int objectIsDoor(Object* object);
static bool gameMouseClickOnInterfaceBar(); static bool gameMouseClickOnInterfaceBar();
@ -950,46 +950,46 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
} }
if (gGameMouseMode == GAME_MOUSE_MODE_ARROW) { if (gGameMouseMode == GAME_MOUSE_MODE_ARROW) {
Object* v5 = gameMouseGetObjectUnderCursor(-1, true, gElevation); Object* targetObj = gameMouseGetObjectUnderCursor(-1, true, gElevation);
if (v5 != nullptr) { if (targetObj != nullptr) {
switch (FID_TYPE(v5->fid)) { switch (FID_TYPE(targetObj->fid)) {
case OBJ_TYPE_ITEM: case OBJ_TYPE_ITEM:
actionPickUp(gDude, v5); actionPickUp(gDude, targetObj);
break; break;
case OBJ_TYPE_CRITTER: case OBJ_TYPE_CRITTER:
if (v5 == gDude) { if (targetObj == gDude) {
if (FID_ANIM_TYPE(gDude->fid) == ANIM_STAND) { if (FID_ANIM_TYPE(gDude->fid) == ANIM_STAND) {
Rect a1; Rect dudeRect;
if (objectRotateClockwise(v5, &a1) == 0) { if (objectRotateClockwise(targetObj, &dudeRect) == 0) {
tileWindowRefreshRect(&a1, v5->elevation); tileWindowRefreshRect(&dudeRect, targetObj->elevation);
} }
} }
} else { } else {
if (_obj_action_can_talk_to(v5)) { if (_obj_action_can_talk_to(targetObj)) {
if (isInCombat()) { if (isInCombat()) {
if (_obj_examine(gDude, v5) == -1) { if (_obj_examine(gDude, targetObj) == -1) {
_obj_look_at(gDude, v5); _obj_look_at(gDude, targetObj);
} }
} else { } else {
actionTalk(gDude, v5); actionTalk(gDude, targetObj);
} }
} else { } else {
_action_loot_container(gDude, v5); _action_loot_container(gDude, targetObj);
} }
} }
break; break;
case OBJ_TYPE_SCENERY: case OBJ_TYPE_SCENERY:
if (_obj_action_can_use(v5)) { if (_obj_action_can_use(targetObj)) {
_action_use_an_object(gDude, v5); _action_use_an_object(gDude, targetObj);
} else { } else {
if (_obj_examine(gDude, v5) == -1) { if (_obj_examine(gDude, targetObj) == -1) {
_obj_look_at(gDude, v5); _obj_look_at(gDude, targetObj);
} }
} }
break; break;
case OBJ_TYPE_WALL: case OBJ_TYPE_WALL:
if (_obj_examine(gDude, v5) == -1) { if (_obj_examine(gDude, targetObj) == -1) {
_obj_look_at(gDude, v5); _obj_look_at(gDude, targetObj);
} }
break; break;
} }
@ -998,16 +998,16 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
} }
if (gGameMouseMode == GAME_MOUSE_MODE_CROSSHAIR) { if (gGameMouseMode == GAME_MOUSE_MODE_CROSSHAIR) {
Object* v7 = gameMouseGetObjectUnderCursor(OBJ_TYPE_CRITTER, false, gElevation); Object* targetObj = gameMouseGetObjectUnderCursor(OBJ_TYPE_CRITTER, false, gElevation);
if (v7 == nullptr) { if (targetObj == nullptr) {
v7 = gameMouseGetObjectUnderCursor(-1, false, gElevation); targetObj = gameMouseGetObjectUnderCursor(-1, false, gElevation);
if (!objectIsDoor(v7)) { if (!objectIsDoor(targetObj)) {
v7 = nullptr; targetObj = nullptr;
} }
} }
if (v7 != nullptr) { if (targetObj != nullptr) {
_combat_attack_this(v7); _combat_attack_this(targetObj);
_gmouse_3d_hover_test = true; _gmouse_3d_hover_test = true;
gGameMouseLastY = mouseY; gGameMouseLastY = mouseY;
gGameMouseLastX = mouseX; gGameMouseLastX = mouseX;
@ -1065,35 +1065,35 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
} }
if ((mouseState & MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT) == MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT && gGameMouseMode == GAME_MOUSE_MODE_ARROW) { if ((mouseState & MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT) == MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT && gGameMouseMode == GAME_MOUSE_MODE_ARROW) {
Object* v16 = gameMouseGetObjectUnderCursor(-1, true, gElevation); Object* targetObj = gameMouseGetObjectUnderCursor(-1, true, gElevation);
if (v16 != nullptr) { if (targetObj != nullptr) {
int actionMenuItemsCount = 0; int actionMenuItemsCount = 0;
int actionMenuItems[6]; int actionMenuItems[6];
switch (FID_TYPE(v16->fid)) { switch (FID_TYPE(targetObj->fid)) {
case OBJ_TYPE_ITEM: case OBJ_TYPE_ITEM:
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE;
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_LOOK; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_LOOK;
if (itemGetType(v16) == ITEM_TYPE_CONTAINER) { if (itemGetType(targetObj) == ITEM_TYPE_CONTAINER) {
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_INVENTORY; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_INVENTORY;
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE_SKILL; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE_SKILL;
} }
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_CANCEL; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_CANCEL;
break; break;
case OBJ_TYPE_CRITTER: case OBJ_TYPE_CRITTER:
if (v16 == gDude) { if (targetObj == gDude) {
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_ROTATE; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_ROTATE;
} else { } else {
if (_obj_action_can_talk_to(v16)) { if (_obj_action_can_talk_to(targetObj)) {
if (!isInCombat()) { if (!isInCombat()) {
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_TALK; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_TALK;
} }
} else { } else {
if (!_critter_flag_check(v16->pid, CRITTER_NO_STEAL)) { if (!_critter_flag_check(targetObj->pid, CRITTER_NO_STEAL)) {
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE;
} }
} }
if (actionCheckPush(gDude, v16)) { if (actionCheckPush(gDude, targetObj)) {
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_PUSH; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_PUSH;
} }
} }
@ -1104,7 +1104,7 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_CANCEL; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_CANCEL;
break; break;
case OBJ_TYPE_SCENERY: case OBJ_TYPE_SCENERY:
if (_obj_action_can_use(v16)) { if (_obj_action_can_use(targetObj)) {
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE;
} }
@ -1115,7 +1115,7 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
break; break;
case OBJ_TYPE_WALL: case OBJ_TYPE_WALL:
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_LOOK; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_LOOK;
if (_obj_action_can_use(v16)) { if (_obj_action_can_use(targetObj)) {
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_INVENTORY; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_INVENTORY;
} }
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_CANCEL; actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_CANCEL;
@ -1123,14 +1123,14 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
} }
if (gameMouseRenderActionMenuItems(mouseX, mouseY, actionMenuItems, actionMenuItemsCount, _scr_size.right - _scr_size.left + 1, _scr_size.bottom - _scr_size.top - 99) == 0) { if (gameMouseRenderActionMenuItems(mouseX, mouseY, actionMenuItems, actionMenuItemsCount, _scr_size.right - _scr_size.left + 1, _scr_size.bottom - _scr_size.top - 99) == 0) {
Rect v43; Rect cursorRect;
int fid = buildFid(OBJ_TYPE_INTERFACE, 283, 0, 0, 0); int fid = buildFid(OBJ_TYPE_INTERFACE, 283, 0, 0, 0);
// NOTE: Uninline. // NOTE: Uninline.
if (gmouse_3d_set_flat_fid(fid, &v43) == 0 && _gmouse_3d_move_to(mouseX, mouseY, gElevation, &v43) == 0) { if (gmouse_3d_set_flat_fid(fid, &cursorRect) == 0 && _gmouse_3d_move_to(mouseX, mouseY, gElevation, &cursorRect) == 0) {
tileWindowRefreshRect(&v43, gElevation); tileWindowRefreshRect(&cursorRect, gElevation);
isoDisable(); isoDisable();
int v33 = mouseY; int newMouseY = mouseY;
int actionIndex = 0; int actionIndex = 0;
while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) == 0) { while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) == 0) {
sharedFpsLimiter.mark(); sharedFpsLimiter.mark();
@ -1141,21 +1141,21 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
actionMenuItems[actionIndex] = 0; actionMenuItems[actionIndex] = 0;
} }
int v48; int updatedMouseX;
int v47; int updatedMouseY;
mouseGetPosition(&v48, &v47); mouseGetPosition(&updatedMouseX, &updatedMouseY);
if (abs(v47 - v33) > 10) { if (abs(updatedMouseY - newMouseY) > 10) {
if (v33 >= v47) { if (newMouseY >= updatedMouseY) {
actionIndex -= 1; actionIndex -= 1;
} else { } else {
actionIndex += 1; actionIndex += 1;
} }
if (gameMouseHighlightActionMenuItemAtIndex(actionIndex) == 0) { if (gameMouseHighlightActionMenuItemAtIndex(actionIndex) == 0) {
tileWindowRefreshRect(&v43, gElevation); tileWindowRefreshRect(&cursorRect, gElevation);
} }
v33 = v47; newMouseY = updatedMouseY;
} }
renderPresent(); renderPresent();
@ -1169,39 +1169,39 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
gGameMouseLastY = mouseY; gGameMouseLastY = mouseY;
_gmouse_3d_last_move_time = getTicks(); _gmouse_3d_last_move_time = getTicks();
_mouse_set_position(mouseX, v33); _mouse_set_position(mouseX, newMouseY);
if (gameMouseUpdateHexCursorFid(&v43) == 0) { if (gameMouseUpdateHexCursorFid(&cursorRect) == 0) {
tileWindowRefreshRect(&v43, gElevation); tileWindowRefreshRect(&cursorRect, gElevation);
} }
switch (actionMenuItems[actionIndex]) { switch (actionMenuItems[actionIndex]) {
case GAME_MOUSE_ACTION_MENU_ITEM_INVENTORY: case GAME_MOUSE_ACTION_MENU_ITEM_INVENTORY:
inventoryOpenUseItemOn(v16); inventoryOpenUseItemOn(targetObj);
break; break;
case GAME_MOUSE_ACTION_MENU_ITEM_LOOK: case GAME_MOUSE_ACTION_MENU_ITEM_LOOK:
if (_obj_examine(gDude, v16) == -1) { if (_obj_examine(gDude, targetObj) == -1) {
_obj_look_at(gDude, v16); _obj_look_at(gDude, targetObj);
} }
break; break;
case GAME_MOUSE_ACTION_MENU_ITEM_ROTATE: case GAME_MOUSE_ACTION_MENU_ITEM_ROTATE:
if (objectRotateClockwise(v16, &v43) == 0) { if (objectRotateClockwise(targetObj, &cursorRect) == 0) {
tileWindowRefreshRect(&v43, v16->elevation); tileWindowRefreshRect(&cursorRect, targetObj->elevation);
} }
break; break;
case GAME_MOUSE_ACTION_MENU_ITEM_TALK: case GAME_MOUSE_ACTION_MENU_ITEM_TALK:
actionTalk(gDude, v16); actionTalk(gDude, targetObj);
break; break;
case GAME_MOUSE_ACTION_MENU_ITEM_USE: case GAME_MOUSE_ACTION_MENU_ITEM_USE:
switch (FID_TYPE(v16->fid)) { switch (FID_TYPE(targetObj->fid)) {
case OBJ_TYPE_SCENERY: case OBJ_TYPE_SCENERY:
_action_use_an_object(gDude, v16); _action_use_an_object(gDude, targetObj);
break; break;
case OBJ_TYPE_CRITTER: case OBJ_TYPE_CRITTER:
_action_loot_container(gDude, v16); _action_loot_container(gDude, targetObj);
break; break;
default: default:
actionPickUp(gDude, v16); actionPickUp(gDude, targetObj);
break; break;
} }
break; break;
@ -1238,12 +1238,12 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
} }
if (skill != -1) { if (skill != -1) {
actionUseSkill(gDude, v16, skill); actionUseSkill(gDude, targetObj, skill);
} }
} }
break; break;
case GAME_MOUSE_ACTION_MENU_ITEM_PUSH: case GAME_MOUSE_ACTION_MENU_ITEM_PUSH:
actionPush(gDude, v16); actionPush(gDude, targetObj);
break; break;
} }
} }
@ -1370,9 +1370,9 @@ void gameMouseSetMode(int mode)
int mouseY; int mouseY;
mouseGetPosition(&mouseX, &mouseY); mouseGetPosition(&mouseX, &mouseY);
Rect r2; Rect cursorRect;
if (_gmouse_3d_move_to(mouseX, mouseY, gElevation, &r2) == 0) { if (_gmouse_3d_move_to(mouseX, mouseY, gElevation, &cursorRect) == 0) {
rectUnion(&rect, &r2, &rect); rectUnion(&rect, &cursorRect, &rect);
} }
int v5 = 0; int v5 = 0;
@ -1386,13 +1386,13 @@ void gameMouseSetMode(int mode)
} }
if (gGameMouseMode == 0) { if (gGameMouseMode == 0) {
if (objectDisableOutline(gGameMouseHexCursor, &r2) == 0) { if (objectDisableOutline(gGameMouseHexCursor, &cursorRect) == 0) {
rectUnion(&rect, &r2, &rect); rectUnion(&rect, &cursorRect, &rect);
} }
} }
} else { } else {
if (objectEnableOutline(gGameMouseHexCursor, &r2) == 0) { if (objectEnableOutline(gGameMouseHexCursor, &cursorRect) == 0) {
rectUnion(&rect, &r2, &rect); rectUnion(&rect, &cursorRect, &rect);
} }
} }
@ -2193,7 +2193,7 @@ int gameMouseUpdateHexCursorFid(Rect* rect)
} }
// 0x44DF94 // 0x44DF94
int _gmouse_3d_move_to(int x, int y, int elevation, Rect* a4) int _gmouse_3d_move_to(int x, int y, int elevation, Rect* rect)
{ {
if (_gmouse_mapper_mode == 0) { if (_gmouse_mapper_mode == 0) {
if (gGameMouseMode != GAME_MOUSE_MODE_MOVE) { if (gGameMouseMode != GAME_MOUSE_MODE_MOVE) {
@ -2214,7 +2214,7 @@ int _gmouse_3d_move_to(int x, int y, int elevation, Rect* a4)
artUnlock(hexCursorFrmHandle); artUnlock(hexCursorFrmHandle);
} }
_obj_move(gGameMouseHexCursor, x + offsetX, y + offsetY, elevation, a4); _obj_move(gGameMouseHexCursor, x + offsetX, y + offsetY, elevation, rect);
} else { } else {
int tile = tileFromScreenXY(x, y, 0); int tile = tileFromScreenXY(x, y, 0);
if (tile != -1) { if (tile != -1) {
@ -2237,7 +2237,7 @@ int _gmouse_3d_move_to(int x, int y, int elevation, Rect* a4)
rectCopy(&rect1, &rect2); rectCopy(&rect1, &rect2);
} }
rectCopy(a4, &rect1); rectCopy(rect, &rect1);
} }
} }
} }
@ -2323,7 +2323,7 @@ int _gmouse_3d_move_to(int x, int y, int elevation, Rect* a4)
} }
if (v1) { if (v1) {
rectCopy(a4, &rect1); rectCopy(rect, &rect1);
} }
} }

View File

@ -6,14 +6,6 @@
namespace fallout { namespace fallout {
typedef enum Hand {
// Item1 (Punch)
HAND_LEFT,
// Item2 (Kick)
HAND_RIGHT,
HAND_COUNT,
} Hand;
#define INDICATOR_BOX_WIDTH 130 #define INDICATOR_BOX_WIDTH 130
#define INDICATOR_BOX_HEIGHT 21 #define INDICATOR_BOX_HEIGHT 21

View File

@ -482,7 +482,7 @@ static void opScrReturn(Program* program)
Script* script; Script* script;
if (scriptGetScript(sid, &script) != -1) { if (scriptGetScript(sid, &script) != -1) {
script->field_28 = data; script->returnValue = data;
} }
} }
@ -934,7 +934,7 @@ static void opCreateObject(Program* program)
goto out; goto out;
} }
script->field_14 = sid - 1; script->index = sid - 1;
if (scriptType == SCRIPT_TYPE_SPATIAL) { if (scriptType == SCRIPT_TYPE_SPATIAL) {
script->sp.built_tile = builtTileCreate(object->tile, object->elevation); script->sp.built_tile = builtTileCreate(object->tile, object->elevation);
@ -942,7 +942,7 @@ static void opCreateObject(Program* program)
} }
object->id = scriptsNewObjectId(); object->id = scriptsNewObjectId();
script->field_1C = object->id; script->ownerId = object->id;
script->owner = object; script->owner = object;
_scr_find_str_run_info(sid - 1, &(script->field_50), object->sid); _scr_find_str_run_info(sid - 1, &(script->field_50), object->sid);
}; };

File diff suppressed because it is too large Load Diff

View File

@ -5,28 +5,41 @@
namespace fallout { namespace fallout {
typedef enum Hand {
// Item1 (Punch)
HAND_LEFT,
// Item2 (Kick)
HAND_RIGHT,
HAND_COUNT,
} Hand;
typedef void InventoryPrintItemDescriptionHandler(char* string); typedef void InventoryPrintItemDescriptionHandler(char* string);
void _inven_reset_dude(); void _inven_reset_dude();
void inventoryOpen(); void inventoryOpen();
void _adjust_ac(Object* critter, Object* oldArmor, Object* newArmor); void _adjust_ac(Object* critter, Object* oldArmor, Object* newArmor);
void inventoryOpenUseItemOn(Object* a1); void inventoryOpenUseItemOn(Object* targetObj);
Object* critterGetItem2(Object* obj); Object* critterGetItem2(Object* critter);
Object* critterGetItem1(Object* obj); Object* critterGetItem1(Object* critter);
Object* critterGetArmor(Object* obj); Object* critterGetArmor(Object* critter);
Object* objectGetCarriedObjectByPid(Object* obj, int pid); Object* objectGetCarriedObjectByPid(Object* obj, int pid);
int objectGetCarriedQuantityByPid(Object* obj, int pid); int objectGetCarriedQuantityByPid(Object* obj, int pid);
Object* _inven_find_type(Object* obj, int a2, int* inout_a3); Object* _inven_find_type(Object* obj, int itemType, int* indexPtr);
Object* _inven_find_id(Object* obj, int a2); Object* _inven_find_id(Object* obj, int id);
Object* _inven_index_ptr(Object* obj, int a2); Object* _inven_index_ptr(Object* obj, int index);
int _inven_wield(Object* a1, Object* a2, int a3); // Makes critter equip a given item in a given hand slot with an animation.
int _invenWieldFunc(Object* a1, Object* a2, int a3, bool a4); // 0 - left hand, 1 - right hand. If item is armor, hand value is ignored.
int _inven_unwield(Object* critter_obj, int a2); int _inven_wield(Object* critter, Object* item, int hand);
int _invenUnwieldFunc(Object* obj, int a2, int a3); // Same as inven_wield but allows to wield item without animation.
int _invenWieldFunc(Object* critter, Object* item, int hand, bool animate);
// Makes critter unequip an item in a given hand slot with an animation.
int _inven_unwield(Object* critter, int hand);
// Same as inven_unwield but allows to unwield item without animation.
int _invenUnwieldFunc(Object* critter, int hand, bool animate);
int inventoryOpenLooting(Object* looter, Object* target); int inventoryOpenLooting(Object* looter, Object* target);
int inventoryOpenStealing(Object* thief, Object* target); int inventoryOpenStealing(Object* thief, Object* target);
void inventoryOpenTrade(int win, Object* barterer, Object* playerTable, Object* bartererTable, int barterMod); void inventoryOpenTrade(int win, Object* barterer, Object* playerTable, Object* bartererTable, int barterMod);
int _inven_set_timer(Object* a1); int _inven_set_timer(Object* item);
Object* inven_get_current_target_obj(); Object* inven_get_current_target_obj();
} // namespace fallout } // namespace fallout

View File

@ -47,8 +47,8 @@ static int _item_move_func(Object* source, Object* target, Object* item, int qua
static bool _item_identical(Object* item1, Object* item2); static bool _item_identical(Object* item1, Object* item2);
static int stealthBoyTurnOn(Object* object); static int stealthBoyTurnOn(Object* object);
static int stealthBoyTurnOff(Object* critter, Object* item); static int stealthBoyTurnOff(Object* critter, Object* item);
static int _insert_drug_effect(Object* critter_obj, Object* item_obj, int a3, int* stats, int* mods); static int _insert_drug_effect(Object* critter, Object* item, int duration, int* stats, int* mods);
static void _perform_drug_effect(Object* critter_obj, int* stats, int* mods, bool is_immediate); static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool isImmediate);
static bool _drug_effect_allowed(Object* critter, int pid); static bool _drug_effect_allowed(Object* critter, int pid);
static int _insert_withdrawal(Object* obj, int a2, int a3, int a4, int a5); static int _insert_withdrawal(Object* obj, int a2, int a3, int a4, int a5);
static int _item_wd_clear_all(Object* a1, void* data); static int _item_wd_clear_all(Object* a1, void* data);
@ -319,7 +319,7 @@ int itemAttemptAdd(Object* owner, Object* itemToAdd, int quantity)
return itemAdd(owner, itemToAdd, quantity); return itemAdd(owner, itemToAdd, quantity);
} }
// item_add // item_add_force
// 0x4772B8 // 0x4772B8
int itemAdd(Object* owner, Object* itemToAdd, int quantity) int itemAdd(Object* owner, Object* itemToAdd, int quantity)
{ {
@ -675,14 +675,15 @@ static bool _item_identical(Object* item1, Object* item2)
return false; return false;
} }
int v1; int item2Quantity;
if (proto->item.type == ITEM_TYPE_AMMO || item1->pid == PROTO_ID_MONEY) { if (proto->item.type == ITEM_TYPE_AMMO || item1->pid == PROTO_ID_MONEY) {
v1 = item2->data.item.ammo.quantity; item2Quantity = item2->data.item.ammo.quantity;
item2->data.item.ammo.quantity = item1->data.item.ammo.quantity; item2->data.item.ammo.quantity = item1->data.item.ammo.quantity;
} }
// NOTE: Probably inlined memcmp, but I'm not sure why it only checks 32 // NOTE: Likely there was a comparison of ItemObjectData structs via inlined memcmp
// bytes. // ItemObjectData are 24 bytes, but compared 32 bytes due to struct alignment or such.
// Another explanation is the presence of 8 more bytes of unknown data that was never used.
int i; int i;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (item1->field_2C_array[i] != item2->field_2C_array[i]) { if (item1->field_2C_array[i] != item2->field_2C_array[i]) {
@ -691,7 +692,7 @@ static bool _item_identical(Object* item1, Object* item2)
} }
if (proto->item.type == ITEM_TYPE_AMMO || item1->pid == PROTO_ID_MONEY) { if (proto->item.type == ITEM_TYPE_AMMO || item1->pid == PROTO_ID_MONEY) {
item2->data.item.ammo.quantity = v1; item2->data.item.ammo.quantity = item2Quantity;
} }
return i == 8; return i == 8;
@ -2593,8 +2594,11 @@ int ammoGetDamageDivisor(Object* armor)
return proto->item.data.ammo.damageDivisor; return proto->item.data.ammo.damageDivisor;
} }
// Adds Drug event to event queue.
// [duration] is in minutes
//
// 0x479B44 // 0x479B44
static int _insert_drug_effect(Object* critter, Object* item, int a3, int* stats, int* mods) static int _insert_drug_effect(Object* critter, Object* item, int duration, int* stats, int* mods)
{ {
int index; int index;
for (index = 0; index < 3; index++) { for (index = 0; index < 3; index++) {
@ -2619,7 +2623,7 @@ static int _insert_drug_effect(Object* critter, Object* item, int a3, int* stats
drugEffectEvent->modifiers[index] = mods[index]; drugEffectEvent->modifiers[index] = mods[index];
} }
int delay = 600 * a3; int delay = 600 * duration;
if (critter == gDude) { if (critter == gDude) {
if (traitIsSelected(TRAIT_CHEM_RESISTANT)) { if (traitIsSelected(TRAIT_CHEM_RESISTANT)) {
delay /= 2; delay /= 2;
@ -2637,25 +2641,23 @@ static int _insert_drug_effect(Object* critter, Object* item, int a3, int* stats
// 0x479C20 // 0x479C20
static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool isImmediate) static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool isImmediate)
{ {
int v10;
int v11;
int v12;
MessageListItem messageListItem; MessageListItem messageListItem;
const char* name; const char* name;
const char* text; const char* text;
char v24[92]; // TODO: Size is probably wrong. char msgBuf[92]; // TODO: Size is probably wrong.
char str[92]; // TODO: Size is probably wrong.
bool statsChanged = false; bool statsChanged = false;
int v5 = 0; int startIndex = 0;
bool v32 = false; bool firstStatIsMinimum = false;
if (stats[0] == -2) { if (stats[0] == -2) {
v5 = 1; startIndex = 1;
v32 = true; firstStatIsMinimum = true;
} }
for (int index = v5; index < 3; index++) { for (int index = startIndex; index < 3; index++) {
int oldStatBonus;
int statBonus;
int stat = stats[index]; int stat = stats[index];
if (stat == -1) { if (stat == -1) {
continue; continue;
@ -2665,32 +2667,31 @@ static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool is
critter->data.critter.combat.maneuver &= ~CRITTER_MANUEVER_FLEEING; critter->data.critter.combat.maneuver &= ~CRITTER_MANUEVER_FLEEING;
} }
v10 = critterGetBonusStat(critter, stat); oldStatBonus = critterGetBonusStat(critter, stat);
int before; int before = (critter == gDude)
if (critter == gDude) { ? critterGetStat(gDude, stat)
before = critterGetStat(gDude, stat); : 0;
}
if (v32) { if (firstStatIsMinimum) {
v11 = randomBetween(mods[index - 1], mods[index]) + v10; statBonus = randomBetween(mods[index - 1], mods[index]) + oldStatBonus;
v32 = false; firstStatIsMinimum = false;
} else { } else {
v11 = mods[index] + v10; statBonus = mods[index] + oldStatBonus;
} }
if (stat == STAT_CURRENT_HIT_POINTS) { if (stat == STAT_CURRENT_HIT_POINTS) {
v12 = critterGetBaseStatWithTraitModifier(critter, STAT_CURRENT_HIT_POINTS); int currentHp = critterGetBaseStatWithTraitModifier(critter, STAT_CURRENT_HIT_POINTS);
if (v11 + v12 <= 0 && critter != gDude) { if (statBonus + currentHp <= 0 && critter != gDude) {
name = critterGetName(critter); name = critterGetName(critter);
// %s succumbs to the adverse effects of chems. // %s succumbs to the adverse effects of chems.
text = getmsg(&gItemsMessageList, &messageListItem, 600); text = getmsg(&gItemsMessageList, &messageListItem, 600);
snprintf(v24, sizeof(v24), text, name); snprintf(msgBuf, sizeof(msgBuf), text, name);
_combatKillCritterOutsideCombat(critter, v24); _combatKillCritterOutsideCombat(critter, msgBuf);
} }
} }
critterSetBonusStat(critter, stat, v11); critterSetBonusStat(critter, stat, statBonus);
if (critter == gDude) { if (critter == gDude) {
if (stat == STAT_CURRENT_HIT_POINTS) { if (stat == STAT_CURRENT_HIT_POINTS) {
@ -2704,8 +2705,8 @@ static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool is
messageListItem.num = after < before ? 2 : 1; messageListItem.num = after < before ? 2 : 1;
if (messageListGetItem(&gItemsMessageList, &messageListItem)) { if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
char* statName = statGetName(stat); char* statName = statGetName(stat);
snprintf(str, sizeof(str), messageListItem.text, after < before ? before - after : after - before, statName); snprintf(msgBuf, sizeof(msgBuf), messageListItem.text, after < before ? before - after : after - before, statName);
displayMonitorAddMessage(str); displayMonitorAddMessage(msgBuf);
statsChanged = true; statsChanged = true;
} }
} }
@ -2725,14 +2726,14 @@ static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool is
// You suffer a fatal heart attack from chem overdose. // You suffer a fatal heart attack from chem overdose.
messageListItem.num = 4; messageListItem.num = 4;
if (messageListGetItem(&gItemsMessageList, &messageListItem)) { if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
strcpy(v24, messageListItem.text); strcpy(msgBuf, messageListItem.text);
// TODO: Why message is ignored? // TODO: Why message is ignored?
} }
} else { } else {
name = critterGetName(critter); name = critterGetName(critter);
// %s succumbs to the adverse effects of chems. // %s succumbs to the adverse effects of chems.
text = getmsg(&gItemsMessageList, &messageListItem, 600); text = getmsg(&gItemsMessageList, &messageListItem, 600);
snprintf(v24, sizeof(v24), text, name); snprintf(msgBuf, sizeof(msgBuf), text, name);
// TODO: Why message is ignored? // TODO: Why message is ignored?
} }
} }

View File

@ -373,7 +373,7 @@ int mapSetElevation(int elevation)
} }
if (elevation != gElevation) { if (elevation != gElevation) {
wmMapMarkMapEntranceState(gMapHeader.field_34, elevation, 1); wmMapMarkMapEntranceState(gMapHeader.index, elevation, 1);
} }
gElevation = elevation; gElevation = elevation;
@ -596,7 +596,7 @@ char* _map_get_description_idx_(int map)
// 0x4826B8 // 0x4826B8
int mapGetCurrentMap() int mapGetCurrentMap()
{ {
return gMapHeader.field_34; return gMapHeader.index;
} }
// 0x4826C0 // 0x4826C0
@ -927,7 +927,7 @@ static int mapLoad(File* stream)
lightSetAmbientIntensity(LIGHT_INTENSITY_MAX, false); lightSetAmbientIntensity(LIGHT_INTENSITY_MAX, false);
objectSetLocation(gDude, gCenterTile, gElevation, nullptr); objectSetLocation(gDude, gCenterTile, gElevation, nullptr);
objectSetRotation(gDude, gEnteringRotation, nullptr); objectSetRotation(gDude, gEnteringRotation, nullptr);
gMapHeader.field_34 = wmMapMatchNameToIdx(gMapHeader.name); gMapHeader.index = wmMapMatchNameToIdx(gMapHeader.name);
if ((gMapHeader.flags & 1) == 0) { if ((gMapHeader.flags & 1) == 0) {
char path[COMPAT_MAX_PATH]; char path[COMPAT_MAX_PATH];
@ -965,10 +965,10 @@ static int mapLoad(File* stream)
Script* script; Script* script;
scriptGetScript(gMapSid, &script); scriptGetScript(gMapSid, &script);
script->field_14 = gMapHeader.scriptIndex - 1; script->index = gMapHeader.scriptIndex - 1;
script->flags |= SCRIPT_FLAG_0x08; script->flags |= SCRIPT_FLAG_0x08;
object->id = scriptsNewObjectId(); object->id = scriptsNewObjectId();
script->field_1C = object->id; script->ownerId = object->id;
script->owner = object; script->owner = object;
_scr_spatials_disable(); _scr_spatials_disable();
scriptExecProc(gMapSid, SCRIPT_PROC_MAP_ENTER); scriptExecProc(gMapSid, SCRIPT_PROC_MAP_ENTER);
@ -1025,8 +1025,8 @@ err:
rc = -1; rc = -1;
} }
wmMapMarkVisited(gMapHeader.field_34); wmMapMarkVisited(gMapHeader.index);
wmMapMarkMapEntranceState(gMapHeader.field_34, gElevation, 1); wmMapMarkMapEntranceState(gMapHeader.index, gElevation, 1);
if (wmCheckGameAreaEvents() != 0) { if (wmCheckGameAreaEvents() != 0) {
rc = -1; rc = -1;
@ -1203,7 +1203,7 @@ static int _map_age_dead_critters()
int _map_target_load_area() int _map_target_load_area()
{ {
int city = -1; int city = -1;
if (wmMatchAreaContainingMapIdx(gMapHeader.field_34, &city) == -1) { if (wmMatchAreaContainingMapIdx(gMapHeader.index, &city) == -1) {
city = -1; city = -1;
} }
return city; return city;
@ -1254,7 +1254,7 @@ int mapHandleTransition()
} }
} else { } else {
if (!isInCombat()) { if (!isInCombat()) {
if (gMapTransition.map != gMapHeader.field_34 || gElevation == gMapTransition.elevation) { if (gMapTransition.map != gMapHeader.index || gElevation == gMapTransition.elevation) {
// SFALL: Remove text floaters after moving to another map. // SFALL: Remove text floaters after moving to another map.
textObjectsReset(); textObjectsReset();
@ -1262,7 +1262,7 @@ int mapHandleTransition()
} }
if (gMapTransition.tile != -1 && gMapTransition.tile != 0 if (gMapTransition.tile != -1 && gMapTransition.tile != 0
&& gMapHeader.field_34 != MAP_MODOC_BEDNBREAKFAST && gMapHeader.field_34 != MAP_THE_SQUAT_A && gMapHeader.index != MAP_MODOC_BEDNBREAKFAST && gMapHeader.index != MAP_THE_SQUAT_A
&& elevationIsValid(gMapTransition.elevation)) { && elevationIsValid(gMapTransition.elevation)) {
objectSetLocation(gDude, gMapTransition.tile, gMapTransition.elevation, nullptr); objectSetLocation(gDude, gMapTransition.tile, gMapTransition.elevation, nullptr);
mapSetElevation(gMapTransition.elevation); mapSetElevation(gMapTransition.elevation);
@ -1276,7 +1276,7 @@ int mapHandleTransition()
memset(&gMapTransition, 0, sizeof(gMapTransition)); memset(&gMapTransition, 0, sizeof(gMapTransition));
int city; int city;
wmMatchAreaContainingMapIdx(gMapHeader.field_34, &city); wmMatchAreaContainingMapIdx(gMapHeader.index, &city);
if (wmTeleportToArea(city) == -1) { if (wmTeleportToArea(city) == -1) {
debugPrint("\nError: couldn't make jump on worldmap for map jump!"); debugPrint("\nError: couldn't make jump on worldmap for map jump!");
} }
@ -1748,7 +1748,7 @@ static int mapHeaderWrite(MapHeader* ptr, File* stream)
if (fileWriteInt32(stream, ptr->flags) == -1) return -1; if (fileWriteInt32(stream, ptr->flags) == -1) return -1;
if (fileWriteInt32(stream, ptr->darkness) == -1) return -1; if (fileWriteInt32(stream, ptr->darkness) == -1) return -1;
if (fileWriteInt32(stream, ptr->globalVariablesCount) == -1) return -1; if (fileWriteInt32(stream, ptr->globalVariablesCount) == -1) return -1;
if (fileWriteInt32(stream, ptr->field_34) == -1) return -1; if (fileWriteInt32(stream, ptr->index) == -1) return -1;
if (fileWriteUInt32(stream, ptr->lastVisitTime) == -1) return -1; if (fileWriteUInt32(stream, ptr->lastVisitTime) == -1) return -1;
if (fileWriteInt32List(stream, ptr->field_3C, 44) == -1) return -1; if (fileWriteInt32List(stream, ptr->field_3C, 44) == -1) return -1;
@ -1768,7 +1768,7 @@ static int mapHeaderRead(MapHeader* ptr, File* stream)
if (fileReadInt32(stream, &(ptr->flags)) == -1) return -1; if (fileReadInt32(stream, &(ptr->flags)) == -1) return -1;
if (fileReadInt32(stream, &(ptr->darkness)) == -1) return -1; if (fileReadInt32(stream, &(ptr->darkness)) == -1) return -1;
if (fileReadInt32(stream, &(ptr->globalVariablesCount)) == -1) return -1; if (fileReadInt32(stream, &(ptr->globalVariablesCount)) == -1) return -1;
if (fileReadInt32(stream, &(ptr->field_34)) == -1) return -1; if (fileReadInt32(stream, &(ptr->index)) == -1) return -1;
if (fileReadUInt32(stream, &(ptr->lastVisitTime)) == -1) return -1; if (fileReadUInt32(stream, &(ptr->lastVisitTime)) == -1) return -1;
if (fileReadInt32List(stream, ptr->field_3C, 44) == -1) return -1; if (fileReadInt32List(stream, ptr->field_3C, 44) == -1) return -1;

View File

@ -51,7 +51,7 @@ typedef struct MapHeader {
int globalVariablesCount; int globalVariablesCount;
// map_number // map_number
int field_34; int index;
// Time in game ticks when PC last visited this map. // Time in game ticks when PC last visited this map.
unsigned int lastVisitTime; unsigned int lastVisitTime;

View File

@ -632,12 +632,12 @@ void mouseGetPosition(int* xPtr, int* yPtr)
} }
// 0x4CAA04 // 0x4CAA04
void _mouse_set_position(int a1, int a2) void _mouse_set_position(int x, int y)
{ {
gMouseCursorX = a1 - _mouse_hotx; gMouseCursorX = x - _mouse_hotx;
gMouseCursorY = a2 - _mouse_hoty; gMouseCursorY = y - _mouse_hoty;
_raw_y = a2 - _mouse_hoty; _raw_y = y - _mouse_hoty;
_raw_x = a1 - _mouse_hotx; _raw_x = x - _mouse_hotx;
_mouse_clip(); _mouse_clip();
} }

View File

@ -42,7 +42,7 @@ bool _mouse_in(int left, int top, int right, int bottom);
bool _mouse_click_in(int left, int top, int right, int bottom); bool _mouse_click_in(int left, int top, int right, int bottom);
void mouseGetRect(Rect* rect); void mouseGetRect(Rect* rect);
void mouseGetPosition(int* out_x, int* out_y); void mouseGetPosition(int* out_x, int* out_y);
void _mouse_set_position(int a1, int a2); void _mouse_set_position(int x, int y);
int mouseGetEvent(); int mouseGetEvent();
bool cursorIsHidden(); bool cursorIsHidden();
void _mouse_get_raw_state(int* out_x, int* out_y, int* out_buttons); void _mouse_get_raw_state(int* out_x, int* out_y, int* out_buttons);

View File

@ -248,6 +248,7 @@ typedef struct MiscObjectData {
int rotation; int rotation;
} MiscObjectData; } MiscObjectData;
// TODO: use C-style inheritance for different ObjectData variants instead of unions within unions.
typedef struct ObjectData { typedef struct ObjectData {
Inventory inventory; Inventory inventory;
union { union {
@ -276,6 +277,7 @@ typedef struct Object {
int flags; // obj_flags int flags; // obj_flags
int elevation; // obj_elev int elevation; // obj_elev
union { union {
// TODO: union of different ObjectData sub-structs
int field_2C_array[14]; int field_2C_array[14];
ObjectData data; ObjectData data;
}; };
@ -286,7 +288,7 @@ typedef struct Object {
int outline; // obj_outline int outline; // obj_outline
int sid; // obj_sid int sid; // obj_sid
Object* owner; Object* owner;
int field_80; int scriptIndex;
} Object; } Object;
typedef struct ObjectListNode { typedef struct ObjectListNode {

View File

@ -430,7 +430,7 @@ int objectRead(Object* obj, File* stream)
if (fileReadInt32(stream, &(obj->lightIntensity)) == -1) return -1; if (fileReadInt32(stream, &(obj->lightIntensity)) == -1) return -1;
if (fileReadInt32(stream, &field_74) == -1) return -1; if (fileReadInt32(stream, &field_74) == -1) return -1;
if (fileReadInt32(stream, &(obj->sid)) == -1) return -1; if (fileReadInt32(stream, &(obj->sid)) == -1) return -1;
if (fileReadInt32(stream, &(obj->field_80)) == -1) return -1; if (fileReadInt32(stream, &(obj->scriptIndex)) == -1) return -1;
obj->outline = 0; obj->outline = 0;
obj->owner = nullptr; obj->owner = nullptr;
@ -539,7 +539,7 @@ static int objectLoadAllInternal(File* stream)
debugPrint("\nError connecting object to script!"); debugPrint("\nError connecting object to script!");
} else { } else {
script->owner = objectListNode->obj; script->owner = objectListNode->obj;
objectListNode->obj->field_80 = script->field_14; objectListNode->obj->scriptIndex = script->index;
} }
} }
@ -661,7 +661,7 @@ static int objectWrite(Object* obj, File* stream)
if (fileWriteInt32(stream, obj->lightIntensity) == -1) return -1; if (fileWriteInt32(stream, obj->lightIntensity) == -1) return -1;
if (fileWriteInt32(stream, obj->outline) == -1) return -1; if (fileWriteInt32(stream, obj->outline) == -1) return -1;
if (fileWriteInt32(stream, obj->sid) == -1) return -1; if (fileWriteInt32(stream, obj->sid) == -1) return -1;
if (fileWriteInt32(stream, obj->field_80) == -1) return -1; if (fileWriteInt32(stream, obj->scriptIndex) == -1) return -1;
if (objectDataWrite(obj, stream) == -1) return -1; if (objectDataWrite(obj, stream) == -1) return -1;
return 0; return 0;
@ -3719,7 +3719,7 @@ static int objectAllocate(Object** objectPtr)
object->pid = -1; object->pid = -1;
object->sid = -1; object->sid = -1;
object->owner = nullptr; object->owner = nullptr;
object->field_80 = -1; object->scriptIndex = -1;
return 0; return 0;
} }

View File

@ -55,7 +55,7 @@ int objectHide(Object* obj, Rect* rect);
int objectEnableOutline(Object* obj, Rect* rect); int objectEnableOutline(Object* obj, Rect* rect);
int objectDisableOutline(Object* obj, Rect* rect); int objectDisableOutline(Object* obj, Rect* rect);
int _obj_toggle_flat(Object* obj, Rect* rect); int _obj_toggle_flat(Object* obj, Rect* rect);
int objectDestroy(Object* a1, Rect* a2); int objectDestroy(Object* object, Rect* rect);
int _obj_inven_free(Inventory* inventory); int _obj_inven_free(Inventory* inventory);
bool _obj_action_can_use(Object* obj); bool _obj_action_can_use(Object* obj);
bool _obj_action_can_talk_to(Object* obj); bool _obj_action_can_talk_to(Object* obj);

View File

@ -58,10 +58,10 @@ typedef struct PartyMemberDescription {
int level_pids[PARTY_MEMBER_MAX_LEVEL]; int level_pids[PARTY_MEMBER_MAX_LEVEL];
} PartyMemberDescription; } PartyMemberDescription;
typedef struct STRU_519DBC { typedef struct PartyMemberLevelUpInfo {
int field_0; int level; // party member level
int field_4; // party member level int numLevelUps; // number of PC level ups with this member in party
int field_8; // early what? int isEarly; // last level up was "early" due to successful roll
} STRU_519DBC; } STRU_519DBC;
typedef struct PartyMemberListItem { typedef struct PartyMemberListItem {
@ -113,7 +113,7 @@ static int _partyStatePrepped = 0;
static PartyMemberDescription* gPartyMemberDescriptions = nullptr; static PartyMemberDescription* gPartyMemberDescriptions = nullptr;
// 0x519DBC // 0x519DBC
static STRU_519DBC* _partyMemberLevelUpInfoList = nullptr; static PartyMemberLevelUpInfo* _partyMemberLevelUpInfoList = nullptr;
// 0x519DC0 // 0x519DC0
static int _curID = 20000; static int _curID = 20000;
@ -164,7 +164,7 @@ int partyMembersInit()
memset(gPartyMemberDescriptions, 0, sizeof(*gPartyMemberDescriptions) * gPartyMemberDescriptionsLength); memset(gPartyMemberDescriptions, 0, sizeof(*gPartyMemberDescriptions) * gPartyMemberDescriptionsLength);
_partyMemberLevelUpInfoList = (STRU_519DBC*)internal_malloc(sizeof(*_partyMemberLevelUpInfoList) * gPartyMemberDescriptionsLength); _partyMemberLevelUpInfoList = (PartyMemberLevelUpInfo*)internal_malloc(sizeof(*_partyMemberLevelUpInfoList) * gPartyMemberDescriptionsLength);
if (_partyMemberLevelUpInfoList == nullptr) goto err; if (_partyMemberLevelUpInfoList == nullptr) goto err;
memset(_partyMemberLevelUpInfoList, 0, sizeof(*_partyMemberLevelUpInfoList) * gPartyMemberDescriptionsLength); memset(_partyMemberLevelUpInfoList, 0, sizeof(*_partyMemberLevelUpInfoList) * gPartyMemberDescriptionsLength);
@ -275,9 +275,9 @@ err:
void partyMembersReset() void partyMembersReset()
{ {
for (int index = 0; index < gPartyMemberDescriptionsLength; index++) { for (int index = 0; index < gPartyMemberDescriptionsLength; index++) {
_partyMemberLevelUpInfoList[index].field_0 = 0; _partyMemberLevelUpInfoList[index].level = 0;
_partyMemberLevelUpInfoList[index].field_4 = 0; _partyMemberLevelUpInfoList[index].numLevelUps = 0;
_partyMemberLevelUpInfoList[index].field_8 = 0; _partyMemberLevelUpInfoList[index].isEarly = 0;
} }
} }
@ -285,9 +285,9 @@ void partyMembersReset()
void partyMembersExit() void partyMembersExit()
{ {
for (int index = 0; index < gPartyMemberDescriptionsLength; index++) { for (int index = 0; index < gPartyMemberDescriptionsLength; index++) {
_partyMemberLevelUpInfoList[index].field_0 = 0; _partyMemberLevelUpInfoList[index].level = 0;
_partyMemberLevelUpInfoList[index].field_4 = 0; _partyMemberLevelUpInfoList[index].numLevelUps = 0;
_partyMemberLevelUpInfoList[index].field_8 = 0; _partyMemberLevelUpInfoList[index].isEarly = 0;
} }
gPartyMemberDescriptionsLength = 0; gPartyMemberDescriptionsLength = 0;
@ -364,9 +364,9 @@ static void partyMemberDescriptionInit(PartyMemberDescription* partyMemberDescri
partyMemberDescription->level_pids[0] = -1; partyMemberDescription->level_pids[0] = -1;
for (int index = 0; index < gPartyMemberDescriptionsLength; index++) { for (int index = 0; index < gPartyMemberDescriptionsLength; index++) {
_partyMemberLevelUpInfoList[index].field_0 = 0; _partyMemberLevelUpInfoList[index].level = 0;
_partyMemberLevelUpInfoList[index].field_4 = 0; _partyMemberLevelUpInfoList[index].numLevelUps = 0;
_partyMemberLevelUpInfoList[index].field_8 = 0; _partyMemberLevelUpInfoList[index].isEarly = 0;
} }
} }
@ -403,7 +403,7 @@ int partyMemberAdd(Object* object)
Script* script; Script* script;
if (scriptGetScript(object->sid, &script) != -1) { if (scriptGetScript(object->sid, &script) != -1) {
script->flags |= (SCRIPT_FLAG_0x08 | SCRIPT_FLAG_0x10); script->flags |= (SCRIPT_FLAG_0x08 | SCRIPT_FLAG_0x10);
script->field_1C = object->id; script->ownerId = object->id;
object->sid = ((object->pid & 0xFFFFFF) + 18000) | (object->sid & 0xFF000000); object->sid = ((object->pid & 0xFFFFFF) + 18000) | (object->sid & 0xFF000000);
script->sid = object->sid; script->sid = object->sid;
@ -528,10 +528,10 @@ int partyMembersSave(File* stream)
} }
for (int index = 1; index < gPartyMemberDescriptionsLength; index++) { for (int index = 1; index < gPartyMemberDescriptionsLength; index++) {
STRU_519DBC* ptr = &(_partyMemberLevelUpInfoList[index]); PartyMemberLevelUpInfo* ptr = &(_partyMemberLevelUpInfoList[index]);
if (fileWriteInt32(stream, ptr->field_0) == -1) return -1; if (fileWriteInt32(stream, ptr->level) == -1) return -1;
if (fileWriteInt32(stream, ptr->field_4) == -1) return -1; if (fileWriteInt32(stream, ptr->numLevelUps) == -1) return -1;
if (fileWriteInt32(stream, ptr->field_8) == -1) return -1; if (fileWriteInt32(stream, ptr->isEarly) == -1) return -1;
} }
return 0; return 0;
@ -763,11 +763,11 @@ int partyMembersLoad(File* stream)
partyFixMultipleMembers(); partyFixMultipleMembers();
for (int index = 1; index < gPartyMemberDescriptionsLength; index++) { for (int index = 1; index < gPartyMemberDescriptionsLength; index++) {
STRU_519DBC* ptr_519DBC = &(_partyMemberLevelUpInfoList[index]); PartyMemberLevelUpInfo* levelUpInfo = &(_partyMemberLevelUpInfoList[index]);
if (fileReadInt32(stream, &(ptr_519DBC->field_0)) == -1) return -1; if (fileReadInt32(stream, &(levelUpInfo->level)) == -1) return -1;
if (fileReadInt32(stream, &(ptr_519DBC->field_4)) == -1) return -1; if (fileReadInt32(stream, &(levelUpInfo->numLevelUps)) == -1) return -1;
if (fileReadInt32(stream, &(ptr_519DBC->field_8)) == -1) return -1; if (fileReadInt32(stream, &(levelUpInfo->isEarly)) == -1) return -1;
} }
return 0; return 0;
@ -1021,8 +1021,8 @@ static int _partyMemberItemSave(Object* object)
} }
if (object->id < 20000) { if (object->id < 20000) {
script->field_1C = _partyMemberNewObjID(); script->ownerId = _partyMemberNewObjID();
object->id = script->field_1C; object->id = script->ownerId;
} }
PartyMemberListItem* node = (PartyMemberListItem*)internal_malloc(sizeof(*node)); PartyMemberListItem* node = (PartyMemberListItem*)internal_malloc(sizeof(*node));
@ -1454,25 +1454,25 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse)
int _partyMemberIncLevels() int _partyMemberIncLevels()
{ {
int i; int i;
PartyMemberListItem* ptr; PartyMemberListItem* listItem;
Object* obj; Object* obj;
PartyMemberDescription* party_member; PartyMemberDescription* memberDescription;
const char* name; const char* name;
int j; int j;
int v0; int memberIndex;
STRU_519DBC* ptr_519DBC; PartyMemberLevelUpInfo* levelUpInfo;
int v24; int levelMod;
char* text; char* text;
MessageListItem msg; MessageListItem msg;
char str[260]; char str[260];
Rect v19; Rect levelUpMessageRect;
v0 = -1; memberIndex = -1;
for (i = 1; i < gPartyMembersLength; i++) { for (i = 1; i < gPartyMembersLength; i++) {
ptr = &(gPartyMembers[i]); listItem = &(gPartyMembers[i]);
obj = ptr->object; obj = listItem->object;
if (partyMemberGetDescription(obj, &party_member) == -1) { if (partyMemberGetDescription(obj, &memberDescription) == -1) {
// SFALL: NPC level fix. // SFALL: NPC level fix.
continue; continue;
} }
@ -1484,67 +1484,75 @@ int _partyMemberIncLevels()
name = critterGetName(obj); name = critterGetName(obj);
debugPrint("\npartyMemberIncLevels: %s", name); debugPrint("\npartyMemberIncLevels: %s", name);
if (party_member->level_up_every == 0) { if (memberDescription->level_up_every == 0) {
continue; continue;
} }
for (j = 1; j < gPartyMemberDescriptionsLength; j++) { for (j = 1; j < gPartyMemberDescriptionsLength; j++) {
if (gPartyMemberPids[j] == obj->pid) { if (gPartyMemberPids[j] == obj->pid) {
v0 = j; memberIndex = j;
} }
} }
if (v0 == -1) { if (memberIndex == -1) {
continue; continue;
} }
if (pcGetStat(PC_STAT_LEVEL) < party_member->level_minimum) { if (pcGetStat(PC_STAT_LEVEL) < memberDescription->level_minimum) {
continue; continue;
} }
ptr_519DBC = &(_partyMemberLevelUpInfoList[v0]); levelUpInfo = &(_partyMemberLevelUpInfoList[memberIndex]);
if (ptr_519DBC->field_0 >= party_member->level_pids_num) { if (levelUpInfo->level >= memberDescription->level_pids_num) {
continue; continue;
} }
ptr_519DBC->field_4++; levelUpInfo->numLevelUps++;
v24 = ptr_519DBC->field_4 % party_member->level_pids_num; levelMod = levelUpInfo->numLevelUps % memberDescription->level_up_every;
debugPrint("pm: levelMod: %d, Lvl: %d, Early: %d, Every: %d", v24, ptr_519DBC->field_4, ptr_519DBC->field_8, party_member->level_up_every); debugPrint("pm: levelMod: %d, Lvl: %d, Early: %d, Every: %d", levelMod, levelUpInfo->numLevelUps, levelUpInfo->isEarly, memberDescription->level_up_every);
if (v24 != 0 || ptr_519DBC->field_8 == 0) { // Party member level up with a probability that depends on how "far" we are in the current "level_up_every" progression.
if (ptr_519DBC->field_8 == 0) { // For example, if level_up_every is 5 and NPC observed 7 level ups with the player, 5 % 7 = 2, 2 * 100 / 5 = 40 (40% probability).
if (v24 == 0 || randomBetween(0, 100) <= 100 * v24 / party_member->level_up_every) { // If levelMod is 0 (so we got 5, 10, etc. levels in the example above), probability is 100% (no roll).
ptr_519DBC->field_0++; // If previous level up occured "early" (due to probability roll), then we skip until we get to levelMod = 0, to begin the next cycle.
if (v24 != 0) {
ptr_519DBC->field_8 = 1;
}
if (_partyMemberCopyLevelInfo(obj, party_member->level_pids[ptr_519DBC->field_0]) == -1) { if (levelUpInfo->isEarly != 0) {
return -1; if (levelMod == 0) {
} levelUpInfo->isEarly = 0;
name = critterGetName(obj);
// %s has gained in some abilities.
text = getmsg(&gMiscMessageList, &msg, 9000);
snprintf(str, sizeof(str), text, name);
displayMonitorAddMessage(str);
debugPrint(str);
// Individual message
msg.num = 9000 + 10 * v0 + ptr_519DBC->field_0 - 1;
if (messageListGetItem(&gMiscMessageList, &msg)) {
name = critterGetName(obj);
snprintf(str, sizeof(str), msg.text, name);
textObjectAdd(obj, str, 101, _colorTable[0x7FFF], _colorTable[0], &v19);
tileWindowRefreshRect(&v19, obj->elevation);
}
}
} }
} else { continue;
ptr_519DBC->field_8 = 0; }
if (levelMod != 0 && randomBetween(0, 100) > 100 * levelMod / memberDescription->level_up_every) {
continue;
}
levelUpInfo->level++;
if (levelMod != 0) {
levelUpInfo->isEarly = 1;
}
if (_partyMemberCopyLevelInfo(obj, memberDescription->level_pids[levelUpInfo->level]) == -1) {
return -1;
}
name = critterGetName(obj);
// %s has gained in some abilities.
text = getmsg(&gMiscMessageList, &msg, 9000);
snprintf(str, sizeof(str), text, name);
displayMonitorAddMessage(str);
debugPrint(str);
// Individual message
msg.num = 9000 + 10 * memberIndex + levelUpInfo->level - 1;
if (messageListGetItem(&gMiscMessageList, &msg)) {
name = critterGetName(obj);
snprintf(str, sizeof(str), msg.text, name);
textObjectAdd(obj, str, 101, _colorTable[0x7FFF], _colorTable[0], &levelUpMessageRect);
tileWindowRefreshRect(&levelUpMessageRect, obj->elevation);
} }
} }
@ -1552,23 +1560,23 @@ int _partyMemberIncLevels()
} }
// 0x495EA8 // 0x495EA8
static int _partyMemberCopyLevelInfo(Object* critter, int a2) static int _partyMemberCopyLevelInfo(Object* critter, int stagePid)
{ {
if (critter == nullptr) { if (critter == nullptr) {
return -1; return -1;
} }
if (a2 == -1) { if (stagePid == -1) {
return -1; return -1;
} }
Proto* proto1; Proto* proto;
if (protoGetProto(critter->pid, &proto1) == -1) { if (protoGetProto(critter->pid, &proto) == -1) {
return -1; return -1;
} }
Proto* proto2; Proto* stageProto;
if (protoGetProto(a2, &proto2) == -1) { if (protoGetProto(stagePid, &stageProto) == -1) {
return -1; return -1;
} }
@ -1583,15 +1591,15 @@ static int _partyMemberCopyLevelInfo(Object* critter, int a2)
critterAdjustHitPoints(critter, maxHp); critterAdjustHitPoints(critter, maxHp);
for (int stat = 0; stat < SPECIAL_STAT_COUNT; stat++) { for (int stat = 0; stat < SPECIAL_STAT_COUNT; stat++) {
proto1->critter.data.baseStats[stat] = proto2->critter.data.baseStats[stat]; proto->critter.data.baseStats[stat] = stageProto->critter.data.baseStats[stat];
} }
for (int stat = 0; stat < SPECIAL_STAT_COUNT; stat++) { for (int stat = 0; stat < SPECIAL_STAT_COUNT; stat++) {
proto1->critter.data.bonusStats[stat] = proto2->critter.data.bonusStats[stat]; proto->critter.data.bonusStats[stat] = stageProto->critter.data.bonusStats[stat];
} }
for (int skill = 0; skill < SKILL_COUNT; skill++) { for (int skill = 0; skill < SKILL_COUNT; skill++) {
proto1->critter.data.skills[skill] = proto2->critter.data.skills[skill]; proto->critter.data.skills[skill] = stageProto->critter.data.skills[skill];
} }
critter->data.critter.hp = critterGetStat(critter, STAT_MAXIMUM_HIT_POINTS); critter->data.critter.hp = critterGetStat(critter, STAT_MAXIMUM_HIT_POINTS);
@ -1604,7 +1612,7 @@ static int _partyMemberCopyLevelInfo(Object* critter, int a2)
if (item2 != nullptr) { if (item2 != nullptr) {
// SFALL: Fix for party member's equipped weapon being placed in the // SFALL: Fix for party member's equipped weapon being placed in the
// incorrect item slot after leveling up. // incorrect item slot after leveling up.
_invenWieldFunc(critter, item2, 1, false); _invenWieldFunc(critter, item2, HAND_RIGHT, false);
} }
return 0; return 0;

View File

@ -14,19 +14,33 @@
namespace fallout { namespace fallout {
enum PerkParamMode {
PERK_PARAM_MODE_FIRST_ONLY,
PERK_PARAM_MODE_OR,
PERK_PARAM_MODE_AND,
};
typedef struct PerkDescription { typedef struct PerkDescription {
char* name; char* name;
char* description; char* description;
int frmId; int frmId;
int maxRank; int maxRank;
int minLevel; int minLevel;
// Critter stat to modify for every perk rank.
int stat; int stat;
// Stat modifier for every perk rank.
int statModifier; int statModifier;
// Skill number, normally. If bit 0x4000000 is set, will be treated as global var number instead.
int param1; int param1;
// Required value of a skill or global var.
int value1; int value1;
int field_24; // Specifies wether to require both params, either one or just use the first one.
int paramMode;
// Skill or gvar number, see param1.
int param2; int param2;
// Required value of a skill or global var.
int value2; int value2;
// Required minimum value for every primary stat.
int stats[PRIMARY_STAT_COUNT]; int stats[PRIMARY_STAT_COUNT];
} PerkDescription; } PerkDescription;
@ -308,7 +322,7 @@ static bool perkCanAdd(Object* critter, int perk)
} }
} }
bool v1 = true; bool req1Fulfilled = true;
int param1 = perkDescription->param1; int param1 = perkDescription->param1;
if (param1 != -1) { if (param1 != -1) {
@ -322,32 +336,32 @@ static bool perkCanAdd(Object* critter, int perk)
if (value1 < 0) { if (value1 < 0) {
if (isVariable) { if (isVariable) {
if (gameGetGlobalVar(param1) >= value1) { if (gameGetGlobalVar(param1) >= value1) {
v1 = false; req1Fulfilled = false;
} }
} else { } else {
if (skillGetValue(critter, param1) >= -value1) { if (skillGetValue(critter, param1) >= -value1) {
v1 = false; req1Fulfilled = false;
} }
} }
} else { } else {
if (isVariable) { if (isVariable) {
if (gameGetGlobalVar(param1) < value1) { if (gameGetGlobalVar(param1) < value1) {
v1 = false; req1Fulfilled = false;
} }
} else { } else {
if (skillGetValue(critter, param1) < value1) { if (skillGetValue(critter, param1) < value1) {
v1 = false; req1Fulfilled = false;
} }
} }
} }
} }
if (!v1 || perkDescription->field_24 == 2) { if (!req1Fulfilled || perkDescription->paramMode == PERK_PARAM_MODE_AND) {
if (perkDescription->field_24 == 0) { if (perkDescription->paramMode == PERK_PARAM_MODE_FIRST_ONLY) {
return false; return false;
} }
if (!v1 && perkDescription->field_24 == 2) { if (!req1Fulfilled && perkDescription->paramMode == PERK_PARAM_MODE_AND) {
return false; return false;
} }

View File

@ -211,13 +211,13 @@ static int _PrintAMelevList(int a1);
static int _PrintAMList(int a1); static int _PrintAMList(int a1);
static void pipboyHandleVideoArchive(int a1); static void pipboyHandleVideoArchive(int a1);
static int pipboyRenderVideoArchive(int a1); static int pipboyRenderVideoArchive(int a1);
static void pipboyHandleAlarmClock(int a1); static void pipboyHandleAlarmClock(int eventCode);
static void pipboyWindowRenderRestOptions(int a1); static void pipboyWindowRenderRestOptions(int a1);
static void pipboyDrawHitPoints(); static void pipboyDrawHitPoints();
static void pipboyWindowCreateButtons(int a1, int a2, bool a3); static void pipboyWindowCreateButtons(int a1, int a2, bool a3);
static void pipboyWindowDestroyButtons(); static void pipboyWindowDestroyButtons();
static bool pipboyRest(int hours, int minutes, int kind); static bool pipboyRest(int hours, int minutes, int kind);
static bool _Check4Health(int a1); static bool _Check4Health(int minutes);
static bool _AddHealth(); static bool _AddHealth();
static void _ClacTime(int* hours, int* minutes, int wakeUpHour); static void _ClacTime(int* hours, int* minutes, int wakeUpHour);
static int pipboyRenderScreensaver(); static int pipboyRenderScreensaver();
@ -1751,9 +1751,9 @@ static int pipboyRenderVideoArchive(int a1)
} }
// 0x499518 // 0x499518
static void pipboyHandleAlarmClock(int a1) static void pipboyHandleAlarmClock(int eventCode)
{ {
if (a1 == 1024) { if (eventCode == 1024) {
if (_critter_can_obj_dude_rest()) { if (_critter_can_obj_dude_rest()) {
pipboyWindowDestroyButtons(); pipboyWindowDestroyButtons();
pipboyWindowRenderRestOptions(0); pipboyWindowRenderRestOptions(0);
@ -1769,15 +1769,14 @@ static void pipboyHandleAlarmClock(int a1)
// appropriate handler (not the alarm clock). // appropriate handler (not the alarm clock).
gPipboyTab = gPipboyPrevTab; gPipboyTab = gPipboyPrevTab;
} }
} else if (a1 >= 4 && a1 <= 17) { } else if (eventCode >= 4 && eventCode <= 17) {
soundPlayFile("ib1p1xx1"); soundPlayFile("ib1p1xx1");
pipboyWindowRenderRestOptions(a1 - 3); pipboyWindowRenderRestOptions(eventCode - 3);
int duration = a1 - 4; int duration = eventCode - 4;
int minutes = 0; int minutes = 0;
int hours = 0; int hours = 0;
int v10 = 0;
switch (duration) { switch (duration) {
case PIPBOY_REST_DURATION_TEN_MINUTES: case PIPBOY_REST_DURATION_TEN_MINUTES:
@ -2160,9 +2159,9 @@ static bool pipboyRest(int hours, int minutes, int duration)
} }
// 0x499FCC // 0x499FCC
static bool _Check4Health(int a1) static bool _Check4Health(int minutes)
{ {
_rest_time += a1; _rest_time += minutes;
if (_rest_time < 180) { if (_rest_time < 180) {
return false; return false;

View File

@ -35,27 +35,29 @@
namespace fallout { namespace fallout {
static int _obj_remove_from_inven(Object* critter, Object* item); static int _obj_remove_from_inven(Object* critter, Object* item);
static int _obj_use_book(Object* item_obj); static int _obj_use_book(Object* item);
static int _obj_use_flare(Object* critter_obj, Object* item_obj); static int _obj_use_flare(Object* critter, Object* item);
static int _obj_use_radio(Object* item_obj); static int _obj_use_radio(Object* item);
static int _obj_use_explosive(Object* explosive); static int _obj_use_explosive(Object* explosive);
static int _obj_use_power_on_car(Object* ammo); static int _obj_use_power_on_car(Object* ammo);
static int _obj_use_misc_item(Object* item_obj); static int _obj_use_misc_item(Object* item);
static int _protinstTestDroppedExplosive(Object* a1); static int _protinstTestDroppedExplosive(Object* explosiveItem);
static int _protinst_default_use_item(Object* a1, Object* a2, Object* item); static int _protinst_default_use_item(Object* user, Object* targetObj, Object* item);
static int useLadderDown(Object* a1, Object* ladder, int a3); static int useLadderDown(Object* user, Object* ladder);
static int useLadderUp(Object* a1, Object* ladder, int a3); static int useLadderUp(Object* user, Object* ladder);
static int useStairs(Object* a1, Object* stairs, int a3); static int useStairs(Object* user, Object* stairs);
static int _set_door_state_open(Object* a1, Object* a2); static int _set_door_state_open(Object* door, Object* obj2);
static int _set_door_state_closed(Object* a1, Object* a2); static int _set_door_state_closed(Object* door, Object* obj2);
static int _check_door_state(Object* a1, Object* a2); static int _check_door_state(Object* door, Object* obj2);
static bool _obj_is_portal(Object* obj); static bool _obj_is_portal(Object* obj);
static bool _obj_is_lockable(Object* obj); static bool _obj_is_lockable(Object* obj);
static bool _obj_is_openable(Object* obj); static bool _obj_is_openable(Object* obj);
static int objectOpenClose(Object* obj); static int objectOpenClose(Object* obj);
static bool objectIsJammed(Object* obj); static bool objectIsJammed(Object* obj);
// 0x49A990 // Accessed but not really used
//
// 0x49A990
static MessageListItem stru_49A990; static MessageListItem stru_49A990;
// 0x49A9A0 // 0x49A9A0
@ -105,10 +107,10 @@ int _obj_new_sid(Object* object, int* sidPtr)
return -1; return -1;
} }
script->field_14 = sid & 0xFFFFFF; script->index = sid & 0xFFFFFF;
if (objectType == OBJ_TYPE_CRITTER) { if (objectType == OBJ_TYPE_CRITTER) {
object->field_80 = script->field_14; object->scriptIndex = script->index;
} }
if (scriptType == SCRIPT_TYPE_SPATIAL) { if (scriptType == SCRIPT_TYPE_SPATIAL) {
@ -120,7 +122,7 @@ int _obj_new_sid(Object* object, int* sidPtr)
object->id = scriptsNewObjectId(); object->id = scriptsNewObjectId();
} }
script->field_1C = object->id; script->ownerId = object->id;
script->owner = object; script->owner = object;
_scr_find_str_run_info(sid & 0xFFFFFF, &(script->field_50), *sidPtr); _scr_find_str_run_info(sid & 0xFFFFFF, &(script->field_50), *sidPtr);
@ -129,9 +131,9 @@ int _obj_new_sid(Object* object, int* sidPtr)
} }
// 0x49AAC0 // 0x49AAC0
int _obj_new_sid_inst(Object* obj, int scriptType, int a3) int _obj_new_sid_inst(Object* obj, int scriptType, int scriptIndex)
{ {
if (a3 == -1) { if (scriptIndex == -1) {
return -1; return -1;
} }
@ -145,7 +147,7 @@ int _obj_new_sid_inst(Object* obj, int scriptType, int a3)
return -1; return -1;
} }
script->field_14 = a3; script->index = scriptIndex;
if (scriptType == SCRIPT_TYPE_SPATIAL) { if (scriptType == SCRIPT_TYPE_SPATIAL) {
script->sp.built_tile = builtTileCreate(obj->tile, obj->elevation); script->sp.built_tile = builtTileCreate(obj->tile, obj->elevation);
script->sp.radius = 3; script->sp.radius = 3;
@ -154,14 +156,14 @@ int _obj_new_sid_inst(Object* obj, int scriptType, int a3)
obj->sid = sid; obj->sid = sid;
obj->id = scriptsNewObjectId(); obj->id = scriptsNewObjectId();
script->field_1C = obj->id; script->ownerId = obj->id;
script->owner = obj; script->owner = obj;
_scr_find_str_run_info(a3 & 0xFFFFFF, &(script->field_50), sid); _scr_find_str_run_info(scriptIndex & 0xFFFFFF, &(script->field_50), sid);
if (PID_TYPE(obj->pid) == OBJ_TYPE_CRITTER) { if (PID_TYPE(obj->pid) == OBJ_TYPE_CRITTER) {
obj->field_80 = script->field_14; obj->scriptIndex = script->index;
} }
return 0; return 0;
@ -810,7 +812,7 @@ static int _obj_use_book(Object* book)
// Light a flare. // Light a flare.
// //
// 0x49BBA8 // 0x49BBA8
static int _obj_use_flare(Object* critter_obj, Object* flare) static int _obj_use_flare(Object* critter, Object* flare)
{ {
MessageListItem messageListItem; MessageListItem messageListItem;
@ -819,7 +821,7 @@ static int _obj_use_flare(Object* critter_obj, Object* flare)
} }
if ((flare->flags & OBJECT_QUEUED) != 0) { if ((flare->flags & OBJECT_QUEUED) != 0) {
if (critter_obj == gDude) { if (critter == gDude) {
// The flare is already lit. // The flare is already lit.
messageListItem.num = 588; messageListItem.num = 588;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) { if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
@ -827,7 +829,7 @@ static int _obj_use_flare(Object* critter_obj, Object* flare)
} }
} }
} else { } else {
if (critter_obj == gDude) { if (critter == gDude) {
// You light the flare. // You light the flare.
messageListItem.num = 588; messageListItem.num = 588;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) { if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
@ -1072,10 +1074,10 @@ int _protinst_use_item(Object* critter, Object* item)
} }
// 0x49BFE8 // 0x49BFE8
static int _protinstTestDroppedExplosive(Object* a1) static int _protinstTestDroppedExplosive(Object* explosiveItem)
{ {
// SFALL // SFALL
if (explosiveIsActiveExplosive(a1->pid)) { if (explosiveIsActiveExplosive(explosiveItem->pid)) {
Attack attack; Attack attack;
attackInit(&attack, gDude, nullptr, HIT_MODE_PUNCH, HIT_LOCATION_TORSO); attackInit(&attack, gDude, nullptr, HIT_MODE_PUNCH, HIT_LOCATION_TORSO);
attack.attackerFlags = DAM_HIT; attack.attackerFlags = DAM_HIT;
@ -1157,7 +1159,7 @@ int _obj_use_item(Object* a1, Object* a2)
} }
// 0x49C240 // 0x49C240
static int _protinst_default_use_item(Object* a1, Object* a2, Object* item) static int _protinst_default_use_item(Object* user, Object* targetObj, Object* item)
{ {
char formattedText[90]; char formattedText[90];
MessageListItem messageListItem; MessageListItem messageListItem;
@ -1165,8 +1167,8 @@ static int _protinst_default_use_item(Object* a1, Object* a2, Object* item)
int rc; int rc;
switch (itemGetType(item)) { switch (itemGetType(item)) {
case ITEM_TYPE_DRUG: case ITEM_TYPE_DRUG:
if (PID_TYPE(a2->pid) != OBJ_TYPE_CRITTER) { if (PID_TYPE(targetObj->pid) != OBJ_TYPE_CRITTER) {
if (a1 == gDude) { if (user == gDude) {
// That does nothing // That does nothing
messageListItem.num = 582; messageListItem.num = 582;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) { if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
@ -1176,7 +1178,7 @@ static int _protinst_default_use_item(Object* a1, Object* a2, Object* item)
return -1; return -1;
} }
if (critterIsDead(a2)) { if (critterIsDead(targetObj)) {
// 583: To your dismay, you realize that it is already dead. // 583: To your dismay, you realize that it is already dead.
// 584: As you reach down, you realize that it is already dead. // 584: As you reach down, you realize that it is already dead.
// 585: Alas, you are too late. // 585: Alas, you are too late.
@ -1188,24 +1190,24 @@ static int _protinst_default_use_item(Object* a1, Object* a2, Object* item)
return -1; return -1;
} }
rc = _item_d_take_drug(a2, item); rc = _item_d_take_drug(targetObj, item);
if (a1 == gDude && a2 != gDude) { if (user == gDude && targetObj != gDude) {
// TODO: Looks like there is bug in this branch, message 580 will never be shown, // TODO: Looks like there is bug in this branch, message 580 will never be shown,
// as we can only be here when target is not dude. // as we can only be here when target is not dude.
// 580: You use the %s. // 580: You use the %s.
// 581: You use the %s on %s. // 581: You use the %s on %s.
messageListItem.num = 580 + (a2 != gDude); messageListItem.num = 580 + (targetObj != gDude);
if (!messageListGetItem(&gProtoMessageList, &messageListItem)) { if (!messageListGetItem(&gProtoMessageList, &messageListItem)) {
return -1; return -1;
} }
snprintf(formattedText, sizeof(formattedText), messageListItem.text, objectGetName(item), objectGetName(a2)); snprintf(formattedText, sizeof(formattedText), messageListItem.text, objectGetName(item), objectGetName(targetObj));
displayMonitorAddMessage(formattedText); displayMonitorAddMessage(formattedText);
} }
if (a2 == gDude) { if (targetObj == gDude) {
interfaceRenderHitPoints(true); interfaceRenderHitPoints(true);
} }
@ -1213,7 +1215,7 @@ static int _protinst_default_use_item(Object* a1, Object* a2, Object* item)
case ITEM_TYPE_AMMO: case ITEM_TYPE_AMMO:
// SFALL: Fix for being able to charge the car by using cells on other // SFALL: Fix for being able to charge the car by using cells on other
// scenery/critters. // scenery/critters.
if (a2->pid == PROTO_ID_CAR || a2->pid == PROTO_ID_CAR_TRUNK) { if (targetObj->pid == PROTO_ID_CAR || targetObj->pid == PROTO_ID_CAR_TRUNK) {
rc = _obj_use_power_on_car(item); rc = _obj_use_power_on_car(item);
if (rc == 1) { if (rc == 1) {
return 1; return 1;
@ -1224,7 +1226,7 @@ static int _protinst_default_use_item(Object* a1, Object* a2, Object* item)
break; break;
case ITEM_TYPE_WEAPON: case ITEM_TYPE_WEAPON:
case ITEM_TYPE_MISC: case ITEM_TYPE_MISC:
rc = _obj_use_flare(a1, item); rc = _obj_use_flare(user, item);
if (rc == 0) { if (rc == 0) {
return 0; return 0;
} }
@ -1240,7 +1242,7 @@ static int _protinst_default_use_item(Object* a1, Object* a2, Object* item)
} }
// 0x49C3CC // 0x49C3CC
int _protinst_use_item_on(Object* a1, Object* a2, Object* item) int _protinst_use_item_on(Object* critter, Object* targetObj, Object* item)
{ {
int messageId = -1; int messageId = -1;
int criticalChanceModifier = 0; int criticalChanceModifier = 0;
@ -1277,55 +1279,55 @@ int _protinst_use_item_on(Object* a1, Object* a2, Object* item)
Script* script; Script* script;
if (item->sid == -1) { if (item->sid == -1) {
if (a2->sid == -1) { if (targetObj->sid == -1) {
return _protinst_default_use_item(a1, a2, item); return _protinst_default_use_item(critter, targetObj, item);
} }
scriptSetObjects(a2->sid, a1, item); scriptSetObjects(targetObj->sid, critter, item);
scriptExecProc(a2->sid, SCRIPT_PROC_USE_OBJ_ON); scriptExecProc(targetObj->sid, SCRIPT_PROC_USE_OBJ_ON);
if (scriptGetScript(a2->sid, &script) == -1) { if (scriptGetScript(targetObj->sid, &script) == -1) {
return -1; return -1;
} }
if (!script->scriptOverrides) { if (!script->scriptOverrides) {
return _protinst_default_use_item(a1, a2, item); return _protinst_default_use_item(critter, targetObj, item);
} }
} else { } else {
scriptSetObjects(item->sid, a1, a2); scriptSetObjects(item->sid, critter, targetObj);
scriptExecProc(item->sid, SCRIPT_PROC_USE_OBJ_ON); scriptExecProc(item->sid, SCRIPT_PROC_USE_OBJ_ON);
if (scriptGetScript(item->sid, &script) == -1) { if (scriptGetScript(item->sid, &script) == -1) {
return -1; return -1;
} }
if (script->field_28 == 0) { if (script->returnValue == 0) {
if (a2->sid == -1) { if (targetObj->sid == -1) {
return _protinst_default_use_item(a1, a2, item); return _protinst_default_use_item(critter, targetObj, item);
} }
scriptSetObjects(a2->sid, a1, item); scriptSetObjects(targetObj->sid, critter, item);
scriptExecProc(a2->sid, SCRIPT_PROC_USE_OBJ_ON); scriptExecProc(targetObj->sid, SCRIPT_PROC_USE_OBJ_ON);
Script* script; Script* script;
if (scriptGetScript(a2->sid, &script) == -1) { if (scriptGetScript(targetObj->sid, &script) == -1) {
return -1; return -1;
} }
if (!script->scriptOverrides) { if (!script->scriptOverrides) {
return _protinst_default_use_item(a1, a2, item); return _protinst_default_use_item(critter, targetObj, item);
} }
} }
} }
return script->field_28; return script->returnValue;
} }
if (isInCombat()) { if (isInCombat()) {
MessageListItem messageListItem; MessageListItem messageListItem;
// You cannot do that in combat. // You cannot do that in combat.
messageListItem.num = 902; messageListItem.num = 902;
if (a1 == gDude) { if (critter == gDude) {
if (messageListGetItem(&gProtoMessageList, &messageListItem)) { if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text); displayMonitorAddMessage(messageListItem.text);
} }
@ -1333,7 +1335,7 @@ int _protinst_use_item_on(Object* a1, Object* a2, Object* item)
return -1; return -1;
} }
if (skillUse(a1, a2, skill, criticalChanceModifier) != 0) { if (skillUse(critter, targetObj, skill, criticalChanceModifier) != 0) {
return 0; return 0;
} }
@ -1343,7 +1345,7 @@ int _protinst_use_item_on(Object* a1, Object* a2, Object* item)
MessageListItem messageListItem; MessageListItem messageListItem;
messageListItem.num = messageId; messageListItem.num = messageId;
if (a1 == gDude) { if (critter == gDude) {
if (messageListGetItem(&gProtoMessageList, &messageListItem)) { if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text); displayMonitorAddMessage(messageListItem.text);
} }
@ -1353,21 +1355,21 @@ int _protinst_use_item_on(Object* a1, Object* a2, Object* item)
} }
// 0x49C5FC // 0x49C5FC
int _obj_use_item_on(Object* a1, Object* a2, Object* a3) int _obj_use_item_on(Object* user, Object* targetObj, Object* item)
{ {
int rc = _protinst_use_item_on(a1, a2, a3); int rc = _protinst_use_item_on(user, targetObj, item);
if (rc == 1) { if (rc == 1) {
if (a1 != nullptr) { if (user != nullptr) {
int flags = a3->flags & OBJECT_IN_ANY_HAND; int flags = item->flags & OBJECT_IN_ANY_HAND;
itemRemove(a1, a3, 1); itemRemove(user, item, 1);
Object* replacedItem = itemReplace(a1, a3, flags); Object* replacedItem = itemReplace(user, item, flags);
// CE: Fix rare crash when using uninitialized action variables. The // CE: Fix rare crash when using uninitialized action variables. The
// following code is on par with |_obj_use_item| which does not // following code is on par with |_obj_use_item| which does not
// crash. // crash.
if (a1 == gDude) { if (user == gDude) {
int leftItemAction; int leftItemAction;
int rightItemAction; int rightItemAction;
interfaceGetItemActions(&leftItemAction, &rightItemAction); interfaceGetItemActions(&leftItemAction, &rightItemAction);
@ -1387,7 +1389,7 @@ int _obj_use_item_on(Object* a1, Object* a2, Object* a3)
} }
} }
_obj_destroy(a3); _obj_destroy(item);
rc = 0; rc = 0;
} }
@ -1429,10 +1431,10 @@ int _check_scenery_ap_cost(Object* obj, Object* a2)
} }
// 0x49C740 // 0x49C740
int _obj_use(Object* a1, Object* a2) int _obj_use(Object* user, Object* targetObj)
{ {
int type = FID_TYPE(a2->fid); int type = FID_TYPE(targetObj->fid);
if (a1 == gDude) { if (user == gDude) {
if (type != OBJ_TYPE_SCENERY) { if (type != OBJ_TYPE_SCENERY) {
return -1; return -1;
} }
@ -1443,22 +1445,22 @@ int _obj_use(Object* a1, Object* a2)
} }
Proto* sceneryProto; Proto* sceneryProto;
if (protoGetProto(a2->pid, &sceneryProto) == -1) { if (protoGetProto(targetObj->pid, &sceneryProto) == -1) {
return -1; return -1;
} }
if (PID_TYPE(a2->pid) == OBJ_TYPE_SCENERY && sceneryProto->scenery.type == SCENERY_TYPE_DOOR) { if (PID_TYPE(targetObj->pid) == OBJ_TYPE_SCENERY && sceneryProto->scenery.type == SCENERY_TYPE_DOOR) {
return _obj_use_door(a1, a2, 0); return _obj_use_door(user, targetObj);
} }
bool scriptOverrides = false; bool scriptOverrides = false;
if (a2->sid != -1) { if (targetObj->sid != -1) {
scriptSetObjects(a2->sid, a1, a2); scriptSetObjects(targetObj->sid, user, targetObj);
scriptExecProc(a2->sid, SCRIPT_PROC_USE); scriptExecProc(targetObj->sid, SCRIPT_PROC_USE);
Script* script; Script* script;
if (scriptGetScript(a2->sid, &script) == -1) { if (scriptGetScript(targetObj->sid, &script) == -1) {
return -1; return -1;
} }
@ -1466,17 +1468,17 @@ int _obj_use(Object* a1, Object* a2)
} }
if (!scriptOverrides) { if (!scriptOverrides) {
if (PID_TYPE(a2->pid) == OBJ_TYPE_SCENERY) { if (PID_TYPE(targetObj->pid) == OBJ_TYPE_SCENERY) {
if (sceneryProto->scenery.type == SCENERY_TYPE_LADDER_DOWN) { if (sceneryProto->scenery.type == SCENERY_TYPE_LADDER_DOWN) {
if (useLadderDown(a1, a2, 0) == 0) { if (useLadderDown(user, targetObj) == 0) {
scriptOverrides = true; scriptOverrides = true;
} }
} else if (sceneryProto->scenery.type == SCENERY_TYPE_LADDER_UP) { } else if (sceneryProto->scenery.type == SCENERY_TYPE_LADDER_UP) {
if (useLadderUp(a1, a2, 0) == 0) { if (useLadderUp(user, targetObj) == 0) {
scriptOverrides = true; scriptOverrides = true;
} }
} else if (sceneryProto->scenery.type == SCENERY_TYPE_STAIRS) { } else if (sceneryProto->scenery.type == SCENERY_TYPE_STAIRS) {
if (useStairs(a1, a2, 0) == 0) { if (useStairs(user, targetObj) == 0) {
scriptOverrides = true; scriptOverrides = true;
} }
} }
@ -1484,7 +1486,7 @@ int _obj_use(Object* a1, Object* a2)
} }
if (!scriptOverrides) { if (!scriptOverrides) {
if (a1 == gDude) { if (user == gDude) {
// You see: %s // You see: %s
MessageListItem messageListItem; MessageListItem messageListItem;
messageListItem.num = 480; messageListItem.num = 480;
@ -1493,7 +1495,7 @@ int _obj_use(Object* a1, Object* a2)
} }
char formattedText[260]; char formattedText[260];
const char* name = objectGetName(a2); const char* name = objectGetName(targetObj);
snprintf(formattedText, sizeof(formattedText), messageListItem.text, name); snprintf(formattedText, sizeof(formattedText), messageListItem.text, name);
displayMonitorAddMessage(formattedText); displayMonitorAddMessage(formattedText);
} }
@ -1505,7 +1507,7 @@ int _obj_use(Object* a1, Object* a2)
} }
// 0x49C900 // 0x49C900
static int useLadderDown(Object* a1, Object* ladder, int a3) static int useLadderDown(Object* user, Object* ladder)
{ {
int builtTile = ladder->data.scenery.ladder.destinationBuiltTile; int builtTile = ladder->data.scenery.ladder.destinationBuiltTile;
if (builtTile == -1) { if (builtTile == -1) {
@ -1528,7 +1530,7 @@ static int useLadderDown(Object* a1, Object* ladder, int a3)
wmMapMarkMapEntranceState(transition.map, elevation, 1); wmMapMarkMapEntranceState(transition.map, elevation, 1);
} else { } else {
Rect updatedRect; Rect updatedRect;
if (objectSetLocation(a1, tile, elevation, &updatedRect) == -1) { if (objectSetLocation(user, tile, elevation, &updatedRect) == -1) {
return -1; return -1;
} }
@ -1539,7 +1541,7 @@ static int useLadderDown(Object* a1, Object* ladder, int a3)
} }
// 0x49C9A4 // 0x49C9A4
static int useLadderUp(Object* a1, Object* ladder, int a3) static int useLadderUp(Object* user, Object* ladder)
{ {
int builtTile = ladder->data.scenery.ladder.destinationBuiltTile; int builtTile = ladder->data.scenery.ladder.destinationBuiltTile;
if (builtTile == -1) { if (builtTile == -1) {
@ -1562,7 +1564,7 @@ static int useLadderUp(Object* a1, Object* ladder, int a3)
wmMapMarkMapEntranceState(transition.map, elevation, 1); wmMapMarkMapEntranceState(transition.map, elevation, 1);
} else { } else {
Rect updatedRect; Rect updatedRect;
if (objectSetLocation(a1, tile, elevation, &updatedRect) == -1) { if (objectSetLocation(user, tile, elevation, &updatedRect) == -1) {
return -1; return -1;
} }
@ -1573,7 +1575,7 @@ static int useLadderUp(Object* a1, Object* ladder, int a3)
} }
// 0x49CA48 // 0x49CA48
static int useStairs(Object* a1, Object* stairs, int a3) static int useStairs(Object* user, Object* stairs)
{ {
int builtTile = stairs->data.scenery.stairs.destinationBuiltTile; int builtTile = stairs->data.scenery.stairs.destinationBuiltTile;
if (builtTile == -1) { if (builtTile == -1) {
@ -1596,7 +1598,7 @@ static int useStairs(Object* a1, Object* stairs, int a3)
wmMapMarkMapEntranceState(transition.map, elevation, 1); wmMapMarkMapEntranceState(transition.map, elevation, 1);
} else { } else {
Rect updatedRect; Rect updatedRect;
if (objectSetLocation(a1, tile, elevation, &updatedRect) == -1) { if (objectSetLocation(user, tile, elevation, &updatedRect) == -1) {
return -1; return -1;
} }
@ -1607,37 +1609,37 @@ static int useStairs(Object* a1, Object* stairs, int a3)
} }
// 0x49CAF4 // 0x49CAF4
static int _set_door_state_open(Object* a1, Object* a2) static int _set_door_state_open(Object* door, Object* obj2)
{ {
a1->data.scenery.door.openFlags |= 0x01; door->data.scenery.door.openFlags |= 0x01;
return 0; return 0;
} }
// 0x49CB04 // 0x49CB04
static int _set_door_state_closed(Object* a1, Object* a2) static int _set_door_state_closed(Object* door, Object* obj2)
{ {
a1->data.scenery.door.openFlags &= ~0x01; door->data.scenery.door.openFlags &= ~0x01;
return 0; return 0;
} }
// 0x49CB14 // 0x49CB14
static int _check_door_state(Object* a1, Object* a2) static int _check_door_state(Object* door, Object* obj2)
{ {
if ((a1->data.scenery.door.openFlags & 0x01) == 0) { if ((door->data.scenery.door.openFlags & 0x01) == 0) {
// SFALL: Fix flags on non-door objects. // SFALL: Fix flags on non-door objects.
if (_obj_is_portal(a1)) { if (_obj_is_portal(door)) {
a1->flags &= ~OBJECT_OPEN_DOOR; door->flags &= ~OBJECT_OPEN_DOOR;
} }
_obj_rebuild_all_light(); _obj_rebuild_all_light();
tileWindowRefresh(); tileWindowRefresh();
if (a1->frame == 0) { if (door->frame == 0) {
return 0; return 0;
} }
CacheEntry* artHandle; CacheEntry* artHandle;
Art* art = artLock(a1->fid, &artHandle); Art* art = artLock(door->fid, &artHandle);
if (art == nullptr) { if (art == nullptr) {
return -1; return -1;
} }
@ -1645,16 +1647,16 @@ static int _check_door_state(Object* a1, Object* a2)
Rect dirty; Rect dirty;
Rect temp; Rect temp;
objectGetRect(a1, &dirty); objectGetRect(door, &dirty);
for (int frame = a1->frame - 1; frame >= 0; frame--) { for (int frame = door->frame - 1; frame >= 0; frame--) {
int x; int x;
int y; int y;
artGetFrameOffsets(art, frame, a1->rotation, &x, &y); artGetFrameOffsets(art, frame, door->rotation, &x, &y);
_obj_offset(a1, -x, -y, &temp); _obj_offset(door, -x, -y, &temp);
} }
objectSetFrame(a1, 0, &temp); objectSetFrame(door, 0, &temp);
rectUnion(&dirty, &temp, &dirty); rectUnion(&dirty, &temp, &dirty);
tileWindowRefreshRect(&dirty, gElevation); tileWindowRefreshRect(&dirty, gElevation);
@ -1663,21 +1665,21 @@ static int _check_door_state(Object* a1, Object* a2)
return 0; return 0;
} else { } else {
// SFALL: Fix flags on non-door objects. // SFALL: Fix flags on non-door objects.
if (_obj_is_portal(a1)) { if (_obj_is_portal(door)) {
a1->flags |= OBJECT_OPEN_DOOR; door->flags |= OBJECT_OPEN_DOOR;
} }
_obj_rebuild_all_light(); _obj_rebuild_all_light();
tileWindowRefresh(); tileWindowRefresh();
CacheEntry* artHandle; CacheEntry* artHandle;
Art* art = artLock(a1->fid, &artHandle); Art* art = artLock(door->fid, &artHandle);
if (art == nullptr) { if (art == nullptr) {
return -1; return -1;
} }
int frameCount = artGetFrameCount(art); int frameCount = artGetFrameCount(art);
if (a1->frame == frameCount - 1) { if (door->frame == frameCount - 1) {
artUnlock(artHandle); artUnlock(artHandle);
return 0; return 0;
} }
@ -1685,16 +1687,16 @@ static int _check_door_state(Object* a1, Object* a2)
Rect dirty; Rect dirty;
Rect temp; Rect temp;
objectGetRect(a1, &dirty); objectGetRect(door, &dirty);
for (int frame = a1->frame + 1; frame < frameCount; frame++) { for (int frame = door->frame + 1; frame < frameCount; frame++) {
int x; int x;
int y; int y;
artGetFrameOffsets(art, frame, a1->rotation, &x, &y); artGetFrameOffsets(art, frame, door->rotation, &x, &y);
_obj_offset(a1, x, y, &temp); _obj_offset(door, x, y, &temp);
} }
objectSetFrame(a1, frameCount - 1, &temp); objectSetFrame(door, frameCount - 1, &temp);
rectUnion(&dirty, &temp, &dirty); rectUnion(&dirty, &temp, &dirty);
tileWindowRefreshRect(&dirty, gElevation); tileWindowRefreshRect(&dirty, gElevation);
@ -1705,20 +1707,20 @@ static int _check_door_state(Object* a1, Object* a2)
} }
// 0x49CCB8 // 0x49CCB8
int _obj_use_door(Object* a1, Object* a2, int a3) int _obj_use_door(Object* user, Object* door, bool animateOnly)
{ {
if (objectIsLocked(a2)) { if (objectIsLocked(door)) {
const char* sfx = sfxBuildOpenName(a2, SCENERY_SOUND_EFFECT_LOCKED); const char* sfx = sfxBuildOpenName(door, SCENERY_SOUND_EFFECT_LOCKED);
soundPlayFile(sfx); soundPlayFile(sfx);
} }
bool scriptOverrides = false; bool scriptOverrides = false;
if (a2->sid != -1) { if (door->sid != -1) {
scriptSetObjects(a2->sid, a1, a2); scriptSetObjects(door->sid, user, door);
scriptExecProc(a2->sid, SCRIPT_PROC_USE); scriptExecProc(door->sid, SCRIPT_PROC_USE);
Script* script; Script* script;
if (scriptGetScript(a2->sid, &script) == -1) { if (scriptGetScript(door->sid, &script) == -1) {
return -1; return -1;
} }
@ -1729,23 +1731,25 @@ int _obj_use_door(Object* a1, Object* a2, int a3)
int start; int start;
int end; int end;
int step; int step;
if (a2->frame != 0) { if (door->frame != 0) {
if (_obj_blocking_at(nullptr, a2->tile, a2->elevation) != nullptr) { if (_obj_blocking_at(nullptr, door->tile, door->elevation) != nullptr) {
MessageListItem messageListItem; MessageListItem messageListItem;
char* text = getmsg(&gProtoMessageList, &messageListItem, 597); char* text = getmsg(&gProtoMessageList, &messageListItem, 597);
displayMonitorAddMessage(text); displayMonitorAddMessage(text);
return -1; return -1;
} }
start = 1; start = 1;
end = (a3 == 0) - 1; // TODO: strange logic, check if correct
end = animateOnly ? -1 : 0;
step = -1; step = -1;
} else { } else {
if (a2->data.scenery.door.openFlags & 0x01) { if (door->data.scenery.door.openFlags & 0x01) {
return -1; return -1;
} }
start = 0; start = 0;
end = (a3 != 0) + 1; // TODO: strange logic, check if correct
end = animateOnly ? 2 : 1;
step = 1; step = 1;
} }
@ -1753,27 +1757,27 @@ int _obj_use_door(Object* a1, Object* a2, int a3)
for (int i = start; i != end; i += step) { for (int i = start; i != end; i += step) {
if (i != 0) { if (i != 0) {
if (a3 == 0) { if (!animateOnly) {
animationRegisterCallback(a2, a2, (AnimationCallback*)_set_door_state_closed, -1); animationRegisterCallback(door, door, (AnimationCallback*)_set_door_state_closed, -1);
} }
const char* sfx = sfxBuildOpenName(a2, SCENERY_SOUND_EFFECT_CLOSED); const char* sfx = sfxBuildOpenName(door, SCENERY_SOUND_EFFECT_CLOSED);
animationRegisterPlaySoundEffect(a2, sfx, -1); animationRegisterPlaySoundEffect(door, sfx, -1);
animationRegisterAnimateReversed(a2, ANIM_STAND, 0); animationRegisterAnimateReversed(door, ANIM_STAND, 0);
} else { } else {
if (a3 == 0) { if (!animateOnly) {
animationRegisterCallback(a2, a2, (AnimationCallback*)_set_door_state_open, -1); animationRegisterCallback(door, door, (AnimationCallback*)_set_door_state_open, -1);
} }
const char* sfx = sfxBuildOpenName(a2, SCENERY_SOUND_EFFECT_OPEN); const char* sfx = sfxBuildOpenName(door, SCENERY_SOUND_EFFECT_OPEN);
animationRegisterPlaySoundEffect(a2, sfx, -1); animationRegisterPlaySoundEffect(door, sfx, -1);
animationRegisterAnimate(a2, ANIM_STAND, 0); animationRegisterAnimate(door, ANIM_STAND, 0);
} }
} }
animationRegisterCallbackForced(a2, a2, (AnimationCallback*)_check_door_state, -1); animationRegisterCallbackForced(door, door, (AnimationCallback*)_check_door_state, -1);
reg_anim_end(); reg_anim_end();
} }
@ -2177,37 +2181,40 @@ int objectUnjamAll()
// critter_attempt_placement // critter_attempt_placement
// 0x49D4D4 // 0x49D4D4
int _obj_attempt_placement(Object* obj, int tile, int elevation, int a4) int _obj_attempt_placement(Object* obj, int tile, int elevation, int radius)
{ {
constexpr int maxDist = 7;
constexpr int maxAttempts = 100;
if (tile == -1) { if (tile == -1) {
return -1; return -1;
} }
int newTile = tile; int newTile = tile;
if (_obj_blocking_at(nullptr, tile, elevation) != nullptr) { if (_obj_blocking_at(nullptr, tile, elevation) != nullptr) {
int v6 = a4; // Find a suitable alternative tile where the dude can get to.
if (a4 < 1) { int dist = radius >= 1 ? radius : 1;
v6 = 1;
}
int attempts = 0; int attempts = 0;
while (v6 < 7) { while (dist < maxDist) {
attempts++; attempts++;
if (attempts >= 100) { if (attempts >= maxAttempts) {
break; break;
} }
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) { for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
newTile = tileGetTileInDirection(tile, rotation, v6); newTile = tileGetTileInDirection(tile, rotation, dist);
if (_obj_blocking_at(nullptr, newTile, elevation) == nullptr && v6 > 1 && _make_path(gDude, gDude->tile, newTile, nullptr, 0) != 0) { if (_obj_blocking_at(nullptr, newTile, elevation) == nullptr
&& dist > 1
&& _make_path(gDude, gDude->tile, newTile, nullptr, 0) != 0) {
break; break;
} }
} }
v6++; dist++;
} }
if (a4 != 1 && v6 > a4 + 2) { // If location is too far (or not found at all), find any free adjacent tile, regardless if it's reachable or not.
if (radius != 1 && dist > radius + 2) {
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) { for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
int candidate = tileGetTileInDirection(tile, rotation, 1); int candidate = tileGetTileInDirection(tile, rotation, 1);
if (_obj_blocking_at(nullptr, candidate, elevation) == nullptr) { if (_obj_blocking_at(nullptr, candidate, elevation) == nullptr) {

View File

@ -17,11 +17,11 @@ int _obj_drop(Object* a1, Object* a2);
int _obj_destroy(Object* obj); int _obj_destroy(Object* obj);
int _protinst_use_item(Object* a1, Object* a2); int _protinst_use_item(Object* a1, Object* a2);
int _obj_use_item(Object* a1, Object* a2); int _obj_use_item(Object* a1, Object* a2);
int _protinst_use_item_on(Object* a1, Object* a2, Object* item); int _protinst_use_item_on(Object* critter, Object* targetObj, Object* item);
int _obj_use_item_on(Object* a1, Object* a2, Object* a3); int _obj_use_item_on(Object* user, Object* targetObj, Object* item);
int _check_scenery_ap_cost(Object* obj, Object* a2); int _check_scenery_ap_cost(Object* obj, Object* a2);
int _obj_use(Object* a1, Object* a2); int _obj_use(Object* user, Object* targetObj);
int _obj_use_door(Object* a1, Object* a2, int a3); int _obj_use_door(Object* user, Object* doorObj, bool animateOnly = false);
int _obj_use_container(Object* critter, Object* item); int _obj_use_container(Object* critter, Object* item);
int _obj_use_skill_on(Object* a1, Object* a2, int skill); int _obj_use_skill_on(Object* a1, Object* a2, int skill);
bool objectIsLocked(Object* obj); bool objectIsLocked(Object* obj);
@ -33,7 +33,7 @@ int objectClose(Object* obj);
int objectJamLock(Object* obj); int objectJamLock(Object* obj);
int objectUnjamLock(Object* obj); int objectUnjamLock(Object* obj);
int objectUnjamAll(); int objectUnjamAll();
int _obj_attempt_placement(Object* obj, int tile, int elevation, int a4); int _obj_attempt_placement(Object* obj, int tile, int elevation, int radius);
int _objPMAttemptPlacement(Object* obj, int tile, int elevation); int _objPMAttemptPlacement(Object* obj, int tile, int elevation);
} // namespace fallout } // namespace fallout

View File

@ -603,7 +603,7 @@ Object* scriptGetSelf(Program* program)
} }
object->id = scriptsNewObjectId(); object->id = scriptsNewObjectId();
v1->field_1C = object->id; v1->ownerId = object->id;
v1->owner = object; v1->owner = object;
for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) { for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) {
@ -924,7 +924,7 @@ int scriptsHandleRequests()
} }
if ((gScriptsRequests & SCRIPT_REQUEST_ELEVATOR) != 0) { if ((gScriptsRequests & SCRIPT_REQUEST_ELEVATOR) != 0) {
int map = gMapHeader.field_34; int map = gMapHeader.index;
int elevation = gScriptsRequestedElevatorLevel; int elevation = gScriptsRequestedElevatorLevel;
int tile = -1; int tile = -1;
@ -933,7 +933,7 @@ int scriptsHandleRequests()
if (elevatorSelectLevel(gScriptsRequestedElevatorType, &map, &elevation, &tile) != -1) { if (elevatorSelectLevel(gScriptsRequestedElevatorType, &map, &elevation, &tile) != -1) {
automapSaveCurrent(); automapSaveCurrent();
if (map == gMapHeader.field_34) { if (map == gMapHeader.index) {
if (elevation == gElevation) { if (elevation == gElevation) {
reg_anim_clear(gDude); reg_anim_clear(gDude);
objectSetRotation(gDude, ROTATION_SE, nullptr); objectSetRotation(gDude, ROTATION_SE, nullptr);
@ -1034,14 +1034,14 @@ int scriptsHandleRequests()
int _scripts_check_state_in_combat() int _scripts_check_state_in_combat()
{ {
if ((gScriptsRequests & SCRIPT_REQUEST_ELEVATOR) != 0) { if ((gScriptsRequests & SCRIPT_REQUEST_ELEVATOR) != 0) {
int map = gMapHeader.field_34; int map = gMapHeader.index;
int elevation = gScriptsRequestedElevatorLevel; int elevation = gScriptsRequestedElevatorLevel;
int tile = -1; int tile = -1;
if (elevatorSelectLevel(gScriptsRequestedElevatorType, &map, &elevation, &tile) != -1) { if (elevatorSelectLevel(gScriptsRequestedElevatorType, &map, &elevation, &tile) != -1) {
automapSaveCurrent(); automapSaveCurrent();
if (map == gMapHeader.field_34) { if (map == gMapHeader.index) {
if (elevation == gElevation) { if (elevation == gElevation) {
reg_anim_clear(gDude); reg_anim_clear(gDude);
objectSetRotation(gDude, ROTATION_SE, nullptr); objectSetRotation(gDude, ROTATION_SE, nullptr);
@ -1276,7 +1276,7 @@ int scriptExecProc(int sid, int proc)
clock(); clock();
char name[16]; char name[16];
if (scriptsGetFileName(script->field_14 & 0xFFFFFF, name, sizeof(name)) == -1) { if (scriptsGetFileName(script->index & 0xFFFFFF, name, sizeof(name)) == -1) {
return -1; return -1;
} }
@ -1817,13 +1817,13 @@ static int scriptWrite(Script* scr, File* stream)
} }
if (fileWriteInt32(stream, scr->flags) == -1) return -1; if (fileWriteInt32(stream, scr->flags) == -1) return -1;
if (fileWriteInt32(stream, scr->field_14) == -1) return -1; if (fileWriteInt32(stream, scr->index) == -1) return -1;
// NOTE: Original code writes `scr->program` pointer which is meaningless. // NOTE: Original code writes `scr->program` pointer which is meaningless.
if (fileWriteInt32(stream, 0) == -1) return -1; if (fileWriteInt32(stream, 0) == -1) return -1;
if (fileWriteInt32(stream, scr->field_1C) == -1) return -1; if (fileWriteInt32(stream, scr->ownerId) == -1) return -1;
if (fileWriteInt32(stream, scr->localVarsOffset) == -1) return -1; if (fileWriteInt32(stream, scr->localVarsOffset) == -1) return -1;
if (fileWriteInt32(stream, scr->localVarsCount) == -1) return -1; if (fileWriteInt32(stream, scr->localVarsCount) == -1) return -1;
if (fileWriteInt32(stream, scr->field_28) == -1) return -1; if (fileWriteInt32(stream, scr->returnValue) == -1) return -1;
if (fileWriteInt32(stream, scr->action) == -1) return -1; if (fileWriteInt32(stream, scr->action) == -1) return -1;
if (fileWriteInt32(stream, scr->fixedParam) == -1) return -1; if (fileWriteInt32(stream, scr->fixedParam) == -1) return -1;
if (fileWriteInt32(stream, scr->actionBeingUsed) == -1) return -1; if (fileWriteInt32(stream, scr->actionBeingUsed) == -1) return -1;
@ -1971,12 +1971,12 @@ static int scriptRead(Script* scr, File* stream)
} }
if (fileReadInt32(stream, &(scr->flags)) == -1) return -1; if (fileReadInt32(stream, &(scr->flags)) == -1) return -1;
if (fileReadInt32(stream, &(scr->field_14)) == -1) return -1; if (fileReadInt32(stream, &(scr->index)) == -1) return -1;
if (fileReadInt32(stream, &(prg)) == -1) return -1; if (fileReadInt32(stream, &(prg)) == -1) return -1;
if (fileReadInt32(stream, &(scr->field_1C)) == -1) return -1; if (fileReadInt32(stream, &(scr->ownerId)) == -1) return -1;
if (fileReadInt32(stream, &(scr->localVarsOffset)) == -1) return -1; if (fileReadInt32(stream, &(scr->localVarsOffset)) == -1) return -1;
if (fileReadInt32(stream, &(scr->localVarsCount)) == -1) return -1; if (fileReadInt32(stream, &(scr->localVarsCount)) == -1) return -1;
if (fileReadInt32(stream, &(scr->field_28)) == -1) return -1; if (fileReadInt32(stream, &(scr->returnValue)) == -1) return -1;
if (fileReadInt32(stream, &(scr->action)) == -1) return -1; if (fileReadInt32(stream, &(scr->action)) == -1) return -1;
if (fileReadInt32(stream, &(scr->fixedParam)) == -1) return -1; if (fileReadInt32(stream, &(scr->fixedParam)) == -1) return -1;
if (fileReadInt32(stream, &(scr->actionBeingUsed)) == -1) return -1; if (fileReadInt32(stream, &(scr->actionBeingUsed)) == -1) return -1;
@ -2196,11 +2196,11 @@ int scriptAdd(int* sidPtr, int scriptType)
scr->sp.built_tile = -1; scr->sp.built_tile = -1;
scr->sp.radius = -1; scr->sp.radius = -1;
scr->flags = 0; scr->flags = 0;
scr->field_14 = -1; scr->index = -1;
scr->program = nullptr; scr->program = nullptr;
scr->localVarsOffset = -1; scr->localVarsOffset = -1;
scr->localVarsCount = 0; scr->localVarsCount = 0;
scr->field_28 = 0; scr->returnValue = 0;
scr->action = 0; scr->action = 0;
scr->fixedParam = 0; scr->fixedParam = 0;
scr->owner = nullptr; scr->owner = nullptr;
@ -2797,7 +2797,7 @@ int scriptGetLocalVar(int sid, int variable, ProgramValue& value)
if (script->localVarsCount == 0) { if (script->localVarsCount == 0) {
// NOTE: Uninline. // NOTE: Uninline.
_scr_find_str_run_info(script->field_14, &(script->field_50), sid); _scr_find_str_run_info(script->index, &(script->field_50), sid);
} }
if (script->localVarsCount > 0) { if (script->localVarsCount > 0) {
@ -2825,7 +2825,7 @@ int scriptSetLocalVar(int sid, int variable, ProgramValue& value)
if (script->localVarsCount == 0) { if (script->localVarsCount == 0) {
// NOTE: Uninline. // NOTE: Uninline.
_scr_find_str_run_info(script->field_14, &(script->field_50), sid); _scr_find_str_run_info(script->index, &(script->field_50), sid);
} }
if (script->localVarsCount <= 0) { if (script->localVarsCount <= 0) {

View File

@ -102,12 +102,12 @@ typedef struct Script {
int flags; int flags;
// scr_script_idx // scr_script_idx
int field_14; int index;
Program* program; Program* program;
// scr_oid // scr_oid
int field_1C; int ownerId;
// scr_local_var_offset // scr_local_var_offset
int localVarsOffset; int localVarsOffset;
@ -115,8 +115,8 @@ typedef struct Script {
// scr_num_local_vars // scr_num_local_vars
int localVarsCount; int localVarsCount;
// return value? // set by scr_return opcode
int field_28; int returnValue;
// Currently executed action. // Currently executed action.
// //

View File

@ -292,7 +292,7 @@ static void op_abs(Program* program)
static void op_get_script(Program* program) static void op_get_script(Program* program)
{ {
Object* obj = static_cast<Object*>(programStackPopPointer(program)); Object* obj = static_cast<Object*>(programStackPopPointer(program));
programStackPushInteger(program, obj->field_80 + 1); programStackPushInteger(program, obj->scriptIndex + 1);
} }
// get_proto_data // get_proto_data

View File

@ -29,7 +29,7 @@ int skillAddForce(Object* critter, int skill);
int skillsGetCost(int a1); int skillsGetCost(int a1);
int skillSub(Object* critter, int skill); int skillSub(Object* critter, int skill);
int skillSubForce(Object* critter, int skill); int skillSubForce(Object* critter, int skill);
int skillRoll(Object* critter, int skill, int a3, int* a4); int skillRoll(Object* critter, int skill, int modifier, int* howMuch);
char* skillGetName(int skill); char* skillGetName(int skill);
char* skillGetDescription(int skill); char* skillGetDescription(int skill);
char* skillGetAttributes(int skill); char* skillGetAttributes(int skill);

View File

@ -167,6 +167,11 @@ typedef enum EncounterConditionalOperator {
ENCOUNTER_CONDITIONAL_OPERATOR_COUNT, ENCOUNTER_CONDITIONAL_OPERATOR_COUNT,
} EncounterConditionalOperator; } EncounterConditionalOperator;
typedef enum EncounterRatioMode {
ENCOUNTER_RATIO_MODE_USE_RATIO,
ENCOUNTER_RATIO_MODE_SINGLE,
} EncounterRatioMode;
typedef enum Daytime { typedef enum Daytime {
DAY_PART_MORNING, DAY_PART_MORNING,
DAY_PART_AFTERNOON, DAY_PART_AFTERNOON,
@ -326,7 +331,7 @@ typedef struct EncounterItem {
typedef struct EncounterEntry { typedef struct EncounterEntry {
char field_0[40]; char field_0[40];
int field_28; int field_28;
int field_2C; int ratioMode;
int ratio; int ratio;
int pid; int pid;
int flags; int flags;
@ -1689,7 +1694,7 @@ static int wmParseEncBaseSubTypeStr(EncounterEntry* encounterEntry, char** strin
} }
if (strParseIntWithKey(&string, "ratio", &(encounterEntry->ratio), ":") == 0) { if (strParseIntWithKey(&string, "ratio", &(encounterEntry->ratio), ":") == 0) {
encounterEntry->field_2C = 0; encounterEntry->ratioMode = ENCOUNTER_RATIO_MODE_USE_RATIO;
} }
if (strstr(string, "dead,") == string) { if (strstr(string, "dead,") == string) {
@ -1739,7 +1744,7 @@ static int wmEncBaseTypeSlotInit(Encounter* encounter)
static int wmEncBaseSubTypeSlotInit(EncounterEntry* encounterEntry) static int wmEncBaseSubTypeSlotInit(EncounterEntry* encounterEntry)
{ {
encounterEntry->field_28 = -1; encounterEntry->field_28 = -1;
encounterEntry->field_2C = 1; encounterEntry->ratioMode = ENCOUNTER_RATIO_MODE_SINGLE;
encounterEntry->ratio = 100; encounterEntry->ratio = 100;
encounterEntry->pid = -1; encounterEntry->pid = -1;
encounterEntry->flags = 0; encounterEntry->flags = 0;
@ -2822,13 +2827,13 @@ bool wmMapIdxIsSaveable(int mapIdx)
// 0x4BFA64 // 0x4BFA64
bool wmMapIsSaveable() bool wmMapIsSaveable()
{ {
return (wmMapInfoList[gMapHeader.field_34].flags & MAP_SAVED) != 0; return (wmMapInfoList[gMapHeader.index].flags & MAP_SAVED) != 0;
} }
// 0x4BFA90 // 0x4BFA90
bool wmMapDeadBodiesAge() bool wmMapDeadBodiesAge()
{ {
return (wmMapInfoList[gMapHeader.field_34].flags & MAP_DEAD_BODIES_AGE) != 0; return (wmMapInfoList[gMapHeader.index].flags & MAP_DEAD_BODIES_AGE) != 0;
} }
// 0x4BFABC // 0x4BFABC
@ -2839,7 +2844,7 @@ bool wmMapCanRestHere(int elevation)
// NOTE: I'm not sure why they're copied. // NOTE: I'm not sure why they're copied.
memcpy(flags, _can_rest_here, sizeof(flags)); memcpy(flags, _can_rest_here, sizeof(flags));
MapInfo* map = &(wmMapInfoList[gMapHeader.field_34]); MapInfo* map = &(wmMapInfoList[gMapHeader.index]);
return (map->flags & flags[elevation]) != 0; return (map->flags & flags[elevation]) != 0;
} }
@ -3791,11 +3796,11 @@ static int wmSetupCritterObjs(int encounterIndex, Object** critterPtr, int critt
} }
int encounterEntryCritterCount; int encounterEntryCritterCount;
switch (encounterEntry->field_2C) { switch (encounterEntry->ratioMode) {
case 0: case ENCOUNTER_RATIO_MODE_USE_RATIO:
encounterEntryCritterCount = encounterEntry->ratio * critterCount / 100; encounterEntryCritterCount = encounterEntry->ratio * critterCount / 100;
break; break;
case 1: case ENCOUNTER_RATIO_MODE_SINGLE:
encounterEntryCritterCount = 1; encounterEntryCritterCount = 1;
break; break;
default: default:
@ -3884,7 +3889,7 @@ static int wmSetupCritterObjs(int encounterIndex, Object** critterPtr, int critt
_obj_disconnect(item, nullptr); _obj_disconnect(item, nullptr);
if (encounterItem->isEquipped) { if (encounterItem->isEquipped) {
if (_inven_wield(object, item, 1) == -1) { if (_inven_wield(object, item, HAND_RIGHT) == -1) {
debugPrint("\nERROR: wmSetupCritterObjs: Inven Wield Failed: %d on %s: Critter Fid: %d", item->pid, critterGetName(object), object->fid); debugPrint("\nERROR: wmSetupCritterObjs: Inven Wield Failed: %d on %s: Critter Fid: %d", item->pid, critterGetName(object), object->fid);
} }
} }
@ -3924,33 +3929,32 @@ static int wmSetupRndNextTileNumInit(Encounter* encounter)
case ENCOUNTER_FORMATION_TYPE_DOUBLE_LINE: case ENCOUNTER_FORMATION_TYPE_DOUBLE_LINE:
case ENCOUNTER_FORMATION_TYPE_WEDGE: case ENCOUNTER_FORMATION_TYPE_WEDGE:
case ENCOUNTER_FORMATION_TYPE_CONE: case ENCOUNTER_FORMATION_TYPE_CONE:
case ENCOUNTER_FORMATION_TYPE_HUDDLE: case ENCOUNTER_FORMATION_TYPE_HUDDLE: {
if (1) { MapInfo* map = &(wmMapInfoList[gMapHeader.index]);
MapInfo* map = &(wmMapInfoList[gMapHeader.field_34]); if (map->startPointsLength != 0) {
if (map->startPointsLength != 0) { int rspIndex = randomBetween(0, map->startPointsLength - 1);
int rspIndex = randomBetween(0, map->startPointsLength - 1); MapStartPointInfo* rsp = &(map->startPoints[rspIndex]);
MapStartPointInfo* rsp = &(map->startPoints[rspIndex]);
wmRndCenterTiles[0] = rsp->tile; wmRndCenterTiles[0] = rsp->tile;
wmRndCenterTiles[1] = wmRndCenterTiles[0]; wmRndCenterTiles[1] = wmRndCenterTiles[0];
wmRndCenterRotations[0] = rsp->rotation; wmRndCenterRotations[0] = rsp->rotation;
wmRndCenterRotations[1] = wmRndCenterRotations[0]; wmRndCenterRotations[1] = wmRndCenterRotations[0];
} else { } else {
wmRndCenterRotations[0] = 0; wmRndCenterRotations[0] = 0;
wmRndCenterRotations[1] = 0; wmRndCenterRotations[1] = 0;
wmRndCenterTiles[0] = gDude->tile; wmRndCenterTiles[0] = gDude->tile;
wmRndCenterTiles[1] = gDude->tile; wmRndCenterTiles[1] = gDude->tile;
}
wmRndTileDirs[0] = tileGetRotationTo(wmRndCenterTiles[0], gDude->tile);
wmRndTileDirs[1] = tileGetRotationTo(wmRndCenterTiles[1], gDude->tile);
wmRndOriginalCenterTile = wmRndCenterTiles[0];
return 0;
} }
wmRndTileDirs[0] = tileGetRotationTo(wmRndCenterTiles[0], gDude->tile);
wmRndTileDirs[1] = tileGetRotationTo(wmRndCenterTiles[1], gDude->tile);
wmRndOriginalCenterTile = wmRndCenterTiles[0];
return 0;
}
default: default:
debugPrint("\nERROR: wmSetupCritterObjs: invalid Formation Type!"); debugPrint("\nERROR: wmSetupCritterObjs: invalid Formation Type!");
@ -3958,6 +3962,8 @@ static int wmSetupRndNextTileNumInit(Encounter* encounter)
} }
} }
// Determines tile to place the next object in the EncounterEntry at.
//
// wmSetupRndNextTileNum // wmSetupRndNextTileNum
// 0x4C16F0 // 0x4C16F0
static int wmSetupRndNextTileNum(Encounter* encounter, EncounterEntry* encounterEntry, int* tilePtr) static int wmSetupRndNextTileNum(Encounter* encounter, EncounterEntry* encounterEntry, int* tilePtr)
@ -3965,41 +3971,40 @@ static int wmSetupRndNextTileNum(Encounter* encounter, EncounterEntry* encounter
int tile; int tile;
int attempt = 0; int attempt = 0;
while (1) { while (true) {
switch (encounter->position) { switch (encounter->position) {
case ENCOUNTER_FORMATION_TYPE_SURROUNDING: case ENCOUNTER_FORMATION_TYPE_SURROUNDING: {
if (1) { int distance;
int distance; if (encounterEntry->distance != 0) {
if (encounterEntry->distance != 0) { distance = encounterEntry->distance;
distance = encounterEntry->distance; } else {
} else { distance = randomBetween(-2, 2);
distance = randomBetween(-2, 2);
distance += critterGetStat(gDude, STAT_PERCEPTION); distance += critterGetStat(gDude, STAT_PERCEPTION);
if (perkHasRank(gDude, PERK_CAUTIOUS_NATURE)) { if (perkHasRank(gDude, PERK_CAUTIOUS_NATURE)) {
distance += 3; distance += 3;
}
} }
if (distance < 0) {
distance = 0;
}
int origin = encounterEntry->tile;
if (origin == -1) {
origin = tileGetTileInDirection(gDude->tile, wmRndTileDirs[0], distance);
}
if (++wmRndTileDirs[0] >= ROTATION_COUNT) {
wmRndTileDirs[0] = 0;
}
int randomizedDistance = randomBetween(0, distance / 2);
int randomizedRotation = randomBetween(0, ROTATION_COUNT - 1);
tile = tileGetTileInDirection(origin, (randomizedRotation + wmRndTileDirs[0]) % ROTATION_COUNT, randomizedDistance);
} }
if (distance < 0) {
distance = 0;
}
int origin = encounterEntry->tile;
if (origin == -1) {
origin = tileGetTileInDirection(gDude->tile, wmRndTileDirs[0], distance);
}
if (++wmRndTileDirs[0] >= ROTATION_COUNT) {
wmRndTileDirs[0] = 0;
}
int randomizedDistance = randomBetween(0, distance / 2);
int randomizedRotation = randomBetween(0, ROTATION_COUNT - 1);
tile = tileGetTileInDirection(origin, (randomizedRotation + wmRndTileDirs[0]) % ROTATION_COUNT, randomizedDistance);
break; break;
}
case ENCOUNTER_FORMATION_TYPE_STRAIGHT_LINE: case ENCOUNTER_FORMATION_TYPE_STRAIGHT_LINE:
tile = wmRndCenterTiles[wmRndIndex]; tile = wmRndCenterTiles[wmRndIndex];
if (wmRndCallCount != 0) { if (wmRndCallCount != 0) {