fallout2-ce/src/animation.cc

3359 lines
103 KiB
C++

#include "animation.h"
#include <stdio.h>
#include <string.h>
#include "art.h"
#include "color.h"
#include "combat.h"
#include "combat_ai.h"
#include "critter.h"
#include "debug.h"
#include "display_monitor.h"
#include "game.h"
#include "game_mouse.h"
#include "game_sound.h"
#include "geometry.h"
#include "input.h"
#include "interface.h"
#include "item.h"
#include "kb.h"
#include "map.h"
#include "mouse.h"
#include "object.h"
#include "party_member.h"
#include "perk.h"
#include "proto.h"
#include "proto_instance.h"
#include "random.h"
#include "scripts.h"
#include "settings.h"
#include "stat.h"
#include "svga.h"
#include "text_object.h"
#include "tile.h"
#include "trait.h"
#include "vcr.h"
namespace fallout {
#define ANIMATION_SEQUENCE_LIST_CAPACITY 32
#define ANIMATION_DESCRIPTION_LIST_CAPACITY 55
#define ANIMATION_SAD_LIST_CAPACITY 24
#define ANIMATION_SEQUENCE_FORCED 0x01
#define PATHFINDING_LIMIT 8192
typedef enum AnimationKind {
ANIM_KIND_MOVE_TO_OBJECT = 0,
ANIM_KIND_MOVE_TO_TILE = 1,
ANIM_KIND_MOVE_TO_TILE_STRAIGHT = 2,
ANIM_KIND_MOVE_TO_TILE_STRAIGHT_AND_WAIT_FOR_COMPLETE = 3,
ANIM_KIND_ANIMATE = 4,
ANIM_KIND_ANIMATE_REVERSED = 5,
ANIM_KIND_ANIMATE_AND_HIDE = 6,
ANIM_KIND_ROTATE_TO_TILE = 7,
ANIM_KIND_ROTATE_CLOCKWISE = 8,
ANIM_KIND_ROTATE_COUNTER_CLOCKWISE = 9,
ANIM_KIND_HIDE = 10,
ANIM_KIND_CALLBACK = 11,
ANIM_KIND_CALLBACK3 = 12,
ANIM_KIND_SET_FLAG = 14,
ANIM_KIND_UNSET_FLAG = 15,
ANIM_KIND_TOGGLE_FLAT = 16,
ANIM_KIND_SET_FID = 17,
ANIM_KIND_TAKE_OUT_WEAPON = 18,
ANIM_KIND_SET_LIGHT_DISTANCE = 19,
ANIM_KIND_MOVE_ON_STAIRS = 20,
ANIM_KIND_CHECK_FALLING = 23,
ANIM_KIND_TOGGLE_OUTLINE = 24,
ANIM_KIND_ANIMATE_FOREVER = 25,
ANIM_KIND_PING = 26,
ANIM_KIND_CONTINUE = 28,
// New animation to update both light distance and intensity. Required to
// impement Sfall's explosion light effects without resorting to hackery.
ANIM_KIND_SET_LIGHT_INTENSITY,
} AnimationKind;
typedef enum AnimationSequenceFlags {
// Specifies that the animation sequence has high priority, it cannot be
// cleared.
ANIM_SEQ_PRIORITIZED = 0x01,
// Specifies that the animation sequence started combat animation mode and
// therefore should balance it with appropriate finish call.
ANIM_SEQ_COMBAT_ANIM_STARTED = 0x02,
// Specifies that the animation sequence is reserved (TODO: explain what it
// actually means).
ANIM_SEQ_RESERVED = 0x04,
// Specifies that the animation sequence is in the process of adding actions
// to it (that is in the middle of begin/end calls).
ANIM_SEQ_ACCUMULATING = 0x08,
// TODO: Add description.
ANIM_SEQ_0x10 = 0x10,
// TODO: Add description.
ANIM_SEQ_0x20 = 0x20,
// Specifies that the animation sequence is negligible and will be end
// immediately when a new animation sequence is requested for the same
// object.
ANIM_SEQ_INSIGNIFICANT = 0x40,
// Specifies that the animation sequence should not return to ANIM_STAND
// when it's completed.
ANIM_SEQ_NO_STAND = 0x80,
} AnimationSequenceFlags;
typedef enum AnimationSadFlags {
// Specifies that the animation should play from end to start.
ANIM_SAD_REVERSE = 0x01,
// Specifies that the animation should use straight movement mode (as
// opposed to normal movement mode).
ANIM_SAD_STRAIGHT = 0x02,
// Specifies that no frame change should occur during animation.
ANIM_SAD_NO_ANIM = 0x04,
// Specifies that the animation should be played fully from start to finish.
//
// NOTE: This flag is only used together with straight movement mode to
// implement knockdown. Without this flag when the knockdown distance is
// short, say 1 or 2 tiles, knockdown animation might not be completed by
// the time critter reached it's destination. With this flag set animation
// will be played to it's final frame.
ANIM_SAD_WAIT_FOR_COMPLETION = 0x10,
// Unknown, set once, never read.
ANIM_SAD_0x20 = 0x20,
// Specifies that the owner of the animation should be hidden when animation
// is completed.
ANIM_SAD_HIDE_ON_END = 0x40,
// Specifies that the animation should never end.
ANIM_SAD_FOREVER = 0x80,
} AnimationSadFlags;
typedef struct AnimationDescription {
int kind;
union {
Object* owner;
// - ANIM_KIND_CALLBACK
// - ANIM_KIND_CALLBACK3
void* param2;
};
union {
// - ANIM_KIND_MOVE_TO_OBJECT
Object* destination;
// - ANIM_KIND_CALLBACK
void* param1;
};
union {
// - ANIM_KIND_MOVE_TO_TILE
// - ANIM_KIND_ANIMATE_AND_MOVE_TO_TILE_STRAIGHT
// - ANIM_KIND_MOVE_TO_TILE_STRAIGHT
struct {
int tile;
int elevation;
};
// ANIM_KIND_SET_FID
int fid;
// ANIM_KIND_TAKE_OUT_WEAPON
int weaponAnimationCode;
// ANIM_KIND_SET_LIGHT_DISTANCE
int lightDistance;
// ANIM_KIND_TOGGLE_OUTLINE
bool outline;
};
int anim;
int delay;
// ANIM_KIND_CALLBACK
AnimationCallback* callback;
// ANIM_KIND_CALLBACK3
AnimationCallback3* callback3;
union {
// - ANIM_KIND_SET_FLAG
// - ANIM_KIND_UNSET_FLAG
unsigned int objectFlag;
// - ANIM_KIND_HIDE
// - ANIM_KIND_CALLBACK
unsigned int extendedFlags;
};
union {
// - ANIM_KIND_MOVE_TO_TILE
// - ANIM_KIND_MOVE_TO_OBJECT
int actionPoints;
// ANIM_KIND_26
int animationSequenceIndex;
// ANIM_KIND_CALLBACK3
void* param3;
// ANIM_KIND_SET_LIGHT_INTENSITY
int lightIntensity;
};
CacheEntry* artCacheKey;
} AnimationDescription;
typedef struct AnimationSequence {
int field_0;
// Index of current animation in [animations] array or -1 if animations in
// this sequence is not playing.
int animationIndex;
// Number of scheduled animations in [animations] array.
int length;
unsigned int flags;
AnimationDescription animations[ANIMATION_DESCRIPTION_LIST_CAPACITY];
} AnimationSequence;
typedef struct PathNode {
int tile;
int from;
// actual type is likely char
int rotation;
int estimate;
int cost;
} PathNode;
// TODO: I don't know what `sad` means, but it's definitely better than
// `STRUCT_530014`. Find a better name.
typedef struct AnimationSad {
unsigned int flags;
Object* obj;
int fid; // fid
int anim;
// Timestamp (in game ticks) when animation last occurred.
unsigned int animationTimestamp;
// Number of ticks per frame (taking art's fps and overall animation speed
// settings into account).
unsigned int ticksPerFrame;
int animationSequenceIndex;
int field_1C; // length of field_28
int field_20; // current index in field_28
int field_24;
union {
unsigned char rotations[3200];
StraightPathNode straightPathNodeList[PATHFINDING_LIMIT];
};
} AnimationSad;
static int _anim_free_slot(int a1);
static int _anim_preload(Object* object, int fid, CacheEntry** cacheEntryPtr);
static void _anim_cleanup();
static int _check_registry(Object* obj);
static int animationRunSequence(int a1);
static int _anim_set_continue(int a1, int a2);
static int _anim_set_end(int a1);
static bool canUseDoor(Object* critter, Object* door);
static int _idist(int a1, int a2, int a3, int a4);
static int _tile_idistance(int tile1, int tile2);
static int animateMoveObjectToObject(Object* from, Object* to, int actionPoints, int anim, int animationSequenceIndex);
static int animateMoveObjectToTile(Object* obj, int tile, int elev, int actionPoints, int anim, int animationSequenceIndex);
static int _anim_move(Object* obj, int tile, int elev, int a3, int anim, int a5, int animationSequenceIndex);
static int animateMoveObjectToTileStraight(Object* obj, int tile, int elevation, int anim, int animationSequenceIndex, int flags);
static int _anim_move_on_stairs(Object* obj, int tile, int elevation, int anim, int animationSequenceIndex);
static int _check_for_falling(Object* obj, int anim, int a3);
static void _object_move(int index);
static void _object_straight_move(int index);
static int _anim_animate(Object* obj, int anim, int animationSequenceIndex, int flags);
static void _object_anim_compact();
static int actionRotate(Object* obj, int delta, int animationSequenceIndex);
static int _anim_hide(Object* object, int animationSequenceIndex);
static int _anim_change_fid(Object* obj, int animationSequenceIndex, int fid);
static int _check_gravity(int tile, int elevation);
static unsigned int animationComputeTicksPerFrame(Object* object, int fid);
static void reportOverloaded(Object* critter);
// 0x510718
static int gAnimationCurrentSad = 0;
// 0x51071C
static int gAnimationSequenceCurrentIndex = -1;
// 0x510720
static bool gAnimationInInit = false;
// 0x510724
static bool gAnimationInStop = false;
// 0x510728
static bool _anim_in_bk = false;
// 0x530014
static AnimationSad gAnimationSads[ANIMATION_SAD_LIST_CAPACITY];
// 0x542FD4
static PathNode gClosedPathNodeList[2000];
// 0x54CC14
static AnimationSequence gAnimationSequences[32];
// 0x561814
static unsigned char gPathfinderProcessedTiles[5000];
// 0x562B9C
static PathNode gOpenPathNodeList[2000];
// 0x56C7DC
static int gAnimationDescriptionCurrentIndex;
// anim_init
// 0x413A20
void animationInit()
{
gAnimationInInit = true;
animationReset();
gAnimationInInit = false;
}
// 0x413A40
void animationReset()
{
if (!gAnimationInInit) {
// NOTE: Uninline.
animationStop();
}
gAnimationCurrentSad = 0;
gAnimationSequenceCurrentIndex = -1;
for (int index = 0; index < ANIMATION_SEQUENCE_LIST_CAPACITY; index++) {
gAnimationSequences[index].field_0 = -1000;
gAnimationSequences[index].flags = 0;
}
}
// 0x413AB8
void animationExit()
{
// NOTE: Uninline.
animationStop();
}
// 0x413AF4
int reg_anim_begin(int requestOptions)
{
if (gAnimationSequenceCurrentIndex != -1) {
return -1;
}
if (gAnimationInStop) {
return -1;
}
int v1 = _anim_free_slot(requestOptions);
if (v1 == -1) {
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[v1]);
animationSequence->flags |= ANIM_SEQ_ACCUMULATING;
if ((requestOptions & ANIMATION_REQUEST_RESERVED) != 0) {
animationSequence->flags |= ANIM_SEQ_RESERVED;
}
if ((requestOptions & ANIMATION_REQUEST_INSIGNIFICANT) != 0) {
animationSequence->flags |= ANIM_SEQ_INSIGNIFICANT;
}
if ((requestOptions & ANIMATION_REQUEST_NO_STAND) != 0) {
animationSequence->flags |= ANIM_SEQ_NO_STAND;
}
gAnimationSequenceCurrentIndex = v1;
gAnimationDescriptionCurrentIndex = 0;
return 0;
}
// 0x413B80
static int _anim_free_slot(int requestOptions)
{
int v1 = -1;
int v2 = 0;
for (int index = 0; index < ANIMATION_SEQUENCE_LIST_CAPACITY; index++) {
AnimationSequence* animationSequence = &(gAnimationSequences[index]);
if (animationSequence->field_0 != -1000 || (animationSequence->flags & ANIM_SEQ_ACCUMULATING) != 0 || (animationSequence->flags & ANIM_SEQ_0x20) != 0) {
if (!(animationSequence->flags & ANIM_SEQ_RESERVED)) {
v2++;
}
} else if (v1 == -1 && ((requestOptions & ANIMATION_REQUEST_PING) == 0 || (animationSequence->flags & ANIM_SEQ_0x10) == 0)) {
v1 = index;
}
}
if (v1 == -1) {
if ((requestOptions & ANIMATION_REQUEST_RESERVED) != 0) {
debugPrint("Unable to begin reserved animation!\n");
}
return -1;
} else if ((requestOptions & ANIMATION_REQUEST_RESERVED) != 0 || v2 < 20) {
return v1;
}
return -1;
}
// 0x413C20
int _register_priority(int a1)
{
if (gAnimationSequenceCurrentIndex == -1) {
return -1;
}
if (a1 == 0) {
return -1;
}
gAnimationSequences[gAnimationSequenceCurrentIndex].flags |= ANIM_SEQ_PRIORITIZED;
return 0;
}
// 0x413C4C
int reg_anim_clear(Object* a1)
{
for (int animationSequenceIndex = 0; animationSequenceIndex < ANIMATION_SEQUENCE_LIST_CAPACITY; animationSequenceIndex++) {
AnimationSequence* animationSequence = &(gAnimationSequences[animationSequenceIndex]);
if (animationSequence->field_0 == -1000) {
continue;
}
int animationDescriptionIndex;
for (animationDescriptionIndex = 0; animationDescriptionIndex < animationSequence->length; animationDescriptionIndex++) {
AnimationDescription* animationDescription = &(animationSequence->animations[animationDescriptionIndex]);
if (a1 != animationDescription->owner || animationDescription->kind == 11) {
continue;
}
break;
}
if (animationDescriptionIndex == animationSequence->length) {
continue;
}
if ((animationSequence->flags & ANIM_SEQ_PRIORITIZED) != 0) {
return -2;
}
_anim_set_end(animationSequenceIndex);
return 0;
}
return -1;
}
// 0x413CCC
int reg_anim_end()
{
if (gAnimationSequenceCurrentIndex == -1) {
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
animationSequence->field_0 = 0;
animationSequence->length = gAnimationDescriptionCurrentIndex;
animationSequence->animationIndex = -1;
animationSequence->flags &= ~ANIM_SEQ_ACCUMULATING;
animationSequence->animations[0].delay = 0;
if (isInCombat()) {
_combat_anim_begin();
animationSequence->flags |= ANIM_SEQ_COMBAT_ANIM_STARTED;
}
int index = gAnimationSequenceCurrentIndex;
gAnimationSequenceCurrentIndex = -1;
if (!(animationSequence->flags & ANIM_SEQ_0x10)) {
_anim_set_continue(index, 1);
}
return 0;
}
// NOTE: Inlined.
//
// 0x413D6C
static int _anim_preload(Object* object, int fid, CacheEntry** cacheEntryPtr)
{
*cacheEntryPtr = nullptr;
if (artLock(fid, cacheEntryPtr) != nullptr) {
artUnlock(*cacheEntryPtr);
*cacheEntryPtr = nullptr;
return 0;
}
return -1;
}
// 0x413D98
static void _anim_cleanup()
{
if (gAnimationSequenceCurrentIndex == -1) {
return;
}
for (int index = 0; index < ANIMATION_SEQUENCE_LIST_CAPACITY; index++) {
gAnimationSequences[index].flags &= ~(ANIM_SEQ_ACCUMULATING | ANIM_SEQ_0x10);
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
for (int index = 0; index < gAnimationDescriptionCurrentIndex; index++) {
AnimationDescription* animationDescription = &(animationSequence->animations[index]);
if (animationDescription->artCacheKey != nullptr) {
artUnlock(animationDescription->artCacheKey);
}
if (animationDescription->kind == ANIM_KIND_CALLBACK && animationDescription->callback == (AnimationCallback*)_gsnd_anim_sound) {
soundEffectDelete((Sound*)animationDescription->param1);
}
}
gAnimationSequenceCurrentIndex = -1;
}
// 0x413E2C
static int _check_registry(Object* obj)
{
if (gAnimationSequenceCurrentIndex == -1) {
return -1;
}
if (gAnimationDescriptionCurrentIndex >= ANIMATION_DESCRIPTION_LIST_CAPACITY) {
return -1;
}
if (obj == nullptr) {
return 0;
}
for (int animationSequenceIndex = 0; animationSequenceIndex < ANIMATION_SEQUENCE_LIST_CAPACITY; animationSequenceIndex++) {
AnimationSequence* animationSequence = &(gAnimationSequences[animationSequenceIndex]);
if (animationSequenceIndex != gAnimationSequenceCurrentIndex && animationSequence->field_0 != -1000) {
for (int animationDescriptionIndex = 0; animationDescriptionIndex < animationSequence->length; animationDescriptionIndex++) {
AnimationDescription* animationDescription = &(animationSequence->animations[animationDescriptionIndex]);
if (obj == animationDescription->owner && animationDescription->kind != 11) {
if ((animationSequence->flags & ANIM_SEQ_INSIGNIFICANT) == 0) {
return -1;
}
_anim_set_end(animationSequenceIndex);
}
}
}
}
return 0;
}
// Returns -1 if object is playing some animation.
//
// 0x413EC8
int animationIsBusy(Object* a1)
{
if (gAnimationDescriptionCurrentIndex >= ANIMATION_DESCRIPTION_LIST_CAPACITY || a1 == nullptr) {
return 0;
}
for (int animationSequenceIndex = 0; animationSequenceIndex < ANIMATION_SEQUENCE_LIST_CAPACITY; animationSequenceIndex++) {
AnimationSequence* animationSequence = &(gAnimationSequences[animationSequenceIndex]);
if (animationSequenceIndex != gAnimationSequenceCurrentIndex && animationSequence->field_0 != -1000) {
for (int animationDescriptionIndex = 0; animationDescriptionIndex < animationSequence->length; animationDescriptionIndex++) {
AnimationDescription* animationDescription = &(animationSequence->animations[animationDescriptionIndex]);
if (a1 != animationDescription->owner) {
continue;
}
if (animationDescription->kind == ANIM_KIND_CALLBACK) {
continue;
}
if (animationSequence->length == 1 && animationDescription->anim == ANIM_STAND) {
continue;
}
return -1;
}
}
}
return 0;
}
// 0x413F5C
int animationRegisterMoveToObject(Object* owner, Object* destination, int actionPoints, int delay)
{
if (_check_registry(owner) == -1 || actionPoints == 0) {
_anim_cleanup();
return -1;
}
if (owner->tile == destination->tile && owner->elevation == destination->elevation) {
return 0;
}
AnimationDescription* animationDescription = &(gAnimationSequences[gAnimationSequenceCurrentIndex].animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_MOVE_TO_OBJECT;
animationDescription->anim = ANIM_WALK;
animationDescription->owner = owner;
animationDescription->destination = destination;
animationDescription->actionPoints = actionPoints;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return animationRegisterRotateToTile(owner, destination->tile);
}
// 0x41405C
int animationRegisterRunToObject(Object* owner, Object* destination, int actionPoints, int delay)
{
if (_check_registry(owner) == -1 || actionPoints == 0) {
_anim_cleanup();
return -1;
}
if (owner->tile == destination->tile && owner->elevation == destination->elevation) {
return 0;
}
if (critterIsEncumbered(owner)) {
if (objectIsPartyMember(owner)) {
reportOverloaded(owner);
}
return animationRegisterMoveToObject(owner, destination, actionPoints, delay);
}
AnimationDescription* animationDescription = &(gAnimationSequences[gAnimationSequenceCurrentIndex].animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_MOVE_TO_OBJECT;
animationDescription->owner = owner;
animationDescription->destination = destination;
if ((FID_TYPE(owner->fid) == OBJ_TYPE_CRITTER && (owner->data.critter.combat.results & DAM_CRIP_LEG_ANY) != 0)
|| (owner == gDude && dudeHasState(DUDE_STATE_SNEAKING) && !perkGetRank(gDude, PERK_SILENT_RUNNING))
|| !artExists(buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, ANIM_RUNNING, 0, owner->rotation + 1))) {
animationDescription->anim = ANIM_WALK;
} else {
animationDescription->anim = ANIM_RUNNING;
}
animationDescription->actionPoints = actionPoints;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return animationRegisterRotateToTile(owner, destination->tile);
}
// 0x414294
int animationRegisterMoveToTile(Object* owner, int tile, int elevation, int actionPoints, int delay)
{
if (_check_registry(owner) == -1 || actionPoints == 0) {
_anim_cleanup();
return -1;
}
if (tile == owner->tile && elevation == owner->elevation) {
return 0;
}
AnimationDescription* animationDescription = &(gAnimationSequences[gAnimationSequenceCurrentIndex].animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_MOVE_TO_TILE;
animationDescription->anim = ANIM_WALK;
animationDescription->owner = owner;
animationDescription->tile = tile;
animationDescription->elevation = elevation;
animationDescription->actionPoints = actionPoints;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414394
int animationRegisterRunToTile(Object* owner, int tile, int elevation, int actionPoints, int delay)
{
if (_check_registry(owner) == -1 || actionPoints == 0) {
_anim_cleanup();
return -1;
}
if (tile == owner->tile && elevation == owner->elevation) {
return 0;
}
if (critterIsEncumbered(owner)) {
if (objectIsPartyMember(owner)) {
reportOverloaded(owner);
}
return animationRegisterMoveToTile(owner, tile, elevation, actionPoints, delay);
}
AnimationDescription* animationDescription = &(gAnimationSequences[gAnimationSequenceCurrentIndex].animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_MOVE_TO_TILE;
animationDescription->owner = owner;
animationDescription->tile = tile;
animationDescription->elevation = elevation;
if ((FID_TYPE(owner->fid) == OBJ_TYPE_CRITTER && (owner->data.critter.combat.results & DAM_CRIP_LEG_ANY) != 0)
|| (owner == gDude && dudeHasState(DUDE_STATE_SNEAKING) && !perkGetRank(gDude, PERK_SILENT_RUNNING))
|| !artExists(buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, ANIM_RUNNING, 0, owner->rotation + 1))) {
animationDescription->anim = ANIM_WALK;
} else {
animationDescription->anim = ANIM_RUNNING;
}
animationDescription->actionPoints = actionPoints;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x4145D0
int animationRegisterMoveToTileStraight(Object* object, int tile, int elevation, int anim, int delay)
{
if (_check_registry(object) == -1) {
_anim_cleanup();
return -1;
}
if (tile == object->tile && elevation == object->elevation) {
return 0;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_MOVE_TO_TILE_STRAIGHT;
animationDescription->owner = object;
animationDescription->tile = tile;
animationDescription->elevation = elevation;
animationDescription->anim = anim;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(object->fid), object->fid & 0xFFF, animationDescription->anim, (object->fid & 0xF000) >> 12, object->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(object, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x4146C4
int animationRegisterMoveToTileStraightAndWaitForComplete(Object* owner, int tile, int elevation, int anim, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
if (tile == owner->tile && elevation == owner->elevation) {
return 0;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_MOVE_TO_TILE_STRAIGHT_AND_WAIT_FOR_COMPLETE;
animationDescription->owner = owner;
animationDescription->tile = tile;
animationDescription->elevation = elevation;
animationDescription->anim = anim;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x4149D0
int animationRegisterAnimate(Object* owner, int anim, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_ANIMATE;
animationDescription->owner = owner;
animationDescription->anim = anim;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414AA8
int animationRegisterAnimateReversed(Object* owner, int anim, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_ANIMATE_REVERSED;
animationDescription->owner = owner;
animationDescription->anim = anim;
animationDescription->delay = delay;
animationDescription->artCacheKey = nullptr;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414B7C
int animationRegisterAnimateAndHide(Object* owner, int anim, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_ANIMATE_AND_HIDE;
animationDescription->owner = owner;
animationDescription->anim = anim;
animationDescription->delay = delay;
animationDescription->artCacheKey = nullptr;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414C50
int animationRegisterRotateToTile(Object* owner, int tile)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_ROTATE_TO_TILE;
animationDescription->delay = -1;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = owner;
animationDescription->tile = tile;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414CC8
int animationRegisterRotateClockwise(Object* owner)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_ROTATE_CLOCKWISE;
animationDescription->delay = -1;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = owner;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414D38
int animationRegisterRotateCounterClockwise(Object* owner)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_ROTATE_COUNTER_CLOCKWISE;
animationDescription->delay = -1;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = owner;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// NOTE: Unused.
//
// 0x414DA8
int animationRegisterHideObject(Object* object)
{
if (_check_registry(object) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_HIDE;
animationDescription->delay = -1;
animationDescription->artCacheKey = nullptr;
animationDescription->extendedFlags = 0;
animationDescription->owner = object;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414E20
int animationRegisterHideObjectForced(Object* object)
{
if (_check_registry(object) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_HIDE;
animationDescription->delay = -1;
animationDescription->artCacheKey = nullptr;
animationDescription->extendedFlags = ANIMATION_SEQUENCE_FORCED;
animationDescription->owner = object;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414E98
int animationRegisterCallback(void* a1, void* a2, AnimationCallback* proc, int delay)
{
if (_check_registry(nullptr) == -1 || proc == nullptr) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_CALLBACK;
animationDescription->extendedFlags = 0;
animationDescription->artCacheKey = nullptr;
animationDescription->param2 = a2;
animationDescription->param1 = a1;
animationDescription->callback = proc;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// Same as `animationRegisterCallback` but accepting 3 parameters.
//
// 0x414F20
int animationRegisterCallback3(void* a1, void* a2, void* a3, AnimationCallback3* proc, int delay)
{
if (_check_registry(nullptr) == -1 || proc == nullptr) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_CALLBACK3;
animationDescription->extendedFlags = 0;
animationDescription->artCacheKey = nullptr;
animationDescription->param2 = a2;
animationDescription->param1 = a1;
animationDescription->callback3 = proc;
animationDescription->param3 = a3;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x414FAC
int animationRegisterCallbackForced(void* a1, void* a2, AnimationCallback* proc, int delay)
{
if (_check_registry(nullptr) == -1 || proc == nullptr) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_CALLBACK;
animationDescription->extendedFlags = ANIMATION_SEQUENCE_FORCED;
animationDescription->artCacheKey = nullptr;
animationDescription->param2 = a2;
animationDescription->param1 = a1;
animationDescription->callback = proc;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// NOTE: Unused.
//
// The [flag] parameter should be one of OBJECT_* flags. The way it's handled
// down the road implies it should not be a group of flags (joined with bitwise
// OR), but a one particular flag.
//
// 0x415034
int animationRegisterSetFlag(Object* object, int flag, int delay)
{
if (_check_registry(object) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_SET_FLAG;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = object;
animationDescription->objectFlag = flag;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// The [flag] parameter should be one of OBJECT_* flags. The way it's handled
// down the road implies it should not be a group of flags (joined with bitwise
// OR), but a one particular flag.
//
// 0x4150A8
int animationRegisterUnsetFlag(Object* object, int flag, int delay)
{
if (_check_registry(object) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_UNSET_FLAG;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = object;
animationDescription->objectFlag = flag;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x41518C
int animationRegisterSetFid(Object* owner, int fid, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_SET_FID;
animationDescription->owner = owner;
animationDescription->fid = fid;
animationDescription->delay = delay;
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x415238
int animationRegisterTakeOutWeapon(Object* owner, int weaponAnimationCode, int delay)
{
const char* sfx = sfxBuildCharName(owner, ANIM_TAKE_OUT, weaponAnimationCode);
if (animationRegisterPlaySoundEffect(owner, sfx, delay) == -1) {
return -1;
}
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_TAKE_OUT_WEAPON;
animationDescription->anim = ANIM_TAKE_OUT;
animationDescription->delay = 0;
animationDescription->owner = owner;
animationDescription->weaponAnimationCode = weaponAnimationCode;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, ANIM_TAKE_OUT, weaponAnimationCode, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x415334
int animationRegisterSetLightDistance(Object* owner, int lightDistance, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_SET_LIGHT_DISTANCE;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = owner;
animationDescription->lightDistance = lightDistance;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// NOTE: Unused.
//
// 0x4153A8
int animationRegisterToggleOutline(Object* object, bool outline, int delay)
{
if (_check_registry(object) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_TOGGLE_OUTLINE;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = object;
animationDescription->outline = outline;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x41541C
int animationRegisterPlaySoundEffect(Object* owner, const char* soundEffectName, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_CALLBACK;
animationDescription->owner = owner;
if (soundEffectName != nullptr) {
int volume = _gsound_compute_relative_volume(owner);
animationDescription->param1 = soundEffectLoadWithVolume(soundEffectName, owner, volume);
if (animationDescription->param1 != nullptr) {
animationDescription->callback = (AnimationCallback*)_gsnd_anim_sound;
} else {
animationDescription->kind = ANIM_KIND_CONTINUE;
}
} else {
animationDescription->kind = ANIM_KIND_CONTINUE;
}
animationDescription->artCacheKey = nullptr;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x4154C4
int animationRegisterAnimateForever(Object* owner, int anim, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_ANIMATE_FOREVER;
animationDescription->owner = owner;
animationDescription->anim = anim;
animationDescription->delay = delay;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x415598
int animationRegisterPing(int flags, int delay)
{
if (_check_registry(nullptr) == -1) {
_anim_cleanup();
return -1;
}
int animationSequenceIndex = _anim_free_slot(flags | ANIMATION_REQUEST_PING);
if (animationSequenceIndex == -1) {
return -1;
}
gAnimationSequences[animationSequenceIndex].flags = ANIM_SEQ_0x10;
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->owner = nullptr;
animationDescription->kind = ANIM_KIND_PING;
animationDescription->artCacheKey = nullptr;
animationDescription->animationSequenceIndex = animationSequenceIndex;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
// 0x4156A8
static int animationRunSequence(int animationSequenceIndex)
{
if (animationSequenceIndex == -1) {
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[animationSequenceIndex]);
if (animationSequence->field_0 == -1000) {
return -1;
}
while (1) {
if (animationSequence->field_0 >= animationSequence->length) {
return 0;
}
if (animationSequence->field_0 > animationSequence->animationIndex) {
AnimationDescription* animationDescription = &(animationSequence->animations[animationSequence->field_0]);
if (animationDescription->delay < 0) {
return 0;
}
if (animationDescription->delay > 0) {
animationDescription->delay--;
return 0;
}
}
AnimationDescription* animationDescription = &(animationSequence->animations[animationSequence->field_0++]);
int rc;
Rect rect;
switch (animationDescription->kind) {
case ANIM_KIND_MOVE_TO_OBJECT:
rc = animateMoveObjectToObject(animationDescription->owner, animationDescription->destination, animationDescription->actionPoints, animationDescription->anim, animationSequenceIndex);
break;
case ANIM_KIND_MOVE_TO_TILE:
rc = animateMoveObjectToTile(animationDescription->owner, animationDescription->tile, animationDescription->elevation, animationDescription->actionPoints, animationDescription->anim, animationSequenceIndex);
break;
case ANIM_KIND_MOVE_TO_TILE_STRAIGHT:
rc = animateMoveObjectToTileStraight(animationDescription->owner, animationDescription->tile, animationDescription->elevation, animationDescription->anim, animationSequenceIndex, 0);
break;
case ANIM_KIND_MOVE_TO_TILE_STRAIGHT_AND_WAIT_FOR_COMPLETE:
rc = animateMoveObjectToTileStraight(animationDescription->owner, animationDescription->tile, animationDescription->elevation, animationDescription->anim, animationSequenceIndex, ANIM_SAD_WAIT_FOR_COMPLETION);
break;
case ANIM_KIND_ANIMATE:
rc = _anim_animate(animationDescription->owner, animationDescription->anim, animationSequenceIndex, 0);
break;
case ANIM_KIND_ANIMATE_REVERSED:
rc = _anim_animate(animationDescription->owner, animationDescription->anim, animationSequenceIndex, ANIM_SAD_REVERSE);
break;
case ANIM_KIND_ANIMATE_AND_HIDE:
rc = _anim_animate(animationDescription->owner, animationDescription->anim, animationSequenceIndex, ANIM_SAD_HIDE_ON_END);
if (rc == -1) {
// NOTE: Uninline.
rc = _anim_hide(animationDescription->owner, animationSequenceIndex);
}
break;
case ANIM_KIND_ANIMATE_FOREVER:
rc = _anim_animate(animationDescription->owner, animationDescription->anim, animationSequenceIndex, ANIM_SAD_FOREVER);
break;
case ANIM_KIND_ROTATE_TO_TILE:
if (!_critter_is_prone(animationDescription->owner)) {
int rotation = tileGetRotationTo(animationDescription->owner->tile, animationDescription->tile);
_dude_stand(animationDescription->owner, rotation, -1);
}
_anim_set_continue(animationSequenceIndex, 0);
rc = 0;
break;
case ANIM_KIND_ROTATE_CLOCKWISE:
rc = actionRotate(animationDescription->owner, 1, animationSequenceIndex);
break;
case ANIM_KIND_ROTATE_COUNTER_CLOCKWISE:
rc = actionRotate(animationDescription->owner, -1, animationSequenceIndex);
break;
case ANIM_KIND_HIDE:
// NOTE: Uninline.
rc = _anim_hide(animationDescription->owner, animationSequenceIndex);
break;
case ANIM_KIND_CALLBACK:
rc = animationDescription->callback(animationDescription->param1, animationDescription->param2);
if (rc == 0) {
rc = _anim_set_continue(animationSequenceIndex, 0);
}
break;
case ANIM_KIND_CALLBACK3:
rc = animationDescription->callback3(animationDescription->param1, animationDescription->param2, animationDescription->param3);
if (rc == 0) {
rc = _anim_set_continue(animationSequenceIndex, 0);
}
break;
case ANIM_KIND_SET_FLAG:
if (animationDescription->objectFlag == OBJECT_LIGHTING) {
if (_obj_turn_on_light(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
} else if (animationDescription->objectFlag == OBJECT_HIDDEN) {
if (objectHide(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
} else {
animationDescription->owner->flags |= animationDescription->objectFlag;
}
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_UNSET_FLAG:
if (animationDescription->objectFlag == OBJECT_LIGHTING) {
if (_obj_turn_off_light(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
} else if (animationDescription->objectFlag == OBJECT_HIDDEN) {
if (objectShow(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
} else {
animationDescription->owner->flags &= ~animationDescription->objectFlag;
}
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_TOGGLE_FLAT:
if (_obj_toggle_flat(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_SET_FID:
rc = _anim_change_fid(animationDescription->owner, animationSequenceIndex, animationDescription->fid);
break;
case ANIM_KIND_TAKE_OUT_WEAPON:
rc = _anim_animate(animationDescription->owner, ANIM_TAKE_OUT, animationSequenceIndex, animationDescription->tile);
break;
case ANIM_KIND_SET_LIGHT_DISTANCE:
objectSetLight(animationDescription->owner, animationDescription->lightDistance, animationDescription->owner->lightIntensity, &rect);
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_SET_LIGHT_INTENSITY:
objectSetLight(animationDescription->owner, animationDescription->lightDistance, animationDescription->lightIntensity, &rect);
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_MOVE_ON_STAIRS:
rc = _anim_move_on_stairs(animationDescription->owner, animationDescription->tile, animationDescription->elevation, animationDescription->anim, animationSequenceIndex);
break;
case ANIM_KIND_CHECK_FALLING:
rc = _check_for_falling(animationDescription->owner, animationDescription->anim, animationSequenceIndex);
break;
case ANIM_KIND_TOGGLE_OUTLINE:
if (animationDescription->outline) {
if (objectEnableOutline(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
} else {
if (objectDisableOutline(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
}
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_PING:
gAnimationSequences[animationDescription->animationSequenceIndex].flags &= ~ANIM_SEQ_0x10;
rc = _anim_set_continue(animationDescription->animationSequenceIndex, 1);
if (rc != -1) {
rc = _anim_set_continue(animationSequenceIndex, 0);
}
break;
case ANIM_KIND_CONTINUE:
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
default:
rc = -1;
break;
}
if (rc == -1) {
_anim_set_end(animationSequenceIndex);
}
if (animationSequence->field_0 == -1000) {
return -1;
}
}
}
// 0x415B44
static int _anim_set_continue(int animationSequenceIndex, int a2)
{
if (animationSequenceIndex == -1) {
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[animationSequenceIndex]);
if (animationSequence->field_0 == -1000) {
return -1;
}
animationSequence->animationIndex++;
if (animationSequence->animationIndex == animationSequence->length) {
return _anim_set_end(animationSequenceIndex);
} else {
if (a2) {
return animationRunSequence(animationSequenceIndex);
}
}
return 0;
}
// 0x415B9C
static int _anim_set_end(int animationSequenceIndex)
{
AnimationSequence* animationSequence;
AnimationDescription* animationDescription;
int i;
if (animationSequenceIndex == -1) {
return -1;
}
animationSequence = &(gAnimationSequences[animationSequenceIndex]);
if (animationSequence->field_0 == -1000) {
return -1;
}
for (i = 0; i < gAnimationCurrentSad; i++) {
AnimationSad* sad = &(gAnimationSads[i]);
if (sad->animationSequenceIndex == animationSequenceIndex) {
sad->field_20 = -1000;
}
}
for (i = 0; i < animationSequence->length; i++) {
animationDescription = &(animationSequence->animations[i]);
if (animationDescription->kind == ANIM_KIND_HIDE && ((i < animationSequence->animationIndex) || (animationDescription->extendedFlags & ANIMATION_SEQUENCE_FORCED))) {
Rect rect;
int elevation = animationDescription->owner->elevation;
objectDestroy(animationDescription->owner, &rect);
tileWindowRefreshRect(&rect, elevation);
}
}
for (i = 0; i < animationSequence->length; i++) {
animationDescription = &(animationSequence->animations[i]);
if (animationDescription->artCacheKey) {
artUnlock(animationDescription->artCacheKey);
}
if (animationDescription->kind != 11 && animationDescription->kind != 12) {
// TODO: Check.
if (animationDescription->kind != ANIM_KIND_PING) {
Object* owner = animationDescription->owner;
if (FID_TYPE(owner->fid) == OBJ_TYPE_CRITTER) {
int j = 0;
for (; j < i; j++) {
AnimationDescription* ad = &(animationSequence->animations[j]);
if (owner == ad->owner) {
if (ad->kind != ANIM_KIND_CALLBACK && ad->kind != ANIM_KIND_CALLBACK3) {
break;
}
}
}
if (i == j) {
int k = 0;
for (; k < animationSequence->animationIndex; k++) {
AnimationDescription* ad = &(animationSequence->animations[k]);
if (ad->kind == ANIM_KIND_HIDE && ad->owner == owner) {
break;
}
}
if (k == animationSequence->animationIndex) {
for (int m = 0; m < gAnimationCurrentSad; m++) {
if (gAnimationSads[m].obj == owner) {
gAnimationSads[m].field_20 = -1000;
break;
}
}
if ((animationSequence->flags & ANIM_SEQ_NO_STAND) == 0 && !_critter_is_prone(owner)) {
_dude_stand(owner, owner->rotation, -1);
}
}
}
}
}
} else if (i >= animationSequence->field_0) {
if (animationDescription->extendedFlags & ANIMATION_SEQUENCE_FORCED) {
animationDescription->callback(animationDescription->param1, animationDescription->param2);
} else {
if (animationDescription->kind == ANIM_KIND_CALLBACK && animationDescription->callback == (AnimationCallback*)_gsnd_anim_sound) {
soundEffectDelete((Sound*)animationDescription->param1);
}
}
}
}
animationSequence->animationIndex = -1;
animationSequence->field_0 = -1000;
if ((animationSequence->flags & ANIM_SEQ_COMBAT_ANIM_STARTED) != 0) {
_combat_anim_finished();
}
if (_anim_in_bk) {
animationSequence->flags = ANIM_SEQ_0x20;
} else {
animationSequence->flags = 0;
}
return 0;
}
// 0x415E24
static bool canUseDoor(Object* critter, Object* door)
{
if (critter == gDude) {
if (!_obj_portal_is_walk_thru(door)) {
return false;
}
}
if (FID_TYPE(critter->fid) != OBJ_TYPE_CRITTER) {
return false;
}
if (FID_TYPE(door->fid) != OBJ_TYPE_SCENERY) {
return false;
}
int bodyType = critterGetBodyType(critter);
if (bodyType != BODY_TYPE_BIPED && bodyType != BODY_TYPE_ROBOTIC) {
return false;
}
Proto* proto;
if (protoGetProto(door->pid, &proto) == -1) {
return false;
}
if (proto->scenery.type != SCENERY_TYPE_DOOR) {
return false;
}
if (objectIsLocked(door)) {
return false;
}
if (critterGetKillType(critter) == KILL_TYPE_GECKO) {
return false;
}
return true;
}
// 0x415EE8
int _make_path(Object* object, int from, int to, unsigned char* rotations, int a5)
{
return pathfinderFindPath(object, from, to, rotations, a5, _obj_blocking_at);
}
// TODO: move pathfinding into another unit
// 0x415EFC
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback)
{
if (a5) {
if (callback(object, to, object->elevation) != nullptr) {
return 0;
}
}
bool isCritter = false;
int critterType = 0;
if (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) {
isCritter = true;
critterType = critterGetKillType(object);
}
bool isNotInCombat = !isInCombat();
memset(gPathfinderProcessedTiles, 0, sizeof(gPathfinderProcessedTiles));
gPathfinderProcessedTiles[from / 8] |= 1 << (from & 7);
gOpenPathNodeList[0].tile = from;
gOpenPathNodeList[0].from = -1;
gOpenPathNodeList[0].rotation = 0;
gOpenPathNodeList[0].estimate = _tile_idistance(from, to);
gOpenPathNodeList[0].cost = 0;
for (int index = 1; index < 2000; index += 1) {
gOpenPathNodeList[index].tile = -1;
}
int toScreenX;
int toScreenY;
tileToScreenXY(to, &toScreenX, &toScreenY, object->elevation);
int closedPathNodeListLength = 0;
int openPathNodeListLength = 1;
PathNode temp;
while (1) {
int v63 = -1;
PathNode* prev = nullptr;
int v12 = 0;
for (int index = 0; v12 < openPathNodeListLength; index += 1) {
PathNode* curr = &(gOpenPathNodeList[index]);
if (curr->tile != -1) {
v12++;
if (v63 == -1 || (curr->estimate + curr->cost) < (prev->estimate + prev->cost)) {
prev = curr;
v63 = index;
}
}
}
PathNode* curr = &(gOpenPathNodeList[v63]);
memcpy(&temp, curr, sizeof(temp));
openPathNodeListLength -= 1;
curr->tile = -1;
if (temp.tile == to) {
if (openPathNodeListLength == 0) {
openPathNodeListLength = 1;
}
break;
}
PathNode* curr1 = &(gClosedPathNodeList[closedPathNodeListLength]);
memcpy(curr1, &temp, sizeof(temp));
closedPathNodeListLength += 1;
if (closedPathNodeListLength == 2000) {
return 0;
}
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
int tile = tileGetTileInDirection(temp.tile, rotation, 1);
int bit = 1 << (tile & 7);
if ((gPathfinderProcessedTiles[tile / 8] & bit) != 0) {
continue;
}
if (tile != to) {
Object* v24 = callback(object, tile, object->elevation);
if (v24 != nullptr) {
if (!canUseDoor(object, v24)) {
continue;
}
}
}
int v25 = 0;
for (; v25 < 2000; v25++) {
if (gOpenPathNodeList[v25].tile == -1) {
break;
}
}
openPathNodeListLength += 1;
if (openPathNodeListLength == 2000) {
return 0;
}
gPathfinderProcessedTiles[tile / 8] |= bit;
PathNode* v27 = &(gOpenPathNodeList[v25]);
v27->tile = tile;
v27->from = temp.tile;
v27->rotation = rotation;
int newX;
int newY;
tileToScreenXY(tile, &newX, &newY, object->elevation);
v27->estimate = _idist(newX, newY, toScreenX, toScreenY);
v27->cost = temp.cost + 50;
if (isNotInCombat && temp.rotation != rotation) {
v27->cost += 10;
}
if (isCritter) {
Object* o = objectFindFirstAtLocation(object->elevation, v27->tile);
while (o != nullptr) {
if (o->pid >= FIRST_RADIOACTIVE_GOO_PID && o->pid <= LAST_RADIOACTIVE_GOO_PID) {
break;
}
o = objectFindNextAtLocation();
}
if (o != nullptr) {
if (critterType == KILL_TYPE_GECKO) {
v27->cost += 100;
} else {
v27->cost += 400;
}
}
}
}
if (openPathNodeListLength == 0) {
break;
}
}
if (openPathNodeListLength != 0) {
unsigned char* v39 = rotations;
int index = 0;
for (; index < 800; index++) {
if (temp.tile == from) {
break;
}
if (v39 != nullptr) {
*v39 = temp.rotation & 0xFF;
v39 += 1;
}
int j = 0;
while (gClosedPathNodeList[j].tile != temp.from) {
j++;
}
PathNode* v36 = &(gClosedPathNodeList[j]);
memcpy(&temp, v36, sizeof(temp));
}
if (rotations != nullptr) {
// Looks like array resevering, probably because A* finishes it's path from end to start,
// this probably reverses it start-to-end.
unsigned char* beginning = rotations;
unsigned char* ending = rotations + index - 1;
int middle = index / 2;
for (int index = 0; index < middle; index++) {
unsigned char rotation = *ending;
*ending = *beginning;
*beginning = rotation;
ending -= 1;
beginning += 1;
}
}
return index;
}
return 0;
}
// 0x41633C
static int _idist(int x1, int y1, int x2, int y2)
{
int dx = x2 - x1;
if (dx < 0) {
dx = -dx;
}
int dy = y2 - y1;
if (dy < 0) {
dy = -dy;
}
int dm = (dx <= dy) ? dx : dy;
return dx + dy - (dm / 2);
}
// 0x416360
static int _tile_idistance(int tile1, int tile2)
{
int x1;
int y1;
tileToScreenXY(tile1, &x1, &y1, gElevation);
int x2;
int y2;
tileToScreenXY(tile2, &x2, &y2, gElevation);
return _idist(x1, y1, x2, y2);
}
// 0x4163AC
int _make_straight_path(Object* obj, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6)
{
return _make_straight_path_func(obj, from, to, straightPathNodeList, obstaclePtr, a6, _obj_blocking_at);
}
// TODO: Rather complex, but understandable, needs testing.
//
// 0x4163C8
int _make_straight_path_func(Object* obj, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback)
{
if (obstaclePtr != nullptr) {
Object* obstacle = callback(obj, from, obj->elevation);
if (obstacle != nullptr) {
if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) {
*obstaclePtr = obstacle;
return 0;
}
}
}
int fromX;
int fromY;
tileToScreenXY(from, &fromX, &fromY, obj->elevation);
fromX += 16;
fromY += 8;
int toX;
int toY;
tileToScreenXY(to, &toX, &toY, obj->elevation);
toX += 16;
toY += 8;
int stepX;
int deltaX = toX - fromX;
if (deltaX > 0) {
stepX = 1;
} else if (deltaX < 0) {
stepX = -1;
} else {
stepX = 0;
}
int stepY;
int deltaY = toY - fromY;
if (deltaY > 0) {
stepY = 1;
} else if (deltaY < 0) {
stepY = -1;
} else {
stepY = 0;
}
int ddx = 2 * abs(toX - fromX);
int ddy = 2 * abs(toY - fromY);
int tileX = fromX;
int tileY = fromY;
int pathNodeIndex = 0;
int prevTile = from;
int v22 = 0;
int tile;
if (ddx <= ddy) {
int middle = ddx - ddy / 2;
while (true) {
tile = tileFromScreenXY(tileX, tileY, obj->elevation);
v22 += 1;
if (v22 == a6) {
if (pathNodeIndex >= PATHFINDING_LIMIT) {
return 0;
}
if (straightPathNodeList != nullptr) {
StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]);
pathNode->tile = tile;
pathNode->elevation = obj->elevation;
tileToScreenXY(tile, &fromX, &fromY, obj->elevation);
pathNode->x = tileX - fromX - 16;
pathNode->y = tileY - fromY - 8;
}
v22 = 0;
pathNodeIndex++;
}
if (tileY == toY) {
if (obstaclePtr != nullptr) {
*obstaclePtr = nullptr;
}
break;
}
if (middle >= 0) {
tileX += stepX;
middle -= ddy;
}
tileY += stepY;
middle += ddx;
if (tile != prevTile) {
if (obstaclePtr != nullptr) {
Object* obstacle = callback(obj, tile, obj->elevation);
if (obstacle != nullptr) {
if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) {
*obstaclePtr = obstacle;
break;
}
}
}
prevTile = tile;
}
}
} else {
int middle = ddy - ddx / 2;
while (true) {
tile = tileFromScreenXY(tileX, tileY, obj->elevation);
v22 += 1;
if (v22 == a6) {
if (pathNodeIndex >= PATHFINDING_LIMIT) {
return 0;
}
if (straightPathNodeList != nullptr) {
StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]);
pathNode->tile = tile;
pathNode->elevation = obj->elevation;
tileToScreenXY(tile, &fromX, &fromY, obj->elevation);
pathNode->x = tileX - fromX - 16;
pathNode->y = tileY - fromY - 8;
}
v22 = 0;
pathNodeIndex++;
}
if (tileX == toX) {
if (obstaclePtr != nullptr) {
*obstaclePtr = nullptr;
}
break;
}
if (middle >= 0) {
tileY += stepY;
middle -= ddx;
}
tileX += stepX;
middle += ddy;
if (tile != prevTile) {
if (obstaclePtr != nullptr) {
Object* obstacle = callback(obj, tile, obj->elevation);
if (obstacle != nullptr) {
if (obstacle != *obstaclePtr && (a6 != 32 || (obstacle->flags & OBJECT_SHOOT_THRU) == 0)) {
*obstaclePtr = obstacle;
break;
}
}
}
prevTile = tile;
}
}
}
if (v22 != 0) {
if (pathNodeIndex >= PATHFINDING_LIMIT) {
return 0;
}
if (straightPathNodeList != nullptr) {
StraightPathNode* pathNode = &(straightPathNodeList[pathNodeIndex]);
pathNode->tile = tile;
pathNode->elevation = obj->elevation;
tileToScreenXY(tile, &fromX, &fromY, obj->elevation);
pathNode->x = tileX - fromX - 16;
pathNode->y = tileY - fromY - 8;
}
pathNodeIndex += 1;
} else {
if (pathNodeIndex > 0 && straightPathNodeList != nullptr) {
straightPathNodeList[pathNodeIndex - 1].elevation = obj->elevation;
}
}
return pathNodeIndex;
}
// 0x4167F8
static int animateMoveObjectToObject(Object* from, Object* to, int actionPoints, int anim, int animationSequenceIndex)
{
bool hidden = (to->flags & OBJECT_HIDDEN);
to->flags |= OBJECT_HIDDEN;
int moveSadIndex = _anim_move(from, to->tile, to->elevation, -1, anim, 0, animationSequenceIndex);
if (!hidden) {
to->flags &= ~OBJECT_HIDDEN;
}
if (moveSadIndex == -1) {
return -1;
}
AnimationSad* sad = &(gAnimationSads[moveSadIndex]);
// NOTE: Original code is somewhat different. Due to some kind of
// optimization this value is either 1 or 2, which is later used in
// subsequent calculations and rotations array lookup.
bool isMultihex = (from->flags & OBJECT_MULTIHEX);
sad->field_1C -= (isMultihex ? 2 : 1);
if (sad->field_1C <= 0) {
sad->field_20 = -1000;
_anim_set_continue(animationSequenceIndex, 0);
}
sad->field_24 = tileGetTileInDirection(to->tile, sad->rotations[isMultihex ? sad->field_1C + 1 : sad->field_1C], 1);
if (isMultihex) {
sad->field_24 = tileGetTileInDirection(sad->field_24, sad->rotations[sad->field_1C], 1);
}
if (actionPoints != -1 && actionPoints < sad->field_1C) {
sad->field_1C = actionPoints;
}
return 0;
}
// 0x41695C
int _make_stair_path(Object* object, int from, int fromElevation, int to, int toElevation, StraightPathNode* a6, Object** obstaclePtr)
{
int elevation = fromElevation;
if (elevation > toElevation) {
elevation = toElevation;
}
int fromX;
int fromY;
tileToScreenXY(from, &fromX, &fromY, fromElevation);
fromX += 16;
fromY += 8;
int toX;
int toY;
tileToScreenXY(to, &toX, &toY, toElevation);
toX += 16;
toY += 8;
if (obstaclePtr != nullptr) {
*obstaclePtr = nullptr;
}
int ddx = 2 * abs(toX - fromX);
int stepX;
int deltaX = toX - fromX;
if (deltaX > 0) {
stepX = 1;
} else if (deltaX < 0) {
stepX = -1;
} else {
stepX = 0;
}
int ddy = 2 * abs(toY - fromY);
int stepY;
int deltaY = toY - fromY;
if (deltaY > 0) {
stepY = 1;
} else if (deltaY < 0) {
stepY = -1;
} else {
stepY = 0;
}
int tileX = fromX;
int tileY = fromY;
int pathNodeIndex = 0;
int prevTile = from;
int iteration = 0;
int tile;
if (ddx > ddy) {
int middle = ddy - ddx / 2;
while (true) {
tile = tileFromScreenXY(tileX, tileY, elevation);
iteration += 1;
if (iteration == 16) {
if (pathNodeIndex >= PATHFINDING_LIMIT) {
return 0;
}
if (a6 != nullptr) {
StraightPathNode* pathNode = &(a6[pathNodeIndex]);
pathNode->tile = tile;
pathNode->elevation = elevation;
tileToScreenXY(tile, &fromX, &fromY, elevation);
pathNode->x = tileX - fromX - 16;
pathNode->y = tileY - fromY - 8;
}
iteration = 0;
pathNodeIndex++;
}
if (tileX == toX) {
break;
}
if (middle >= 0) {
tileY += stepY;
middle -= ddx;
}
tileX += stepX;
middle += ddy;
if (tile != prevTile) {
if (obstaclePtr != nullptr) {
*obstaclePtr = _obj_blocking_at(object, tile, object->elevation);
if (*obstaclePtr != nullptr) {
break;
}
}
prevTile = tile;
}
}
} else {
int middle = ddx - ddy / 2;
while (true) {
tile = tileFromScreenXY(tileX, tileY, elevation);
iteration += 1;
if (iteration == 16) {
if (pathNodeIndex >= PATHFINDING_LIMIT) {
return 0;
}
if (a6 != nullptr) {
StraightPathNode* pathNode = &(a6[pathNodeIndex]);
pathNode->tile = tile;
pathNode->elevation = elevation;
tileToScreenXY(tile, &fromX, &fromY, elevation);
pathNode->x = tileX - fromX - 16;
pathNode->y = tileY - fromY - 8;
}
iteration = 0;
pathNodeIndex++;
}
if (tileY == toY) {
break;
}
if (middle >= 0) {
tileX += stepX;
middle -= ddy;
}
tileY += stepY;
middle += ddx;
if (tile != prevTile) {
if (obstaclePtr != nullptr) {
*obstaclePtr = _obj_blocking_at(object, tile, object->elevation);
if (*obstaclePtr != nullptr) {
break;
}
}
prevTile = tile;
}
}
}
if (iteration != 0) {
if (pathNodeIndex >= PATHFINDING_LIMIT) {
return 0;
}
if (a6 != nullptr) {
StraightPathNode* pathNode = &(a6[pathNodeIndex]);
pathNode->tile = tile;
pathNode->elevation = elevation;
tileToScreenXY(tile, &fromX, &fromY, elevation);
pathNode->x = tileX - fromX - 16;
pathNode->y = tileY - fromY - 8;
}
pathNodeIndex++;
} else {
if (pathNodeIndex > 0) {
if (a6 != nullptr) {
a6[pathNodeIndex - 1].elevation = toElevation;
}
}
}
return pathNodeIndex;
}
// 0x416CFC
static int animateMoveObjectToTile(Object* obj, int tile, int elev, int actionPoints, int anim, int animationSequenceIndex)
{
int index = _anim_move(obj, tile, elev, -1, anim, 0, animationSequenceIndex);
if (index == -1) {
return -1;
}
if (_obj_blocking_at(obj, tile, elev)) {
AnimationSad* sad = &(gAnimationSads[index]);
sad->field_1C--;
if (sad->field_1C <= 0) {
sad->field_20 = -1000;
_anim_set_continue(animationSequenceIndex, 0);
}
sad->field_24 = tileGetTileInDirection(tile, sad->rotations[sad->field_1C], 1);
if (actionPoints != -1 && actionPoints < sad->field_1C) {
sad->field_1C = actionPoints;
}
}
return 0;
}
// 0x416DFC
static int _anim_move(Object* obj, int tile, int elev, int a3, int anim, int a5, int animationSequenceIndex)
{
if (gAnimationCurrentSad == ANIMATION_SAD_LIST_CAPACITY) {
return -1;
}
AnimationSad* sad = &(gAnimationSads[gAnimationCurrentSad]);
sad->obj = obj;
if (a5) {
sad->flags = ANIM_SAD_0x20;
} else {
sad->flags = 0;
}
sad->field_20 = -2000;
sad->fid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, anim, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
sad->animationTimestamp = 0;
sad->ticksPerFrame = animationComputeTicksPerFrame(obj, sad->fid);
sad->field_24 = tile;
sad->animationSequenceIndex = animationSequenceIndex;
sad->anim = anim;
sad->field_1C = _make_path(obj, obj->tile, tile, sad->rotations, a5);
if (sad->field_1C == 0) {
sad->field_20 = -1000;
return -1;
}
if (a3 != -1 && sad->field_1C > a3) {
sad->field_1C = a3;
}
return gAnimationCurrentSad++;
}
// 0x416F54
static int animateMoveObjectToTileStraight(Object* obj, int tile, int elevation, int anim, int animationSequenceIndex, int flags)
{
if (gAnimationCurrentSad == ANIMATION_SAD_LIST_CAPACITY) {
return -1;
}
AnimationSad* sad = &(gAnimationSads[gAnimationCurrentSad]);
sad->obj = obj;
sad->flags = flags | ANIM_SAD_STRAIGHT;
if (anim == -1) {
sad->fid = obj->fid;
sad->flags |= ANIM_SAD_NO_ANIM;
} else {
sad->fid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, anim, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
}
sad->field_20 = -2000;
sad->animationTimestamp = 0;
sad->ticksPerFrame = animationComputeTicksPerFrame(obj, sad->fid);
sad->animationSequenceIndex = animationSequenceIndex;
int v15;
if (FID_TYPE(obj->fid) == OBJ_TYPE_CRITTER) {
if (FID_ANIM_TYPE(obj->fid) == ANIM_JUMP_BEGIN)
v15 = 16;
else
v15 = 4;
} else {
v15 = 32;
}
sad->field_1C = _make_straight_path(obj, obj->tile, tile, sad->straightPathNodeList, nullptr, v15);
if (sad->field_1C == 0) {
sad->field_20 = -1000;
return -1;
}
gAnimationCurrentSad++;
return 0;
}
// 0x41712C
static int _anim_move_on_stairs(Object* obj, int tile, int elevation, int anim, int animationSequenceIndex)
{
if (gAnimationCurrentSad == ANIMATION_SAD_LIST_CAPACITY) {
return -1;
}
AnimationSad* sad = &(gAnimationSads[gAnimationCurrentSad]);
sad->flags = ANIM_SAD_STRAIGHT;
sad->obj = obj;
if (anim == -1) {
sad->fid = obj->fid;
sad->flags |= ANIM_SAD_NO_ANIM;
} else {
sad->fid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, anim, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
}
sad->field_20 = -2000;
sad->animationTimestamp = 0;
sad->ticksPerFrame = animationComputeTicksPerFrame(obj, sad->fid);
sad->animationSequenceIndex = animationSequenceIndex;
sad->field_1C = _make_stair_path(obj, obj->tile, obj->elevation, tile, elevation, sad->straightPathNodeList, nullptr);
if (sad->field_1C == 0) {
sad->field_20 = -1000;
return -1;
}
gAnimationCurrentSad++;
return 0;
}
// 0x417248
static int _check_for_falling(Object* obj, int anim, int a3)
{
if (gAnimationCurrentSad == ANIMATION_SAD_LIST_CAPACITY) {
return -1;
}
if (_check_gravity(obj->tile, obj->elevation) == obj->elevation) {
return -1;
}
AnimationSad* sad = &(gAnimationSads[gAnimationCurrentSad]);
sad->flags = ANIM_SAD_STRAIGHT;
sad->obj = obj;
if (anim == -1) {
sad->fid = obj->fid;
sad->flags |= ANIM_SAD_NO_ANIM;
} else {
sad->fid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, anim, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
}
sad->field_20 = -2000;
sad->animationTimestamp = 0;
sad->ticksPerFrame = animationComputeTicksPerFrame(obj, sad->fid);
sad->animationSequenceIndex = a3;
sad->field_1C = _make_straight_path_func(obj, obj->tile, obj->tile, sad->straightPathNodeList, nullptr, 16, _obj_blocking_at);
if (sad->field_1C == 0) {
sad->field_20 = -1000;
return -1;
}
gAnimationCurrentSad++;
return 0;
}
// 0x417360
static void _object_move(int index)
{
AnimationSad* sad = &(gAnimationSads[index]);
Object* object = sad->obj;
Rect dirtyRect;
Rect tempRect;
if (sad->field_20 == -2000) {
objectSetLocation(object, object->tile, object->elevation, &dirtyRect);
objectSetFrame(object, 0, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
objectSetRotation(object, sad->rotations[0], &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
int fid = buildFid(FID_TYPE(object->fid), object->fid & 0xFFF, sad->anim, (object->fid & 0xF000) >> 12, object->rotation + 1);
objectSetFid(object, fid, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
sad->field_20 = 0;
} else {
objectSetNextFrame(object, &dirtyRect);
}
int frameX;
int frameY;
CacheEntry* cacheHandle;
Art* art = artLock(object->fid, &cacheHandle);
if (art != nullptr) {
artGetFrameOffsets(art, object->frame, object->rotation, &frameX, &frameY);
artUnlock(cacheHandle);
} else {
frameX = 0;
frameY = 0;
}
_obj_offset(object, frameX, frameY, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
int rotation = sad->rotations[sad->field_20];
int y = dword_51D984[rotation];
int x = _off_tile[rotation];
if ((x > 0 && x <= object->x) || (x < 0 && x >= object->x) || (y > 0 && y <= object->y) || (y < 0 && y >= object->y)) {
x = object->x - x;
y = object->y - y;
int nextTile = tileGetTileInDirection(object->tile, rotation, 1);
Object* obstacle = _obj_blocking_at(object, nextTile, object->elevation);
if (obstacle != nullptr) {
if (!canUseDoor(object, obstacle)) {
sad->field_1C = _make_path(object, object->tile, sad->field_24, sad->rotations, 1);
if (sad->field_1C != 0) {
objectSetLocation(object, object->tile, object->elevation, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
objectSetFrame(object, 0, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
objectSetRotation(object, sad->rotations[0], &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
sad->field_20 = 0;
} else {
sad->field_20 = -1000;
}
nextTile = -1;
} else {
_obj_use_door(object, obstacle, 0);
}
}
if (nextTile != -1) {
objectSetLocation(object, nextTile, object->elevation, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
bool cannotMove = false;
if (isInCombat() && FID_TYPE(object->fid) == OBJ_TYPE_CRITTER) {
int actionPointsRequired = critterGetMovementPointCostAdjustedForCrippledLegs(object, 1);
if (actionPointsRequired > _combat_free_move) {
actionPointsRequired -= _combat_free_move;
_combat_free_move = 0;
if (actionPointsRequired > object->data.critter.combat.ap) {
object->data.critter.combat.ap = 0;
} else {
object->data.critter.combat.ap -= actionPointsRequired;
}
} else {
_combat_free_move -= actionPointsRequired;
}
if (object == gDude) {
interfaceRenderActionPoints(gDude->data.critter.combat.ap, _combat_free_move);
}
cannotMove = (object->data.critter.combat.ap + _combat_free_move) <= 0;
}
sad->field_20 += 1;
if (sad->field_20 == sad->field_1C || cannotMove) {
sad->field_20 = -1000;
} else {
objectSetRotation(object, sad->rotations[sad->field_20], &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
_obj_offset(object, x, y, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
}
}
}
tileWindowRefreshRect(&dirtyRect, object->elevation);
if (sad->field_20 == -1000) {
_anim_set_continue(sad->animationSequenceIndex, 1);
}
}
// 0x4177C0
static void _object_straight_move(int index)
{
AnimationSad* sad = &(gAnimationSads[index]);
Object* object = sad->obj;
Rect dirtyRect;
Rect tempRect;
if (sad->field_20 == -2000) {
objectSetFid(object, sad->fid, &dirtyRect);
sad->field_20 = 0;
} else {
objectGetRect(object, &dirtyRect);
}
CacheEntry* cacheHandle;
Art* art = artLock(object->fid, &cacheHandle);
if (art != nullptr) {
int lastFrame = artGetFrameCount(art) - 1;
artUnlock(cacheHandle);
if ((sad->flags & ANIM_SAD_NO_ANIM) == 0) {
if ((sad->flags & ANIM_SAD_WAIT_FOR_COMPLETION) == 0 || object->frame < lastFrame) {
objectSetNextFrame(object, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
}
}
if (sad->field_20 < sad->field_1C) {
StraightPathNode* straightPathNode = &(sad->straightPathNodeList[sad->field_20]);
objectSetLocation(object, straightPathNode->tile, straightPathNode->elevation, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
_obj_offset(object, straightPathNode->x, straightPathNode->y, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
sad->field_20++;
}
if (sad->field_20 == sad->field_1C) {
if ((sad->flags & ANIM_SAD_WAIT_FOR_COMPLETION) == 0 || object->frame == lastFrame) {
sad->field_20 = -1000;
}
}
tileWindowRefreshRect(&dirtyRect, sad->obj->elevation);
if (sad->field_20 == -1000) {
_anim_set_continue(sad->animationSequenceIndex, 1);
}
}
}
// 0x4179B8
static int _anim_animate(Object* obj, int anim, int animationSequenceIndex, int flags)
{
if (gAnimationCurrentSad == ANIMATION_SAD_LIST_CAPACITY) {
return -1;
}
AnimationSad* sad = &(gAnimationSads[gAnimationCurrentSad]);
int fid;
if (anim == ANIM_TAKE_OUT) {
sad->flags = 0;
fid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, ANIM_TAKE_OUT, flags, obj->rotation + 1);
} else {
sad->flags = flags;
fid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, anim, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
}
if (!artExists(fid)) {
return -1;
}
sad->obj = obj;
sad->fid = fid;
sad->animationSequenceIndex = animationSequenceIndex;
sad->animationTimestamp = 0;
sad->ticksPerFrame = animationComputeTicksPerFrame(obj, sad->fid);
sad->field_20 = 0;
sad->field_1C = 0;
gAnimationCurrentSad++;
return 0;
}
// 0x417B30
void _object_animate()
{
if (gAnimationCurrentSad == 0) {
return;
}
_anim_in_bk = true;
for (int index = 0; index < gAnimationCurrentSad; index++) {
AnimationSad* sad = &(gAnimationSads[index]);
if (sad->field_20 == -1000) {
continue;
}
Object* object = sad->obj;
unsigned int time = getTicks();
if (getTicksBetween(time, sad->animationTimestamp) < sad->ticksPerFrame) {
continue;
}
sad->animationTimestamp = time;
if (animationRunSequence(sad->animationSequenceIndex) == -1) {
continue;
}
if (sad->field_1C > 0) {
if ((sad->flags & ANIM_SAD_STRAIGHT) != 0) {
_object_straight_move(index);
} else {
int savedTile = object->tile;
_object_move(index);
if (savedTile != object->tile) {
scriptsExecSpatialProc(object, object->tile, object->elevation);
}
}
continue;
}
if (sad->field_20 == 0) {
for (int index = 0; index < gAnimationCurrentSad; index++) {
AnimationSad* otherSad = &(gAnimationSads[index]);
if (object == otherSad->obj && otherSad->field_20 == -2000) {
otherSad->field_20 = -1000;
_anim_set_continue(otherSad->animationSequenceIndex, 1);
}
}
sad->field_20 = -2000;
}
Rect dirtyRect;
Rect tempRect;
objectGetRect(object, &dirtyRect);
if (object->fid == sad->fid) {
if ((sad->flags & ANIM_SAD_REVERSE) == 0) {
CacheEntry* cacheHandle;
Art* art = artLock(object->fid, &cacheHandle);
if (art != nullptr) {
if ((sad->flags & ANIM_SAD_FOREVER) == 0 && object->frame == artGetFrameCount(art) - 1) {
sad->field_20 = -1000;
artUnlock(cacheHandle);
if ((sad->flags & ANIM_SAD_HIDE_ON_END) != 0) {
// NOTE: Uninline.
_anim_hide(object, -1);
}
_anim_set_continue(sad->animationSequenceIndex, 1);
continue;
} else {
objectSetNextFrame(object, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
int frameX;
int frameY;
artGetFrameOffsets(art, object->frame, object->rotation, &frameX, &frameY);
_obj_offset(object, frameX, frameY, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
artUnlock(cacheHandle);
}
}
tileWindowRefreshRect(&dirtyRect, gElevation);
continue;
}
if ((sad->flags & ANIM_SAD_FOREVER) != 0 || object->frame != 0) {
int x;
int y;
CacheEntry* cacheHandle;
Art* art = artLock(object->fid, &cacheHandle);
if (art != nullptr) {
artGetFrameOffsets(art, object->frame, object->rotation, &x, &y);
artUnlock(cacheHandle);
}
objectSetPrevFrame(object, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
_obj_offset(object, -x, -y, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
tileWindowRefreshRect(&dirtyRect, gElevation);
continue;
}
sad->field_20 = -1000;
_anim_set_continue(sad->animationSequenceIndex, 1);
} else {
int x;
int y;
CacheEntry* cacheHandle;
Art* art = artLock(object->fid, &cacheHandle);
if (art != nullptr) {
artGetRotationOffsets(art, object->rotation, &x, &y);
artUnlock(cacheHandle);
} else {
x = 0;
y = 0;
}
objectSetFid(object, sad->fid, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
art = artLock(object->fid, &cacheHandle);
if (art != nullptr) {
int frame;
if ((sad->flags & ANIM_SAD_REVERSE) != 0) {
frame = artGetFrameCount(art) - 1;
} else {
frame = 0;
}
objectSetFrame(object, frame, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
int frameX;
int frameY;
artGetFrameOffsets(art, object->frame, object->rotation, &frameX, &frameY);
Rect tempRect;
_obj_offset(object, x + frameX, y + frameY, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
artUnlock(cacheHandle);
} else {
objectSetFrame(object, 0, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
}
tileWindowRefreshRect(&dirtyRect, gElevation);
}
}
_anim_in_bk = 0;
_object_anim_compact();
}
// 0x417F18
static void _object_anim_compact()
{
for (int index = 0; index < ANIMATION_SEQUENCE_LIST_CAPACITY; index++) {
AnimationSequence* animationSequence = &(gAnimationSequences[index]);
if ((animationSequence->flags & ANIM_SEQ_0x20) != 0) {
animationSequence->flags = 0;
}
}
int index = 0;
for (; index < gAnimationCurrentSad; index++) {
if (gAnimationSads[index].field_20 == -1000) {
int nextIndex = index + 1;
for (; nextIndex < gAnimationCurrentSad; nextIndex++) {
if (gAnimationSads[nextIndex].field_20 != -1000) {
break;
}
}
if (nextIndex == gAnimationCurrentSad) {
break;
}
if (index != nextIndex) {
memcpy(&(gAnimationSads[index]), &(gAnimationSads[nextIndex]), sizeof(AnimationSad));
gAnimationSads[nextIndex].field_20 = -1000;
gAnimationSads[nextIndex].flags = 0;
}
}
}
gAnimationCurrentSad = index;
}
// 0x417FFC
int _check_move(int* actionPointsPtr)
{
int x;
int y;
mouseGetPosition(&x, &y);
int tile = tileFromScreenXY(x, y, gElevation);
if (tile == -1) {
return -1;
}
if (isInCombat()) {
if (*actionPointsPtr != -1) {
if (gPressedPhysicalKeys[SDL_SCANCODE_RCTRL] || gPressedPhysicalKeys[SDL_SCANCODE_LCTRL]) {
int hitMode;
bool aiming;
interfaceGetCurrentHitMode(&hitMode, &aiming);
*actionPointsPtr -= itemGetActionPointCost(gDude, hitMode, aiming);
if (*actionPointsPtr <= 0) {
return -1;
}
}
}
} else {
if (settings.system.interrupt_walk) {
reg_anim_clear(gDude);
}
}
return tile;
}
// 0x4180B4
int _dude_move(int actionPoints)
{
// 0x51072C
static int lastDestination = -2;
int tile = _check_move(&actionPoints);
if (tile == -1) {
return -1;
}
if (lastDestination == tile) {
return _dude_run(actionPoints);
}
lastDestination = tile;
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterMoveToTile(gDude, tile, gDude->elevation, actionPoints, 0);
return reg_anim_end();
}
// 0x41810C
int _dude_run(int actionPoints)
{
int tile = _check_move(&actionPoints);
if (tile == -1) {
return -1;
}
if (!perkGetRank(gDude, PERK_SILENT_RUNNING)) {
dudeDisableState(DUDE_STATE_SNEAKING);
}
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterRunToTile(gDude, tile, gDude->elevation, actionPoints, 0);
return reg_anim_end();
}
// 0x418168
void _dude_fidget()
{
// 0x510730
static unsigned int lastTime = 0;
// 0x510734
static unsigned int nextTime = 0;
// 0x56C7E0
static Object* candidates[100];
if (_game_user_wants_to_quit != 0) {
return;
}
if (isInCombat()) {
return;
}
if (vcrGetState() != VCR_STATE_TURNED_OFF) {
return;
}
if ((gDude->flags & OBJECT_HIDDEN) != 0) {
return;
}
unsigned int currentTime = _get_bk_time();
if (getTicksBetween(currentTime, lastTime) <= nextTime) {
return;
}
lastTime = currentTime;
int candidatesLength = 0;
Object* object = objectFindFirstAtElevation(gDude->elevation);
while (object != nullptr) {
if (candidatesLength >= 100) {
break;
}
if ((object->flags & OBJECT_HIDDEN) == 0 && FID_TYPE(object->fid) == OBJ_TYPE_CRITTER && FID_ANIM_TYPE(object->fid) == ANIM_STAND && !critterIsDead(object)) {
Rect rect;
objectGetRect(object, &rect);
Rect intersection;
if (rectIntersection(&rect, &_scr_size, &intersection) == 0 && (gMapHeader.field_34 != 97 || object->pid != 0x10000FA)) {
candidates[candidatesLength++] = object;
}
}
object = objectFindNextAtElevation();
}
int delayInSeconds;
if (candidatesLength != 0) {
int index = randomBetween(0, candidatesLength - 1);
Object* object = candidates[index];
reg_anim_begin(ANIMATION_REQUEST_UNRESERVED | ANIMATION_REQUEST_INSIGNIFICANT);
bool shoudPlaySound = false;
if (object == gDude) {
shoudPlaySound = true;
} else {
char fileName[16];
fileName[0] = '\0';
artCopyFileName(1, object->fid & 0xFFF, fileName);
if (fileName[0] == 'm' || fileName[0] == 'M') {
if (objectGetDistanceBetween(object, gDude) < critterGetStat(gDude, STAT_PERCEPTION) * 2) {
shoudPlaySound = true;
}
}
}
if (shoudPlaySound) {
const char* sfx = sfxBuildCharName(object, ANIM_STAND, CHARACTER_SOUND_EFFECT_UNUSED);
animationRegisterPlaySoundEffect(object, sfx, 0);
}
animationRegisterAnimate(object, ANIM_STAND, 0);
reg_anim_end();
delayInSeconds = 20 / candidatesLength;
} else {
delayInSeconds = 7;
}
if (delayInSeconds < 1) {
delayInSeconds = 1;
} else if (delayInSeconds > 7) {
delayInSeconds = 7;
}
nextTime = randomBetween(0, 3000) + 1000 * delayInSeconds;
}
// 0x418378
void _dude_stand(Object* obj, int rotation, int fid)
{
Rect rect;
objectSetRotation(obj, rotation, &rect);
int x = 0;
int y = 0;
int weaponAnimationCode = (obj->fid & 0xF000) >> 12;
if (weaponAnimationCode != 0) {
if (fid == -1) {
int takeOutFid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, ANIM_TAKE_OUT, weaponAnimationCode, obj->rotation + 1);
CacheEntry* takeOutFrmHandle;
Art* takeOutFrm = artLock(takeOutFid, &takeOutFrmHandle);
if (takeOutFrm != nullptr) {
int frameCount = artGetFrameCount(takeOutFrm);
for (int frame = 0; frame < frameCount; frame++) {
int offsetX;
int offsetY;
artGetFrameOffsets(takeOutFrm, frame, obj->rotation, &offsetX, &offsetY);
x += offsetX;
y += offsetY;
}
artUnlock(takeOutFrmHandle);
CacheEntry* standFrmHandle;
int standFid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, ANIM_STAND, 0, obj->rotation + 1);
Art* standFrm = artLock(standFid, &standFrmHandle);
if (standFrm != nullptr) {
int offsetX;
int offsetY;
if (artGetRotationOffsets(standFrm, obj->rotation, &offsetX, &offsetY) == 0) {
x += offsetX;
y += offsetY;
}
artUnlock(standFrmHandle);
}
}
}
}
if (fid == -1) {
int anim;
if (FID_ANIM_TYPE(obj->fid) == ANIM_FIRE_DANCE) {
anim = ANIM_FIRE_DANCE;
} else {
anim = ANIM_STAND;
}
fid = buildFid(FID_TYPE(obj->fid), (obj->fid & 0xFFF), anim, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
}
Rect temp;
objectSetFid(obj, fid, &temp);
rectUnion(&rect, &temp, &rect);
objectSetLocation(obj, obj->tile, obj->elevation, &temp);
rectUnion(&rect, &temp, &rect);
objectSetFrame(obj, 0, &temp);
rectUnion(&rect, &temp, &rect);
_obj_offset(obj, x, y, &temp);
rectUnion(&rect, &temp, &rect);
tileWindowRefreshRect(&rect, obj->elevation);
}
// 0x418574
void _dude_standup(Object* a1)
{
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
int anim;
if (FID_ANIM_TYPE(a1->fid) == ANIM_FALL_BACK) {
anim = ANIM_BACK_TO_STANDING;
} else {
anim = ANIM_PRONE_TO_STANDING;
}
animationRegisterAnimate(a1, anim, 0);
reg_anim_end();
a1->data.critter.combat.results &= ~DAM_KNOCKED_DOWN;
}
// 0x4185EC
static int actionRotate(Object* obj, int delta, int animationSequenceIndex)
{
if (!_critter_is_prone(obj)) {
int rotation = obj->rotation + delta;
if (rotation >= ROTATION_COUNT) {
rotation = ROTATION_NE;
} else if (rotation < 0) {
rotation = ROTATION_NW;
}
_dude_stand(obj, rotation, -1);
}
_anim_set_continue(animationSequenceIndex, 0);
return 0;
}
// NOTE: Inlined.
//
// 0x41862C
static int _anim_hide(Object* object, int animationSequenceIndex)
{
Rect rect;
if (objectHide(object, &rect) == 0) {
tileWindowRefreshRect(&rect, object->elevation);
}
if (animationSequenceIndex != -1) {
_anim_set_continue(animationSequenceIndex, 0);
}
return 0;
}
// 0x418660
static int _anim_change_fid(Object* obj, int animationSequenceIndex, int fid)
{
if (FID_ANIM_TYPE(fid)) {
Rect dirtyRect;
Rect tempRect;
objectSetFid(obj, fid, &dirtyRect);
objectSetFrame(obj, 0, &tempRect);
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
tileWindowRefreshRect(&dirtyRect, obj->elevation);
} else {
_dude_stand(obj, obj->rotation, fid);
}
_anim_set_continue(animationSequenceIndex, 0);
return 0;
}
// 0x4186CC
void animationStop()
{
gAnimationInStop = true;
gAnimationSequenceCurrentIndex = -1;
for (int index = 0; index < ANIMATION_SEQUENCE_LIST_CAPACITY; index++) {
_anim_set_end(index);
}
gAnimationInStop = false;
gAnimationCurrentSad = 0;
}
// 0x418708
static int _check_gravity(int tile, int elevation)
{
for (; elevation > 0; elevation--) {
int x;
int y;
tileToScreenXY(tile, &x, &y, elevation);
int squareTile = squareTileFromScreenXY(x + 2, y + 8, elevation);
int fid = buildFid(OBJ_TYPE_TILE, _square[elevation]->field_0[squareTile] & 0xFFF, 0, 0, 0);
if (fid != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
break;
}
}
return elevation;
}
// 0x418794
static unsigned int animationComputeTicksPerFrame(Object* object, int fid)
{
int fps;
CacheEntry* handle;
Art* frm = artLock(fid, &handle);
if (frm != nullptr) {
fps = artGetFramesPerSecond(frm);
artUnlock(handle);
} else {
fps = 10;
}
if (isInCombat()) {
if (FID_ANIM_TYPE(fid) == ANIM_WALK) {
if (object != gDude || settings.preferences.player_speedup) {
fps += settings.preferences.combat_speed;
}
}
}
return 1000 / fps;
}
int animationRegisterSetLightIntensity(Object* owner, int lightDistance, int lightIntensity, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_SET_LIGHT_INTENSITY;
animationDescription->artCacheKey = nullptr;
animationDescription->owner = owner;
animationDescription->lightDistance = lightDistance;
animationDescription->lightIntensity = lightIntensity;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
static void reportOverloaded(Object* critter)
{
MessageListItem messageListItem;
char formattedText[100];
if (critter == gDude) {
// You are overloaded.
snprintf(formattedText, sizeof(formattedText),
"%s",
getmsg(&gMiscMessageList, &messageListItem, 8000));
} else {
// %s is overloaded.
snprintf(formattedText, sizeof(formattedText),
getmsg(&gMiscMessageList, &messageListItem, 8001),
critterGetName(critter));
}
displayMonitorAddMessage(formattedText);
}
} // namespace fallout