NPCAnim.zh ver 1.4.2.0

+---------------+
| What and Why? |
+---------------+

This is a header for handling basic enemy animations and movement with npc scripts.
Think animation handler crossed with LinkMovement.zh but for enemies. What this 
isn't is a substitute for ghost, I have no plans to make anything as robust or user 
friendly as that. This is just my answer to the many times I've reinvented the wheel
when writing npc scripts. I'm hoping that this time I have a system that I can 
stick with.

+-----------------------+
| 2.55 vs. 3.0 versions |
+-----------------------+

The header has now been split into two versions for ZC 2.55 and 3.0.
As of the time of writing both versions are functionally indentical,
but the 3.0 one won't spit out deprecation warnings in 3.0. 

+------------+
| How to use |
+------------+

All scripts using this sytem should start by declaring an AnimHandler object 
for storing animation data. Individual animations can then be defined with 
the AddAnim function. In order to update animations, there's a special Waitframe
function that takes the npc pointer as an argument. If you have a custom waitframe 
function for your enemy, you can call NPCAnim::Waitframe() at the end of that 
function in place of Waitframe(). From there you can start an animation with 
the AnimHandler's PlayAnim() function. Here's a basic example:

npc script Example{
	using namespace NPCAnim;

	// Stick your animations in an enum of unique IDs
	enum Animations{
		WALKING,
		ATTACKING
	};
	void run(){
		// Declare the AnimHandler pointer
		AnimHandler aptr = new AnimHandler(this);

		// Add animations with the IDs from the enum
		aptr->AddAnim(WALKING, 0, 4, 8, ADF_4WAY);
		// Play an animation
		aptr->PlayAnim(WALKING);

		while(true){
			Waitframe(this)
		}
	}
}

+-----------+
| Functions |
+-----------+

Several of the following functions run off the AnimHandler pointer, indicated by
aptr-> in front. If you need to access this pointer outside of the run function, 
for example in a custom waitframe, you can fetch it easily off the enemy:

AnimHandler GetAnimHandler(npc n)
 * Gets a pointer to the AnimHandler object from one of the NPC's Misc[] indices.
 * Which index it's stored in is configurable with NPCM_ANIMPOINTER.

PlayerMovementTracker GetPlayerMovementTracker(npc n)
 * Gets a pointer to an PlayerMovementTracker object if the npc has one. 
 * Set the npc's movement tracker with aptr->SetMovementTracking().

+---------------+
| Enemy Classes |
+---------------+

Often you'll want to make your own enemy classes to store your own enemy related data.
NPCAnim can store a pointer to one of these so you can access it any time in your script
by pulling it off the enemy. Bear in mind that you should only use these inside the 
enemy's script as the pointer may go invalid before the enemy does.

untyped GetEnemyClassPointer(npc n)
 * Returns the enemy class pointer.

void StoreEnemyClassPointer(npc n, untyped ptr)
 * Stores an enemy class pointer to the AnimHandler. 
 * This should be called at the start of your script, after both the AnimHandler
 * and the enemy class pointer has been created.

+----------------+
| Init Functions |
+----------------+

void aptr->SetFlags(int flags)
void aptr->UnsetFlags(int flags)
 * Use this to add or remove the following flags controlling enemy behavior:
 *
 * AHF_NORMAL
 *     Combination of AHF_DOSLIDE, AHF_DOSTUN, and AHF_DOPITFALL
 * AHF_DOSLIDE
 *     The enemy does engine knockback
 * AHF_DOSTUN
 *     Waitframe will suspend while the enemy is stunned
 * AHF_DOPITFALL
 *     The script will quit out when the enemy falls in a pit


void aptr->SetHitbox(int w, int h, int hitOffsets[], bool center=false)
 * Use this to quickly resize and adjust an enemy's damage hitbox.
 * w and h are the enemy's width and height in tiles.
 * hitOffsets[] is an an arry of pixels to shave off each respective side of 
 * its hitbox, starting from the width and height of its tiles.
 * These go in the order of up, down, left, right.
 * If center is true, the enemy will be repositioned after being resized.

void aptr->SetMovementHitbox(int collOffsets[], bool center=false)
 * Use this to adjust the enemy's hitbox for movement. 
 * collOffsets[] is an array of pixels to shave off each respective side of
 * its hitbox, starting from the width and height of its tiles.
 * These go in the order of up, down, left, right.
 * Until this function is called, the movement and damage hitboxes are 
 * considered to be the same.

int aptr->AddAnim(int animID, int tile, int numFrames, int aSpeed, 
            int loopFrame, int nextAnim, int flags)
int aptr->AddAnim(int animID, int tile, int numFrames, int aSpeed, 
            int loopFrame, int flags)
int aptr->AddAnim(int animID, int tile, int numFrames, int aSpeed, int flags)
 * Defines an animation. If animID is -1, will pick a new ID and return that, 
 * else pass it a unique animation ID constant. loopFrame is the frame to loop back
 * to when an animation ends. nextAnim is an animation to immediately play when 
 * the animation ends. flags should be any of the following flags ORed together, 
 * or 0 for none:
 * 
 * ADF_4WAY
 *     The animation comes in 4 different directions.
 * ADF_8WAY
 *     The animation comes in 8 different directions.
 * ADF_2WAYLR
 *     The animation comes in 2 directions: left and right.
 * ADF_2WAYUD
 *     The animation comes in 2 directions: up and down
 * ADF_4WAYDIAG
 *     The animation comes in 4 different directions, but only going at diagonals.
 * ADF_FLIPUP
 * ADF_FLIPDOWN
 * ADF_FLIPLEFT
 * ADF_FLIPRIGHT
 *     In multi directional animations, sprites will be flipped when facing the given 
 *     direction. If not a multi direction animation, will always be flipped.
 *     Frames covered by the flipped direction are removed from the tile layout.
 * ADF_NOLOOP
 *     The animation repeats its last frame when it finishes.
 * ADF_NORELATIVE
 *     The tile given for the animation is absolute rather than relative to the 
 *     original tile in the editor.
 * ADF_NORESET
 *     The animation doesn't reset when switched to another. May look odd if frame 
 *     counts are different.
 * ADF_REVERSE
 *     The animation plays in reverse. loopFrame is then counted backwards from 
 *     the last frame.
 * ADF_VERTICALDIRECTIONS
 *     When used alongside ADF_4WAY and ADF_8WAY, the directional animations are arranged
 *     vertically instead of horizontally.
 * ADF_VERTICALTILES
 *     Tiles in the animation are arranged vertically.
 * ADF_COMBOSYNC
 *     When used alongside a combo animation, its animation will be synced with its 
 *     combo's animation timer.
 * ADF_CONSTHFLIP
 *     This animation will horizontally flip on reaching the end of its animation.
 *     Not compatible with directional animations, use an animation group for those instead.
 * ADF_CONSTVFLIP
 *     This animation will vertically flip on reaching the end of its animation.
 *     Not compatible with directional animations, use an animation group for those instead.

int aptr->AddAnimCombo(int animID, int combo, int loopFrame, int nextAnim, int flags)
int aptr->AddAnimCombo(int animID, int combo, int loopFrame, int flags)
int aptr->AddAnimCombo(int animID, int combo, int flags)
 * Like the above, but will add animations based on a combo ID instead of tiles.
 * The tile, flip, number of frames and animation speed will be read off the combo,
 * and ADF_NORELATIVE will do nothing as a result.

void aptr->ExtendAnim(int animID, int tileW, int tileH, int hitOffsets[], int collOffsets[], int drawOffsets[], int posOffsets[])
 * Extends an animation, so it can have a different width and height than the
 * enemy's default size. Takes four array literals as arguments. Entering 0 for any
 * of them will exclude them.
 *
 * hitOffsets[4] - A set of 4 hit offsets (top, bottom, left, right) base on the
 *                 animation's tile width and height.
 * collOffsets[4] - Same as above, but for collision with terrain. If these
 *                  are unset but hitOffsets is, will be set to the same.
 * drawOffsets[2] - An x,y offset for just the enemy's sprite. Can be used to
 *                  place it up and left from its position.
 * posOffsets[2] - These will offset the enemy's actual position when transformed.

void aptr->AddAnimGroup(int animID, int type, int animGroup[], bool noreset=true)
 * Defines a special animation that's used as a group of other animations based on direction.
 * If animID is -1, will pick a new ID and return that, else pass it a unique animation ID constant.
 * type defines the number of directions and should be set to ANIMTYPE_4DIR or ANIMTYPE_8DIR
 * animGroup[] is an array of animation IDs for each direction and should be size 4 or 8.
 * If an index of the animGroup array is -1, that direction will not have an animation.
 * If noreset is true, the animations in the group won't reset when changing direction.

void aptr->SetFakeShadow(int spr, int w=1, int h=1, int offset=0)
 * Sets up a fake shadow sprite to draw under the enemy. w and h let you 
 * expand the size of the shadow and offset moves it down or up on the Y axis.

+---------------------+
| Animation Functions |
+---------------------+

void aptr->PlayAnim(int animID, bool noReset=false, float speed=-1)
 * This will play one of the NPC's animations. animID is a constant or variable
 * defined by the user. If noReset is true, the animation will not reset the frame
 * and animation clock on the new animation.
 * If animID is -1, this will instead render the enemy invisible and invulnerable.
 * If speed is 0 or higher, will set the enemy's animation speed multiplier to that number.

void aptr->PlayAnimAndWait(int animID, int frames=0, bool noReset=false, float speed=-1)
 * Plays one of the NPC's animations and waits for it to finish.
 * If frames >0, will wait that duration instead of the animation's length.
 * If speed is 0 or higher, will set the enemy's animation speed multiplier to that number.

void aptr->PlayDeathAnim(int spr, int w=1, int h=1, int sfx=0, bool freeze=false)
void aptr->PlayDeathAnim(bool freeze=false)
 * Plays a simple explosion animation for bosses. If spr is > 0, will use a
 * sprite animation with size w and h and play an explosion sound specified by sfx.
 * If freeze is false, the enemy will keep animating while exploding.
 * Note: This function requires npc->Immortal be set for it to work.

bool aptr->AnimFinished()
 * Returns true if the current playing animation is finished playing. 
 * This will return on a looped animation but not on one that has switched 
 * to a new animation with the nextAnim property.

int aptr->GetCurAnim()
 * This will return the enemy's current playing animation.

int aptr->GetCurSingleAnim()
 * This will return the enemy's current playing animation, not including animation groups.

int aptr->GetCurAnimFrame()
 * This will return the current animation frame of the currently playing animation.

void aptr->SetAnimSpeedMultiplier(int mult)
 * This will set a speed multiplier for all of the enemy's animations. 
 * 1 is the base speed and higher or lower will make it animate
 * faster or slower respectively.

+------------------+
| Update Functions |
+------------------+

void Waitframe(npc n)
void Waitframe(npc n, int frames)
 * A replacement waitframe that updates animations for the frame.

void Waitanim(npc n)
 * Waits until the enemy's current animation has ended. 
 * This will halt on a looping animation but not on one that has switched 
 * to a new animation with the nextAnim property.

void Waitanim(npc n, int animID)
 * Waits until the enemy's current animation is not animID.

void Waitspawn(npc n)
 * Waits until the enemy's spawn animation has finished

+-------------------+
| Utility Functions |
+-------------------+
These are helper and shortcut functions for enemies that aren't essential but 
could be useful for making enemies. Use the NPCAnim::Utility namespace to access them.

int DefaultValue(int val, int def)
 * Returns a default value def if val is 0.

void SetAllDefenses(npc n, int dt)
 * Set all of an enemy's defenses to dt.

void StoreDefenses(npc n, int[] defenses)
 * Store all an enemy's defenses in an array.
 * The array's size should be MAX_DEFENSE.

void SetDefenses(npc n, int[] defenses)
 * Set all an enemy's defenses based on an array.

void FaceLink(npc n, bool dir8=false)
 * Face the enemy towards Link.

void FacePoint(npc n, int x, int y, bool dir8=false)
 * Face the enemy towards a point. Uses the enemy's top-left corner as a reference point
 * for both the enemy's position and the target point.

float AngleLink(npc n)
 * Returns the angle from the enemy's center to Link's.

float DistanceLink(npc n)
 * Returns the distance from the enemy's center to Link's.

float AnglePoint(npc n, int x, int y)
 * Returns the angle from the enemy to a point. Uses the enemy's top-left corner as a 
 * reference point for both the enemy's position and the target point.

float DistancePoint(npc n, int x, int y)
 * Returns the distance from the enemy to a point. Uses the enemy's top-left corner as a 
 * reference point for both the enemy's position and the target point.

bool EngineMoveXY(npc n, int dx, int dy, int special=0)
bool EngineMove(npc n, int dir, int pxamnt, int special=0)
bool EngineMoveAtAngle(npc n, int degrees, int pxamnt, int special=0)
bool EngineCanMoveXY(npc n, int dx, int dy, int special=0)
bool EngineCanMove(npc n, int dir, int pxamnt, int special=0)
bool EngineCanMoveAtAngle(npc n, int degrees, int pxamnt, int special=0)
bool EngineCanPlace(npc n, int nx, int ny, int special = SPW_NONE, bool knockback = false, int nw = -1, int nh = -1)
 * All these will call their ZScript default counterparts, but resize the enemy's hitbox
 * to its movement hitbox size beforehand and revert it after. This way you can make use
 * of it for engine functions just like the ones in NPCAnim::Movement.

void MoveTowardLink(npc n, int step, int special=0)
 * Move the enemy towards Link based on their center points. 
 * If MOVEMENT_USES_COLLISION_HITBOX is true, this will use the movement hitbox.

bool MoveToPoint(npc n, int x, int y, int step, int special=0)
 * Move the enemy towards a point based on their top-left corner positions.
 * If MOVEMENT_USES_COLLISION_HITBOX is true, this will use the movement hitbox.

bool CanMoveAngle(npc n, int angle, int step, int special=0, bool requireBoth=false)
 * Returns true if the enemy can move at an angle. 
 * If requireBoth is true, it must be either going straight or collide on two sides.
 * If MOVEMENT_USES_COLLISION_HITBOX is true, this will use the movement hitbox.

bool GotHit(npc n)
 * Returns true if the enemy got hit this frame.

int PredictDurationFromJump(int jump, int grav, int terminalv, int z=0)
 * Returns a prediction for how many frames a jump will take in engine.

int PredictJumpFromDuration(int frames, int grav, int terminalv)
 * Returns a prediction for how high a jump needs to be to reach a frame duration.
 * This uses logic from ghost.zh and is not accurate.

int PredictJumpFromDurationBruteForce(int frames, int grav, int terminalv, int z)
 * Returns a prediction for how high a jump needs to be to reach a frame duration.
 * This is slower than the ghost.zh method, but more accurate. It starts with that method
 * and then does several more passes using averages to try and converge on the 
 * correct answer. 

+--------------------+
| Movement Functions |
+--------------------+

NOTE: Movement functions have now been moved to the NPCAnim::Movement namespace.
In most cases they've been obsoleted by the internal ZScript movement functions, so use those 
instead unless you're using CanMovePixelCustom. 

The following functions all have a moveStyle argument. This is one of several 
presets for making certain types of enemy movement. A hackish substitute for ghost's
movement flags. You can also write your own with the CanMovePixelCustom function.

 * AM_ZQ
 *    Tries to replicate movement based on the enemy editor's Move Flags tab settings.
 * AM_NONE
 *    Typical walking behavior. Can't walk on solids, no enemy, liquids, pits, 
 *    warps, or offscreen
 * AM_FLIER
 *    For flying enemies. Can fly over most things but not no fly zones.
 * AM_WATERONLY
 *    A waterlocked enemy that can only move on shallow or deep water.
 * AM_DEEPWATERONLY
 *    Same as above but only for deep water
 * AM_PITONLY
 *    Same as above but for pits
 * AM_IGNOREALL
 *    Obeys no solidity except for screen edges
 * AM_IGNOREALLOFFSCREEN
 *    Can move anywhere.

bool CanMovePixel(int x, int y, int moveStyle=AM_ZQ)
 * Returns true if a pixel position is walkable for the given moveStyle.

bool CanMove(npc n, int dir, int moveStyle=AM_ZQ)
 * Returns true if the enemy can walk one pixel forward in the given direction.

bool CanMove8(npc n, int dir, int moveStyle=AM_ZQ, bool reqBoth=false)
 * Same as the above  but accepts diagonal directions. 
 * If reqBoth is true, it will return false if half of a diagonal is blocked.

bool CanPlace(npc n, int x, int y, int moveStyle=AM_ZQ)
 * Returns true if the enemy can be placed at the given position.

void MoveXY(npc n, int vx, int vy, int moveStyle=AM_ZQ)
 * Moves the enemy on the X and Y axis in the given moveStyle. 
 * Works similar to LinkMovement.zh.

void MoveAtAngle(npc n, int angle, int step, int moveStyle=AM_ZQ)
 * Moves the enemy at an angle by a pixel step.

void MoveTowardLink(npc n, int step, int moveStyle=AM_ZQ)
 * Moves the enemy toward Link by a pixel step.

bool MoveTowardPoint(npc n, int tx, int ty, int step, int moveStyle=AM_ZQ)
 * Moves the enemy toward a point by a pixel step.
 * It's assumed that the object it's moving toward is the same size as the enemy.
 * Returns true if it has more to move, returning false when it hits the point.

TryUnstickHitbox(npc n, int moveStyle=AM_ZQ)
 * Tries to get the enemy unstuck if it has partially clipped into a wall.

+-------------------+
| Custom Movestyles |
+-------------------+

You can give the above functions whatever collision logic you want by modifying the
CanMovePixelCustom function and using a negative moveStyle. All negative movestyles
are reserved for user defined ones. I don't advise doing this for database scripts
though, as it will require modifying the header. 

+-----------------------------+
| Movement Tracking Functions |
+-----------------------------+

The following functions relate to a movement tracking system that can be attached 
to the animation handler for the purpose of predicting Link's movement.

PlayerMovementTracker aptr->SetMovementTracking(int steps=8, int freq=1, int maxDist=512)
* Attaches a new movement tracker to the animation handler, which will update during 
* the NPCAnim custom waitframe function. 
* It will update every 'freq' frames and collect a backlog of 'steps' changes in position.
* Generally the longer the period of tracking, the less accurate the result will be.

float aptr->PredictLinkX(int frames=1)
float aptr->PredictLinkY(int frames=1)
* Predicts Link's X/Y position within a number of frames.

float aptr->PredictChangeX(int frames=1)
float aptr->PredictChangeY(int frames=1)
* Predicts a change in Link's X/Y position within a number of frames.

+--------------------+
| Internal Functions |
+--------------------+

These two functions are for reading and writing from the aptr array. 
They're mostly just for readability but if you need to access something 
I didn't write another function for, be my guest:

int aptr->GetAnimProperty(int animID, int prop)
* Gets property prop from animation animID in aptr. 
* See the AnimDefIndex enum for the property constants.

void aptr->SetAnimProperty(int animID, int prop)
* Sets property prop for animation animID in aptr to value. 
* See the AnimDefIndex enum for the property constants.

------------------------------------------------------------------------------------