/*
	Beats of Rage
	Side-scrolling beat-'em-up
*/

#if DOS
#include	"dosport.h"
#include	"keyboard.h"
#include	"joy.h"
#endif

#if PSP
#include	"pspport.h"
#endif

#if SDL
#include	"sdlport.h"
#endif

#if DC
#include	"dcport.h"
#endif

#if XBOX
#include	"xboxport.h"
#endif

#include	"types.h"
#include	"video.h"
#include	"vga.h"
#include	"screen.h"
#include	"loadimg.h"
#include	"bitmap.h"
#include	"sprite.h"
#include	"spriteq.h"
#include	"font.h"
#include	"timer.h"
#include	"rand32.h"
#include	"sblaster.h"
#include	"soundmix.h"
#include	"control.h"
#include	"draw.h"
#include	"packfile.h"
#include	"palette.h"
#include	"anigif.h"
#include	"texture.h"

#define		exit				borExit
#define		time				borTime
#define		kill				borKill
#define		pause				borPause
#define		shutdown			borShutdown

#pragma pack (4)

#define		VERSION				0x00020055
#define		COMPATIBLEVERSION	0x00020054
#define		CV_SAVED_GAME		0x00020050
#define		CV_HIGH_SCORE		0x00020048

#define		THINK_SPEED			2
#define		COUNTER_SPEED		(GAME_SPEED*2)
#define		MAX_SPRITES			5000
#define		MAX_ANIMS			1000
#define		MAX_MODELS			200
#define		MAX_ENTS			150
#define		MAX_PANELS			52
#define		ANI_MAX_FRAMES		64
#define		MAX_WEAPONS			10					// Max increased to 10
#define		MAX_COLOUR_MAPS		14
#define		MAX_NAME_LEN		40
#define		LEVEL_MAX_SPAWNS	600
#define		LEVEL_MAX_PANELS	2000
#define		LEVEL_MAX_HOLES		40
#define		LEVEL_MAX_WALLS		40
#define		MAX_LEVELS			100
#define		MAX_DIFFICULTIES	10
#define		MAX_SOUND_CACHE		256
#define		MAX_SPECIALS		8					// Added for customizable freespecials
#define		MAX_ATCHAIN			5					// Maximum attack chain length
#define		MAX_ATTACKS			4					// Total number of attacks players have
#define     MAX_FOLLOW          4					// For followup animations 
#define     MAX_PLAYERS         4
#define     MAX_STRING_TABLE	79					// Total size of stringtable
#define		MAX_FILENAME_LEN	128
#define		MAX_ARG_LEN			512

#define		FLAG_ESC			0x00000001
#define		FLAG_START			0x00000002
#define		FLAG_MOVELEFT		0x00000004
#define		FLAG_MOVERIGHT		0x00000008
#define		FLAG_MOVEUP			0x00000010
#define		FLAG_MOVEDOWN		0x00000020
#define		FLAG_ATTACK			0x00000040
#define		FLAG_JUMP			0x00000080
#define		FLAG_SPECIAL		0x00000100
#define		FLAG_SCREENSHOT		0x00000200
#define		FLAG_ANYBUTTON		(FLAG_START|FLAG_SPECIAL|FLAG_ATTACK|FLAG_JUMP)
#define		FLAG_FORWARD		0					// Flags to be able to distinguish forward/backward movement
#define		FLAG_BACKWARD		1					// Flags to be able to distinguish forward/backward movement

#define		SDID_MOVEUP			0
#define		SDID_MOVEDOWN		1
#define		SDID_MOVELEFT		2
#define		SDID_MOVERIGHT		3
#define		SDID_SPECIAL		4
#define		SDID_ATTACK			5
#define		SDID_JUMP			6
#define		SDID_START			7
#define		SDID_SCREENSHOT		8

#define		TYPE_NONE			0
#define		TYPE_PLAYER			1
#define		TYPE_ENEMY			2
#define		TYPE_ITEM			4
#define		TYPE_OBSTACLE		8
#define		TYPE_STEAMER		16
#define		TYPE_SHOT			32					// 7-1-2005 type to use for player projectiles
#define		TYPE_TRAP			64					// 7-1-2005 lets face it enemies are going to just let you storm in without setting a trap or two!
#define		TYPE_TEXTBOX		128					// New textbox type for displaying messages
#define		TYPE_ENDLEVEL		256					// New endlevel type that ends the level when touched
#define     TYPE_NPC            512                 // A character can be an ally or enemy.

#define		SUBTYPE_NONE		0
#define		SUBTYPE_BIKER		1
#define		SUBTYPE_NOTGRAB		2					//7-1-2005 new subtype for those ungrabbable enemies
#define		SUBTYPE_ARROW		3					//7-1-2005  subtype for an "enemy" that flies across the screen and dies
#define		SUBTYPE_TOUCH		4					// ltb 1-18-05  new Item subtype for a more platformer feel.
#define		SUBTYPE_WEAPON		5
#define		SUBTYPE_NOSKIP		6					// Text type that can't be skipped
#define		SUBTYPE_FLYDIE		7					// Now obstacles can be hit and fly like on Simpsons/TMNT
#define		SUBTYPE_BOTH		8					// Used with TYPE_ENDLEVEL to force both players to reach the point before ending level
#define		SUBTYPE_PROJECTILE	9					// New weapon projectile type that can be picked up by players/enemies
#define     SUBTYPE_FOLLOW      10                  // Used by NPC character, if set, they will try to follow players
#define     SUBTYPE_CHASE       11                  // Used by enemy always chasing you

//------------reserved for A.I. types-------------------------
//  A.I. type1, affect movement path
#define     AITYPE1_NORMAL      0                   // Current default style
#define     AITYPE1_CHASE       1                   // alway move towards target, and can run to them if target is farway
#define     AITYPE1_CHASEZ      2                   // only try to get close in z direction
#define     AITYPE1_CHASEX      3                   // only try to get colse in x direction
#define     AITYPE1_AVOID       4                   // try to avoid target
#define     AITYPE1_AVOIDZ      5                   // only try to avoid target in z direction
#define     AITYPE1_AVOIDX      6                   // only try to avoid target in x direction
#define     AITYPE1_WANDER      7                   // ignore the target's position completely, wander everywhere, long idle time

// A.I type2, affect terrain reflect 
#define     AITYPE2_NORMAL      0                   // Current default style
#define     AITYPE2_IGNOREHOLE  256                   // don't avoid holes

// A.I. type3, affect attacking style
#define     AITYPE3_NORMAL      0                   // Current default style
#define     AITYPE3_LONG        1                   // Long range first
#define     AITYPE3_MELEE       2                   // Melee attack first

//  A.I. type4, affect Defending style
#define     AITYPE4_NORMAL      0                   // Current default style, don't dodge at all
#define     AITYPE4_DODGE       256                 // Use dodge animation to avoid attack
#define     AITYPE4_DODGEMOVE   512                 // Try to move in z direction if a jump attack is about to hit him
                                                       // and try to step back if a melee attack is about to hit him
                                                       

// Note: the minimum Z coordinate of the player is important
// for several other drawing operations.
// movement restirctions are here!
int			PLAYER_MIN_Z		= 160;				// 2-10-05  adjustable walking area
int			PLAYER_MAX_Z		= 232;				// 2-10-05  adjustable walking area
int			BGHEIGHT			= 160;				// 2-10-05  adjustable BGHeight
#define		FRONTPANEL_Z		(PLAYER_MAX_Z+50)
#define		HOLE_Z				(PLAYER_MIN_Z-46)
#define		NEONPANEL_Z			(PLAYER_MIN_Z-47)
#define		SHADOW_Z			(PLAYER_MIN_Z-48)
#define		SCREENPANEL_Z		(PLAYER_MIN_Z-49)
#define		PANEL_Z				(PLAYER_MIN_Z-50)
#define		PIT_DEPTH			-250
#define		P2_STATS_DIST		180
#define		CONTACT_DIST_H		30					// Distance to make contact
#define		CONTACT_DIST_V		12
#define		GRAB_DIST			36					// Grabbing ents will be placed this far apart.
#define		GRAB_STALL			(GAME_SPEED * 8 / 10)



#define		ATK_NORMAL			0
#define		ATK_NORMAL2			1
#define		ATK_NORMAL3			2
#define		ATK_NORMAL4			3
#define		ATK_BLAST			4
#define		ATK_BURN			5
#define		ATK_FREEZE			6
#define		ATK_SHOCK			7
#define		ATK_STEAL			8					// Steal opponents life
#define		ATK_NORMAL5			9
#define		ATK_NORMAL6			10
#define		ATK_NORMAL7			11
#define		ATK_NORMAL8			12
#define		ATK_NORMAL9			13
#define		ATK_NORMAL10		14




#define		SCROLL_RIGHT		0
#define		SCROLL_DOWN			1
#define		SCROLL_LEFT			2
#define		SCROLL_UP			3
#define		SCROLL_BOTH			4



#define		ANI_IDLE			0
#define		ANI_WALK			1
#define		ANI_JUMP			2
#define		ANI_LAND			3
#define		ANI_PAIN			4
#define		ANI_FALL			5
#define		ANI_RISE			6
#define		ANI_ATTACK1			7
#define		ANI_ATTACK2			8
#define		ANI_ATTACK3			9
#define		ANI_ATTACK4			10					// Very important
#define		ANI_UPPER			11
#define		ANI_BLOCK			12					// New block animation
#define		ANI_JUMPATTACK		13
#define		ANI_JUMPATTACK2		14
#define		ANI_GET				15
#define		ANI_GRAB			16
#define		ANI_GRABATTACK		17
#define		ANI_GRABATTACK2		18
#define		ANI_THROW			19
#define		ANI_SPECIAL			20
#define		ANI_FREESPECIAL		21
#define		ANI_SPAWN			22 					// 26-12-2004 new animation added here ani_spawn
#define		ANI_DIE				23					// 29-12-2004 new animation added here ani_die
#define		ANI_PICK			24					// 7-1-2005 used when players select their character at the select screen
#define		ANI_FREESPECIAL2	25
#define		ANI_JUMPATTACK3		26
#define		ANI_FREESPECIAL3	27
#define		ANI_UP				28					// Mar 2, 2005 - Animation for when going up
#define		ANI_DOWN			29					// Mar 2, 2005 - Animation for when going down
#define		ANI_SHOCK			30					// Animation played when knocked down by shock attack
#define		ANI_BURN			31					// Animation played when knocked down by burn attack
#define		ANI_SHOCKPAIN		32					// Animation played when not knocked down by shock attack
#define		ANI_BURNPAIN		33					// Animation played when not knocked down by shock attack
#define		ANI_GRABBED			34					// Animation played when grabbed
#define		ANI_SPECIAL2		35					// Animation played for when pressing forward special
#define		ANI_RUN				36					// Animation played when a player is running
#define		ANI_RUNATTACK		37					// Animation played when a player is running and presses attack
#define		ANI_RUNJUMPATTACK	38					// Animation played when a player is running and jumps and presses attack
#define		ANI_ATTACKUP		39					// u u animation
#define		ANI_ATTACKDOWN		40					// d d animation
#define		ANI_ATTACKFORWARD	41					// f f animation
#define		ANI_ATTACKBACKWARD	42					// Used for attacking backwards
#define		ANI_FREESPECIAL4	43					// More freespecials added
#define		ANI_FREESPECIAL5	44					// More freespecials added
#define		ANI_FREESPECIAL6	45					// More freespecials added
#define		ANI_FREESPECIAL7	46					// More freespecials added
#define		ANI_FREESPECIAL8	47					// More freespecials added
#define		ANI_RISEATTACK		48					// Attack used for enemies when players are crowding around after knocking them down
#define		ANI_DODGE			49					// Used for up up / down down SOR3 dodge moves for players
#define		ANI_ATTACKBOTH		50					// Used for when a player holds down attack and presses jump
#define		ANI_GRABFORWARD		51					//	New grab attack for when a player holds down forward/attack
#define		ANI_GRABFORWARD2	52					// New second grab attack for when a player holds down forward/attack
#define		ANI_JUMPFORWARD		53					// Attack when a player is moving and jumps
#define		ANI_GRABDOWN		54					// Attack when a player has grabbed an opponent and presses down/attack
#define		ANI_GRABDOWN2		55					// Attack when a player has grabbed an opponent and presses down/attack
#define		ANI_GRABUP			56					// Attack when a player has grabbed an opponent and presses up/attack
#define		ANI_GRABUP2			57					// Attack when a player has grabbed an opponent and presses up/attack
#define		ANI_SELECT			58					// Animation that is displayed at the select screen
#define		ANI_DUCK			59					// Animation that is played when pressing down in "platform" type levels
#define		ANI_FAINT			60  				//faint animations for players/enemys by tails
#define		ANI_CANT			61  				//cant animation for players(animation when mp is less than mpcost) by tails
#define		ANI_THROWATTACK		62					// Added for subtype projectile
#define		ANI_CHARGEATTACK	63
#define		ANI_VAULT			64  				// Now you can flip over people like in SOR
#define		ANI_JUMPCANT		65
#define		ANI_JUMPSPECIAL		66
#define		ANI_BURNDIE			67
#define		ANI_SHOCKDIE		68
#define		ANI_PAIN2			69
#define		ANI_PAIN3			70
#define		ANI_PAIN4			71
#define		ANI_FALL2			72
#define		ANI_FALL3			73
#define		ANI_FALL4			74
#define		ANI_DIE2			75
#define		ANI_DIE3			76
#define		ANI_DIE4			77
#define		ANI_CHARGE			78
#define		ANI_BACKWALK		79
#define		ANI_SLEEP			80
#define		ANI_FOLLOW1			81
#define		ANI_FOLLOW2			82
#define		ANI_FOLLOW3			83
#define		ANI_FOLLOW4			84
#define		ANI_PAIN5			85
#define		ANI_PAIN6			86
#define		ANI_PAIN7			87
#define		ANI_PAIN8			88
#define		ANI_PAIN9			89
#define		ANI_PAIN10			90
#define		ANI_FALL5			91
#define		ANI_FALL6			92
#define		ANI_FALL7			93
#define		ANI_FALL8			94
#define		ANI_FALL9			95
#define		ANI_FALL10			96
#define		ANI_DIE5			97
#define		ANI_DIE6			98
#define		ANI_DIE7			99
#define		ANI_DIE8			100
#define		ANI_DIE9			101
#define		ANI_DIE10			102
#define     ANI_TURN            103   // turn back/flip
#define     ANI_RESPAWN         104    //now spawn works for players

#define		MAX_ANIS			105					// max_anis increased for new ANIs



typedef struct{
	unsigned long	version;
	unsigned long	compatibleversion;
	int				gamma;
	int				brightness;
	int				usesound;						// Use SB
	int				soundrate;						// SB freq
	int				soundvol;						// SB volume
	int				usemusic;						// Play music
	int				musicvol;						// Music volume
	int				effectvol;						// Sound fx volume
	int				soundbits;						// SB bits
	int				usejoy;
	int				soundstereo;					// Stereo bits
	int				mode;							// Mode now saves
	int				unused1;
	int				unused2[2][8];
    int				windowpos;
	int				keys[4][9];
	int				showtitles;
	int				videoFile;						// To use or not video.txt

#if SDL
#ifndef GP2X
	int				fullscreen;						// Window or Full Screen Mode
	int				scalevideo;						// Scaled Video Resolution
#endif
#endif

#if PSP
	int				scalevideo;						// Scaled Video Resolution
	int				pspcpuspeed;					// PSP CPU Speed
	char			pspPakPath[MAXPATH];			// PSP Pak Location
#endif

#if PSP || SDL
	int				fps;							// Frames Per Second
#endif

#if PSP || DC
	int				memory;							// Memory CleanUp
	unsigned long	memoryThreshold;				// Memory Threshold for auto memory CleanUp
#endif
}s_savedata;



typedef struct{
	unsigned long	compatibleversion;
	char			dName[MAX_NAME_LEN+1];			// Difficulty Name
	int				level;							// Level Number
	int				stage;							// Stage
	int				pLives[4];						// Player Lives Left
	int				pCredits[4];					// Player Credits Left
	unsigned long	pScores[4];						// Player Scores
	int				credits;						// Number Of Credits
	int				times_completed;
	int				which_set;
}s_savelevel;



typedef struct{
	unsigned long	compatibleversion;
	unsigned long	highsc[10];						// 02-Mar-05 drikobruschi High score table code
	char			hscoren[10][MAX_NAME_LEN+1];	
}s_savescore;



typedef struct{
    int				numframes;
    int 			loop;
    int				tossframe;						// Used to determine which frame will toss a bomb/grenade
    int				throwframe;
    int				throwa;							//	Used for setting the "a" at which weapons are spawned
    char			custknife[MAX_NAME_LEN+1];
    char			custstar[MAX_NAME_LEN+1];
    char			custbomb[MAX_NAME_LEN+1];		// Used for new projectile bomb
    int				energycost;						// 1-10-05 to adjust the amount of energy used for specials
	int				mponly;							// Does energy get taken from magic bar, lifebar or both?
    int				shootframe;
    int				jumpframe;
    int				moveflag;						// So movement forward can be specified for jumpframes
    int				soundframe;
    int				soundtoplay;
    float			dive[2];						// new dive kick by tails
    int				fastattack;						// Flag to determine if the opponent uses their pain time
    int				hitsound;					// Sound effect to be played when attack hits opponent
    char			hitflash[MAX_NAME_LEN+1];		// Custom flash for each animation
    int				sprite[ANI_MAX_FRAMES];
    int				delay[ANI_MAX_FRAMES];
    int				move[ANI_MAX_FRAMES];
    int				movez[ANI_MAX_FRAMES];
    int				movea[ANI_MAX_FRAMES];
    int				seta[ANI_MAX_FRAMES];			// Now characters can have a custom "a" value
    int				vulnerable[ANI_MAX_FRAMES];
    int				bbox_coords[ANI_MAX_FRAMES][4];
    int				attack_coords[ANI_MAX_FRAMES][4];
    int				attack_force[ANI_MAX_FRAMES];
    int				attack_drop[ANI_MAX_FRAMES];
    int				attack_type[ANI_MAX_FRAMES];
    int				attack_length[ANI_MAX_FRAMES];	// How long an attack effect lasts
    int				no_block[ANI_MAX_FRAMES];		// Flag to determine if an attack is blockable (default 0 - blockable)
    int				no_flash[ANI_MAX_FRAMES];		// Flag to determine if an attack spawns a flash or not
    int				pause_add[ANI_MAX_FRAMES];		// Flag to determine if an attack adds a pause before updating the animation
    float			platform[ANI_MAX_FRAMES][8];	// Now entities can have others land on them
    int				range[2];						// Use for attacks
	int				flipframe;						// Turns entities around on the desired frame
    int				followanim;						// use which FOLLOW anim?
    int				followcond;						// conditions under which to use a followup
    int				counterframe;					// frame in which a counter is possible
    int				countercond;					// conditions to switch to a FOLLOW anim
    int				counterdamage;					// You can specify if counterattackers take damage
	int				weaponframe[2];					// Specify with a frame when to switch to a weapon model
	int				quakeframe[3];					// Specify with a frame, repeat, quake (4 is highest)
	char            subentity[MAX_NAME_LEN+1];      // Store the sub-entity's name for further use
	int             spawnframe[5];                  // Spawn the subentity as its default type. {frame} {x} {z} {a} {relative?}
	int             summonframe[5];                 // Summon the subentity as an ally, only one though {frame} {x} {z} {a} {relative?}
	int             unsummonframe;                  // Un-summon the entity
}s_anim;



typedef struct{
    char			name[MAX_NAME_LEN+1];
    int				health;
	int				mp;								// mp's variable for mpbar by tails
    int				counter;						// counter of weapons by tails
    int				shootnum;	 					// counter of shots by tails
    int				reload; 						// reload max shots by tails
    int				typeshot; 						// see if weapon is a gun or knife by tails
    int				animal;  						// see is the weapon is a animal by tails
	int 			nolife;	 						// Feb 25, 2005 - Variable flag to show life 0 = no, else yes
	int				makeinv;	 					// Option to spawn player invincible
	int				specpower;	 					// Mar 12, 2005 - Specify enemy damage by smartbomb
	int				spectype;						// Apr 15, 2005 - Specify which type of attack will be used
	int				speclen;						// Apr 15, 2005 - Specify the duration of a freeze smartbomb
	int				dofreeze;						// Flag to freeze all enemies/players while special is executed
	int				noquake;						// Flag to make the screen shake when entity lands 1 = no, else yes
	int				hitenemy;						// Flag to determine if enemy projectiles will hit enemies as well
	int				ground;							// Flag to determine if enemy projectiles only hit the enemy when hitting the ground
    int				score;
    int				multiple;						// So you can control how many points are given for hitting opponents
    int				type;
    int				subtype;
    int				icon;
    int   			iconpain;   					// 20-1-2005   New icons
    int				iconget;    					// 20-1-2005   New icons
    int				icondie;     					// 20-1-2005   New icons
    int				iconw;							// icon for the weapon like in beat of fighting by tails
	int				iconmp[3];						// icon for the mpbar 3 levels
    int				parrow[4][3];					// Image to be displayed when player spawns invincible
    int				shadow;
    int				aironly;						// Used to determine if shadows will be shown when jumping only
    int				setlayer;						// Used for forcing enities to be displayed behind
    int				fmap;							// Corresponds to which remap to use for when a character is frozen
    int				alpha;							// New alpha variable to determine if the entity uses alpha transparency
    int				toflip;							// Flag to determine if flashes flip or not
    int				thold;							// The entities threshold for block
    int				nomove;							// Flag for static enemies
    int				noflip;							// Flag to determine if static enemies flip or stay facing the same direction
    int				nodrop;							// Flag to determine if enemies can be knocked down
    int				nodieblink;						// Flag to determine if blinking while playing die animation
    int				blockodds;						// Odds that an enemy will block an attack (1 : blockodds)
    int				holdblock;
    float			runspeed;						// The speed the character runs at
    float			runjumpheight;					// The height the character jumps when running
    float			runjumpdist;					// The distance the character jumps when running
    int				runupdown;						// Flag to determine if a player will continue to run while pressing up or down
    int				runhold;						// Flag to determine if a player will continue to run if holding down forward when landing
    int				remove;							// Flag to remove a projectile on contact or not
    int				noatflash;						// Flag to determine if attacking characters attack spawns a flash
    float			throwheight;					// The height at which an opponent can now be adjusted
    float			throwdist;						// The distance an opponent can now be adjusted
    int             throwframewait;                 // The frame victim is thrown during ANIM_THROW, added by kbandressen 10/20/06
    int				special[MAX_SPECIALS][5];		// Stores freespecials
    int				specials_loaded;				// Stores how many specials have been loaded
    int				valid_special;					// Used for setting when a valid special has been found
    int				diesound;
    int				secret;
    int				weapnum;
    char			weapon[MAX_WEAPONS][MAX_NAME_LEN+1];
	int				weaploss;						// Determines possibility of losing weapon. 
    char			project[MAX_NAME_LEN+1];
    char			rider[MAX_NAME_LEN+1];			// 7-1-2005 now every "biker" can have a new driver!
    char			knife[MAX_NAME_LEN+1];			// 7-1-2005 now every enemy can have their own "knife" projectile
    char			star[MAX_NAME_LEN+1];			// 7-1-2005 now every enemy can have their own "ninja star" projectiles
    char			bomb[MAX_NAME_LEN+1];			// New projectile type for exploding bombs/grenades/dynamite
    char			flash[MAX_NAME_LEN+1];			// Now each entity can have their own flash
    char			bflash[MAX_NAME_LEN+1];			// Flash that plays when an attack is blocked
    char			dust[MAX_NAME_LEN+1];			// New "dust" entity spawns when entity hits the ground
    int				height;							// Used to set height of player in pixels
    float			speed;
    float			grabdistance;					// 30-12-2004	grabdistance varirable adder per character
    float			jumpheight;						// 28-12-2004	Jump height variable added per character
    int 			cantgrab;						// 27-12-2004	can't grab variable added per character
    int				grabback;						// Flag to determine if entities grab images display behind opponenets
    int				death; 							// 6-2-2005 die animation?
    int				throwdamage;					// 1-14-05  adjust throw damage
    int				maps_loaded;					// Used for player colourmap selecting
    char			*colourmap[MAX_COLOUR_MAPS];
    s_anim			*animation[MAX_ANIS];
    int				atchain[MAX_ATCHAIN];			// For ATTACK4.
    int			    chainlength;
	int				nopain;
    int				credit;
    int				escapehits;						// Escape spammers!
    int				chargerate;						// For the charge animation
    int				mprate;							// For time-based mp recovery.
    int				aggression;						// For enemy A.I.
    int				risetime;						// For enemy A.I.
    int				sleepwait;
	int				ptype;							// Projectile Type: knife, playershot, playershotno, etc...
	int				blockpain;
	int				paingrab;						// Can only be grabbed when in pain
	int             facing;                         // see entity
	int             turndelay;                      // turn delay
	int             lifespan;                       // lifespan count down
	int             summonkill;                     // kill it's summoned entity when died
                                                    // 0. dont kill 1. kill summoned only 2. kill all spawned entity
    //---------------new A.I. switches-----------
    int             hostile;                        // specify hostile types
    int             candamage;                      // specify types that can be damaged by this entity
    int             projectilehit;                  // specify types that can be hit by this entity if it is thrown
    
    int             aimove;                         // move tyle
    int             aiattack;                       // attack/defend style
    
    //--------------new property for endlevel item--------
    char            branch[MAX_NAME_LEN + 1];       //level branch name
}s_model;




typedef struct{										// Cache list entry for models
    char			name[MAX_NAME_LEN+1];
    char			path[256];
}s_modelcache;



typedef struct entity{
    int				exists;
    int				health;
    int				mp;								// mp's variable for mpbar by tails
    int				counter;						// counter of weapons by tails
    int				shootnum;						// counter of shots by tails
    int				reload;							// reload max shoots by tails
    int				score;							// So the score value can be overridden for enemies/obstacles
    int				multiple;						// So the multiple can be overridden for enemies/obstacles
    int				oldhealth;
    int				maxhealth;
    int				oldmp;							//mp's variable for mp for players by tails
    int				maxmp;							//mp's variable for mp for players by tails
    int				type;
    int				playerindex;
    char			name[MAX_NAME_LEN+1];
    char			item[MAX_NAME_LEN+1];
    char			itemalias[MAX_NAME_LEN+1];		// Now items spawned can have their properties changed
    int				if2p;
    int	        	if3p; //4player
    int	          	if4p; //4player    
    int				itemmap;						// Now items spawned can have their properties changed
    int				itemhealth;						// Now items spawned can have their properties changed
    float			x;								// X
    float			z;								// Depth
    float			a;								// Altitude
    float			xdir;
    float			zdir;
    float			base;							// Default altitude
    float           altbase;                        //altitude affected by movea
    float			tossv;							// Effect of gravity
    unsigned long	toss_time;						// Used by gravity code
    int				hithead;						// Flag for when a player jumps and hits head on the bottom of a platform
    int				direction;						// 0=left 1=right
    int				layer;							// Used to improve layering when entities are on the same "z"
    int				boss;
	int				dying;							// Coresponds with which remap is to be used for the dying flash
	int				per1;							// Used to store at what health value the entity begins to flash
	int				per2;							// Used to store at what health value the entity flashes more rapidly
	int				nolife;							// Flag to determine if health/icon/name is displayed when hit
	int				noquake;						// Flag to determine if an entity will cause the screen to shake upon landing
    int				makeinv;						// Option to spawn player invincible (1 = yes, else no)
    int				nograb;							// Some enemies cannot be grabbed (bikes) - now used with cantgrab as well
    unsigned long	stalltime;
    unsigned long	combotime;						// For multiple-hit combo
    int				combostep;
    int				combostep2;						// Now you can have forward attack grab attacks
    int				combostep3;						// Now you can have up attack grab attacks
    int				combostep4;						// Now you can have down attack grab attacks
    unsigned long	movetime;						// For special move
    unsigned long	lastmove;
    int				movestep;
    unsigned long	releasetime;
    int				jumping;						// Stuff useful for AI
    int				attacking;
    int				getting;
    int				running;						// Flag to determine if a player is running
    int				projectile;
    int				blasted;
    int				frozen;							// Flag to determine if an entity is frozen
    unsigned long	freezetime;						// Used to store at what point the a frozen entity becomes unfrozen
    unsigned long	magictime;
    int				toexplode;						// Needed to determine if the projectile is a type that will explode (bombs, dynamite, etc)
    int				damage_on_landing;
    s_model 		*model;
    s_anim			*animation;
    int				animpos;
    int				lastanimpos;					// Used by AI
    unsigned long	nextanim;
    int				currentsprite;
    int				animating;						// Set by animation code
    int				autokill;						// Kill on end animation
    void			(*think)();
    unsigned long	nextthink;
    void			(*takedamage)(struct entity*,int,int,int);
    int				attack_id;
    int				hit_by_attack_id;
    int				remove_on_attack;
    unsigned long	pain_time;
    int				blink;
	int				invincible;						// Flag used to determine if player is currently invincible
	int				invinctime;						// Used to set time for invincibility to expire
	int				arrowon;						// Flag to display parrow/parrow2 or not
	int				tocost;							// Flag to determine if special costs life if doesn't hit an enemy
	int				cantfire;						// Flag to determine if another shot can be fired that costs energy
	int				animnum;						// Added for nocost magic
    int				screen;
    char			*colourmap;
    char			*map;							// Stores the colourmap for restoring purposes
    struct entity	*link;							// Used to link 2 entities together.
    struct entity	*owner;							// Added for "hitenemy" flag so projectile recognizes its owner
    struct entity	*grabbing;						// Added for "platform level" layering
    struct entity	*weapent;
    struct entity*  parent;                          //It's spawner
    struct entity*  subentity;                       //store the sub entity
	int				credit;
    int				itemtrans;
    int				escapecount;					// For escapehits
    unsigned long   mpchargetime;					// For the CHARGE animation
    unsigned long   sleeptime;						// For the SLEEP animation
    int				aggression;						// For enemy A.I.
	int				ptype;							// Projectile Type: knife, playershot, playershotno, etc...
	int             facing;                         // 0 no effect, 1 alway right, 2 always left, 3, affected by level dir
    unsigned long   turntime;                       // time to turn back
	int				rush[2];						// rush combo and max combo
	unsigned long	rushtime;						// rush combo timer
	int             lifespancountdown;              // life span count down
	//----------A.I. control variants----------------
	// copied from model so we might change them later without changing the model itself
	int             hostile;
	int             candamage;
	int             projectilehit;
	
}entity;



typedef struct{
    char			name[MAX_NAME_LEN+1];
    s_anim			*animation[MAX_ANIS];
    int				colourmap;
    unsigned long	score;
    int				lives;
	int				credits;
    int				iconmode;
    entity			*ent;
    entity			*opponent;
    unsigned long	keys;
    unsigned long	newkeys;
    unsigned long	playkeys;
    int				spawnhealth;
	int				spawnmp;						// Used for the mp bar
    int				joining;
    int				hasplayed;
	int				weapnum;
}s_player;



typedef struct{
    s_sprite		*sprite_normal;
    s_sprite		*sprite_neon;
    s_sprite		*sprite_screen;
}s_panel;



typedef struct{
    int				at;
    int				wait;
    int             nojoin;                     // dont allow new hero to join
    int				groupmin;
    int				groupmax;
    char			name[MAX_NAME_LEN+1];
    char			alias[MAX_NAME_LEN+1];
    char			item[MAX_NAME_LEN+1];
    char			itemalias[MAX_NAME_LEN+1];
    int				itemhealth;
    int				itemmap;
    int				if2p;
	int				if3p; //4player
	int				if4p; //4player

       
    int				alpha;							// Used for alpha effects
    int				spawn2p;						// If set to 1, will only be spawned if 2 players are playing
    int				spawn3p;  //4player
    int				spawn4p; //4player
    int				health2p;						// If set, will change the health to increase the challenge for 2 players
    int				health3p; //4player
    int				health4p; //4player
    int				health;
    int				mp;								// mp's variable for mpbar by tails
    int				score;							// So score can be overridden for enemies/obstacles
    int				multiple;						// So score can be overridden for enemies/obstacles
    int				x;
    int				z;
    int				a;
    int				colourmap;
	int				dying;							// Used for the dying flash animation
	int				per1;							// Used to store at what health value the entity begins to flash
	int				per2;							// Used to store at what health value the entity flashes more rapidly
	int				nolife;							// So nolife can be overriden for all characters
    int				boss;
    int				flip;
	int				credit;
    int				itemtrans;
    int				aggression;						// For enemy A.I.
    char            weapon[MAX_NAME_LEN+1];         // spawn with a weapon, 2007-2-12   by UTunnels
}s_spawn_entry;



typedef struct{
    char            branchname[MAX_NAME_LEN+1];      //2007-2-22, use a name so we can find this level in branches
    char			filename[128];
    int				gonext;                          //2007-2-24 0. dont complete this level and display score,  
                                                      //1. complete level and display score, 
                                                      //2. complete game, show hall of fame
    int				is_scene;
    int				z_coords[3];					// Used for setting custom "z"
}s_level_entry;



typedef struct{
    int				numspawns;
    s_spawn_entry	spawnpoints[LEVEL_MAX_SPAWNS];
    int				numpanels;
    int				order[LEVEL_MAX_PANELS];
    int				numholes;
    int				numwalls;						// Stores number of walls loaded
    float			holes[LEVEL_MAX_HOLES][7];
    float			walls[LEVEL_MAX_WALLS][8];		// Now you can have walls for different walkable areas
    int				exit_blocked;
    int				exit_hole;
    int				scrolldir;
    int				width;
    int				rocking;
    float			bgspeed;						// Used to make autoscrolling backgrounds
    int				bgdir;							// Used to set which direction the backgrounds scroll for autoscrolling backgrounds
    int				mirror;
    char			bossmusic[256];
    int 			settime;						// Set time limit per level
    int				notime;							// Used to specify if the time is displayed 1 = no, else yes
    int				noreset;						// If set, clock will not reset when players spawn/die
    int 			type;							// Used to specify which level type (1 = bonus, else regular)
    int				nospecial;						// Used to specify if you can use your special during bonus levels
    int				nohurt;							// Used to specify if you can hurt the other player during bonus levels
    int				noslow;							// Flag so the level doesn't slow down after a boss is defeated
    int				nohit;							// Not able to grab / hit other player on a per level basis
    int				spawn[4][4];					// Used to determine the spawn position of players
    int				setweap;						// Levels can now specified which weapon will be used by default
    int             facing;                         // Force the players to face to ... 0 no effects, 1 right, 2 left, 3 affected by level dir
}s_level;



typedef struct{
    int				index;
    char			filename[MAX_FILENAME_LEN];
}s_soundcache;



typedef struct{
    char			filename[MAX_FILENAME_LEN];
	int				ofsx;
    int				ofsy;
}s_spritecache;



s_savedata		savedata;
s_savelevel		savelevel[MAX_DIFFICULTIES];
s_savescore		savescore;
int				saveslot;


char			*stringtable[MAX_STRING_TABLE];



char			pal[768];
char			*lut_mul		= NULL;
char			*lut_screen		= NULL;
char			*lut_overlay	= NULL;
char			*lut_hl			= NULL;
char			*lut_dodge		= NULL;
char			*lut_half		= NULL;

s_screen		*vscreen		= NULL;
s_screen		*background		= NULL;
s_screen		*rescreen		= NULL;
s_level			*level			= NULL;
s_bitmap		*texture		= NULL;


int				fade			= 24;			// 13-1-2005 fade speed selection
int				credits;
int				GAME_SPEED		= 200;			// 13-1-2005 Game speed is now changable.
int				gosound			= 0;			// 26-12-2004 - Used to prevent go sound playing too frequently,
int				musicoverlap	= 0;
int				colorbars		= 0;
int				slo				= 0;			// 4-7-2006 update timer fix defaults to 0
int				levelpos;
float			advancex;
float			advancey;
unsigned long	advancetime;
int				current_spawn;
int				level_waiting;
int             nojoin;                         // dont allow new hero to join in, use "Please Wait" instead of "Select Hero"
int				groupmin, groupmax;
int				selectScreen;					// Flag to determine if at select screen (used for setting animations)
int				quake			= 0;
unsigned long	quaketime;
unsigned long	go_time;
int				level_completed;
int				tospeedup;						// If set will speed the level back up after a boss hits the ground
int 			reached[4];	// Used with TYPE_ENDLEVEL to determine which players have reached the point //4player
int				noslowfx;						// Flag to determine if sound speed when hitting opponent slows or not
int				hiscorebg;						// If set to 1, will look for a background image to display at the highscore screen
int				completebg;						// If set to 1, will look for a background image to display at the showcomplete screen
int				unlockbg;						// If set to 1, will look for a different background image after defeating the game
int				pause;
int				endgame;
int				forcecheatsoff	= 0;
int				cheats			= 0;
int				livescheat		= 0;
int				creditscheat	= 0;
int				healthcheat		= 0;
int				showtimeover;
char 			select_scene[4][128]; //4player
int				sameplayer		= 0;			// 7-1-2005  flag to determine if players can use the same character
int				PLAYER_LIVES	= 3;			// 7-1-2005  default setting for Lives
int				CONTINUES		= 5;			// 7-1-2005  default setting for continues
int				colourselect	= 0;			//6-2-2005 Colour select is optional
int				autoland		= 0;			// Default set to no autoland and landing is valid with u j combo
int				ajspecial		= 0;			// Flag to determine if holding down attack and pressing jump executes special
int				nolost			= 0;			// variable to control if drop weapon when grab a enemy by tails
int				nocost			= 0;			// If set, special will not cost life unless an enemy is hit
int             mpstrict        = 0;            // If current system will check all animation's energy cost when set new animations
int				magic_type		= 0;			// use for restore mp by time by tails
int				freezeall		= 0;			// Flag to determine if all entities should not be able to move while a special is executed
//int             nojoin          = 0;            // Use Wait instead of Select Hero, maybe in cut scene
int				plife[4][2];					// Used for customizable player lifebar
int				plifeX[4][2];					// Used for customizable player lifebar 'x'
int				plifeN[4][2];					// Used for customizable player lifebar number of lives
int				picon[4][2];					// Used for customizable player icon
int				piconw[4][2];					// Used for customizable player weapon icons
int				mpicon[4][2];					// Used for customizable magicbar player icon
int				pnameJ[4][6];					// Used for customizable player name, Select Hero, (Credits, Press Start, Game Over) when joining
int				pscore[4][6];					// Used for customizable player name, dash, score
int				pshoot[4][2];					// Used for customizable player shootnum
int				prush[4][8];					// Used for customizable player combo/rush system
int				psmenu[4][4];					// Used for customizable player placement in select menu
int				lbarsize[3]		= {0,0,0};		// Used for customizable lifebar size
int				timeloc[5]		= {0,0,0,0,0};	// Used for customizable timeclock location/size
int				timeicon[3]		= {0,0,0};		// Used for customizable time icon
int				elife[4][2];					// Used for customizable enemy lifebar
int				ename[4][2];					// Used for customizable enemy name
int				eicon[4][2];					// Used for customizable enemy icon
int				scomplete[6]	= {0,0,0,0,0,0};// Used for customizable Stage # Complete
int				cbonus[10];						// Used for customizable clear bonus
int				lbonus[10];						// Used for customizable life bonus
int				tscore[10];						// Used for customizable total score
int				noshare;						// Used for when you want to keep p1 & p2 credits separate
int				maxplayers		= 2;			// 7-1-2005  flag to set single player mode
int				ctrlmaxplayers	= 0;			// Disable or Enable user control of maxplayers in options.
int				nodropen;						// Drop or not when spawning is now a modder option
int				gfx_y_offset	= 0;
unsigned long	time			= 0;
int				timeleft;
int				holez;							// Used for setting spawn points
int				allow_secret_chars = 0;
int				lifescore		= 50000;		// Number of points needed to earn a 1-up
int				credscore		= 0;			// Number of points needed to earn a credit
int				mpblock			= 0;			// Take chip damage from health or MP first?
int				blockratio		= 0;			// Take half-damage while blocking?
int				nochipdeath		= 0;			// Prevents entities from dying due to chip damage (damage while blocking)
int				noaircancel;					// Now, you can make jumping attacks uncancellable!
int				mpbarsize[3]	= {0,0,0};		// Used for customizable mpbar size
int				pmp[4][2];						// Used for customizable player mpbar
int				spdirection[4]	= {1,0,1,0};	// Used for Select Player Direction for select player screen
int				bonus			= 0;			// Used for unlocking Bonus difficulties
int				versusdamage	= 2;			// Used for setting mode. (ability to hit other players)

s_level_entry	*levelorder[MAX_DIFFICULTIES][MAX_LEVELS];
unsigned int	num_levels[MAX_DIFFICULTIES];
unsigned int	ifcomplete[MAX_DIFFICULTIES];
char			set_names[MAX_DIFFICULTIES][MAX_NAME_LEN+1];
unsigned int	num_difficulties;
unsigned int	difflives[MAX_DIFFICULTIES];	// 7-1-2005 what too easy?  change the # of lives players get
unsigned int	custfade[MAX_DIFFICULTIES];		// 8-2-2005 custom fade
unsigned int	diffcreds[MAX_DIFFICULTIES];	// 7-1-2005 What still to easy - lets see how they do without continues!
unsigned int	diffoverlap[MAX_DIFFICULTIES];	// 9-2-2005 music overlap
int				same[MAX_DIFFICULTIES];			// ltb 1-13-05   sameplayer
int				z_coords[3];					// Used for setting customizable walkable area
char            branch_name[MAX_NAME_LEN+1];    // Used for branches
unsigned int	typemp[MAX_DIFFICULTIES];
int				rush[6]			= {0, 2, 3, 3, 3, 3};
char			rush_names[2][MAX_NAME_LEN];



s_panel			panels[MAX_PANELS];
unsigned int	panels_loaded	= 0;
int				panel_width		= 0;
int             panel_height    = 0;

s_sprite		*frontpanels[MAX_PANELS];
unsigned int	frontpanels_loaded = 0;

s_sprite		*sprites[2][MAX_SPRITES];
s_spritecache	spritecache[MAX_SPRITES];
unsigned int	sprites_loaded	= 0;

s_model			*model_list[MAX_MODELS];
unsigned int	models_loaded	= 0;

s_modelcache	*model_cache[MAX_MODELS];
unsigned int	models_cached	= 0;

s_anim			*anim_list[MAX_ANIMS];
unsigned int	anims_loaded	= 0;

entity			*ent_list[MAX_ENTS];
entity			*self;

s_soundcache	soundcache[MAX_SOUND_CACHE];

s_player		player[4];
unsigned long	bothkeys, bothnewkeys;


s_playercontrols	playercontrols1;
s_playercontrols	playercontrols2;
s_playercontrols	playercontrols3;
s_playercontrols	playercontrols4;
s_playercontrols	*playercontrolpointers[] = {&playercontrols1, &playercontrols2, &playercontrols3, &playercontrols4};



// Funny neon lights
char			neontable[256];
unsigned long	neon_time		= 0;


float			lasthitx, lasthitz, lasthita;



// Special sprites
int				shadowsprites[6];
int				gosprite;
int				golsprite;						// ltb 1-17-05 new sprite for left direction
int				holesprite;



// 7-1-2005 Some colours used here and there added more colors
int				color_black		= 0;
int				color_red		= 0;
int				color_orange	= 0;
int				color_yellow	= 0;
int				color_white		= 0;
int				color_blue		= 0;
int				color_green		= 0;
int				color_pink		= 0;
int				color_purple	= 0;
int				color_magic		= 0;
int				color_magic2	= 0;



int smp_go;							// 26-12-2004 - New sample created - Go sample
int smp_beat;
int	smp_block;	// Sample for when an attack is blocked (defaults to the same as smp_beat)
int smp_indirect;
int smp_get;
int smp_get2;
int smp_fall;
int smp_jump;
int smp_punch;
int smp_1up;
int smp_timeover;
int smp_beep;
int smp_beep2;
int smp_bike;


int videoMode	= 0;
int	forceVideoMode  = 0;

int hRes1		= 320;	// Gameplay Video
int vRes1		= 240;	// Gameplay Video
int hRes2		= 320;	// Scenes, Titles, Background, Select Screen
int vRes2		= 240;	// Scenes, Titles, Background, Select Screen
int hOffset		= 0;	// Offset for X-Axis Text
int xOffset		= 0;	// Offset for X-Axis Text for VideoMode 0
int vOffset		= 0;	// Offset for Y-Axis Text
int yOffset		= 0;	// Offset for Y-Axis Text for VideoMode 2
int dOffset		= 231;	// Offset for Debug Text
int shiftpos[4] = {0,0,0,0};

char customScenes[128]		= {"empty"};
char customBackgrounds[128] = {"empty"};
char customLevels[128]		= {"empty"};


// Required prototypes
void shutdown(char *, ...);
void options(void);
void system_options(void);
void soundcard_options(void);
void free_modelcache(void);
void free_models(void);
void load_special_sprites(void);
void freesprites(void);
int load_models(void);
int is_set(s_model * model, int m);


#if PSP || SDL
void load_videomode(int set);
#endif



// ------------------------ Save/load -----------------------------

void clearsettings(void){
    savedata.version = VERSION;
    savedata.compatibleversion = COMPATIBLEVERSION;
    savedata.gamma = 0;
    savedata.brightness = 0;
    savedata.usesound = 1;
	savedata.soundrate = 44100;
	savedata.soundbits = 16;
	savedata.soundvol = 15;
	savedata.usemusic = 1;
	savedata.musicvol = 100;
	savedata.effectvol = 120;
    savedata.usejoy = 1;
    savedata.mode = 0;
	savedata.showtitles = 0;
	savedata.windowpos = 0;

#if DC || DOS || SDL || XBOX
	savedata.soundstereo = 0;
	savedata.videoFile = 0;
#if SDL
#ifndef GP2X
	savedata.fullscreen = 1;
	savedata.scalevideo = 0;
#endif
#endif
#endif

#if PSP || SDL
	savedata.fps = 0;
#endif

#if PSP
	savedata.soundstereo = 1;
	savedata.scalevideo = 0;
	savedata.videoFile = 1;
	savedata.pspcpuspeed = 1;
	strncpy(savedata.pspPakPath,"ms0:/PSP/",MAXPATH);
#endif

#if PSP || DC
	savedata.memory = 0;
	savedata.memoryThreshold = 4000000;
#endif

	savedata.keys[0][SDID_MOVEUP]    = CONTROL_DEFAULT1_UP;
	savedata.keys[0][SDID_MOVEDOWN]  = CONTROL_DEFAULT1_DOWN;
	savedata.keys[0][SDID_MOVELEFT]  = CONTROL_DEFAULT1_LEFT;
	savedata.keys[0][SDID_MOVERIGHT] = CONTROL_DEFAULT1_RIGHT;
	savedata.keys[0][SDID_SPECIAL]   = CONTROL_DEFAULT1_FIRE3;
	savedata.keys[0][SDID_ATTACK]    = CONTROL_DEFAULT1_FIRE1;
	savedata.keys[0][SDID_JUMP]      = CONTROL_DEFAULT1_FIRE2;
	savedata.keys[0][SDID_START]     = CONTROL_DEFAULT1_START;
	savedata.keys[0][SDID_SCREENSHOT]= CONTROL_DEFAULT1_SCREENSHOT;

	savedata.keys[1][SDID_MOVEUP]    = CONTROL_DEFAULT2_UP;
    savedata.keys[1][SDID_MOVEDOWN]  = CONTROL_DEFAULT2_DOWN;
    savedata.keys[1][SDID_MOVELEFT]  = CONTROL_DEFAULT2_LEFT;
    savedata.keys[1][SDID_MOVERIGHT] = CONTROL_DEFAULT2_RIGHT;
    savedata.keys[1][SDID_SPECIAL]   = CONTROL_DEFAULT2_FIRE3;
    savedata.keys[1][SDID_ATTACK]    = CONTROL_DEFAULT2_FIRE1;
    savedata.keys[1][SDID_JUMP]      = CONTROL_DEFAULT2_FIRE2;
    savedata.keys[1][SDID_START]     = CONTROL_DEFAULT2_START;
    savedata.keys[1][SDID_SCREENSHOT]= CONTROL_DEFAULT2_SCREENSHOT;

	savedata.keys[2][SDID_MOVEUP]    = CONTROL_DEFAULT3_UP;
	savedata.keys[2][SDID_MOVEDOWN]  = CONTROL_DEFAULT3_DOWN;
	savedata.keys[2][SDID_MOVELEFT]  = CONTROL_DEFAULT3_LEFT;
	savedata.keys[2][SDID_MOVERIGHT] = CONTROL_DEFAULT3_RIGHT;
	savedata.keys[2][SDID_SPECIAL]   = CONTROL_DEFAULT3_FIRE3;
	savedata.keys[2][SDID_ATTACK]    = CONTROL_DEFAULT3_FIRE1;
	savedata.keys[2][SDID_JUMP]      = CONTROL_DEFAULT3_FIRE2;
	savedata.keys[2][SDID_START]     = CONTROL_DEFAULT3_START;
	savedata.keys[2][SDID_SCREENSHOT]= CONTROL_DEFAULT3_SCREENSHOT;

	savedata.keys[3][SDID_MOVEUP]    = CONTROL_DEFAULT4_UP;
    savedata.keys[3][SDID_MOVEDOWN]  = CONTROL_DEFAULT4_DOWN;
    savedata.keys[3][SDID_MOVELEFT]  = CONTROL_DEFAULT4_LEFT;
    savedata.keys[3][SDID_MOVERIGHT] = CONTROL_DEFAULT4_RIGHT;
    savedata.keys[3][SDID_SPECIAL]   = CONTROL_DEFAULT4_FIRE3;
    savedata.keys[3][SDID_ATTACK]    = CONTROL_DEFAULT4_FIRE1;
    savedata.keys[3][SDID_JUMP]      = CONTROL_DEFAULT4_FIRE2;
    savedata.keys[3][SDID_START]     = CONTROL_DEFAULT4_START;
    savedata.keys[3][SDID_SCREENSHOT]= CONTROL_DEFAULT4_SCREENSHOT;
}


void savesettings(void){
#ifndef DC
	FILE *handle = NULL;
	char path[128] = {""};
	getBasePath(path, "Saves", 0);
	strncat(path, "settings.sav", 128);
    handle = fopen(path, "wb");
	if(handle==NULL) return;
	fwrite(&savedata, 1, sizeof(s_savedata), handle);
	fclose(handle);
#endif
}

void loadsettings(void){
#ifndef DC
	FILE *handle = NULL;
	char path[128] = {""};
	getBasePath(path, "Saves", 0);
	strncat(path, "settings.sav", 128);
	clearsettings();
    handle = fopen(path, "rb");
	if(handle == NULL) return;
	fread(&savedata, 1, sizeof(s_savedata), handle);
	fclose(handle);
	if(savedata.compatibleversion != COMPATIBLEVERSION) clearsettings();

#if SDL
#ifndef GP2X
	if(!savedata.fullscreen) fullscreen = 0;
	if(savedata.scalevideo) scaleVideo = savedata.scalevideo;
#endif
#endif
#if PSP
	if(savedata.scalevideo) scaleVideo = savedata.scalevideo;
#endif

#else
	clearsettings();
#endif
}



void clearSavedGame(void){
	int i;
	
	for(i=0; i<MAX_DIFFICULTIES; i++){
		savelevel[i].compatibleversion = CV_SAVED_GAME;
		strncpy(savelevel[i].dName, "", MAX_NAME_LEN+1);
		savelevel[i].level = 0;
		savelevel[i].stage = 1;
		savelevel[i].credits = 0;
		savelevel[i].times_completed = 0;
		savelevel[i].pLives[0] = 0;
		savelevel[i].pLives[1] = 0;
		savelevel[i].pLives[2] = 0;
		savelevel[i].pLives[3] = 0;
		savelevel[i].pCredits[0] = 0;
		savelevel[i].pCredits[1] = 0;
		savelevel[i].pCredits[2] = 0;
		savelevel[i].pCredits[3] = 0;
		savelevel[i].pScores[0] = 0;
		savelevel[i].pScores[1] = 0;
		savelevel[i].pScores[2] = 0;
		savelevel[i].pScores[3] = 0;
	}
}



void clearHighScore(void){
	savescore.compatibleversion = CV_HIGH_SCORE;
	savescore.highsc[0] = 0;	// Resets all the highscores to 0
    savescore.highsc[1] = 0;
    savescore.highsc[2] = 0;
    savescore.highsc[3] = 0;
    savescore.highsc[4] = 0;
    savescore.highsc[5] = 0;
    savescore.highsc[6] = 0;
    savescore.highsc[7] = 0;
    savescore.highsc[8] = 0;
    savescore.highsc[9] = 0;
	strcpy(savescore.hscoren[0], "None");	// Resets all the highscore names to "None"
	strcpy(savescore.hscoren[1], "None");
	strcpy(savescore.hscoren[2], "None");
	strcpy(savescore.hscoren[3], "None");
	strcpy(savescore.hscoren[4], "None");
	strcpy(savescore.hscoren[5], "None");
	strcpy(savescore.hscoren[6], "None");
	strcpy(savescore.hscoren[7], "None");
	strcpy(savescore.hscoren[8], "None");
	strcpy(savescore.hscoren[9], "None");
}



void saveGameFile(void){
#ifndef DC
	FILE *handle = NULL;
	char path[256] = {""};
	char tmpname[256] = {""};
	getBasePath(path, "Saves", 0);
	getPakName(tmpname, 0);
	strcat(path, tmpname);
	if(!savelevel[saveslot].level) return;
    handle = fopen(path, "wb");
    if(handle == NULL) return;
    fwrite(&savelevel, 1, sizeof(s_savelevel), handle);
    fclose(handle);
#endif
}


int loadGameFile(void){
#ifndef DC
	FILE *handle = NULL;
	int i;
	char path[256] = {""};
	char tmpname[256] = {""};
	getBasePath(path, "Saves", 0);
	getPakName(tmpname, 0);
    strcat(path,tmpname);
    handle = fopen(path, "rb");
    if(handle == NULL) return 0;
    fread(&savelevel, 1, sizeof(s_savelevel), handle);
    fclose(handle);
	for(i=0; i<MAX_DIFFICULTIES; i++) if(savelevel[i].compatibleversion != CV_SAVED_GAME) clearSavedGame();
	return 1;
#else
	clearSavedGame();
	return 0;
#endif
}


void saveHighScoreFile(void){
#ifndef DC
	FILE *handle = NULL;
	char path[256] = {""};
	char tmpname[256] = {""};
	getBasePath(path, "Saves", 0);
	getPakName(tmpname, 1);
    strcat(path, tmpname);	
	handle = fopen(path, "wb");
	if(handle == NULL) return;
	fwrite(&savescore, 1, sizeof(s_savescore), handle);
	fclose(handle);
#endif
}


void loadHighScoreFile(void){
#ifndef DC
	FILE *handle = NULL;
	char path[256] = {""};
	char tmpname[256] = {""};
	getBasePath(path, "Saves", 0);
	getPakName(tmpname, 1);
	strcat(path,tmpname);
	clearHighScore();
	handle = fopen(path, "rb");
	if(handle == NULL) return;
	fread(&savescore, 1, sizeof(s_savescore), handle);
	fclose(handle);
	if(savescore.compatibleversion != CV_HIGH_SCORE) clearHighScore();
#else
	clearHighScore();
#endif
}

// ----------------------- Sound ------------------------------


void music(char *filename, int loop){
#if PSP || DOS || SDL || XBOX
    char t[64];
    char a[64];

    if(!savedata.usemusic) return;

    if(!sound_open_music(filename, packfile, savedata.musicvol, loop)){
        debug_printf("Can't play music file '%s'", filename);
    }
    else if(savedata.showtitles && sound_query_music(a,t)){
        if(a[0] && t[0]) debug_printf("Playing \"%s\" by %s", t, a);
        else if(a[0]) debug_printf("Playing unknown song by %s", a);
        else if(t[0]) debug_printf("Playing \"%s\" by unknown artist", t);
        else debug_printf("");
    }
#endif

#if DC
	if(!savedata.usemusic) return;
	sound_async_open_music(filename, packfile, savedata.musicvol, loop);
#endif
}

#if DC
void check_music_opened(void){
        char t[64];
        char a[64];

        if(!savedata.usemusic) return;

        if(!sound_was_music_opened()) return;

        if(savedata.showtitles && sound_query_music(a,t)){
                if(a[0] && t[0]) debug_printf("Playing \"%s\" by %s", t, a);
                else if(a[0]) debug_printf("Playing unknown song by %s", a);
                else if(t[0]) debug_printf("Playing \"%s\" by unknown artist", t);
                else debug_printf("");
        }
}
#endif

// Load a sound or return index from cache
int loadcache_sound(char *filename){
	int i;

	for(i=0; i<MAX_SOUND_CACHE; i++){
		if(stricmp(filename, soundcache[i].filename)==0) return soundcache[i].index;
	}

	for(i=0; i<MAX_SOUND_CACHE; i++){
		if(!soundcache[i].filename[0]) break;
	}
	if(i==MAX_SOUND_CACHE) return -1;

	soundcache[i].index = sound_load_sample(filename, packfile);
	if(soundcache[i].index < 0) return -1;
	strcpy(soundcache[i].filename, filename);
	return soundcache[i].index;
}

// Changed to conserve memory: added this function
void unloaduncache_sound(int index){
	int i;

	if(index<0 || index>=MAX_SOUND_CACHE) return;

	for(i=0; i<MAX_SOUND_CACHE; i++){
		if(soundcache[i].filename[0] && soundcache[i].index==index){
			sound_unload_sample(index);
			soundcache[i].index = -1;
			soundcache[i].filename[0] = 0;
		}
	}
}



// ----------------------- General ------------------------------

void load_stringtable(char *stringtable[MAX_STRING_TABLE], char *filename){

	char *buf;
	int size;
	int handle;
	int i;
	int foundline, stringi;

	stringtable[0] = "Start Game";
	stringtable[1] = "Options";
	stringtable[2] = "How To Play";
	stringtable[3] = "Quit";
	stringtable[4] = "Frequency:";
	stringtable[5] = "Bits:";
	stringtable[6] = "Apply";
	stringtable[7] = "Discard";
	stringtable[8] = "Back";
	stringtable[9] = "GAME OVER";
	stringtable[10] = "Player";
	stringtable[11] = "Brightness:";
	stringtable[12] = "Gamma:";
	stringtable[13] = "Control Options...";
	stringtable[14] = "Sound Hardware...";
	stringtable[15] = "Sound Options...";
	stringtable[16] = "Window Offset";
	stringtable[17] = "10-20 needed for some TV's";
	stringtable[18] = "Versus Damage:";
	stringtable[19] = "Versus Damage:";
	stringtable[20] = "Enemies don't blink when they die";
	stringtable[21] = "Enemies blink when they die";
	stringtable[22] = "Enemies fall down when you respawn";
	stringtable[23] = "Enemies don't fall down when you respawn";
	stringtable[24] = "Game Speed";
	stringtable[25] = "Control Options";
	stringtable[26] = "GamePads Enabled";
	stringtable[27] = " - Device Not Ready";
	stringtable[28] = "GamePads Disabled";
	stringtable[29] = "Setup Player 1...";
	stringtable[30] = "Setup Player 2...";
	stringtable[31] = "Move up";
	stringtable[32] = "Move down";
	stringtable[33] = "Move left";
	stringtable[34] = "Move right";
	stringtable[35] = "Special";
	stringtable[36] = "Attack";
	stringtable[37] = "Jump";
	stringtable[38] = "Start";
	stringtable[39] = "ScreenShot";
	stringtable[40] = "OK";
	stringtable[41] = "Cancel";
	stringtable[42] = "Sound Options";
	stringtable[43] = "Sound Volume:";
	stringtable[44] = "Sound Effects Volume:";
	stringtable[45] = "Background Music:";
	stringtable[46] = "Music Volume:";
	stringtable[47] = "Show Music Titles:";
	stringtable[48] = "Enabled";
	stringtable[49] = "Disabled";
	stringtable[50] = "Yes";
	stringtable[51] = "No";
	stringtable[52] = "Pause";
	stringtable[53] = "Continue";
	stringtable[54] = "End Game";
	stringtable[55] = "Select Hero";
	stringtable[56] = "Press Start";
	stringtable[57] = "Credit";
	stringtable[58] = "Loading...";
	stringtable[59] = "PRESS START";
	stringtable[60] = "Difficulty";
	stringtable[61] = "- Finish Game";
	stringtable[62] = "Times To UnLock";
	stringtable[63] = "- Finish Game To UnLock";
	stringtable[64] = "Player";
	stringtable[65] = "Choose a Different Character!";
	stringtable[66] = "Ready!";
	stringtable[67] = "Stage";
	stringtable[68] = "Complete!";
	stringtable[69] = "Clear Bonus";
	stringtable[70] = "Life Bonus";
	stringtable[71] = "Total Score";
	stringtable[72] = "Hall Of Fame";
	stringtable[73] = "Cheat Options";
	stringtable[74] = "Setup controls P3...";	// 4 player
	stringtable[75] = "Setup controls P4...";	// 4 player
	stringtable[76] = "Players: ";				// 4 player
    stringtable[77] = "Are you sure";			// Are you sure you want to quit
    stringtable[78] = "Please Wait";

	// Try to open file and determine size
	if((handle=openpackfile(filename,packfile)) >= 0) printf("Loading %s...\n",filename);
	else{
		if((handle=openpackfile("langpack.txt",packfile)) >= 0) printf("Loading langpack.txt...\n");
		else{
			printf("Loading StringTable...\n");
			return;
		}
	}

	size = seekpackfile(handle,0,SEEK_END);
	seekpackfile(handle,0,SEEK_SET);


	// Alloc buffer
	buf = (char*)tracemalloc("read_string_table 2",size+1);
	if(buf == NULL){
		closepackfile(handle);
		shutdown("Error reading '%s'\n",filename);
	}
	memset(buf, 0, size+1);


	// Read entire file
	readpackfile(handle, buf, size);

	// OK, close it.
	closepackfile(handle);


	// Find lines
	stringi = 0;
	foundline = 1;
	for(i=0; buf[i]; i++){
		if(foundline){
			stringtable[stringi] = buf + i ;
			stringi++;
			foundline = 0;
		}

		if(buf[i] == '\r') buf[i] = 0;

		if(buf[i] == '\n'){
			buf[i] = 0;	// Terminate string
			if(stringi >= MAX_STRING_TABLE) break;
			foundline = 1;
		}
	}
	tracefree(buf);
	buf = NULL;

	if(MAX_STRING_TABLE != stringi) shutdown("Stringtable Size %d, Entries Found %d\n", MAX_STRING_TABLE, stringi);
}



char *findarg(char *command, int which){
    int d;
    int argc;
    int inarg;
    int argstart;
    static char arg[MAX_ARG_LEN];


    // Copy the command line, replacing spaces by zeroes,
    // finally returning a pointer to the requested arg.
    d = 0;
    inarg = 0;
    argstart = 0;
    argc = -1;

    while(d<MAX_ARG_LEN-1 && command[d]){
        // Zero out whitespace
        if(command[d]==' ' || command[d]=='\t'){
            arg[d] = 0;
            inarg = 0;
            if(argc == which) return arg + argstart;
        }
        else if(command[d]==0 || command[d]=='\n' || command[d]=='\r' || command[d]=='#'){
            // End of line
            arg[d] = 0;
            if(argc == which) return arg + argstart;
            return arg + d;
        }
        else{
            if(!inarg){
                // if(argc==-1 && command[d]=='#') return arg;
                inarg = 1;
                argstart = d;
                argc++;
            }
            arg[d] = command[d];
        }
        ++d;
    }

    return arg;
}




float diff(float a, float b){
    if(a<b) return b-a;
    return a-b;
}



int inair(entity *e){
	return (diff(e->a, e->base) >= 0.1);
}



float randf(float max){
    float f;
    if(max==0) return 0;
    f = (rand32()%1000);
    f /= (1000/max);
    return f;
}



// ----------------------- Loaders ------------------------------




// Creates a remapping table from two images
int load_colourmap(s_model * model, char *image1, char *image2){
    int i, j, k;
	char *map;
	s_bitmap *bitmap1 = NULL;
	s_bitmap *bitmap2 = NULL;

    // Can't use same image twice!
    if(stricmp(image1,image2)==0) return 0;

    // Find an empty slot... ;)
	for(k=0; k<MAX_COLOUR_MAPS && model->colourmap[k]; k++);
	if(k>=MAX_COLOUR_MAPS) return -1;

    map = (char*)tracemalloc("load_colormap",256);
    if(map==NULL) return -2;

	bitmap1 = loadbitmap(image1, packfile);
	if(bitmap1==NULL){
		if(map != NULL){
			tracefree(map);
			map = NULL;
		}
		return -3;
	}
	bitmap2 = loadbitmap(image2, packfile);
	if(bitmap2==NULL){
		freebitmap(bitmap1);
		if(map != NULL){
			tracefree(map);
			map = NULL;
		}
		return -4;
	}

	// Create the colour map
	for(i=0;i<256;i++) map[i] = i;
	for(j=0; j<bitmap1->height && j<bitmap2->height; j++){
		for(i=0; i<bitmap1->width && i<bitmap2->width; i++){
			map[(unsigned)(bitmap1->data[j*bitmap1->width+i])] = bitmap2->data[j*bitmap2->width+i];
		}
	}

    freebitmap(bitmap1);
    freebitmap(bitmap2);

    model->colourmap[k] = map;
    model->maps_loaded = k + 1;
    return 1;
}



// Load colour 0-127 from data/pal.act
void standard_palette(){
    int handle;
    handle = openpackfile("data/pal.act", packfile);
    readpackfile(handle, pal, 128*3);
    closepackfile(handle);
    pal[0] = pal[1] = pal[2] = 0;
    palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
}



void unload_background(){
	if(background != NULL){
		freescreen(background);
		background = NULL;
	}
	if(lut_mul != NULL){
		tracefree(lut_mul);
		lut_mul = NULL;
	}
	if(lut_screen != NULL){
		tracefree(lut_screen);
		lut_screen = NULL;
	}
	if(lut_overlay != NULL){
		tracefree(lut_overlay);
		lut_overlay = NULL;
	}
	if(lut_hl != NULL){
		tracefree(lut_hl);
	    lut_hl = NULL;
	}
	if(lut_dodge != NULL){
		tracefree(lut_dodge);
	    lut_dodge = NULL;
	}
	if(lut_half != NULL){
		tracefree(lut_half);
	    lut_half = NULL;
	}
}



// ltb 1-17-05   new function for lifebar colors
void lifebar_colors(){
    char * filename = "data/lifebar.txt";
    int handle;
    char *buf;
    unsigned int size;
    int pos;

    char * command;
    char * value;
    char * value2;
    char * value3;



    if((handle=openpackfile(filename,packfile)) < 0){
        color_black = 0;
        color_red = 0;
        color_orange = 0;
        color_yellow = 0;
        color_white = 0;
        color_blue = 0;
        color_green = 0;
        color_pink = 0;
        color_purple = 0;
        color_magic = 0;
		color_magic2 = 0;

        return;
    }
    size = seekpackfile(handle,0,SEEK_END);
    seekpackfile(handle,0,SEEK_SET);

	buf = (char*)tracemalloc("lifebar_colors",size+1);
	if(buf==NULL){
		closepackfile(handle);
		shutdown("Not enough memory to allocate %i-byte buffer for file %s", size, filename);
	}
	if(readpackfile(handle, buf, size) != size){
		if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
		closepackfile(handle);
		shutdown("Read error accessing file %s", filename);
	}
	buf[size] = 0;		// Terminate string (important!)
	closepackfile(handle);


    pos = 0;
    colorbars=1;
    while(pos<size){
        command = findarg(buf+pos, 0);
        if(command[0]){
            if(stricmp(command, "blackbox")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_black = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "whitebox")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_white = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "color300")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_orange = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "color25")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_red = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "color50")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_yellow = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "color100")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_green = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "color200")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_blue = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "color400")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_pink = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
            else if(stricmp(command, "color500")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
                value3 = findarg(buf+pos, 3);
                color_purple = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
            }
				//magic bars color declarations by tails
			else if(stricmp(command, "colormagic")==0){
				value = findarg(buf+pos, 1);
				value2 = findarg(buf+pos, 2);
				value3 = findarg(buf+pos, 3);
				color_magic = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
			}
			else if(stricmp(command, "colormagic2")==0){
				value = findarg(buf+pos, 1);
				value2 = findarg(buf+pos, 2);
				value3 = findarg(buf+pos, 3);
				color_magic2 = palette_find(pal, atoi(value),atoi(value2),atoi(value3));
			}
				//end of magic bars color declarations by tails
            else{
                tracefree(buf);
                return;
            }
        }

		// Go to next line
		while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
		while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
	}
	if(buf != NULL){
		tracefree(buf);
		buf = NULL;
	}
}

// ltb 1-17-05 end new lifebar colors



void load_background(char *filename, int createtables, int dText){

	unload_background();
	video_clearscreen(0);

	background = loadscreen(filename, packfile, pal);
	if(background==NULL) shutdown("Error loading file '%s'", filename);
	pal[0] = pal[1] = pal[2] = 0;
	palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);

#if PSP || SDL	
	if(videoMode == 2){
	freescreen(vscreen);
		if((vscreen = allocscreen(hRes2, vRes2)) == NULL){
			shutdown("Not enough memory!\n");
		}
	}
#endif

	clearscreen(vscreen);
	spriteq_clear();

	if(dText) font_printf(140+hOffset+xOffset,110+vOffset, 0, "%s",stringtable[58]);
	
	spriteq_draw(vscreen);
	if(rescreen){
		scalescreen(rescreen, vscreen);
		video_copy_screen(rescreen);
	}
	else video_copy_screen(vscreen);

	lifebar_colors();	// ltb 1-17-05  load custom colors

	if(!color_black) color_black = palette_find(pal, 0,0,0);		// black boxes 500-600HP
	if(!color_red) color_red = palette_find(pal, 255,0,0);		// 1% - 25% Full Health
    if(!color_orange) color_orange = palette_find(pal, 255,150,0);	// 200-300HP
    if(!color_yellow) color_yellow = palette_find(pal, 0xF8,0xB8,0x40);	// 26%-50% Full health
	if(!color_white) color_white = palette_find(pal, 255,255,255);	// white boxes 600+ HP
	if(!color_blue) color_blue = palette_find(pal, 0,0,255);		// 100-200 HP
	if(!color_green) color_green = palette_find(pal, 0,255,0);		// 51% - 100% full health
	if(!color_pink) color_pink = palette_find(pal, 255,0,255);		// 300-400HP
	if(!color_purple) color_purple = palette_find(pal, 128,48,208);	// transbox 400-500HP
	if(!color_magic) color_magic = palette_find(pal, 98,180,255);	// 1st magic bar color by tails
    if(!color_magic2) color_magic2 = palette_find(pal, 24,48,143);	// 2sec magic bar color by tails


	if(createtables){
		standard_palette();

		lut_mul = palette_table_multiply(pal);
		if(lut_mul==NULL) shutdown("Failed to create colour conversion table! (Out of memory?)");
		lut_screen = palette_table_screen(pal);
		if(lut_screen==NULL) shutdown("Failed to create colour conversion table! (Out of memory?)");
		lut_overlay = palette_table_overlay(pal);
        if(lut_overlay==NULL) shutdown("Failed to create colour conversion table! (Out of memory?)");
        lut_hl = palette_table_hardlight(pal);
        if(lut_hl==NULL) shutdown("Failed to create colour conversion table! (Out of memory?)");
        lut_dodge = palette_table_dodge(pal);
        if(lut_dodge==NULL) shutdown("Failed to create colour conversion table! (Out of memory?)");
        lut_half = palette_table_half(pal);
        if(lut_half==NULL) shutdown("Failed to create colour conversion table! (Out of memory?)");
	}
}



void unload_texture(){
    if(texture) freebitmap(texture);
    texture = NULL;
}



void load_texture(char *filename){
    unload_texture();
    texture = loadbitmap(filename, packfile);
    if(texture==NULL) shutdown("Error loading file '%s'", filename);
}



void freepanels(){
	int i;
	for(i=0; i<MAX_PANELS; i++){
		if(panels[i].sprite_normal != NULL){
			tracefree(panels[i].sprite_normal);
			panels[i].sprite_normal = NULL;
		}
		if(panels[i].sprite_neon != NULL){
			tracefree(panels[i].sprite_neon);
			panels[i].sprite_neon = NULL;
		}
		if(panels[i].sprite_screen != NULL){
			tracefree(panels[i].sprite_screen);
			panels[i].sprite_screen = NULL;
		}
		if(frontpanels[i] != NULL){
			tracefree(frontpanels[i]);
			frontpanels[i] = NULL;
		}
	}
	panels_loaded = 0;
	frontpanels_loaded = 0;
	panel_width = 0;
	panel_height = 0;
}



s_sprite * loadpanel2(char *filename){
    int size;
    s_bitmap *bitmap = NULL;
    s_sprite *sprite = NULL;
    int clipl, clipr, clipt, clipb;

    bitmap = loadbitmap(filename, packfile);
    if(!bitmap) return NULL;
    if(bitmap->width > panel_width) panel_width = bitmap->width;
    if(bitmap->height > panel_height) panel_height = bitmap->height;
    clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);
    size = fakey_encodesprite(bitmap);
    sprite = (s_sprite*)tracemalloc("loadpanel2",size);
    if(!sprite){
        freebitmap(bitmap);
        return NULL;
    }
    encodesprite(-clipl, -clipt, bitmap, sprite);
    freebitmap(bitmap);

    return sprite;
}



int loadpanel(char *filename_normal, char *filename_neon, char *filename_screen){

    int i = 0;

    if(panels_loaded >= MAX_PANELS) return 0;

    if(stricmp(filename_normal,"none")!=0 && *filename_normal){
        panels[panels_loaded].sprite_normal = loadpanel2(filename_normal);
        if(panels[panels_loaded].sprite_normal == NULL) return 0;
        i++;
    }
    if(stricmp(filename_neon,"none")!=0 && *filename_neon){
        panels[panels_loaded].sprite_neon = loadpanel2(filename_neon);
        if(panels[panels_loaded].sprite_neon == NULL) return 0;
        i++;
    }
    if(stricmp(filename_screen,"none")!=0 && *filename_screen){
        panels[panels_loaded].sprite_screen = loadpanel2(filename_screen);
        if(panels[panels_loaded].sprite_screen == NULL) return 0;
        i++;
    }
    if(i<1) return 0;	// Nothing was loaded!

    ++panels_loaded;

    return 1;
}



int loadfrontpanel(char *filename){

    int size;
    s_bitmap *bitmap = NULL;
    int clipl, clipr, clipt, clipb;


    if(frontpanels_loaded >= MAX_PANELS) return 0;
    bitmap = loadbitmap(filename, packfile);
    if(!bitmap) return 0;

    clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);

    size = fakey_encodesprite(bitmap);
    frontpanels[frontpanels_loaded] = (s_sprite*)tracemalloc("loadfrontpanel",size);
    if(!frontpanels[frontpanels_loaded]){
        freebitmap(bitmap);
        return 0;
    }
    encodesprite(-clipl, -clipt, bitmap, frontpanels[frontpanels_loaded]);

    freebitmap(bitmap);
    ++frontpanels_loaded;

    return 1;
}




void resourceCleanUp(void){
	freesprites();			// Changed to conserve memory
	free_models();			// Changed to conserve memory
	free_modelcache();		// Changed to conserve memory
	load_models();			// Changed to conserve memory
	load_special_sprites();	// Changed to conserve memory
}

void freesprites(){
	int i;
	for(i=0; i<MAX_SPRITES; i++){
		if(sprites[0][i] != NULL){
			tracefree(sprites[0][i]);
			sprites[0][i] = NULL;
		}
		if(sprites[1][i]!= NULL){ 
			tracefree(sprites[1][i]);
			sprites[1][i] = NULL;
		}
		strncpy(spritecache[i].filename, "", MAX_FILENAME_LEN);
		spritecache[i].ofsx = -1;
		spritecache[i].ofsy = -1;
	}
	sprites_loaded = 0;
}



// Returns sprite index.
// Does not return on error, as it would shut the program down.
int loadsprite(char *filename, int ofsx, int ofsy){

    int i, size;
    s_bitmap *bitmap = NULL;
    int clipl, clipr, clipt, clipb;

	for(i=0; i<sprites_loaded; i++){
		if(stricmp(spritecache[i].filename, filename) == 0 && 
			spritecache[i].ofsx == ofsx && spritecache[i].ofsy == ofsy){
			return i;
		}
	}
	strncpy(spritecache[sprites_loaded].filename, filename, MAX_FILENAME_LEN);
	spritecache[sprites_loaded].ofsx = ofsx;
	spritecache[sprites_loaded].ofsy = ofsy;

    if(sprites_loaded >= MAX_SPRITES) shutdown("Too many sprites (max. %u)", MAX_SPRITES);
    bitmap = loadbitmap(filename, packfile);
    if(bitmap == NULL) shutdown("Unable to load file '%s'\n", filename);

    clipbitmap(bitmap, &clipl, &clipr, &clipt, &clipb);

    size = fakey_encodesprite(bitmap);
    sprites[1][sprites_loaded] = (s_sprite*)tracemalloc("loadsprite 1",size);
    if(!sprites[1][sprites_loaded]){
        freebitmap(bitmap);
		shutdown("loadsprite() #1 Out of memory!\n");
    }
    encodesprite(ofsx-clipl, ofsy-clipt, bitmap, sprites[1][sprites_loaded]);

#if PSP || DC
    // Now flip it
    size = fakey_encodesprite_flip(bitmap);
	sprites[0][sprites_loaded] = (s_sprite*)tracemalloc("loadsprite 2",size);
	if(!sprites[0][sprites_loaded]){
		freebitmap(bitmap);
		shutdown("loadsprite() #2 Out of memory!\n");
	}
	encodesprite_flip((bitmap->width+clipl)-ofsx-1, ofsy-clipt, sprites[1][sprites_loaded], sprites[0][sprites_loaded]);
#endif

#if DOS || SDL || XBOX
	// Now flip it
    flipbitmap(bitmap);
    size = fakey_encodesprite(bitmap);
    sprites[0][sprites_loaded] = (s_sprite*)tracemalloc("loadsprite 2", size);
    if(!sprites[0][sprites_loaded]){
        freebitmap(bitmap);
        shutdown("loadsprite() #2 Out of memory!\n");
    }
    encodesprite((bitmap->width+clipl)-ofsx-1, ofsy-clipt, bitmap, sprites[0][sprites_loaded]);
#endif

	freebitmap(bitmap);
	++sprites_loaded;

	return sprites_loaded-1;
}



// Changed to conserve memory: this function added
void load_special_sprites(){
	shadowsprites[0] = loadsprite("data/sprites/shadow1",9,3);
	shadowsprites[1] = loadsprite("data/sprites/shadow2",14,5);
	shadowsprites[2] = loadsprite("data/sprites/shadow3",19,6);
	shadowsprites[3] = loadsprite("data/sprites/shadow4",24,8);
	shadowsprites[4] = loadsprite("data/sprites/shadow5",29,9);
	shadowsprites[5] = loadsprite("data/sprites/shadow6",34,11);
	gosprite = loadsprite("data/sprites/arrow",35,23);
	if(openpackfile("data/sprites/arrowl.gif", packfile) >=0) golsprite = loadsprite("data/sprites/arrowl",35,23);
}
// Changed to conserve memory: this function added
void load_special_sounds(){
	smp_go = loadcache_sound("data/sounds/go.wav");   //26-12-2004  - Loads "Go.wav" sample into smp_go
	smp_beat = loadcache_sound("data/sounds/beat1.wav");
	smp_block = loadcache_sound("data/sounds/block.wav");	// New block sound effect
	smp_fall = loadcache_sound("data/sounds/fall.wav");
	smp_get =  loadcache_sound("data/sounds/get.wav");
	smp_get2 = loadcache_sound("data/sounds/money.wav");
	smp_jump = loadcache_sound("data/sounds/jump.wav");
	smp_indirect = loadcache_sound("data/sounds/indirect.wav");
	smp_punch = loadcache_sound("data/sounds/punch.wav");
	smp_1up = loadcache_sound("data/sounds/1up.wav");
	smp_timeover = loadcache_sound("data/sounds/timeover.wav");
	smp_beep = loadcache_sound("data/sounds/beep.wav");
	smp_beep2 = loadcache_sound("data/sounds/beep2.wav");
	smp_bike = loadcache_sound("data/sounds/bike.wav");
}
s_model * find_model(char *name){
    int i;
    for(i=0; i<models_loaded; i++){
        if(stricmp(model_list[i]->name, name)==0){
            return model_list[i];
        }
    }
    return NULL;
}



// Use by player select menus
s_model * nextplayermodel(void *current){
    int i;
    int curindex = -1;
    int loops;

    if(current){
        // Find index of current player model
        for(i=0; i<models_loaded; i++){
            if(model_list[i] == current){
                curindex = i;
                break;
            }
        }
    }

    // Find next player model (first one after current index)

    for(i=curindex+1, loops=0; loops<models_loaded; i++, loops++){
        if(i >= models_loaded) i = 0;
        if(model_list[i]->type==TYPE_PLAYER && (allow_secret_chars || !model_list[i]->secret)){
            return model_list[i];
        }
    };

    shutdown("Fatal: can't find any player models!");
    return NULL;
}



// Use by player select menus
s_model * prevplayermodel(void *current){
    int i;
    int curindex = -1;
    int loops;

    if(current){
        // Find index of current player model
        for(i=0; i<models_loaded; i++){
            if(model_list[i] == current){
                curindex = i;
                break;
            }
        }
    }

    // Find next player model (first one after current index)
    for(i=curindex-1, loops=0; loops<models_loaded; i--, loops++){
        if(i < 0) i = models_loaded-1;
        if(model_list[i]->type==TYPE_PLAYER && (allow_secret_chars || !model_list[i]->secret)){
            return model_list[i];
        }
    };

    shutdown("Fatal: can't find any player models!");
    return NULL;
}



// Unload all models and animations
void free_models(){
	int i;
	int j;
	for(i=0; i<MAX_MODELS; i++){
		if(model_list[i] != NULL){
			for(j=0;j<MAX_COLOUR_MAPS;j++){
				if(model_list[i]->colourmap[j] != NULL){ 
					tracefree(model_list[i]->colourmap[j]);
					model_list[i]->colourmap[j] = NULL;
				}
			}
			//unloaduncache_sound(model_list[i]->diesound);	// Changed to conserve memory
			tracefree(model_list[i]);
			model_list[i] = NULL;
		}
	}
	models_loaded = 0;

	for(i=0; i<MAX_ANIMS; i++){
		if(anim_list[i] != NULL){
			unloaduncache_sound(anim_list[i]->soundtoplay);	// Changed to conserve memory
			unloaduncache_sound(anim_list[i]->hitsound);	// Changed to conserve memory
			tracefree(anim_list[i]);
			anim_list[i] = NULL;
		}
	}
	anims_loaded = 0;
}



s_anim * alloc_anim(){
    s_anim * new;

    if(anims_loaded >= MAX_ANIMS) return NULL;
    new = (s_anim *)tracemalloc("alloc_anim",sizeof(s_anim));
    if(new == NULL) return NULL;
    memset(new, 0, sizeof(s_anim));

    anim_list[anims_loaded] = new;
    ++anims_loaded;

    return new;
}



// Add another frame to an animation (if possible)
int addframe(s_anim * a, int spriteindex, int delay, int *bbox, int *attack, int attackforce, int attackdrop,
int attacktype, int move, int movez, int movea, int seta, int attacklength, int noblock, int noflash, int pauseadd, int* platform){	// Added attacklength for freeze attacks
    if(a->numframes >= ANI_MAX_FRAMES) return ANI_MAX_FRAMES;
    a->sprite[a->numframes] = spriteindex;
    a->delay[a->numframes] = delay * GAME_SPEED / 100;

    if((bbox[2]-bbox[0]) && (bbox[3]-bbox[1])){
        a->bbox_coords[a->numframes][0] = bbox[0];
        a->bbox_coords[a->numframes][1] = bbox[1];
        a->bbox_coords[a->numframes][2] = bbox[2];
        a->bbox_coords[a->numframes][3] = bbox[3];
        a->vulnerable[a->numframes] = 1;
    }

    a->attack_coords[a->numframes][0] = attack[0];
    a->attack_coords[a->numframes][1] = attack[1];
    a->attack_coords[a->numframes][2] = attack[2];
    a->attack_coords[a->numframes][3] = attack[3];
    a->attack_force[a->numframes] = attackforce;
    a->attack_drop[a->numframes] = attackdrop;
    a->attack_type[a->numframes] = attacktype;
    a->attack_length[a->numframes] = attacklength;	// Freezetime corresponds with the respective frame
    a->no_block[a->numframes] = noblock;	// Determine if an attack is blockable (default 0 - blockable)
	a->no_flash[a->numframes] = noflash;	// Determine if the attack spawns a flash or not
	a->pause_add[a->numframes] = pauseadd;	// Determine if the attack adds a pause before updating animation
    a->move[a->numframes] = move;
    a->movez[a->numframes] = movez;	// Move command for the "z" axis
    a->movea[a->numframes] = movea;	// Move command for moving along the "a" axis
    a->seta[a->numframes] = seta;	// Sets the "a" for the character on a frame/frame basis
    a->platform[a->numframes][0] = platform[0];	// Used so entity can be landed on
    a->platform[a->numframes][1] = platform[1];	// Used so entity can be landed on
    a->platform[a->numframes][2] = platform[2];	// Used so entity can be landed on
    a->platform[a->numframes][3] = platform[3];	// Used so entity can be landed on
    a->platform[a->numframes][4] = platform[4];	// Used so entity can be landed on
    a->platform[a->numframes][5] = platform[5];	// Used so entity can be landed on
    ++a->numframes;
    return a->numframes;
}



void cache_model(char *name, char *path){
    if(models_cached >= MAX_MODELS) shutdown("Too many models, unable to cache '%s'", name);
    printf("Cacheing '%s'\n", name);

    model_cache[models_cached] = (void*)tracemalloc("cache_model",sizeof(s_modelcache));
    if(model_cache[models_cached] == NULL) shutdown("Out of memory cacheing '%s'", name);

    strncpy(model_cache[models_cached]->name, name, MAX_NAME_LEN);
    strncpy(model_cache[models_cached]->path, path, 255);

    ++models_cached;
}



void remove_from_cache(char * name){
    int i;
    void *tp;

	for(i=0; i<models_cached; i++){
		if(stricmp(name, model_cache[i]->name)==0){
			tp = model_cache[i];
			model_cache[i] = model_cache[models_cached-1];
			if(tp != NULL){
				tracefree(tp);
				tp = NULL;
			}
			model_cache[models_cached-1] = NULL;
			models_cached--;
			return;
		}
	}
}



void free_modelcache(){
	int i;
	for(i=0; i<MAX_MODELS; i++){
		if(model_cache[i] != NULL){
			tracefree(model_cache[i]);
			model_cache[i] = NULL;
		}
	}
	models_cached = 0;
}



char *get_cached_model_path(char * name){
    int i;

    for(i=0; i<models_cached; i++){
        if(stricmp(name, model_cache[i]->name)==0){
            return model_cache[i]->path;
        }
    }
    return NULL;
}



void load_cached_model(char * name){

    char *filename;
	int i = 0;
    int handle;
    char *buf;
    unsigned int size;
    int pos;

    s_model * newchar = NULL;
    s_anim * newanim = NULL;
    int index;

    char * command;
    char * value;
    char * value2;

    int curframe = 0;

    int delay = 0;
    int bbox[4] = { 0,0,0,0 };
    int attack[4] = { 0,0,0,0 };
    int platform[8] = { 0,0,0,0,0,0,0,0 };
    int attackforce = 0;
    int attackdrop = 0;
    int attacktype = 0;
    int attacklength = 0;	// Default attacklength set to 0
    int noblock = 0;	// Default set to blockable
    int	noflash = 0;	// Default noflash set to 1 (does spawn flash)
    int pauseadd = 0;	// How much of a pause to add when an attack hits an opponent
    int offset[2] = { 0,0 };
    int move = 0;
    int movez = 0;
    int movea = 0;
    int seta = -1;	// Used for setting custom "a". Set to -1 to distinguish between disabled and setting "a" to 0

    int bbox_con[4];
    int attack_con[4];
    int platform_con[6];
    int weap, last = 0;
	int players_weapon = 0;
	int errorVal = 0;


    if(find_model(name)) return;		// Model already loaded


    filename = get_cached_model_path(name);
    if(filename == NULL){
        shutdown("Fatal: no cache entry for '%s'", name);
    }


    if(models_loaded >= MAX_MODELS){
        shutdown("Cannot load model from '%s' - too many models", filename);
    }
    // printf("Loading model %s...\n", filename);

    if((handle=openpackfile(filename,packfile)) < 0) shutdown("Unable to open file '%s'", filename);
    size = seekpackfile(handle,0,SEEK_END);
    seekpackfile(handle,0,SEEK_SET);

    buf = (char*)tracemalloc("load_cached_model 1",size+1);
    if(buf==NULL){
        closepackfile(handle);
        shutdown("Unable to create buffer for file '%s' (%i bytes)", filename, size);
    }
    if(readpackfile(handle, buf, size) != size){
        if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
        closepackfile(handle);
        shutdown("Read error accessing file '%s'", filename);
    }
    buf[size] = 0;		// Terminate string (important!)
    closepackfile(handle);


    // Alloc space for game model
    newchar = (s_model *)tracemalloc("load_cached_model 2",sizeof(s_model));

    if(newchar == NULL){
        if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
        shutdown("Out of memory loading model from '%s'", filename);
    }

    model_list[models_loaded] = newchar;
    memset(newchar,0,sizeof(s_model));
    newchar->death = 0;   // 6-2-2005 Falldie code
    newchar->jumpheight = 4;// 28-12-2004   Set default jump height to 4, if not specified
    newchar->runjumpheight = 4;	// Default jump height if a player is running
    newchar->runjumpdist = 1;	// Default jump distane if a player is running
    newchar->grabdistance = 36;//  30-12-2004 Default grabdistance is same as originally set
    newchar->throwdamage = 21;	// default throw damage
    newchar->icon = -1;
    newchar->iconw = -1;	// No weapon icon set yet
    newchar->diesound = -1;
	newchar->nolife = 0;	// default show life = 1 (yes)
	newchar->remove = 1;	// Flag set to weapons are removed upon hitting an opponent
	newchar->blockodds = 1;	// Odds are about 1:1 that the enemy will block your shot
	newchar->holdblock = 0;
	newchar->blockpain = 0;
	newchar->paingrab  = 0;
	newchar->throwdist = 2.5;
	newchar->throwheight = 0;
	newchar->counter = 3;	// Default 3 times to drop a weapon / projectile
	newchar->atchain[0] = 1; // Default to the ordinary attack string
	newchar->atchain[1] = 1;
	newchar->atchain[2] = 2;
	newchar->atchain[3] = 3;
	newchar->hitenemy = -1; // 2007/2/21 for further use, see function spawn, initialize this value manually
	newchar->chainlength = 3;
	newchar->throwframewait = -1;  // makes sure throw animations run normally unless throwfram is specified, added by kbandressen 10/20/06

	for(i=0; i<3; i++) newchar->iconmp[i] = -1;	// No magicbar icon set yet
	for(i = 4; i < MAX_ATCHAIN; i++) newchar->atchain[i] = 0;

	if(magic_type == 1) newchar->mprate = 1;
    else newchar->mprate = 2;
    newchar->chargerate = 2;
    newchar->aggression = 0;            // For enemy A.I.
    newchar->risetime = 0;				// For enemy A.I.
    newchar->sleepwait = 1000;
	newchar->ptype = 0;					// Used to determine projectile: defaults to knife.
	newchar->weaploss = -1;
	newchar->facing = 0;
    newchar->turndelay = 0;
    newchar->lifespan = -1;
    newchar->summonkill = 1;
    newchar->candamage = -1;
    newchar->hostile = -1;
    newchar->projectilehit = -1;

    strncpy(newchar->rider, "K'", MAX_NAME_LEN);
    strncpy(newchar->flash, "flash", MAX_NAME_LEN);	// Now each character can have their own flash
    strncpy(newchar->bflash, "flash", MAX_NAME_LEN);	// Flash for when an attack is blocked
    models_loaded++;


    // Now interpret the contents of buf line by line
    pos = 0;

    while(pos<size){
        command = findarg(buf+pos, 0);

        if(command[0]){
            if(stricmp(command, "name")==0){
                value = findarg(buf+pos, 1);
                if(find_model(value)){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Duplicate model name '%s'", value);
                }
                strncpy(newchar->name, value, MAX_NAME_LEN);
            }
			else if(stricmp(command, "type")==0){	// Moved here to be able to access the character type later on
                value = findarg(buf+pos, 1);
                if(stricmp(value, "none")==0){
                    newchar->type = TYPE_NONE;
                }
                else if(stricmp(value, "player")==0){
                    newchar->type = TYPE_PLAYER;
                }
                else if(stricmp(value, "enemy")==0){
                    newchar->type = TYPE_ENEMY;
                }
                else if(stricmp(value, "item")==0){
                    newchar->type = TYPE_ITEM;
                }
                else if(stricmp(value, "obstacle")==0){
                    newchar->type = TYPE_OBSTACLE;
                }
                else if(stricmp(value, "steamer")==0){
                    newchar->type = TYPE_STEAMER;
                }
                // my new types   7-1-2005
                else if(stricmp(value, "pshot")==0){
                    newchar->type = TYPE_SHOT;
                }
                else if(stricmp(value, "trap")==0){
                    newchar->type = TYPE_TRAP;
                }
                else if(stricmp(value, "text")==0){	// Used for displaying text/images and freezing the screen
					newchar->type = TYPE_TEXTBOX;
                }
				else if(stricmp(value, "endlevel")==0){	// Used for ending the level when the players reach a certain point
					newchar->type = TYPE_ENDLEVEL;
				}
				else if(stricmp(value, "npc")==0){	// NPC type
					newchar->type = TYPE_NPC;
				}
                else{
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Model '%s' has invalid type: '%s'", filename, value);
                }
            }
            else if(stricmp(command, "subtype")==0){
                value = findarg(buf+pos, 1);
                if(stricmp(value, "biker")==0){
                    newchar->subtype = SUBTYPE_BIKER;
                }
                else if(stricmp(value, "arrow")==0){  // 7-1-2005 Arrow type
                    newchar->subtype = SUBTYPE_ARROW;   // 7-1-2005 Arrow type
                }																		// 7-1-2005 Arrow type
                else if(stricmp(value, "notgrab")==0){  // 7-1-2005 notgrab type
                    newchar->subtype = SUBTYPE_NOTGRAB;   // 7-1-2005 notgrab type
                }
                //	ltb 1-18-05  Item Subtype
                else if(stricmp(value, "touch")==0){  // 7-1-2005 notgrab type
                    newchar->subtype = SUBTYPE_TOUCH;   // 7-1-2005 notgrab type
                }
                else if(stricmp(value, "weapon")==0){  // 7-1-2005 notgrab type
                    newchar->subtype = SUBTYPE_WEAPON;   // 7-1-2005 notgrab type
                }
                else if(stricmp(value, "noskip")==0){	// Text animation cannot be skipped if subtype noskip
					newchar->subtype = SUBTYPE_NOSKIP;
				}
                else if(stricmp(value, "flydie")==0){	// Obstacle will fly across the screen when hit if subtype flydie
					newchar->subtype = SUBTYPE_FLYDIE;
				}
				else if(stricmp(value, "both")==0){
					newchar->subtype = SUBTYPE_BOTH;
				}
				else if(stricmp(value, "project")==0){
					newchar->subtype = SUBTYPE_PROJECTILE;
				}
				else if(stricmp(value, "follow")==0){
					newchar->subtype = SUBTYPE_FOLLOW;
				}
				else if(stricmp(value, "chase")==0){
					newchar->subtype = SUBTYPE_CHASE;
				}
                	//	end new subtype
                else{
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Model '%s' has invalid subtype: '%s'", filename, value);
                }
            }
            else if(stricmp(command, "health")==0){
                value = findarg(buf+pos, 1);
                newchar->health = atoi(value);
            }
			else if(stricmp(command, "mp")==0){// mp values to put max mp for player by tails
				value = findarg(buf+pos, 1);
				newchar->mp = atoi(value);
	    	}
			else if(stricmp(command, "nolife")==0){	// Feb 25, 2005 - Flag to display enemy life or not
				newchar->nolife = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "makeinv")==0){	// Mar 12, 2005 - If a value is supplied, corresponds to amount of time the player spawns invincible
				newchar->makeinv = atoi(findarg(buf+pos, 1)) * GAME_SPEED;
			}
            else if(stricmp(command, "load")==0){
                value = findarg(buf+pos, 1);
                load_cached_model(value);
            }
            else if(stricmp(command, "score")==0){
                newchar->score = atoi(findarg(buf+pos, 1));
                newchar->multiple = atoi(findarg(buf+pos, 2));	// New var multiple for force/scoring
            }
            else if(stricmp(command, "smartbomb")==0){	// Mar 12, 2005 - Special can now be a smart bomb
				newchar->specpower = atoi(findarg(buf+pos, 1));	// Special force
				newchar->spectype = atoi(findarg(buf+pos, 2));	// Special attack type

				for(i=0; i<models_loaded; i++){
					if(model_list[i]->type == TYPE_PLAYER){
						for(weap=0; weap<MAX_WEAPONS; weap++){
							if(stricmp(newchar->name, model_list[i]->weapon[weap]) == 0){
								players_weapon = 1;
								break;
							}
						}
					}
					if(players_weapon) break;
				}

				if(newchar->type == TYPE_PLAYER || players_weapon){
					newchar->dofreeze = atoi(findarg(buf+pos, 3));	// Are all animations frozen during special
					newchar->speclen = atoi(findarg(buf+pos, 4)) * GAME_SPEED;
				}
				else{
					newchar->dofreeze = 0;	// Items don't animate
					newchar->speclen = atoi(findarg(buf+pos, 3)) * GAME_SPEED;
				}
			}
			else if(stricmp(command, "noquake")==0){	// Mar 12, 2005 - Flag to determine if entity shakes screen
				newchar->noquake = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "hitenemy")==0){	// Flag to determine if an enemy projectile will hit enemies
                newchar->hitenemy = atoi(findarg(buf+pos, 1));
                newchar->ground = atoi(findarg(buf+pos, 2));	// Added to determine if enemies are damaged with mid air projectiles or ground only
			}
			else if(stricmp(command, "hostile")==0){
                i = 1;
                newchar->hostile = 0;
                value = findarg(buf+pos, i);
                while(value && value[0])
                {
                    if(stricmp(value, "enemy")==0){
                        newchar->hostile |= TYPE_ENEMY;
                    } else if(stricmp(value, "player")==0){
                        newchar->hostile |= TYPE_PLAYER;
                    } else if(stricmp(value, "obstacle")==0){
                        newchar->hostile |= TYPE_OBSTACLE;
                    } else if(stricmp(value, "shot")==0){
                        newchar->hostile |= TYPE_SHOT;
                    } else if(stricmp(value, "npc")==0){
                        newchar->hostile |= TYPE_NPC;
                    }
                    i++;
                    value = findarg(buf+pos, i);
                }
            }
			else if(stricmp(command, "candamage")==0){
                i = 1;
                newchar->candamage = 0;
                value = findarg(buf+pos, i);
                while(value && value[0])
                {
                    if(stricmp(value, "enemy")==0){
                        newchar->candamage |= TYPE_ENEMY;
                    } else if(stricmp(value, "player")==0){
                        newchar->candamage |= TYPE_PLAYER;
                    } else if(stricmp(value, "obstacle")==0){
                        newchar->candamage |= TYPE_OBSTACLE;
                    } else if(stricmp(value, "shot")==0){
                        newchar->candamage |= TYPE_SHOT;
                    } else if(stricmp(value, "npc")==0){
                        newchar->candamage |= TYPE_NPC;
                    } else if(stricmp(value, "ground")==0){ // not really needed, though
                        newchar->ground = 1;
                    } //else {
                        // wont shut down 
                    //}
                    i++;
                    value = findarg(buf+pos, i);
                }
            }
			else if(stricmp(command, "projectilehit")==0){
                i = 1;
                newchar->projectilehit = 0;
                value = findarg(buf+pos, i);
                while(value && value[0])
                {
                    if(stricmp(value, "enemy")==0){
                        newchar->projectilehit |= TYPE_ENEMY;
                    } else if(stricmp(value, "player")==0){
                        newchar->projectilehit |= TYPE_PLAYER;
                    } else if(stricmp(value, "obstacle")==0){
                        newchar->projectilehit |= TYPE_OBSTACLE;
                    } else if(stricmp(value, "shot")==0){
                        newchar->projectilehit |= TYPE_SHOT;
                    } else if(stricmp(value, "npc")==0){
                        newchar->projectilehit |= TYPE_NPC;
                    } //else {
                        // wont shut down 
                    //}
                    i++;
                    value = findarg(buf+pos, i);
                }
            }
            else if(stricmp(command, "secret")==0){
                newchar->secret = atoi(findarg(buf+pos, 1));
            }
	        // weapons
			else if(stricmp(command, "weaploss")==0){
                newchar->weaploss = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "weapnum")==0){
                newchar->weapnum = atoi(findarg(buf+pos, 1));
            }
			else if(stricmp(command, "project")==0){  // New projectile subtype
				value = findarg(buf+pos, 1);
				find_model(value);
				strncpy(newchar->project, value, MAX_NAME_LEN);   // New projectile subtype
			}
            else if(stricmp(command, "weapons")==0){
                for(weap = 0; weap<MAX_WEAPONS; weap++){
                    value = findarg(buf+pos, weap+1);
                    if(value){
                        if(stricmp(value, "none")){
                            strncpy(newchar->weapon[weap], value, MAX_NAME_LEN);
                        } else { // make empty weapon slots  2007-2-16
                            strncpy(newchar->weapon[weap], "", MAX_NAME_LEN);
                        }
                        last = weap;
                    }
                    else strncpy(newchar->weapon[weap], newchar->weapon[last], MAX_NAME_LEN);
                }
            }
				//here weapons things like shoot rest type of weapon ect..by tails
			else if(stricmp(command, "shootnum")==0){
				newchar->shootnum = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "reload")==0){
				newchar->reload = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "typeshot")==0){
				newchar->typeshot = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "counter")==0){
				newchar->counter = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "animal")==0){
				newchar->animal = atoi(findarg(buf+pos, 1));
			}
	            // end weapons
            else if(stricmp(command, "rider")==0){   //7-1-2005 new parts start here Playshot/knife/fireball/star
                value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->rider, value, MAX_NAME_LEN);
            }
            else if(
				stricmp(command, "knife")==0 ||
				stricmp(command, "playshot")==0 ||
				stricmp(command, "fireb")==0 ||
				stricmp(command, "playshotw")==0
			){
                value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->knife, value, MAX_NAME_LEN);
				newchar->ptype = 0;
            }
			else if(stricmp(command, "playshotno")==0){
				value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->knife, value, MAX_NAME_LEN);
				newchar->ptype = 1;
			}
			else if(stricmp(command, "star")==0){
                value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->star, value, MAX_NAME_LEN);
            }
            else if(stricmp(command, "bomb")==0 || stricmp(command, "playbomb")==0){
                value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->bomb, value, MAX_NAME_LEN);
            }
            else if(stricmp(command, "flash")==0){	// Now all characters can have their own flash - even projectiles (useful for blood)
                value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->flash, value, MAX_NAME_LEN);
            }
            else if(stricmp(command, "bflash")==0){	// Flash that is spawned if an attack is blocked
                value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->bflash, value, MAX_NAME_LEN);
            }
            else if(stricmp(command, "dust")==0){	// Spawned when hitting the ground to "kick up dust"
                value = findarg(buf+pos, 1);
                find_model(value);
                strncpy(newchar->dust, value, MAX_NAME_LEN);
            }

            // 7-1-2005 ends here
            
            else if(stricmp(command, "branch")==0){	// for endlevel item's level branch
                value = findarg(buf+pos, 1);
                strncpy(newchar->branch, value, MAX_NAME_LEN);
            }

            else if(stricmp(command, "cantgrab")==0){  //27-12-2004 Finds can't grab variable in character.txt
                newchar->cantgrab = atoi(findarg(buf+pos, 1));		 //27-12-2004 stores variable for character
            }
            else if(stricmp(command, "grabback")==0){
				newchar->grabback = atoi(findarg(buf+pos, 1));
			}
            else if((stricmp(command, "death")==0) || (stricmp(command, "falldie")==0)){  
                newchar->death = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "notgrab")==0){  //27-12-2004 Finds can't grab variable in character.txt
                newchar->cantgrab = atoi(findarg(buf+pos, 1));		//27-12-2004 stores variable for character
            }
            else if(stricmp(command, "speed")==0){
                value = findarg(buf+pos, 1);
                newchar->speed = atoi(value);
                newchar->speed /= 10;

                if(newchar->speed < 0.5) newchar->speed = 0.5;
                if(newchar->speed > 30) newchar->speed = 30;
            }
            else if(stricmp(command, "height")==0){
				newchar->height = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "jumpheight")==0){		// 28-12-2004 if string for jump height found
                value = findarg(buf+pos, 1);								// 28-12-2004 find value
                newchar->jumpheight = atoi(value);					// 28-12-2004 and store for character
            }
            else if(stricmp(command, "grabdistance")==0){		// 30-12-2004 if string for grabdistance found
                newchar->grabdistance = atoi(findarg(buf+pos, 1));					// 30-12-2004 and store for character
            }
            // 1-18-05 adjust throwdamage
            else if(stricmp(command, "throwdamage")==0){
                newchar->throwdamage = atoi(findarg(buf+pos, 1));
            }
            //
            else if(stricmp(command, "shadow")==0){
                newchar->shadow = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "aironly")==0){	// Shadows display in air only?
				newchar->aironly = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "fmap")==0){	// Map that corresponds with the remap when a character is frozen
				newchar->fmap = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "setlayer")==0){
				newchar->setlayer = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "toflip")==0){	// Flag to determine if flashes images will be flipped or not
				newchar->toflip = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "nodieblink")==0){	// Added to determine if dying animation blinks or not
				newchar->nodieblink = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "noatflash")==0){	// Flag to determine if an opponents attack spawns their flash or not
				newchar->noatflash = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "nomove")==0){	// If set, enemy will be static (speed must be set to 0 or left blank)
				newchar->nomove = atoi(findarg(buf+pos, 1));
				newchar->noflip = atoi(findarg(buf+pos, 2));	// If set, static enemy will not flip directions
			}
			else if(stricmp(command, "nodrop")==0){
				newchar->nodrop = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "thold")==0){	// Threshold for enemies/players block
				newchar->thold = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "running")==0){	// The speed at which the player runs
				newchar->runspeed = atoi(findarg(buf+pos, 1));
                newchar->runspeed /= 10;

				if(newchar->runspeed < 0.5) newchar->runspeed = 0.5;

                newchar->runjumpheight = atoi(findarg(buf+pos, 2));	// The height at which a player jumps when running
                newchar->runjumpdist = atoi(findarg(buf+pos, 3));	// The distance a player jumps when running
                newchar->runupdown = atoi(findarg(buf+pos, 4));
                newchar->runhold = atoi(findarg(buf+pos, 5));
			}
			else if(stricmp(command, "blockodds")==0){	// Odds that an attack will hit an enemy (1 : blockodds)
				newchar->blockodds = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "holdblock")==0){
				newchar->holdblock = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "blockpain")==0){
				newchar->blockpain = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "paingrab")==0){
				newchar->paingrab = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "throw")==0){
				newchar->throwdist = atoi(findarg(buf+pos, 1));
				newchar->throwheight = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "throwframewait")==0){
				newchar->throwframewait = atoi(findarg(buf+pos, 1));              
			}
            else if(stricmp(command, "diesound")==0){
                newchar->diesound = loadcache_sound(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "icon")==0){
                value = findarg(buf+pos, 1);

                if(newchar->icon > -1){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error: model '%s' has multiple icons defined", filename);
                }

                newchar->icon = loadsprite(value,0,0);
                newchar->iconpain = loadsprite(value,0,0);   // 20-1-2005 load icon into empty values
                newchar->icondie = loadsprite(value,0,0);   // 20-1-2005 load icon into empty values
                newchar->iconget = loadsprite(value,0,0);  // 20-1-2005 load icon into empty values
            }
            else if(stricmp(command, "iconpain")==0){    // 20-1-2005 New icons here
                value = findarg(buf+pos, 1);
                newchar->iconpain = loadsprite(value,0,0);
            }
            else if(stricmp(command, "icondie")==0){
                value = findarg(buf+pos, 1);
                newchar->icondie = loadsprite(value,0,0);
            }
            else if(stricmp(command, "iconget")==0){
                value = findarg(buf+pos, 1);
                newchar->iconget = loadsprite(value,0,0);
            }                                // 20-1-2005 New icons finish here
			else if(stricmp(command, "iconw")==0){
				value = findarg(buf+pos, 1);
				newchar->iconw = loadsprite(value,0,0);
			}
			else if(stricmp(command, "iconmphigh")==0){
				value = findarg(buf+pos, 1);
				newchar->iconmp[0] = loadsprite(value,0,0);
			}
			else if(stricmp(command, "iconmphalf")==0){
				value = findarg(buf+pos, 1);
				newchar->iconmp[1] = loadsprite(value,0,0);
			}
			else if(stricmp(command, "iconmplow")==0){
				value = findarg(buf+pos, 1);
				newchar->iconmp[2] = loadsprite(value,0,0);
			}
            else if(stricmp(command, "parrow")==0){	// Image that is displayed when player 1 spawns invincible
				value = findarg(buf+pos, 1);
				newchar->parrow[0][0] = loadsprite(value,0,0);
				newchar->parrow[0][1] = atoi(findarg(buf+pos, 2));
				newchar->parrow[0][2] = atoi(findarg(buf+pos, 3));
			}
            else if(stricmp(command, "parrow2")==0){	// Image that is displayed when player 2 spawns invincible
				value = findarg(buf+pos, 1);
				newchar->parrow[1][0] = loadsprite(value,0,0);
				newchar->parrow[1][1] = atoi(findarg(buf+pos, 2));
				newchar->parrow[1][2] = atoi(findarg(buf+pos, 3));
			}
            else if(stricmp(command, "parrow3")==0){	// Image that is displayed when player 3 spawns invincible
				value = findarg(buf+pos, 1);
				newchar->parrow[2][0] = loadsprite(value,0,0);
				newchar->parrow[2][1] = atoi(findarg(buf+pos, 2));
				newchar->parrow[2][2] = atoi(findarg(buf+pos, 3));
			}
            else if(stricmp(command, "parrow4")==0){	// Image that is displayed when player 4 spawns invincible
				value = findarg(buf+pos, 1);
				newchar->parrow[3][0] = loadsprite(value,0,0);
				newchar->parrow[3][1] = atoi(findarg(buf+pos, 2));
				newchar->parrow[3][2] = atoi(findarg(buf+pos, 3));
			}
			else if(stricmp(command, "atchain")==0){

				newchar->chainlength = 0;

				for(i = 0; i < MAX_ATCHAIN; i++){

					newchar->atchain[i] = atoi(findarg(buf+pos, i + 1));

					if(newchar->atchain[i] < 0) newchar->atchain[i] = 0;
					if(newchar->atchain[i] > MAX_ATTACKS) newchar->atchain[i] = MAX_ATTACKS;
					if(newchar->atchain[i]) newchar->chainlength = i;
				}
			}
			else if(stricmp(command, "credit")==0){
                newchar->credit = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "nopain")==0){
                newchar->nopain = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "escapehits")==0){      // How many times an enemy can be hit before retaliating
                newchar->escapehits = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "chargerate")==0){      // How much mp does this character gain while recharging?
                newchar->chargerate = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "mprate")==0){
                newchar->mprate = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "sleepwait")==0){
                newchar->sleepwait = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "aggression")==0){
                value = findarg(buf+pos, 1);
                newchar->aggression = atoi(value);
            }
            else if(stricmp(command, "risetime")==0){
                value = findarg(buf+pos, 1);
                newchar->risetime = atoi(value);
            }
            else if(stricmp(command, "facing")==0){
                value = findarg(buf+pos, 1);
                newchar->facing = atoi(value);
            }
            else if(stricmp(command, "turndelay")==0){
                value = findarg(buf+pos, 1);
                newchar->turndelay = atoi(value);
            }
            else if(stricmp(command, "lifespan")==0){
                value = findarg(buf+pos, 1);
                newchar->lifespan = atoi(value);
            }
            else if(stricmp(command, "summonkill")==0){
                value = findarg(buf+pos, 1);
                newchar->summonkill = atoi(value);
            }


			// Section for custom freespecials starts here

            else if(stricmp(command, "com")==0){
				int i;
				int t;
				for(i = 0, t = 1; i < 4; i++, t++){
					value = findarg(buf+pos, t);
					if(stricmp(value, "u")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_MOVEUP;
					}
					else if(stricmp(value, "d")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_MOVEDOWN;
					}
					else if(stricmp(value, "f")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_FORWARD;
					}
					else if(stricmp(value, "b")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_BACKWARD;
					}
					else if(stricmp(value, "a")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_ATTACK;
					}
					else if(stricmp(value, "j")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_JUMP;
					}
					else if(stricmp(value, "s")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_SPECIAL;
					}
					else if(stricmp(value, "k")==0){
						newchar->special[newchar->specials_loaded][i] = FLAG_SPECIAL;
					}
					else if(stricmp(value, "freespecial")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL;
					}
					else if(stricmp(value, "freespecial2")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL2;
					}
					else if(stricmp(value, "freespecial3")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL3;
					}
					else if(stricmp(value, "freespecial4")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL4;
					}
					else if(stricmp(value, "freespecial5")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL5;
					}
					else if(stricmp(value, "freespecial6")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL6;
					}
					else if(stricmp(value, "freespecial7")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL7;
					}
					else if(stricmp(value, "freespecial8")==0){
						newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL8;
					}
					else{
						if(buf != NULL){
							tracefree(buf);
							buf = NULL;
						}
					    shutdown("Invalid freespecial command '%s'", value);
                	}
				}
				newchar->specials_loaded++;
			}

            // End section for custom freespecials

            else if(stricmp(command, "remap")==0){
                value = findarg(buf+pos, 1);
                value2 = findarg(buf+pos, 2);
				errorVal = load_colourmap(newchar, value, value2);

                if(!errorVal){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
					switch(errorVal){
						case 0:
							shutdown("Failed to create colourmap from images\n\t'%s'\nand\n\t'%s'. Image Used Twice!", value, value2);
							break;
						case -1:
							shutdown("Failed to create colourmap from images\n\t'%s'\nand\n\t'%s'. MAX_COLOUR_MAPS full!", value, value2);
							break;
						case -2:
							shutdown("Failed to create colourmap from images\n\t'%s'\nand\n\t'%s'. Failed to tracemalloc(256)!", value, value2);
							break;
						case -3:
							shutdown("Failed to create colourmap from images\n\t'%s'\nand\n\t'%s'. Failed to create bitmap1", value, value2);
							break;
						case -4:
							shutdown("Failed to create colourmap from images\n\t'%s'\nand\n\t'%s'. Failed to create bitmap2", value, value2);
							break;
					}
                }
            }
				//new alpha effects by tails
		    else if(stricmp(command, "alpha")==0){
				newchar->alpha = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "remove")==0){
				newchar->remove = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "anim")==0){
                value = findarg(buf+pos, 1);
                // Create new animation
                newanim = alloc_anim();
                if(newanim==NULL){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Not enough memory for animations!");
                }
                if(stricmp(value, "idle")==0){
                    newchar->animation[ANI_IDLE] = newanim;
                }
                else if(stricmp(value, "waiting")==0){
					newchar->animation[ANI_SELECT] = newanim;
				}
                else if(stricmp(value, "walk")==0){
                    newchar->animation[ANI_WALK] = newanim;
                }
				else if(stricmp(value, "sleep")==0){
                    newchar->animation[ANI_SLEEP] = newanim;
                } 
                else if(stricmp(value, "run")==0){
                    newchar->animation[ANI_RUN] = newanim;
                }
				else if(stricmp(value, "up")==0){	// Mar 2, 2005 - Used for when going up
					newchar->animation[ANI_UP] = newanim;
				}
				else if(stricmp(value, "down")==0){	// Mar 2, 2005 - Used for when going down
					newchar->animation[ANI_DOWN] = newanim;
				}
				else if(stricmp(value, "backwalk")==0){
                    newchar->animation[ANI_BACKWALK] = newanim;
                } 
                else if(stricmp(value, "jump")==0){
                    newchar->animation[ANI_JUMP] = newanim;
                    newchar->animation[ANI_JUMP]->range[0] = 50;	// Used for enemies that jump on walls
                    newchar->animation[ANI_JUMP]->range[1] = 60;	// Used for enemies that jump on walls
                }
                else if(stricmp(value, "duck")==0){
                    newchar->animation[ANI_DUCK] = newanim;
                }
                else if(stricmp(value, "land")==0){
                    newchar->animation[ANI_LAND] = newanim;
                }
                else if(stricmp(value, "pain")==0 || stricmp(value, "pain1")==0){
                    newchar->animation[ANI_PAIN] = newanim;
                } 
				else if(stricmp(value, "pain2")==0){
                    newchar->animation[ANI_PAIN2] = newanim;
                }
                else if(stricmp(value, "pain3")==0){
                    newchar->animation[ANI_PAIN3] = newanim;
                }
                else if(stricmp(value, "pain4")==0){
                    newchar->animation[ANI_PAIN4] = newanim;
                } 
				else if(stricmp(value, "pain5")==0){
                    newchar->animation[ANI_PAIN5] = newanim;
                }
                else if(stricmp(value, "pain6")==0){
                    newchar->animation[ANI_PAIN6] = newanim;
                }
                else if(stricmp(value, "pain7")==0){
                    newchar->animation[ANI_PAIN7] = newanim;
                }
				else if(stricmp(value, "pain8")==0){
                    newchar->animation[ANI_PAIN8] = newanim;
                }
                else if(stricmp(value, "pain9")==0){
                    newchar->animation[ANI_PAIN9] = newanim;
                }
                else if(stricmp(value, "pain10")==0){
                    newchar->animation[ANI_PAIN10] = newanim;
                }
                else if(stricmp(value, "spain")==0){	// If shock attacks don't knock opponent down, play this
                    newchar->animation[ANI_SHOCKPAIN] = newanim;
                }
                else if(stricmp(value, "bpain")==0){	// If burn attacks don't knock opponent down, play this
                    newchar->animation[ANI_BURNPAIN] = newanim;
                }
                else if(stricmp(value, "fall")==0 || stricmp(value, "fall1")==0){
                    newchar->animation[ANI_FALL] = newanim;   // If no new animation, load fall animation into both "respawn" & "fall"
                }
                else if(stricmp(value, "fall2")==0){
                    newchar->animation[ANI_FALL2] = newanim;
                }
                else if(stricmp(value, "fall3")==0){
                    newchar->animation[ANI_FALL3] = newanim;
                }
                else if(stricmp(value, "fall4")==0){
                    newchar->animation[ANI_FALL4] = newanim;
                }
				else if(stricmp(value, "fall5")==0){
                    newchar->animation[ANI_FALL5] = newanim;
                }
                else if(stricmp(value, "fall6")==0){
                    newchar->animation[ANI_FALL6] = newanim;
                }
                else if(stricmp(value, "fall7")==0){
                    newchar->animation[ANI_FALL7] = newanim;
                }
				else if(stricmp(value, "fall8")==0){
                    newchar->animation[ANI_FALL8] = newanim;
                }
                else if(stricmp(value, "fall9")==0){
                    newchar->animation[ANI_FALL9] = newanim;
                }
                else if(stricmp(value, "fall10")==0){
                    newchar->animation[ANI_FALL10] = newanim;
                }
                else if(stricmp(value, "shock")==0){	// If shock attacks do knock opponent down, play this
					newchar->animation[ANI_SHOCK] = newanim;
				}
                else if(stricmp(value, "burn")==0){	// If burn attacks do knock opponent down, play this
					newchar->animation[ANI_BURN] = newanim;
				}
                else if(stricmp(value, "death")==0 || stricmp(value, "death1")==0){      //  If new animation is present, overwrite new_anim with it.
                    newchar->animation[ANI_DIE] = newanim;
                }
                else if(stricmp(value, "death2")==0){
                    newchar->animation[ANI_DIE2] = newanim;
                }
                else if(stricmp(value, "death3")==0){
                    newchar->animation[ANI_DIE3] = newanim;
                }
                else if(stricmp(value, "death4")==0){
                    newchar->animation[ANI_DIE4] = newanim;
                }
				else if(stricmp(value, "death5")==0){
                    newchar->animation[ANI_DIE5] = newanim;
                }
                else if(stricmp(value, "death6")==0){
                    newchar->animation[ANI_DIE6] = newanim;
                }
                else if(stricmp(value, "death7")==0){
                    newchar->animation[ANI_DIE7] = newanim;
                }
				else if(stricmp(value, "death8")==0){
                    newchar->animation[ANI_DIE8] = newanim;
                }
                else if(stricmp(value, "death9")==0){
                    newchar->animation[ANI_DIE9] = newanim;
                }
                else if(stricmp(value, "death10")==0){
                    newchar->animation[ANI_DIE10] = newanim;
                }
                else if(stricmp(value, "sdie")==0){
                    newchar->animation[ANI_SHOCKDIE] = newanim;
                }
                else if(stricmp(value, "bdie")==0){
                    newchar->animation[ANI_BURNDIE] = newanim;
                }
                else if(stricmp(value, "rise")==0){
                    newchar->animation[ANI_RISE] = newanim;
                }
                else if(stricmp(value, "riseattack")==0){
					newchar->animation[ANI_RISEATTACK] = newanim;
				}
                else if(stricmp(value, "select")==0){
                    newchar->animation[ANI_PICK] = newanim;
                }
                else if(stricmp(value, "attack1")==0){
                    newchar->animation[ANI_ATTACK1] = newanim;
                }
                else if(stricmp(value, "attack2")==0){
                    newchar->animation[ANI_ATTACK2] = newanim;
                }
                else if(stricmp(value, "attack3")==0){
                    newchar->animation[ANI_ATTACK3] = newanim;
                }
                else if(stricmp(value, "attack4")==0){
                    newchar->animation[ANI_ATTACK4] = newanim;
                }
                else if(stricmp(value, "throwattack")==0){
                    newchar->animation[ANI_THROWATTACK] = newanim;
                }
                else if(stricmp(value, "upper")==0){
                    newchar->animation[ANI_UPPER] = newanim;
                }
				else if(stricmp(value, "cant")==0){
					newchar->animation[ANI_CANT] = newanim;
				}
				else if(stricmp(value, "jumpcant")==0){
                    newchar->animation[ANI_JUMPCANT] = newanim;
                } 
				else if(stricmp(value, "charge")==0){
                    newchar->animation[ANI_CHARGE] = newanim;
                } 
				else if(stricmp(value, "faint")==0){
					newchar->animation[ANI_FAINT] = newanim;
				}
                else if(stricmp(value, "dodge")==0){	
					newchar->animation[ANI_DODGE] = newanim;
				}
                else if(stricmp(value, "special")==0 || stricmp(value, "special1")==0){
                    newchar->animation[ANI_SPECIAL] = newanim;
                    newchar->animation[ANI_SPECIAL]->energycost = 6;    
                }
                else if(stricmp(value, "special2")==0){
                    newchar->animation[ANI_SPECIAL2] = newanim;
                }
                else if(stricmp(value, "special3")==0 || stricmp(value, "jumpspecial")==0){
                    newchar->animation[ANI_JUMPSPECIAL] = newanim;
                } 
                else if(stricmp(value, "freespecial")==0 || stricmp(value, "freespecial1")==0){
                    newchar->animation[ANI_FREESPECIAL] = newanim;
                    if(!is_set(newchar, ANI_FREESPECIAL)){
                        newchar->special[newchar->specials_loaded][0] = FLAG_FORWARD;
                        newchar->special[newchar->specials_loaded][1] = FLAG_FORWARD;
                        newchar->special[newchar->specials_loaded][2] = FLAG_ATTACK;
                        newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL;
                        newchar->specials_loaded++;
                    }
                }
                else if(stricmp(value, "freespecial2")==0){
                    newchar->animation[ANI_FREESPECIAL2] = newanim;
                    if(!is_set(newchar, ANI_FREESPECIAL2)){ // Set defaults if not set
                        newchar->special[newchar->specials_loaded][0] = FLAG_MOVEDOWN;
                        newchar->special[newchar->specials_loaded][1] = FLAG_MOVEDOWN;
                        newchar->special[newchar->specials_loaded][2] = FLAG_ATTACK;
                        newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL2;
                        newchar->specials_loaded++;
                    }
                }
                else if(stricmp(value, "freespecial3")==0){
                    newchar->animation[ANI_FREESPECIAL3] = newanim;
                    if(!is_set(newchar, ANI_FREESPECIAL3)){     // Set defaults if not set
                        newchar->special[newchar->specials_loaded][0] = FLAG_MOVEUP;
                        newchar->special[newchar->specials_loaded][1] = FLAG_MOVEUP;
                        newchar->special[newchar->specials_loaded][2] = FLAG_ATTACK;
                        newchar->special[newchar->specials_loaded][3] = ANI_FREESPECIAL3;
                        newchar->specials_loaded++;
                    }
                }

                // New freespecials here freespecial4 - freespecial8

                else if(stricmp(value, "freespecial4")==0){
                    newchar->animation[ANI_FREESPECIAL4] = newanim;
                }
                else if(stricmp(value, "freespecial5")==0){
                    newchar->animation[ANI_FREESPECIAL5] = newanim;
                }
                else if(stricmp(value, "freespecial6")==0){
                    newchar->animation[ANI_FREESPECIAL6] = newanim;
                }
                else if(stricmp(value, "freespecial7")==0){
                    newchar->animation[ANI_FREESPECIAL7] = newanim;
                }
                else if(stricmp(value, "freespecial8")==0){
                    newchar->animation[ANI_FREESPECIAL8] = newanim;
                } 
                else if(stricmp(value, "jumpattack")==0){
                    newchar->animation[ANI_JUMPATTACK] = newanim;
                }
                else if(stricmp(value, "jumpattack2")==0){
                    newchar->animation[ANI_JUMPATTACK2] = newanim;
                }
                else if(stricmp(value, "jumpattack3")==0){
                    newchar->animation[ANI_JUMPATTACK3] = newanim;
                }
                else if(stricmp(value, "jumpforward")==0){
					newchar->animation[ANI_JUMPFORWARD] = newanim;
				}
				else if(stricmp(value, "runjumpattack")==0){
					newchar->animation[ANI_RUNJUMPATTACK] = newanim;
                }
                else if(stricmp(value, "runattack")==0){
                    newchar->animation[ANI_RUNATTACK] = newanim;	// New attack for when a player is running
                }
                else if(stricmp(value, "attackup")==0){
                    newchar->animation[ANI_ATTACKUP] = newanim;	// New attack for when a player presses u u
                }
                else if(stricmp(value, "attackdown")==0){
                    newchar->animation[ANI_ATTACKDOWN] = newanim;	// New attack for when a player presses d d
                }
                else if(stricmp(value, "attackforward")==0){
                    newchar->animation[ANI_ATTACKFORWARD] = newanim;	// New attack for when a player presses f f
                }
                else if(stricmp(value, "attackbackward")==0){
                    newchar->animation[ANI_ATTACKBACKWARD] = newanim;	// New attack for when a player presses b a
                }
                else if(stricmp(value, "attackboth")==0){	// Attack that is executed by holding down j and pressing a
					newchar->animation[ANI_ATTACKBOTH] = newanim;
				}
                else if(stricmp(value, "get")==0){
                    newchar->animation[ANI_GET] = newanim;
                }
                else if(stricmp(value, "grab")==0){
                    newchar->animation[ANI_GRAB] = newanim;
                }
                else if(stricmp(value, "grabbed")==0){	// New grabbed animation for when grabbed
                    newchar->animation[ANI_GRABBED] = newanim;
                }
                else if(stricmp(value, "grabattack")==0){
                    newchar->animation[ANI_GRABATTACK] = newanim;
                }
                else if(stricmp(value, "grabattack2")==0){
                    newchar->animation[ANI_GRABATTACK2] = newanim;
                }
                else if(stricmp(value, "grabforward")==0){	// New grab attack for when pressing forward attack
                    newchar->animation[ANI_GRABFORWARD] = newanim;
                }
                else if(stricmp(value, "grabforward2")==0){	// New grab attack for when pressing forward attack
                    newchar->animation[ANI_GRABFORWARD2] = newanim;
                }
                else if(stricmp(value, "grabup")==0){	// New grab attack for when pressing up attack
                    newchar->animation[ANI_GRABUP] = newanim;
                }
                else if(stricmp(value, "grabup2")==0){	// New grab attack for when pressing up attack
                    newchar->animation[ANI_GRABUP2] = newanim;
                }
                else if(stricmp(value, "grabdown")==0){	// New grab attack for when pressing down attack
                    newchar->animation[ANI_GRABDOWN] = newanim;
                }
                else if(stricmp(value, "grabdown2")==0){	// New grab attack for when pressing down attack
                    newchar->animation[ANI_GRABDOWN2] = newanim;
                }
                else if(stricmp(value, "spawn")==0){      //  spawn/respawn works separately now
                    newchar->animation[ANI_SPAWN] = newanim;
                }
                else if(stricmp(value, "respawn")==0){      //  spawn/respawn works separately now
                    newchar->animation[ANI_RESPAWN] = newanim;
                }
                else if(stricmp(value, "throw")==0){
                    newchar->animation[ANI_THROW] = newanim;
                }
                else if(stricmp(value, "block")==0){	// Now enemies can block attacks on occasion
                    newchar->animation[ANI_BLOCK] = newanim;
                }
				else if(stricmp(value, "follow1")==0){
                    newchar->animation[ANI_FOLLOW1] = newanim;
                }
                else if(stricmp(value, "follow2")==0){
                    newchar->animation[ANI_FOLLOW2] = newanim;
                }
                else if(stricmp(value, "follow3")==0){
                    newchar->animation[ANI_FOLLOW3] = newanim;
                }
                else if(stricmp(value, "follow4")==0){
                    newchar->animation[ANI_FOLLOW4] = newanim;
                }
                else if(stricmp(value, "chargeattack")==0){
                    newchar->animation[ANI_CHARGEATTACK] = newanim;
                }
                else if(stricmp(value, "vault")==0){
                    newchar->animation[ANI_VAULT] = newanim;
                } 
                else if(stricmp(value, "turn")==0){
                    newchar->animation[ANI_TURN] = newanim;
                }
                else{
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Invalid animation name '%s'", value);
                }

                // Reset vars
                curframe = 0;
                memset(bbox, 0, 4*sizeof(int));
                memset(attack, 0, 4*sizeof(int));
                memset(offset, 0, 2*sizeof(int));
                memset(platform, 0, 8*sizeof(int));
                attackforce = 0;
                move = 0;
                movez = 0;
                movea = 0;
                seta = -1;

                if(!newanim->range[0]) newanim->range[0] = -10;
				newanim->range[1] = newchar->jumpheight*20; // 30-12-2004 default range affected by jump height

                newanim->moveflag = 0;	// Default disabled
                newanim->fastattack = 0;
                newanim->mponly = 0;
                newanim->throwframe = -1;
                newanim->tossframe = -1;// this get 1 of weapons numshots shots in the animation that you want(normaly the last)by tails
                newanim->shootframe = -1;
                newanim->jumpframe = -1;
                newanim->soundtoplay = -1;
                newanim->hitsound = -1;
                newanim->dive[0] = newanim->dive[1] = 0;//new dive kick by tails
				newanim->flipframe = -1;
				newanim->followanim = 0; // Default disabled
                newanim->followcond = 0;
                newanim->counterframe = -1;
                newanim->countercond = 0;
                newanim->counterdamage = 0; // Default no damage on counter
				for(i=0; i<2; i++) newanim->weaponframe[i] = -1;
				for(i=0; i<2; i++) newanim->quakeframe[i] = -1;
				for(i=1; i<5; i++) newanim->summonframe[i] = 0;
				for(i=1; i<5; i++) newanim->spawnframe[i] = 0;
				newanim->summonframe[0] = newanim->spawnframe[0] = -1;
                newanim->unsummonframe = -1;
				newanim->quakeframe[3] = 0;
            }
            else if(stricmp(command, "loop")==0){
                if(newanim == NULL){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Can't set loop: no animation specified!");
                }

                newanim->loop = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "delay")==0){
                delay = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "offset")==0){
                offset[0] = atoi(findarg(buf+pos, 1));
                offset[1] = atoi(findarg(buf+pos, 2));
            }
				// 1-10-05  adjust the energycost of specials
            else if(stricmp(command, "energycost")==0 || stricmp(command, "mpcost")==0){
                newanim->energycost = atoi(findarg(buf+pos, 1));
                if(newanim->energycost < 0) newanim->energycost = 0;	// No more negative energycosts
            }
	    	else if(stricmp(command, "mponly")==0){
				newanim->mponly = atoi(findarg(buf+pos, 1));
			}
				//dive kicks for players like tmnt by tails
			else if(stricmp(command, "dive")==0){
				newanim->dive[0] = atoi(findarg(buf+pos, 1));
				newanim->dive[1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "dive1")==0){
				newanim->dive[0] = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "dive2")==0){
				newanim->dive[1] = atoi(findarg(buf+pos, 1));
			}
            else if(
				stricmp(command, "throwframe")==0 ||
				stricmp(command, "pshotframe")==0 ||
				stricmp(command, "pshotframeno")==0 ||
				stricmp(command, "pshotframew")==0
			){
                newanim->throwframe = atoi(findarg(buf+pos, 1));
                newanim->throwa = atoi(findarg(buf+pos, 2));

                if(!newanim->throwa) newanim->throwa = 70;
                else if(newanim->throwa == -1) newanim->throwa = 0;
            }
            else if(stricmp(command, "tossframe")==0 || stricmp(command, "pbombframe")==0){	// Frame at which bombs are thrown
                newanim->tossframe = atoi(findarg(buf+pos, 1));
                newanim->throwa = atoi(findarg(buf+pos, 2));

                if(!newanim->throwa) newanim->throwa = 70;
                else if(newanim->throwa == -1) newanim->throwa = 0;
			}
	            // 7-1-2005 players need to throw their projectile!
			else if(stricmp(command, "custpshot")==0 || stricmp(command, "custknife")==0 || stricmp(command, "custpshotw")==0){
				value = findarg(buf+pos, 1);
				find_model(value);
				strncpy(newanim->custknife, value, MAX_NAME_LEN);
			}
			else if(stricmp(command, "custpbomb")==0 || stricmp(command, "custbomb")==0){
				value = findarg(buf+pos, 1);
				find_model(value);
				strncpy(newanim->custbomb, value, MAX_NAME_LEN);
			}
			else if(stricmp(command, "custstar")==0){
				value = findarg(buf+pos, 1);
				find_model(value);
				strncpy(newanim->custstar, value, MAX_NAME_LEN);
			}
            else if(stricmp(command, "shootframe")==0){
                newanim->shootframe = atoi(findarg(buf+pos, 1));
                newanim->throwa = atoi(findarg(buf+pos, 2));

                if(newanim->throwa == -1) newanim->throwa = 0;	// Incase user enters -1
            }
            else if(stricmp(command, "jumpframe")==0){
                newanim->jumpframe = atoi(findarg(buf+pos, 1));
                newanim->moveflag = atoi(findarg(buf+pos, 2));	// Added so movement can be customized for jumpframes
            }
            else if(stricmp(command, "sound")==0){
                newanim->soundframe = curframe;
                newanim->soundtoplay = loadcache_sound(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "hitfx")==0){
				newanim->hitsound = loadcache_sound(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "hitflash")==0){
				value = findarg(buf+pos, 1);
				find_model(value);
                strncpy(newanim->hitflash, value, MAX_NAME_LEN);
			}
			else if(stricmp(command, "fastattack")==0){
				newanim->fastattack = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "bbox")==0){
                bbox[0] = atoi(findarg(buf+pos, 1));
                bbox[1] = atoi(findarg(buf+pos, 2));
                bbox[2] = atoi(findarg(buf+pos, 3));
                bbox[3] = atoi(findarg(buf+pos, 4));

                // printf("Bounding box at %i,%i size %i,%i\n", bbox[0], bbox[1], bbox[2], bbox[3]);
            }
            else if(stricmp(command, "platform")==0){
				platform[0] = atoi(findarg(buf+pos, 1));
				platform[1] = atoi(findarg(buf+pos, 2));
				platform[2] = atoi(findarg(buf+pos, 3));
				platform[3] = atoi(findarg(buf+pos, 4));
				platform[4] = atoi(findarg(buf+pos, 5));
				platform[5] = atoi(findarg(buf+pos, 6));
				platform[6] = atoi(findarg(buf+pos, 7));
				platform[7] = atoi(findarg(buf+pos, 8));
			}
            else if(stricmp(command, "attack")==0  || stricmp(command, "attack1")==0 ||
                    stricmp(command, "attack2")==0 || stricmp(command, "attack3")==0 ||
					stricmp(command, "attack4")==0 || stricmp(command, "attack5")==0 ||
					stricmp(command, "attack6")==0 || stricmp(command, "attack7")==0 ||
					stricmp(command, "attack8")==0 || stricmp(command, "attack9")==0 ||
                    stricmp(command, "attack10")==0 || stricmp(command, "shock")==0   ||
                    stricmp(command, "burn")==0    || stricmp(command, "steal")==0 ||
                    stricmp(command, "freeze")==0){
                        attack[0] = atoi(findarg(buf+pos, 1));
						attack[1] = atoi(findarg(buf+pos, 2));
						attack[2] = atoi(findarg(buf+pos, 3));
						attack[3] = atoi(findarg(buf+pos, 4));
						attackforce = atoi(findarg(buf+pos, 5));
						if(stricmp(command, "freeze")==0){
							attacklength = atoi(findarg(buf+pos, 6)) * GAME_SPEED;
							attackdrop = 0;
						}
						else attackdrop = atoi(findarg(buf+pos, 6));

						noblock = atoi(findarg(buf+pos, 7));
						noflash = atoi(findarg(buf+pos, 8));
						pauseadd = atoi(findarg(buf+pos, 9));

						if(stricmp(command, "attack")==0 || stricmp(command, "attack1")==0) attacktype = ATK_NORMAL;
						else if(stricmp(command, "attack2")==0) attacktype = ATK_NORMAL2;
						else if(stricmp(command, "attack3")==0) attacktype = ATK_NORMAL3;
						else if(stricmp(command, "attack4")==0) attacktype = ATK_NORMAL4;
						else if(stricmp(command, "attack5")==0) attacktype = ATK_NORMAL5;
						else if(stricmp(command, "attack6")==0) attacktype = ATK_NORMAL6;
						else if(stricmp(command, "attack7")==0) attacktype = ATK_NORMAL7;
						else if(stricmp(command, "attack8")==0) attacktype = ATK_NORMAL8;
						else if(stricmp(command, "attack9")==0) attacktype = ATK_NORMAL9;
						else if(stricmp(command, "attack10")==0) attacktype = ATK_NORMAL10;
						else if(stricmp(command, "shock")==0) attacktype = ATK_SHOCK;
						else if(stricmp(command, "burn")==0) attacktype = ATK_BURN;
						else if(stricmp(command, "steal")==0) attacktype = ATK_STEAL;
						else if(stricmp(command, "freeze")==0) attacktype = ATK_FREEZE;
            }
            else if(stricmp(command, "blast")==0){
                attack[0] = atoi(findarg(buf+pos, 1));
                attack[1] = atoi(findarg(buf+pos, 2));
                attack[2] = atoi(findarg(buf+pos, 3));
                attack[3] = atoi(findarg(buf+pos, 4));
                attackforce = atoi(findarg(buf+pos, 5));
                noblock = atoi(findarg(buf+pos, 6));
                noflash = atoi(findarg(buf+pos, 7));
                pauseadd = atoi(findarg(buf+pos, 8));
                attackdrop = 1;
                attacktype = ATK_BLAST;
            }
            else if(stricmp(command, "move")==0){
                move = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "movez")==0){
                movez = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "movea")==0){
                movea = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "seta")==0){
				seta = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "range")==0){
                if(newanim==NULL){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Cannot set range: no animation!");
                }
                newanim->range[0] = atoi(findarg(buf+pos, 1));
                newanim->range[1] = atoi(findarg(buf+pos, 2));
            }
            else if(stricmp(command, "frame")==0){
                if(newanim==NULL){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Cannot add frame: animation not specified!");
                }
                value = findarg(buf+pos, 1);
                // printf("Load sprite '%s'...\n", value);
                index = loadsprite(value, offset[0], offset[1]);

                // Adjust coords: add offsets and change size to coords
                bbox_con[0] = bbox[0] - offset[0];
                bbox_con[1] = bbox[1] - offset[1];
                bbox_con[2] = bbox[2] + bbox_con[0];
                bbox_con[3] = bbox[3] + bbox_con[1];
                attack_con[0] = attack[0] - offset[0];
                attack_con[1] = attack[1] - offset[1];
                attack_con[2] = attack[2] + attack_con[0];
                attack_con[3] = attack[3] + attack_con[1];
                platform_con[0] = platform[0] - offset[0];
                platform_con[1] = platform[1] - offset[0];
                platform_con[2] = platform[2] - offset[0];
                platform_con[3] = platform[3] - offset[0];
                platform_con[4] = platform[4];
                platform_con[5] = platform[5];

                curframe = addframe(newanim, index, delay, bbox_con, attack_con, attackforce, attackdrop, attacktype, move, movez, movea, seta, attacklength, noblock, noflash, pauseadd, platform_con);	// Added attacklength for freeze attacks and noblock for unblockable attacks
            }
			else if(stricmp(command, "flipframe")==0){
                newanim->flipframe = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "followanim")==0){
              newanim->followanim = atoi(findarg(buf+pos, 1));
              if(newanim->followanim > 4) newanim->followanim = 4;
              if(newanim->followanim < 0) newanim->followanim = 0;
            }
            else if(stricmp(command, "followcond")==0){
              newanim->followcond = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "counterframe")==0){
              newanim->counterframe = atoi(findarg(buf+pos, 1));
              newanim->countercond = atoi(findarg(buf+pos, 2));
              newanim->counterdamage = atoi(findarg(buf+pos, 3));
            }
			else if(stricmp(command, "weaponframe")==0){
              newanim->weaponframe[0] = atoi(findarg(buf+pos, 1));
              newanim->weaponframe[1] = atoi(findarg(buf+pos, 2));
            }
			else if(stricmp(command, "quakeframe")==0){
              newanim->quakeframe[0] = atoi(findarg(buf+pos, 1));
              newanim->quakeframe[1] = atoi(findarg(buf+pos, 2));
			  newanim->quakeframe[2] = atoi(findarg(buf+pos, 3));
            }
			else if(stricmp(command, "subentity")==0 || stricmp(command, "custentity")==0){
				value = findarg(buf+pos, 1);
				find_model(value);
                strncpy(newanim->subentity, value, MAX_NAME_LEN);
            }
			else if(stricmp(command, "spawnframe")==0){
              newanim->spawnframe[0] = atoi(findarg(buf+pos, 1));
              newanim->spawnframe[1] = atoi(findarg(buf+pos, 2));
			  newanim->spawnframe[2] = atoi(findarg(buf+pos, 3));
			  newanim->spawnframe[3] = atoi(findarg(buf+pos, 4));
			  newanim->spawnframe[4] = atoi(findarg(buf+pos, 5));
            }
			else if(stricmp(command, "summonframe")==0){
              newanim->summonframe[0] = atoi(findarg(buf+pos, 1));
              newanim->summonframe[1] = atoi(findarg(buf+pos, 2));
			  newanim->summonframe[2] = atoi(findarg(buf+pos, 3));
			  newanim->summonframe[3] = atoi(findarg(buf+pos, 4));
			  newanim->summonframe[4] = atoi(findarg(buf+pos, 5));
            }
			else if(stricmp(command, "unsummonframe")==0){
              newanim->unsummonframe = atoi(findarg(buf+pos, 1));
            }
            else{
                if(buf != NULL){
					tracefree(buf);
					buf = NULL;
				}
                shutdown("Command '%s' not understood in file '%s'!", command, filename);
            }
        }

        // Go to next line
        while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
        while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
    }
    if(buf != NULL){
		tracefree(buf);
		buf = NULL;
	}

    // We need a little more work to initialize the new A.I. types if they are not loaded from file
	if(newchar->hostile<0) {// not been initialized, so initialize it
	    switch (newchar->type){
             case TYPE_ENEMY:
             newchar->hostile = TYPE_PLAYER | TYPE_SHOT;       
             break;
             case TYPE_PLAYER: // dont really needed, since you don't need A.I. control for players
             newchar->hostile = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
             break;
             case TYPE_TRAP: 
             newchar->hostile  = TYPE_ENEMY | TYPE_PLAYER;
             case TYPE_OBSTACLE:
             newchar->hostile = 0;
             break;
             case TYPE_SHOT:  // only target enemies
             newchar->hostile = TYPE_ENEMY ;
             break;
             case TYPE_NPC: // default npc behivior
             newchar->hostile = TYPE_ENEMY | TYPE_OBSTACLE;
             break;
        }
    }
    
	if(newchar->candamage<0) {// not been initialized, so initialize it
	    switch (newchar->type){
             case TYPE_ENEMY:
             newchar->candamage = TYPE_PLAYER | TYPE_OBSTACLE | TYPE_SHOT;       
             break;
             case TYPE_PLAYER:
             newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
             break;
             case TYPE_TRAP:
             newchar->candamage  = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
             case TYPE_OBSTACLE:
             newchar->candamage = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
             break;
             case TYPE_SHOT:
             newchar->candamage = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
             break;
             case TYPE_NPC:
             newchar->candamage = TYPE_ENEMY | TYPE_OBSTACLE;
             break;
        }
    }
    
	if(newchar->projectilehit<0) {// not been initialized, so initialize it
	    switch (newchar->type){
             case TYPE_ENEMY:
             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;       
             break;
             case TYPE_PLAYER:
             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
             break;
             case TYPE_TRAP: // hmm, don't really needed
             newchar->projectilehit  = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
             case TYPE_OBSTACLE: // hmm, don't really needed
             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
             break;
             case TYPE_SHOT: // hmm, don't really needed
             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
             break;
             case TYPE_NPC:
             newchar->projectilehit = TYPE_ENEMY | TYPE_PLAYER | TYPE_OBSTACLE;
             break;
        }
    }

    // If all models are loaded, the cache becomes obsolete
    // if(models_loaded >= models_cached) free_modelcache();
    remove_from_cache(name);
}



int is_set(s_model * model, int m){	// New function to determine if a freespecial has been set
	int i;

	for(i = 0; i < model->specials_loaded; i++){
		if(model->special[i][3] == m){
			return 1;
		}
	}

	return 0;
}



// Load / cache all models
int load_models(){
    char * filename = "data/models.txt";

    int handle;
    char *buf;
    unsigned int size;
    int pos;

    char * command;
    char value1[128];
    char value2[128];



    // Read file

    if((handle=openpackfile(filename,packfile)) < 0) shutdown("Error loading model list from %s", filename);
    size = seekpackfile(handle,0,SEEK_END);
    seekpackfile(handle,0,SEEK_SET);

    buf = (char*)tracemalloc("load_models",size+1);
    if(buf==NULL){
        closepackfile(handle);
        shutdown("Not enough memory to allocate %i-byte buffer for file %s", size, filename);
    }
    if(readpackfile(handle, buf, size) != size){
        if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
        closepackfile(handle);
        shutdown("Read error accessing file %s", filename);
    }
    buf[size] = 0;		// Terminate string (important!)
    closepackfile(handle);



    // Now interpret the contents of buf line by line
    pos = 0;
    while(pos<size){
        command = findarg(buf+pos, 0);
        if(command[0]){
            if(stricmp(command, "load")==0){
                // Add path to cache list
                strncpy(value1, findarg(buf+pos, 1), 127);
                strncpy(value2, findarg(buf+pos, 2), 127);
                cache_model(value1, value2);

                // Now load the cached model
                load_cached_model(value1);
            }
            else if(stricmp(command, "colourselect")==0){   // 6-2-2005 if string for colourselect found
                colourselect =  atoi(findarg(buf+pos, 1));          //  6-2-2005
            }
			else if(stricmp(command, "spdirection")==0){				// Select Player Direction for select player screen
                spdirection[0] =  atoi(findarg(buf+pos, 1));			
				spdirection[1] =  atoi(findarg(buf+pos, 2));			
				spdirection[2] =  atoi(findarg(buf+pos, 3));			
				spdirection[3] =  atoi(findarg(buf+pos, 4));			
            }
            else if(stricmp(command, "autoland")==0){	// New flag to determine if a player auto lands when thrown by another player (2 completely disables the ability to land)
				autoland = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "nolost")==0){	// this is use for dont lost your weapon if you grab a enemy flag it to 1 to no drop by tails
				nolost = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "ajspecial")==0){	// Flag to determine if a + j executes special
				ajspecial = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "nocost")==0){	// Nocost set in models.txt
				nocost = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "nocheats")==0){   // 6-2-2005 if string for colourselect found
                forcecheatsoff =  atoi(findarg(buf+pos, 1));          //  6-2-2005
            }
            else if(stricmp(command, "nodropen")==0){
				nodropen = 1;
			}
            else if(stricmp(command, "know")==0){
                // Just add path to cache list
                strncpy(value1, findarg(buf+pos, 1), 127);
                strncpy(value2, findarg(buf+pos, 2), 127);
                cache_model(value1, value2);
            }
			else if(stricmp(command, "noaircancel")==0){
                 noaircancel = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "mpblock")==0){               // Take from MP first?
                 mpblock = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "blockratio")==0){            // Nullify or reduce damage?
                 blockratio = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "nochipdeath")==0){
                 nochipdeath = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "lifescore")==0){
                lifescore =  atoi(findarg(buf+pos, 1));
            }                    // Number of points needed to earn a 1-up
            else if(stricmp(command, "credscore")==0){
                credscore =  atoi(findarg(buf+pos, 1));
            }                    // Number of points needed to earn a credit
			else if(stricmp(command, "versusdamage")==0){
                versusdamage =  atoi(findarg(buf+pos, 1));
				if(versusdamage == 0 || versusdamage == 1) savedata.mode = versusdamage^1;
            }
            else{
                if(buf != NULL){
					tracefree(buf);
					buf = NULL;
				}
                shutdown("Command '%s' not understood in file '%s'!", command, filename);
            }
        }

        // Go to next line
        while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
        while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
    }


    if(buf != NULL){
		tracefree(buf);
		buf = NULL;
	}
    return 1;
}




void unload_levelorder(){
	int i, j;
	for(j=0; j<MAX_DIFFICULTIES; j++){
		for(i=0; i<MAX_LEVELS; i++){
			if(levelorder[j][i] != NULL){
				tracefree(levelorder[j][i]);
				levelorder[j][i] = NULL;
			}
		}
		num_levels[j] = 0;
		strcpy(set_names[j], "");
	}
	num_difficulties = 0;
}



// Add a level to the level order
void add_level(char *filename, int diff){
	int Zs[3] = {0,0,0};

	if(z_coords[0] > 0) Zs[0] = z_coords[0];
	else Zs[0] = PLAYER_MIN_Z;

	if(z_coords[1] > 0) Zs[1] = z_coords[1];
	else Zs[1] = PLAYER_MAX_Z;

	if(z_coords[2] > 0) Zs[2] = z_coords[2];
	else Zs[2] = PLAYER_MIN_Z;

	if(diff > MAX_DIFFICULTIES) return;
	if(num_levels[diff] >= MAX_LEVELS) shutdown("Too many entries in level order (max. %i)!", MAX_LEVELS);

    levelorder[diff][num_levels[diff]] = (s_level_entry*)tracemalloc("add_level",sizeof(s_level_entry));

    if(levelorder[diff][num_levels[diff]] == NULL) shutdown("Out of memory loading level order!");

    memset(levelorder[diff][num_levels[diff]], 0, sizeof(s_level_entry));
    strncpy(levelorder[diff][num_levels[diff]]->filename, filename, 127);
    strncpy(levelorder[diff][num_levels[diff]]->branchname, branch_name, MAX_NAME_LEN);
    levelorder[diff][num_levels[diff]]->z_coords[0] = Zs[0];
	levelorder[diff][num_levels[diff]]->z_coords[1] = Zs[1];
	levelorder[diff][num_levels[diff]]->z_coords[2] = Zs[2];
    num_levels[diff]++;
}



// Add a scene to the level order
void add_scene(char *filename, int diff){
    if(diff > MAX_DIFFICULTIES) return;
    if(num_levels[diff] >= MAX_LEVELS) shutdown("Too many entries in level order (max. %i)!", MAX_LEVELS);

    levelorder[diff][num_levels[diff]] = (s_level_entry*)tracemalloc("add_scene",sizeof(s_level_entry));

    if(levelorder[diff][num_levels[diff]] == NULL) shutdown("Out of memory loading level order!");

    memset(levelorder[diff][num_levels[diff]], 0, sizeof(s_level_entry));
    strncpy(levelorder[diff][num_levels[diff]]->filename, filename, 127);
    strncpy(levelorder[diff][num_levels[diff]]->branchname, branch_name, MAX_NAME_LEN);
    levelorder[diff][num_levels[diff]]->is_scene = 1;
    num_levels[diff]++;
}



// Load list of levels
void load_levelorder(){
	
	char filename[128] = "";

	int handle;
	char *buf;
	unsigned int size;
	int pos;
	int current_set;
	char * command;
	char value1[128];
	int plifeUsed[2]	 = {0,0};
	int elifeUsed[2]	 = {0,0};
	int piconUsed[2]	 = {0,0};
	int piconwUsed[2]	 = {0,0};
	int eiconUsed[4]	 = {0,0,0,0};
	int pmpUsed[4]		 = {0,0,0,0};
	int plifeXused[4]	 = {0,0,0,0};		// 4-7-2006 New custimizable variable for players 'x'
	int plifeNused[4]	 = {0,0,0,0};		// 4-7-2006 New custimizable variable for players 'lives'
	int enameused[4]	 = {0,0,0,0};		// 4-7-2006 New custimizable variable for enemy names
	int pnameJused[4]	 = {0,0,0,0};		// 1-8-2006 New custimizable variable for players name Select Hero
	int pscoreUsed[4]	 = {0,0,0,0};		// 1-8-2006 New custimizable variable for players name Select Hero

	unload_levelorder();

	if(strcmp(customLevels,"empty") == 0) strcpy(filename,"data/levels.txt");
	else{
		strcpy(filename,"data/");
		strcat(filename,customLevels);
	} 

    // Read file

    if((handle=openpackfile(filename,packfile)) < 0) shutdown("Error loading level list from %s", filename);
    size = seekpackfile(handle,0,SEEK_END);
    seekpackfile(handle,0,SEEK_SET);

    buf = (char*)tracemalloc("load_levelorder",size+1);

    if(buf==NULL){
        closepackfile(handle);
        shutdown("Not enough memory to allocate %i-byte buffer for file %s", size, filename);
    }

    if(readpackfile(handle, buf, size) != size){
        if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
        closepackfile(handle);
        shutdown("Read error accessing file %s", filename);
    }

    buf[size] = 0;		// Terminate string (important!)
    closepackfile(handle);



    // Now interpret the contents of buf line by line
    pos = 0;
    current_set = -1;

	// Custom lifebar/timebox/icon positioning and size
	picon[0][0] = piconw[0][0] = picon[2][0] = piconw[2][0] = eicon[0][0] = eicon[2][0] = 2;
	picon[1][0] = piconw[1][0] = picon[3][0] = piconw[3][0] = eicon[1][0] = eicon[3][0] = 2 + P2_STATS_DIST;
	picon[0][1] = piconw[0][1] = picon[1][1] = piconw[1][1] = 2;
	picon[2][1] = piconw[2][1] = picon[3][1] = piconw[3][1] = 202;
	plife[0][0] = pmp[0][0] = plife[2][0] = pmp[2][0] = elife[0][0] = elife[2][0] = 20;
	plife[1][0] = pmp[1][0] = plife[3][0] = pmp[3][0] = elife[1][0] = elife[3][0] = 20 + P2_STATS_DIST;
	plife[0][1] = plife[1][1] = 10;
	plife[2][1] = plife[3][1] = 210;
	pmp[0][1] = pmp[1][1] = 18; 
	pmp[2][1] = pmp[3][1] = 218; 

	eicon[0][1] = eicon[1][1] = 19;
	eicon[2][1] = eicon[3][1] = 220;
	elife[0][1] = elife[1][1] = 27;
	elife[2][1] = elife[3][1] = 227;
	
	timeloc[0] = 149;
	timeloc[1] = 4;
	timeloc[2] = 21;
	timeloc[3] = 20;
	timeloc[4] = 0;

	lbarsize[0]  = mpbarsize[0] = 100;
    lbarsize[1]  = 5;
    mpbarsize[1] = 3;
    lbarsize[2]  = mpbarsize[2] = 0; 

	// Show Complete Default Values
	scomplete[0] = 75;
	scomplete[1] = 60;
	scomplete[2] = 0;
	scomplete[3] = 0;
	scomplete[4] = 0;
	scomplete[5] = 0;

	// Show Complete Y Values
	cbonus[0] = lbonus[0] = tscore[0] = 10;
	cbonus[1] = cbonus[3] = cbonus[5] = cbonus[7] = cbonus[9] = 100;
	lbonus[1] = lbonus[3] = lbonus[5] = lbonus[7] = lbonus[9] = 120;
	tscore[1] = tscore[3] = tscore[5] = tscore[7] = tscore[9] = 140;
	
	// Show Complete X Values
	cbonus[2] = lbonus[2] = tscore[2] = 100;
	cbonus[4] = lbonus[4] = tscore[4] = 155;
	cbonus[6] = lbonus[6] = tscore[6] = 210;
	cbonus[8] = lbonus[8] = tscore[8] = 265;
	

    while(pos<size){
        command = findarg(buf+pos, 0);

        if(command[0]){
            if(stricmp(command, "set")==0){
				if(num_difficulties>=MAX_DIFFICULTIES){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
					shutdown("Too many sets of levels (max %u)!", MAX_DIFFICULTIES);
				}
				++num_difficulties;
				++current_set;
				strncpy(set_names[current_set], findarg(buf+pos, 1), MAX_NAME_LEN);
				ifcomplete[current_set] = 0;
				branch_name[0] = 0;
            }
            else if(stricmp(command, "ifcomplete")==0){
                if(current_set<0){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                ifcomplete[current_set] = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "file")==0){
                if(current_set<0){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                strncpy(value1, findarg(buf+pos, 1), 127);
                add_level(value1, current_set);
            }
            else if(stricmp(command, "scene")==0){
                if(current_set<0){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                strncpy(value1, findarg(buf+pos, 1), 127);
                add_scene(value1, current_set);
            }
            else if(stricmp(command, "next")==0){
                if(current_set<0){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                // Set 'gonext' flag of last loaded level
                if(num_levels[current_set]<1){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order (next before file)!");
                }
                levelorder[current_set][num_levels[current_set]-1]->gonext = 1;
            }
            else if(stricmp(command, "end")==0){
                if(current_set<0){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                // Set endgame flag of last loaded level
                if(num_levels[current_set]<1){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order (next before file)!");
                }
                levelorder[current_set][num_levels[current_set]-1]->gonext = 2;
            }
            // 7-1-2005  credits/lives/singleplayer start here
            // used to read the new # of lives/credits from the levels.txt
            else if(stricmp(command, "lives")==0){
                if(current_set<0){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                difflives[current_set] = atoi(findarg(buf+pos, 1));
            }
            //	2-10-05  adjust the walkable coordinates
			else if(stricmp(command, "z")==0){
				if(current_set<0){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
					shutdown("Error in level order: a set must be specified.");
				}

				z_coords[0] = atoi(findarg(buf+pos, 1));
				z_coords[1] = atoi(findarg(buf+pos, 2));
				z_coords[2] = atoi(findarg(buf+pos, 3));
			}
            //	2007-2-22 level branch name
			else if(stricmp(command, "branch")==0){
				if(current_set<0){
					if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
					shutdown("Error in level order: a set must be specified.");
				}
                strncpy(branch_name, findarg(buf+pos, 1), MAX_NAME_LEN);
			}
    		else if(stricmp(command, "p1life")==0){
				if(atoi(findarg(buf+pos, 1))) plife[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plife[0][1] = atoi(findarg(buf+pos, 2));
			}
    		else if(stricmp(command, "p2life")==0){
				if(atoi(findarg(buf+pos, 1))) plife[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plife[1][1] = atoi(findarg(buf+pos, 2));
			}
    		else if(stricmp(command, "p3life")==0){
				if(atoi(findarg(buf+pos, 1))) plife[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plife[2][1] = atoi(findarg(buf+pos, 2));
				plifeUsed[0] = 1;
			}
    		else if(stricmp(command, "p4life")==0){
				if(atoi(findarg(buf+pos, 1))) plife[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plife[3][1] = atoi(findarg(buf+pos, 2));
				plifeUsed[1] = 1;
			}			
			else if(stricmp(command, "p1mp")==0){
                if(atoi(findarg(buf+pos, 1))) pmp[0][0] = atoi(findarg(buf+pos, 1));
                if(atoi(findarg(buf+pos, 2))) pmp[0][1] = atoi(findarg(buf+pos, 2));
				pmpUsed[0] = 1;
            }
            else if(stricmp(command, "p2mp")==0){
                if(atoi(findarg(buf+pos, 1))) pmp[1][0] = atoi(findarg(buf+pos, 1));
                if(atoi(findarg(buf+pos, 2))) pmp[1][1] = atoi(findarg(buf+pos, 2));
				pmpUsed[1] = 1;
            }
			else if(stricmp(command, "p3mp")==0){
                if(atoi(findarg(buf+pos, 1))) pmp[2][0] = atoi(findarg(buf+pos, 1));
                if(atoi(findarg(buf+pos, 2))) pmp[2][1] = atoi(findarg(buf+pos, 2));
				pmpUsed[2] = 1;
            }
            else if(stricmp(command, "p4mp")==0){
                if(atoi(findarg(buf+pos, 1))) pmp[3][0] = atoi(findarg(buf+pos, 1));
                if(atoi(findarg(buf+pos, 2))) pmp[3][1] = atoi(findarg(buf+pos, 2));
				pmpUsed[3] = 1;
            }
			else if(stricmp(command, "p1lifex")==0){
				if(atoi(findarg(buf+pos, 1))) plifeX[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeX[0][1] = atoi(findarg(buf+pos, 2));
				plifeXused[0] = 1;
			}
    		else if(stricmp(command, "p2lifex")==0){
				if(atoi(findarg(buf+pos, 1))) plifeX[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeX[1][1] = atoi(findarg(buf+pos, 2));
				plifeXused[1] = 1;
			}
			else if(stricmp(command, "p3lifex")==0){
				if(atoi(findarg(buf+pos, 1))) plifeX[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeX[2][1] = atoi(findarg(buf+pos, 2));
				plifeXused[2] = 1;
			}
    		else if(stricmp(command, "p4lifex")==0){
				if(atoi(findarg(buf+pos, 1))) plifeX[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeX[3][1] = atoi(findarg(buf+pos, 2));
				plifeXused[3] = 1;
			}
			else if(stricmp(command, "p1lifen")==0){
				if(atoi(findarg(buf+pos, 1))) plifeN[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeN[0][1] = atoi(findarg(buf+pos, 2));
				plifeNused[0] = 1;
			}
    		else if(stricmp(command, "p2lifen")==0){
				if(atoi(findarg(buf+pos, 1))) plifeN[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeN[1][1] = atoi(findarg(buf+pos, 2));
				plifeNused[1] = 1;
			}
			else if(stricmp(command, "p3lifen")==0){
				if(atoi(findarg(buf+pos, 1))) plifeN[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeN[2][1] = atoi(findarg(buf+pos, 2));
				plifeNused[2] = 1;
			}
    		else if(stricmp(command, "p4lifen")==0){
				if(atoi(findarg(buf+pos, 1))) plifeN[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) plifeN[3][1] = atoi(findarg(buf+pos, 2));
				plifeNused[3] = 1;
			}
    		else if(stricmp(command, "e1life")==0){
				if(atoi(findarg(buf+pos, 1))) elife[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) elife[0][1] = atoi(findarg(buf+pos, 2));
			}
    		else if(stricmp(command, "e2life")==0){
				if(atoi(findarg(buf+pos, 1))) elife[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) elife[1][1] = atoi(findarg(buf+pos, 2));
			}
    		else if(stricmp(command, "e3life")==0){
				if(atoi(findarg(buf+pos, 1))) elife[2][0] = atoi(findarg(buf+pos, 1)); //4player new
				if(atoi(findarg(buf+pos, 2))) elife[2][1] = atoi(findarg(buf+pos, 2));
				elifeUsed[0] = 1;
			}
    		else if(stricmp(command, "e4life")==0){
				if(atoi(findarg(buf+pos, 1))) elife[3][0] = atoi(findarg(buf+pos, 1)); //4player new
				if(atoi(findarg(buf+pos, 2))) elife[3][1] = atoi(findarg(buf+pos, 2));
				elifeUsed[1] = 1;
			}			
			else if(stricmp(command, "p1icon")==0){
				if(atoi(findarg(buf+pos, 1))) picon[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) picon[0][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p2icon")==0){
				if(atoi(findarg(buf+pos, 1))) picon[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) picon[1][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p3icon")==0){
				if(atoi(findarg(buf+pos, 1))) picon[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) picon[2][1] = atoi(findarg(buf+pos, 2));
				piconUsed[0] = 1;
			}
			else if(stricmp(command, "p4icon")==0){
				if(atoi(findarg(buf+pos, 1))) picon[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) picon[3][1] = atoi(findarg(buf+pos, 2));
				piconUsed[1] = 1;
			}
			else if(stricmp(command, "p1iconw")==0){
				if(atoi(findarg(buf+pos, 1))) piconw[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) piconw[0][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p2iconw")==0){
				if(atoi(findarg(buf+pos, 1))) piconw[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) piconw[1][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p3iconw")==0){
				if(atoi(findarg(buf+pos, 1))) piconw[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) piconw[2][1] = atoi(findarg(buf+pos, 2));
				piconwUsed[0] = 1;
			}
			else if(stricmp(command, "p4iconw")==0){
				if(atoi(findarg(buf+pos, 1))) piconw[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) piconw[3][1] = atoi(findarg(buf+pos, 2));
				piconwUsed[1] = 1;
			}
			else if(stricmp(command, "mp1icon")==0){
				if(atoi(findarg(buf+pos, 1))) mpicon[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) mpicon[0][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "mp2icon")==0){
				if(atoi(findarg(buf+pos, 1))) mpicon[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) mpicon[1][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "mp3icon")==0){
				if(atoi(findarg(buf+pos, 1))) mpicon[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) mpicon[2][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "mp4icon")==0){
				if(atoi(findarg(buf+pos, 1))) mpicon[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) mpicon[3][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p1namej")==0){
				if(atoi(findarg(buf+pos, 1))) pnameJ[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pnameJ[0][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pnameJ[0][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pnameJ[0][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pnameJ[0][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pnameJ[0][5] = atoi(findarg(buf+pos, 6));
				pnameJused[0] = 1;
			}
			else if(stricmp(command, "p2namej")==0){
				if(atoi(findarg(buf+pos, 1))) pnameJ[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pnameJ[1][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pnameJ[1][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pnameJ[1][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pnameJ[1][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pnameJ[1][5] = atoi(findarg(buf+pos, 6));
				pnameJused[1] = 1;
			}
			else if(stricmp(command, "p3namej")==0){
				if(atoi(findarg(buf+pos, 1))) pnameJ[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pnameJ[2][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pnameJ[2][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pnameJ[2][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pnameJ[2][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pnameJ[2][5] = atoi(findarg(buf+pos, 6));
				pnameJused[2] = 1;
			}
			else if(stricmp(command, "p4namej")==0){
				if(atoi(findarg(buf+pos, 1))) pnameJ[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pnameJ[3][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pnameJ[3][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pnameJ[3][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pnameJ[3][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pnameJ[3][5] = atoi(findarg(buf+pos, 6));
				pnameJused[3] = 1;
			}
			else if(stricmp(command, "p1score")==0){
				if(atoi(findarg(buf+pos, 1))) pscore[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pscore[0][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pscore[0][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pscore[0][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pscore[0][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pscore[0][5] = atoi(findarg(buf+pos, 6));
				pscoreUsed[0] = 1;
			}
			else if(stricmp(command, "p2score")==0){
				if(atoi(findarg(buf+pos, 1))) pscore[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pscore[1][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pscore[1][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pscore[1][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pscore[1][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pscore[1][5] = atoi(findarg(buf+pos, 6));
				pscoreUsed[1] = 1;
			}
			else if(stricmp(command, "p3score")==0){
				if(atoi(findarg(buf+pos, 1))) pscore[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pscore[2][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pscore[2][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pscore[2][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pscore[2][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pscore[2][5] = atoi(findarg(buf+pos, 6));
				pscoreUsed[2] = 1;
			}
			else if(stricmp(command, "p4score")==0){
				if(atoi(findarg(buf+pos, 1))) pscore[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pscore[3][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) pscore[3][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) pscore[3][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) pscore[3][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) pscore[3][5] = atoi(findarg(buf+pos, 6));
				pscoreUsed[3] = 1;
			}
			else if(stricmp(command, "p1shoot")==0){
				if(atoi(findarg(buf+pos, 1))) pshoot[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pshoot[0][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p2shoot")==0){
				if(atoi(findarg(buf+pos, 1))) pshoot[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pshoot[1][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p3shoot")==0){
				if(atoi(findarg(buf+pos, 1))) pshoot[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pshoot[2][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p4shoot")==0){
				if(atoi(findarg(buf+pos, 1))) pshoot[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) pshoot[3][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "p1rush")==0){
				if(atoi(findarg(buf+pos, 1))) prush[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) prush[0][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) prush[0][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) prush[0][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) prush[0][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) prush[0][5] = atoi(findarg(buf+pos, 6));
				if(atoi(findarg(buf+pos, 7))) prush[0][6] = atoi(findarg(buf+pos, 7));
				if(atoi(findarg(buf+pos, 8))) prush[0][7] = atoi(findarg(buf+pos, 8));
			}
			else if(stricmp(command, "p2rush")==0){
				if(atoi(findarg(buf+pos, 1))) prush[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) prush[1][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) prush[1][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) prush[1][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) prush[1][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) prush[1][5] = atoi(findarg(buf+pos, 6));
				if(atoi(findarg(buf+pos, 7))) prush[1][6] = atoi(findarg(buf+pos, 7));
				if(atoi(findarg(buf+pos, 8))) prush[1][7] = atoi(findarg(buf+pos, 8));
			}
			else if(stricmp(command, "p3rush")==0){
				if(atoi(findarg(buf+pos, 1))) prush[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) prush[2][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) prush[2][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) prush[2][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) prush[2][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) prush[2][5] = atoi(findarg(buf+pos, 6));
				if(atoi(findarg(buf+pos, 7))) prush[2][6] = atoi(findarg(buf+pos, 7));
				if(atoi(findarg(buf+pos, 8))) prush[2][7] = atoi(findarg(buf+pos, 8));
			}
			else if(stricmp(command, "p4rush")==0){
				if(atoi(findarg(buf+pos, 1))) prush[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) prush[3][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) prush[3][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) prush[3][3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) prush[3][4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) prush[3][5] = atoi(findarg(buf+pos, 6));
				if(atoi(findarg(buf+pos, 7))) prush[3][6] = atoi(findarg(buf+pos, 7));
				if(atoi(findarg(buf+pos, 8))) prush[3][7] = atoi(findarg(buf+pos, 8));
			}
			else if(stricmp(command, "e1icon")==0){
				if(atoi(findarg(buf+pos, 1))) eicon[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) eicon[0][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "e2icon")==0){
				if(atoi(findarg(buf+pos, 1))) eicon[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) eicon[1][1] = atoi(findarg(buf+pos, 2));
			}
			else if(stricmp(command, "e3icon")==0){
				if(atoi(findarg(buf+pos, 1))) eicon[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) eicon[2][1] = atoi(findarg(buf+pos, 2));
				eiconUsed[0] = 1;
			}
			else if(stricmp(command, "e4icon")==0){
				if(atoi(findarg(buf+pos, 1))) eicon[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) eicon[3][1] = atoi(findarg(buf+pos, 2));
				eiconUsed[1] = 1;
			}
			else if(stricmp(command, "e1name")==0){
				if(atoi(findarg(buf+pos, 1))) ename[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) ename[0][1] = atoi(findarg(buf+pos, 2));
				enameused[0] = 1;
			}
			else if(stricmp(command, "e2name")==0){
				if(atoi(findarg(buf+pos, 1))) ename[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) ename[1][1] = atoi(findarg(buf+pos, 2));
				enameused[1] = 1;
			}
			else if(stricmp(command, "e3name")==0){
				if(atoi(findarg(buf+pos, 1))) ename[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) ename[2][1] = atoi(findarg(buf+pos, 2));
				enameused[2] = 1;
			}
			else if(stricmp(command, "e4name")==0){
				if(atoi(findarg(buf+pos, 1))) ename[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) ename[3][1] = atoi(findarg(buf+pos, 2));
				enameused[3] = 1;
			}
			else if(stricmp(command, "p1smenu")==0){
				if(atoi(findarg(buf+pos, 1))) psmenu[0][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) psmenu[0][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) psmenu[0][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) psmenu[0][3] = atoi(findarg(buf+pos, 4));
			}
			else if(stricmp(command, "p2smenu")==0){
				if(atoi(findarg(buf+pos, 1))) psmenu[1][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) psmenu[1][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) psmenu[1][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) psmenu[1][3] = atoi(findarg(buf+pos, 4));
			}
			else if(stricmp(command, "p3smenu")==0){
				if(atoi(findarg(buf+pos, 1))) psmenu[2][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) psmenu[2][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) psmenu[2][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) psmenu[2][3] = atoi(findarg(buf+pos, 4));
			}
			else if(stricmp(command, "p4smenu")==0){
				if(atoi(findarg(buf+pos, 1))) psmenu[3][0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) psmenu[3][1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) psmenu[3][2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) psmenu[3][3] = atoi(findarg(buf+pos, 4));
			}
			else if(stricmp(command, "timeicon")==0){
				timeicon[0] = loadsprite(findarg(buf+pos, 1),0,0);
				if(atoi(findarg(buf+pos, 2))) timeicon[1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) timeicon[2] = atoi(findarg(buf+pos, 3));
			}
    		else if(stricmp(command, "timeloc")==0){
				if(atoi(findarg(buf+pos, 1))) timeloc[0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) timeloc[1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) timeloc[2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) timeloc[3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) timeloc[4] = atoi(findarg(buf+pos, 5));
			}
			else if(stricmp(command, "lbarsize")==0){
				if(atoi(findarg(buf+pos, 1))) lbarsize[0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) lbarsize[1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) lbarsize[2] = atoi(findarg(buf+pos, 3));
			}
            else if(stricmp(command, "mpbarsize")==0){
                if(atoi(findarg(buf+pos, 1))) mpbarsize[0] = atoi(findarg(buf+pos, 1));
                if(atoi(findarg(buf+pos, 2))) mpbarsize[1] = atoi(findarg(buf+pos, 2));
                if(atoi(findarg(buf+pos, 3))) mpbarsize[2] = atoi(findarg(buf+pos, 3));
            }
			else if(stricmp(command, "showcomplete")==0){
				if(atoi(findarg(buf+pos, 1))) scomplete[0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) scomplete[1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) scomplete[2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) scomplete[3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) scomplete[4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) scomplete[5] = atoi(findarg(buf+pos, 6));
			}
			else if(stricmp(command, "clearbonus")==0){
				if(atoi(findarg(buf+pos, 1))) cbonus[0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) cbonus[1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) cbonus[2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) cbonus[3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) cbonus[4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) cbonus[5] = atoi(findarg(buf+pos, 6));
				if(atoi(findarg(buf+pos, 7))) cbonus[6] = atoi(findarg(buf+pos, 7));
				if(atoi(findarg(buf+pos, 8))) cbonus[7] = atoi(findarg(buf+pos, 8));
				if(atoi(findarg(buf+pos, 9))) cbonus[8] = atoi(findarg(buf+pos, 9));
				if(atoi(findarg(buf+pos, 10))) cbonus[9] = atoi(findarg(buf+pos, 10));
			}
			else if(stricmp(command, "lifebonus")==0){
				if(atoi(findarg(buf+pos, 1))) lbonus[0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) lbonus[1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) lbonus[2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) lbonus[3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) lbonus[4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) lbonus[5] = atoi(findarg(buf+pos, 6));
				if(atoi(findarg(buf+pos, 7))) lbonus[6] = atoi(findarg(buf+pos, 7));
				if(atoi(findarg(buf+pos, 8))) lbonus[7] = atoi(findarg(buf+pos, 8));
				if(atoi(findarg(buf+pos, 9))) lbonus[8] = atoi(findarg(buf+pos, 9));
				if(atoi(findarg(buf+pos, 10))) lbonus[9] = atoi(findarg(buf+pos, 10));
			}
			else if(stricmp(command, "totalscore")==0){
				if(atoi(findarg(buf+pos, 1))) tscore[0] = atoi(findarg(buf+pos, 1));
				if(atoi(findarg(buf+pos, 2))) tscore[1] = atoi(findarg(buf+pos, 2));
				if(atoi(findarg(buf+pos, 3))) tscore[2] = atoi(findarg(buf+pos, 3));
				if(atoi(findarg(buf+pos, 4))) tscore[3] = atoi(findarg(buf+pos, 4));
				if(atoi(findarg(buf+pos, 5))) tscore[4] = atoi(findarg(buf+pos, 5));
				if(atoi(findarg(buf+pos, 6))) tscore[5] = atoi(findarg(buf+pos, 6));
				if(atoi(findarg(buf+pos, 7))) tscore[6] = atoi(findarg(buf+pos, 7));
				if(atoi(findarg(buf+pos, 8))) tscore[7] = atoi(findarg(buf+pos, 8));
				if(atoi(findarg(buf+pos, 9))) tscore[8] = atoi(findarg(buf+pos, 9));
				if(atoi(findarg(buf+pos, 10))) tscore[9] = atoi(findarg(buf+pos, 10));
			}
            else if(stricmp(command, "musicoverlap")==0){
                if(current_set<0){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					};
                    shutdown("Error in level order: a set must be specified.");
                }
                diffoverlap[current_set] = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "noslowfx")==0){
				noslowfx = 1;
			}
			else if(stricmp(command, "hiscorebg")==0){	// If set to 1, will look for a background image
				hiscorebg = 1;
			}
			else if(stricmp(command, "completebg")==0){	// If set to 1, will look for a background image
				completebg = 1;
			}
			else if(stricmp(command, "unlockbg")==0){
				unlockbg = 1;
			}
			else if(stricmp(command, "noshare")==0){
				noshare = 1;
			}

            //8-2-2005 custom fade
            else if(stricmp(command, "custfade")==0){
                if(current_set<0){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                custfade[current_set] = atoi(findarg(buf+pos, 1));
            }
            //8-2-2005 custom fade

            else if(stricmp(command, "credits")==0){
                if(current_set<0){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                diffcreds[current_set] = atoi(findarg(buf+pos, 1));
            }
				//typemp for change for mp restored by time (0) to by enemys (1) or no restore (2) by tails
			else if(stricmp(command, "typemp")==0){
                if(current_set<0){
                    tracefree(buf);
                    shutdown("Error in level order: a set must be specified.");
                }

				typemp[current_set] = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "single")==0){
				maxplayers = 1;
				ctrlmaxplayers = 1;
            }  // 7-1-2005  credits/lives/singleplayer end here
            else if(stricmp(command, "maxplayers")==0){
				maxplayers = atoi(findarg(buf+pos, 1));

				if(maxplayers < 0 || maxplayers > 4) maxplayers = 2;
				else ctrlmaxplayers = maxplayers;
            }
            else if(stricmp(command, "nosame")==0){
                if(current_set<0){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Error in level order: a set must be specified.");
                }
                same[current_set] = atoi(findarg(buf+pos, 1));
            }
	        else if(stricmp(command, "rush")==0){
				rush[0] = atoi(findarg(buf+pos, 1));
				rush[1] = atoi(findarg(buf+pos, 2));
				strncpy(rush_names[0], findarg(buf+pos, 3), MAX_NAME_LEN);
				rush[2] = atoi(findarg(buf+pos, 4));
				rush[3] = atoi(findarg(buf+pos, 5));
				strncpy(rush_names[1], findarg(buf+pos, 6), MAX_NAME_LEN);
				rush[4] = atoi(findarg(buf+pos, 7));
				rush[5] = atoi(findarg(buf+pos, 8));
            }
            else{
                if(buf != NULL){
					tracefree(buf);
					buf = NULL;
				}
                shutdown("Command '%s' not understood in level order!", command);
            }
        }

        // Go to next line
        while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
        while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
    }
    if(buf != NULL){
		tracefree(buf);
		buf = NULL;
	}

	// Variables without defaults will be auto populated.
	if(!plifeUsed[0]){ plife[2][0] = plife[0][0]; plife[2][1] = plife[2][1] + (plife[0][1] - 10); }
	if(!plifeUsed[1]){ plife[3][0] = plife[1][0]; plife[3][1] = plife[3][1] + (plife[1][1] - 10); }

	if(!elifeUsed[0]){ elife[2][0] = elife[0][0]; elife[2][1] = elife[2][1] + (elife[0][1] - 27); }
	if(!elifeUsed[1]){ elife[3][0] = elife[1][0]; elife[3][1] = elife[3][1] + (elife[1][1] - 27); }

	if(!piconUsed[0]){ picon[2][0] = picon[0][0]; picon[2][1] = picon[2][1] + (picon[0][1] - 2); }
	if(!piconUsed[1]){ picon[3][0] = picon[1][0]; picon[3][1] = picon[3][1] + (picon[1][1] - 2); }

	if(!piconwUsed[0]){ piconw[2][0] = piconw[0][0]; piconw[2][1] = piconw[2][1] + (piconw[0][1] - 2); }
	if(!piconwUsed[1]){ piconw[3][0] = piconw[1][0]; piconw[3][1] = piconw[3][1] + (piconw[1][1] - 2); }

	if(!eiconUsed[0]){ eicon[2][0] = eicon[0][0]; eicon[2][1] = eicon[2][1] + (eicon[0][1] - 19); }
	if(!eiconUsed[1]){ eicon[3][0] = eicon[1][0]; eicon[3][1] = eicon[3][1] + (eicon[1][1] - 19); }

	if(!pmpUsed[0]){  pmp[0][0] = plife[0][0]; pmp[0][1] = plife[0][1] + 8; }
	if(!pmpUsed[1]){  pmp[1][0] = plife[1][0]; pmp[1][1] = plife[1][1] + 8; }
	if(!pmpUsed[2]){  pmp[2][0] = pmp[0][0]; pmp[2][1] = pmp[2][1] + (pmp[0][1] - 18); }
	if(!pmpUsed[3]){  pmp[3][0] = pmp[1][0]; pmp[3][1] = pmp[1][1] + (pmp[1][1] - 18); }

	if(!plifeXused[0]){ plifeX[0][0] = plife[0][0] + lbarsize[0] + 4; plifeX[0][1] = picon[0][1] + 7; }
	if(!plifeXused[1]){ plifeX[1][0] = plife[1][0] + lbarsize[0] + 4; plifeX[1][1] = picon[1][1] + 7; }
	if(!plifeXused[2]){ plifeX[2][0] = plife[2][0] + lbarsize[0] + 4; plifeX[2][1] = picon[2][1] + 7; }
	if(!plifeXused[3]){ plifeX[3][0] = plife[3][0] + lbarsize[0] + 4; plifeX[3][1] = picon[3][1] + 7; }

	if(!plifeNused[0]){ plifeN[0][0] = plife[0][0] + lbarsize[0] + 11; plifeN[0][1] = picon[0][1]; }
	if(!plifeNused[1]){ plifeN[1][0] = plife[1][0] + lbarsize[0] + 11; plifeN[1][1] = picon[1][1]; }
	if(!plifeNused[2]){ plifeN[2][0] = plifeN[0][0]; plifeN[2][1] = picon[2][1]; }
	if(!plifeNused[3]){ plifeN[3][0] = plifeN[1][0]; plifeN[3][1] = picon[3][1]; }

	if(!pnameJused[0]){ pnameJ[0][2] = pnameJ[0][4] = pnameJ[0][0] = plife[0][0] + 1; pnameJ[0][5] = pnameJ[0][1] = picon[0][1]; pnameJ[0][3] = 10 + pnameJ[0][5]; }
	if(!pnameJused[1]){ pnameJ[1][2] = pnameJ[1][4] = pnameJ[1][0] = plife[1][0] + 1; pnameJ[1][5] = pnameJ[1][1] = picon[1][1]; pnameJ[1][3] = 10 + pnameJ[1][5]; }
	if(!pnameJused[2]){ pnameJ[2][2] = pnameJ[2][4] = pnameJ[2][0] = plife[2][0] + 1; pnameJ[2][5] = pnameJ[2][1] = picon[2][1]; pnameJ[2][3] = 10 + pnameJ[2][5]; }
	if(!pnameJused[3]){ pnameJ[3][2] = pnameJ[3][4] = pnameJ[3][0] = plife[3][0] + 1; pnameJ[3][5] = pnameJ[3][1] = picon[3][1]; pnameJ[3][3] = 10 + pnameJ[3][5]; }

	if(!pscoreUsed[0]){ pscore[0][0] = plife[0][0] + 1; pscore[0][1] = picon[0][1];	}
	if(!pscoreUsed[1]){ pscore[1][0] = plife[1][0] + 1; pscore[1][1] = picon[1][1];	}
	if(!pscoreUsed[2]){ pscore[2][0] = plife[2][0] + 1; pscore[2][1] = picon[2][1];	}
	if(!pscoreUsed[3]){ pscore[3][0] = plife[3][0] + 1; pscore[3][1] = picon[3][1];	}

	if(!enameused[0]){ ename[0][0] = elife[0][0] + 1; ename[0][1] = eicon[0][1]; }
	if(!enameused[1]){ ename[1][0] = elife[1][0] + 1; ename[1][1] = eicon[1][1]; }
	if(!enameused[2]){ ename[2][0] = ename[0][0]; ename[2][1] = eicon[2][1]; }
	if(!enameused[3]){ ename[3][0] = ename[1][0]; ename[3][1] = eicon[3][1]; }
    
    branch_name[0] = 0; //clear up branch name, so we can use it in game
    
	if(current_set<0) shutdown("No levels were loaded!");
}





void unload_level(){
    unload_background();
    unload_texture();
    freepanels();

    if(level != NULL){
		tracefree(level);
		level = NULL;
	}

    levelpos = 0;
    advancex = 0;
    advancey = 0;
    advancetime = 0;
    quake = 0;
    quaketime = 0;
    level_waiting = 0;
    nojoin = 0;
    current_spawn = 0;
    groupmin = 100;
    groupmax = 100;
    level_completed = 0;
    tospeedup = 0;	// Reset so it sets to normal speed for the next level
    reached[0] = reached[1] = reached[2] = reached[3] = 0;	// TYPE_ENDLEVEL values reset after level completed //4player
    player[0].iconmode = player[1].iconmode = player[2].iconmode = player[3].iconmode = 0;	// Incase player clears the level while being hit //4player
    showtimeover = 0;
    pause = 0;
    endgame = 0;
    go_time = 0;
    neon_time = 0;
    time = 0;
    gfx_y_offset = 0;	// Added so select screen graphics display correctly
}



void load_level(char *filename){
    int handle;
    char * buf;
    int size;
    int pos;
    char * command;
    char * value;
    s_spawn_entry next;
    int i, j;

    unload_level();

	// Cleanup resources
#if PSP || DC
	if(ramAvailable() < savedata.memoryThreshold) resourceCleanUp();
#endif

	// Clear background
	unload_background();
	background = allocscreen(hRes1, vRes1);
	if(background==NULL) shutdown("Out of memory! Function Call 'load level'");

	if(!video_set_mode(hRes1, vRes1)) shutdown("Out of memory! Function Call 'load level'");
	clearscreen(vscreen);
	spriteq_clear();
	
    memset(&next, 0, sizeof(s_spawn_entry));
    // timeleft = 100 * COUNTER_SPEED; - This line moved down a bit to set custom level times

    level = (s_level*)tracemalloc("load_level 1",sizeof(s_level));
	if(level==NULL) shutdown("load_level() #1 FATAL: Out of memory!\n");
    memset(level, 0, sizeof(s_level));

    if((handle=openpackfile(filename,packfile)) < 0) shutdown("Unable to load level file '%s'", filename);
    size = seekpackfile(handle,0,SEEK_END);
    seekpackfile(handle,0,SEEK_SET);

    buf = (char*)tracemalloc("load_level 2",size+1);

    if(buf==NULL){
        closepackfile(handle);
        shutdown("load_level() #2 FATAL: out of memory!\n");
    }

    if(readpackfile(handle, buf, size) != size){
        if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
        closepackfile(handle);
        shutdown("FATAL: read error accessing file '%s'", filename);
    }

    buf[size] = 0;		// Terminate string (important!)
    closepackfile(handle);

	level->settime = 100;	// Feb 25, 2005 - Default time limit set to 100
	level->nospecial = 0;	// Default set to specials can be used during bonus levels
	level->nohurt = 0;	// Default set to players can hurt each other during bonus levels
	level->nohit = 0;	// Default able to hit the other player
	level->spawn[0][2] = level->spawn[1][2] = level->spawn[2][2] = level->spawn[3][2] = 300;	// Set the default spawn a to 300
	level->setweap = 0;

    // Now interpret the contents of buf line by line
    pos = 0;
    while(pos<size){
        command = findarg(buf+pos, 0);
        if(command[0]){
            if(stricmp(command, "background")==0){
                load_background(findarg(buf+pos, 1), 1, 1);
                standard_palette();
            }
            else if(stricmp(command, "water")==0){
                load_texture(findarg(buf+pos, 1));
                i = atoi(findarg(buf+pos, 2));
                if(i<2) i = 2;
                texture_set_wave(i);
            }
            else if(stricmp(command, "direction")==0){
                value = findarg(buf+pos, 1);
                if(stricmp(value, "up")==0) level->scrolldir = SCROLL_UP;
                else if(stricmp(value, "down")==0) level->scrolldir = SCROLL_DOWN;
                else if(stricmp(value, "left")==0) level->scrolldir = SCROLL_LEFT;
                else if(stricmp(value, "both")==0) level->scrolldir = SCROLL_BOTH;
                else level->scrolldir = SCROLL_RIGHT;
            }
            else if(stricmp(command, "facing")==0){
                level->facing = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "rock")==0){
                level->rocking = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "bgspeed")==0){
                level->bgspeed = atoi(findarg(buf+pos, 1));
                level->bgdir = atoi(findarg(buf+pos, 2));
            }
            else if(stricmp(command, "mirror")==0){
                level->mirror = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "music")==0){
                music(findarg(buf+pos, 1), 1);
            }
            else if(stricmp(command, "bossmusic")==0){
                strncpy(level->bossmusic, findarg(buf+pos, 1), 255);
            }
			else if(stricmp(command, "settime")==0){	// If settime is found, overwrite the default 100 for time limit
				if(atoi(findarg(buf+pos, 1)) > 100 || atoi(findarg(buf+pos, 1)) < 0) level->settime = 100;
				else level->settime = atoi(findarg(buf+pos, 1)); // Feb 25, 2005 - Time limit loaded from individual .txt file
			}
			else if(stricmp(command, "setweap")==0){	// Specify a weapon for each level
				level->setweap = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "notime")==0){	// Flag to if the time should be displayed 1 = no, else yes
				level->notime = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "noreset")==0){	// Flag to if the time should be displayed 1 = no, else yes
				level->noreset = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "noslow")==0){	// If set, level will not slow down when bosses are defeated
				level->noslow = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "type")==0){
				level->type = atoi(findarg(buf+pos, 1));	// Level type - 1 = bonus, else regular

				if(atoi(findarg(buf+pos, 2))) level->nospecial = atoi(findarg(buf+pos, 2));	// Can use specials during bonus levels (default 0 - yes)
				if(atoi(findarg(buf+pos, 3))) level->nohurt = atoi(findarg(buf+pos, 3));	// Can hurt other players during bonus levels (default 0 - yes)
			}
			else if(stricmp(command, "nohit")==0){
				level->nohit = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "spawn1")==0){
				level->spawn[0][0] = atoi(findarg(buf+pos, 1));
				level->spawn[0][1] = atoi(findarg(buf+pos, 2));
				level->spawn[0][2] = atoi(findarg(buf+pos, 3));

				if(level->spawn[0][1] > 232 || level->spawn[0][1] < 0) level->spawn[0][1] = 232;
				if(level->spawn[0][2] < 0) level->spawn[0][2] = 300;
			}
			else if(stricmp(command, "spawn2")==0){
				level->spawn[1][0] = atoi(findarg(buf+pos, 1));
				level->spawn[1][1] = atoi(findarg(buf+pos, 2));
				level->spawn[1][2] = atoi(findarg(buf+pos, 3));

				if(level->spawn[1][1] > 232 || level->spawn[1][1] < 0) level->spawn[1][1] = 232;
				if(level->spawn[1][2] < 0) level->spawn[1][2] = 300;
			}
			else if(stricmp(command, "spawn3")==0){
				level->spawn[2][0] = atoi(findarg(buf+pos, 1));
				level->spawn[2][1] = atoi(findarg(buf+pos, 2));
				level->spawn[2][2] = atoi(findarg(buf+pos, 3));

				if(level->spawn[2][1] > 232 || level->spawn[2][1] < 0) level->spawn[2][1] = 232;
				if(level->spawn[2][2] < 0) level->spawn[2][2] = 300;
			}
			else if(stricmp(command, "spawn4")==0){
				level->spawn[3][0] = atoi(findarg(buf+pos, 1));
				level->spawn[3][1] = atoi(findarg(buf+pos, 2));
				level->spawn[3][2] = atoi(findarg(buf+pos, 3));

				if(level->spawn[3][1] > 232 || level->spawn[3][1] < 0) level->spawn[3][1] = 232;
				if(level->spawn[3][2] < 0) level->spawn[3][2] = 300;
			}
            else if(stricmp(command, "frontpanel")==0){
                value = findarg(buf+pos, 1);

                if(!loadfrontpanel(value)){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Unable to load '%s'!", value);
                }
            }
            else if(stricmp(command, "panel")==0){
                if(!loadpanel(findarg(buf+pos, 1), findarg(buf+pos, 2), findarg(buf+pos, 3))){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("Panel load error in '%s'!", filename);
                }
            }
            else if(stricmp(command, "order")==0){
                // Append to order
                if(panels_loaded<1){
                    if(buf != NULL){
						tracefree(buf);
						buf = NULL;
					}
                    shutdown("You must load the panels before entering the level layout!");
                }
                value = findarg(buf+pos, 1);
                i = 0;

                while(value[i] && level->numpanels < LEVEL_MAX_PANELS){
                    j = value[i];

                    if(j>='A' && j<='Z') j-='A';
                    else if(j>='a' && j<='z') j-='a';
                    else{
                        if(buf != NULL){
							tracefree(buf);
							buf = NULL;
						}
                        shutdown("Illegal character in panel order: '%c' (%02Xh)", j, j);
                    }

                    if(j >= panels_loaded){
                        if(buf != NULL){
							tracefree(buf);
							buf = NULL;
						}
                        shutdown("Illegal panel index: %i (only %i panels loaded)", j, panels_loaded);
                    }
                    level->order[level->numpanels] = j;
                    level->numpanels++;
                    i++;
                }
            }
            else if(stricmp(command, "hole")==0){
                value = findarg(buf+pos, 1);	// ltb	1-18-05  adjustable hole sprites

                if(openpackfile(value, packfile) >= 0) holesprite = loadsprite(value,0,0);// ltb 1-18-05  load new hole sprite
                else holesprite = loadsprite("data/sprites/hole",0,0);	// ltb 1-18-05  no new sprite load the default

                if(level->numholes >= LEVEL_MAX_HOLES) shutdown("Too many holes in level (max %i)!", LEVEL_MAX_HOLES);
                level->holes[level->numholes][0] = atoi(findarg(buf+pos, 1));
                level->holes[level->numholes][1] = atoi(findarg(buf+pos,2));
                level->holes[level->numholes][2] = atoi(findarg(buf+pos,3));
                level->holes[level->numholes][3] = atoi(findarg(buf+pos,4));
                level->holes[level->numholes][4] = atoi(findarg(buf+pos,5));
                level->holes[level->numholes][5] = atoi(findarg(buf+pos,6));
                level->holes[level->numholes][6] = atoi(findarg(buf+pos,7));

                if(!level->holes[level->numholes][1]) level->holes[level->numholes][1] = 240;
                if(!level->holes[level->numholes][2]) level->holes[level->numholes][2] = 12;
                if(!level->holes[level->numholes][3]) level->holes[level->numholes][3] = 1;
                if(!level->holes[level->numholes][4]) level->holes[level->numholes][4] = 200;
                if(!level->holes[level->numholes][5]) level->holes[level->numholes][5] = 287;
                if(!level->holes[level->numholes][6]) level->holes[level->numholes][6] = 45;
                level->numholes++;
            }
            else if(stricmp(command, "wall")==0){
                level->walls[level->numwalls][0] = atoi(findarg(buf+pos, 1));
                level->walls[level->numwalls][1] = atoi(findarg(buf+pos,2));
                level->walls[level->numwalls][2] = atoi(findarg(buf+pos,3));
                level->walls[level->numwalls][3] = atoi(findarg(buf+pos,4));
                level->walls[level->numwalls][4] = atoi(findarg(buf+pos,5));
                level->walls[level->numwalls][5] = atoi(findarg(buf+pos,6));
                level->walls[level->numwalls][6] = atoi(findarg(buf+pos,7));
                level->walls[level->numwalls][7] = atoi(findarg(buf+pos,8));
                level->numwalls++;
			}
            else if(stricmp(command, "blocked")==0){
                level->exit_blocked = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "endhole")==0){
                level->exit_hole = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "wait")==0){
                // Clear spawn thing, set wait state instead
                memset(&next,0,sizeof(s_spawn_entry));
                next.wait = 1;
            }
            else if(stricmp(command, "nojoin")==0){
                // Clear spawn thing, set nojoin state instead
                memset(&next,0,sizeof(s_spawn_entry));
                next.nojoin = 1;
            }
            else if(stricmp(command, "canjoin")==0){
                // Clear spawn thing, set nojoin state instead
                memset(&next,0,sizeof(s_spawn_entry));
                next.nojoin = -1;
            }
            else if(stricmp(command, "group")==0){
                // Clear spawn thing, set group instead
                memset(&next,0,sizeof(s_spawn_entry));
                next.groupmin = atoi(findarg(buf+pos, 1));
                next.groupmax = atoi(findarg(buf+pos, 2));
                if(next.groupmax < 1) next.groupmax = 1;
                if(next.groupmin < 1) next.groupmin = 100;
            }
            else if(stricmp(command, "spawn")==0){
                // Back to defaults
                memset(&next,0,sizeof(s_spawn_entry));
                // Name of entry to be spawned
                strncpy(next.name, findarg(buf+pos, 1), MAX_NAME_LEN);
                // Load model (if not loaded already)
                load_cached_model(next.name);
            }
            else if(stricmp(command, "2pspawn")==0){
                // Entity only for 2p game
                next.spawn2p = 1;
            }
            else if(stricmp(command, "3pspawn")==0){  //4player
                // Entity only for 4p game
                next.spawn3p = 1;
            }
            else if(stricmp(command, "4pspawn")==0){ //4player
                // Entity only for 4p game
                next.spawn4p = 1;
            }            
            else if(stricmp(command, "boss")==0){
                next.boss = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "flip")==0){
                next.flip = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "health")==0){
                next.health = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "2phealth")==0){	// Health the spawned entity will have if 2 people are playing
				next.health2p = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "3phealth")==0){	// Health the spawned entity will have if 2 people are playing
				next.health3p = atoi(findarg(buf+pos, 1));  //4player
			}
            else if(stricmp(command, "4phealth")==0){	// Health the spawned entity will have if 2 people are playing
				next.health4p = atoi(findarg(buf+pos, 1));  //4player
			}			
		    else if(stricmp(command, "mp")==0){		// mp values to put max mp for player by tails
				next.mp = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "score")==0){	// So score can be overriden in the levels .txt file
                next.score = atoi(findarg(buf+pos, 1));
				if(next.score < 0) next.score = 0;	// So negative values cannot be added
                next.multiple = atoi(findarg(buf+pos, 2));
                if(next.multiple < 0) next.multiple = 0;	// So negative values cannot be added
            }
            else if(stricmp(command, "nolife")==0){	// Flag to determine if entity life is shown when hit
				next.nolife = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "alias")==0){
                // Alias (name displayed) of entry to be spawned
                strncpy(next.alias, findarg(buf+pos, 1), MAX_NAME_LEN);
            }
            else if(stricmp(command, "map")==0){
                // Colourmap for new entry
                next.colourmap = atoi(findarg(buf+pos, 1));
            }
		    else if(stricmp(command, "alpha")==0){
                // Item to be contained by new entry
                next.alpha = atoi(findarg(buf+pos, 1));
            }
			else if(stricmp(command, "dying")==0){	// Used to store which remake corresponds with the dying flash
				next.dying = atoi(findarg(buf+pos, 1));
				next.per1 = atoi(findarg(buf+pos, 2));
				next.per2 = atoi(findarg(buf+pos, 3));
			}
            else if(stricmp(command, "item")==0){
                // Item to be contained by new entry
                next.if2p = 0;
				next.if3p = 0; //4player
				next.if4p = 0; //4player                
                
                strncpy(next.item, findarg(buf+pos, 1), MAX_NAME_LEN);
                // Load model (if not loaded already)
                load_cached_model(next.item);
            }
            else if(stricmp(command, "2pitem")==0){
                // Item only for 2p game
                next.if2p = 1;
                strncpy(next.item, findarg(buf+pos, 1), MAX_NAME_LEN);
                // Load model (if not loaded already)  // 2007-2-12, inserted by UTunnels
                load_cached_model(next.item);
            }
            else if(stricmp(command, "3pitem")==0){
                // Item only for 2p game
                next.if3p = 1; //4player
                strncpy(next.item, findarg(buf+pos, 1), MAX_NAME_LEN);
                // Load model (if not loaded already)  // 2007-2-12, inserted by UTunnels
                load_cached_model(next.item);
            }
            else if(stricmp(command, "4pitem")==0){
                // Item only for 2p game
                next.if4p = 1; //4player 
                strncpy(next.item, findarg(buf+pos, 1), MAX_NAME_LEN);
                // Load model (if not loaded already) // 2007-2-12, inserted by UTunnels
                load_cached_model(next.item);
            }            
            else if(stricmp(command, "itemmap")==0){
				next.itemmap = atoi(findarg(buf+pos, 1));
			}
            else if(stricmp(command, "itemhealth")==0){
				next.itemhealth = atoi(findarg(buf+pos, 1));
			}
			else if(stricmp(command, "itemalias")==0){
				strncpy(next.itemalias, findarg(buf+pos, 1), MAX_NAME_LEN);
			}
			else if(stricmp(command, "weapon")==0){  //spawn with a weapon 2007-2-12 by UTunnels
				strncpy(next.weapon, findarg(buf+pos, 1), MAX_NAME_LEN);
                // Load model (if not loaded already)
                load_cached_model(next.weapon);
			}
			else if(stricmp(command, "aggression")==0){ // Aggression can be set per spawn.
                 next.aggression = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "credit")==0){
                next.credit = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "itemtrans")==0 || stricmp(command, "itemalpha")==0){
                next.itemtrans = atoi(findarg(buf+pos, 1));
            }
            else if(stricmp(command, "coords")==0){
                next.x = atoi(findarg(buf+pos, 1));
                next.z = atoi(findarg(buf+pos, 2));
                next.a = atoi(findarg(buf+pos, 3));
            }
            else if(stricmp(command, "at")==0){
                // Place entry on queue
                next.at = atoi(findarg(buf+pos, 1));

                if(level->numspawns >= LEVEL_MAX_SPAWNS) shutdown("Level error: too many entries (max. %i)", LEVEL_MAX_SPAWNS);
                memcpy(&level->spawnpoints[level->numspawns], &next, sizeof(s_spawn_entry));
                level->numspawns++;

                // And clear...
                memset(&next,0,sizeof(s_spawn_entry));
            }
            else{
                if(buf != NULL){
					tracefree(buf);
					buf = NULL;
				}
                shutdown("Command '%s' not understood!", command);
            }
        }

        // Go to next line
        while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
        while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
    }
    if(buf != NULL){
		tracefree(buf);
		buf = NULL;
	}

	timeleft = level->settime * COUNTER_SPEED;	// Feb 24, 2005 - This line moved here to set custom time
    level->width = level->numpanels * panel_width;

    if(level->scrolldir==SCROLL_LEFT) advancex = level->width-hRes1;

    if(level->numpanels < 1) shutdown("Level error: level has no panels");
}






// ---------------------------------- Status --------------------------------


void drawlifebar(int x, int y, int h, int maxh){

    if (colorbars==1) {

        int overflow = 0;
        int overflo2 = 0;
        int overflo3 = 0;
        int overflo4 = 0;
        int overflo5 = 0;
        int overflo6 = 0;
        int overflo7 = 0;

        if(maxh<=0) return;
        if(h<0) h = 0;
        if(maxh < h) maxh = h;
        if(h > lbarsize[0]){
            overflow = h % lbarsize[0];
            if(h > 200) overflo2 = (h - 100) % lbarsize[0];
            if(h > 300) overflo3 = (h - 200) % lbarsize[0];
            if(h > 400) overflo4 = (h - 300) % lbarsize[0];
            if(h > 500) overflo5 = (h - 400) % lbarsize[0];
            if(h > 600) overflo6 = (h - 500) % lbarsize[0];
            if(h > 700) overflo7 = (h - 600) % lbarsize[0];
            h = lbarsize[0];
        }
        if(maxh > lbarsize[0]) maxh = lbarsize[0];

        if(!lbarsize[2]){
        	line(x+1,      y+lbarsize[1] + 2, x+maxh+2, y+lbarsize[1] + 2, color_black, vscreen);
        	line(x+maxh+2, y+1, x+maxh+2, y+lbarsize[1] + 2, color_black, vscreen);

        	line(x,        y,   x+maxh+1, y,   color_white, vscreen);
        	line(x,        y,   x,        y+lbarsize[1] + 1, color_white, vscreen);
        	line(x,        y+lbarsize[1] + 1, x+maxh+1, y+lbarsize[1] + 1, color_white, vscreen);
        	line(x+maxh+1, y,   x+maxh+1, y+lbarsize[1] + 1, color_white, vscreen);
		}

        drawbox_trans(x+h+1, y+1, maxh-h, lbarsize[1], color_purple, vscreen, lut_mul);
        drawbox(x+1, y+1, h, lbarsize[1], color_green, vscreen);

        if(h <= maxh*.5) drawbox(x+1, y+1, h, lbarsize[1], color_yellow, vscreen);   // if players are between 26%- 50% life change the HP Bar color
        if(h <= maxh*.25) drawbox(x+1, y+1, h, lbarsize[1], color_red, vscreen);     // if player are below 26% life change the color again
        if(overflow) drawbox(x+1, y+1, overflow, lbarsize[1], color_blue, vscreen);
        if(overflo2){
            drawbox(x+1, y+1, h, lbarsize[1], color_blue, vscreen);
            drawbox(x+1, y+1, overflo2, lbarsize[1], color_orange, vscreen);
        }
        if(overflo3){
            drawbox(x+1, y+1, h, lbarsize[1], color_orange, vscreen);
            drawbox(x+1, y+1, overflo3, lbarsize[1], color_pink, vscreen);
        }
        if(overflo4){
            drawbox(x+1, y+1, h, lbarsize[1], color_pink, vscreen);
            drawbox(x+1, y+1, overflo4, lbarsize[1], color_purple, vscreen);
        }
        if(overflo5){
            drawbox(x+1, y+1, h, lbarsize[1], color_purple, vscreen);
            drawbox(x+1, y+1, overflo5, lbarsize[1], color_black, vscreen);
        }
        if(overflo6){
            drawbox(x+1, y+1, h, lbarsize[1], color_black, vscreen);
            drawbox(x+1, y+1, overflo6, lbarsize[1], color_white, vscreen);
        }
        if(overflo7){
            drawbox(x+1, y+1, h, lbarsize[1], color_white, vscreen);
            drawbox(x+1, y+1, overflo7, lbarsize[1], color_white, vscreen);
        }
    }
    else {
        int overflow = 0;

        if(maxh<=0) return;
        if(h<0) h = 0;
        if(maxh < h) maxh = h;

        if(h > lbarsize[0]){
            overflow = h % lbarsize[0];
            h = lbarsize[0];
        }

        if(maxh > lbarsize[0]) maxh = lbarsize[0];

        if(!lbarsize[2]){
	        line(x+1,      y+lbarsize[1] + 2, x+maxh+2, y+lbarsize[1] + 2, color_black, vscreen);
	        line(x+maxh+2, y+1, x+maxh+2, y+lbarsize[1] + 2, color_black, vscreen);

	        line(x,        y,   x+maxh+1, y,   color_white, vscreen);
	        line(x,        y,   x,        y+lbarsize[1] + 1, color_white, vscreen);
	        line(x,        y+lbarsize[1] + 1, x+maxh+1, y+lbarsize[1] + 1, color_white, vscreen);
	        line(x+maxh+1, y,   x+maxh+1, y+lbarsize[1] + 1, color_white, vscreen);
		}

        drawbox_trans(x+h+1, y+1, maxh-h, lbarsize[1], color_red, vscreen, lut_mul);
        drawbox(x+1, y+1, h, lbarsize[1], color_yellow, vscreen);

        if(overflow) drawbox(x+1, y+1, overflow, lbarsize[1], color_orange, vscreen);
    }

}

// end new lifebar colors!---------------------------------------------------------------------



void drawmpbar(int x, int y, int m, int maxm){
        int overflow = 0;
        int overflo2 = 0;
        int overflo3 = 0;
        int overflo4 = 0;
        int overflo5 = 0;
        int overflo6 = 0;
        int overflo7 = 0;	

        if(maxm<=0) return;
        if(m<0) m = 0;
        if(maxm < m) maxm = m;
        if(m > mpbarsize[0]){
            overflow = m % mpbarsize[0];
            if(m > 200) overflo2 = (m - 100) % mpbarsize[0];// so this use lifebar to be larger of in position  by tails
            if(m > 300) overflo3 = (m - 200) % mpbarsize[0];
            if(m > 400) overflo4 = (m - 300) % mpbarsize[0];
            if(m > 500) overflo5 = (m - 400) % mpbarsize[0];
            if(m > 600) overflo6 = (m - 500) % mpbarsize[0];
            if(m > 700) overflo7 = (m - 600) % mpbarsize[0];
            m = mpbarsize[0];
        }
        if(maxm > mpbarsize[0]) maxm = mpbarsize[0];

		if(!mpbarsize[2]){
        	line(x+1,      y+mpbarsize[1] + 2, x+maxm+2, y+mpbarsize[1] + 2, color_black, vscreen);
        	line(x+maxm+2, y+1, x+maxm+2, y+mpbarsize[1] + 2, color_black, vscreen);

			line(x,        y,   x+maxm+1, y,   color_white, vscreen);
			line(x,        y,   x,        y+mpbarsize[1]+1, color_white, vscreen);
			line(x,        y+mpbarsize[1]+1, x+maxm+1, y+mpbarsize[1]+1, color_white, vscreen);
			line(x+maxm+1, y,   x+maxm+1, y+mpbarsize[1]+1, color_white, vscreen);
		}

        drawbox_trans(x+m+1, y+1, maxm-m, mpbarsize[1], color_magic, vscreen, lut_mul);
        drawbox(x+1, y+1, m, mpbarsize[1], color_magic, vscreen);

        if(m <= maxm*.5) drawbox(x+1, y+1, m, mpbarsize[1], color_magic, vscreen);   // if players are between 26%- 50% life change the HP Bar color
        if(m <= maxm*.25) drawbox(x+1, y+1, m, mpbarsize[1], color_magic, vscreen);     // if player are below 26% life change the color again
        if(overflow) drawbox(x+1, y+1, overflow, mpbarsize[1], color_magic2, vscreen);
        if(overflo2){
                drawbox(x+1, y+1, m, mpbarsize[1], color_magic, vscreen);
                drawbox(x+1, y+1, overflo2, mpbarsize[1], color_magic, vscreen);
        }
        if(overflo3){
                drawbox(x+1, y+1, m, mpbarsize[1], color_magic2, vscreen);
                drawbox(x+1, y+1, overflo3, mpbarsize[1], color_magic, vscreen);
        }
        if(overflo4){
                drawbox(x+1, y+1, m, mpbarsize[1], color_magic, vscreen);
                drawbox(x+1, y+1, overflo4, mpbarsize[1], color_magic, vscreen);
        }
        if(overflo5){
                drawbox(x+1, y+1, m, mpbarsize[1], color_magic2, vscreen);
                drawbox(x+1, y+1, overflo5, mpbarsize[1], color_magic, vscreen);
        }
        if(overflo6){
                drawbox(x+1, y+1, m, mpbarsize[1], color_black, vscreen);
                drawbox(x+1, y+1, overflo6, mpbarsize[1], color_magic, vscreen);
        }
        if(overflo7){
                drawbox(x+1, y+1, m, mpbarsize[1], color_white, vscreen);
                drawbox(x+1, y+1, overflo7, mpbarsize[1], color_magic, vscreen);
        }
}



void spawnplayer(int);
void drop_all_enemies();
void kill_all_enemies();
void smart_bomb(entity *e, int p, int t, int l);	// New function
void ent_set_colourmap(entity *ent, unsigned int which);




void predrawstatus(){

    int dt;
	int tperror=0; //4player    
    int icon = 0;
    int i,x;
    static int pauselector = 0;
	s_model * model = NULL;
	
	for(i=0; i<maxplayers; i++){
        if(player[i].ent){

			if(!pscore[i][2] && !pscore[i][3] && !pscore[i][4] && !pscore[i][5]) font_printf(shiftpos[i]+pscore[i][0], savedata.windowpos+pscore[i][1], 0, "%s - %06u", player[i].ent->name, player[i].score);
			else{
				font_printf(shiftpos[i]+pscore[i][0], savedata.windowpos+pscore[i][1], 0, "%s", player[i].ent->name);
				font_printf(shiftpos[i]+pscore[i][2], savedata.windowpos+pscore[i][3], 0, "-");
				font_printf(shiftpos[i]+pscore[i][4], savedata.windowpos+pscore[i][5], 0, "%06u", player[i].score);
			}

			if(player[i].iconmode == 0) icon = player[i].ent->model->icon;    // 20-1-2005 New icons here
            else if(player[i].iconmode == 1) icon = player[i].ent->model->iconpain;// 20-1-2005 New icons here
            else if(player[i].iconmode == 2) icon = player[i].ent->model->icondie;// 20-1-2005 New icons here
            else if(player[i].iconmode == 3) icon = player[i].ent->model->iconget;// 20-1-2005 New icons here

            if(icon>=0) spriteq_add(shiftpos[i]+picon[i][0],savedata.windowpos+picon[i][1],10000, sprites[1][icon], SFX_REMAP, player[i].ent->map);

		    if(player[i].ent->weapent){
			    if(player[i].ent->weapent->model->iconw >= 0)
					spriteq_add(shiftpos[i]+piconw[i][0],savedata.windowpos+piconw[i][1],10000, sprites[1][player[i].ent->weapent->model->iconw], SFX_REMAP, player[i].ent->weapent->map);

	    		if(player[i].ent->weapent->model->typeshot && player[i].ent->weapent->shootnum)
	    			font_printf(shiftpos[i]+pshoot[i][0], savedata.windowpos+pshoot[i][1], 2, "%u", player[i].ent->weapent->shootnum);
	    	}

			if(player[i].ent->maxmp){
				if(player[i].ent->model->iconmp[0] > 0 && (player[i].ent->oldmp >= (player[i].ent->maxmp * .66))){
					spriteq_add(shiftpos[i]+mpicon[i][0],savedata.windowpos+mpicon[i][1],10000, sprites[1][player[i].ent->model->iconmp[0]], SFX_REMAP, player[i].ent->map);
				}
				else if(player[i].ent->model->iconmp[1] > 0 && (player[i].ent->oldmp >= (player[i].ent->maxmp * .33) && player[i].ent->oldmp < (player[i].ent->maxmp * .66))){
					spriteq_add(shiftpos[i]+mpicon[i][0],savedata.windowpos+mpicon[i][1],10000, sprites[1][player[i].ent->model->iconmp[1]], SFX_REMAP, player[i].ent->map);
				}
				else if(player[i].ent->model->iconmp[2] > 0 && (player[i].ent->oldmp >= 0 && player[i].ent->oldmp < (player[i].ent->maxmp * .33))){
					spriteq_add(shiftpos[i]+mpicon[i][0],savedata.windowpos+mpicon[i][1],10000, sprites[1][player[i].ent->model->iconmp[2]], SFX_REMAP, player[i].ent->map);
				}
				else if(player[i].ent->model->iconmp[0] > 0 && player[i].ent->model->iconmp[1] == -1 && player[i].ent->model->iconmp[2] == -1){
					spriteq_add(shiftpos[i]+mpicon[i][0],savedata.windowpos+mpicon[i][1],10000, sprites[1][player[i].ent->model->iconmp[0]], SFX_REMAP, player[i].ent->map);
				}
			}

			font_printf(shiftpos[i]+plifeX[i][0],savedata.windowpos+plifeX[i][1], 0, "x");
			font_printf(shiftpos[i]+plifeN[i][0],savedata.windowpos+plifeN[i][1], 3, "%i", player[i].lives);

			if(rush[0] && player[i].ent->rush[0] > 1 && time <= player[i].ent->rushtime){
				font_printf(shiftpos[i]+prush[i][0],shiftpos[i]+prush[i][1], rush[2], "%s", rush_names[0]);
				font_printf(shiftpos[i]+prush[i][2],shiftpos[i]+prush[i][3], rush[3], "%i", player[i].ent->rush[0]);
				
				if(rush[0] != 2){
					font_printf(shiftpos[i]+prush[i][4],shiftpos[i]+prush[i][5], rush[4], "%s", rush_names[1]);
					font_printf(shiftpos[i]+prush[i][6],shiftpos[i]+prush[i][7], rush[5], "%i", player[i].ent->rush[1]);
				}
			}

			if(rush[0] == 2){
				font_printf(shiftpos[i]+prush[i][4],shiftpos[i]+prush[i][5], rush[4], "%s", rush_names[1]);
				font_printf(shiftpos[i]+prush[i][6],shiftpos[i]+prush[i][7], rush[5], "%i", player[i].ent->rush[1]);
			}
			
            if(player[i].opponent && !player[i].opponent->nolife){	// Displays life unless overridden by flag
                font_printf(shiftpos[i]+ename[i][0], savedata.windowpos+ename[i][1], 0, player[i].opponent->name);
                icon = player[i].opponent->model->icon;

                if(icon>=0) spriteq_add(shiftpos[i]+eicon[i][0],savedata.windowpos+eicon[i][1],10000, sprites[1][icon],SFX_REMAP, player[i].opponent->map);	// Feb 26, 2005 - Changed to opponent->map so icons don't pallete swap with die animation
            }
        }
        else if(player[i].joining && strlen(player[i].name)){
        	model = find_model(player[i].name);
            font_printf(shiftpos[i]+pnameJ[i][0], savedata.windowpos+pnameJ[i][1], 0, player[i].name);
            if(nojoin) font_printf(shiftpos[i]+pnameJ[i][2], savedata.windowpos+pnameJ[i][3], 0, "%s",stringtable[78]); //please wait
            else font_printf(shiftpos[i]+pnameJ[i][2], savedata.windowpos+pnameJ[i][3], 0, "%s",stringtable[55]); //select hero
            icon = model->icon;


            if(icon>=0 && !player[i].colourmap){
            	spriteq_add(shiftpos[i]+picon[i][0],picon[i][1],10000, sprites[1][icon], 0, NULL);
			}
            else if(icon>=0) spriteq_add(shiftpos[i]+picon[i][0],picon[i][1], 10000, sprites[1][icon], SFX_REMAP, model->colourmap[player[i].colourmap - 1]);

            if(player[i].playkeys & FLAG_ANYBUTTON && !freezeall && !nojoin){	// Can't join while animations are frozen

				// reports error if players try to use the same character and sameplay mode is off
				if(sameplayer){
					for(x=0; x<maxplayers; x++){
						if((strncmp(player[i].name,player[x].name,strlen(player[i].name)) == 0) && (i != x)){
							tperror = i+1;
							break;
						}
					}
				}

				if (!tperror){	// Fixed so players can be selected if other player is no longer va //4player	                    player[i].playkeys = player[i].newkeys = 0;
                    player[i].lives = PLAYER_LIVES;			// to address new lives settings
                    player[i].joining = 0;
                    player[i].hasplayed = 1;

                    player[i].spawnhealth = model->health;
                    player[i].spawnmp = model->mp;
                    spawnplayer(i);

                    if(!nodropen) drop_all_enemies();   //27-12-2004  If drop enemies is on, drop all enemies

                    if(!level->noreset) timeleft = level->settime * COUNTER_SPEED;	// Feb 24, 2005 - This line moved here to set custom time
                }
            }
            else if(player[i].playkeys & FLAG_MOVELEFT){
				player[i].colourmap = i;
				model = prevplayermodel(model);
				strcpy(player[i].name, model->name);
                player[i].playkeys = 0;
            }
            else if(player[i].playkeys & FLAG_MOVERIGHT){
				player[i].colourmap = i;
				model = nextplayermodel(model);
				strcpy(player[i].name, model->name);
                player[i].playkeys = 0;
            }
            // don't like a characters color try a new one!
            else if(player[i].playkeys & FLAG_MOVEUP && colourselect){

				do{
					player[i].colourmap++;

					if(player[i].colourmap > model->maps_loaded) player[i].colourmap = 0;
				}
				while(	// Keep looping until a non-fmap is found
					model->fmap &&
					model->colourmap[player[i].colourmap - 1] == model->colourmap[model->fmap - 1]
				);

                player[i].playkeys = 0;
            }
            else if(player[i].playkeys & FLAG_MOVEDOWN && colourselect){

				do{
					player[i].colourmap--;

					if(player[i].colourmap < 0) player[i].colourmap = model->maps_loaded;
				}
				while(	// Keep looping until a non-fmap is found
					model->fmap &&
					model->colourmap[player[i].colourmap - 1] == model->colourmap[model->fmap - 1]
				);

				player[i].playkeys = 0;
            }
        }
        else if(player[i].credits || credits || (!player[i].hasplayed && noshare)){
            if(player[i].credits && (time/(GAME_SPEED*2)) & 1) font_printf(shiftpos[i]+pnameJ[i][4], savedata.windowpos+pnameJ[i][5], 0, "%s %i",stringtable[57], player[i].credits); //Credit
            else if(credits && (time/(GAME_SPEED*2)) & 1) font_printf(shiftpos[i]+pnameJ[i][4], savedata.windowpos+pnameJ[i][5], 0, "%s %i",stringtable[57], credits); //Credit
            else if(nojoin) font_printf(shiftpos[i]+pnameJ[i][4], savedata.windowpos+pnameJ[i][5], 0, "%s",stringtable[78]); //Please Wait
            else font_printf(shiftpos[i]+pnameJ[i][4], savedata.windowpos+pnameJ[i][5], 0, "%s",stringtable[56]); //Press start

            if(player[i].playkeys & FLAG_START){
                player[i].lives = 0;
				model = nextplayermodel(NULL);
				strcpy(player[i].name, model->name);
				player[i].colourmap = i;
                player[i].joining = 1;
                player[i].playkeys = 0;

                if(!level->noreset) timeleft = level->settime * COUNTER_SPEED;	// Feb 24, 2005 - This line moved here to set custom time

				if(!player[i].hasplayed && noshare) player[i].credits = CONTINUES;

                if(!creditscheat){
                	if(noshare) --player[i].credits;
					else --credits;
				}
            }
        }
        else font_printf(shiftpos[i]+pnameJ[i][4], savedata.windowpos+pnameJ[i][5], 0, "%s", stringtable[9]); //GAME OVER
    }

#if PSP || SDL
	if(fpsCounter) font_printf(2, dOffset, 0, "FPS: %02d", current_fps);
#endif

	dt = timeleft/COUNTER_SPEED;
    if(dt>99) dt = 99;
    if(dt<0) dt = 0;
    if(!level->notime) font_printf(hOffset+xOffset+timeloc[0]+ 2,savedata.windowpos+timeloc[1] + 2, 3, "%02i", dt);
	if(showtimeover) font_printf(hOffset+xOffset+113,110, 3, "TIME OVER");

	if(dt<99) showtimeover = 0;

    if(go_time>time){
        dt = (go_time-time)%GAME_SPEED;

        if(dt < GAME_SPEED/2){
            if(level->scrolldir==SCROLL_LEFT){

                if(golsprite) spriteq_add(40,60+vOffset,10000, sprites[1][golsprite], 0, NULL); // new sprite for left direction
                else spriteq_add(40,60+vOffset,10000, sprites[0][gosprite], 0, NULL);

                if(gosound == 0 ){

                    if(smp_go >= 0) sound_play_sample(smp_go, 0, savedata.effectvol,savedata.effectvol, 100);// 26-12-2004 Play go sample as arrow flashes

                    gosound = 1;				// 26-12-2004 Sets sample as already played - stops sample repeating too much
                }
            }
            else{
                spriteq_add(hRes1-40,60+vOffset,10000, sprites[1][gosprite], 0, NULL);

				if(gosound == 0 ){

                    if(smp_go >= 0) sound_play_sample(smp_go, 0, savedata.effectvol,savedata.effectvol, 100); // 26-12-2004 Play go sample as arrow flashes

                    gosound = 1;  // 26-12-2004 Sets sample as already played - stops sample repeating too much
                }
            }
        }
        else gosound = 0;    //26-12-2004 Resets go sample after loop so it can be played again next time
    }


    if(pause){
		font_printf(135+hOffset+xOffset,100+vOffset, 3, "%s",stringtable[52]);
        font_printf(135+hOffset+xOffset,120+vOffset, (pauselector==0), "%s",stringtable[53]);
        font_printf(135+hOffset+xOffset,132+vOffset, (pauselector==1), "%s",stringtable[54]);

		if(bothnewkeys & (FLAG_MOVEUP|FLAG_MOVEDOWN)){
			pauselector ^= 1;
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
        if(bothnewkeys & FLAG_START){
            if(pauselector){
                player[0].lives = player[1].lives = player[2].lives = player[3].lives = 0; //4player
                endgame = 1;
            }
            pause = 0;
            sound_pause_music(0);
			sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
        }
        if(bothnewkeys & FLAG_ESC){
            pause = 0;
            sound_pause_music(0);
			sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
        }
		if(bothnewkeys & FLAG_SCREENSHOT){
            pause = 1;
            sound_pause_music(1);
			sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
			options();
        }

        bothnewkeys = 0;
    }
    else if(
        (player[0].ent && (player[0].newkeys & FLAG_START)) ||
        (player[1].ent && (player[1].newkeys & FLAG_START)) ||
        (player[2].ent && (player[2].newkeys & FLAG_START)) ||
        (player[3].ent && (player[3].newkeys & FLAG_START)) ||
        (bothnewkeys & FLAG_ESC)){

		pause = 1;
		sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
        sound_pause_music(1);
        pauselector = 0;
    }
}



void drawstatus(){
	int i;

	for(i=0; i < MAX_PLAYERS; i++){
        if(player[i].ent){
			// Health bars
			drawlifebar(shiftpos[i]+plife[i][0], savedata.windowpos+plife[i][1], player[i].ent->oldhealth, player[i].ent->maxhealth);
	        if(player[i].opponent && !player[i].opponent->nolife) drawlifebar(shiftpos[i]+elife[i][0], savedata.windowpos+elife[i][1], player[i].opponent->oldhealth, player[i].opponent->maxhealth); // Tied in with the nolife flag
		    // Draw mpbar
			if(player[i].ent->maxmp) drawmpbar(shiftpos[i]+pmp[i][0], savedata.windowpos+pmp[i][1], player[i].ent->oldmp, player[i].ent->maxmp);
        }
    }

    // Time box
    if(!level->notime && !timeloc[4] && !timeicon[0])	// Only draw if notime is set to 0 or not specified
    {
		line(hOffset+xOffset+timeloc[0], vOffset+yOffset+savedata.windowpos+timeloc[1], hOffset+xOffset+timeloc[0]+timeloc[2], vOffset+yOffset+savedata.windowpos+timeloc[1],   color_black, vscreen);
		line(hOffset+xOffset+timeloc[0], vOffset+yOffset+savedata.windowpos+timeloc[1], hOffset+xOffset+timeloc[0], vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3],  color_black, vscreen);
		line(hOffset+xOffset+timeloc[0]+timeloc[2], vOffset+yOffset+savedata.windowpos+timeloc[1], hOffset+xOffset+timeloc[0]+timeloc[2], vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3],  color_black, vscreen);
		line(hOffset+xOffset+timeloc[0], vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3], hOffset+xOffset+timeloc[0]+timeloc[2], vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3], color_black, vscreen);
		line(hOffset+xOffset+timeloc[0] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1] - 1, hOffset+xOffset+timeloc[0]+timeloc[2] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1] - 1,   color_white, vscreen);
		line(hOffset+xOffset+timeloc[0] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1] - 1, hOffset+xOffset+timeloc[0] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3] - 1,  color_white, vscreen);
		line(hOffset+xOffset+timeloc[0]+timeloc[2] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1] - 1, hOffset+xOffset+timeloc[0]+timeloc[2] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3] - 1,  color_white, vscreen);
		line(hOffset+xOffset+timeloc[0] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3] - 1, hOffset+xOffset+timeloc[0]+timeloc[2] - 1, vOffset+yOffset+savedata.windowpos+timeloc[1]+timeloc[3] - 1, color_white, vscreen);
	}
	else if(timeicon[0]) spriteq_add(hOffset+xOffset+timeicon[1],vOffset+yOffset+savedata.windowpos+timeicon[2],10000, sprites[1][timeicon[0]], 0, NULL);
}



void addscore(int playerindex, unsigned long add){
    unsigned long s = player[playerindex&3].score;
    unsigned long next1up;

    playerindex &= 3;

    next1up = ((s/50000)+1) * 50000;

    s += add;
    if(s>999999999) s=999999999;

    while(s>next1up){

        if(smp_1up >= 0) sound_play_sample(smp_1up, 0, savedata.effectvol,savedata.effectvol, 100);

        player[playerindex].lives++;
        next1up += 50000;
    }

    player[playerindex].score = s;
}




// ---------------------------- Object handling ------------------------------




void free_ents(){
	int i;
	for(i=0; i<MAX_ENTS; i++){
		if(ent_list[i] != NULL){
			tracefree(ent_list[i]);
			ent_list[i] = NULL;
		}
	}
}


int alloc_ents(){
    int i;
    for(i=0; i<MAX_ENTS; i++){
        ent_list[i] = (entity*)tracemalloc("alloc_ents",sizeof(entity));
        if(!ent_list[i]){
            free_ents();
            return 0;
        }
        memset(ent_list[i], 0, sizeof(entity));
    }
    return 1;
}


int checkwall(float x, float z);
int checkhole(float x, float z);
int player_trymove(float xdir, float zdir);
int enemy_trymove(float xdir, float zdir);
entity * check_platform(float x, float z);
entity * homing_find_target(int type);
void enemy_fall(void);
void toss(entity *ent, float lift);	// Needs to be declared so it can be used in the update_ents() section
void knife_spawn(float x, float z, float a, int direction, int flag);	// Needs to be declared so it can be used in the update_ents() section
void bomb_spawn(float x, float z, float a, int direction);	// Needs to be declared so it can be used in the update_ents() section
void enemy_jumpattack(void);	// Needs to be declared so it can be used in the update_ents() section
void enemy_block(void);	//	Moved here so they can be used in the do_attack function
void enemy_attack(void);	//	Moved here so they can be used in the do_attack function
void enemy_pain(void);	//	Moved here so they can be used in the do_attack function
void player_pain(void);	//	Moved here so they can be used in the do_attack function
void player_spawn(void);
void enemy_spawn(void);
void player_think(void);
void enemy_think(void);
void subtract_shot(void);
void set_weapon(entity* ent, int wpnum, int pi);
void dropweapon(int flag);
void player_jump(void);
void biker_drive(void);
void arrow_fly(void);
void enemy_drop(void);
void trap_think(void);
void steamer_think(void);
void text_think(void);
void anything_walk(void);
void kill(entity*);

void player_takedamage(entity *other, int force, int drop, int type);
void enemy_takedamage(entity *other, int force, int drop, int type);
void biker_takedamage(entity *other, int force, int drop, int type);
void obstacle_takedamage(entity *other, int force, int drop, int type);

void ent_set_anim(entity*, int, int);

entity* spawn(float,float,float,char[]);


// ---------------------------------- A.I. ------------------------------------

void suicide(void);
void prethrow(void);

void player_jump(void);
void player_land(void);
void player_grab(void);
void player_grabattack(void);
void player_grabbed(void);
void player_throw(void);
void player_throw_wait(void);
void player_fall(void);
void player_rise(void);
void player_blink(void);
void player_vault(void);
void dojump(int speedmul);

void enemy_drop(void);		// Enter field
void enemy_prepare(void);
void enemy_runoff(void);
void enemy_rise(void);
void enemy_lie(entity *other, int force, int drop, int type);
void enemy_grabbed(void);
void enemy_throw(void);
void enemy_throw_wait(void);
void enemy_grab(void);
void enemy_takedamage(entity * other, int force, int drop, int type);	// Added so enemy projectiles can be knocked down
void arrow_fly(void);  // 7-1-2005  new types here
void knife_think(void);
void biker_drive(void); // 7-1-2005  new types here
void biker_takedamage(entity *other, int force, int drop, int type); // 7-1-2005  new types here

// NPC A.I. 
#define npc_find_item  enemy_find_item
#define npc_pickupitem enemy_pickupitem
void npc_throw_wait(void);
void npc_throw(void);
void npc_fall(void);
void npc_block(void);
void npc_spawn(void);
void npc_pain(void);
void npc_drop(void);
void npc_warp(void);
void npc_attack(void);
void npc_prepare(void);
void npc_runoff(void);
void npc_rise(void);
void npc_grab(void);
void npc_grabbed(void);
void npc_think(void);
int  npc_trymove(float xdir, float zdir);
void npc_takedamage(entity *other, int force, int drop, int type);

void ent_default_init(entity* e){
	int dodrop;

    switch(e->type){
    case TYPE_ITEM:
        //	do nothing 
        break;

    case TYPE_PLAYER:
        e->direction = (level->scrolldir != SCROLL_LEFT);
        e->takedamage = player_takedamage;

        if(time){
            // Mid-level spawn
            if(e->model->animation[ANI_RESPAWN]){
                ent_set_anim(e, ANI_RESPAWN, 0);
                e->think = player_spawn;
            } else {
    			if(level->spawn[e->playerindex][2] > e->a) e->a = level->spawn[e->playerindex][2]; //4player
    
                e->jumping = 1;
                ent_set_anim(e, ANI_JUMP, 0);
                e->think = player_jump;
            }

			if(e->model->makeinv > 0){	// Spawn invincible code
				e->invincible = 1;
				e->blink = 1;
				e->invinctime = time + e->model->makeinv;
				e->arrowon = 1;	// Display the image above the player
			}
        }
        else{

			//ent_set_anim(e, ANI_IDLE, 0);
            if(e->model->animation[ANI_SPAWN]) {
                ent_set_anim(e, ANI_SPAWN, 0);
                e->think = player_spawn;
            }
            else {
                ent_set_anim(e, ANI_IDLE, 0);
                e->think = player_think;
            }
        }

        break;
        
    case TYPE_NPC: // use NPC(or A.I. player) instread of an enemy subtype or trap subtype, for further A.I. use

       	if(!e->model->speed && !e->model->nomove) e->model->speed = 1;
    	else if(e->model->nomove) e->model->speed = 0;

    	if(!e->multiple) e->multiple = 5;

        if(e->model->subtype == SUBTYPE_NOTGRAB) e->nograb = 1;

        if(e->model->cantgrab) e->nograb = 1;

        e->takedamage = npc_takedamage; 

        if(e->model->animation[ANI_SPAWN]) e->think = npc_spawn; 
		else{
            e->think = npc_drop; //npc_drop
            dodrop = (level && (level->scrolldir==SCROLL_UP || level->scrolldir==SCROLL_DOWN));

            if((dodrop && e->x > -30 && e->x < 350) ){
				// PSP Video Update from 240 to vRes1
                e->a += vRes1 + randf(40);						

                if(e->health < 1){
                    // Funny: drop dead right away
                    ent_set_anim(e, ANI_FALL, 0);
                    e->xdir = (e->direction)?(-1.2):1.2;
                    e->think = npc_fall; 
                }
            }
            dodrop = 0;//reset the temp value for enemy code
        }
        break;

    case TYPE_ENEMY:

        if(e->model->subtype == SUBTYPE_BIKER){
            e->takedamage = biker_takedamage;
            e->think = biker_drive;
            e->nograb = 1;
            e->attacking = 1;

            if(e->x < 0) e->direction = 0;
            else e->direction = 1;

            e->xdir = 2;
            break;
        }
        // define new subtypes
        else if(e->model->subtype == SUBTYPE_ARROW){
            e->takedamage = enemy_takedamage;
            e->health = 1;

            if(!e->model->speed && !e->model->nomove) e->model->speed = 2;	// Set default speed to 2 for arrows
            else if(e->model->nomove) e->model->speed = 0;

            e->think = arrow_fly;
            e->nograb = 1;
            e->attacking = 1;
            break;
        }
        else{	// Must just be a regular enemy, set defaults accordingly

	       	if(!e->model->speed && !e->model->nomove) e->model->speed = 1;
        	else if(e->model->nomove) e->model->speed = 0;

        	if(!e->multiple) e->multiple = 5;
		}

        if(e->model->subtype == SUBTYPE_NOTGRAB) e->nograb = 1;

        if(e->model->cantgrab) e->nograb = 1;	//27-12-2004 if can't grab is on, character is ungrabbable

        e->takedamage = enemy_takedamage;

        // 1-10-05  no more enemies dropping in unless they don't have a ANI_SPWN
        if(e->model->animation[ANI_SPAWN]) e->think = enemy_spawn;
		else{
            e->think = enemy_drop;
            dodrop = (level && (level->scrolldir==SCROLL_UP || level->scrolldir==SCROLL_DOWN));

            if(dodrop && e->x > -30 && e->x < 350){
				// PSP Video Update from 240 to vRes1
                e->a += vRes1 + randf(40);						

                if(e->health < 1){
                    // Funny: drop dead right away
                    ent_set_anim(e, ANI_FALL, 0);
                    e->xdir = (e->direction)?(-1.2):1.2;
                    e->think = enemy_fall;
                }
            }
        }
        break;
        // define trap type
    case TYPE_TRAP:
        e->think = trap_think;
        e->takedamage =  enemy_takedamage;
        break;
        //

    case TYPE_OBSTACLE:     
        e->takedamage = obstacle_takedamage;
        break;

    case TYPE_STEAMER:
        e->think = steamer_think;
        e->base = e->a;
        break;

	case TYPE_TEXTBOX:	// New type for displaying text purposes
		e->think = text_think;
		freezeall = 1;	// Pause all animations while text is displayed
		break;
		
	case TYPE_SHOT:
         e->think = knife_think;
         e->attacking = 1;
         if(e->ptype == 0) e->base = e->a;
         else if(e->ptype == 1) e->base = 0;
         break;

    case TYPE_NONE:
        if(e->model->animation[ANI_WALK]){

            if(e->x - advancex < (hRes1/2)) e->xdir = e->model->speed + randf(3);
            else e->xdir = -(e->model->speed + randf(3));

            e->think = anything_walk;
            ent_set_anim(e, ANI_WALK, 0);
        }
        break;
    }     
}

void ent_spawn_ent(entity* ent){
    entity* s_ent = NULL;
    int* spawnframe = ent->animation->spawnframe;
    if(spawnframe[4] == 0)// spawn point relative to current entity
        s_ent = spawn(ent->x + ((ent->direction)?spawnframe[1]:-spawnframe[1]),ent->z + spawnframe[2], ent->a + spawnframe[3], ent->animation->subentity);
    else if(spawnframe[4] == 1){//relative to screen position
        if(level && level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN)
             s_ent = spawn(advancex+spawnframe[1], advancey+spawnframe[2],spawnframe[3], ent->animation->subentity);
        else s_ent = spawn(advancex+spawnframe[1], spawnframe[2],spawnframe[3], ent->animation->subentity);
    }
    else s_ent = spawn(spawnframe[1], spawnframe[2], spawnframe[3], ent->animation->subentity);//absolute position in level
     
    if(s_ent) {
        if(!spawnframe[4]) s_ent->direction = ent->direction;
        ent_default_init(s_ent);
        if(ent->type == TYPE_PLAYER) s_ent->playerindex = ent->playerindex;
        else s_ent->owner = ent;
        s_ent->parent = ent;  //maybe used by A.I.  
    }
}

void ent_summon_ent(entity* ent){
    entity* s_ent = NULL;
    int* spawnframe = ent->animation->summonframe;
    if(spawnframe[4] == 0)// spawn point relative to current entity
        s_ent = spawn(ent->x + ((ent->direction)?spawnframe[1]:-spawnframe[1]),ent->z + spawnframe[2], ent->a + spawnframe[3], ent->animation->subentity);
    else if(spawnframe[4] == 1){//relative to screen position
        if(level && level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN)
             s_ent = spawn(advancex+spawnframe[1], advancey+spawnframe[2],spawnframe[3], ent->animation->subentity);
        else s_ent = spawn(advancex+spawnframe[1], spawnframe[2],spawnframe[3], ent->animation->subentity);
    }
    else s_ent = spawn(spawnframe[1], spawnframe[2], spawnframe[3], ent->animation->subentity);//absolute position in level
     
    if(s_ent) {
         if(!spawnframe[4]) s_ent->direction = ent->direction;
         ent_default_init(s_ent);
         if(ent->type == TYPE_PLAYER) s_ent->playerindex = ent->playerindex;
         else s_ent->owner = ent;
         s_ent->parent = ent;  //maybe used by A.I.  
         ent->subentity = s_ent;
     }
}

void ent_set_anim(entity *ent, int aninum, int resetable){
    s_anim *ani = NULL;

    int temp = 0;	// Temporary variable added for animation purposes

    if(ent==NULL) shutdown("FATAL: tried to set animation with invalid address (no such object)");
    if(aninum<0 || aninum>=MAX_ANIS) shutdown("FATAL: tried to set animation with invalid index (%i)", aninum);

    ani = ent->model->animation[aninum];

    if(ani==NULL) shutdown("FATAL: tried to set animation with invalid address (%s, %i)", ent->name, aninum);
    if(!resetable && ent->animation == ani) return;

	// Flag incase going from grab animation to up/down/walk
	if(ent->animation == ent->model->animation[ANI_GRAB] || ent->animation == ent->model->animation[ANI_IDLE]) temp = 1;

	// Animations can now be overridden with nothing
    if(ani->numframes == 0 || (ani->energycost > 0 && ent->cantfire && 
	  (ani->tossframe >= 0 || ani->throwframe >= 0 || ani->shootframe >= 0))){	
		ent->attacking = 0;
		return;
	}
	else if(mpstrict) {
        
         
    }

	if(aninum!=ANI_SLEEP) ent->sleeptime = time + ent->model->sleepwait;
    ent->animation = ani;
    ent->animnum = aninum;	// Stored for nocost usage

	if(
		((aninum == ANI_UP ||
		aninum == ANI_DOWN ||
		aninum == ANI_WALK ||
        aninum == ANI_BACKWALK ||
        aninum == ANI_RUN) &&
		(temp == 0 &&
		ent->type == TYPE_PLAYER))
		&& (ent->model->animation[ANI_UP] ||
		ent->model->animation[ANI_DOWN] ||
        ent->model->animation[ANI_BACKWALK] ||
        ent->model->animation[ANI_RUN])
	)
		return;	// Used to help animation be more fluid

    ent->animpos = 0;

    if(!resetable) ent->lastanimpos = -1;

    ent->currentsprite = ani->sprite[0];
    ent->nextanim = time + ani->delay[0];

	if(level && (ent->animation->move[0] != 0 || ent->animation->movez[0] != 0)){

		if(ent->type == TYPE_PLAYER){

			if(ent->direction) player_trymove(ent->animation->move[0], ent->animation->movez[0]);
			else player_trymove(-ent->animation->move[0], ent->animation->movez[0]);
		}
		else if(
			(ent->type == TYPE_ENEMY  &&
			!ent->model->subtype) ||
			(ent->type == TYPE_ENEMY  &&
			ent->model->subtype == SUBTYPE_NOTGRAB)
		){

			if(ent->direction) enemy_trymove(ent->animation->move[0], ent->animation->movez[0]);
			else enemy_trymove(-ent->animation->move[0], ent->animation->movez[0]);
		}
		else if(
			(ent->type == TYPE_NPC  &&
			!ent->model->subtype) ||
			(ent->type == TYPE_NPC  &&
			ent->model->subtype == SUBTYPE_NOTGRAB)
		){

			if(ent->direction) npc_trymove(ent->animation->move[0], ent->animation->movez[0]);
			else npc_trymove(-ent->animation->move[0], ent->animation->movez[0]);
		}
		else{

			if(ent->direction) ent->x += ent->animation->move[0];
			else ent->x -= ent->animation->move[0];

			ent->z += ent->animation->movez[0];
		}
	}

	if(ent->animation->seta[0] >= 0 && ent->base == 0) ent->base = ent->animation->seta[0];

	ent->base += ent->animation->movea[0];
	ent->altbase = ent->animation->movea[0];

	if(ent->animation->flipframe == 0) ent->direction = !ent->direction;
	
	if(ent->animation->weaponframe[0] == 0){
		dropweapon(2);
		if(ent->type == TYPE_PLAYER) set_weapon(NULL, ent->animation->weaponframe[1], ent->playerindex);
		else set_weapon(ent, ent->animation->weaponframe[1], -1);
		self->weapent = self;
	}

	if(ent->animation->quakeframe[0]+ent->animation->quakeframe[3] == 0){
		if(ent->animation->quakeframe[3]%2 || ent->animation->quakeframe[2] > 0) quake = ent->animation->quakeframe[2];
		else quake = ent->animation->quakeframe[2] * -1;
		if((ent->animation->quakeframe[1]-ent->animation->quakeframe[3]) > 1) ent->animation->quakeframe[3]++;
		else ent->animation->quakeframe[3] = 0;
	}

	//spawn / summon /unsummon features
	if(ent->animation->spawnframe[0] == 0){
        ent_spawn_ent(ent);
    }
    
	if(ent->animation->summonframe[0] == 0){
        if(!ent->subentity || ent->subentity->health <=0)//subentity is dead
            ent_summon_ent(ent);
    }
    
    if(ent->animation->unsummonframe == 0){
        if(ent->subentity) {
            if(ent->subentity->takedamage) ent->subentity->takedamage(ent->subentity, 1000000, 1, ATK_NORMAL);
            else kill(ent->subentity);
            ent->subentity = NULL;
        }
    }

    if(ent->animation->soundtoplay>0 && ent->animation->soundframe == 0)
    	sound_play_sample(ent->animation->soundtoplay, 0, savedata.effectvol,savedata.effectvol, 100);

	if(ent->animation->jumpframe == 0){

		if(ent->animation->moveflag > 0) toss(ent, ent->animation->moveflag);	// Set custom jumpheight for jumpframes
		else{

			if(ent->type == TYPE_PLAYER) toss(ent, ent->model->jumpheight/2);
			else toss(ent, ent->model->jumpheight);
		}
	}

	if(ent->animation->throwframe == 0 && ent->think != enemy_jumpattack){

		knife_spawn(ent->x, ent->z, ent->a + ent->animation->throwa, ent->direction, 0);
		subtract_shot();
	}

	if(ent->animation->shootframe == 0){

		knife_spawn(ent->x, ent->z, ent->a, ent->direction, 1);
		subtract_shot();
	}

	if(ent->animation->tossframe == 0){

		bomb_spawn(ent->x, ent->z, ent->a + ent->animation->throwa, ent->direction);
		subtract_shot();
	}

    ent->animating = 1;
}



// 0 = none, 1+ = alternative
void ent_set_colourmap(entity *ent, unsigned int which){
    if(which>MAX_COLOUR_MAPS) which = 0;

    if(which==0) ent->colourmap = NULL;
    else ent->colourmap = ent->model->colourmap[which-1];
}



void ent_set_model(entity * ent, char * modelname){
    s_model *m = NULL;

    if(ent==NULL) shutdown("FATAL: tried to change model of invalid object");

    m = find_model(modelname);
    if(m==NULL) shutdown("Model not found: '%s'", modelname);
    ent->model = m;

    if(!selectScreen && !time)
        if( ent->model->animation[ANI_SPAWN]) ent_set_anim(ent, ANI_SPAWN, 0); // use new playerselect spawn anim
        else ent_set_anim(ent, ANI_IDLE, 0);
    else if(!selectScreen && time) // mid-level respawn
        if( ent->model->animation[ANI_RESPAWN]) ent_set_anim(ent, ANI_RESPAWN, 0); 
        else if( ent->model->animation[ANI_SPAWN]) ent_set_anim(ent, ANI_SPAWN, 0);
        else ent_set_anim(ent, ANI_IDLE, 0);
    else if(selectScreen && ent->model->animation[ANI_SELECT]) ent_set_anim(ent, ANI_SELECT, 0);
    else ent_set_anim(ent, ANI_IDLE, 0);

    ent_set_colourmap(ent, 0);
}



entity * spawn(float x, float z, float a, char * name){   // 7-1-2005
    entity *e = NULL;
    entity *other = NULL;
    int i, wall;
    s_model * model = NULL;

    model = find_model(name);
    if(model==NULL){
        // Be a bit more tolerant...
        return NULL;
        // shutdown("FATAL: attempt to spawn object with invalid model (%s)!", name);
    }

    for(i=0; i<MAX_ENTS; i++){

        if(!ent_list[i]->exists){

            e = ent_list[i];
            memset(e, 0, sizeof(entity));
            e->exists = 1;
            e->model = model;
            e->health = model->health;
            e->maxhealth = model->health;
		    e->mp = model->mp;
		    e->maxmp = model->mp;
            e->score = model->score;	// Added so score can be overriden
            e->multiple = model->multiple;	// Added so multiple can be overriden
            e->nolife = model->nolife;	// Added so nolife can be overriden
            e->x = x;
            e->z = z;
            e->a = a;
            e->nextthink = time + 1;
            e->type = model->type;
            e->screen = model->alpha;
            e->counter = model->counter;
            e->shootnum = model->shootnum;
			e->credit = model->credit;
            e->aggression = model->aggression;		// New 'control's for enemy A.I.: alters delay before attacking
			e->facing = model->facing;
			e->lifespancountdown = model->lifespan*GAME_SPEED; // new life span countdown
			e->hostile = model->hostile;
			e->candamage = model->candamage;
			e->projectilehit = model->projectilehit;

            if(!selectScreen && !time)
                if( e->model->animation[ANI_SPAWN]) ent_set_anim(e, ANI_SPAWN, 0); // use new playerselect spawn anim
                else ent_set_anim(e, ANI_IDLE, 0);
            else if(!selectScreen && time) // mid-level respawn
                if( e->model->animation[ANI_RESPAWN]) ent_set_anim(e, ANI_RESPAWN, 0); 
                else if( e->model->animation[ANI_SPAWN]) ent_set_anim(e, ANI_SPAWN, 0);
                else ent_set_anim(e, ANI_IDLE, 0);
            else if(selectScreen && e->model->animation[ANI_SELECT]) ent_set_anim(e, ANI_SELECT, 0);
            else ent_set_anim(e, ANI_IDLE, 0);

			other = check_platform(e->x, e->z);
			wall = checkwall(e->x, e->z);

			if(other && other != e)	e->base += other->a + other->animation->platform[other->animpos][5];
			else if(wall >= 0) e->base += level->walls[wall][7];

            strncpy(e->name, e->model->name, MAX_NAME_LEN);
            return e;
        }
    }

    return NULL;
}



// Break the link an entity has with another one
void ent_unlink(entity *e){
    if(e->link){
        e->link->link = NULL;
    }
    e->link = NULL;
}



// Link two entities together
void ents_link(entity *e1, entity *e2){
    ent_unlink(e1);
    ent_unlink(e2);
    e1->grabbing = e2;	// Added for platform layering
    e1->link = e2;
    e2->link = e1;
}



void kill(entity *victim){
    if(!victim || !victim->exists) return;

	if(victim->type == TYPE_SHOT && player[victim->playerindex].ent) player[victim->playerindex].ent->cantfire = 0;

    ent_unlink(victim);
    victim->weapent = NULL;
    victim->health = 0;
    victim->exists = 0;
    
    entity* tempent = NULL;
    int i;
    if(level){
        if(victim->parent && victim->parent->subentity == victim) victim->parent->subentity = NULL;
        victim->parent = NULL;
     // kill minions
        if(victim->model->summonkill == 1 && victim->subentity) { // kill only summoned one
            victim->subentity->parent = NULL;
            tempent = self;
            self = victim->subentity;
            if(self->takedamage) self->takedamage(self, 10000, 1, ATK_NORMAL);
            else kill(self);
            self = tempent;
        } else if(victim->model->summonkill == 2){// kill all 
    		for(i=0; i<MAX_ENTS; i++){
    			if(ent_list[i]->exists && ent_list[i]->parent == victim){
                    tempent = self;
                    self = ent_list[i];
                    self->parent = NULL;
                    if(self->takedamage) self->takedamage(self, 10000, 1, ATK_NORMAL);
                    else kill(self);
                    self = tempent;
    			}
    		}
        }
    	for(i=0; i<MAX_ENTS; i++){
    		if(ent_list[i]->exists && ent_list[i]->parent == victim){
                ent_list[i]->parent = NULL;
    		}
    	}
        victim->subentity = NULL;
    }
    
    if(victim==player[0].ent) player[0].ent = NULL;
    if(victim==player[0].opponent) player[0].opponent = NULL;
    if(victim==player[1].ent) player[1].ent = NULL;
    if(victim==player[1].opponent) player[1].opponent = NULL;
    if(victim==player[2].ent) player[2].ent = NULL;
    if(victim==player[2].opponent) player[2].opponent = NULL; //4player
    if(victim==player[3].ent) player[3].ent = NULL;
    if(victim==player[3].opponent) player[3].opponent = NULL; //4player
}




void kill_all(){
    int i;
    for(i=0; i<MAX_ENTS; i++) kill(ent_list[i]);
    time = 0;
}



int checkhit(entity *attacker, entity *target, int counter){
    int * coords1;
    int * coords2;
    int x1, x2, y1, y2;
    float medx, medy;
    int debug_coords[2][4];
    int topleast, bottomleast, leftleast, rightleast;

    if(attacker == target) return 0;
    if(diff(attacker->z,target->z) > (self->model->grabdistance/3)) return 0;
    if(!target->animation->vulnerable[target->animpos]) return 0;

    coords1 = attacker->animation->attack_coords[attacker->animpos];

    if(!counter) coords2 = target->animation->bbox_coords[target->animpos];
    else coords2 = target->animation->attack_coords[target->animpos];

    x1 = attacker->x;
    y1 = attacker->z - attacker->a;
    x2 = target->x;
    y2 = target->z - target->a;


    if(attacker->direction==0){
        debug_coords[0][0] = x1-coords1[2];
        debug_coords[0][1] = y1+coords1[1];
        debug_coords[0][2] = x1-coords1[0];
        debug_coords[0][3] = y1+coords1[3];
    }
    else{
        debug_coords[0][0] = x1+coords1[0];
        debug_coords[0][1] = y1+coords1[1];
        debug_coords[0][2] = x1+coords1[2];
        debug_coords[0][3] = y1+coords1[3];
    }
    if(target->direction==0){
        debug_coords[1][0] = x2-coords2[2];
        debug_coords[1][1] = y2+coords2[1];
        debug_coords[1][2] = x2-coords2[0];
        debug_coords[1][3] = y2+coords2[3];
    }
    else{
        debug_coords[1][0] = x2+coords2[0];
        debug_coords[1][1] = y2+coords2[1];
        debug_coords[1][2] = x2+coords2[2];
        debug_coords[1][3] = y2+coords2[3];
    }

    if(debug_coords[0][0] > debug_coords[1][2]) return 0;
    if(debug_coords[1][0] > debug_coords[0][2]) return 0;
    if(debug_coords[0][1] > debug_coords[1][3]) return 0;
    if(debug_coords[1][1] > debug_coords[0][3]) return 0;


    // Find center of attack area
    leftleast = debug_coords[0][0];
    if(leftleast < debug_coords[1][0]) leftleast = debug_coords[1][0];
    topleast = debug_coords[0][1];
    if(topleast < debug_coords[1][1]) topleast = debug_coords[1][1];
    rightleast = debug_coords[0][2];
    if(rightleast > debug_coords[1][2]) rightleast = debug_coords[1][2];
    bottomleast = debug_coords[0][3];
    if(bottomleast > debug_coords[1][3]) bottomleast = debug_coords[1][3];

    medx = (leftleast + rightleast) / 2;
    medy = (topleast + bottomleast) / 2;

    // Now convert these coords to 3D
    lasthitx = medx;

    if(attacker->z > target->z) lasthitz = attacker->z + 1;	// Changed so flashes always spawn in front
    else lasthitz = target->z + 1;

    lasthita = lasthitz - medy;

    return 1;
}



/*
	Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
	the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
	left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
	within the bottom/top and the left/right area.  If so, returns "1" (found), otherwise returns "0" (not found)
*/

int checkhole(float x, float z){
    float coef1, coef2;
    int i;

    if(level==NULL) return 0;

    if(level->exit_hole){
        if(x > level->width-(PLAYER_MAX_Z-z)) return 2;
    }

    for(i=0; i<level->numholes; i++){

		if(z < level->holes[i][1] && z > level->holes[i][1] - level->holes[i][6]){

			coef1 = (level->holes[i][1] - z) * ((level->holes[i][2] - level->holes[i][3]) / level->holes[i][6]);
			coef2 = (level->holes[i][1] - z) * ((level->holes[i][4] - level->holes[i][5]) / level->holes[i][6]);

			if(x > level->holes[i][0] + level->holes[i][3] + coef1 && x < level->holes[i][0] + level->holes[i][5] + coef2){
				holez = i;
				return 1;
			}

		}

    }

    return 0;
}



/*
	Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
	the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
	left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
	within the bottom/top and the left/right area.  If so, returns the first wall found, otherwise returns -1 (none)
*/

int checkwall(float x, float z){
    float coef1, coef2;
    int i;

    if(level==NULL) return -1;

    for(i=0; i<level->numwalls; i++){

		if(z < level->walls[i][1] && z > level->walls[i][1] - level->walls[i][6]){

			coef1 = (level->walls[i][1] - z) * ((level->walls[i][2] - level->walls[i][3]) / level->walls[i][6]);
			coef2 = (level->walls[i][1] - z) * ((level->walls[i][4] - level->walls[i][5]) / level->walls[i][6]);

			if(x > level->walls[i][0] + level->walls[i][3] + coef1 && x < level->walls[i][0] + level->walls[i][5] + coef2) return i;
		}
    }

    return -1;
}



/*
	Calculates the coef relative to the bottom left point. This is done by figuring out how far the entity is from
	the bottom of the platform and multiplying the result by the difference of the bottom left point and the top
	left point divided by depth of the platform. The same is done for the right side, and checks to see if they are
	within the bottom/top and the left/right area.  If so, returns the first entity found, otherwise returns NULL
*/

entity * check_platform(float x, float z){
    float coef1, coef2;
    entity *plat = NULL;
    int i;

    if(level==NULL) return NULL;

    for(i=0; i<MAX_ENTS; i++){
        if(ent_list[i]->exists && ent_list[i]->animation->platform[ent_list[i]->animpos][5]){
			plat = ent_list[i];

			if(z <= plat->z + 3 && z > plat->z - plat->animation->platform[plat->animpos][4]){

				coef1 = (plat->z - z) * ((plat->animation->platform[plat->animpos][0] -
				plat->animation->platform[plat->animpos][1]) / plat->animation->platform[plat->animpos][4]);

				coef2 = (plat->z - z) * ((plat->animation->platform[plat->animpos][2] -
				plat->animation->platform[plat->animpos][3]) / plat->animation->platform[plat->animpos][4]);

				if(x > plat->x + plat->animation->platform[plat->animpos][1] + coef1 && x < plat->x +
				plat->animation->platform[plat->animpos][3] + coef2) return plat;

			}
		}
	}

    return NULL;
}



void do_attack(entity *e){
    int them;
    int i;
    int force;
    entity *temp = NULL;
    entity *flash = NULL;	// Used so new flashes can be used
    entity *def = NULL;
	int didhit = 0;
    int didblock = 0;	// So a different sound effect can be played when an attack is blocked
    int current_attack_id;
	int current_follow_id = 0;
    static unsigned int new_attack_id = 1;


    // Can't get hit after this
    if(level_completed) return;
    // define "them" according to new types and settings
/*    if(e->projectile > 0) them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
    else if(e->type == TYPE_PLAYER && (!savedata.mode && !level->nohit)) them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
    else if(e->type == TYPE_SHOT && (!savedata.mode && !level->nohit)) them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
    else if(e->model->hitenemy == 1) them = TYPE_PLAYER | TYPE_ENEMY;	// Enemy projectiles can now hit enemies and players
	else if(e->model->hitenemy == 2) them = TYPE_PLAYER;				// Enemy projectiles only hit players
    else if(e->type == TYPE_ENEMY && e->model->subtype == SUBTYPE_ARROW) them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;	// Added so arrows can damage enemies, obstacles and players
    else if(e->type == TYPE_ENEMY) them = TYPE_PLAYER;
    else if(e->type == TYPE_PLAYER) them = TYPE_ENEMY | TYPE_OBSTACLE;
    else if(e->type == TYPE_SHOT) them = TYPE_ENEMY | TYPE_OBSTACLE;
    else if(e->type == TYPE_TRAP) them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
    else if(e->type == TYPE_OBSTACLE) them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;	// Added so obstacles can explode and damage enemies/players/obstacles
    else return;
    //*/
    if(e->projectile>0) them = e->projectilehit; 
    else if(e->type == TYPE_PLAYER && (savedata.mode || level->nohit)) them = (e->candamage)&(~TYPE_PLAYER);// dont hurt players
    else if(e->type == TYPE_SHOT && (savedata.mode || level->nohit)) them = (e->candamage)&(~TYPE_PLAYER);//dont hurt players
    else if(e->model->hitenemy == 1) them = TYPE_PLAYER | TYPE_ENEMY; //backward compatible for enemy projs
    else if(e->model->hitenemy == 2) them = TYPE_PLAYER;              //backward compatible for enemy projs
    else them = e->candamage;

    // Every attack gets a unique ID to make sure no one
    // gets hit more than once by the same attack
    current_attack_id = e->attack_id;

    if(!current_attack_id){
        ++new_attack_id;
        if(new_attack_id==0) new_attack_id = 1;
        e->attack_id = current_attack_id = new_attack_id;
    }


    force = e->animation->attack_force[e->animpos];

    for(i=0; i<MAX_ENTS; i++){
        if(ent_list[i]->exists &&
                (ent_list[i]->type & them) &&
                (ent_list[i]->pain_time<time || e->animation->fastattack) &&
                ent_list[i]->takedamage &&
                ent_list[i]->hit_by_attack_id != current_attack_id &&
                (!e->grabbing ||
                (e->grabbing && e->grabbing == ent_list[i])) &&
                checkhit(e, ent_list[i], 0)){

            didhit = 1;
            // don't hit the player if they are the owner of the projectile
            if(!savedata.mode && !level->nohit && e->type == TYPE_SHOT && ent_list[i]->type == TYPE_PLAYER){

                if(e->playerindex == ent_list[i]->playerindex) didhit = 0;
			}

            if(
				(e->owner &&
				e->owner == ent_list[i] &&
				(e->animation == e->model->animation[ANI_IDLE] ||
            	e->animation == e->model->animation[ANI_FAINT])) ||
            	(e->owner &&
            	ent_list[i]->owner) ||
            	(e->model->ground &&
            	inair(e))
            )
            	didhit = 0;	// Added so enemy projectiles don't hit the owner and so projectiles don't collide

            temp = self;
            self = ent_list[i];
			def = self;

            //			don't hit the dead
            if(self->health <= 0 || self->invincible) didhit = 0;	// Mar 2, 2005 - Used to make sure player can't be hit
            if(e->model->subtype == SUBTYPE_ARROW && self->model->subtype == SUBTYPE_ARROW) didhit = 0;	// So 2 arrows don't collide and destroy each other

            if(didhit){

				// New blocking code section

				if(self->toexplode == 1) self->toexplode = 2;	// Used so the bomb type entity explodes when hit
				if(e->toexplode == 1) e->toexplode = 2;	// Used so the bomb type entity explodes when hitting

				if(
					self->type == TYPE_ENEMY &&
					!(self->link ||
					inair(self) ||
					self->frozen ||
					self->direction == e->direction ||
					self->think == enemy_attack ||
					self->think == enemy_pain) &&	// Can't block if linked/in the air or frozen or opponent is a projectile
					self->model->animation[ANI_BLOCK] &&
					!e->animation->no_block[e->animpos] &&	// If unblockable, will automatically hit
					(rand32()&self->model->blockodds) == 1 && // Randomly blocks depending on blockodds (1 : blockodds ratio)
					(!self->model->thold ||
					(self->model->thold > 0 &&
					self->model->thold > force))
				){	// New threshold - can only block up to specified force

					self->think = enemy_block;
					ent_set_anim(self, ANI_BLOCK, 0);
					didblock = 1;	// Used for when playing the block.wav sound

	                // Spawn a flash
	                if(!e->animation->no_flash[e->animpos]) flash = spawn(lasthitx,lasthitz,lasthita,ent_list[i]->model->bflash);	// New block flash that can be smaller
				}
				
				else if(
					self->type == TYPE_NPC &&
					!(self->link ||
					inair(self) ||
					self->frozen ||
					self->direction == e->direction ||
					self->think == npc_attack ||
					self->think == npc_pain) &&	// Can't block if linked/in the air or frozen or opponent is a projectile
					self->model->animation[ANI_BLOCK] &&
					!e->animation->no_block[e->animpos] &&	// If unblockable, will automatically hit
					(rand32()&self->model->blockodds) == 1 && // Randomly blocks depending on blockodds (1 : blockodds ratio)
					(!self->model->thold ||
					(self->model->thold > 0 &&
					self->model->thold > force))
				){	// New threshold - can only block up to specified force

					self->think = npc_block;
					ent_set_anim(self, ANI_BLOCK, 0);
					didblock = 1;	// Used for when playing the block.wav sound

	                // Spawn a flash
	                if(!e->animation->no_flash[e->animpos]) flash = spawn(lasthitx,lasthitz,lasthita,ent_list[i]->model->bflash);	// New block flash that can be smaller
				}

				else if(self->type == TYPE_PLAYER &&
					  !(self->direction == e->direction ||
					    self->frozen) &&	// Can't block if facing the wrong direction or frozen in the block animation or opponent is a projectile
					    self->animation == self->model->animation[ANI_BLOCK] &&
					      !e->animation->no_block[e->animpos] &&	// Make sure you are actually blocking and that the attack is blockable
					  (!self->model->thold ||
					   (self->model->thold > 0 &&
					    self->model->thold > force))){	// Only block if the attack is less than the players threshold

                            didblock = 1;	// Used for when playing the block.wav sound
							if(self->model->blockpain) self->think = player_pain;		// Used for when playing pain animation during block.

							// Spawn a flash
							if(!e->animation->no_flash[e->animpos]) flash = spawn(lasthitx,lasthitz,lasthita,ent_list[i]->model->bflash);	// New block flash that can be smaller
				}

				else if(def->animation->followanim &&                        // Is there a follow anim?
                        def->animation->counterframe == def->animpos &&     // is this the right frame?
                      !(def->frozen || didblock) &&                        // check for frozen or blocked attacks  
                      ((def->animation->countercond <= 1) || (e->type & them)) &&           // Does type matter?
                      ((def->animation->countercond <= 2) || !(e->animation->no_block[e->animpos] ||  // Can you counter unblockables?
                       (def->direction == e->direction) ||   // How about back attacks?
                         (e->animation->attack_type[e->animpos] == ATK_FREEZE))) && // And freeze attacks?
                      (!def->animation->counterdamage || (def->health > force))){                   // Does damage matter?

                          if(def->animation->counterdamage) def->health -= force;     // Take damage?
                          
						  current_follow_id = ANI_FOLLOW1 + def->animation->followanim - 1;
						  ent_set_anim(def, current_follow_id, 0);
                          def->hit_by_attack_id = current_attack_id;

                          if(!e->animation->no_flash[e->animpos]) flash = spawn(lasthitx,lasthitz,lasthita,def->model->bflash);   // New block flash that can be smaller
                }

				else{	// Didn't block so go ahead and take the damage
					self->takedamage(e, force,
									 e->animation->attack_drop[e->animpos],
									 e->animation->attack_type[e->animpos]);

					// Spawn a flash
					if(!e->animation->no_flash[e->animpos]){

						if(!self->model->noatflash){

							if(e->animation->hitflash[0]) flash = spawn(lasthitx, lasthitz, lasthita, e->animation->hitflash);
							else flash = spawn(lasthitx, lasthitz, lasthita, e->model->flash);
						}
						else flash = spawn(lasthitx,lasthitz,lasthita,ent_list[i]->model->flash);
					}

					if(e->type==TYPE_PLAYER) e->combotime = time + (GAME_SPEED/2);

					if(e->animpos != e->lastanimpos || inair(e)){	// Adds pause to the current animation
						e->toss_time += e->animation->pause_add[e->animpos];	// So jump height pauses in midair
						e->nextanim += e->animation->pause_add[e->animpos];	// Animations pause for a bit when attacks hit
						e->nextthink += e->animation->pause_add[e->animpos];	// So anything that auto moves will pause
					}

					e->lastanimpos = e->animpos;

					self->toss_time += e->animation->pause_add[e->animpos];	// So jump height pauses in midair
					self->nextanim += e->animation->pause_add[e->animpos];	// Animations pause for a bit when attacks hit
					self->nextthink += e->animation->pause_add[e->animpos];	// So anything that auto moves will pause

				}

				if(flash && !e->animation->no_flash[e->animpos]){

					if(flash->model->toflip) flash->direction = (e->x > ent_list[i]->x);	// Now the flash will flip depending on which side the attacker is on

					flash->base = lasthita;
					flash->autokill = 1;
				}

				self = temp;

				if((e->animation->followanim) &&                                        // follow up?
                   (e->animation->counterframe == -1) &&                                // This isn't suppossed to be a counter, right?
                  ((e->animation->followcond < 2) || (def->type & them)) &&    // Does type matter?
                  ((e->animation->followcond < 3) || ((def->health > 0) &&
                   !didblock)) &&                   // check if health or blocking matters
                  ((e->animation->followcond < 4) || !def->model->cantgrab)){          // check if nograb matters
					  current_follow_id = ANI_FOLLOW1 + e->animation->followanim - 1;
                      ent_set_anim(e, current_follow_id, 0);          // Then go to it!
                }

				ent_list[i]->hit_by_attack_id = current_attack_id;
			}
        }

    }



    // Special case: uppercut.
    // With the uppercut, an enemy can hit a player
    // through their attacks... like a counter.
    if(e->animation == e->model->animation[ANI_UPPER]){
        for(i=0; i<MAX_ENTS; i++){
            if(ent_list[i]->exists &&
                    (ent_list[i]->type & them) &&
                    (ent_list[i]->pain_time<time || e->animation->fastattack) &&
                    ent_list[i]->takedamage &&
                    !ent_list[i]->invincible &&		// Added so invincible players don't get uppercutted
                    checkhit(e, ent_list[i], 1)){

                temp = self;
                self = ent_list[i];
                self->takedamage(e, force, 1, e->animation->attack_type[e->animpos]);
                self = temp;

                // Spawn a flash

				if(!e->animation->no_flash[e->animpos]){

					if(!self->model->noatflash){

						if(e->animation->hitflash[0]) flash = spawn(lasthitx,lasthitz,lasthita,e->animation->hitflash);
						else flash = spawn(lasthitx, lasthitz, lasthita, e->model->flash);
					}
					else flash = spawn(lasthitx,lasthitz,lasthita,ent_list[i]->model->flash);
				}

                if(flash && !e->animation->no_flash[e->animpos]){

					if(flash->model->toflip) flash->direction = (e->x > ent_list[i]->x);	// Now the flash will flip depending on which side the attacker is on

                    flash->base = lasthita;
                    flash->autokill = 1;
                }

                if(e->type==TYPE_PLAYER) e->combotime = time + (GAME_SPEED/2);

                didhit = 1;
            }
        }
    }

    if(didhit){

        if(e->type == TYPE_PLAYER && e->animation->energycost > 0 && nocost && !healthcheat) e->tocost = 1;	// Set flag so life is subtracted when animation is finished
        else if(e->type == TYPE_SHOT && e->animation->energycost > 0 && nocost && !healthcheat && !e->tocost){

			if(e->animation->mponly != 2 && player[e->playerindex].ent->mp > 0){
				player[e->playerindex].ent->mp -= e->animation->energycost;

				if(player[e->playerindex].ent->mp < 0) player[e->playerindex].ent->mp = 0;
			}
			else{
				player[e->playerindex].ent->health -= e->animation->energycost;

				if(player[e->playerindex].ent->health <= 0) player[e->playerindex].ent->health = 1;
			}

    		player[e->playerindex].ent->cantfire = 0;	// Life subtracted, so go ahead and allow firing
			e->tocost = 1;	// Little backwards, but set to 1 so cost doesn't get subtracted multiple times
		}

		// New blocking checks
        if(didblock){
           if(blockratio){              // Is damage reduced?
               force = force / 4;
               if(def->type == TYPE_PLAYER &&
                  mpblock){             // Drain MP bar first?
                  def->mp -= force;
                  if(def->mp < 0){
                     force = -def->mp;
                     def->mp = 0;
                  }
                  else force = 0;       // Damage removed from MP!
               }
               if(force < def->health)  // If an attack won't deal damage, this line won't do anything anyway.
                  def->health -= force;
               else if(nochipdeath)     // No chip deaths?
                  def->health = 1;
               else def->takedamage(e, force, e->animation->attack_drop[e->animpos],   // Must be a fatal attack, then!
                                              e->animation->attack_type[e->animpos]);
           }
        }            

		if(!didblock){
			e->rushtime = time + (GAME_SPEED/rush[1]);
			e->rush[0]++;
			if(e->rush[0] > e->rush[1] && e->rush[0] > 1) e->rush[1] = e->rush[0];
		}

		if(didblock && smp_block >= 0) sound_play_sample(smp_block, 0, savedata.effectvol,savedata.effectvol, 100);	// New block sound effect
        else if(e->projectile > 0 && smp_indirect >= 0) sound_play_sample(smp_indirect, 0, savedata.effectvol,savedata.effectvol, 100);
        else{

			if(noslowfx){
				if(!current_follow_id && e->animation->hitsound >= 0) sound_play_sample(e->animation->hitsound, 0, savedata.effectvol,savedata.effectvol, 100);
				else if(!current_follow_id && smp_beat >= 0) sound_play_sample(smp_beat, 0, savedata.effectvol,savedata.effectvol, 100);
			}
			else{
				if(!current_follow_id && e->animation->hitsound >= 0) sound_play_sample(e->animation->hitsound, 0, savedata.effectvol,savedata.effectvol, 105 - force);
				else if(!current_follow_id && smp_beat >= 0) sound_play_sample(smp_beat, 0, savedata.effectvol,savedata.effectvol, 105 - force);
			}
		}

		if(e->remove_on_attack) kill(e);
    }
}



// Update all entities that wish to think or animate in this cycle.
// All loops are separated because "self" might die during a pass.
void update_ents(){
    int f;
    int i;
    int wall;
    entity *other = NULL;
    
    if(level){
		// save weapon ents :(
		for(i=0; i<MAX_ENTS; i++){
			if(ent_list[i]->exists){
				self = ent_list[i];
				if(self->weapent) self->weapent->x = self->x;
			}
		}
		// Lost ents
		for(i=0; i<MAX_ENTS; i++){
			if(ent_list[i]->exists){
				self = ent_list[i];
				if(self->x < advancex-1000 || self->a<PIT_DEPTH*2) kill(self);
				else if(!self->lifespancountdown){
                     if(self->takedamage) self->takedamage(self,10000,1,ATK_NORMAL);
                     else kill(self);
                }//else
                // Doom count down
                if((!freezeall && !self->frozen) || (freezeall && self->animation == self->model->animation[ANI_SPECIAL]))
                self->lifespancountdown--;
			}
		}

			// Gravity
			for(i=0; i<MAX_ENTS; i++){
				if(ent_list[i]->exists){
					self = ent_list[i];

				if((!freezeall && !self->frozen) || (freezeall && self->animation == self->model->animation[ANI_SPECIAL])){	// Incase an entity is in the air, don't update animations
					if((self->tossv>0 || inair(self)) && self->toss_time <= time && !self->animation->dive[0]){

						other = check_platform(self->x, self->z);

						if(
							other &&
							self->a < other->a &&
							self->a + self->model->height >= other->a + 4 +	(self->z - other->z) &&
							self->hithead == 0
						){
							self->tossv = 0;
							self->hithead = 1;
						}

						self->a += self->tossv;

						if(self->tossv > -6) self->tossv -= 0.1;

						if(self->a < PIT_DEPTH){

							if(!self->takedamage) kill(self);
							else self->takedamage(self,10000,0,ATK_NORMAL);
							continue;
						}

						if(self->a <= self->base){
							self->a = self->base;
							self->hithead = 0;

							if(self->animation != self->model->animation[ANI_JUMP]) self->running = 0;	// Incase player was in the air and was running set to not running
						}

						self->toss_time = time + (GAME_SPEED/100);
					}

					// Check for holes
					if(
						self->base==0 &&
						!inair(self) &&
						self->type != TYPE_NONE &&	// Don't want flashes / dust to fall in holes
						(checkhole(self->x, self->z))
					){
						self->base = -1000;
						ent_unlink(self);	// Release grabs


					}
				}
			}
		}
	}


    // A.I.
    for(i=0; i<MAX_ENTS; i++){
        if(ent_list[i]->exists){
            self = ent_list[i];
            if(self->nextthink == time){
                if(self->think){
                    self->nextthink = time + THINK_SPEED;
                    self->think();
                }
            }
        }
    }



	// Animation
    for(i=0; i<MAX_ENTS; i++){
        if(ent_list[i]->exists){
            self = ent_list[i];

	        if(level){
                if(self->facing == 1 || level->facing == 1) self->direction = 1;
                else if(self->facing == 2 || level->facing == 2) self->direction = 0;
                else if((self->facing == 3 || level->facing == 3) && level->scrolldir == SCROLL_LEFT) self->direction = 1;
                else if((self->facing == 3 || level->facing == 3) && level->scrolldir == SCROLL_RIGHT) self->direction = 0;
            }
    
			if(freezeall){
				if(self->invinctime > 0) self->invinctime += 1;	// Extends invincible time if animations are frozen
				if(self->freezetime > 0) self->freezetime += 1;	// Extends frozen time if animations are frozen
			}

			if(self->invincible && time >= self->invinctime){	// Invincible time has run out, turn off
				self->invincible = 0;
				self->blink = 0;
				self->invinctime = 0;
				self->arrowon = 0;
			}

			if(self->dying && !self->frozen){	// Code for doing dying flash
				if((self->health <= self->per1 && self->health > self->per2 && (time %(GAME_SPEED / 10)) < (GAME_SPEED / 40)) ||
					(self->health <= self->per2)){

					if(self->colourmap == self->model->colourmap[self->dying - 1] || self->health <= 0 || freezeall){
						self->colourmap = self->map;
					}
					else ent_set_colourmap(self, self->dying);
				}
			}

			// Used so all entities can have a spawn animation, and then just changes to the idle animation when done

			if((self->animation == self->model->animation[ANI_SPAWN] || self->animation == self->model->animation[ANI_RESPAWN]) 
               && !self->animating && !inair(self) && self->think != player_spawn && self->think != enemy_spawn && self->think != npc_spawn)
				ent_set_anim(self, ANI_IDLE, 0);

			if(self->nextanim == time ||  //4player
            (freezeall && self->type == TYPE_TEXTBOX && self->model->subtype != SUBTYPE_NOSKIP &&	// Textbox will autoupdate if a valid player presses an action button
            ((player[0].ent && (player[0].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL) ||
            	player[0].newkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL))) ||
            (player[1].ent && (player[1].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL) ||
            player[1].newkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL)))||
            (player[2].ent && (player[2].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL) ||
            player[2].newkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL)))||
            (player[3].ent && (player[3].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL) ||
            player[3].newkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL)))))){	// Now you can display text and cycle through with any jump/attack/special unless SUBTYPE_NOSKIP
               
				// So the player doesn't execute an attack after skipping
                if(freezeall && player[0].ent && player[0].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL))
					player[0].playkeys = player[0].newkeys = 0;
                if(freezeall && player[1].ent && player[1].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL))
                	player[1].playkeys = player[1].newkeys = 0;
                if(freezeall && player[2].ent && player[2].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL))
                	player[2].playkeys = player[2].newkeys = 0;
                if(freezeall && player[3].ent && player[3].playkeys&(FLAG_JUMP|FLAG_ATTACK|FLAG_SPECIAL))
                	player[3].playkeys = player[3].newkeys = 0;


                f = self->animpos + self->animating;

                if((unsigned)f >= (unsigned)self->animation->numframes){

                    if(f<0) f = self->animation->numframes-1;
                    else f = 0;

                    if(!self->animation->loop){
                        self->animating = 0;

                        if(self->autokill){
                            kill(self);
                            continue;
                        }
                    }
                }

                if(self->animating){
                    self->nextanim = time + (self->animation->delay[f]);


					// Need to still allow textboxes to update, or players executing a smartbomb that animates

					// Only the one doing the special will update
                    if(!freezeall || (freezeall && (self->type == TYPE_TEXTBOX ||
                    	self->animation == self->model->animation[ANI_SPECIAL]))){	

						if(!self->frozen || time >= self->freezetime){	// Won't update animation if frozen

							if(self->frozen && self->colourmap == self->model->colourmap[self->model->fmap - 1])
								self->colourmap = self->map;

							self->frozen = 0;
							self->freezetime = 0;
							self->currentsprite = self->animation->sprite[f];
							self->animpos = f;

							if(level && (self->animation->move[f] != 0 || self->animation->movez[f] != 0)){

								if(self->type == TYPE_PLAYER){
							    	if(self->direction) player_trymove(self->animation->move[f], self->animation->movez[f]);
									else player_trymove(-self->animation->move[f], self->animation->movez[f]);
								}
								else if(
									(self->type == TYPE_ENEMY &&
									!self->model->subtype) ||
									(self->type == TYPE_ENEMY &&
									self->model->subtype == SUBTYPE_NOTGRAB)
								){

									if(self->direction) enemy_trymove(self->animation->move[f], self->animation->movez[f]);
									else enemy_trymove(-self->animation->move[f], self->animation->movez[f]);
								}
								else{
									if(self->direction) self->x += self->animation->move[f];
									else self->x -= self->animation->move[f];

									self->z += self->animation->movez[f];
								}
							}
							
							self->base += self->animation->movea[f]; 
							if(self->animation->movea[f]!=0) self->altbase += self->animation->movea[f]; 
							else self->altbase = 0;

							if(self->animpos == self->animation->flipframe) self->direction = !self->direction;

							if(self->animation->soundtoplay>0 && self->animation->soundframe==f) sound_play_sample(self->animation->soundtoplay, 0, savedata.effectvol,savedata.effectvol, 100);

							if(f == self->animation->jumpframe){

								if(self->animation->moveflag > 0) toss(self, self->animation->moveflag);	// Set custom jumpheight for jumpframes
								else{

									if(self->type == TYPE_PLAYER) toss(self, self->model->jumpheight/2);
									else toss(self, self->model->jumpheight);
								}
						    }

						    if(f == self->animation->throwframe && self->think != enemy_jumpattack){

        						knife_spawn(self->x, self->z, self->a + self->animation->throwa, self->direction, 0);
        						subtract_shot();
    						}

    						if(f == self->animation->shootframe){
    						    knife_spawn(self->x, self->z, self->a, self->direction, 1);
    						    subtract_shot();
    						}

    						if(f == self->animation->tossframe){
    						    bomb_spawn(self->x, self->z, self->a + self->animation->throwa, self->direction);
    						    subtract_shot();
    						}

							if(self->animpos == self->animation->weaponframe[0]){
								dropweapon(2);
								if(self->type == TYPE_PLAYER) set_weapon(NULL, self->animation->weaponframe[1], self->playerindex);
								else set_weapon(self, self->animation->weaponframe[1], -1);
								self->weapent = self;
							}

							if(self->animpos == self->animation->quakeframe[0]+self->animation->quakeframe[3]){
								if(self->animation->quakeframe[3]%2 || self->animation->quakeframe[2] > 0) quake = self->animation->quakeframe[2];
								else quake = self->animation->quakeframe[2] * -1;
								if((self->animation->quakeframe[1]-self->animation->quakeframe[3]) > 1) self->animation->quakeframe[3]++;
								else self->animation->quakeframe[3] = 0;
							}
							
                        	//spawn / summon /unsummon features
                        	if(self->animpos == self->animation->spawnframe[0]){
                                ent_spawn_ent(self);
                            }
                            
                        	if(self->animpos == self->animation->summonframe[0]){
                                if(!self->subentity || self->subentity->health<=0)
                                    ent_summon_ent(self);
                            }
                            
                            if(self->animpos == self->animation->unsummonframe){
                                if(self->subentity) {
                                    if(self->subentity->takedamage) self->subentity->takedamage(self->subentity, 1000000, 1, ATK_NORMAL);
                                    else kill(self->subentity);
                                    self->subentity = NULL;
                                }
                            }
						}
					}
                }
            }


            // Checks to see if entity is over a wall and or obstacle, and adjusts the base accordingly

			wall = checkwall(self->x, self->z);
			other = check_platform(self->x, self->z);

			if(self->base != -1000){
				if(
					other &&
					other != self &&
					self->type != TYPE_NONE &&	// Base of flashes should only be changed by seta command
					((!self->owner &&
					self->type != TYPE_SHOT) ||
					((self->owner ||
					self->type == TYPE_SHOT) &&
					(self->animation == self->model->animation[ANI_FALL] ||	// Base needs to be adjusted if projectile has been hit
					self->toexplode == 1))) &&	// Base needs to be adjusted if a grenade/bomb
					self->a >= other->animation->platform[other->animpos][5] &&
					(self->type != TYPE_ENEMY ||	// So enemies continue to move through obstacles unless no bbox set
					(self->type == TYPE_ENEMY &&
					(self->projectile == 0 ||
					!other->animation->vulnerable[other->animpos])))
				)
					self->base = (self->animation->seta[self->animpos] + self->altbase >=0 ) * (self->animation->seta[self->animpos]+self->altbase) +
					//self->base = (self->animation->seta[self->animpos]>=0 ) * (self->animation->seta[self->animpos]) +
					(other->a + other->animation->platform[other->animpos][5]) ;

				else if(
					wall >= 0 &&
					self->type != TYPE_NONE &&
					((!self->owner &&
					self->type != TYPE_SHOT) ||
					((self->owner ||
					self->type == TYPE_SHOT) &&
					(self->animation == self->model->animation[ANI_FALL] ||
					self->toexplode == 1)))
				)
					self->base = (self->animation->seta[self->animpos] + self->altbase >=0 ) * (self->animation->seta[self->animpos]+self->altbase) +
					//self->base = (self->animation->seta[self->animpos]  >=0 ) * (self->animation->seta[self->animpos]) +
					(self->a >= level->walls[wall][7]) * level->walls[wall][7] ;

				//else if(self->animation->seta[self->animpos] + self->altbase >= 0) self->base = self->animation->seta[self->animpos] + self->altbase;
				else if(self->animation->seta[self->animpos] >= 0) self->base = self->animation->seta[self->animpos] ;
				else if(
					self->animation != self->model->animation[ANI_VAULT] &&	// Don't want to adjust the base if vaulting
					((self->animation->movea[self->animpos] == 0 ||
					(self->base > -1000 && self->base < 0)) &&
					self->type != TYPE_NONE &&
					((!self->owner &&
					self->type != TYPE_SHOT) ||
					((self->owner ||
					self->type == TYPE_SHOT) &&
					(self->animation == self->model->animation[ANI_FALL] ||
					self->toexplode == 1))))
				) 
                	self->base = 0;	// No obstacle/wall or seta, so just set to 0
			}

			// Code for when entities move (useful for moving platforms, etc)

			if(other && other != self && other->nextanim == time){
				f = other->animpos + other->animating;
				if((unsigned)f >= (unsigned)other->animation->numframes){
					if(f<0) f = other->animation->numframes-1;
					else f = 0;
				}

				if(self->a <= other->a + other->animation->platform[other->animpos][5] &&
				self->a + self->model->height >= other->a){

					if(self->type == TYPE_PLAYER) player_trymove(other->animation->move[f] * -1, other->animation->movez[f] * -1);
					else enemy_trymove(other->animation->move[f] * -1, other->animation->movez[f] * -1);
				}
			}
        }
    }


    // Collission detection (for a part of the entities)
    for(i=(time&1); i<MAX_ENTS; i+=2){
        if(ent_list[i]->exists){
            self = ent_list[i];

            // Attack stuff
            f = self->animpos;

            if((self->attacking || self->projectile > 0) && self->animation->attack_force[f] && !self->frozen){	// Can't hit an opponent if you are frozen
                do_attack(self);
            }
            else self->attack_id = 0;
        }
    }


    // Update displayed health
    for(i=0; i<MAX_ENTS; i++){
        if(ent_list[i]->exists){
            self = ent_list[i];

			// this is for restoring mp by time by tails
			if(magic_type == 0){
                if(time >= self->magictime){
                    self->mp += self->model->mprate; // I don't know what you called the variable that stores magic life
                    self->magictime = time + GAME_SPEED;
                }
			}
            if(self->animation == self->model->animation[ANI_CHARGE] &&
               time >= self->mpchargetime){
                   self->mp += self->model->chargerate;
                   self->mpchargetime = time + (GAME_SPEED / 4);
			}               
			if(self->mp > self->maxmp) self->mp = self->maxmp; // Don't want to add more than the max

            if(self->oldhealth < self->health) self->oldhealth++;
            else if(self->oldhealth > self->health) self->oldhealth--;

			if(self->oldmp < self->mp) self->oldmp++;
			else if(self->oldmp > self->mp) self->oldmp--;

        }
    }

}


entity * find_ent_here(entity *exclude, float x, float z, int types);


void display_ents(){
    unsigned f;
    int i, z, wall = 0;
    entity *e = NULL;
    entity *other = NULL;

    
    int use_mirror = (level && level->mirror);

#if DOS // Testing possible bug || SDL
	int x, layer = 0;
	// Loop through, checking to see if there are multiple entities on the same "z".  If so, offsets it 1 per entity
	// so there are not any that will overlap, resulting in "flickering" back and forth when updating

	for(x = PLAYER_MAX_Z; x >= PLAYER_MIN_Z; x--){
		for(i = 0; i < MAX_ENTS; i++){
			if(ent_list[i]->exists && ent_list[i]->z == x && !ent_list[i]->model->setlayer){

				ent_list[i]->layer = layer;
				layer++;
			}
		}

		if(layer > 0) layer--;
	}
#endif

    for(i=0; i<MAX_ENTS; i++){
		if(ent_list[i]->exists){

			e = ent_list[i];

			if(freezeall || !(e->blink && (time%(GAME_SPEED/10))<(GAME_SPEED/20))){	// If special is being executed, display all entities regardless
            	f = e->currentsprite;

           		other = check_platform(e->x, e->z);
           		wall = checkwall(e->x, e->z);

            	if(f<sprites_loaded){

					// var "z" takes into account whether it has a setlayer set, whether there are other entities on
					// the same "z", in which case there is a layer offset, whether the entity is on an obstacle, and
					// whether the entity is grabbing someone and has grabback set

					z = e->z - e->layer;	// Set the layer offset

					if(e->link && e->animation == e->model->animation[ANI_GRAB] && e->model->grabback)
						z = e->link->z - e->link->layer - 1;	// Grab animation displayed behind
					else if(e->link && !e->model->grabback && e->grabbing)
						z = e->link->z - e->link->layer + 1;

					if(other && other != e && e->a >= other->a + other->animation->platform[other->animpos][5]){

						if(
							e->link &&
							((e->model->grabback &&
							e->animation != e->model->animation[ANI_GRAB]) ||
							(e->link->model->grabback &&
							e->link->animation == e->link->model->animation[ANI_GRAB]) ||
							e->grabbing)
						)
							z = other->z + 2;	// Make sure entities get displayed in front of obstacle and grabbee

						else z = other->z + 1;	// Entity should always display in front of the obstacle

					}

					if(e->type == TYPE_SHOT || e->owner) z = e->z + e->layer + 1;	// Always in front

					if(e->model->setlayer) z = HOLE_Z + e->model->setlayer;	// Setlayer takes precedence

					if(checkhole(e->x, e->z)==2) z = PANEL_Z-1;		// place behind panels

					if(e->colourmap){
						spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
						SFX_REMAP, e->colourmap);
					}
					else if(e->screen==1){
						spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
						SFX_BLEND, lut_screen);
					}
					// You can now use six different kinds of transparency!
                    else if(e->screen==2){
                         spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
                         SFX_BLEND, lut_mul);
                    }
                    else if(e->screen==3){
                        spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
                        SFX_BLEND, lut_overlay);
                    }
                    else if(e->screen==4){
						spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
                        SFX_BLEND, lut_hl);
                    }
                    else if(e->screen==5){
						spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
                        SFX_BLEND, lut_dodge);
                    }
                    else if(e->screen==6){
						spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
                        SFX_BLEND, lut_half);
                    }
					else{
						spriteq_add(e->x - advancex, e->z-e->a + gfx_y_offset, z, sprites[e->direction&1][f],
						0, NULL);
					}
				}

				if(use_mirror){

					z = PANEL_Z + 15 - (e->z - PANEL_Z) - e->layer;

					if(e->link && e->animation == e->model->animation[ANI_GRAB] && e->model->grabback){
						z = PANEL_Z + 15 - (e->link->z - PANEL_Z) + 1;	// Grabber should display in front, because it is reflected
					}
					else if(e->link && !e->model->grabback && e->grabbing)
						z = PANEL_Z + 15 - (e->link->z - PANEL_Z) - 1;


					if(other && other != e && e->a >= other->a + other->animation->platform[other->animpos][5]){

						if(
							e->link &&
							((e->model->grabback &&
							e->animation == e->model->animation[ANI_GRAB]) ||
							(e->link->model->grabback &&
							e->link->animation != e->link->model->animation[ANI_GRAB]) ||
							!e->grabbing)
						)
							z = other->z + 2;	// Make sure entities get displayed right

						else z = other->z + 1;	// Entity should always display in front of the obstacle

					}

					if(e->type == TYPE_SHOT || e->owner) z = e->z + e->layer + 1;	// Always in front

					if(e->model->setlayer) z = FRONTPANEL_Z - e->model->setlayer;	// Setlayer takes precedence

					if(e->colourmap){
						spriteq_add(e->x-advancex, (PLAYER_MIN_Z-10)+(PLAYER_MIN_Z - e->z)-e->a+gfx_y_offset, z,
						sprites[e->direction&1][f], SFX_REMAP, e->colourmap);
					}
					else{
						spriteq_add(e->x-advancex, (PLAYER_MIN_Z-10)+(PLAYER_MIN_Z - e->z)-e->a+gfx_y_offset, z,
						sprites[e->direction&1][f], 0, NULL);
					}
				}

				if(e->model->shadow && e->a>=0 && !checkhole(e->x, e->z) && (!e->model->aironly || (e->model->aironly && inair(e)))){

					if(other && other != e && e->a >= other->a + other->animation->platform[other->animpos][5]){
						spriteq_add(
							e->x - advancex,
							e->z - other->a - other->animation->platform[other->animpos][5] + gfx_y_offset,
							other->z + 1,
							sprites[e->direction&1][shadowsprites[e->model->shadow-1]],
							SFX_BLEND,
							lut_mul
						);
					}
					else if(level && wall >= 0 && e->a >= level->walls[wall][7]){
						spriteq_add(
							e->x - advancex,
							e->z - level->walls[wall][7] + gfx_y_offset,
							SHADOW_Z,
							sprites[e->direction&1][shadowsprites[e->model->shadow-1]],
							SFX_BLEND,
							lut_mul
						);
					}
					else{
						spriteq_add(
							e->x - advancex,
							e->z + gfx_y_offset,
							SHADOW_Z,
							sprites[e->direction&1][shadowsprites[e->model->shadow-1]],
							SFX_BLEND,
							lut_mul
						);
					}
				}
			}

			if(e->arrowon){	// Display the players image while invincible to indicate player number

				if(e->model->parrow[e->playerindex][0] && e->invincible)

					spriteq_add(e->x - advancex + e->model->parrow[e->playerindex][1], e->z-e->a+gfx_y_offset +
					e->model->parrow[e->playerindex][2], e->z, sprites[1][e->model->parrow[e->playerindex][0]], 0, NULL);
			}
		}
	}
}



void toss(entity *ent, float lift){
    ent->toss_time = time + 1;
    ent->tossv = lift;
    ent->a += 0.5;		// Get some altitude (needed for checks)
}



entity * findent(int types){
    int i;
    for(i=0; i<MAX_ENTS; i++){

        if(ent_list[i]->exists && (ent_list[i]->type & types) &&
        (ent_list[i]->model->nodieblink != 3 ||
        ((ent_list[i]->model->nodieblink == 3 &&
		(ent_list[i]->health > 0 ||
        (ent_list[i]->health <= 0 && (ent_list[i]->animating || inair(ent_list[i])))))))){
            return ent_list[i];
        }
    }
    return NULL;
}



int count_ents(int types){
    int i;
    int count = 0;
    for(i=0; i<MAX_ENTS; i++){

        if(ent_list[i]->exists && (ent_list[i]->type & types) &&
        (ent_list[i]->model->nodieblink != 3 ||
        ((ent_list[i]->model->nodieblink == 3 &&
		(ent_list[i]->health > 0 ||
        (ent_list[i]->health <= 0 && (ent_list[i]->animating || inair(ent_list[i])))))))){
            ++count;
        }
    }
    return count;
}



entity * find_ent_here(entity *exclude, float x, float z, int types){
    int i;
    for(i=0; i<MAX_ENTS; i++){
        if(		ent_list[i]->exists
                && ent_list[i] != exclude
                && (ent_list[i]->type & types)
                && diff(ent_list[i]->x,x)<(self->model->grabdistance*0.83333)
                && diff(ent_list[i]->z,z)<(self->model->grabdistance/3)
                && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]
          ){
            return ent_list[i];
        }
    }
    return NULL;
}


void set_death(entity *iDie, int type, int reset){
	if(type == ATK_SHOCK && iDie->model->animation[ANI_SHOCKDIE]) ent_set_anim(iDie, ANI_SHOCKDIE, reset);
    else if(type == ATK_BURN && iDie->model->animation[ANI_BURNDIE]) ent_set_anim(iDie, ANI_BURNDIE, reset);
	else if(type == ATK_NORMAL10 && iDie->model->animation[ANI_DIE10]) ent_set_anim(iDie, ANI_DIE10, reset);
	else if(type == ATK_NORMAL9 && iDie->model->animation[ANI_DIE9]) ent_set_anim(iDie, ANI_DIE9, reset);
    else if(type == ATK_NORMAL8 && iDie->model->animation[ANI_DIE8]) ent_set_anim(iDie, ANI_DIE8, reset);
	else if(type == ATK_NORMAL7 && iDie->model->animation[ANI_DIE7]) ent_set_anim(iDie, ANI_DIE7, reset);
	else if(type == ATK_NORMAL6 && iDie->model->animation[ANI_DIE6]) ent_set_anim(iDie, ANI_DIE6, reset);
    else if(type == ATK_NORMAL5 && iDie->model->animation[ANI_DIE5]) ent_set_anim(iDie, ANI_DIE5, reset);
	else if(type == ATK_NORMAL4 && iDie->model->animation[ANI_DIE4]) ent_set_anim(iDie, ANI_DIE4, reset);
	else if(type == ATK_NORMAL3 && iDie->model->animation[ANI_DIE3]) ent_set_anim(iDie, ANI_DIE3, reset);
    else if(type == ATK_NORMAL2 && iDie->model->animation[ANI_DIE2]) ent_set_anim(iDie, ANI_DIE2, reset);
	else if(iDie->model->animation[ANI_DIE]) ent_set_anim(iDie, ANI_DIE, reset);
}



void set_fall(entity *iFall, int type, int reset){
	if(type == ATK_SHOCK && iFall->model->animation[ANI_SHOCK]) ent_set_anim(iFall, ANI_SHOCK, reset);
	else if(type == ATK_BURN && iFall->model->animation[ANI_BURN]) ent_set_anim(iFall, ANI_BURN, reset);
	else if(type == ATK_NORMAL10 && iFall->model->animation[ANI_FALL10]) ent_set_anim(iFall, ANI_FALL10, reset);
	else if(type == ATK_NORMAL9 && iFall->model->animation[ANI_FALL9]) ent_set_anim(iFall, ANI_FALL9, reset);
	else if(type == ATK_NORMAL8 && iFall->model->animation[ANI_FALL8]) ent_set_anim(iFall, ANI_FALL8, reset);
	else if(type == ATK_NORMAL7 && iFall->model->animation[ANI_FALL7]) ent_set_anim(iFall, ANI_FALL7, reset);
	else if(type == ATK_NORMAL6 && iFall->model->animation[ANI_FALL6]) ent_set_anim(iFall, ANI_FALL6, reset);
	else if(type == ATK_NORMAL5 && iFall->model->animation[ANI_FALL5]) ent_set_anim(iFall, ANI_FALL5, reset);
	else if(type == ATK_NORMAL4 && iFall->model->animation[ANI_FALL4]) ent_set_anim(iFall, ANI_FALL4, reset);
	else if(type == ATK_NORMAL3 && iFall->model->animation[ANI_FALL3]) ent_set_anim(iFall, ANI_FALL3, reset);
	else if(type == ATK_NORMAL2 && iFall->model->animation[ANI_FALL2]) ent_set_anim(iFall, ANI_FALL2, reset);
	else if(iFall->model->animation[ANI_FALL]) ent_set_anim(iFall, ANI_FALL, reset);
}



void set_pain(entity *iPain, int type, int reset){
    if(type == ATK_SHOCK && iPain->model->animation[ANI_SHOCKPAIN]) ent_set_anim(iPain, ANI_SHOCKPAIN, reset);
    else if(type == ATK_BURN && iPain->model->animation[ANI_BURNPAIN]) ent_set_anim(iPain, ANI_BURNPAIN, reset);
    else if(type == ATK_NORMAL10 && iPain->model->animation[ANI_PAIN10]) ent_set_anim(iPain, ANI_PAIN10, reset);
    else if(type == ATK_NORMAL9 && iPain->model->animation[ANI_PAIN9]) ent_set_anim(iPain, ANI_PAIN9, reset);
    else if(type == ATK_NORMAL8 && iPain->model->animation[ANI_PAIN8]) ent_set_anim(iPain, ANI_PAIN8, reset); 
    else if(type == ATK_NORMAL7 && iPain->model->animation[ANI_PAIN7]) ent_set_anim(iPain, ANI_PAIN7, reset);
    else if(type == ATK_NORMAL6 && iPain->model->animation[ANI_PAIN6]) ent_set_anim(iPain, ANI_PAIN6, reset);
    else if(type == ATK_NORMAL5 && iPain->model->animation[ANI_PAIN5]) ent_set_anim(iPain, ANI_PAIN5, reset); 
    else if(type == ATK_NORMAL4 && iPain->model->animation[ANI_PAIN4]) ent_set_anim(iPain, ANI_PAIN4, reset);
    else if(type == ATK_NORMAL3 && iPain->model->animation[ANI_PAIN3]) ent_set_anim(iPain, ANI_PAIN3, reset);
    else if(type == ATK_NORMAL2 && iPain->model->animation[ANI_PAIN2]) ent_set_anim(iPain, ANI_PAIN2, reset); 
    else if(iPain->model->animation[ANI_PAIN]) ent_set_anim(iPain, ANI_PAIN, reset);
	else ent_set_anim(iPain, ANI_IDLE, 1);
}



void set_weapon(entity* ent, int wpnum, int pi){
    entity* tempent = NULL;
    s_model* newmodel = NULL;
    s_model* model = NULL;
	char* map = NULL;
	int   colormap = 0;
	int   w = 0;

	if(pi >= 0){
		if(!player[pi].ent) return;
		model = find_model(player[pi].name);
		tempent = player[pi].ent;
		colormap = player[pi].colourmap;
	}
	else{
		if(!ent) return;
		model = ent->model;
		tempent = ent;
		map = ent->colourmap;
	}

	self->attacking = 0;

    if(wpnum > 0 && wpnum <= MAX_WEAPONS) newmodel = find_model(model->weapon[wpnum-1]);
	else{
		if(pi >= 0) newmodel = find_model(player[pi].name);
		else newmodel = find_model(ent->name);
	}
	newmodel->weapnum = wpnum;

    if(!newmodel->health) newmodel->health = tempent->model->health;
    if(!newmodel->mp) newmodel->mp = tempent->model->mp;
    if(!newmodel->speed) newmodel->speed = tempent->model->speed;
    if(!newmodel->runspeed) newmodel->runspeed = tempent->model->runspeed;
    if(!newmodel->runjumpheight) newmodel->runjumpheight = tempent->model->runjumpheight;
    if(!newmodel->runjumpdist) newmodel->runjumpdist = tempent->model->runjumpdist;
    if(!newmodel->runupdown) newmodel->runupdown = tempent->model->runupdown;
	if(!newmodel->runhold) newmodel->runhold = tempent->model->runupdown;
    if(!newmodel->icon) newmodel->icon = tempent->model->icon;
    if(!newmodel->iconpain) newmodel->iconpain = tempent->model->iconpain;
    if(!newmodel->iconget) newmodel->iconget = tempent->model->iconget;
    if(!newmodel->icondie) newmodel->icondie = tempent->model->icondie;
    if(!newmodel->shadow) newmodel->shadow = tempent->model->shadow;
    if(!newmodel->diesound) newmodel->diesound = tempent->model->diesound;

    if(newmodel->knife) strncpy(model->knife, newmodel->knife, MAX_NAME_LEN);
    else strncpy(model->knife, tempent->model->knife, MAX_NAME_LEN);

    if(newmodel->bomb) strncpy(model->bomb, newmodel->bomb, MAX_NAME_LEN);
    else strncpy(model->bomb, tempent->model->bomb, MAX_NAME_LEN);

    if(newmodel->star) strncpy(model->star, newmodel->star, MAX_NAME_LEN);
    else strncpy(model->star, tempent->model->star, MAX_NAME_LEN);

    if(newmodel->flash) strncpy(model->flash, newmodel->flash, MAX_NAME_LEN);
    else strncpy(model->flash, tempent->model->flash, MAX_NAME_LEN);

    for(w=0; w<MAX_ANIS; w++){
		if(!newmodel->animation[w]) newmodel->animation[w] = model->animation[w];
    }

    for(w=0; w<MAX_WEAPONS; w++) strncpy(newmodel->weapon[w], tempent->model->weapon[w], MAX_NAME_LEN);

	if(pi >= 0){
		ent_set_model(player[pi].ent, newmodel->name);
		strncpy(player[pi].ent->name, tempent->name, MAX_NAME_LEN);
		player[pi].ent->type = tempent->type;
		player[pi].ent->health = tempent->health;
		player[pi].ent->maxhealth = tempent->maxhealth;
		player[pi].ent->oldhealth = tempent->oldhealth;
		player[pi].ent->mp = tempent->mp;
		player[pi].ent->maxmp = tempent->maxmp;
		player[pi].colourmap = colormap;
		ent_set_colourmap(player[pi].ent, player[pi].colourmap);
		if(newmodel->weaploss == 3) player[pi].weapnum = newmodel->weapnum;
		else player[pi].weapnum = level->setweap;
	}
	else{	
		ent_set_model(ent, newmodel->name);
		ent->colourmap = map;
	}
	self->facing = newmodel->facing;
}




//////////////////////////////////////////////////////////////////////////
//                  common A.I. code for enemies & NPCs
//////////////////////////////////////////////////////////////////////////
void normal_attack_proc(void);
void common_think(void);

entity* melee_find_target()
{
    return NULL;
}

entity* long_find_target()
{
    return NULL;
}

entity* normal_find_target()
{
    return NULL;
}

int long_attack()
{
    return 0;
}

int melee_attack()
{
    return 0;
}

void normal_prepare()
{
}

void common_jump()
{
}

// Normal attack style
int normal_attack()
{
    entity* target = normal_find_target();
    if(target){
    	if(!self->model->noflip) self->direction = (self->x < target->x);	// Don't flip if set
    
        if(self->animating && (self->animation == self->model->animation[ANI_WALK] ||
    	   self->animation == self->model->animation[ANI_UP] ||
           self->animation == self->model->animation[ANI_DOWN] ||      // Include ANI_UP, ANI_DOWN animations
           self->animation == self->model->animation[ANI_BACKWALK])){
    
               if((self->direction ? self->xdir<0 : self->xdir>0) &&
                  !self->model->animation[ANI_BACKWALK]) self->animating = -1;         // Walking backwards
    		   
    		   else self->animating = 1;
        }
    
    
        // Target in range?
        if(diff(self->z, target->z) < (self->model->grabdistance/3)){
    
            // Target jumping? Try uppercut!
            if(target->jumping && self->model->animation[ANI_UPPER] &&
                    (self->direction ?
                     self->x<target->x-10 && target->x<self->x+120 :
                     self->x>target->x+10 && target->x>self->x-120 )){
    
                // Don't waste any time!
                ent_set_anim(self, ANI_UPPER, 0);
                self->think = normal_attack_proc;
                return 1;
            }
    
            if(self->direction ?
                    (self->model->animation[ANI_ATTACK1] && target->x > self->x+self->model->animation[ANI_ATTACK1]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK1]->range[1]) ||
                    (self->model->animation[ANI_ATTACK2] && target->x > self->x+self->model->animation[ANI_ATTACK2]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK2]->range[1]) ||
                    (self->model->animation[ANI_ATTACK3] && target->x > self->x+self->model->animation[ANI_ATTACK3]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK3]->range[1]) ||
                    (self->model->animation[ANI_ATTACK4] && target->x > self->x+self->model->animation[ANI_ATTACK4]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK4]->range[1]) ||
                    (self->weapent &&
                    (self->model->animation[ANI_THROWATTACK] && target->x > self->x+self->model->animation[ANI_THROWATTACK]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_THROWATTACK]->range[1]))
                    :
                    (self->model->animation[ANI_ATTACK1] && target->x < self->x-self->model->animation[ANI_ATTACK1]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK1]->range[1]) ||
                    (self->model->animation[ANI_ATTACK2] && target->x < self->x-self->model->animation[ANI_ATTACK2]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK2]->range[1]) ||
                    (self->model->animation[ANI_ATTACK3] && target->x < self->x-self->model->animation[ANI_ATTACK3]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK3]->range[1]) ||
                    (self->model->animation[ANI_ATTACK4] && target->x < self->x-self->model->animation[ANI_ATTACK4]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK4]->range[1]) ||
                    (self->weapent &&
    	            (self->model->animation[ANI_THROWATTACK] && target->x < self->x-self->model->animation[ANI_THROWATTACK]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_THROWATTACK]->range[1]))
              ){
    
    			if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
    			else ent_set_anim(self, ANI_IDLE, 0);
                self->zdir = self->xdir = 0;
                self->think = normal_prepare;
    			self->stalltime = time + (GAME_SPEED/4) + (rand32()%(GAME_SPEED/10) - self->aggression);
    
                return 1;
            }
    
            if((self->model->animation[ANI_JUMPATTACK] || self->model->animation[ANI_JUMPATTACK2])){
    
                int rnum = 0;
    
                if(!self->model->animation[ANI_JUMPATTACK]) rnum = 1;
                else if(self->model->animation[ANI_JUMPATTACK2] && (rand32()&1)) rnum = 1;
    
                if(rnum==0 &&
                // do a jumpattack
                (self->direction ?
                    ((self->model->jumpheight == 4 && target->x > self->x+150 && target->x < self->x+200) ||
                    (self->model->jumpheight != 4 && target->x > self->x+(self->model->animation[ANI_JUMPATTACK]->range[0]-10) &&
                    target->x < self->x+(self->model->animation[ANI_JUMPATTACK]->range[1]+5))):  
                    ((self->model->jumpheight == 4 && target->x < self->x-150 && target->x > self->x-200) ||
                    (self->model->jumpheight != 4 && target->x < self->x-(self->model->animation[ANI_JUMPATTACK]->range[0]-10) &&
                    target->x > self->x-(self->model->animation[ANI_JUMPATTACK]->range[1]+5))))
                    ){
        
                    ent_set_anim(self, ANI_JUMPATTACK, 0);
                    if(self->direction) self->xdir = 1.3;
                    else self->xdir = -1.3;
                    
                }
                else if(rnum==1 &&
                // do a jumpattack2
                (self->direction ?
                    (target->x > self->x+(self->model->animation[ANI_JUMPATTACK2]->range[0]-10) &&
                    target->x < self->x+(self->model->animation[ANI_JUMPATTACK2]->range[1]+5)):  
                    (target->x < self->x-(self->model->animation[ANI_JUMPATTACK2]->range[0]-10) &&
                    target->x > self->x-(self->model->animation[ANI_JUMPATTACK2]->range[1]+5)))
                ) {
                    ent_set_anim(self, ANI_JUMPATTACK2, 0);
                    self->xdir = 0;
                } else {
                    rnum = -1;
                }
                
                if(rnum >= 0){
                    self->think = common_jump;
                    self->attacking = 1;
                    self->jumping = 1;
        
                    toss(self, self->model->jumpheight);
        
                    return 1;
                }
            }
    
        }
    }
    return 0;
}

int normal_trymove(float xdir, float zdir)
{

    entity *other = NULL;
    int wall = 0;
    float coef1;
    float coef2;

	if(self->projectile == 0){

        // Walking

        // Out of bounds? Return to level!
        if(!inair(self)){
        	if(self->zdir && self->z < PLAYER_MIN_Z) self->zdir = self->model->speed/2;
        	if(self->zdir && self->z > PLAYER_MAX_Z) self->zdir = -self->model->speed/2;
		}

        if((self->xdir || self->zdir) && !inair(self)){

            // Don't walk into a hole or walk off platforms into holes
           	if(
				checkhole(self->x + xdir*5, self->z) &&
				checkwall(self->x + xdir*5, self->z) == -1 &&
				(!check_platform(self->x + xdir*5, self->z) ||
				((other = check_platform(self->x + xdir*5, self->z)) &&
				self->base < other->a + other->animation->platform[other->animpos][5]))
			)
				xdir = 0;

           	if(
				checkhole(self->x, self->z + zdir*5) &&
				checkwall(self->x, self->z + zdir*5) == -1 &&
				(!check_platform(self->x, self->z + zdir*5) ||
				((other = check_platform(self->x, self->z + zdir*5)) &&
				self->base < other->a + other->animation->platform[other->animpos][5]))
			)
				zdir = 0;


			if(xdir && (other = find_ent_here(self, self->x + xdir*2, self->z, (TYPE_OBSTACLE | TYPE_TRAP)))){
				if(!other->animation->platform[other->animpos][5]){
					if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
				}
			}

			if(zdir && (other = find_ent_here(self, self->x, self->z + zdir*2, (TYPE_OBSTACLE | TYPE_TRAP)))){
				if(!other->animation->platform[other->animpos][5]){
					if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;
				}
			}
		}
	}

	// Check for obstacles with platform code and adjust base accordingly
	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(other = check_platform(self->x + xdir*10, self->z + zdir*10))) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(other = check_platform(self->x + xdir, self->z + zdir)))) &&
		(self->projectile == 0 ||
		!other->animation->vulnerable[other->animpos]) &&
		self->a < other->a + other->animation->platform[other->animpos][5] &&
		self->a + self->model->height >= other->a
	){

		if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
		if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;

	}

	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(wall = checkwall(self->x + xdir*4, self->z + zdir*4)) >= 0) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(wall = checkwall(self->x + xdir, self->z + zdir)) >= 0)) &&
		self->a < level->walls[wall][7]
	){

		coef1 = (level->walls[wall][1] - self->z) * ((level->walls[wall][2] - level->walls[wall][3]) / level->walls[wall][6]);
		coef2 = (level->walls[wall][1] - self->z) * ((level->walls[wall][4] - level->walls[wall][5]) / level->walls[wall][6]);

		if(self->z > level->walls[wall][1] || self->z < level->walls[wall][1] - level->walls[wall][6]){
			zdir = 0;
		}
		else{

			if(self->x < (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 &&
			self->x > level->walls[wall][0] + level->walls[wall][3] + coef1){

				self->x = level->walls[wall][0] + level->walls[wall][3] + coef1;
			}
			else if(self->x > (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 &&
			self->x < level->walls[wall][0] + level->walls[wall][5] + coef2){

				self->x = level->walls[wall][0] + level->walls[wall][5] + coef2;
			}

			xdir = 0;
		}

	}

    self->x += xdir;
    self->z += zdir;

	if(self->z > PLAYER_MAX_Z) self->z = PLAYER_MAX_Z;	// Added so enemies don't move out of range
	if(self->z < PLAYER_MIN_Z) self->z = PLAYER_MIN_Z;	// Added so enemies don't move out of range

    // Do a throw?
    if(		self->health>0 &&
            self->model->animation[ANI_THROW] &&
            !inair(self) &&
            !self->link &&
            self->animation->move[self->animpos] == 0 &&
            self->animation->movez[self->animpos] == 0 &&
            self->animation != self->model->animation[ANI_FALL] &&	// No more grabbing while falling
            self->animation != self->model->animation[ANI_FALL2] && 
            self->animation != self->model->animation[ANI_FALL3] && 
            self->animation != self->model->animation[ANI_FALL4] && 
			self->animation != self->model->animation[ANI_FALL5] && 
            self->animation != self->model->animation[ANI_FALL6] && 
            self->animation != self->model->animation[ANI_FALL7] && 
			self->animation != self->model->animation[ANI_FALL8] && 
            self->animation != self->model->animation[ANI_FALL9] && 
            self->animation != self->model->animation[ANI_FALL10] && 
            self->animation != self->model->animation[ANI_SHOCK] &&	
            self->animation != self->model->animation[ANI_BURN] &&	
            (other = find_ent_here(self, self->x + xdir, self->z + zdir, self->hostile)) &&
            ((other->type) & (TYPE_PLAYER | TYPE_ENEMY | TYPE_NPC)) &&//cant grab obstacles or sth like that
            !inair(other) &&
            self->a == other->a &&
            !other->invincible &&
            other->animation != other->model->animation[ANI_DODGE] &&	// Can't throw a dodging player
            (self->direction ? other->x>self->x : other->x<self->x)
      ){

        // Reposition both on average height and such
        zdir = (self->z + other->z) / 2;
		self->z = other->z = zdir;

		if((wall == -1 || (wall >= 0 && self->a != level->walls[wall][7])) &&
		(checkwall(self->x - self->model->grabdistance/2, self->z) >= 0 ||
		checkwall(other->x - self->model->grabdistance/2, other->z) >= 0)){

			if(self->x < other->x) other->x = self->x + self->model->grabdistance;
			else self->x = other->x + self->model->grabdistance;
		}
		else if((wall == -1 || (wall >= 0 && self->a != level->walls[wall][7])) &&
		(checkwall(self->x + self->model->grabdistance/2, self->z) >= 0 ||
		checkwall(other->x + self->model->grabdistance/2, other->z) >= 0)){

			if(self->x > other->x) other->x = self->x - self->model->grabdistance;
			else self->x = other->x - self->model->grabdistance;
		}
		else{
	        xdir = (self->x + other->x) / 2;

	        if(self->x < other->x){
	            self->x = xdir - (self->model->grabdistance/2);  // 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
	        else{
	            self->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir - (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
		}

		self->direction = (self->x < other->x);

        if(other->type == TYPE_PLAYER) player[other->playerindex].opponent = self;
        other->attacking = 0;

        // use original throw code if throwframewait not present, kbandressen 10/20/06
        if(self->model->throwframewait == -1){
        	self->think = enemy_throw;
        	ent_set_anim(self, ANI_THROW, 0);
        	ent_unlink(self);

			if(other->model->throwheight) toss(other, other->model->throwheight);
        	else toss(other, other->model->jumpheight);

        	other->direction = self->direction;
			other->projectile = 2;
			other->xdir = (other->direction)?(-other->model->throwdist):(other->model->throwdist);

            if(other->type == TYPE_PLAYER)other->think = player_fall;
            else if(other->type == TYPE_NPC)other->think = npc_fall;
            else other->think = enemy_fall; //grabbing enemy?
            other->damage_on_landing = player[other->playerindex].ent->model->throwdamage;	// use adjusted throwdamage
        	ent_set_anim(other, ANI_FALL, 0);
        	ent_unlink(other);
        }
        // otherwise enemy_throw_wait will be used, kbandressen 10/20/06
        else{
            self->think = enemy_throw_wait;
            ent_set_anim(self, ANI_THROW, 0);
            self->attacking = 1;
            ents_link(self, other);
            
            other->think = prethrow;
            if(other->type == TYPE_PLAYER)player[other->playerindex].opponent = self;
            if(other->model->animation[ANI_GRABBED]) ent_set_anim(other, ANI_GRABBED, 0);	// New grabbed animation
            else ent_set_anim(other, ANI_PAIN, 0);
        }

        return 1;
    }


    // Do a grab?
    if(		self->health>0 &&
            self->model->animation[ANI_GRAB] &&
            !inair(self) &&
            !self->link &&
            self->animation->move[self->animpos] == 0 &&
            self->animation->movez[self->animpos] == 0 &&
            self->animation != self->model->animation[ANI_FALL] &&	// No more grabbing while falling
            self->animation != self->model->animation[ANI_FALL2] &&
            self->animation != self->model->animation[ANI_FALL3] &&
            self->animation != self->model->animation[ANI_FALL4] &&
			self->animation != self->model->animation[ANI_FALL5] &&
            self->animation != self->model->animation[ANI_FALL6] &&
            self->animation != self->model->animation[ANI_FALL7] &&
			self->animation != self->model->animation[ANI_FALL8] &&
            self->animation != self->model->animation[ANI_FALL9] &&
            self->animation != self->model->animation[ANI_FALL10] &&
            self->animation != self->model->animation[ANI_SHOCK] &&
            self->animation != self->model->animation[ANI_BURN] &&
            (other = find_ent_here(self, self->x + xdir, self->z + zdir, self->hostile)) &&
            ((other->type) & (TYPE_PLAYER | TYPE_ENEMY | TYPE_NPC)) &&//cant grab obstacles or sth like that
            !inair(other) &&
            self->a == other->a &&
            !other->invincible &&
            other->animation != other->model->animation[ANI_DODGE] &&	// Can't grab a dodging player
            (self->direction ? other->x>self->x : other->x<self->x)
      ){

		// Reposition both on average height and such
		zdir = (self->z + other->z) / 2;
		self->z = other->z = zdir;

		if(
			(wall == -1 ||
			(wall >= 0 &&
			self->a != level->walls[wall][7])) &&
			(checkwall(self->x - self->model->grabdistance/2, self->z) >=0 ||
			checkwall(other->x - self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x < other->x) other->x = self->x + self->model->grabdistance;
			else self->x = other->x + self->model->grabdistance;
		}
		else if(
			(wall == -1 ||
			(wall >= 0 &&
			self->a != level->walls[wall][7])) &&
			(checkwall(self->x + self->model->grabdistance/2, self->z) >= 0 ||
			checkwall(other->x + self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x > other->x) other->x = self->x - self->model->grabdistance;
			else self->x = other->x - self->model->grabdistance;
		}
		else{
	        xdir = (self->x + other->x) / 2;

	        if(self->x < other->x){
	            self->x = xdir - (self->model->grabdistance/2);  // 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
	        else{
	            self->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir - (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
		}

		self->direction = (self->x < other->x);
		other->direction = !self->direction;

        if(other->type == TYPE_PLAYER)player[other->playerindex].opponent = self;
        other->attacking = 0;

		self->think = enemy_grab;
		self->attacking = 1;
		ent_set_anim(self, ANI_GRAB, 0);
		ents_link(self, other);

		if(other->type == TYPE_PLAYER) {
            other->think = player_grabbed;
            player[other->playerindex].opponent = self;
        }
		else if(other->type == TYPE_NPC) other->think = npc_grabbed;
		else other->think = enemy_grabbed;


		if(other->model->animation[ANI_GRABBED]) ent_set_anim(other, ANI_GRABBED, 0);	// New grabbed animation
		else ent_set_anim(other, ANI_PAIN, 0);
	
        return 1;
    }


    // End of level is blocked?
    if(level->exit_blocked){
        if(self->x > level->width-30-(PLAYER_MAX_Z-self->z)) self->x = level->width-30-(PLAYER_MAX_Z-self->z);
    }

    return 0;
}

void normal_runoff()
{
    entity *target = normal_find_target();

    if(!target || self->frozen || freezeall) return;	// There are no players?
	if(!self->model->noflip) self->direction = (self->x < target->x);
    if(self->direction) self->xdir = -self->model->speed/2;
    else self->xdir = self->model->speed/2;
    normal_trymove(self->xdir, 0);

    if(time > self->stalltime) self->think = common_think;
}

void normal_attack_proc()
{
    entity *target = NULL;

	if(self->frozen || freezeall) return;

    self->attacking = 1;

	// If they're attacking, reset their escapecount.
    if(self->model->escapehits) self->escapecount = 0;

	if(inair(self) && !freezeall){	// Now enemies can move with their jumpframes
		if(self->direction) normal_trymove((self->animation->moveflag > 0) * 1.3, 0);	// If moveflag > 0 will move 1.3 else 0
		else normal_trymove((self->animation->moveflag > 0) * -1.3, 0);
		return;
	}

    if(self->animating) return;

    self->attacking = 0;
    self->xdir = 0;
    self->zdir = 0;

    target = normal_find_target();


    if(target && !self->model->nomove && diff(self->x, target->x)<80 && (rand32()&3)){
		ent_set_anim(self, ANI_WALK, 0);
        self->think = normal_runoff;
    }
    else{
		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);

        self->think = common_think;
    }


    self->stalltime = time + GAME_SPEED - self->aggression;
}

entity* common_find_target()
{
    return NULL;
}

int common_attack(void)
{
    int result = 0;
    //long range attack first
    if(self->model->aiattack & AITYPE3_LONG){
                      
    }
    //melee attack first
    else if(self->model->aiattack & AITYPE3_MELEE){
         
    }
    //default
    else {
        result = normal_attack();
    }
    
    return result;
}

int common_move(void)
{
    return 0;
}

void common_think(void)
{
    if(common_attack()) return;
    
    common_move();
}

//////////////////////////////////////////////////////////////////////////

void suicide(void){

	if(freezeall){	// Extend time if a special is being executed
		self->nextthink = time + GAME_SPEED * 3;
		return;
	}

    level_completed |= self->boss;
	kill(self);
}



void prethrow(void){
	
	/*if(self->type == TYPE_PLAYER) */self->running = 0;	// Quits running if grabbed by opponent

    // Just check if we're still grabbed...
    if(self->link) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

	if(self->type == TYPE_PLAYER) self->think = player_think;
	else if(self->type == TYPE_ENEMY){
		self->xdir = self->zdir = 0;
		self->think = enemy_think;
	}else if(self->type == TYPE_NPC){
		self->xdir = self->zdir = 0;
		self->think = npc_think;
    }		
}



// Re-enter playfield
// Used by player_fall and player_takedamage
void player_die(){
	int wall, i;
	entity *other = NULL;
	entity* tempent = NULL;

 // kill minions
    if(self->model->summonkill == 1 && self->subentity) { // kill only summoned one
        self->subentity->parent = NULL;
        tempent = self;
        self = self->subentity;
        if(self->takedamage) self->takedamage(self, 10000, 1, ATK_NORMAL);
        else kill(self);
        self = tempent;
    } else if(self->model->summonkill == 2){// kill all 
		for(i=0; i<MAX_ENTS; i++){
			if(ent_list[i]->exists && ent_list[i]->parent == self){
                tempent = self;
                self = ent_list[i];
                self->parent = NULL;
                if(self->takedamage) self->takedamage(self, 10000, 1, ATK_NORMAL);
                else kill(self);
                self = tempent;
			}
		}
    }
	for(i=0; i<MAX_ENTS; i++){
		if(ent_list[i]->exists && ent_list[i]->parent == self){
            ent_list[i]->parent = NULL;
		}
	}
    self->subentity = NULL;
    self->lifespancountdown = self->model->lifespan*GAME_SPEED;
    
	if(!livescheat) --player[self->playerindex].lives;

	// Drop Weapon due to Players Death.
	if(self->model->weaploss <= 2) dropweapon(1);
    
	if(player[self->playerindex].weapnum) set_weapon(NULL, player[self->playerindex].weapnum, self->playerindex);
	else set_weapon(NULL, level->setweap, self->playerindex);
    
	player[self->playerindex].iconmode = 0;   // 20-1-2005 New icons here

    self->weapent = NULL;

    if(player[self->playerindex].lives <= 0){
        kill(self);
        player[self->playerindex].iconmode = 0;   // 20-1-2005 New icons here
        player[self->playerindex].ent = NULL;

        if(!player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent){  //4player
            timeleft = 10 * COUNTER_SPEED;

            if((!noshare && credits < 1) || (noshare && player[0].credits < 1 && player[1].credits < 1 && player[2].credits <1 && player[3].credits <1)) timeleft = COUNTER_SPEED/2; //4player
        }
        return;
    }

    ent_unlink(self);
    self->blink = 0;

	if(level->scrolldir == SCROLL_LEFT){
		if(level->spawn[self->playerindex][0]) self->x = advancex + hRes1 - level->spawn[self->playerindex][0];
		else self->x = advancex + (hRes1-30);

		self->direction = 0;
	}
	else{
		if(level->spawn[self->playerindex][0]) self->x = advancex + level->spawn[self->playerindex][0];
		else self->x = advancex + 20;

		self->direction = 1;
	}

	if(level->spawn[self->playerindex][1]){
		if(!checkhole(self->x, PLAYER_MIN_Z + level->spawn[self->playerindex][1])){
			self->z = PLAYER_MIN_Z + level->spawn[self->playerindex][1];
		}
		else if(!checkhole(self->x, (level->holes[holez][1] - level->holes[holez][6] + PLAYER_MIN_Z) / 2)){
			self->z = (level->holes[holez][1] - level->holes[holez][6] + PLAYER_MIN_Z) / 2;
		}
		else self->z = (level->holes[holez][1] + PLAYER_MAX_Z) / 2;
	}
	else if(PLAYER_MIN_Z - PLAYER_MAX_Z > 5) self->z = PLAYER_MIN_Z + 5;
	else self->z = PLAYER_MIN_Z;

	if(checkhole(self->x, self->z)){
		self->z = (level->holes[holez][1] * 2 - level->holes[holez][6]) / 2;

		if(level->scrolldir == SCROLL_LEFT){
			self->x = (level->holes[holez][0] * 2 + level->holes[holez][2] + level->holes[holez][3]) / 2 - 5;
		}
		else self->x = self->x = (level->holes[holez][0] * 2 + level->holes[holez][4] + level->holes[holez][5]) / 2 + 5;
	}

	wall = checkwall(self->x, self->z);
	other = check_platform(self->x, self->z);

	if(other){
		if(level->spawn[self->playerindex][2] > other->a + other->animation->platform[other->animpos][5]){
			self->a = level->spawn[self->playerindex][2];
		}
		else self->a = other->a + other->animation->platform[other->animpos][5];

		self->base = other->a + other->animation->platform[other->animpos][5];
	}
	else if(wall){
		if(level->spawn[self->playerindex][2] > level->walls[wall][7]) self->a = level->spawn[self->playerindex][2];
		else self->a = level->walls[wall][7];

		self->base = level->walls[wall][7];
	}

	self->tossv = 0;	// Added so players don't spawn at different rates
	self->xdir = 0;	// Added so players don't spawn moving
	self->running = 0;
	self->attacking = 0;
	self->projectile = 0;
	self->blasted = 0;
	self->frozen = 0;	// Added so players don't spawn frozen

	if(self->model->animation[ANI_RESPAWN]) {
        ent_set_anim(self, ANI_RESPAWN, 0); // now, respawn animation works
        self->think = player_spawn;
    }
	else if(self->model->animation[ANI_SPAWN]) {
        ent_set_anim(self, ANI_SPAWN, 0); 
        self->think = player_spawn;
    }
	else { 
    	self->jumping = 1;
        ent_set_anim(self, ANI_JUMP, 0);
	    self->think = player_jump;
    }
    
	self->health = self->maxhealth;
	self->mp = self->maxmp;// mp bar spawn when die by tails(i put spawn at max)

	if(!nodropen){
#if DC
		dcpad_rumblepower[self->playerindex]=125;
#endif
		drop_all_enemies();   //27-12-2004  If drop enemies is on, drop all enemies
	}

	if(self->model->makeinv > 0){	// If spawn invincible, sets invincible for specified time
		self->invincible = 1;
		self->blink = 1;
		self->invinctime = time + self->model->makeinv;
		self->arrowon = 1;
	}

	if(!level->noreset) timeleft = level->settime * COUNTER_SPEED;	// Feb 24, 2005 - This line moved here to set custom time

}



int player_trymove(float xdir, float zdir){
    entity *other = NULL;
    entity *endlevel = NULL;
    int wall = 0;
    float coef1;
    float coef2;
    int hostile = self->hostile;
    if(level->nohit || savedata.mode){
        hostile &= ~TYPE_PLAYER;
     }

	if((other = find_ent_here(self, self->x, self->z, TYPE_ENDLEVEL)) && self->a -
	(self->animation->seta[self->animpos] >= 0) * self->animation->seta[self->animpos] == other->a){

		if(!reached[0] && !reached[1]) addscore(self->playerindex, other->score);

		reached[self->playerindex] = 1;

		if (!other->model->subtype ||(other->model->subtype == SUBTYPE_BOTH &&( 
 			(reached[0]+reached[1]+reached[2]+reached[3]) == (count_ents(TYPE_PLAYER))))||
			(!player[0].ent || !player[1].ent || !player[2].ent || !player[3].ent)){
			level_completed = 1;  //4player
			strncpy( branch_name, other->model->branch, MAX_NAME_LEN); //now, you can branch to another level
        }
	}

	if(xdir && (other = find_ent_here(self, self->x + xdir*2, self->z, TYPE_OBSTACLE))){
		if(!other->animation->platform[other->animpos][5]){
			if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
		}
	}
	if(zdir && (other=find_ent_here(self, self->x, self->z + zdir*2, TYPE_OBSTACLE))){
		if(!other->animation->platform[other->animpos][5]){
			if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;
		}
	}

	// Check for obstacles with platform code and adjust base accordingly
	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(other = check_platform(self->x + xdir*10, self->z + zdir*10))) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(other = check_platform(self->x + xdir, self->z + zdir)))) &&
		self->a < other->a + other->animation->platform[other->animpos][5] &&
		self->a + self->model->height >= other->a
	){

		if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
		if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;

	}

	// Check for walls and adjust base accordingly

	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(wall = checkwall(self->x + xdir*4, self->z + zdir*4)) >= 0) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(wall = checkwall(self->x + xdir, self->z + zdir)) >= 0)) &&
		self->a < level->walls[wall][7]
	){

		coef1 = (level->walls[wall][1] - self->z) * ((level->walls[wall][2] - level->walls[wall][3]) / level->walls[wall][6]);
		coef2 = (level->walls[wall][1] - self->z) * ((level->walls[wall][4] - level->walls[wall][5]) / level->walls[wall][6]);

		if(self->z > level->walls[wall][1] || self->z < level->walls[wall][1] - level->walls[wall][6]){
			zdir = 0;
		}
		else{

			if(self->x < (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 && self->x > level->walls[wall][0] + level->walls[wall][3] + coef1){
				self->x = level->walls[wall][0] + level->walls[wall][3] + coef1;
			}
			else if(self->x > (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 && self->x < level->walls[wall][0] + level->walls[wall][5] + coef2){
				self->x = level->walls[wall][0] + level->walls[wall][5] + coef2;
			}

			xdir = 0;
		}
	}

    // Can't grab anyone while (they're) in mid-air...
    // And we can't grab anyone behind us???
    //  && (self->direction ? other->x>self->x : other->x<self->x)

    if(
		self->model->animation[ANI_GRAB] &&
		self->health > 0 &&
		!(inair(self) ||
		self->jumping ||
		self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0 ||
		self->animation == self->model->animation[ANI_DODGE]) &&
    	(other = find_ent_here(self, self->x + xdir, self->z + zdir, hostile)) &&
        ((other->type) & (TYPE_PLAYER | TYPE_ENEMY | TYPE_NPC)) &&//cant grab obstacles or sth like that
    	!(other->link ||
    	inair(other) ||
    	other->nograb ||
    	other->model->animal ||
    	other->invincible) &&
    	other->a == self->a &&
    	(!find_ent_here(self, other->x, other->z, TYPE_ENDLEVEL) ||
    	((endlevel = find_ent_here(self, other->x, other->z, TYPE_ENDLEVEL)) &&
    	other->a - (other->animation->seta[other->animpos] >= 0) * other->animation->seta[other->animpos] != endlevel->a)) && 
		((other->model->paingrab && other->think == enemy_pain) || !other->model->paingrab)
    ){

        // Reposition both on average height and such
        zdir = (self->z + other->z) / 2;
		self->z = other->z = zdir;

		if(
			(wall == -1 ||
			(wall >= 0 &&
			self->a != level->walls[wall][7])) &&
			(checkwall(self->x - self->model->grabdistance/2, self->z) >=0 ||
			checkwall(other->x - self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x < other->x) other->x = self->x + self->model->grabdistance;
			else self->x = other->x + self->model->grabdistance;
		}
		else if(
			(wall == -1 ||
			(wall >= 0 && self->a != level->walls[wall][7])) &&
			(checkwall(self->x + self->model->grabdistance/2, self->z) >= 0 ||
			checkwall(other->x + self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x > other->x) other->x = self->x - self->model->grabdistance;
			else self->x = other->x - self->model->grabdistance;
		}
		else{
	        xdir = (self->x + other->x) / 2;

	        if(self->x < other->x){
	            self->x = xdir - (self->model->grabdistance/2);  // 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
	        else{
	            self->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir - (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
		}

		self->direction = (self->x < other->x);

        ents_link(self, other);
        player[self->playerindex].opponent = other;
        other->attacking = 0;
        other->direction = !self->direction;

        self->combostep = self->combostep2 = self->combostep3 = self->combostep4 = 0;	// Reset combosteps for grabbing purposes
        self->think = player_grab;

        if(other->type==TYPE_ENEMY){
            other->think = enemy_grabbed;
            other->playerindex = self->playerindex;
            other->stalltime = time + GRAB_STALL;
        }
        else if(other->type==TYPE_NPC){
            other->think = npc_grabbed;
            other->playerindex = self->playerindex;
            other->stalltime = time + GRAB_STALL;
        }
        if(other->type==TYPE_PLAYER){
            other->think = player_grabbed;
            player[other->playerindex].opponent = self;
        }

		if(other->model->animation[ANI_GRABBED]) ent_set_anim(other, ANI_GRABBED, 0);	// New grabbed animation
		else ent_set_anim(other, ANI_PAIN, 0);
        ent_set_anim(self, ANI_GRAB, 0);

        return 0;
    }

    self->x += xdir;
    self->z += zdir;


    if(self->z < PLAYER_MIN_Z) self->z = PLAYER_MIN_Z;
    if(self->z > PLAYER_MAX_Z) self->z = PLAYER_MAX_Z;
    if(self->x < advancex+10) self->x = advancex+10;
    if(self->x > advancex+(hRes1-10)) self->x = advancex+(hRes1-10);


    // End of level is blocked?
    if(level->exit_blocked){
        if(self->x > level->width-30-(PLAYER_MAX_Z-self->z)){
			self->x = level->width-30-(PLAYER_MAX_Z-self->z);
		}
    }

    return 1;
}
/*
int ent_check_energy(entity* ent, int ani){
	if(
         ent && ent->model->animation[ani] &&
         ((ent->model->animation[ani]->mponly == 0 &&
           ent->mp + ent->health > ent->model->animation[ani]->energycost)||
          (ent->model->animation[ani]->mponly == 1 &&
           ent->mp > ent->model->animation[ani]->energycost) ||
          (ent->model->animation[ani]->mponly == 2 
	)
		return 1;

	return 0;
}*/

int player_check_energy(int which, int ani){
	if(
		self->model->animation[ani] &&
		((which &&	// Checks magic validity
		(self->model->animation[ani]->mponly != 2) &&	// 2 = From health bar only, 0 from both
		(self->mp > self->model->animation[ani]->energycost)) ||
		(!which &&	// Checks health validity
		(self->model->animation[ani]->mponly != 1) &&	// 1 = From magic bar only, 0 from both
		(self->health > self->model->animation[ani]->energycost)))
	)
		return 1;

	return 0;
}


// Check keys for special move. Used several times, so I func'd it.
// 1-10-05 changed self->health>6 to self->health > self->model->animation[ANI_SPECIAL]->energycost

int player_check_special(){
    if(!level->nospecial && (player_check_energy(0, ANI_SPECIAL) || player_check_energy(1, ANI_SPECIAL))){

    	if((!ajspecial || (ajspecial && !self->model->animation[ANI_BLOCK])) &&
    	(player[self->playerindex].playkeys & FLAG_SPECIAL)){
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
		}
    	else if(ajspecial && ((player[self->playerindex].playkeys & FLAG_JUMP) &&
    	(player[self->playerindex].keys & FLAG_ATTACK))){	// Now a j can be used for specials as well
			player[self->playerindex].playkeys -= FLAG_JUMP;
			self->stalltime = 0;
		}
		else return 0;

		self->attacking = 1;
		self->combostep = 0;
		ent_unlink(self);

		if(self->grabbing) self->grabbing = NULL;

		player[self->playerindex].iconmode = 0;	// Added incase smartbomb overrides pain/grabbed animation

		if(self->model->specpower && !self->model->dofreeze)
			smart_bomb(self, self->model->specpower, self->model->spectype, self->model->speclen);	// If specpower is used, will execute a smartbomb

		self->running = 0;	// If special is executed while running, ceases to run
		ent_set_anim(self, ANI_SPECIAL, 0);

		if(self->model->dofreeze) freezeall = 1;	// Freezes the animations of all enemies/players while special is executed

		if(!nocost && !healthcheat){

			if(player_check_energy(1, ANI_SPECIAL)) self->mp -= self->model->animation[ANI_SPECIAL]->energycost;
			else self->health -= self->model->animation[ANI_SPECIAL]->energycost;
		}

		return 1;
    }
    return 0;
}

// throw animation with throwframewait, added by kbandressen 10/20/06
void player_throw_wait(void){

	entity *other = NULL;

    // returns player to idle animation if link is broken before other is actually thrown
    if(!(self->link)){
        if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	    else ent_set_anim(self, ANI_IDLE, 0);
	    return;
     }
    other = self->link;
    if(self->animpos != self->model->throwframewait) return;
    if(other->model->throwheight) toss(other, other->model->throwheight);
        else toss(other, self->model->jumpheight);

        other->direction = self->direction;
        other->projectile = 2;
	    other->xdir = (other->direction)?(-other->model->throwdist):(other->model->throwdist);

        if(other->type == TYPE_ENEMY){
            other->think = enemy_fall;
            other->damage_on_landing = player[other->playerindex].ent->model->throwdamage;
        }
        else if(other->type == TYPE_NPC){
            other->think = npc_fall;
            other->damage_on_landing = player[other->playerindex].ent->model->throwdamage;
        }
        else if(other->type == TYPE_PLAYER){
            other->think = player_fall;

			if(autoland == 1) other->damage_on_landing = -1;	// Sets damage to -1 so it can tell player was thrown by a player
	        else other->damage_on_landing = (player[other->playerindex].ent->model->throwdamage/2);
        }

        ent_set_anim(other, ANI_FALL, 0);
        ent_unlink(self);
    self->think = player_throw;
}


void player_throw(void){
    if(self->animating) return;
    self->think = player_think;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
}



void player_pain(void){
    if(player_check_special()) {
        self->think = player_think;
        return;
    }

    if(self->animating) {
        player[self->playerindex].iconmode = 1;   // 20-1-2005 New icons here
        return;
    }
    player[self->playerindex].iconmode = 0;    // 20-1-2005 New icons here

    if(self->link){
        self->think = player_grabbed;

        if(self->model->animation[ANI_GRABBED]) ent_set_anim(self, ANI_GRABBED, 0);	// New grabbed animation
        else ent_set_anim(self, ANI_PAIN, 0);
    }
    else{
        self->think = player_think;

		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else if(self->model->blockpain) ent_set_anim(self, ANI_BLOCK, 1);
		else ent_set_anim(self, ANI_IDLE, 0);
    }
}



void player_land(void){
    if(self->animating) return;
    self->think = player_think;

    if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
}



//animal run when you lost it 3 times by tails
void runanimal(void){
	ent_set_anim(self, ANI_WALK, 0);

    if(self->x < advancex - 80 || self->x > advancex + (hRes1+80)){
        kill(self);
        return;
    }

	if(self->direction) self->x += self->model->speed;
	else self->x -= self->model->speed;
}



void player_fall(void){
	entity *dust = NULL;	// New "dust" entity for when characters hit the ground

	if(freezeall) return;	// Added incase player is in midair
    player[self->playerindex].iconmode=1;  // 20-1-2005 New icons here

	self->stalltime = 0;	// Disable the attack3 if falling

	if(self->frozen){	// If player is falling, will become unfrozen

		if(self->colourmap == self->model->colourmap[self->model->fmap - 1]) self->colourmap = self->map;

		self->frozen = 0;
		self->freezetime = 0;
	}

    // Still falling?
    if(inair(self)){
/*
        if(self->projectile == 1){

            if(self->direction) player_trymove(-2.5, 0);
            else player_trymove(2.5, 0);
        }
        else if(self->projectile == 2){

			if(self->direction) player_trymove(-self->model->throwdist, 0);
			else player_trymove(self->model->throwdist, 0);
		}
        else{

            if(self->direction) player_trymove(-1, 0);
            else player_trymove(1, 0);
        }*/
        player_trymove(self->xdir, 0);
        return;
    }

    if(self->projectile > 0){
        self->projectile = 0;

        if(!self->blasted){

            if(
				(autoland == 1 &&
				self->damage_on_landing == -1) ||
            	(autoland != 2 &&
            	(player[self->playerindex].keys & (FLAG_MOVEUP|FLAG_JUMP)) == (FLAG_MOVEUP|FLAG_JUMP))
            ){	// Added autoland option for landing

                player[self->playerindex].playkeys ^= (FLAG_MOVEUP|FLAG_JUMP);

                if(self->model->animation[ANI_LAND]){
                    ent_set_anim(self, ANI_LAND, 0);
                    player[self->playerindex].iconmode=0;	// Safely landed, set to regular icon
                    self->think = player_land;
                    self->direction = !self->direction;
                }
                else{
					if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
					else ent_set_anim(self, ANI_IDLE, 0);
                }
                return;
            }
        }

        if(!level->nohurt && !healthcheat) self->health -= self->damage_on_landing;	// Added so can't die in bonus levels or with healthcheat enabled

        self->damage_on_landing = 0;

        if(!self->model->noquake) quake = 4;	// Only shake if noquake not set
    }

    if(self->animation == self->model->animation[ANI_SHOCK] || self->animation == self->model->animation[ANI_BURN]){ // New animations
		ent_set_anim(self, ANI_FALL, 0);	// Set to normal fall animation and change to last frame
		self->currentsprite = self->animation->sprite[self->animation->numframes - 1];
		self->animpos = self->animation->numframes - 1;
	}

    // Hard landing? Quake and bounce!
    if(self->tossv < -2){
        if(!self->model->noquake) quake = 2;	// Only shake if noquake not set

		if(self->model->dust[0]){
			dust = spawn(self->x, self->z, self->a, self->model->dust);
			dust->autokill = 1;
		}

        if(smp_fall >= 0) sound_play_sample(smp_fall, 0, savedata.effectvol,savedata.effectvol, 100);

        toss(self, 1);
#if DC
		dcpad_rumblepower[self->playerindex] = 100;
#endif
        return;
    }

    if(self->health <= 0){
        player[self->playerindex].iconmode=2;   //20-1-2005   New icons
        if(self->model->diesound>=0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100); // 29-12-2004 Diesound fixed for player
		if(!self->model->nodieblink || (self->model->nodieblink == 1 && !self->animating)){	// Now have the option to blink or not
			self->think = player_blink;
			self->blink = 1;
			self->stalltime = time + GAME_SPEED * 2;
		}
		else if(self->model->nodieblink == 2 && !self->animating) player_die();
        return;
    }

	// Drop Weapon due to being knocked down.
	if(self->model->weaploss == 1) dropweapon(1);


	self->think = player_rise;
	ent_set_anim(self, ANI_RISE, 0);

}



void player_blink(void){

	if(freezeall){
		self->stalltime = time + GAME_SPEED;
		return;
	}
    if(time >= self->stalltime) player_die();
}



void player_rise(void){
    if(self->animating) return;

    self->pain_time = time + (GAME_SPEED/5);
    self->think = player_think;
    player[self->playerindex].iconmode=0;  // 20-1-2005 New icons here

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->think = player_think;
}



void player_grabbed(void){
	self->running = 0;	// Quits running if grabbed by opponent

    if(player_check_special()) {
    	self->think = player_think;
	    return;
	}

    // Just check if we're still grabbed...
    if(self->link) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->think = player_think;
}



void player_grab(void){
    entity * other = self->link;

	self->running = 0;	// Quits running if grabbing an opponent

    if(player_check_special()) return;

    if(other == NULL){
        self->think = player_think;
        return;
    }

    if(self->base != other->base){       // Change this from ->a to ->base 
      ent_unlink(self); 
      self->think = player_think; 
      return; 
	}

	if(!nolost && self->model->weaploss <= 0) dropweapon(1);

    if(self->direction ?
            (player[self->playerindex].keys & FLAG_MOVELEFT) :
            (player[self->playerindex].keys & FLAG_MOVERIGHT)){

        if(time > self->releasetime){
            // Release
            ent_unlink(self);
            self->think = player_think;
        }
 	}
    else self->releasetime = time + (GAME_SPEED/2);


    if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
            (self->direction ?
             (player[self->playerindex].keys & FLAG_MOVELEFT) :
             (player[self->playerindex].keys & FLAG_MOVERIGHT))){

        player[self->playerindex].playkeys -= FLAG_ATTACK;

		if(self->model->throwframewait >= 0){
			ent_set_anim(self, ANI_THROW, 0);
			other->think = prethrow;
        	self->think = player_throw_wait;
        }
        else{
        	if(other->model->throwheight) toss(other, other->model->throwheight);
        	else toss(other, self->model->jumpheight);

        	other->direction = self->direction;
	        other->projectile = 2;
	        other->xdir = (other->direction)?(-other->model->throwdist):(other->model->throwdist);

            if(other->type == TYPE_NPC){
                other->think = npc_fall;
            	other->damage_on_landing = player[other->playerindex].ent->model->throwdamage;
            }
    	    if(other->type == TYPE_ENEMY){
            	other->think = enemy_fall;
            	other->damage_on_landing = player[other->playerindex].ent->model->throwdamage;
        	}
	        if(other->type == TYPE_PLAYER){
    	        other->think = player_fall;
				if(autoland == 1) other->damage_on_landing = -1;	// Sets damage to -1 so it can tell player was thrown by a player
		        else other->damage_on_landing = (player[other->playerindex].ent->model->throwdamage/2);
    	    }
			ent_set_anim(other, ANI_FALL, 0);
    	    ent_unlink(self);
        	ent_set_anim(self, ANI_THROW, 0);
        	self->think = player_throw;
    	}
    }

    // New forward attack grab animation

	else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
			self->model->animation[ANI_GRABFORWARD] &&
			(!self->direction ?
			(player[self->playerindex].keys & FLAG_MOVELEFT) :
			(player[self->playerindex].keys & FLAG_MOVERIGHT))){

		player[self->playerindex].playkeys -= FLAG_ATTACK;
		self->attacking = 1;

		++self->combostep2;

        if(self->combostep2 < 3){

            ent_set_anim(self, ANI_GRABFORWARD, 0);
            self->think = player_grabattack;
        }
        else{

            if(self->model->animation[ANI_GRABFORWARD2]) ent_set_anim(self, ANI_GRABFORWARD2, 0);
            else ent_set_anim(self, ANI_ATTACK3, 0);

            self->think = player_think;
            ent_unlink(self);
            self->combostep = 0;
        }

	}
	else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
			self->model->animation[ANI_GRABUP] && (player[self->playerindex].keys & FLAG_MOVEUP)){

		player[self->playerindex].playkeys -= FLAG_ATTACK;
		self->attacking = 1;

		++self->combostep3;

        if(self->combostep3 < 3){

            ent_set_anim(self, ANI_GRABUP, 0);
            self->think = player_grabattack;
        }
        else{

            if(self->model->animation[ANI_GRABUP2]) ent_set_anim(self, ANI_GRABUP2, 0);
            else ent_set_anim(self, ANI_ATTACK3, 0);

            self->think = player_think;
            ent_unlink(self);
            self->combostep = 0;
        }

	}
	else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
			self->model->animation[ANI_GRABDOWN] && (player[self->playerindex].keys & FLAG_MOVEDOWN)){

		player[self->playerindex].playkeys -= FLAG_ATTACK;
		self->attacking = 1;

		++self->combostep4;

        if(self->combostep4 < 3){

            ent_set_anim(self, ANI_GRABDOWN, 0);
            self->think = player_grabattack;
        }
        else{

            if(self->model->animation[ANI_GRABDOWN2]) ent_set_anim(self, ANI_GRABDOWN2, 0);
            else ent_set_anim(self, ANI_ATTACK3, 0);

            self->think = player_think;
            ent_unlink(self);
            self->combostep = 0;
        }

	}
    else if((player[self->playerindex].playkeys & FLAG_ATTACK) &&
            self->model->animation[ANI_GRABATTACK]){

        player[self->playerindex].playkeys -= FLAG_ATTACK;
        self->attacking = 1;

        ++self->combostep;

        if(self->combostep < 3){

            ent_set_anim(self, ANI_GRABATTACK, 0);
            self->think = player_grabattack;
        }
        else{

            if(self->model->animation[ANI_GRABATTACK2]) ent_set_anim(self, ANI_GRABATTACK2, 0);
            else ent_set_anim(self, ANI_ATTACK3, 0);

            self->think = player_think;
            ent_unlink(self);
            self->combostep = 0;
        }

    }
    // Vaulting.
	else if((player[self->playerindex].playkeys & FLAG_JUMP) &&
			(self->model->animation[ANI_VAULT])){

		player[self->playerindex].playkeys -= FLAG_JUMP;

		self->attacking = 1;
		self->x = other->x;
		self->base = other->base + other->model->height;
		ent_set_anim(self, ANI_VAULT, 0);
		self->think = player_vault;
    }
    else if(player[self->playerindex].playkeys & (FLAG_JUMP|FLAG_ATTACK)){
        player[self->playerindex].playkeys -= player[self->playerindex].playkeys&(FLAG_JUMP|FLAG_ATTACK);
        self->attacking = 1;
        self->think = player_think;
        ent_unlink(self);
        self->combostep = 0;

		// Perform final blow
		if(self->model->animation[ANI_GRABATTACK2]) ent_set_anim(self, ANI_GRABATTACK2, 0);
		else if(self->model->animation[ANI_ATTACK3]) ent_set_anim(self, ANI_ATTACK3, 0);
		else{
			dojump(1);
			return;
		}
    }
}



void player_grabattack(void){

	if(self->animating) return;

    self->attacking = 0;

    if(self->link){
        ent_set_anim(self, ANI_GRAB, 0);
        self->think = player_grab;
    }
    else{
        if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);

        self->think = player_think;
    }
}

// The vault.
void player_vault(void){
    if(freezeall) return;

    if(!self->animating){
        self->attacking = 0;
        self->direction = !self->direction;
        self->a = self->base = self->link->base;

        if(self->direction) self->x = self->link->x - self->model->grabdistance;
        else self->x = self->link->x + self->model->grabdistance;

        ent_set_anim(self, ANI_GRAB, 0);
        self->think = player_grab;
        return;
    }
}



// Fugues Dive Code
/*
void player_jump(){
    int jumpattacktype = -1;   // Which jump attack will we use?

    if(freezeall) return;

    if(self->animation == self->model->animation[ANI_JUMPSPECIAL]){
        if(self->animating) self->tossv = 0; // Players hover during a jumpspecial
        else ent_set_anim(self, ANI_JUMP, 0);   // Stop the animation if it's over
    }

    player_trymove(self->xdir, 0);

        // Midair special?
        if(player[self->playerindex].playkeys & FLAG_SPECIAL){
            
          player[self->playerindex].playkeys -= FLAG_SPECIAL;
          if(self->model->animation[ANI_JUMPSPECIAL] &&
            (!noaircancel ||
             self->animation == self->model->animation[ANI_JUMP] ||
            (noaircancel == 1 && !self->animating))){

            jumpattacktype = ANI_JUMPSPECIAL;
          }
        }

        // Normal midair attack?
        else if(player[self->playerindex].playkeys & FLAG_ATTACK){
            
          player[self->playerindex].playkeys -= FLAG_ATTACK;
          
          if(self->animation != self->model->animation[ANI_JUMPCANT] &&
             (!noaircancel ||
              self->animation == self->model->animation[ANI_JUMP] ||
             (noaircancel == 1 && !self->animating))){      // Can you cancel midair attacks?
                 
            self->attacking = 1;

            if((player[self->playerindex].playkeys & FLAG_MOVEDOWN) && self->model->animation[ANI_JUMPATTACK2])
              jumpattacktype = ANI_JUMPATTACK2;
            else if((player[self->playerindex].playkeys & FLAG_MOVEUP) && self->model->animation[ANI_JUMPATTACK3])
              jumpattacktype = ANI_JUMPATTACK3;
            else if(self->model->animation[ANI_RUNJUMPATTACK] && self->running)
              jumpattacktype = ANI_RUNJUMPATTACK;      // Added so an extra strong jump attack can be executed
            else if(self->model->animation[ANI_JUMPFORWARD] && self->xdir != 0)
              jumpattacktype = ANI_JUMPFORWARD;       // If moving and set, do this attack
  
            else if(self->model->animation[ANI_JUMPATTACK])
              jumpattacktype = ANI_JUMPATTACK;
          }
 
        }

       if(jumpattacktype != -1){        // MP cost checks
          if(player_check_energy(1, jumpattacktype)){
             if(!healthcheat) self->mp -= self->model->animation[jumpattacktype]->energycost;
             ent_set_anim(self, jumpattacktype, 0);
             if(jumpattacktype == ANI_JUMPSPECIAL){
                self->attacking = 1;
                self->xdir = 0;                         // Kill movement when the special starts
                self->tossv = 0;
             }
          }
          else if(player_check_energy(0, jumpattacktype)){
             if(!healthcheat) self->health -= self->model->animation[jumpattacktype]->energycost;
             ent_set_anim(self, jumpattacktype, 0);
             if(jumpattacktype == ANI_JUMPSPECIAL){
                self->attacking = 1;
                self->xdir = 0;                         // Kill movement when the special starts
                self->tossv = 0;
             }
          }
          else if(self->model->animation[ANI_JUMPCANT]){
             ent_set_anim(self, ANI_JUMPCANT, 0);
             self->tossv = 0;
             }
             
       }

                if(inair(self)){

                        if(self->direction) self->xdir += self->animation->dive[0];
                        else self->xdir -= self->animation->dive[0];

                        self->tossv -= self->animation->dive[1];
                }

    if(!inair(self)){
        self->tossv = 0;
        self->a = self->base;

        if(!self->model->runhold || !(player[self->playerindex].keys & FLAG_MOVELEFT || player[self->playerindex].keys & FLAG_MOVERIGHT)) self->running = 0;    // So players can continue to run if holding down forward when landing
        self->jumping = 0;
        self->attacking = 0;

                if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
                else ent_set_anim(self, ANI_IDLE, 0);

        self->think = player_think;
        self->stalltime = 0;
    }
}
*/

// CGR Dive Code
void player_jump(){
	if(freezeall) return;

    player_trymove(self->xdir, 0);
    
    //check noaircancel first
    if(!noaircancel || !self->animating || self->animation == self->model->animation[ANI_JUMP]){
        //air special, copied and changed from Fugue's code
        if(!level->nospecial && player[self->playerindex].playkeys & FLAG_SPECIAL){
            player[self->playerindex].playkeys -= FLAG_SPECIAL;
            if(self->model->animation[ANI_JUMPSPECIAL]){
                int candospecial = 0;
                if(player_check_energy(1, ANI_JUMPSPECIAL)){
                    if(!healthcheat) self->mp -= self->model->animation[ANI_JUMPSPECIAL]->energycost;
                    candospecial = 1;
                }
                else if(player_check_energy(0, ANI_JUMPSPECIAL)){
                    if(!healthcheat) self->health -= self->model->animation[ANI_JUMPSPECIAL]->energycost;
                    candospecial = 1;
                }
                else if(self->model->animation[ANI_JUMPCANT]){
                    ent_set_anim(self, ANI_JUMPCANT, 0);
                    self->tossv = 0;
                }
                
                if(candospecial){
                    ent_set_anim(self, ANI_JUMPSPECIAL, 0);
                    self->attacking = 1;
                    self->xdir = 0;                         // Kill movement when the special starts
                    self->tossv = 0;
                }
            }
        }//end of jumpspecial
        
        //jumpattacks, up down forward normal....we don't check energy cost
        else if(player[self->playerindex].playkeys & FLAG_ATTACK){
            player[self->playerindex].playkeys -= FLAG_ATTACK;
            self->attacking = 1;
            if((player[self->playerindex].playkeys & FLAG_MOVEDOWN) && self->model->animation[ANI_JUMPATTACK2]) ent_set_anim(self, ANI_JUMPATTACK2, 0);
            else if((player[self->playerindex].playkeys & FLAG_MOVEUP) && self->model->animation[ANI_JUMPATTACK3]) ent_set_anim(self, ANI_JUMPATTACK3, 0);
            else if(self->model->animation[ANI_RUNJUMPATTACK] && self->running) ent_set_anim(self, ANI_RUNJUMPATTACK, 0);	// Added so an extra strong jump attack can be executed
            else if(self->model->animation[ANI_JUMPFORWARD] && self->xdir != 0) ent_set_anim(self, ANI_JUMPFORWARD, 0);	// If moving and set, do this attack
            else if(self->model->animation[ANI_JUMPATTACK]) ent_set_anim(self, ANI_JUMPATTACK, 0);
    	}//end of jumpattack
    }

	if(inair(self) && self->animation->dive[0] > 0){

		self->tossv = 0;	// Void tossv so "a" can be adjusted manually
		self->toss_time = 0;

		if(self->direction) self->xdir = self->animation->dive[0];
		else self->xdir = -self->animation->dive[0];

		self->a -= self->animation->dive[1];

		if(self->a <= self->base) self->a = self->base;	// Don't want to go below ground
	}

    if(!inair(self)){
        self->tossv = 0;
        self->a = self->base;

        if(!self->model->runhold || !(player[self->playerindex].keys & FLAG_MOVELEFT || player[self->playerindex].keys & FLAG_MOVERIGHT)) self->running = 0;	// So players can continue to run if holding down forward when landing
        self->jumping = 0;
        self->attacking = 0;

		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);

        self->think = player_think;
        self->stalltime = 0;
    }
}

void dojump(int speedmul){

    if(smp_jump >= 0) sound_play_sample(smp_jump, 0, savedata.effectvol,savedata.effectvol, 100);

    self->jumping = 1;
    self->attacking = 1;	// This is needed for Mighty
    ent_set_anim(self, ANI_JUMP, 0);

    if(self->model->animation[ANI_JUMP]->numframes == 0){
		self->jumping = 0;
		return;
	}

	if(self->running) toss(self, self->model->runjumpheight);	// Jump higher if running/jumping
    else toss(self, self->model->jumpheight);

 	if(player[self->playerindex].keys & FLAG_MOVELEFT){
        self->direction = 0;
        self->xdir = -self->model->speed;

        if(self->xdir > -1) self->xdir = -1;

        self->xdir *= speedmul;
    }
    else if(player[self->playerindex].keys & FLAG_MOVERIGHT){
        self->direction = 1;
        self->xdir = self->model->speed;

        if(self->xdir < 1) self->xdir = 1;

        self->xdir *= speedmul;
    }
    else self->xdir = 0;

    self->think = player_jump;
}


// Function to check custom combos. If movestep is 0, means ready to check to see if the second step in the combo is
// valid. If so, returns 1, setting each valid combo in the list so far to 1, otherwise 0. If movestep is > 0, means
// ready to check the action button step of the combo. Loops through the "valid combo" list and sees if the action
// button step is valid, returning 1 if true, otherwise 0.

int check_combo(int m){	// New function to check combos to make sure they are valid
	int i;
	int found = 0;	// Default not found unless overridden by finding a valid combo

	if(time > self->movetime) return 0;	// Too much time passed so return 0

	if(!self->movestep){	// movestep set to 0 and got past the time > self->movetime, so must be the second step in the combo
		for(i = 0; i < self->model->specials_loaded; i++){
			if(
				(self->model->special[i][0] == self->lastmove &&
				self->model->special[i][1] == m) ||
				(self->model->special[i][0] == self->lastmove &&
				self->model->special[i][0] != FLAG_FORWARD &&
				self->model->special[i][1] == FLAG_FORWARD &&
				(m == FLAG_FORWARD || m == FLAG_BACKWARD)) ||
				(self->model->special[i][1] == m &&
				self->model->special[i][0] == FLAG_FORWARD &&
				(self->lastmove == FLAG_FORWARD || self->lastmove == FLAG_BACKWARD))
			){

				self->model->special[i][4] = 1;	// Marks all valid directional combos with a 1
				found = 1;	// There is at least 1 valid combo, so return found
			}
			else self->model->special[i][4] = 0;	// Marks all invalid directional combos with a 0
		}

		return found;	// Returns 1 if found, otherwise returns 0
	}
	else{	// movestep greater than 0 and got past time > self->movetime, so must be about to execute the action button part of the combo
		for(i = 0; i < self->model->specials_loaded; i++){
			if(self->model->special[i][4] && self->model->special[i][2] == m){	// Checks only valid directional combos to see if the action button matches

				if(player_check_energy(1, self->model->special[i][3])){

					if(!nocost && !healthcheat) self->mp -= self->model->animation[self->model->special[i][3]]->energycost;

					self->model->valid_special = i;	// Says which one is valid and returns that it was found
					return 1;
				}
				else if(player_check_energy(0, self->model->special[i][3])){	// Compares the action button with the 3rd element in the array

					if(!nocost && !healthcheat) self->health -= self->model->animation[self->model->special[i][3]]->energycost;	// Now all freespecials can cost health

					self->model->valid_special = i;	// Says which one is valid and returns that it was found
					return 1;	// Valid combo found, go ahead and return
				}
				else return 0;	// Found, but cost more health than the player had
			}
		}

		return 0;	// No valid combos found, return 0
	}
}



// Function that causes the player to continue to move up or down until the animation has finished playing

void player_dodge(){	// New function so players can dodge with up up or down down

	if(freezeall) return;	// Pause all movement/animation if freezeall is on

	if(self->animating){	// Continues to move as long as the player is animating

		if(self->lastmove == FLAG_MOVEUP) player_trymove(0, -self->model->speed * 1.75);
		else player_trymove(0, self->model->speed * 1.75);
	}
	else{	// Once done animating, returns to thinking
		self->think = player_think;

		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);
	}
}



// Function simply continues to return until the animation is done playing

void player_block(){	// New method for player blocking

	if(self->animating) return;
	self->think = player_think;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
}



// Function created to combine the action taken if either picking up an item, or running into an item that is a
// SUBTYPE_TOUCH, executing the appropriate action based on which type of item is picked up

void didfind_item(entity *other){	// Function that takes care of items when picked up
	player[self->playerindex].opponent = other;

		//for reload weapons that are guns(no knife) we use this items reload for ours shot at max and shootnum in items for get a amount of shoots by tails
	if(other->model->reload){

		if(self->weapent && self->weapent->model->typeshot){
			self->weapent->shootnum += other->model->reload;
			if(self->weapent->shootnum > self->weapent->model->shootnum) self->weapent->shootnum = self->weapent->model->shootnum;
			if(smp_get >= 0) sound_play_sample(smp_get, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		else{
			addscore(self->playerindex, other->model->score);

			if(smp_get2 >= 0) sound_play_sample(smp_get2, 0, savedata.effectvol,savedata.effectvol, 100);
		}
	}
		//end of weapons items section
	else if(other->score){
		addscore(self->playerindex, other->score);

		if(smp_get2 >= 0) sound_play_sample(smp_get2, 0, savedata.effectvol,savedata.effectvol, 100);
	}
	else if(other->health){
		self->health += other->health;

		if(self->health > self->model->health) self->health = self->model->health;

		other->health = 0;

		if(smp_get >= 0) sound_play_sample(smp_get, 0, savedata.effectvol,savedata.effectvol, 100);
	}
    else if(other->model->mp){
		self->mp += other->model->mp;

		if(self->mp > self->model->mp) self->mp = self->model->mp;

		other->mp = 0;
		sound_play_sample(smp_get, 0, savedata.effectvol,savedata.effectvol, 100);
	}
	else if(stricmp(other->model->name, "Time")==0){
		timeleft = level->settime * COUNTER_SPEED;	// Feb 24, 2005 - This line moved here to set custom time

		if(smp_get2 >= 0) sound_play_sample(smp_get2, 0, savedata.effectvol,savedata.effectvol, 100);
	}
	else if(other->model->makeinv){	// Mar 2, 2005 - New item makes player invincible
		self->invincible = 1;
		self->invinctime = time + other->model->makeinv;
		self->blink = 1;

		if(smp_get2 >= 0) sound_play_sample(smp_get2, 0, savedata.effectvol,savedata.effectvol, 100);
	}
	else if(other->model->specpower){	// Damages everything on the screen
		smart_bomb(self, other->model->specpower, other->model->spectype, other->model->speclen);

		if(smp_get2 >= 0) sound_play_sample(smp_get2, 0, savedata.effectvol,savedata.effectvol, 100);
	}
	else if(other->model->subtype == SUBTYPE_WEAPON){

		dropweapon(0);
		self->weapent = other;
		set_weapon(NULL, other->model->weapnum, self->playerindex);

		if(!other->model->typeshot && self->model->typeshot) other->model->typeshot = 1;

		if(smp_get >= 0) sound_play_sample(smp_get, 0, savedata.effectvol,savedata.effectvol, 100);
	}
	else if(other->model->subtype == SUBTYPE_PROJECTILE){

		dropweapon(0);
		self->weapent = other;

		if(smp_get >= 0) sound_play_sample(smp_get, 0, savedata.effectvol,savedata.effectvol, 100);
	}
	else if(other->credit){
        if(!noshare) credits++;
        else player[self->playerindex].credits++;

        if(smp_1up >= 0) sound_play_sample(smp_1up, 0, savedata.effectvol,savedata.effectvol, 100);
    }
	else{
		// Must be a 1up then.
		player[self->playerindex].lives++;

		if(smp_1up >= 0) sound_play_sample(smp_1up, 0, savedata.effectvol,savedata.effectvol, 100);
	}

	if(other->model->subtype != SUBTYPE_WEAPON && other->model->subtype != SUBTYPE_PROJECTILE){
		other->think = suicide;
		other->nextthink = time + GAME_SPEED * 3;
	}

	other->z = 100000;
}

void player_spawn(void)
{
     if(!self->animating) {
         self->think = player_think;
         ent_set_anim(self, ANI_IDLE, 0);
     }
}

//turn back
void player_turn(void)
{
    int walking  = 0;
    
    if(self->animation != self->model->animation[ANI_TURN]){
        if(self->direction) {
            walking = player_trymove(-self->model->speed, 0);
        } else {
            walking = player_trymove(self->model->speed, 0);
        }
    } 
     
    if(self->link) {
        self->think = player_grab;
        return;		// Grabbed someone
    }
     
    if(self->turntime && self->turntime < time - self->model->turndelay){
        if(self->model->animation[ANI_TURN]){
            ent_set_anim(self, ANI_TURN, 0);
            self->turntime = 0;
            walking = 0;
        } else {
            self->think = player_think;
            self->direction = !self->direction;
            ent_set_anim(self, ANI_WALK, 0);
            self->turntime = 0;
            return;
        }
    }
    
    if(self->animation == self->model->animation[ANI_TURN] && !self->animating){
        self->think = player_think;
        self->direction = !self->direction;
        ent_set_anim(self, ANI_IDLE, 0);
        self->turntime = 0;
        return;
    }
     
    if(!walking && self->animation != self->model->animation[ANI_TURN]) {
        if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
        else ent_set_anim(self, ANI_IDLE, 0);
        self->think = player_think;
        self->turntime = 0;
        return;
    }
    
    if(self->animation != self->model->animation[ANI_TURN] && //don't quit while playing turnback animation
       !((player[self->playerindex].keys & FLAG_MOVELEFT && self->direction) ||
        (player[self->playerindex].keys & FLAG_MOVERIGHT && !self->direction))){
       // self->direction = !self->direction;
        ent_set_anim(self, ANI_WALK, 0);
        self->turntime = 0;
        self->think = player_think; //other keys pressed, quit
    }
}


void player_think(void){
    int walking = 0;
    int up = 0;	// New up animation variable
    int down = 0;	// New down animation variable
    int	running = 0;	// New running animation variable
    int bkwalk = 0;   //backwalk 

    entity *other = NULL;
    if(self->grabbing && self->animation == self->model->animation[ANI_IDLE]) self->grabbing = NULL;

    if(endgame || self->frozen || (freezeall && self->animating)) return;	// Can't think if frozen or special is being executed

	if(time > player[self->playerindex].ent->rushtime){
		player[self->playerindex].ent->rush[0] = 0;
		player[self->playerindex].ent->rushtime = 0;
	}

	// Changed the way combos are checked so combos can be customized

    if(player[self->playerindex].playkeys & FLAG_MOVEUP && !inair(self)){
        player[self->playerindex].playkeys -= FLAG_MOVEUP;
        if(time < self->movetime && self->lastmove==FLAG_MOVEUP && self->model->animation[ANI_ATTACKUP]){	// New u u combo attack
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
			self->attacking = 1;
			self->combostep = 0;
			ent_set_anim(self, ANI_ATTACKUP, 0);
			return;
		}
		else if(time < self->movetime && self->lastmove==FLAG_MOVEUP && self->model->animation[ANI_DODGE] &&
		!self->attacking && !inair(self)){	// New dodge move like on SOR3
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
			self->combostep = 0;
			self->getting = 0;
			self->think = player_dodge;
			ent_set_anim(self, ANI_DODGE, 0);
			return;
		}
		else if(check_combo(FLAG_MOVEUP)) ++self->movestep;	// Check the combo and increase movestep if valid
		else self->movestep = 0;

        self->lastmove = FLAG_MOVEUP;
        self->movetime = time + (GAME_SPEED/4);
    }

	if(player[self->playerindex].playkeys & FLAG_MOVEDOWN && !inair(self)){
		player[self->playerindex].playkeys -= FLAG_MOVEDOWN;
		if(self->lastmove==FLAG_MOVEDOWN && self->model->animation[ANI_ATTACKDOWN] && time < self->movetime){	// New d d combo attack
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
			self->attacking = 1;
			self->combostep = 0;
			ent_set_anim(self, ANI_ATTACKDOWN, 0);
			return;
		}
		else if(time < self->movetime && self->lastmove == FLAG_MOVEDOWN && self->model->animation[ANI_DODGE] &&
		!self->attacking && !inair(self)){	// New dodge move like on SOR3
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
			self->combostep = 0;
			self->getting = 0;
			self->think = player_dodge;
			ent_set_anim(self, ANI_DODGE, 0);
			return;
		}
		else if(check_combo(FLAG_MOVEDOWN))  ++self->movestep;	// Check the combo and increase movestep if valid
		else self->movestep = 0;

		self->lastmove = FLAG_MOVEDOWN;
		self->movetime = time + (GAME_SPEED/4);
    }

	// Left/right movement for combos is more complicated because forward/backward is relative to the direction the player is facing
	// Checks on current direction have to be made before and after executing the combo to make sure they get the right one

    if((player[self->playerindex].playkeys & FLAG_MOVELEFT) && !inair(self)){
        player[self->playerindex].playkeys -= FLAG_MOVELEFT;
        
		if(self->model->animation[ANI_RUN] && time < self->movetime && (self->lastmove==FLAG_FORWARD ||
		self->lastmove==FLAG_BACKWARD) && !self->direction) self->running = 1;	// Player begins to run

		else if(self->model->animation[ANI_ATTACKFORWARD] && time < self->movetime &&
		(self->lastmove == FLAG_FORWARD || self->lastmove == FLAG_BACKWARD) && !self->direction){
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
			self->attacking = 1;
			self->combostep = 0;
			ent_set_anim(self, ANI_ATTACKFORWARD, 0);
			return;
		}
		else if(!self->direction && check_combo(FLAG_FORWARD))  ++self->movestep;	// Check direction to distinguish forward/backward movements
		else if(self->direction && check_combo(FLAG_BACKWARD))  ++self->movestep;	// Check direction to distinguish forward/backward movements
		else self->movestep = 0;

		if(self->direction) self->lastmove = FLAG_BACKWARD;
		else self->lastmove = FLAG_FORWARD;

        self->movetime = time + (GAME_SPEED/4);
    }

	if((player[self->playerindex].playkeys & FLAG_MOVERIGHT) && !inair(self)){
		player[self->playerindex].playkeys -= FLAG_MOVERIGHT;

		if(self->model->animation[ANI_RUN] && time < self->movetime && (self->lastmove==FLAG_FORWARD ||
		self->lastmove==FLAG_BACKWARD) && self->direction) self->running = 1;	// Player begins to run

		else if(self->model->animation[ANI_ATTACKFORWARD] && time < self->movetime &&
		(self->lastmove == FLAG_FORWARD || self->lastmove == FLAG_BACKWARD) && self->direction){
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
			self->attacking = 1;
			self->combostep = 0;
			ent_set_anim(self, ANI_ATTACKFORWARD, 0);
			return;
		}
		else if(!self->direction && check_combo(FLAG_BACKWARD))  ++self->movestep;	// Check direction to distinguish forward/backward movements
		else if(self->direction && check_combo(FLAG_FORWARD))  ++self->movestep;	// Check direction to distinguish forward/backward movements
		else self->movestep = 0;

		if(!self->direction) self->lastmove = FLAG_BACKWARD;
		else self->lastmove = FLAG_FORWARD;

		self->movetime = time + (GAME_SPEED/4);
    }

    if(self->attacking){

        if(inair(self) && !freezeall){	// Don't want to move if frozen

            if(self->direction) player_trymove((self->animation->moveflag == 0), 0);
            else player_trymove((self->animation->moveflag == 0) * -1, 0);

            self->nextthink = time + (THINK_SPEED/2);
            return;	// No more moving/attacking if executing a jumpframe
        }

		if((self->attacking = self->animating)) return;

        if(self->tocost){	// Enemy was hit with a special so go ahead and subtract life

			if(player_check_energy(1, self->animnum)){
				self->mp -= self->animation->energycost;
			}
			else self->health -= self->animation->energycost;

			self->tocost = 0;	// Life is subtracted, so go ahead and reset the flag
		}

        if(freezeall){	// Player is done with the special animation, so unfreeze and execute a smart bomb
			freezeall = 0;
			smart_bomb(self, self->model->specpower, self->model->spectype, self->model->speclen);
		}

		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);
    }

    if(self->getting){
        if((self->getting = self->animating)) return;
        player[self->playerindex].iconmode = 0;  //20-1-2005   New icons

		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);
    }

    if(!ajspecial && (player[self->playerindex].playkeys & FLAG_JUMP) && self->model->animation[ANI_ATTACKBOTH] && !inair(self)){
		if(player[self->playerindex].keys & FLAG_ATTACK){
			player[self->playerindex].playkeys -= FLAG_JUMP;
			self->attacking = 1;
			self->combostep = 0;
			self->movestep = 0;
			self->stalltime = 0;	// If attack is pressed, holding down attack to execute attack3 is no longer valid
			ent_set_anim(self, ANI_ATTACKBOTH, 0);
			return;
		}
	}

	if((player[self->playerindex].playkeys & FLAG_JUMP) &&
        self->model->animation[ANI_CHARGE] &&
        !inair(self)){
            if(player[self->playerindex].playkeys & FLAG_SPECIAL &&
               count_ents(TYPE_ENEMY)){
                   self->combostep = 0;
                   self->movestep = 0;
                   self->stalltime = 0;
                   ent_set_anim(self, ANI_CHARGE, 0);
                   return;
			}
    }

	if(player[self->playerindex].playkeys & FLAG_SPECIAL && !inair(self)){	//	The special button can now be used for freespecials

		if(
			(player_check_energy(0, ANI_SPECIAL2) || player_check_energy(1, ANI_SPECIAL2)) &&
			!level->nospecial &&
			(!self->direction ?
			(player[self->playerindex].keys & FLAG_MOVELEFT) :
			(player[self->playerindex].keys & FLAG_MOVERIGHT))
		){

			player[self->playerindex].playkeys -= FLAG_SPECIAL;

			if(!nocost && !healthcheat){

				if(player_check_energy(1, ANI_SPECIAL2)) self->mp -= self->model->animation[ANI_SPECIAL2]->energycost;
				else self->health -= self->model->animation[ANI_SPECIAL2]->energycost;
			}

			self->attacking = 1;
			self->combostep = 0;
			self->movestep = 0;
			ent_set_anim(self, ANI_SPECIAL2, 0);
			return;
		}

		if(self->movestep >= 1 && check_combo(FLAG_SPECIAL)){
			player[self->playerindex].playkeys -= FLAG_SPECIAL;

			if(!self->model->animation[self->model->special[self->model->valid_special][3]]){
				// This is for Mighty
				self->combostep = 0;
				dojump(2);
				return;
			}

			self->attacking = 1;
			self->combostep = 0;
			self->movestep = 0;
			ent_set_anim(self, self->model->special[self->model->valid_special][3], 0);
			return;
		}

		if(self->model->animation[ANI_BLOCK] && !self->model->holdblock){	// New block code for players
			player[self->playerindex].playkeys -= FLAG_SPECIAL;

			self->attacking = 0;
			self->combostep = 0;
			self->movestep = 0;
			self->think = player_block;
			ent_set_anim(self, ANI_BLOCK, 0);
			return;
		}
	}

	if(!inair(self) && player_check_special()) return;	// So you don't perform specials falling off the edge

    if(!(player[self->playerindex].keys & FLAG_ATTACK) && !inair(self)){

        if(self->stalltime && self->stalltime+(GAME_SPEED*2) < time){
            self->attacking = 1;
            self->combostep = 0;
            if(self->model->animation[ANI_CHARGEATTACK]) ent_set_anim(self, ANI_CHARGEATTACK, 0);
            else ent_set_anim(self, self->model->atchain[self->model->chainlength] + ANI_ATTACK1 - 1, 0);

            if(smp_punch >= 0) sound_play_sample(smp_punch, 0, savedata.effectvol,savedata.effectvol, 100);

            self->stalltime = 0;
            return;

        }
        self->stalltime = 0;
    }

    if((player[self->playerindex].playkeys & FLAG_ATTACK) && !inair(self)){
		player[self->playerindex].playkeys -= FLAG_ATTACK;
        self->stalltime = 0;	// Disable the attack3 stalltime

		if(self->running && self->model->animation[ANI_RUNATTACK]){	// New run attack code section
			player[self->playerindex].playkeys -= FLAG_SPECIAL;
			self->attacking = 1;
            self->combostep = 0;
            self->running = 0;
			ent_set_anim(self, ANI_RUNATTACK, 0);
			return;
		}

        // Perform special move, Now checks custom combos
        if(self->movestep >= 1 && check_combo(FLAG_ATTACK)){

            if(!self->model->animation[self->model->special[self->model->valid_special][3]]){
                // This is for Mighty
                self->combostep = 0;
                dojump(2);
                return;
            }
            player[self->playerindex].playkeys -= FLAG_SPECIAL;
            self->attacking = 1;
            self->combostep = 0;
            self->movestep = 0;
            ent_set_anim(self, self->model->special[self->model->valid_special][3], 0);
            return;
        }

		if(self->model->animation[ANI_ATTACKBACKWARD] && time < self->movetime - (GAME_SPEED/10) &&
		!self->movestep && self->lastmove == FLAG_BACKWARD){	// New back attacks

			self->attacking = 1;

			if(self->direction && (player[self->playerindex].keys & FLAG_MOVERIGHT)) self->direction = 0;	// Since the back part of the combo will flip the player, need to flip them back around
			else if(!self->direction && (player[self->playerindex].keys & FLAG_MOVELEFT)) self->direction = 1;	// Since the back part of the combo will flip the player, need to flip them back around

			self->combostep = 0;
			ent_set_anim(self, ANI_ATTACKBACKWARD, 0);

			return;
		}

		if(
			(other = find_ent_here(self, self->x, self->z, TYPE_ITEM)) &&
			other->model->subtype != SUBTYPE_TOUCH && !other->blink &&
			diff(self->a - (self->animation->seta[self->animpos] >= 0) * self->animation->seta[self->animpos] , other->a)<0.1
		){
            //dont pickup a weapon that is not in weapon list
            if(!((other->model->subtype==SUBTYPE_WEAPON && !stricmp(self->model->weapon[other->model->weapnum-1], "")) ||
            //if on an animal, can't pick up weapons 2007 - 2 - 11 by UTunnels
              (self->model->animal==2 && other->model->subtype == SUBTYPE_WEAPON))){
    			didfind_item(other);
                self->getting = 1;
                if(self->model->animation[ANI_GET]) ent_set_anim(self, ANI_GET, 0);
                player[self->playerindex].iconmode = 3;  //20-1-2005   New icons
                return;
            }
        }

        // Use stalltime to charge end-move
        self->stalltime = time;
 		self->attacking = 1;

        if(self->combotime > time && 
		  (!self->weapent || (self->weapent && self->weapent->model->subtype == SUBTYPE_WEAPON))){
			self->combostep++;
		}
        else self->combostep = 0;

        if(!self->model->animation[ANI_ATTACK1]){
            // This is for Mighty
            self->combostep = 0;
            dojump(1);
            return;
        }

        if(self->combostep < self->model->chainlength){

			if(
				self->weapent &&
				self->weapent->model->subtype == SUBTYPE_PROJECTILE &&
				self->model->animation[ANI_THROWATTACK]
			){

				ent_set_anim(self, ANI_THROWATTACK, 0);
			}
			else ent_set_anim(self, self->model->atchain[self->combostep] + ANI_ATTACK1 - 1, 0);
        }
        else{

            ent_set_anim(self, self->model->atchain[self->combostep] + ANI_ATTACK1 - 1, 0);
            self->combostep = 0;
        }

        if(smp_punch >= 0 && self->attacking) sound_play_sample(smp_punch, 0, savedata.effectvol,savedata.effectvol, 100);

        return;
    }
    // 7-1-2005 spawn projectile end

    // Mighty hass no attack animations, he just jumps.
    if(player[self->playerindex].playkeys & FLAG_JUMP && !inair(self)){	// Added !inair(self) so players can't jump when falling into holes
        player[self->playerindex].playkeys -= FLAG_JUMP;

        if(self->running) dojump(self->model->runjumpdist);	// Player jumps a further distance if running/jumping
        else{
			if(self->movestep >= 1 && check_combo(FLAG_JUMP)){	// Jump can now be used with freespecials
				if(!self->model->animation[self->model->special[self->model->valid_special][3]]){
					// This is for Mighty
					self->combostep = 0;
					dojump(2);
					return;
				}

				player[self->playerindex].playkeys -= FLAG_SPECIAL;
				self->attacking = 1;
				self->combostep = 0;
				self->movestep = 0;
				ent_set_anim(self, self->model->special[self->model->valid_special][3], 0);
				return;
			}
			else dojump(1);
		}
        return;
    }

	if(self->model->animation[ANI_BLOCK] && self->model->holdblock && player[self->playerindex].keys & FLAG_SPECIAL && !inair(self)){
		ent_set_anim(self, ANI_BLOCK, 0);
		return;
	}

	if(PLAYER_MIN_Z != PLAYER_MAX_Z){	// More of a platform feel

		if(player[self->playerindex].keys & FLAG_MOVEUP && !inair(self)){

			if(!self->model->runupdown || self->model->animation[ANI_UP]) self->running = 0;	// Quits running if player presses up or the up animation exists

			if(self->model->animation[ANI_UP]) up = player_trymove(0, -self->model->speed/2);	// Used for up animation
			else if(self->running) running = player_trymove(0, -self->model->runspeed/2);	// Moves up at a faster rate running
			else walking = player_trymove(0, -self->model->speed/2);
		}
		else if(player[self->playerindex].keys & FLAG_MOVEDOWN && !inair(self)){

			if(!self->model->runupdown || self->model->animation[ANI_DOWN]) self->running = 0;	// Quits running if player presses down or the down animation exists

			if(self->model->animation[ANI_DOWN]) down = player_trymove(0, self->model->speed/2);	// Used for down animation
			else if(self->running) running = player_trymove(0, self->model->runspeed/2);	// Moves down at a faster rate running
			else walking = player_trymove(0, self->model->speed/2);
		}
	}
	else if(self->model->animation[ANI_DUCK] && player[self->playerindex].keys & FLAG_MOVEDOWN && !inair(self)){
		ent_set_anim(self, ANI_DUCK, 0);
		return;
	}

    if(self->link) return;		// Grabbed someone

    if(player[self->playerindex].keys & FLAG_MOVELEFT){
		if(self->direction) {
            self->running = 0;	// Quits running if player changes direction
            if(!self->turntime && (self->model->turndelay>0 || self->model->animation[ANI_TURN]) 
            ) {
                self->turntime = time;
                if(self->model->animation[ANI_BACKWALK]) ent_set_anim(self, ANI_BACKWALK, 0);
                else ent_set_anim(self, ANI_WALK, 0);
                self->think = player_turn;
                return;
            } 
        }
        
        self->direction = 0;

        if(self->running) running = player_trymove(-self->model->runspeed, 0);	// If running, player moves at a faster rate
        else walking = player_trymove(-self->model->speed, 0);
    }
    else if(player[self->playerindex].keys & FLAG_MOVERIGHT){

		if(!self->direction) {
            self->running = 0;	// Quits running if player changes direction
            if(!self->turntime && (self->model->turndelay>0 || self->model->animation[ANI_TURN]) 
            ) {
                 self->turntime = time;
                 if(self->model->animation[ANI_BACKWALK]) ent_set_anim(self, ANI_BACKWALK, 0);
                 else ent_set_anim(self, ANI_WALK, 0);
                 self->think = player_turn;
                 return;
            } 
        }
        
        self->direction = 1;

        if(self->running) running = player_trymove(self->model->runspeed, 0);	// If running, player moves at a faster rate
        else walking = player_trymove(self->model->speed, 0);
    }
    else if(self->running && !((player[self->playerindex].keys & FLAG_MOVELEFT) ||
    	(player[self->playerindex].keys & FLAG_MOVERIGHT))
    )
        self->running = 0;	// Player let go of left/right and so quits running


    if(self->link) return;		// Grabbed someone

    //	ltb 1-18-05  new Item get code to address new subtype

    if(
		(other = find_ent_here(self, self->x, self->z, TYPE_ITEM)) &&
		other->model->subtype == SUBTYPE_TOUCH && !other->blink &&
    	diff(self->a - (self->animation->seta[self->animpos] >= 0) * self->animation->seta[self->animpos] , other->a)<0.1 &&
    	(walking || up || down || running)
    ){
        //dont pickup a weapon that is not in weapon list
        if(!((other->model->subtype==SUBTYPE_WEAPON && !stricmp(self->model->weapon[other->model->weapnum-1], "")) ||
        //return if on an animal, can't pick up weapons 2007 - 2 - 11 by UTunnels
          (self->model->animal==2 && other->model->subtype == SUBTYPE_WEAPON)))
          didfind_item(other);	// Added function to clean code up a bit
	}
	
 	if(up) ent_set_anim(self, ANI_UP, 0);	// Set to up animation if exists
 	else if(down) ent_set_anim(self, ANI_DOWN, 0);	// Set to down animation if exists
 	else if(running) ent_set_anim(self, ANI_RUN, 0);	// Set to run animation if exists
 	else if(walking) {
        // back walk feature
        if(level && self->model->animation[ANI_BACKWALK]){
            if(self->facing == 1 || level->facing == 1)  bkwalk = !self->direction;
            else if(self->facing == 2 || level->facing == 2) bkwalk = self->direction;
            else if((self->facing == 3 || level->facing == 3) && level->scrolldir == SCROLL_LEFT && !self->direction ) bkwalk = 1;
            else if((self->facing == 3 || level->facing == 3) && level->scrolldir == SCROLL_RIGHT && self->direction) bkwalk = 1;
            if(bkwalk) ent_set_anim(self, ANI_BACKWALK, 0);
            else ent_set_anim(self, ANI_WALK, 0);	// If neither up nor down exist, set to walk
        }
        else ent_set_anim(self, ANI_WALK, 0);	// If neither up nor down exist, set to walk
    }
	else if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else if(self->model->animation[ANI_SLEEP] && (time >= self->sleeptime) && self->animating) ent_set_anim(self, ANI_SLEEP, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
	
	self->turntime = 0;
}

void subtract_shot(){

	if(self->weapent && self->weapent->shootnum){
		self->weapent->shootnum--;

		if(!self->weapent->shootnum){
			self->weapent->counter = 0;
			dropweapon(0);
		}
	}

}


void dropweapon(int flag){
	int wall;
	entity* other = NULL;

	if(self->weapent){
		if(self->weapent->model->typeshot || (!self->weapent->model->typeshot && self->weapent->shootnum)){
			self->weapent->direction = self->direction;//same direction as players, 2007 -2 - 11   by UTunnels
			if(flag < 2) self->weapent->counter -= flag;
			self->weapent->z = self->z;
			self->weapent->x = self->x;
			self->weapent->a = self->a;

			other = check_platform(self->weapent->x, self->weapent->z);
			wall = checkwall(self->weapent->x, self->weapent->z);

			if(other && other != self->weapent)   self->weapent->base += other->a + other->animation->platform[other->animpos][5];
			else if(wall >= 0) self->weapent->base += level->walls[wall][7];

			if(self->weapent->model->animation[ANI_RESPAWN]) ent_set_anim(self->weapent, ANI_RESPAWN, 0);
			else if(self->weapent->model->animation[ANI_SPAWN]) ent_set_anim(self->weapent, ANI_SPAWN, 0);
			else ent_set_anim(self->weapent, ANI_IDLE, 0);

			if(!self->weapent->counter){
	            if(!self->model->animal){
		           self->weapent->blink = 1;
			       self->weapent->think = enemy_lie;
				}
				else{
					self->weapent->type = TYPE_NONE;
					self->weapent->think = runanimal;
				}
	            self->weapent->nextthink = time + 1;
		     }
		}		
		self->weapent = NULL;

		if(flag < 2){
		if(self->type == TYPE_PLAYER)  set_weapon(NULL, level->setweap, self->playerindex);
		else set_weapon(self, 0, -1);
		}
	}
}


void player_takedamage(entity *other, int force, int drop, int type){

    self->attacking = 0;
    self->jumping = 0;
    self->getting = 0;	// Added so players can move if being hurt while getting an item
    self->running = 0;	// Can't continue to run if being hurt

    if(self->a < PIT_DEPTH){
        player_die();
        return;
    }

	if(self->model->animal) drop = 1;

	// Drop Weapon due to being hit.
	if(self->model->weaploss <= 0) dropweapon(1);

	// Players can't be hurt if cheating or specified in the level->type
	if(healthcheat || level->nohurt) force = 0;	
    if(other != self->link || drop) ent_unlink(self);
	
	// Need to display the health bar of the one that's attacking the player if it's an enemy
    if(other != self && other->type != TYPE_SHOT) player[self->playerindex].opponent = other;	
    
	// Show health bar of the one being attacked if it's a player/pshot attacking
	if(other->type == TYPE_PLAYER || other->type == TYPE_SHOT) player[other->playerindex].opponent = self; 
    
	// Need to display the health bar of the owner of the pshot
	if(other->type == TYPE_SHOT) player[self->playerindex].opponent = player[other->playerindex].ent;	

    self->pain_time = time + (GAME_SPEED / 5);

	// Can't turn to player if frozen
    if(type != ATK_FREEZE || (type == ATK_FREEZE && self->frozen)) self->direction = (self->x < other->x);	
    self->xdir = -self->xdir;

	// Include player projectiles as well
    if(other->type == TYPE_PLAYER || other->type == TYPE_SHOT) force /= 2;	

	// If in the air, automatically fall (freeze will override later if not frozen)
	if(inair(self) || self->animation == self->model->animation[ANI_VAULT]) drop = 1;       

    if(type == ATK_BLAST){
        self->projectile = 1;
        self->blasted = 1;
        drop = 1;
    }

	// Takes life from opponent and adds it to yours
	if(type == ATK_STEAL){	

		if(self->health >= force) other->health += force;
		else other->health += self->health;

		if(other->health > other->maxhealth) other->health = other->maxhealth;
	}

	// Only freeze if not already frozen
    if(type == ATK_FREEZE && !self->frozen){	

		// Changes to frozen map and sets freezetime
		if(self->model->fmap) ent_set_colourmap(self, self->model->fmap);	

		self->frozen = 1;
		self->freezetime = time + other->animation->attack_length[other->animpos];
		// Disable the attack3 if frozen
		self->stalltime = 0;	
		drop = 0;
	}
	// If already frozen, instanly drop regardless of attack
	else if(self->frozen){	
		drop = 1;
		// Added so entities don't continue to be linked
		ent_unlink(self);	
	}

    self->health -= force;

#if DC
	dcpad_rumblepower[self->playerindex] = force * 3;
#endif


    if(drop || self->health <= 0 || inair(self)){
        if(self->health <= 0 && self->model->death &&
		  (self->model->animation[ANI_DIE]      || self->model->animation[ANI_DIE2] ||
		   self->model->animation[ANI_DIE3]     || self->model->animation[ANI_DIE4] ||
		   self->model->animation[ANI_DIE5]     || self->model->animation[ANI_DIE6] ||
		   self->model->animation[ANI_DIE7]     || self->model->animation[ANI_DIE8] ||
		   self->model->animation[ANI_DIE9]     || self->model->animation[ANI_DIE10] ||
		   self->model->animation[ANI_SHOCKDIE] || self->model->animation[ANI_BURNDIE])
		   ){

            if(self->model->diesound >= 0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100);
            player[self->playerindex].iconmode=2;
			set_death(self, type, 0);
		}
		else{
			player[self->playerindex].iconmode=2;
			self->xdir = (self->blasted)?((self->direction)?(-2.5):2.5):((self->direction)?(-1):1);
			toss(self, self->model->jumpheight/1.333);
			set_fall(self, type, 1);
		}
        self->damage_on_landing = 0;
        self->think = player_fall;
#if DC
		dcpad_rumblepower[self->playerindex] *= 2;
#endif
        return;
    }

	// Only play ANI_PAIN if not frozen so frozen in current animation
	if(!self->frozen){
		set_pain(self, type, 1);
        self->think = player_pain;
     }
}






////////////////////////////////



// Called when player re-enters the game.
// Drop all enemies EXCEPT for the linked/frozen ones.
void drop_all_enemies(){
    int i;
	entity* weapself = self;
    for(i=0; i<MAX_ENTS; i++){
        if(ent_list[i]->exists &&
                ent_list[i]->health>0 &&
                ent_list[i]->type==TYPE_ENEMY &&
                !ent_list[i]->owner &&	// Don't want to knock down a projectile
                ent_list[i]->model->subtype != SUBTYPE_ARROW &&	// Don't want to knock down an arrow
                !ent_list[i]->link &&
                !ent_list[i]->frozen &&	// Don't want to unfreeze a frozen enemy
                !ent_list[i]->model->nomove &&
                !ent_list[i]->model->nodrop &&
                ent_list[i]->model->animation[ANI_FALL]
          ){
            ent_list[i]->attacking = 0;
            ent_list[i]->projectile = 0;
            ent_list[i]->xdir = (self->direction)?(-1.2):1.2;
            ent_list[i]->think = enemy_fall;
            ent_list[i]->damage_on_landing = 0;
			self = ent_list[i]; 	 
	        dropweapon(1); 	 
	        self = weapself;
			toss(ent_list[i], 2.5 + randf(1));
            ent_set_anim(ent_list[i], ANI_FALL, 1);
        }
    }
}



// Called when boss dies
void kill_all_enemies(){
    int i;
    entity * tmpself = NULL;

    tmpself = self;
    for(i=0; i<MAX_ENTS; i++){
        if(		ent_list[i]->exists
                && ent_list[i]->health>0
                && ent_list[i]->type==TYPE_ENEMY
                && ent_list[i]->takedamage){
            self = ent_list[i];
            self->takedamage(tmpself, 1000000, 1, ATK_NORMAL);
        }
    }
    self = tmpself;
}



void smart_bomb(entity* e, int p, int t, int l){	// New method for smartbombs
    int i;

    entity * tmpself = NULL;

    tmpself = self;
    for(i=0; i<MAX_ENTS; i++){
        if(		ent_list[i]->exists
                && ent_list[i]->health>0
                && ent_list[i]->type==TYPE_ENEMY
                && ent_list[i]->takedamage
                && !ent_list[i]->model->nomove
                && ent_list[i]->think != enemy_lie
          ){

            self = ent_list[i];

            if(t == ATK_FREEZE) self->freezetime = time + l;
			self->takedamage(e, p, 1, t);
        }
    }
    self = tmpself;
}

////////////////////////////////



void anything_walk(void){
    if(freezeall) return;
    if(self->x < advancex - 80 || self->x > advancex + (hRes1+80)){
        kill(self);
        return;
    }
    self->x += self->xdir;
}



void knife_think(void){
	int wall;
	entity* target = NULL;

	if(freezeall) return;	// Don't update animation if special is being executed
    if(
        self->x < advancex - 180 || self->x > advancex + (hRes1+180) ||
        (level && level->scrolldir != SCROLL_UP && level->scrolldir != SCROLL_DOWN &&
         (self->z < advancey - 180 || self->z > advancey + (vRes1+180))) ||
        (level && (level->scrolldir == SCROLL_UP || level->scrolldir == SCROLL_DOWN) &&
         (self->z < -180 || self->z > vRes1 + 180))
     ){
        kill(self);
        return;
    }
    
    // new subtype chase
    if(self->model->subtype == SUBTYPE_CHASE){
        if(self->model->hitenemy < 0) 
            target = homing_find_target(self->hostile);
        else if(self->model->hitenemy == 1){// lol, backward compatible stuff
            target = homing_find_target(TYPE_ENEMY | TYPE_PLAYER);
        } else {
            target = homing_find_target( TYPE_PLAYER);
        }
        if(target) {
            if(!self->model->noflip){self->direction = (target->x > self->x);}
            // start chasing the target
            float dx = diff(self->x, target->x);
            float dz = diff(self->z, target->z);
            float maxspeed = self->model->speed*1.5;
            
            if(!dz && !dx) self->xdir = self->zdir = 0;
            else {
                self->xdir = maxspeed * dx / (dx+dz);
                self->zdir = maxspeed * dz / (dx+dz);
            }
            if(self->direction!=1) self->xdir = -self->xdir;
            if(self->z > target->z) self->zdir = -self->zdir;
        } else {
            if(!self->xdir && !self->zdir){
                if(self->direction==1) self->xdir = self->model->speed;
                else self->xdir = -self->model->speed;
            }
        }  
        if(!self->model->nomove )
        {
 			if(target && self->z > target->z && self->model->animation[ANI_UP])
 				ent_set_anim(self, ANI_UP, 0);
 			else if(target && target->z > self->z && self->model->animation[ANI_DOWN])
 				ent_set_anim(self, ANI_DOWN, 0);
 			else if(self->model->animation[ANI_WALK]) ent_set_anim(self, ANI_WALK, 0);
            else ent_set_anim(self, ANI_IDLE, 0 );
        }
        self->x += self->xdir;
        self->z += self->zdir;
        //if(target)printf("zdir: %f   targetz: %f  selfz: %f\n", self->zdir, target->z,  self->z);
    } else {
        if(self->direction) self->x += self->model->speed;	// Now projectiles can have custom speeds
        else self->x -= self->model->speed;
    }

	if(level){
		if((level->exit_blocked && self->x > level->width-30-(PLAYER_MAX_Z-self->z)) ||
		  ((wall = checkwall(self->x, self->z)) >= 0 && self->a < level->walls[wall][7])){

			if(self->model->animation[ANI_FALL]){	// Added so projectiles bounce off blocked exits
				self->attacking = 0;
				self->health -= 100000;
				self->projectile = 0;
				self->xdir = (self->direction)?(-1.2):(1.2);
				self->think = enemy_fall;
				self->damage_on_landing = 0;
				toss(self, 2.5 + randf(1));
				ent_set_anim(self, ANI_FALL, 0);
			}
		}
	}

    if(self->ptype == 0) self->nextthink = time + THINK_SPEED / 2;
	else if(self->ptype == 1) self->nextthink = self->autokill = 1;
}



void knife_spawn(float x, float z, float a, int direction, int flag){
    entity *e = NULL;

	if(self->weapent && self->weapent->model->project[0]) e = spawn(x, z, a, self->weapent->model->project);
    else if(self->animation->custknife[0]) e = spawn(x, z, a, self->animation->custknife);
    else if(self->model->knife[0] && (self->model->ptype == 0)) e = spawn(x, z, a, self->model->knife);
	else if(self->model->knife[0] && (self->model->ptype == 1)) e = spawn(x, z, 0, self->model->knife);
    else if(!flag ) e = spawn(x, z, a, "Knife");
    else if( flag) e = spawn(x, z, a, "Shot");
    else return;

    if(e==NULL) return;

	if(self->type == TYPE_ENEMY ){
		e->type = TYPE_ENEMY;
		e->screen = e->model->alpha;
		e->takedamage = enemy_takedamage;	// Players can now hit projectiles
		e->owner = self;	// Added so enemy projectiles don't hit the owner
		e->nograb = 1;	// Prevents trying to grab a projectile
	}
	else if(self->type == TYPE_PLAYER){
		e->type = TYPE_SHOT;
		e->playerindex = self->playerindex;

		if(self->animation->energycost > 0 && nocost) self->cantfire = 1;	// Can't fire if still exists on screen
	}
	else if(self->type == TYPE_NPC ){
		e->type = TYPE_NPC;
		e->screen = e->model->alpha;
		e->takedamage = npc_takedamage;	
		e->owner = self;
		e->nograb = 1;
	}

    e->attacking = 1;

    if(!e->model->speed && !e->model->nomove) e->model->speed = 2;
    else if(e->model->nomove) e->model->speed = 0;

    e->direction = direction;
    e->think = knife_think;
    e->remove_on_attack = e->model->remove;
    e->autokill = e->model->nomove;
	e->ptype = self->model->ptype;

	if(e->ptype == 0) e->base = a;
	else if(e->ptype == 1) e->base = 0;

}



void bomb_explode(void){
    self->screen = self->model->alpha;//to put a explotion transparent effects
	if(self->animating) return;
	kill(self);
}



void bomb_think(void){

	if(freezeall) return;

	if(!(self->x > level->width - 30 - (PLAYER_MAX_Z-self->z))){
    	if(self->direction) self->x += self->model->speed;	// Now projectiles can have custom speeds
    	else self->x -= self->model->speed;
	}

	self->nextthink = time + THINK_SPEED / 2;

	if(inair(self) && self->toexplode == 1) return;

	self->tossv = 0;	// Stop moving up/down
	self->base = self->a;	// Stop moving up/down

	if(self->model->diesound >= 0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100);

	if(self->toexplode == 2 && self->model->animation[ANI_ATTACK2]) ent_set_anim(self, ANI_ATTACK2, 0);	// If bomb never reaces the ground, play this
	else ent_set_anim(self, ANI_ATTACK1, 0);

	self->think = bomb_explode;
}



void bomb_spawn(float x, float z, float a, int direction){
	entity *e = NULL;

	if(self->weapent && self->weapent->model->subtype == SUBTYPE_PROJECTILE && self->weapent->model->project[0]) e = spawn(x, z, a, self->weapent->model->project);
	else if(self->animation->custbomb[0]) e = spawn(x, z, a, self->animation->custbomb);
	else if(self->model->bomb[0]) e = spawn(x, z, a, self->model->bomb);
	else return;

	if(e==NULL) return;

	if(self->type == TYPE_ENEMY ){
		e->takedamage = enemy_takedamage;	// Players can now hit projectiles
		e->owner = self;	// Added so enemy projectiles don't hit the owner
		e->nograb = 1;	// Prevents trying to grab a projectile
	}
	else if(self->type == TYPE_PLAYER){
		e->playerindex = self->playerindex;

		if(self->animation->energycost > 0 && nocost) self->cantfire = 1;	// Can't fire if still exists on screen
	}
	else if(self->type == TYPE_NPC){
		e->takedamage = npc_takedamage;	// Players can now hit projectiles
		e->owner = self;	// Added so enemy projectiles don't hit the owner
		e->nograb = 1;	// Prevents trying to grab a projectile
	}

	e->attacking = 1;

    if(!e->model->speed && !e->model->nomove) e->model->speed = 2;
    else if(e->model->nomove) e->model->speed = 0;

    e->toexplode = 1;	// Set to distinguish exploding projectiles and also so stops falling when hitting an opponent
    e->direction = direction;
 	toss(e, e->model->jumpheight);
 	e->think = bomb_think;
    e->remove_on_attack = 0;
    e->autokill = e->model->nomove;
}



void star_think(void){
	int wall;

	if(freezeall) return;	// Don't update animation if special is being executed

    if(self->x<advancex-80 || self->x>advancex+(hRes1+80) || self->base<0){
        kill(self);
        return;
    }

    self->x += self->xdir;
    self->base -= 2;
    self->a = self->base;

	if(self->model->animation[ANI_FALL]){	// Added so projectiles bounce off blocked exits

		if((level->exit_blocked && self->x > level->width-30-(PLAYER_MAX_Z-self->z)) ||
		((wall = checkwall(self->x, self->z)) >= 0 && self->a < level->walls[wall][7])){
			self->attacking = 0;
			self->health -= 100000;
			self->projectile = 0;
			self->xdir = (self->direction)?(-1.2):1.2;
			self->think = enemy_fall;
			self->damage_on_landing = 0;
			toss(self, 2.5 + randf(1));
			ent_set_anim(self, ANI_FALL, 0);
		}
	}

    self->nextthink = time + THINK_SPEED / 2;
}



// Spawn 3 stars
void star_spawn(float x, float z, float a, int direction){ // added entity to know which star to load
    entity *e = NULL;
    int i;
    char* starname;
    float fd = (direction ? 2 : -2);

    //merge enemy/player together, use the same rules
	if(self->weapent && self->weapent->model->subtype==SUBTYPE_PROJECTILE && self->weapent->model->project[0]) starname = self->weapent->model->project;
    else if(self->animation->custstar[0]) starname = self->animation->custstar; //use any star
    else if(self->model->star[0]) starname = self->model->star;
    else starname = "Star"; // this is default star

    for(i = 0; i<3; i++){
        e = spawn(x, z, a, starname);
        if(!e) return;
        
        self->attacking = 0;
    
        e->takedamage = enemy_takedamage;	// Players can now hit projectiles
        e->owner = self;	// Added so enemy projectiles don't hit the owner
        e->attacking = 1;
        e->nograb = 1;	// Prevents trying to grab a projectile
        e->xdir = fd/2 * (float)i;
        e->think = star_think;
        e->remove_on_attack = e->model->remove;
        e->base = a;
        e->a = a;
        e->direction = direction;
        e->screen = e->model->alpha;

    }
}



void steam_think(void){
	if(freezeall) return;	// Don't update animation if special is being executed

    if(!self->animating){
        kill(self);
        return;
    }

    self->base += 1;
    self->a = self->base;
}



// for the "trap" type   7-1-2005  trap start
void trap_think(void){
	if(freezeall) return;	// Don't update animation if special is being executed

    if(self->x < advancex-80 || self->x > advancex+(hRes1+80)){
        //		kill(self);   // 6-2-2005 removed temporarily
        return;
    }

    self->attacking = 1;
    self->nextthink = time + 1;
}
//    7-1-2005  trap end



void steam_spawn(float x, float z, float a){
    entity *e = NULL;

    e = spawn(x, z, a, "Steam");

    if(e==NULL) return;

    e->base = a;
    e->think = steam_think;
    e->screen = 1;
}



void steamer_think(void){
	if(freezeall) return;	// Don't update animation if special is being executed

    if(self->x < advancex-80 || self->x > advancex+(hRes1+80)){
        kill(self);
        return;
    }

    steam_spawn(self->x, self->z, self->a);
    self->nextthink = time + (GAME_SPEED/10) + (rand32()&31);
}



void text_think(void){	// New function so text can be displayed
	if(!self->animating){
		freezeall = 0;	// Done displaying, unfreeze the animations and destroy the textbox
		kill(self);
	}

	return;
}

////////////////////////////////



/*

A brief description of enemy behaviour:

The enemy moves about quite randomly.
If a player is located on the same level (close-to-same Z coordinate),
the enemy will be inclined to move forward and backward only
(still facing the player!).
All of the movement patterns are subject to some randomness.

If, at any time, a player is spotted at a proper location for attack
(e.g. right in front of the enemy), the enemy waits for a half a second
or so, and attacks.

If the player is in mid-air, an uppercut attack should be preferred.

*/


// Commented out for 4-Player code
/*
entity * enemy_find_target(){
    float dx1, dz1;
    float dx2, dz2;


    // One or less players present?
    if(!player[0].ent) return player[1].ent;
    if(!player[1].ent) return player[0].ent;


    dx1 = self->x - player[0].ent->x;
    dz1 = self->z - player[0].ent->z;
    dx2 = self->x - player[1].ent->x;
    dz2 = self->z - player[1].ent->z;


    // Is self located between two players (x-wise)?
    if((dx1<0 && dx2>0) || (dx1>0 && dx2<0)){
        // Stay targeted at the one in front of self
        if(self->direction ? dx1<0 : dx1>0) return player[0].ent;
        return player[1].ent;
    }

    // Target the closest opponent
    if(dx1<0) dx1 = -dx1;
    if(dz1<0) dz1 = -dz1;
    if(dx2<0) dx2 = -dx2;
    if(dz2<0) dz2 = -dz2;

    if(dx1+dz1 < dx2+dz2) return player[0].ent;
    return player[1].ent;
}
*/
/*
entity * enemy_find_target(){  //4player
	int i;								// for loop count, goes from 1 to number of players
	static int targnum;					// Nearest player so far
	float tdist = 0;					// tdist is distance from enemy to current closest player
	int ddir[4];						// Is the enemy facing the player?
	float dx[4], dz[4], dc[4];			// dx is player x distance from computer, 
										// dz is player y (or z) distance, 
										// dc is combined distance from enemy

	for(i=0; i<maxplayers; i++){					// start loop
		ddir[i] = 0;								// reset direction to 0
		if (player[i].ent){							// if player exists
			dx[i] = (self->x - player[i].ent->x);	// check distance from enemy x
			dz[i] = (self->z - player[i].ent->z);	// check distance from enemy y/z
		}
		else{										// if player doesn't exist
			dx[i] = 999;							//fill x/y with large values so player won't be picked as nearest.
			dz[i] = 999;
		}
		// Next line checks if enemy is facing player.
		if(((dx[i]<0 && self->direction) || (dx[i]>0 && !self->direction)) && dx[i]!=999) ddir[i] = 1;
		if(dx[i]<0) dx[i] = -dx[i];					// if distances are negative, make them positive
		if(dz[i]<0) dz[i] = -dz[i];					// do this for both x & z
		dc[i] = dx[i]+dz[i];						// combine the values into one total distance.

		if ((dc[i]<tdist || tdist==0) && ddir[i]){   
			// if current player is closer than old nearest player in memory
			// and enemy is facing them.
			tdist = dc[i];							// change old nearest player distance to current player distance
			targnum = i;								// change current target to that player.
		}
	}
    return player[targnum].ent; // return nearest player as target.
}*/

entity* enemy_find_target()
{
	int i , min, max;
	int index = -1;
	//use the walk animation's range
//	if(self->model->animation[ANI_WALK]){
//        min = self->model->animation[ANI_WALK]->range[0];
//        max = self->model->animation[ANI_WALK]->range[1];
//        if(!max || max < min) max = 999;
//    } else {
        min = 0;
        max = 999;
//    }
	//find the 'nearest' one
    for(i=0; i<MAX_ENTS; i++){
        if(		ent_list[i]->exists && ent_list[i] != self //cant target self
                && (ent_list[i]->type & self->hostile)
                && ent_list[i]->health > 0 //must be alive
/*                && ent_list[i]->think != knife_think //hmm, chasing a projectile?
                && ent_list[i]->think != star_think
                && ent_list[i]->think != bomb_think*/
                && diff(ent_list[i]->x,self->x)+ diff(ent_list[i]->z,self->z) >= min
                && diff(ent_list[i]->x,self->x)+ diff(ent_list[i]->z,self->z) <= max
                //&& ent_list[i]->animation->vulnerable[ent_list[i]->animpos]
          ){
            if(index <0 || 
               ((self->x < ent_list[i]->x) == (self->direction) && // don't turn to the one on the back
               diff(ent_list[i]->x, self->x)+diff(ent_list[i]->z, self->z) < diff(ent_list[index]->x, self->x)+diff(ent_list[index]->z, self->z)))
                index = i;
        }
    }
    if( index >=0) return ent_list[index];
    return NULL;

}


//homing arrow find its target
// type : target type
entity * homing_find_target(int type){
	int i, min, max;
	int index = -1;
	//use the walk animation's range
	if(self->model->animation[ANI_WALK]){
        min = self->model->animation[ANI_WALK]->range[0];
        max = self->model->animation[ANI_WALK]->range[1];
    } else {
        min = 0;
        max = 999;
    }
	//find the 'nearest' one
    for(i=0; i<MAX_ENTS; i++){
        if(		ent_list[i]->exists && ent_list[i] != self //cant target self
                && (ent_list[i]->type & type)
                && diff(ent_list[i]->x,self->x)+ diff(ent_list[i]->z,self->z) >= min
                && diff(ent_list[i]->x,self->x)+ diff(ent_list[i]->z,self->z) <= max
                && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]
          ){
            if(index <0 || diff(ent_list[i]->x, self->x)+diff(ent_list[i]->z, self->z) < diff(ent_list[index]->x, self->x)+diff(ent_list[index]->z, self->z))
                index = i;
        }
    }
    if( index >=0) return ent_list[index];
    return NULL;
}

// 2007 - 2 - 11  by UTunnels
// An enemy tries to find an piackable item, eg, a weapon, a projectile
// The enemy can only try to pick up an item that is pickable for it, 
// That's to say, the item's subtype is touch or the enemy has get animation, 
// for weapon items, the enemy only tries to pick them up if it has that weapon slot.
entity * enemy_find_item(){  
 
	int i;
	int index = -1;
	//find the 'nearest' one
    for(i=0; i<MAX_ENTS; i++){
        if(		ent_list[i]->exists
                && (ent_list[i]->type & TYPE_ITEM)
                && diff(ent_list[i]->x,self->x)+ diff(ent_list[i]->z,self->z)< 300
                && ent_list[i]->animation->vulnerable[ent_list[i]->animpos]
                && (self->model->animation[ANI_GET] || ent_list[i]->model->subtype == SUBTYPE_TOUCH)
                && ((ent_list[i]->model->subtype == SUBTYPE_WEAPON && !self->weapent && self->model->weapon[ent_list[i]->model->weapnum])
                  ||(ent_list[i]->model->subtype == SUBTYPE_PROJECTILE && !self->weapent)
                  ||(ent_list[i]->health && self->health < self->maxhealth && ent_list[i]->model->subtype != SUBTYPE_PROJECTILE && ent_list[i]->model->subtype != SUBTYPE_WEAPON)
                  )
          ){
            if(index <0 || diff(ent_list[i]->x, self->x)+diff(ent_list[i]->z, self->z) < diff(ent_list[index]->x, self->x)+diff(ent_list[index]->z, self->z))
                index = i;
        }
    }
    if( index >=0) return ent_list[index];
    return NULL;
}// end of enemy_find_item()


// Returns 1 if a player was thrown!
int enemy_trymove(float xdir, float zdir){

    entity *other = NULL;
    int wall = 0;
    float coef1;
    float coef2;

	if(self->projectile == 0){

        // Walking

        // Out of bounds? Return to level!
        if(!inair(self)){
        	if(self->zdir && self->z < PLAYER_MIN_Z) self->zdir = self->model->speed/2;
        	if(self->zdir && self->z > PLAYER_MAX_Z) self->zdir = -self->model->speed/2;
		}

        if((self->xdir || self->zdir) && !inair(self)){

            // Don't walk into a hole or walk off platforms into holes
           	if(
				checkhole(self->x + xdir*5, self->z) &&
				checkwall(self->x + xdir*5, self->z) == -1 &&
				(!check_platform(self->x + xdir*5, self->z) ||
				((other = check_platform(self->x + xdir*5, self->z)) &&
				self->base < other->a + other->animation->platform[other->animpos][5]))
			)
				xdir = 0;

           	if(
				checkhole(self->x, self->z + zdir*5) &&
				checkwall(self->x, self->z + zdir*5) == -1 &&
				(!check_platform(self->x, self->z + zdir*5) ||
				((other = check_platform(self->x, self->z + zdir*5)) &&
				self->base < other->a + other->animation->platform[other->animpos][5]))
			)
				zdir = 0;


			if(xdir && (other = find_ent_here(self, self->x + xdir*2, self->z, (TYPE_OBSTACLE | TYPE_TRAP)))){
				if(!other->animation->platform[other->animpos][5]){
					if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
				}
			}

			if(zdir && (other = find_ent_here(self, self->x, self->z + zdir*2, (TYPE_OBSTACLE | TYPE_TRAP)))){
				if(!other->animation->platform[other->animpos][5]){
					if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;
				}
			}
		}
	}

	// Check for obstacles with platform code and adjust base accordingly
	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(other = check_platform(self->x + xdir*10, self->z + zdir*10))) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(other = check_platform(self->x + xdir, self->z + zdir)))) &&
		(self->projectile == 0 ||
		!other->animation->vulnerable[other->animpos]) &&
		self->a < other->a + other->animation->platform[other->animpos][5] &&
		self->a + self->model->height >= other->a
	){

		if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
		if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;

	}

	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(wall = checkwall(self->x + xdir*4, self->z + zdir*4)) >= 0) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(wall = checkwall(self->x + xdir, self->z + zdir)) >= 0)) &&
		self->a < level->walls[wall][7]
	){

		coef1 = (level->walls[wall][1] - self->z) * ((level->walls[wall][2] - level->walls[wall][3]) / level->walls[wall][6]);
		coef2 = (level->walls[wall][1] - self->z) * ((level->walls[wall][4] - level->walls[wall][5]) / level->walls[wall][6]);

		if(self->z > level->walls[wall][1] || self->z < level->walls[wall][1] - level->walls[wall][6]){
			zdir = 0;
		}
		else{

			if(self->x < (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 &&
			self->x > level->walls[wall][0] + level->walls[wall][3] + coef1){

				self->x = level->walls[wall][0] + level->walls[wall][3] + coef1;
			}
			else if(self->x > (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 &&
			self->x < level->walls[wall][0] + level->walls[wall][5] + coef2){

				self->x = level->walls[wall][0] + level->walls[wall][5] + coef2;
			}

			xdir = 0;
		}

	}

    self->x += xdir;
    self->z += zdir;

	if(self->z > PLAYER_MAX_Z) self->z = PLAYER_MAX_Z;	// Added so enemies don't move out of range
	if(self->z < PLAYER_MIN_Z) self->z = PLAYER_MIN_Z;	// Added so enemies don't move out of range

    // Do a throw?
    if(		self->health>0 &&
            self->model->animation[ANI_THROW] &&
            !inair(self) &&
            !self->link &&
            self->animation->move[self->animpos] == 0 &&
            self->animation->movez[self->animpos] == 0 &&
            self->animation != self->model->animation[ANI_FALL] &&	// No more grabbing while falling
            self->animation != self->model->animation[ANI_FALL2] && 
            self->animation != self->model->animation[ANI_FALL3] && 
            self->animation != self->model->animation[ANI_FALL4] && 
			self->animation != self->model->animation[ANI_FALL5] && 
            self->animation != self->model->animation[ANI_FALL6] && 
            self->animation != self->model->animation[ANI_FALL7] && 
			self->animation != self->model->animation[ANI_FALL8] && 
            self->animation != self->model->animation[ANI_FALL9] && 
            self->animation != self->model->animation[ANI_FALL10] && 
            self->animation != self->model->animation[ANI_SHOCK] &&	
            self->animation != self->model->animation[ANI_BURN] &&	
            (other = find_ent_here(self, self->x + xdir, self->z + zdir, self->hostile)) &&
            ((other->type) & (TYPE_PLAYER | TYPE_ENEMY | TYPE_NPC)) &&//cant grab obstacles or sth like that
            !inair(other) &&
            self->a == other->a &&
            !other->invincible &&
            other->animation != other->model->animation[ANI_DODGE] &&	// Can't throw a dodging player
            (self->direction ? other->x>self->x : other->x<self->x)
      ){

        // Reposition both on average height and such
        zdir = (self->z + other->z) / 2;
		self->z = other->z = zdir;

		if((wall == -1 || (wall >= 0 && self->a != level->walls[wall][7])) &&
		(checkwall(self->x - self->model->grabdistance/2, self->z) >= 0 ||
		checkwall(other->x - self->model->grabdistance/2, other->z) >= 0)){

			if(self->x < other->x) other->x = self->x + self->model->grabdistance;
			else self->x = other->x + self->model->grabdistance;
		}
		else if((wall == -1 || (wall >= 0 && self->a != level->walls[wall][7])) &&
		(checkwall(self->x + self->model->grabdistance/2, self->z) >= 0 ||
		checkwall(other->x + self->model->grabdistance/2, other->z) >= 0)){

			if(self->x > other->x) other->x = self->x - self->model->grabdistance;
			else self->x = other->x - self->model->grabdistance;
		}
		else{
	        xdir = (self->x + other->x) / 2;

	        if(self->x < other->x){
	            self->x = xdir - (self->model->grabdistance/2);  // 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
	        else{
	            self->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir - (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
		}

		self->direction = (self->x < other->x);

        if(other->type == TYPE_PLAYER) player[other->playerindex].opponent = self;
        other->attacking = 0;

        // use original throw code if throwframewait not present, kbandressen 10/20/06
        if(self->model->throwframewait == -1){
        	self->think = enemy_throw;
        	ent_set_anim(self, ANI_THROW, 0);
        	ent_unlink(self);

			if(other->model->throwheight) toss(other, other->model->throwheight);
        	else toss(other, other->model->jumpheight);

        	other->direction = self->direction;
			other->projectile = 2;
			other->xdir = (other->direction)?(-other->model->throwdist):(other->model->throwdist);

            if(other->type == TYPE_PLAYER)other->think = player_fall;
            else if(other->type == TYPE_NPC)other->think = npc_fall;
            else other->think = enemy_fall; //grabbing enemy?
            other->damage_on_landing = player[other->playerindex].ent->model->throwdamage;	// use adjusted throwdamage
        	ent_set_anim(other, ANI_FALL, 0);
        	ent_unlink(other);
        }
        // otherwise enemy_throw_wait will be used, kbandressen 10/20/06
        else{
            self->think = enemy_throw_wait;
            ent_set_anim(self, ANI_THROW, 0);
            self->attacking = 1;
            ents_link(self, other);
            
            other->think = prethrow;
            if(other->type == TYPE_PLAYER)player[other->playerindex].opponent = self;
            if(other->model->animation[ANI_GRABBED]) ent_set_anim(other, ANI_GRABBED, 0);	// New grabbed animation
            else ent_set_anim(other, ANI_PAIN, 0);
        }

        return 1;
    }


    // Do a grab?
    if(		self->health>0 &&
            self->model->animation[ANI_GRAB] &&
            !inair(self) &&
            !self->link &&
            self->animation->move[self->animpos] == 0 &&
            self->animation->movez[self->animpos] == 0 &&
            self->animation != self->model->animation[ANI_FALL] &&	// No more grabbing while falling
            self->animation != self->model->animation[ANI_FALL2] &&
            self->animation != self->model->animation[ANI_FALL3] &&
            self->animation != self->model->animation[ANI_FALL4] &&
			self->animation != self->model->animation[ANI_FALL5] &&
            self->animation != self->model->animation[ANI_FALL6] &&
            self->animation != self->model->animation[ANI_FALL7] &&
			self->animation != self->model->animation[ANI_FALL8] &&
            self->animation != self->model->animation[ANI_FALL9] &&
            self->animation != self->model->animation[ANI_FALL10] &&
            self->animation != self->model->animation[ANI_SHOCK] &&
            self->animation != self->model->animation[ANI_BURN] &&
            (other = find_ent_here(self, self->x + xdir, self->z + zdir, self->hostile)) &&
            ((other->type) & (TYPE_PLAYER | TYPE_ENEMY | TYPE_NPC)) &&//cant grab obstacles or sth like that
            !inair(other) &&
            self->a == other->a &&
            !other->invincible &&
            other->animation != other->model->animation[ANI_DODGE] &&	// Can't grab a dodging player
            (self->direction ? other->x>self->x : other->x<self->x)
      ){

		// Reposition both on average height and such
		zdir = (self->z + other->z) / 2;
		self->z = other->z = zdir;

		if(
			(wall == -1 ||
			(wall >= 0 &&
			self->a != level->walls[wall][7])) &&
			(checkwall(self->x - self->model->grabdistance/2, self->z) >=0 ||
			checkwall(other->x - self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x < other->x) other->x = self->x + self->model->grabdistance;
			else self->x = other->x + self->model->grabdistance;
		}
		else if(
			(wall == -1 ||
			(wall >= 0 &&
			self->a != level->walls[wall][7])) &&
			(checkwall(self->x + self->model->grabdistance/2, self->z) >= 0 ||
			checkwall(other->x + self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x > other->x) other->x = self->x - self->model->grabdistance;
			else self->x = other->x - self->model->grabdistance;
		}
		else{
	        xdir = (self->x + other->x) / 2;

	        if(self->x < other->x){
	            self->x = xdir - (self->model->grabdistance/2);  // 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
	        else{
	            self->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir - (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
		}

		self->direction = (self->x < other->x);
		other->direction = !self->direction;

        if(other->type == TYPE_PLAYER)player[other->playerindex].opponent = self;
        other->attacking = 0;

		self->think = enemy_grab;
		self->attacking = 1;
		ent_set_anim(self, ANI_GRAB, 0);
		ents_link(self, other);

		if(other->type == TYPE_PLAYER) {
            other->think = player_grabbed;
            player[other->playerindex].opponent = self;
        }
		else if(other->type == TYPE_NPC) other->think = npc_grabbed;
		else other->think = enemy_grabbed;


		if(other->model->animation[ANI_GRABBED]) ent_set_anim(other, ANI_GRABBED, 0);	// New grabbed animation
		else ent_set_anim(other, ANI_PAIN, 0);
	
        return 1;
    }


    // End of level is blocked?
    if(level->exit_blocked){
        if(self->x > level->width-30-(PLAYER_MAX_Z-self->z)) self->x = level->width-30-(PLAYER_MAX_Z-self->z);
    }

    return 0;
}


void enemy_jump(void){
	if(freezeall || self->frozen) return;

    enemy_trymove(self->xdir, 0);
    if(inair(self)) return;

    self->jumping = 0;
    self->xdir = 0;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->think = enemy_think;
}

//enemy pickup an item
// 2007 -2 -12 by UTunnels
void enemy_pickupitem(entity* other){
    int pickup = 0;
    //weapons
	if(!self->weapent &&
       other->model->subtype == SUBTYPE_WEAPON
       && self->model->animation[ANI_GET])
	{
	    dropweapon(0);  //don't bother dropping the previous one though, scine it won't pickup another
	    self->weapent = other;
	    set_weapon(self, other->model->weapnum, -1);
        ent_set_anim(self, ANI_GET, 0);
	    self->getting = 1;
	    self->xdir=self->zdir=0;//stop moving
	    pickup = 1;
     }
     // projectiles
     else if(!self->weapent &&
     other->model->subtype == SUBTYPE_PROJECTILE
       && self->model->animation[ANI_GET])
     {
        dropweapon(0);
	    self->weapent = other;
        ent_set_anim(self, ANI_GET, 0);
	    self->getting = 1;
	    self->xdir=self->zdir=0;//stop moving
	    pickup = 1;
     }
     // other items
     else if(other->model->subtype != SUBTYPE_WEAPON && 
             other->model->subtype != SUBTYPE_PROJECTILE     )
     {
	    if(self->model->animation[ANI_GET])
	    {
            ent_set_anim(self, ANI_GET, 0);
	        self->getting = 1;
	        self->xdir=self->zdir=0;//stop moving
        }
        if(other->health){
    		self->health += other->health;
    		if(self->health > self->maxhealth) self->health = self->maxhealth;
    		other->health = 0;
    		//if(smp_get >= 0) sound_play_sample(smp_get, 0, savedata.effectvol,savedata.effectvol, 100);
        }
        // else if, TODO: other effects
        // kill that item
        other->think = suicide;
	    other->nextthink = time + GAME_SPEED * 3;
	    pickup = 1;
     }
     // hide it
	 if(pickup) other->z = 100000;
}//end of enemy_pickupitem()

// An enemy turns back
void enemy_turn()
{
    if(!self->animating) {
        if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
        else ent_set_anim(self, ANI_IDLE, 0);
        self->direction = !self->direction;
        self->think = enemy_think;
    }
}

void enemy_think(void){     
    entity *target = enemy_find_target();
    entity *other = NULL;
    float maxspeed;
    float dx,dz;
    int rnum;
    int walk;
    int run = 0;
    int wall;
    int xdir;
    int predir;
    
    predir = self->direction;

	if(self->grabbing && (self->animation == self->model->animation[ANI_IDLE] 
                       || self->animation == self->model->animation[ANI_FAINT] )) self->grabbing = NULL;

    if(self->frozen || freezeall) return;	// There are no players/enemy is frozen/special is being executed?

	if(self->getting){
		 if((self->getting = self->animating)) return;
		 ent_set_anim(self, ANI_IDLE, 0);
	}
        
	// Reset their escapecount if they aren't being spammed anymore.
	if(self->model->escapehits &&
     !(self->animation == self->model->animation[ANI_IDLE] || self->animation == self->model->animation[ANI_FAINT] ||
       self->animation == self->model->animation[ANI_PAIN] || self->animation == self->model->animation[ANI_PAIN2] ||
       self->animation == self->model->animation[ANI_PAIN3] || self->animation == self->model->animation[ANI_PAIN4] ||
	   self->animation == self->model->animation[ANI_PAIN5] || self->animation == self->model->animation[ANI_PAIN6] ||
       self->animation == self->model->animation[ANI_PAIN7] || self->animation == self->model->animation[ANI_PAIN8] ||
	   self->animation == self->model->animation[ANI_PAIN9] || self->animation == self->model->animation[ANI_PAIN10] ||
       self->animation == self->model->animation[ANI_SHOCKPAIN] || self->animation == self->model->animation[ANI_BURNPAIN])){
           self->escapecount = 0;
    }

	if(self->model->animation[ANI_JUMP]){

		// Check to see if there is a wall within jumping distance and within a jumping height

		if(self->direction) xdir = self->x + self->model->animation[ANI_JUMP]->range[0];
		else xdir = self->x - self->model->animation[ANI_JUMP]->range[0];


		if(
			(wall = checkwall(xdir, self->z)) >= 0 &&
			level->walls[wall][7] <= self->a + self->model->animation[ANI_JUMP]->range[1] &&
			!inair(self) &&
			self->a < level->walls[wall][7]
		){

			if(
				(self->direction &&
				self->x < (level->walls[wall][0] * 2 + level->walls[wall][1] + level->walls[wall][4]) / 2) ||
				(!self->direction &&
				self->x > (level->walls[wall][0] * 2 + level->walls[wall][1] + level->walls[wall][4]) / 2)
			){

				ent_set_anim(self, ANI_JUMP, 0);

				if(self->direction) self->xdir = 1.3;
				else self->xdir = -1.3;

				self->jumping = 1;
				self->think = enemy_jump;
				toss(self, self->model->jumpheight);
			}
		}
	}
 
    if(target){ // try to attack
    
    	if(!self->model->noflip) self->direction = (self->x < target->x);	// Don't flip if set
    	
        if(self->direction != predir && self->model->animation[ANI_TURN]){
            self->direction = !self->direction;
            ent_set_anim(self, ANI_TURN, 0);
            self->xdir = self->zdir = 0;
            self->think = enemy_turn;
            return;
        }
    
        if(self->animating && (self->animation == self->model->animation[ANI_WALK] ||
    	   self->animation == self->model->animation[ANI_UP] ||
           self->animation == self->model->animation[ANI_DOWN] ||      // Include ANI_UP, ANI_DOWN animations
           self->animation == self->model->animation[ANI_BACKWALK])){
    
               if((self->direction ? self->xdir<0 : self->xdir>0) &&
                  !self->model->animation[ANI_BACKWALK]) self->animating = -1;         // Walking backwards
    		   
    		   else self->animating = 1;
        }
    
    
        // Target in range?
        if(diff(self->z, target->z) < (self->model->grabdistance/3)){
    
            // Target jumping? Try uppercut!
            if(target->jumping && self->model->animation[ANI_UPPER] &&
                    (self->direction ?
                     self->x<target->x-10 && target->x<self->x+120 :
                     self->x>target->x+10 && target->x>self->x-120 )){
    
                // Don't waste any time!
                ent_set_anim(self, ANI_UPPER, 0);
                self->think = enemy_attack;
                return;
            }
    
            if(self->direction ?
                    (self->model->animation[ANI_ATTACK1] && target->x > self->x+self->model->animation[ANI_ATTACK1]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK1]->range[1]) ||
                    (self->model->animation[ANI_ATTACK2] && target->x > self->x+self->model->animation[ANI_ATTACK2]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK2]->range[1]) ||
                    (self->model->animation[ANI_ATTACK3] && target->x > self->x+self->model->animation[ANI_ATTACK3]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK3]->range[1]) ||
                    (self->model->animation[ANI_ATTACK4] && target->x > self->x+self->model->animation[ANI_ATTACK4]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK4]->range[1]) ||
                    (self->weapent &&
                    (self->model->animation[ANI_THROWATTACK] && target->x > self->x+self->model->animation[ANI_THROWATTACK]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_THROWATTACK]->range[1]))
                    :
                    (self->model->animation[ANI_ATTACK1] && target->x < self->x-self->model->animation[ANI_ATTACK1]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK1]->range[1]) ||
                    (self->model->animation[ANI_ATTACK2] && target->x < self->x-self->model->animation[ANI_ATTACK2]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK2]->range[1]) ||
                    (self->model->animation[ANI_ATTACK3] && target->x < self->x-self->model->animation[ANI_ATTACK3]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK3]->range[1]) ||
                    (self->model->animation[ANI_ATTACK4] && target->x < self->x-self->model->animation[ANI_ATTACK4]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK4]->range[1]) ||
                    (self->weapent &&
    	            (self->model->animation[ANI_THROWATTACK] && target->x < self->x-self->model->animation[ANI_THROWATTACK]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_THROWATTACK]->range[1]))
              ){
    
    			if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
    			else ent_set_anim(self, ANI_IDLE, 0);
                self->zdir = self->xdir = 0;
                self->think = enemy_prepare;
    			self->stalltime = time + (GAME_SPEED/4) + (rand32()%(GAME_SPEED/10) - self->aggression);
    
                return;
            }
    
            if((self->model->animation[ANI_JUMPATTACK] || self->model->animation[ANI_JUMPATTACK2])
         /*           && (self->direction ?
                        ((self->model->jumpheight == 4 && target->x > self->x+150 && target->x < self->x+200) ||
                        (self->model->jumpheight != 4 && target->x > self->x+(self->model->animation[ANI_JUMPATTACK]->range[1]-10) &&
                        target->x < self->x+(self->model->animation[ANI_JUMPATTACK]->range[1]+5))):  //30-12-2004  changed to be affected by range
                        ((self->model->jumpheight == 4 && target->x < self->x-150 && target->x > self->x-200) ||
                        (self->model->jumpheight != 4 && target->x < self->x-(self->model->animation[ANI_JUMPATTACK]->range[1]-10) &&
                        target->x > self->x-(self->model->animation[ANI_JUMPATTACK]->range[1]+5))))  //30-12-2004  Attack distance for jump attack
                        */
                        // lol, comment them off
              ){
    
                rnum = 0;
    
                if(!self->model->animation[ANI_JUMPATTACK]) rnum = 1;
                else if(self->model->animation[ANI_JUMPATTACK2] && (rand32()&1)) rnum = 1;
    
                if(rnum==0 &&
                // do a jumpattack
                (self->direction ?
                    ((self->model->jumpheight == 4 && target->x > self->x+150 && target->x < self->x+200) ||
                    (self->model->jumpheight != 4 && target->x > self->x+(self->model->animation[ANI_JUMPATTACK]->range[0]-10) &&
                    target->x < self->x+(self->model->animation[ANI_JUMPATTACK]->range[1]+5))):  
                    ((self->model->jumpheight == 4 && target->x < self->x-150 && target->x > self->x-200) ||
                    (self->model->jumpheight != 4 && target->x < self->x-(self->model->animation[ANI_JUMPATTACK]->range[0]-10) &&
                    target->x > self->x-(self->model->animation[ANI_JUMPATTACK]->range[1]+5))))
                    ){
        
                    ent_set_anim(self, ANI_JUMPATTACK, 0);
                    if(self->direction) self->xdir = 1.3;
                    else self->xdir = -1.3;
                    
                }
                else if(rnum==1 &&
                // do a jumpattack2
                (self->direction ?
                    (target->x > self->x+(self->model->animation[ANI_JUMPATTACK2]->range[0]-10) &&
                    target->x < self->x+(self->model->animation[ANI_JUMPATTACK2]->range[1]+5)):  
                    (target->x < self->x-(self->model->animation[ANI_JUMPATTACK2]->range[0]-10) &&
                    target->x > self->x-(self->model->animation[ANI_JUMPATTACK2]->range[1]+5)))
                ) {
                    ent_set_anim(self, ANI_JUMPATTACK2, 0);
                    self->xdir = 0;
                } else {
                   rnum = -1;
                }
                
                if(rnum >= 0){
                    self->think = enemy_jumpattack;
                    self->attacking = 1;
                    self->jumping = 1;
        
                    toss(self, self->model->jumpheight);
        
                    return;
                }
            }
    
        }
    }// end of attack section

    // pick up code , 2007 - 2 - 11   by UTunnels
    if((other = enemy_find_item()))   //try to pick up an item, if possible
        if(!self->model->noflip) self->direction = (self->x < other->x);
    
    if(self->direction != predir && self->model->animation[ANI_TURN]){
        self->direction = !self->direction;
        ent_set_anim(self, ANI_TURN, 0);
        self->xdir = self->zdir = 0;
        self->think = enemy_turn;
        return;
    }

    if(self->stalltime < time ){
        if(other){
        // if there's an item to pick up, move towards it.
			maxspeed = self->model->speed*1.5;
            dx = diff(self->x, other->x);
            dz = diff(self->z, other->z);
            if(!dz && !dx) self->xdir = self->zdir = 0;
            else {
                self->xdir = maxspeed * dx / (dx+dz);
                self->zdir = maxspeed * dz / (dx+dz);
            }
            if(self->direction!=1) self->xdir = -self->xdir;
            if(self->z > other->z) self->zdir = -self->zdir;
            
            if(!self->model->nomove)
            {
     			if(self->z > other->z && self->model->animation[ANI_UP]){	// Used for enemy up animation
     				ent_set_anim(self, ANI_UP, 0);
     			}
     			else if(other->z > self->z && self->model->animation[ANI_DOWN]){	// Used for enemy down animation
     				ent_set_anim(self, ANI_DOWN, 0);
     			}
     			else ent_set_anim(self, ANI_WALK, 0);  
            }
            walk = 1;
        }
        else if(target && self->model->subtype == SUBTYPE_CHASE){
        // start chasing the player
            dx = diff(self->x, target->x);
            dz = diff(self->z, target->z);
            // if target is far away, run instead of walk
            if(dx+dz > 200 && self->model->animation[ANI_RUN]) {maxspeed = self->model->runspeed*1.5; run = 1;}
            else maxspeed = self->model->speed*1.5;
            
            if(!dz && !dx) self->xdir = self->zdir = 0;
            else {
                self->xdir = maxspeed * dx / (dx+dz);
                self->zdir = maxspeed * dz / (dx+dz);
            }
            if(self->direction!=1) self->xdir = -self->xdir;
            if(self->z > target->z) self->zdir = -self->zdir;
            
            if(!self->model->nomove)
            {
                if(run ) ent_set_anim(self, ANI_RUN, 0);
                else {
         			if(self->z > target->z && self->model->animation[ANI_UP]){	// Used for enemy up animation
         				ent_set_anim(self, ANI_UP, 0);
         			}
         			else if(target->z > self->z && self->model->animation[ANI_DOWN]){	// Used for enemy down animation
         				ent_set_anim(self, ANI_DOWN, 0);
         			}
         			else ent_set_anim(self, ANI_WALK, 0);  
                }
            }
            walk = 1;
        }
        else if(target){
            // Decide next direction... randomly.
    
            walk = 0;
            self->xdir = self->zdir = 0;
    
            rnum = rand32();
            if(((rnum & 7) < 3) || diff(self->x, target->x) > (hRes1/2) ){
                // Walk forward
                if(self->direction==1) self->xdir = self->model->speed;
                else self->xdir = -self->model->speed;
                walk = 1;
            }
            else if((rnum & 7) > 4){
                // Walk backward
                if(self->direction==1) self->xdir = -self->model->speed/2;
                else self->xdir = self->model->speed/2;
                walk = -1;
            }
    
            rnum = rand32();
            if((rnum & 7) < 2){
                // Move up
                self->zdir = -self->model->speed/2;
                walk |= 1;
            }
            else if((rnum & 7) > 5){
                // Move down
                self->zdir = self->model->speed/2;
                walk |= 1;
            }
    
     		if(walk && !self->model->nomove){
     			if(self->z - target->z > 5 && self->model->animation[ANI_UP]){	// Used for enemy up animation
     				ent_set_anim(self, ANI_UP, 0);
     			}
     			else if(target->z - self->z > 5 && self->model->animation[ANI_DOWN]){	// Used for enemy down animation
     				ent_set_anim(self, ANI_DOWN, 0);
     			}
    			else if(walk<0 && self->model->animation[ANI_BACKWALK]){
    				ent_set_anim(self, ANI_BACKWALK, 0);
    			}
     			else ent_set_anim(self, ANI_WALK, 0);
     			// if(walk<0) self->animating = -1;
     		}
    		else{
    			if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
    			else ent_set_anim(self, ANI_IDLE, 0);
    			self->xdir = self->zdir = 0;
    		}
        } else { // no target to pick up, just relex and idle
			if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
			else ent_set_anim(self, ANI_IDLE, 0);
			self->xdir = self->zdir = 0;
        }
        //end of if

        self->stalltime = time + GAME_SPEED - self->aggression;
    }

    // new pick up code by UTunnels
		if(
		/*walk &&*/  //always pickup both when walking or idle
		(other && other == find_ent_here(self, self->x, self->z, TYPE_ITEM)) && //won't pickup an item that is not previous one
			!other->blink &&
		(diff(self->a - (self->animation->seta[self->animpos] >= 0) * self->animation->seta[self->animpos] , other->a)<0.1)
			)
		{
        enemy_pickupitem(other);
    }

    enemy_trymove(self->xdir, self->zdir);
}



void enemy_drop(void){
    if(inair(self)) return;
    self->think = enemy_think;
    if(self->health<=0) kill(self);
}



//  1-10-05   new enemy spawning function
void enemy_spawn(void){
    if(self->animating) return;
    self->think = enemy_think;
}
//



void enemy_jumpattack(void){
	if(freezeall || self->frozen) return;

    if(self->animpos != self->lastanimpos && self->animpos == self->animation->throwframe){
        star_spawn(self->x + (self->direction ? 56 : -56), self->z, self->a+67, self->direction);
    }

    self->lastanimpos = self->animpos;

    enemy_trymove(self->xdir, 0);
    if(inair(self)) return;

    self->attacking = 0;
    self->jumping = 0;
    self->xdir = self->zdir = 0;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->think = enemy_think;
}


// added by kbandressen, 10/20/06
void enemy_throw_wait(void){

	entity *other = NULL;

    // returns enemy to idle animation if link is broken before other is actually thrown
    if(!(self->link)){
        if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	    else ent_set_anim(self, ANI_IDLE, 0);
	    self->think = enemy_think;
	    return;
     }
    if(self->animpos != self->model->throwframewait) return;
    
    other = self->link;
    if(other->model->throwheight) toss(other, other->model->throwheight);
    else toss(other, other->model->jumpheight);

    other->direction = self->direction;
    other->projectile = 2;
    other->xdir = (other->direction)?(-other->model->throwdist):(other->model->throwdist);

    if(other->type == TYPE_PLAYER) other->think = player_fall;
    else if(other->type == TYPE_NPC) other->think = npc_fall;
    else if(other->type == TYPE_ENEMY) other->think = enemy_fall;
    
    other->damage_on_landing = player[other->playerindex].ent->model->throwdamage;	// use adjusted throwdamage
    ent_set_anim(other, ANI_FALL, 0);
    ent_unlink(other);
    self->think = enemy_throw;
}


void enemy_throw(void){
    if(self->animating) return;
    self->think = enemy_think;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
}



void enemy_grab(void){
    // if(self->link) return;
    if(self->animating) return;
    ent_unlink(self);
    self->think = enemy_think;
    self->attacking = 0;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
}



void enemy_grabbed(void){
    // Escape?
    if(time >= self->stalltime && self->model->animation[ANI_SPECIAL]){
        ent_unlink(self);
        ent_set_anim(self, ANI_SPECIAL, 0);
        self->attacking = 1;
        self->think = enemy_attack;
        return;
    }

    // Just check if we're still grabbed...
    if(self->link) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->xdir = self->zdir = 0;
    self->think = enemy_think;
}



void enemy_runoff(){
    entity *target = enemy_find_target();

    if(!target || self->frozen || freezeall) return;	// There are no players?
	if(!self->model->noflip) self->direction = (self->x < target->x);
    if(self->direction) self->xdir = -self->model->speed/2;
    else self->xdir = self->model->speed/2;
    enemy_trymove(self->xdir, 0);

    if(time > self->stalltime) self->think = enemy_think;
}



void enemy_attack(){
    entity *target = NULL;

	if(self->frozen || freezeall) return;

    self->attacking = 1;

	// If they're attacking, reset their escapecount.
    if(self->model->escapehits) self->escapecount = 0;

	if(inair(self) && !freezeall){	// Now enemies can move with their jumpframes
		if(self->direction) enemy_trymove((self->animation->moveflag > 0) * 1.3, 0);	// If moveflag > 0 will move 1.3 else 0
		else enemy_trymove((self->animation->moveflag > 0) * -1.3, 0);
		return;
	}

    if(self->animating) return;

    self->attacking = 0;
    self->xdir = 0;
    self->zdir = 0;

    target = enemy_find_target();


    if(target && !self->model->nomove && diff(self->x, target->x)<80 && (rand32()&3)){
		ent_set_anim(self, ANI_WALK, 0);
        self->think = enemy_runoff;
    }
    else{
		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);

        self->think = enemy_think;
    }


    self->stalltime = time + GAME_SPEED - self->aggression;
}



// Idle a bit before attacking
void enemy_prepare(){
    entity *target = enemy_find_target();
    int pickable[5];

    if(!target || self->frozen){	// Added so enemies don't attack while frozen
        // There are no players/self frozen?
        self->think = enemy_think;
        return;
    }
    if(!self->model->noflip) self->direction = (self->x < target->x);

    // Wait...
    if(time < self->stalltime) return;


    // Pick an attack
    pickable[0] = pickable[1] = pickable[2] = pickable[3] = pickable[4] = 0;

    if(self->model->animation[ANI_ATTACK1] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK1]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK1]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK1]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK1]->range[1])){

        pickable[0] = 1;
    }
    if(self->model->animation[ANI_ATTACK2] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK2]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK2]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK2]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK2]->range[1])){

        pickable[1] = 1;
        if((rand32() & 31) < 10) pickable[0] = 0;
    }
    if(self->model->animation[ANI_ATTACK3] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK3]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK3]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK3]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK3]->range[1])){

        pickable[2] = 1;
        if((rand32() & 31) < 10) pickable[1] = 0;
    }
    if(self->model->animation[ANI_ATTACK4] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK4]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK4]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK4]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK4]->range[1])){

        pickable[3] = 1;
        if((rand32() & 31) < 10) pickable[2] = 0;
    }
    if(self->weapent && self->weapent->model->subtype == SUBTYPE_PROJECTILE && self->model->animation[ANI_THROWATTACK] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_THROWATTACK]->range[0] &&
            target->x < self->x+self->model->animation[ANI_THROWATTACK]->range[1]
            : target->x < self->x-self->model->animation[ANI_THROWATTACK]->range[0] &&
            target->x > self->x-self->model->animation[ANI_THROWATTACK]->range[1])){

        pickable[4] = 1;
        if((rand32() & 31) < 10) pickable[3] = 0;
    }

    if(pickable[0]){
        ent_set_anim(self, ANI_ATTACK1, 0);
        self->think = enemy_attack;
        return;
    }
    if(pickable[1]){
        ent_set_anim(self, ANI_ATTACK2, 0);
        self->think = enemy_attack;
        return;
    }
    if(pickable[2]){
        ent_set_anim(self, ANI_ATTACK3, 0);
        self->think = enemy_attack;
        return;
    }
    if(pickable[3]){
        ent_set_anim(self, ANI_ATTACK4, 0);
        self->think = enemy_attack;
        return;
    }
    if(pickable[4]){
        ent_set_anim(self, ANI_THROWATTACK, 0);
        self->think = enemy_attack;
        return;
    }

    // No attack to perform, return to normal
    self->think = enemy_think;
}



void enemy_fall(void){
	entity *dust = NULL;	// New "dust" entity type for when characters hit the ground

	if(freezeall) return;	// Added incase enemy is in midair when a special is executed

	if(self->frozen){	// If falling, unfreeze enemy
		if(self->colourmap == self->model->colourmap[self->model->fmap - 1]) self->colourmap = self->map;
		self->frozen = 0;
		self->freezetime = 0;
	}
/*
	if(self->projectile == 1){
        if(self->direction) enemy_trymove(-2.5, 0);
        else enemy_trymove(2.5, 0);
    }
    else if(self->projectile == 2){
		if(self->direction) enemy_trymove(-self->model->throwdist, 0);
		else enemy_trymove(self->model->throwdist, 0);
	}
    else{
        if(self->direction) enemy_trymove(-1.2, 0);
        else enemy_trymove(1.2, 0);
    }*/
    enemy_trymove(self->xdir, 0);

    // Still falling?
    if(inair(self)){
        if(self->a < PIT_DEPTH && self->health<=0) kill(self);
        return;
    }

    // Landed
    if(self->projectile > 0){
        if(!self->model->noquake) quake = 4;	// Don't shake if specified
        if(self->takedamage && self->damage_on_landing){
            self->takedamage(self, self->damage_on_landing, 0, ATK_NORMAL);
        }
        self->damage_on_landing = 0;
        self->projectile = 0;
    }

    if(self->animation == self->model->animation[ANI_SHOCK] || self->animation == self->model->animation[ANI_BURN]){ // New animations
		ent_set_anim(self, ANI_FALL, 0);	// Set to normal fall animation and change to last frame
		self->currentsprite = self->animation->sprite[self->animation->numframes - 1];
		self->animpos = self->animation->numframes - 1;
	}

    // Hard landing? Quake and bounce!
    if(self->tossv < -2){

		if(self->model->dust[0]){
			dust = spawn(self->x, self->z, self->a, self->model->dust);
			dust->autokill = 1;
		}

        if(!self->model->noquake) quake = 2;	// Don't shake if specified

        if(!self->owner && self->type != TYPE_SHOT && smp_fall >= 0)
			sound_play_sample(smp_fall, 0, savedata.effectvol,savedata.effectvol, 100);

        toss(self, self->model->jumpheight/4);
#if DC
		int i;
		for(i=0; i<maxplayers; i++) dcpad_rumblepower[i] = 75;
#endif
        return;
    }

	// Drop Weapon due to Enemy Falling.
	if(self->model->weaploss == 1) dropweapon(1);

	if(self->boss && level_completed) tospeedup = 1;

    // Pause a bit...
    self->think = enemy_lie;
    self->stalltime = time + GAME_SPEED - self->model->risetime;
}


// Commented out for 4 player code
/*
void enemy_lie(void){
    entity * target = NULL;

    // Died?
    if(self->health <= 0){

        if(self->model->falldie) ent_set_anim(self, ANI_DIE, 0);  //6-2-2005 fall and then play die animation?

	    if(!self->model->nodieblink || (self->model->nodieblink == 1 && !self->animating)){	// Now have the option to blink or not
    		self->think = suicide;
    		self->blink = 1;
    		self->nextthink = time + GAME_SPEED * 2;
		}
		else if(self->model->nodieblink == 2 && !self->animating) self->think = suicide;

        return;
    }

     // Stall
    if(time < self->stalltime){
		if(freezeall) self->stalltime += 2;
		return;
	}

	// Used to pick a target for a riseattack

	if(
		player[1].ent &&
		diff(self->z, player[1].ent->z) < (self->model->grabdistance / 3) &&	// Used so closer player is attacked
		diff(self->x, player[1].ent->x) < diff(self->x, player[0].ent->x)
	)

		target = player[1].ent;

	else target = player[0].ent;

	self->think = enemy_rise;

    // Get up again

    // New riseattack section for a quicker counter when a player is hanging around waiting to cheap-shot the enemy
	if(
		self->model->animation[ANI_RISEATTACK] &&
		diff(self->z, target->z) < (self->model->grabdistance/3) &&	// If the target is at the correct z
		((target->x > self->x+self->model->animation[ANI_RISEATTACK]->range[0] &&
		target->x < self->x+self->model->animation[ANI_RISEATTACK]->range[1]) ||
		(target->x < self->x-self->model->animation[ANI_RISEATTACK]->range[0] &&
		target->x > self->x-self->model->animation[ANI_RISEATTACK]->range[1]))
	){

		self->direction = (target->x > self->x);	// Stands up and swings in the right direction depending on chosen target
		self->attacking = 1;
		ent_set_anim(self, ANI_RISEATTACK, 0);
	}
	else ent_set_anim(self, ANI_RISE, 0);

	self->xdir = self->zdir = 0;
}
*/

// 4 player code
void enemy_lie(entity *other, int force, int drop, int type){
    entity * target;

	int i=0;  //4player
	int targnum=0;  //4player
	float dx[4], dz[4], dc[4];  //4player
	float tdist=0;  //4player

    // Died?
    if(self->health <= 0){

		// Drop Weapon due to death.
		if(self->model->weaploss <= 2) dropweapon(1);

		if(self->model->death == 2) set_death(self, type, 0);

	    if(!self->model->nodieblink || (self->model->nodieblink == 1 && !self->animating)){	// Now have the option to blink or not
    		self->think = suicide;
    		self->blink = 1;
    		self->nextthink = time + GAME_SPEED * 2;
		}
		else if(self->model->nodieblink == 2 && !self->animating) self->think = suicide;

        return;
    }

     // Stall
    if(time < self->stalltime){
		if(freezeall) self->stalltime += 2;
		return;
	}
	// Used to pick a target for a riseattack  //



	    for(i=0; (i<maxplayers-1); i++)  //4player
		{ 
			if (player[i].ent)	
			{
				dx[i+1] =(self->x - player[i].ent->x);
				dz[i+1] =(self->z - player[i].ent->z);
			}
			else 
			{
				dx[i+1] =999;
				dz[i+1] =999;
			}

			if (dx[i+1]<0) (dx[i+1]=-dx[i+1]);
			if (dz[i+1]<0) (dz[i+1]=-dz[i+1]);
			dc[i+1]=(dx[i+1]+dz[i+1]);
			if (dc[i+1]<tdist || tdist ==0) 
			{
				tdist=dc[i+1];
				targnum=i;
			}

		}
	target=player[targnum].ent;
	self->think = enemy_rise;

    // Get up again

    // New riseattack section for a quicker counter when a player is hanging around waiting to cheap-shot the enemy
	if(
		self->model->animation[ANI_RISEATTACK] &&
		diff(self->z, target->z) < (self->model->grabdistance/3) &&	// If the target is at the correct z
		((target->x > self->x+self->model->animation[ANI_RISEATTACK]->range[0] &&
		target->x < self->x+self->model->animation[ANI_RISEATTACK]->range[1]) ||
		(target->x < self->x-self->model->animation[ANI_RISEATTACK]->range[0] &&
		target->x > self->x-self->model->animation[ANI_RISEATTACK]->range[1]))
	){

		self->direction = (target->x > self->x);	// Stands up and swings in the right direction depending on chosen target
		self->attacking = 1;
		ent_set_anim(self, ANI_RISEATTACK,0);
	}
	else ent_set_anim(self, ANI_RISE,0);

	self->xdir = self->zdir = 0;
}




void enemy_rise(void){
    if(self->animating) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->xdir = self->zdir = 0;
    self->think = enemy_think;
}




void enemy_pain(void){

	// Enemies can now escape non-knockdown spammage (What a weird phrase)!
    if((self->escapecount > self->model->escapehits) && !inair(self) && self->model->animation[ANI_SPECIAL2]){
        // Counter the player!
		ent_set_anim(self, ANI_SPECIAL2, 0);
		self->attacking = 1;
		self->escapecount = 0;
		self->think = enemy_attack;
		return;
    }

    if(self->animating || inair(self)) return;

    if(self->link){

		if(self->model->animation[ANI_GRABBED]) ent_set_anim(self, ANI_GRABBED, 0);	// New grabbed animation
		else ent_set_anim(self, ANI_PAIN, 0);

        self->think = enemy_grabbed;
    }
    else{
		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);

        self->think = enemy_think;
    }
}



void enemy_block(void){	// New function for when an enemy blocks an attack
	if(self->animating) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

	self->think = enemy_think;
}



void enemy_takedamage(entity *other, int force, int drop, int type){
    entity *item = NULL;	// 7-1-2005 Enemies drop items
    int i;

    if(self->a<=PIT_DEPTH && self->health<=0){
        // Don't scream twice
        kill(self);
        return;
    }

    if(self->model->animal) drop = 1;// inserted by UTunnels
	
	// Drop Weapon due to being hit.
	if(self->model->weaploss <= 0) dropweapon(1);

    self->pain_time = time + (GAME_SPEED / 5);

	if((type != ATK_FREEZE || (type == ATK_FREEZE && self->frozen)) && !self->model->noflip && !inair(self)){	// Added !inair(self) so enemies don't change directions mid-air when a boss is defeated
    	if(self->x < other->x) self->direction = 1;
    	else if(self->x > other->x) self->direction = 0;
	}

    // nopain flag
    if(self->health <= force || drop || !self->model->nopain){
		self->attacking = 0;

        if(other != self->link) ent_unlink(self);

        if(inair(self)) drop = 1;       // If in the air, automatically fall (freeze will override later if not frozen)
    }
    if(type == ATK_BLAST){
        self->projectile = 1;
        drop = 1;
    }
	if(type == ATK_STEAL){	// New steal attack adds health removed from opponent
		if(self->health >= force) other->health += force;
		else other->health += self->health;
		if(other->health > other->maxhealth) other->health = other->maxhealth;
	}
    if(type == ATK_FREEZE && !self->frozen && !self->owner && !self->model->nomove){	// New freeze attack - If not frozen, freeze entity unless it's a projectile
		self->frozen = 1;
		if(self->freezetime == 0) self->freezetime = time + other->animation->attack_length[other->animpos];
		if(self->model->fmap) ent_set_colourmap(self, self->model->fmap);
		drop = 0;
	}
	else if(self->frozen) drop = 1; // If frozen and any other attack is executed, drop the opponent

	if(self->model->nomove || self->model->nodrop) drop = 0;	// Static enemies/nodrop enemies cannot be knocked down

    self->health -= force;

	// mprate can also control the MP recovered per hit.
    if(magic_type == 1){
        if(other->model->mprate) other->mp += other->model->mprate;
        else other->mp++;

        if(other->mp > other->maxmp) other->mp = other->maxmp;
    }

    if(other->type == TYPE_PLAYER || other->type == TYPE_OBSTACLE || other->type == TYPE_SHOT){	// Added obstacle so explosions can hurt enemies
        player[other->playerindex].opponent = self;
        self->playerindex = other->playerindex;
        addscore(other->playerindex, force*self->multiple);	// New multiple variable
#if DC
		dcpad_rumblepower[other->playerindex] = force*2;
#endif
    }

	if(self->toexplode == 2) return;

    if(self->health <= 0){
		addscore(other->playerindex, self->score);	// Add score to the player
        self->nograb = 1;

        if(self->model->diesound>= 0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100);

        // enemies can spawn item - including traps and other enemies!   7-1-2005  ltb 1-18-05 cleaned
        if(self->item[0] && (!self->if2p || count_ents(TYPE_PLAYER)>1) && (!self->if3p || count_ents(TYPE_PLAYER)>2)&& (!self->if4p || count_ents(TYPE_PLAYER)>3)){ //4player

            if(self->x < (int)advancex) item = spawn((int)advancex + 10, self->z, self->a, self->item);
            else if(self->x > (int)advancex + (hRes1)) item = spawn((int)advancex + (hRes1 - 10), self->z, self->a, self->item);
            else item = spawn(self->x, self->z, self->a, self->item);
            //flip the item, 2007-2-11 by UTunnels
            item->direction = self->direction;
            if(item->type == TYPE_ENEMY){
                item->boss = self->boss;
                self->boss = 0;

                if(self->itemhealth > 0) item->health = self->itemhealth;

                ent_set_colourmap(item, self->itemmap);

                if(self->itemalias[0]) strncpy(item->name, self->itemalias, MAX_NAME_LEN);
                
                if(item->model->animation[ANI_RESPAWN]){
                    ent_set_anim(item, ANI_RESPAWN, 0);
                    item->think = enemy_spawn;
                }
                else if(item->model->animation[ANI_SPAWN]){
                    ent_set_anim(item, ANI_SPAWN, 0);
                    item->think = enemy_spawn;
                }
				else item->think = enemy_drop;

				if(self->itemtrans) item->screen = self->itemtrans;

                item->takedamage = enemy_takedamage;

                if(!item->model->speed && !item->model->nomove) item->model->speed = 1;
                else if(item->model->nomove) item->model->speed = 0;

                if(item->model->subtype==SUBTYPE_NOTGRAB) item->nograb = 1;
                else if(item->model->subtype==SUBTYPE_BIKER){
                    item->think = biker_drive;
                    item->boss = self->boss;
                    self->boss = 0;
                    item->takedamage = biker_takedamage;
                }
                else if(item->model->subtype==SUBTYPE_ARROW) item->think = arrow_fly;
            }
            else if(item->type == TYPE_TRAP){
                item->think = trap_think;
            }
        }
        // end

        for(i=0; i<MAX_ENTS; i++){	// ltb 1-17-05  multiple bosses
            if((ent_list[i]->boss) && (ent_list[i] != self)) self->boss = 0;
        }

        if(self->boss){
            kill_all_enemies();
            level_completed = 1;
        }

        // Fell in a hole
        if(self->a < PIT_DEPTH){
            kill(self);
            return;
        }
    }


    // Don't animate or fall if hurt by self, since
    // it means self fell to the ground already. :)
    if(other==self && self->damage_on_landing){
        addscore(other->playerindex, force);
        return;
    }

	// Enemies can now use SPECIAL2 to escape cheap attack strings!
	if(self->model->escapehits){
        if(drop) self->escapecount = 0;
		else self->escapecount++;
    }

	// New pain, fall, and death animations. Also, the nopain flag.
    if(drop || self->health <= 0 || (inair(self) && !self->frozen)){	// So enemies frozen are frozen in midair

        ent_unlink(self);

		// Drop Weapon due to death.
		if(self->model->weaploss <= 2) dropweapon(1);

        if(self->health <= 0 && self->model->death == 1) set_death(self, type, 0);
		else{
			// Now if no fall/die animations exist, entity simply disapears
			if(!self->model->animation[ANI_FALL]  && !self->model->animation[ANI_FALL2] &&
			   !self->model->animation[ANI_FALL3] && !self->model->animation[ANI_FALL4] &&
			   !self->model->animation[ANI_FALL5] && !self->model->animation[ANI_FALL6] &&
			   !self->model->animation[ANI_FALL7] && !self->model->animation[ANI_FALL8] &&
			   !self->model->animation[ANI_FALL9] && !self->model->animation[ANI_FALL10]){
				kill(self);
				return;
			}
			self->xdir = (self->projectile==1)?((self->direction)?(-2.5):2.5):((self->direction)?(-1.2):1.2);
            toss(self, self->model->jumpheight/1.6 + randf(self->model->jumpheight/4));
			set_fall(self, type, 1);
        }
		self->think = enemy_fall;
    }
	// Don't change to pain animation if frozen 
    else if(!self->frozen && !self->model->nopain){   
		set_pain(self, type, 1);
        self->think = enemy_pain;
    }
}



//  for the arrow type  7-1-2005 Arrow start
void arrow_fly(void){
	int wall;

	if(freezeall) return;	// Don't move if special is being executed

    self->attacking = 1;

    if((!self->direction && self->x < advancex - 200) || (self->direction && self->x > advancex + (hRes1+200))){
        kill(self);
        return;
    }

	if(!inair(self)){
	    if(self->direction) self->x += self->model->speed;	// Now arrows can have custom speeds
	    else self->x -= self->model->speed;	// Now arrows can have custom speeds
	}

	if(self->model->animation[ANI_FALL]){	// Added so projectiles bounce off blocked exits
		if((level->exit_blocked && self->x > level->width-30-(PLAYER_MAX_Z-self->z)) ||
		((wall = checkwall(self->x, self->z)) >= 0 && self->a < level->walls[wall][7])){
			self->attacking = 0;
			self->health -= 100000;
			self->projectile = 0;
			self->xdir = (self->direction)?(-1.2):1.2;
			self->think = enemy_fall;
			self->damage_on_landing = 0;
			toss(self, 2.5 + randf(1));
			ent_set_anim(self, ANI_FALL, 1);
		}
	}

    self->nextthink = time + THINK_SPEED / 2;
}
//   7-1-2005 Arrow end.


void biker_drive(void){
	if(freezeall) return;	// Don't move if a special is being executed
    self->attacking = 1;
    if(self->direction){
        self->x += self->xdir;
        if(self->x > advancex+(hRes1+200)){
            self->z = PLAYER_MIN_Z + randf(PLAYER_MAX_Z-PLAYER_MIN_Z);
            self->direction = 0;
            self->attack_id = 0;

            if(smp_bike >= 0) sound_play_sample(smp_bike, 0, savedata.effectvol,savedata.effectvol, 100);

            self->xdir = 1.7 + randf(0.6);
        }
    }
    else{
        self->x -= self->xdir;
        if(self->x < advancex-200){
            self->z = PLAYER_MIN_Z + randf(PLAYER_MAX_Z-PLAYER_MIN_Z);
            self->direction = 1;
            self->attack_id = 0;

            if(smp_bike >= 0) sound_play_sample(smp_bike, 0, savedata.effectvol,savedata.effectvol, 100);

            self->xdir = 1.7 + randf(0.6);
        }
    }

	self->nextthink = time + THINK_SPEED / 2;
}



void bike_crash(void){
    if(self->direction) self->x += 2;
    else self->x -= 2;

    self->nextthink = time + THINK_SPEED / 2;

#if DC
	int i;
	for(i=0; i<maxplayers; i++)	dcpad_rumblepower[i] = 100;
#endif

    if(self->x < advancex-100 || self->x > advancex+(hRes1+100)) kill(self);
}



void biker_takedamage(entity *other, int force, int drop, int type){  // 7-1-2005 - whole section replaced with lord ball's drop item code

    entity *driver = NULL;

    if(other->type == TYPE_PLAYER || other->type == TYPE_OBSTACLE || other->type == TYPE_SHOT){	// Added so obstacles can damage bikers
        player[other->playerindex].opponent = self;
        self->playerindex = other->playerindex;
        addscore(other->playerindex, force*self->multiple);	// Added force multiple
#if DC
		dcpad_rumblepower[other->playerindex] = force*2;
#endif
    }

	if(self->boss){
		kill_all_enemies();
		level_completed = 1;
	}

    // Fell in a hole
    if(self->a < PIT_DEPTH){

        if(self->model->diesound>= 0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100);

        kill(self);
        return;
    }

	set_pain(self, type, 1);
    self->attacking = 1;
    self->think = bike_crash;
    self->nextthink = time + THINK_SPEED;

    if((driver = spawn(self->x, self->z, 10, self->model->rider))){

		if(!driver->model->speed && !driver->model->nomove) driver->model->speed = 1;
		else if(driver->model->nomove) driver->model->speed = 0;

        //	ltb 1-18-05 new biker item code
        strncpy(driver->item, self->item, MAX_NAME_LEN);
        //	end new code

        driver->maxhealth = self->maxhealth;
        driver->health = self->health - (force*3);
        driver->boss = self->boss;	// so bikers can be bosses or rather their drivers

		if(driver->model->subtype == SUBTYPE_NOTGRAB) driver->nograb = 1;

    	if(driver->health <= 0){
            if(self->model->diesound>= 0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100);
        }

        toss(driver, 2.5 + randf(1));
        driver->xdir = (driver->direction)?(-1.2):1.2;
        set_fall(driver, type, 1);
        driver->takedamage = enemy_takedamage;
        driver->think = enemy_fall;
    }

    self->boss = 0;	// because the bike isn't really the boss

    self->health = 0;
    addscore(other->playerindex, self->score);	// Add score to player
}



void obstacle_fall(void){
	if(freezeall) return;	// Added incase obstacle is in the air when special is executed

    if(inair(self)) self->x += self->xdir;
    else if((!self->animating && self->model->animation[ANI_DIE]) || !self->model->animation[ANI_DIE]) kill(self);	// Fixed so ANI_DIE can be used
}



void obstacle_fly(void){	// Now obstacles can fly when hit like on Simpsons/TMNT
	if(freezeall) return;

    self->x += self->xdir * 4;	// Equivelant of speed 40
    if(self->x > advancex+(hRes1 + 200) || self->x < advancex-200) kill(self);

    self->nextthink = time + 2;
}



void obstacle_takedamage(entity *other, int force, int unused1, int unused2){
    entity *item = NULL;

    if(self->a <= PIT_DEPTH){
        kill(self);
        return;
    }

	self->pain_time = time + (GAME_SPEED / 5);	// Added so obstacles don't receive extra damage by falling entities/multiple simultaneous attacks

	if(other->type==TYPE_PLAYER || other->type == TYPE_SHOT || other->type == TYPE_OBSTACLE){
		player[other->playerindex].opponent = self;
#if DC
		dcpad_rumblepower[other->playerindex] = 75;
#endif
	}

    self->health -= force;
	self->playerindex = other->playerindex;	// Added so points go to the correct player
    addscore(other->playerindex, force*self->multiple);	// Points can now be given for hitting an obstacle

    if(self->health<=0){
		addscore(other->playerindex, self->score);	// Additional score can be added

        if(self->model->diesound>= 0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100);

        if(self->item[0] && (!self->if2p || count_ents(TYPE_PLAYER)>1)  && (!self->if3p || count_ents(TYPE_PLAYER)>2)&& (!self->if4p || count_ents(TYPE_PLAYER)>3)){
// 4player (line above)
            if(self->x < (int)advancex) item = spawn((int)advancex + 10, self->z, self->a, self->item);
            else if(self->x > (int)advancex + hRes1) item = spawn((int)advancex + (hRes1-10), self->z, self->a, self->item);
            else item = spawn(self->x, self->z, self->a, self->item);

            if(item){

				if(item->type == TYPE_ITEM) item->direction = 1;
				else if(item->type == TYPE_ENEMY){	// Obstacle spawning move here to fix "bouncing enemy" bug

                    if(item->model->animation[ANI_RESPAWN]){
                        ent_set_anim(item, ANI_RESPAWN, 0);
                        item->think = enemy_spawn;
                    }
                    else if(item->model->animation[ANI_SPAWN]){
                        ent_set_anim(item, ANI_SPAWN, 0);
                        item->think = enemy_spawn;
                    }
    				else item->think = enemy_drop;

					item->boss = self->boss;
					self->boss = 0;

	                if(self->itemhealth > 0) item->health = self->itemhealth;

	                ent_set_colourmap(item, self->itemmap);

	                if(self->itemalias[0]) strncpy(item->name, self->itemalias, MAX_NAME_LEN);

					if(self->itemtrans) item->screen = self->itemtrans;

					if(!item->model->speed && !item->model->nomove) item->model->speed = 1;	// Sets default speed if not set and not static
					else if(item->model->nomove) item->model->speed = 0;

					item->takedamage = enemy_takedamage;

					if(item->model->subtype==SUBTYPE_NOTGRAB) item->nograb = 1;
					else if(item->model->subtype==SUBTYPE_BIKER){
						item->think = biker_drive;
						item->boss = self->boss;
						self->boss = 0;
						item->takedamage = biker_takedamage;
					}
					else if(item->model->subtype==SUBTYPE_ARROW) item->think = arrow_fly;
				}
				else if(item->type == TYPE_TRAP) item->think = trap_think;
			//  7-1-2005 - new section ends
			}
        }

        if(other->x < self->x) self->xdir = 1;
        else self->xdir = -1;

        self->attacking = 1;	// So obstacles can explode and hurt players/enemies

		if(self->model->subtype == SUBTYPE_FLYDIE){	// Now obstacles can fly like on Simpsons/TMNT
			self->think = obstacle_fly;
			ent_set_anim(self, ANI_FALL, 0);
		}
		else{
			self->think = obstacle_fall;

			if(self->model->animation[ANI_DIE]) ent_set_anim(self, ANI_DIE, 0);	//  LTB 1-13-05  Die before toss
			else{
				toss(self, self->model->jumpheight/1.333);
				ent_set_anim(self, ANI_FALL, 0);
			}

			if(!self->model->nodieblink) self->blink = 1;
		}
  	}

    self->nextthink = time + 1;
}

// copied from enemy trymove
int npc_trymove(float xdir, float zdir)
{

    entity *other = NULL;
    int wall = 0;
    float coef1;
    float coef2;

	if(self->projectile == 0){

        // Walking

        // Out of bounds? Return to level!
        // Far from its parent entity also walk back.
        if(!inair(self)){
            // too far away , do a warp
            if(self->model->subtype == SUBTYPE_FOLLOW && self->parent &&
               (diff(self->z, self->parent->z) > self->model->animation[ANI_IDLE]->range[1] ||
                diff(self->x, self->parent->x) > self->model->animation[ANI_IDLE]->range[1]) ){
                self->think = npc_warp;
                return 1;
            }
                         
        	if(self->zdir && self->model->subtype == SUBTYPE_FOLLOW && self->parent && self->parent->z - self->z > self->model->animation[ANI_IDLE]->range[0]) self->zdir = self->model->speed/2;
            else if(self->zdir && self->model->subtype == SUBTYPE_FOLLOW && self->parent && self->z - self->parent->z > self->model->animation[ANI_IDLE]->range[0]) self->zdir = -self->model->speed/2;
            
        	if(self->zdir && self->model->subtype == SUBTYPE_FOLLOW && self->parent && self->parent->x - self->x > self->model->animation[ANI_IDLE]->range[0]) self->xdir = self->model->speed;
            else if(self->zdir && self->model->subtype == SUBTYPE_FOLLOW && self->parent && self->x - self->parent->x > self->model->animation[ANI_IDLE]->range[0]) self->xdir = -self->model->speed;
            
        	if(self->zdir && self->z < PLAYER_MIN_Z) self->zdir = self->model->speed/2;
        	if(self->zdir && self->z > PLAYER_MAX_Z) self->zdir = -self->model->speed/2;
		}

        if((self->xdir || self->zdir) && !inair(self)){

            // Don't walk into a hole or walk off platforms into holes
           	if(
				checkhole(self->x + xdir*5, self->z) &&
				checkwall(self->x + xdir*5, self->z) == -1 &&
				(!check_platform(self->x + xdir*5, self->z) ||
				((other = check_platform(self->x + xdir*5, self->z)) &&
				self->base < other->a + other->animation->platform[other->animpos][5]))
			)
				xdir = 0;

           	if(
				checkhole(self->x, self->z + zdir*5) &&
				checkwall(self->x, self->z + zdir*5) == -1 &&
				(!check_platform(self->x, self->z + zdir*5) ||
				((other = check_platform(self->x, self->z + zdir*5)) &&
				self->base < other->a + other->animation->platform[other->animpos][5]))
			)
				zdir = 0;


			if(xdir && (other = find_ent_here(self, self->x + xdir*2, self->z, (TYPE_OBSTACLE | TYPE_TRAP)))){
				if(!other->animation->platform[other->animpos][5]){
					if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
				}
			}

			if(zdir && (other = find_ent_here(self, self->x, self->z + zdir*2, (TYPE_OBSTACLE | TYPE_TRAP)))){
				if(!other->animation->platform[other->animpos][5]){
					if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;
				}
			}
		}
	}

	// Check for obstacles with platform code and adjust base accordingly
	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(other = check_platform(self->x + xdir*10, self->z + zdir*10))) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(other = check_platform(self->x + xdir, self->z + zdir)))) &&
		(self->projectile == 0 ||
		!other->animation->vulnerable[other->animpos]) &&
		self->a < other->a + other->animation->platform[other->animpos][5] &&
		self->a + self->model->height >= other->a
	){

		if(xdir>0 ? other->x>self->x : other->x<self->x) xdir = 0;
		if(zdir>0 ? other->z>self->z : other->z<self->z) zdir = 0;

	}

	if(
		((self->animation->move[self->animpos] == 0 &&
		self->animation->movez[self->animpos] == 0 &&
		(wall = checkwall(self->x + xdir*4, self->z + zdir*4)) >= 0) ||
		((self->animation->move[self->animpos] != 0 ||
		self->animation->movez[self->animpos] != 0) &&
		(wall = checkwall(self->x + xdir, self->z + zdir)) >= 0)) &&
		self->a < level->walls[wall][7]
	){

		coef1 = (level->walls[wall][1] - self->z) * ((level->walls[wall][2] - level->walls[wall][3]) / level->walls[wall][6]);
		coef2 = (level->walls[wall][1] - self->z) * ((level->walls[wall][4] - level->walls[wall][5]) / level->walls[wall][6]);

		if(self->z > level->walls[wall][1] || self->z < level->walls[wall][1] - level->walls[wall][6]){
			zdir = 0;
		}
		else{

			if(self->x < (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 &&
			self->x > level->walls[wall][0] + level->walls[wall][3] + coef1){

				self->x = level->walls[wall][0] + level->walls[wall][3] + coef1;
			}
			else if(self->x > (level->walls[wall][0] * 2 + level->walls[wall][3] + level->walls[wall][5]) / 2 &&
			self->x < level->walls[wall][0] + level->walls[wall][5] + coef2){

				self->x = level->walls[wall][0] + level->walls[wall][5] + coef2;
			}

			xdir = 0;
		}

	}

    self->x += xdir;
    self->z += zdir;

	if(self->z > PLAYER_MAX_Z) self->z = PLAYER_MAX_Z;	
	if(self->z < PLAYER_MIN_Z) self->z = PLAYER_MIN_Z;	

    // Do a throw?
    if(		self->health>0 &&
            self->model->animation[ANI_THROW] &&
            !inair(self) &&
            !self->link &&
            self->animation->move[self->animpos] == 0 &&
            self->animation->movez[self->animpos] == 0 &&
            self->animation != self->model->animation[ANI_FALL] &&	// No more grabbing while falling
            self->animation != self->model->animation[ANI_FALL2] && 
            self->animation != self->model->animation[ANI_FALL3] && 
            self->animation != self->model->animation[ANI_FALL4] && 
			self->animation != self->model->animation[ANI_FALL5] && 
            self->animation != self->model->animation[ANI_FALL6] && 
            self->animation != self->model->animation[ANI_FALL7] && 
			self->animation != self->model->animation[ANI_FALL8] && 
            self->animation != self->model->animation[ANI_FALL9] && 
            self->animation != self->model->animation[ANI_FALL10] && 
            self->animation != self->model->animation[ANI_SHOCK] &&	
            self->animation != self->model->animation[ANI_BURN] &&	
            (other = find_ent_here(self, self->x + xdir, self->z + zdir, self->hostile)) &&
            ((other->type) & (TYPE_PLAYER | TYPE_ENEMY | TYPE_NPC)) &&//cant grab obstacles or sth like that
            !inair(other) &&
            self->a == other->a &&
            !other->invincible &&
            other->animation != other->model->animation[ANI_DODGE] &&	// Can't throw a dodging player
            (self->direction ? other->x>self->x : other->x<self->x)
      ){

        // Reposition both on average height and such
        zdir = (self->z + other->z) / 2;
		self->z = other->z = zdir;

		if((wall == -1 || (wall >= 0 && self->a != level->walls[wall][7])) &&
		(checkwall(self->x - self->model->grabdistance/2, self->z) >= 0 ||
		checkwall(other->x - self->model->grabdistance/2, other->z) >= 0)){

			if(self->x < other->x) other->x = self->x + self->model->grabdistance;
			else self->x = other->x + self->model->grabdistance;
		}
		else if((wall == -1 || (wall >= 0 && self->a != level->walls[wall][7])) &&
		(checkwall(self->x + self->model->grabdistance/2, self->z) >= 0 ||
		checkwall(other->x + self->model->grabdistance/2, other->z) >= 0)){

			if(self->x > other->x) other->x = self->x - self->model->grabdistance;
			else self->x = other->x - self->model->grabdistance;
		}
		else{
	        xdir = (self->x + other->x) / 2;

	        if(self->x < other->x){
	            self->x = xdir - (self->model->grabdistance/2);  // 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
	        else{
	            self->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir - (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
		}

		self->direction = (self->x < other->x);

        if(other->type == TYPE_PLAYER) player[other->playerindex].opponent = self;
        other->attacking = 0;

        // use original throw code if throwframewait not present, kbandressen 10/20/06
        if(self->model->throwframewait == -1){
        	self->think = npc_throw;
        	ent_set_anim(self, ANI_THROW, 0);
        	ent_unlink(self);

			if(other->model->throwheight) toss(other, other->model->throwheight);
        	else toss(other, other->model->jumpheight);

        	other->direction = self->direction;
			other->projectile = 2;
			other->xdir = (other->direction)?(-other->model->throwdist):(other->model->throwdist);

    	    if(other->type == TYPE_PLAYER) other->think = player_fall;
    	    else if(other->type == TYPE_ENEMY) other->think = enemy_fall;
    	    else if(other->type == TYPE_NPC) other->think = npc_fall;
	        other->damage_on_landing = other->model->throwdamage;	// use adjusted throwdamage
        	ent_set_anim(other, ANI_FALL, 0);
        	ent_unlink(other);
        }
        // otherwise enemy_throw_wait will be used, kbandressen 10/20/06
        else{
            self->think = npc_throw_wait;
            ent_set_anim(self, ANI_THROW, 0);
            self->attacking = 1;
            ents_link(self, other);
            
            other->think = prethrow;
            if(other->type == TYPE_PLAYER) player[other->playerindex].opponent = self;
            if(other->model->animation[ANI_GRABBED]) ent_set_anim(other, ANI_GRABBED, 0);	// New grabbed animation
            else ent_set_anim(other, ANI_PAIN, 0);
        }

        return 1;
    }


    // Do a grab?
    if(		self->health>0 &&
            self->model->animation[ANI_GRAB] &&
            !inair(self) &&
            !self->link &&
            self->animation->move[self->animpos] == 0 &&
            self->animation->movez[self->animpos] == 0 &&
            self->animation != self->model->animation[ANI_FALL] &&	// No more grabbing while falling
            self->animation != self->model->animation[ANI_FALL2] &&
            self->animation != self->model->animation[ANI_FALL3] &&
            self->animation != self->model->animation[ANI_FALL4] &&
			self->animation != self->model->animation[ANI_FALL5] &&
            self->animation != self->model->animation[ANI_FALL6] &&
            self->animation != self->model->animation[ANI_FALL7] &&
			self->animation != self->model->animation[ANI_FALL8] &&
            self->animation != self->model->animation[ANI_FALL9] &&
            self->animation != self->model->animation[ANI_FALL10] &&
            self->animation != self->model->animation[ANI_SHOCK] &&
            self->animation != self->model->animation[ANI_BURN] &&
            (other = find_ent_here(self, self->x + xdir, self->z + zdir, self->hostile)) &&
            ((other->type) & (TYPE_PLAYER | TYPE_ENEMY | TYPE_NPC)) &&//cant grab obstacles or sth like that
            !inair(other) &&
            self->a == other->a &&
            !other->invincible &&
            other->animation != other->model->animation[ANI_DODGE] &&	// Can't grab a dodging player
            (self->direction ? other->x>self->x : other->x<self->x)
      ){

		// Reposition both on average height and such
		zdir = (self->z + other->z) / 2;
		self->z = other->z = zdir;

		if(
			(wall == -1 ||
			(wall >= 0 &&
			self->a != level->walls[wall][7])) &&
			(checkwall(self->x - self->model->grabdistance/2, self->z) >=0 ||
			checkwall(other->x - self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x < other->x) other->x = self->x + self->model->grabdistance;
			else self->x = other->x + self->model->grabdistance;
		}
		else if(
			(wall == -1 ||
			(wall >= 0 &&
			self->a != level->walls[wall][7])) &&
			(checkwall(self->x + self->model->grabdistance/2, self->z) >= 0 ||
			checkwall(other->x + self->model->grabdistance/2, other->z) >= 0)
		){

			if(self->x > other->x) other->x = self->x - self->model->grabdistance;
			else self->x = other->x - self->model->grabdistance;
		}
		else{
	        xdir = (self->x + other->x) / 2;

	        if(self->x < other->x){
	            self->x = xdir - (self->model->grabdistance/2);  // 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
	        else{
	            self->x = xdir + (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	            other->x = xdir - (self->model->grabdistance/2);// 30-12-2004 changed GRAB_DIST to grabdistance per player
	        }
		}

		self->direction = (self->x < other->x);
		other->direction = !self->direction;

        if(other->type == TYPE_PLAYER) {
            player[other->playerindex].opponent = self;
            other->think = player_grabbed;
        } else if(other->type == TYPE_NPC) other->think = enemy_grabbed;
        else other->think = npc_grabbed;
        other->attacking = 0;

		self->think = npc_grab;
		self->attacking = 1;
		ent_set_anim(self, ANI_GRAB, 0);
		ents_link(self, other);

		if(other->model->animation[ANI_GRABBED]) ent_set_anim(other, ANI_GRABBED, 0);	// New grabbed animation
		else ent_set_anim(other, ANI_PAIN, 0);
	
        return 1;
    }


    // End of level is blocked?
    if(level->exit_blocked){
        if(self->x > level->width-30-(PLAYER_MAX_Z-self->z)) self->x = level->width-30-(PLAYER_MAX_Z-self->z);
    }

    return 0;
}


void npc_throw(void){
    if(self->animating) return;
    self->think = npc_think;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
}

void npc_throw_wait(void){

	entity *other = NULL;

    // returns to idle animation if link is broken before other is actually thrown
    if(!(self->link)){
        if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	    else ent_set_anim(self, ANI_IDLE, 0);
	    self->think = npc_think;
	    return;
     }
    if(self->animpos != self->model->throwframewait) return;
    
    other = self->link;
    if(other->model->throwheight) toss(other, other->model->throwheight);
    else toss(other, other->model->jumpheight);

    other->direction = self->direction;
    other->projectile = 2;
    other->xdir = (other->direction)?(-other->model->throwdist):(other->model->throwdist);

    if(other->type == TYPE_PLAYER) other->think = player_fall;
    else if(other->type == TYPE_NPC) other->think = npc_fall;
    else if(other->type == TYPE_ENEMY) other->think = enemy_fall;
    
    other->damage_on_landing = other->model->throwdamage;	// use adjusted throwdamage
    ent_set_anim(other, ANI_FALL, 0);
    ent_unlink(other);
    self->think = npc_throw;
}

void npc_grab(void){
    // if(self->link) return;
    if(self->animating) return;
    ent_unlink(self);
    self->think = npc_think;
    self->attacking = 0;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);
}



void npc_grabbed(void){
    // Escape?
    if(time >= self->stalltime && self->model->animation[ANI_SPECIAL]){
        ent_unlink(self);
        ent_set_anim(self, ANI_SPECIAL, 0);
        self->attacking = 1;
        self->think = npc_attack;
        return;
    }

    // Just check if we're still grabbed...
    if(self->link) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->xdir = self->zdir = 0;
    self->think = npc_think;
}

entity* npc_find_target()
{
	int i, min, max;
	int index = -1;
	//use the walk animation's range
	if(self->model->animation[ANI_WALK]){
        min = self->model->animation[ANI_WALK]->range[0];
        max = self->model->animation[ANI_WALK]->range[1];
        if(!max || max < min) max = 999;
    } else {
        min = 0;
        max = 999;
    }
	//find the 'nearest' one
    for(i=0; i<MAX_ENTS; i++){
        if(		ent_list[i]->exists && ent_list[i] != self //cant target self
                && (ent_list[i]->type & self->hostile)
                && ent_list[i]->health > 0 //must be alive
/*                && ent_list[i]->think != knife_think //hmm, chasing a projectile?
                && ent_list[i]->think != star_think
                && ent_list[i]->think != bomb_think*/
                && diff(ent_list[i]->x,self->x)+ diff(ent_list[i]->z,self->z) >= min
                && diff(ent_list[i]->x,self->x)+ diff(ent_list[i]->z,self->z) <= max
                //&& ent_list[i]->animation->vulnerable[ent_list[i]->animpos]
          ){
            if(index <0 || 
               ((self->x < ent_list[i]->x) == (self->direction) && // don't turn to the one on the back
               diff(ent_list[i]->x, self->x)+diff(ent_list[i]->z, self->z) < diff(ent_list[index]->x, self->x)+diff(ent_list[index]->z, self->z)))
                index = i;
        }
    }
    if( index >=0) return ent_list[index];
    return NULL;
}

void npc_jump()
{
	if(freezeall || self->frozen) return;

    npc_trymove(self->xdir, 0);
    if(inair(self)) return;

    self->jumping = 0;
    self->xdir = 0;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->think = npc_think;
}

void npc_runoff()
{
    entity *target = npc_find_target();

    if(!target || self->frozen || freezeall) return;
    
	if(!self->model->noflip) self->direction = (self->x < target->x);
    if(self->direction) self->xdir = -self->model->speed/2;
    else self->xdir = self->model->speed/2;
    npc_trymove(self->xdir, 0);

    if(time > self->stalltime) self->think = npc_think;
}

void npc_attack()
{
    entity *target = NULL;
    
	if(self->frozen || freezeall) return;

    self->attacking = 1;

	// If they're attacking, reset their escapecount.
    if(self->model->escapehits) self->escapecount = 0;

	if(inair(self) && !freezeall){	
		if(self->direction) npc_trymove((self->animation->moveflag > 0) * 1.3, 0);	// If moveflag > 0 will move 1.3 else 0
		else npc_trymove((self->animation->moveflag > 0) * -1.3, 0);
		return;
	}

    if(self->animating) return;

    self->attacking = 0;
    self->xdir = 0;
    self->zdir = 0;

    target = npc_find_target();


    if(target && !self->model->nomove && diff(self->x, target->x)<80 && (rand32()&3)){
		ent_set_anim(self, ANI_WALK, 0);
        self->think = npc_runoff;
    }
    else{
		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);

        self->think = npc_think;
    }


    self->stalltime = time + GAME_SPEED - self->aggression;
}

void npc_lie(entity *other, int force, int drop, int type){
    entity * target = NULL;

    // Died?
    if(self->health <= 0){

		// Drop Weapon due to death.
		if(self->model->weaploss <= 2) dropweapon(1);

		if(self->model->death == 2) set_death(self, type, 0);

	    if(!self->model->nodieblink || (self->model->nodieblink == 1 && !self->animating)){	// Now have the option to blink or not
    		self->think = suicide;
    		self->blink = 1;
    		self->nextthink = time + GAME_SPEED * 2;
		}
		else if(self->model->nodieblink == 2 && !self->animating) self->think = suicide;

        return;
    }

     // Stall
    if(time < self->stalltime){
		if(freezeall) self->stalltime += 2;
		return;
	}
	// Used to pick a target for a riseattack  //
    target = npc_find_target();
    
	self->think = npc_rise;

    // Get up again

    // New riseattack section for a quicker counter when a player is hanging around waiting to cheap-shot the enemy
	if(
		self->model->animation[ANI_RISEATTACK] &&
		diff(self->z, target->z) < (self->model->grabdistance/3) &&	// If the target is at the correct z
		((target->x > self->x+self->model->animation[ANI_RISEATTACK]->range[0] &&
		target->x < self->x+self->model->animation[ANI_RISEATTACK]->range[1]) ||
		(target->x < self->x-self->model->animation[ANI_RISEATTACK]->range[0] &&
		target->x > self->x-self->model->animation[ANI_RISEATTACK]->range[1]))
	){

		self->direction = (target->x > self->x);	// Stands up and swings in the right direction depending on chosen target
		self->attacking = 1;
		ent_set_anim(self, ANI_RISEATTACK,0);
	}
	else ent_set_anim(self, ANI_RISE,0);

	self->xdir = self->zdir = 0;
}

void npc_rise(void){
    if(self->animating) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->xdir = self->zdir = 0;
    self->think = npc_think;
}

void npc_pain(void){

	// Enemies can now escape non-knockdown spammage (What a weird phrase)!
    if((self->escapecount > self->model->escapehits) && !inair(self) && self->model->animation[ANI_SPECIAL2]){
        // Counter the player!
		ent_set_anim(self, ANI_SPECIAL2, 0);
		self->attacking = 1;
		self->escapecount = 0;
		self->think = npc_attack;
		return;
    }

    if(self->animating || inair(self)) return;

    if(self->link){

		if(self->model->animation[ANI_GRABBED]) ent_set_anim(self, ANI_GRABBED, 0);	// New grabbed animation
		else ent_set_anim(self, ANI_PAIN, 0);

        self->think = npc_grabbed;
    }
    else{
		if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
		else ent_set_anim(self, ANI_IDLE, 0);

        self->think = npc_think;
    }
}



void npc_block(void){
	if(self->animating) return;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

	self->think = npc_think;
}

void npc_fall(void){
	entity *dust = NULL;

	if(freezeall) return;

	if(self->frozen){
		if(self->colourmap == self->model->colourmap[self->model->fmap - 1]) self->colourmap = self->map;
		self->frozen = 0;
		self->freezetime = 0;
	}
/*
	if(self->projectile == 1){
        if(self->direction) npc_trymove(-2.5, 0);
        else npc_trymove(2.5, 0);
    }
    else if(self->projectile == 2){
		if(self->direction) npc_trymove(-self->model->throwdist, 0);
		else npc_trymove(self->model->throwdist, 0);
	}
    else{
        if(self->direction) npc_trymove(-1.2, 0);
        else npc_trymove(1.2, 0);
    }*/
    npc_trymove(self->xdir, 0);

    // Still falling?
    if(inair(self)){
        if(self->a < PIT_DEPTH && self->health<=0) kill(self);
        return;
    }

    // Landed
    if(self->projectile > 0){
        if(!self->model->noquake) quake = 4;	// Don't shake if specified
        if(self->takedamage && self->damage_on_landing){
            self->takedamage(self, self->damage_on_landing, 0, ATK_NORMAL);
        }
        self->damage_on_landing = 0;
        self->projectile = 0;
    }

    if(self->animation == self->model->animation[ANI_SHOCK] || self->animation == self->model->animation[ANI_BURN]){ // New animations
		ent_set_anim(self, ANI_FALL, 0);	// Set to normal fall animation and change to last frame
		self->currentsprite = self->animation->sprite[self->animation->numframes - 1];
		self->animpos = self->animation->numframes - 1;
	}

    // Hard landing? Quake and bounce!
    if(self->tossv < -2){

		if(self->model->dust[0]){
			dust = spawn(self->x, self->z, self->a, self->model->dust);
			dust->autokill = 1;
		}

        if(!self->model->noquake) quake = 2;	// Don't shake if specified

        if(!self->owner && self->type != TYPE_SHOT && smp_fall >= 0)
			sound_play_sample(smp_fall, 0, savedata.effectvol,savedata.effectvol, 100);

        toss(self, self->model->jumpheight/4);
#if DC
		int i;
		for(i=0; i<maxplayers; i++) dcpad_rumblepower[i] = 75;
#endif
        return;
    }

	// Drop Weapon due to Enemy Falling.
	if(self->model->weaploss == 1) dropweapon(1);

	if(self->boss && level_completed) tospeedup = 1;

    // Pause a bit...
    self->think = npc_lie;
    self->stalltime = time + GAME_SPEED - self->model->risetime;
}

void npc_prepare()
{
    entity *target = npc_find_target();
    int pickable[5];

    if(!target || self->frozen){
        self->think = npc_think;
        return;
    }
    if(!self->model->noflip) self->direction = (self->x < target->x);

    // Wait...
    if(time < self->stalltime) return;


    // Pick an attack
    pickable[0] = pickable[1] = pickable[2] = pickable[3] = pickable[4] = 0;

    if(self->model->animation[ANI_ATTACK1] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK1]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK1]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK1]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK1]->range[1])){

        pickable[0] = 1;
    }
    if(self->model->animation[ANI_ATTACK2] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK2]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK2]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK2]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK2]->range[1])){

        pickable[1] = 1;
        if((rand32() & 31) < 10) pickable[0] = 0;
    }
    if(self->model->animation[ANI_ATTACK3] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK3]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK3]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK3]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK3]->range[1])){

        pickable[2] = 1;
        if((rand32() & 31) < 10) pickable[1] = 0;
    }
    if(self->model->animation[ANI_ATTACK4] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_ATTACK4]->range[0] &&
            target->x < self->x+self->model->animation[ANI_ATTACK4]->range[1]
            : target->x < self->x-self->model->animation[ANI_ATTACK4]->range[0] &&
            target->x > self->x-self->model->animation[ANI_ATTACK4]->range[1])){

        pickable[3] = 1;
        if((rand32() & 31) < 10) pickable[2] = 0;
    }
    if(self->weapent && self->weapent->model->subtype == SUBTYPE_PROJECTILE && self->model->animation[ANI_THROWATTACK] &&
            (self->direction ? target->x > self->x+self->model->animation[ANI_THROWATTACK]->range[0] &&
            target->x < self->x+self->model->animation[ANI_THROWATTACK]->range[1]
            : target->x < self->x-self->model->animation[ANI_THROWATTACK]->range[0] &&
            target->x > self->x-self->model->animation[ANI_THROWATTACK]->range[1])){

        pickable[4] = 1;
        if((rand32() & 31) < 10) pickable[3] = 0;
    }

    if(pickable[0]){
        ent_set_anim(self, ANI_ATTACK1, 0);
        self->think = npc_attack;
        return;
    }
    if(pickable[1]){
        ent_set_anim(self, ANI_ATTACK2, 0);
        self->think = npc_attack;
        return;
    }
    if(pickable[2]){
        ent_set_anim(self, ANI_ATTACK3, 0);
        self->think = npc_attack;
        return;
    }
    if(pickable[3]){
        ent_set_anim(self, ANI_ATTACK4, 0);
        self->think = npc_attack;
        return;
    }
    if(pickable[4]){
        ent_set_anim(self, ANI_THROWATTACK, 0);
        self->think = npc_attack;
        return;
    }

    // No attack to perform, return to normal
    self->think = npc_think;
}

void npc_jumpattack()
{
	if(freezeall || self->frozen) return;

    if(self->animpos != self->lastanimpos && self->animpos == self->animation->throwframe){
        star_spawn(self->x + (self->direction ? 56 : -56), self->z, self->a+67, self->direction);
    }

    self->lastanimpos = self->animpos;

    npc_trymove(self->xdir, 0);
    if(inair(self)) return;

    self->attacking = 0;
    self->jumping = 0;
    self->xdir = 0;

	if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
	else ent_set_anim(self, ANI_IDLE, 0);

    self->think = npc_think;
}

void npc_takedamage(entity *other, int force, int drop, int type){
    entity *item = NULL;	//NPC can also drop item

    if(self->a<=PIT_DEPTH && self->health<=0){
        // Don't scream twice
        kill(self);
        return;
    }

    if(self->model->animal) drop = 1;
	
	// Drop Weapon due to being hit.
	if(self->model->weaploss <= 0) dropweapon(1);

    self->pain_time = time + (GAME_SPEED / 5);

	if((type != ATK_FREEZE || (type == ATK_FREEZE && self->frozen)) && !self->model->noflip && !inair(self)){	// Added !inair(self) so enemies don't change directions mid-air when a boss is defeated
    	if(self->x < other->x) self->direction = 1;
    	else if(self->x > other->x) self->direction = 0;
	}

    // nopain flag
    if(self->health <= force || drop || !self->model->nopain){
		self->attacking = 0;

        if(other != self->link) ent_unlink(self);

        if(inair(self)) drop = 1;       // If in the air, automatically fall (freeze will override later if not frozen)
    }
    if(type == ATK_BLAST){
        self->projectile = 1;
        drop = 1;
    }
	if(type == ATK_STEAL){	// New steal attack adds health removed from opponent
		if(self->health >= force) other->health += force;
		else other->health += self->health;
		if(other->health > other->maxhealth) other->health = other->maxhealth;
	}
    if(type == ATK_FREEZE && !self->frozen && !self->owner && !self->model->nomove){	// New freeze attack - If not frozen, freeze entity unless it's a projectile
		self->frozen = 1;
		if(self->freezetime == 0) self->freezetime = time + other->animation->attack_length[other->animpos];
		if(self->model->fmap) ent_set_colourmap(self, self->model->fmap);
		drop = 0;
	}
	else if(self->frozen) drop = 1; // If frozen and any other attack is executed, drop the opponent

	if(self->model->nomove || self->model->nodrop) drop = 0;	// Static enemies/nodrop enemies cannot be knocked down

    self->health -= force;

	if(self->toexplode == 2) return;

    if(self->health <= 0){
        self->nograb = 1;

        if(self->model->diesound>= 0) sound_play_sample(self->model->diesound, 0, savedata.effectvol,savedata.effectvol, 100);

        // enemies can spawn item - including traps and other enemies!   7-1-2005  ltb 1-18-05 cleaned
        if(self->item[0] && (!self->if2p || count_ents(TYPE_PLAYER)>1) && (!self->if3p || count_ents(TYPE_PLAYER)>2)&& (!self->if4p || count_ents(TYPE_PLAYER)>3)){ //4player

            if(self->x < (int)advancex) item = spawn((int)advancex + 10, self->z, self->a, self->item);
            else if(self->x > (int)advancex + (hRes1)) item = spawn((int)advancex + (hRes1 - 10), self->z, self->a, self->item);
            else item = spawn(self->x, self->z, self->a, self->item);
            
            ent_default_init(item);
            
            item->direction = self->direction;
            
            item->boss = self->boss;
            self->boss = 0;

            if(self->itemhealth > 0) item->health = self->itemhealth;

            ent_set_colourmap(item, self->itemmap);

            if(self->itemalias[0]) strncpy(item->name, self->itemalias, MAX_NAME_LEN);
        }
        // end

        // Fell in a hole
        if(self->a < PIT_DEPTH){
            kill(self);
            return;
        }
    }

	// Enemies can now use SPECIAL2 to escape cheap attack strings!
	if(self->model->escapehits){
        if(drop) self->escapecount = 0;
		else self->escapecount++;
    }

	// New pain, fall, and death animations. Also, the nopain flag.
    if(drop || self->health <= 0 || (inair(self) && !self->frozen)){	// So enemies frozen are frozen in midair

        ent_unlink(self);

		// Drop Weapon due to death.
		if(self->model->weaploss <= 2) dropweapon(1);

        if(self->health <= 0 && self->model->death == 1) set_death(self, type, 0);
		else{
			// Now if no fall/die animations exist, entity simply disapears
			if(!self->model->animation[ANI_FALL]  && !self->model->animation[ANI_FALL2] &&
			   !self->model->animation[ANI_FALL3] && !self->model->animation[ANI_FALL4] &&
			   !self->model->animation[ANI_FALL5] && !self->model->animation[ANI_FALL6] &&
			   !self->model->animation[ANI_FALL7] && !self->model->animation[ANI_FALL8] &&
			   !self->model->animation[ANI_FALL9] && !self->model->animation[ANI_FALL10]){
				kill(self);
				return;
			}
			self->xdir = (self->projectile==1)?((self->direction)?(-2.5):2.5):((self->direction)?(-1.2):1.2);
            toss(self, self->model->jumpheight/1.6 + randf(self->model->jumpheight/4));
			set_fall(self, type, 1);
        }
		self->think = npc_fall;
    }
	// Don't change to pain animation if frozen 
    else if(!self->frozen && !self->model->nopain){   
		set_pain(self, type, 1);
        self->think = npc_pain;
    }
}

// warp to its parent entity, just like skeletons in Diablo 2
void npc_warp()
{
    if(!self->parent || self->frozen || freezeall) return;
    self->z = self->parent->z; 
    self->x = self->parent->x;
    self->a = self->parent->a;
    self->xdir = self->zdir = 0;
    self->base = self->parent->base;
    self->tossv = 0;
    
    if(self->model->animation[ANI_RESPAWN]) {
        self->think = npc_spawn; 
        ent_set_anim(self, ANI_RESPAWN, 0);
    } else if(self->model->animation[ANI_SPAWN]) {
        self->think = npc_spawn; 
        ent_set_anim(self, ANI_SPAWN, 0);
    }
	else{
        self->think = npc_drop;
        int dodrop = (level && (level->scrolldir==SCROLL_UP || level->scrolldir==SCROLL_DOWN));

        if((dodrop && self->x > -30 && self->x < 350) ){
			// PSP Video Update from 240 to vRes1
            self->a += vRes1 + randf(40);						

            if(self->health < 1){
                ent_set_anim(self, ANI_FALL, 0);
                self->think = npc_fall; 
            }
        }
    }
}

void npc_turn()
{
    if(!self->animating) {
        if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
        else ent_set_anim(self, ANI_IDLE, 0);
        self->direction = !self->direction;
        self->think = npc_think;
    }
}

void npc_drop(void){
    if(inair(self)) return;
    self->think = npc_think;
    if(self->health<=0) kill(self);
}


void npc_spawn(void){
    if(!self->animating){
        self->think = npc_think;
        ent_set_anim(self, ANI_IDLE, 0);
    }
}

// NPC A.I. section
void npc_think(void){
    entity *target = npc_find_target();
    entity *other = NULL;
    float maxspeed;
    float dx,dz;
    int rnum;
    int run = 0;
    int walk;
    int wall;
    int xdir;
    int predir;
    
    predir = self->direction;

	if(self->grabbing && (self->animation == self->model->animation[ANI_IDLE]
                        ||self->animation == self->model->animation[ANI_FAINT]) ) self->grabbing = NULL;

    if( self->frozen || freezeall) return;	// if there's no target, don't stop,since it's a npc

	if(self->getting){
		 if((self->getting = self->animating)) return;
		 ent_set_anim(self, ANI_IDLE, 0);
	}

   // there's a target, do attack
    if(target) {
    	// Reset their escapecount if they aren't being spammed anymore.
    	if(self->model->escapehits &&
         !(self->animation == self->model->animation[ANI_IDLE] || self->animation == self->model->animation[ANI_FAINT] ||
           self->animation == self->model->animation[ANI_PAIN] || self->animation == self->model->animation[ANI_PAIN2] ||
           self->animation == self->model->animation[ANI_PAIN3] || self->animation == self->model->animation[ANI_PAIN4] ||
    	   self->animation == self->model->animation[ANI_PAIN5] || self->animation == self->model->animation[ANI_PAIN6] ||
           self->animation == self->model->animation[ANI_PAIN7] || self->animation == self->model->animation[ANI_PAIN8] ||
    	   self->animation == self->model->animation[ANI_PAIN9] || self->animation == self->model->animation[ANI_PAIN10] ||
           self->animation == self->model->animation[ANI_SHOCKPAIN] || self->animation == self->model->animation[ANI_BURNPAIN])){
               self->escapecount = 0;
        }
    
    	if(self->model->animation[ANI_JUMP]){
    
    		// Check to see if there is a wall within jumping distance and within a jumping height
    
    		if(self->direction) xdir = self->x + self->model->animation[ANI_JUMP]->range[0];
    		else xdir = self->x - self->model->animation[ANI_JUMP]->range[0];
    
    
    		if(
    			(wall = checkwall(xdir, self->z)) >= 0 &&
    			level->walls[wall][7] <= self->a + self->model->animation[ANI_JUMP]->range[1] &&
    			!inair(self) &&
    			self->a < level->walls[wall][7]
    		){
    
    			if(
    				(self->direction &&
    				self->x < (level->walls[wall][0] * 2 + level->walls[wall][1] + level->walls[wall][4]) / 2) ||
    				(!self->direction &&
    				self->x > (level->walls[wall][0] * 2 + level->walls[wall][1] + level->walls[wall][4]) / 2)
    			){
    
    				ent_set_anim(self, ANI_JUMP, 0);
    
    				if(self->direction) self->xdir = 1.3;
    				else self->xdir = -1.3;
    
    				self->jumping = 1;
    				self->think = npc_jump;
    				toss(self, self->model->jumpheight);
    			}
    		}
    	}
    
    	if(!self->model->noflip) self->direction = (self->x < target->x);	// Don't flip if set
    
        if(self->animating && (self->animation == self->model->animation[ANI_WALK] ||
    	   self->animation == self->model->animation[ANI_UP] ||
           self->animation == self->model->animation[ANI_DOWN] ||      // Include ANI_UP, ANI_DOWN animations
           self->animation == self->model->animation[ANI_BACKWALK])){
    
               if((self->direction ? self->xdir<0 : self->xdir>0) &&
                  !self->model->animation[ANI_BACKWALK]) self->animating = -1;         // Walking backwards
    		   
    		   else self->animating = 1;
        }
    
    
        // Target in range?
        if(diff(self->z, target->z) < (self->model->grabdistance/3)){
    
            // Target jumping? Try uppercut!
            if(target->jumping && self->model->animation[ANI_UPPER] &&
                    (self->direction ?
                     self->x<target->x-10 && target->x<self->x+120 :
                     self->x>target->x+10 && target->x>self->x-120 )){
    
                // Don't waste any time!
                ent_set_anim(self, ANI_UPPER, 0);
                self->think = npc_attack;
                return;
            }
    
            if(self->direction ?
                    (self->model->animation[ANI_ATTACK1] && target->x > self->x+self->model->animation[ANI_ATTACK1]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK1]->range[1]) ||
                    (self->model->animation[ANI_ATTACK2] && target->x > self->x+self->model->animation[ANI_ATTACK2]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK2]->range[1]) ||
                    (self->model->animation[ANI_ATTACK3] && target->x > self->x+self->model->animation[ANI_ATTACK3]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK3]->range[1]) ||
                    (self->model->animation[ANI_ATTACK4] && target->x > self->x+self->model->animation[ANI_ATTACK4]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_ATTACK4]->range[1]) ||
                    (self->weapent &&
                    (self->model->animation[ANI_THROWATTACK] && target->x > self->x+self->model->animation[ANI_THROWATTACK]->range[0] &&
                    target->x < self->x+self->model->animation[ANI_THROWATTACK]->range[1]))
                    :
                    (self->model->animation[ANI_ATTACK1] && target->x < self->x-self->model->animation[ANI_ATTACK1]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK1]->range[1]) ||
                    (self->model->animation[ANI_ATTACK2] && target->x < self->x-self->model->animation[ANI_ATTACK2]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK2]->range[1]) ||
                    (self->model->animation[ANI_ATTACK3] && target->x < self->x-self->model->animation[ANI_ATTACK3]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK3]->range[1]) ||
                    (self->model->animation[ANI_ATTACK4] && target->x < self->x-self->model->animation[ANI_ATTACK4]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_ATTACK4]->range[1]) ||
                    (self->weapent &&
    	            (self->model->animation[ANI_THROWATTACK] && target->x < self->x-self->model->animation[ANI_THROWATTACK]->range[0] &&
                    target->x > self->x-self->model->animation[ANI_THROWATTACK]->range[1]))
              ){
    
    			if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
    			else ent_set_anim(self, ANI_IDLE, 0);
                self->zdir = self->xdir = 0;
                self->think = npc_prepare;
    			self->stalltime = time + (GAME_SPEED/4) + (rand32()%(GAME_SPEED/10) - self->aggression);
    
                return;
            }
    
            if(self->model->animation[ANI_JUMPATTACK] || self->model->animation[ANI_JUMPATTACK2]){
    
                rnum = 0;
    
                if(!self->model->animation[ANI_JUMPATTACK]) rnum = 1;
                else if(self->model->animation[ANI_JUMPATTACK2] && (rand32()&1)) rnum = 1;
    
                if(rnum==0 &&
                // do a jumpattack
                (self->direction ?
                    ((self->model->jumpheight == 4 && target->x > self->x+150 && target->x < self->x+200) ||
                    (self->model->jumpheight != 4 && target->x > self->x+(self->model->animation[ANI_JUMPATTACK]->range[0]-10) &&
                    target->x < self->x+(self->model->animation[ANI_JUMPATTACK]->range[1]+5))):  
                    ((self->model->jumpheight == 4 && target->x < self->x-150 && target->x > self->x-200) ||
                    (self->model->jumpheight != 4 && target->x < self->x-(self->model->animation[ANI_JUMPATTACK]->range[0]-10) &&
                    target->x > self->x-(self->model->animation[ANI_JUMPATTACK]->range[1]+5))))
                    ){
        
                    ent_set_anim(self, ANI_JUMPATTACK, 0);
                    if(self->direction) self->xdir = 1.3;
                    else self->xdir = -1.3;
                    
                }
                else if(rnum==1 &&
                // do a jumpattack2
                (self->direction ?
                    (target->x > self->x+(self->model->animation[ANI_JUMPATTACK2]->range[0]-10) &&
                    target->x < self->x+(self->model->animation[ANI_JUMPATTACK2]->range[1]+5)):  
                    (target->x < self->x-(self->model->animation[ANI_JUMPATTACK2]->range[0]-10) &&
                    target->x > self->x-(self->model->animation[ANI_JUMPATTACK2]->range[1]+5)))
                ) {
                    ent_set_anim(self, ANI_JUMPATTACK2, 0);
                    self->xdir = 0;
                } else {
                   rnum = -1;
                }
                
                if(rnum >= 0){
                    self->think = npc_jumpattack;
                    self->attacking = 1;
                    self->jumping = 1;
        
                    toss(self, self->model->jumpheight);
        
                    return;
                }
            }
    
        }
    }// end of attack section
    
    if((other = npc_find_item()))   //try to pick up an item, if possible
        if(!self->model->noflip) self->direction = (self->x < other->x);
    
    if(self->direction != predir && self->model->animation[ANI_TURN]){
        self->direction = !self->direction;
        ent_set_anim(self, ANI_TURN, 0);
        self->xdir = self->zdir = 0;
        self->think = npc_turn;
        return;
    }
    
    // move section
    if(self->stalltime < time ){
        if(other){
        // if there's an item to pick up, move towards it.
			maxspeed = self->model->speed*1.5;
            dx = diff(self->x, other->x);
            dz = diff(self->z, other->z);
            if(!dz && !dx) self->xdir = self->zdir = 0;
            else {
                self->xdir = maxspeed * dx / (dx+dz);
                self->zdir = maxspeed * dz / (dx+dz);
            }
            if(self->direction!=1) self->xdir = -self->xdir;
            if(self->z > other->z) self->zdir = -self->zdir;
            
            if(!self->model->nomove)
            {
     			if(self->z > other->z && self->model->animation[ANI_UP]){	// Used for enemy up animation
     				ent_set_anim(self, ANI_UP, 0);
     			}
     			else if(other->z > self->z && self->model->animation[ANI_DOWN]){	// Used for enemy down animation
     				ent_set_anim(self, ANI_DOWN, 0);
     			}
     			else ent_set_anim(self, ANI_WALK, 0);  
            }
            walk = 1;
        } else if(target && self->parent && self->parent->type == TYPE_PLAYER){
        // start chasing the target
            dx = diff(self->x, target->x);
            dz = diff(self->z, target->z);
            // if target is far away, run instead of walk
            if(dx+dz > 200 && self->model->animation[ANI_RUN]) {maxspeed = self->model->runspeed*1.5; run = 1;}
            else maxspeed = self->model->speed*1.5;
            
            if(!dz && !dx) self->xdir = self->zdir = 0;
            else {
                self->xdir = maxspeed * dx / (dx+dz);
                self->zdir = maxspeed * dz / (dx+dz);
            }
            if(self->direction!=1) self->xdir = -self->xdir;
            if(self->z > target->z) self->zdir = -self->zdir;
            
            if(!self->model->nomove)
            {
                if(run ) ent_set_anim(self, ANI_RUN, 0);
                else {
         			if(self->z > target->z && self->model->animation[ANI_UP]){	// Used for enemy up animation
         				ent_set_anim(self, ANI_UP, 0);
         			}
         			else if(target->z > self->z && self->model->animation[ANI_DOWN]){	// Used for enemy down animation
         				ent_set_anim(self, ANI_DOWN, 0);
         			}
         			else ent_set_anim(self, ANI_WALK, 0);  
                }
            }
            walk = 1;
        }
        else if(target){
            // Decide next direction... randomly.
    
            walk = 0;
            self->xdir = self->zdir = 0;
    
            rnum = rand32();
            if(((rnum & 7) < 3) || diff(self->x, target->x) > (hRes1/2)){
                // Walk forward
                if(self->direction==1) self->xdir = self->model->speed;
                else self->xdir = -self->model->speed;
                walk = 1;
            }
            else if((rnum & 7) > 4){
                // Walk backward
                if(self->direction==1) self->xdir = -self->model->speed/2;
                else self->xdir = self->model->speed/2;
                walk = -1;
            }
    
            rnum = rand32();
            if((rnum & 7) < 2){
                // Move up
                self->zdir = -self->model->speed/2;
                walk |= 1;
            }
            else if((rnum & 7) > 5){
                // Move down
                self->zdir = self->model->speed/2;
                walk |= 1;
            }
    
     		if(walk && !self->model->nomove){
     			if(self->z - target->z > 5 && self->model->animation[ANI_UP]){
     				ent_set_anim(self, ANI_UP, 0);
     			}
     			else if(target->z - self->z > 5 && self->model->animation[ANI_DOWN]){
     				ent_set_anim(self, ANI_DOWN, 0);
     			}
    			else if(walk<0 && self->model->animation[ANI_BACKWALK]){
    				ent_set_anim(self, ANI_BACKWALK, 0);
    			}
     			else ent_set_anim(self, ANI_WALK, 0);
     			// if(walk<0) self->animating = -1;
     		}
    		else{
    			if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
    			else ent_set_anim(self, ANI_IDLE, 0);
    			self->xdir = self->zdir = 0;
    		}
        }
        else { //there is no target or item   
            // wander, randomly 
            walk = 0;
            self->xdir = self->zdir = 0;
    
            rnum = rand32();
            if(((rnum & 7) < 3) ){
                // Walk forward
                if(self->direction==1) self->xdir = self->model->speed;
                else self->xdir = -self->model->speed;
                walk = 1;
            }
            else if((rnum & 7) > 4){
                // Walk backward
                if(self->direction==1) self->xdir = -self->model->speed/2;
                else self->xdir = self->model->speed/2;
                walk = -1;
            }
    
            rnum = rand32();
            if((rnum & 7) < 2){
                // Move up
                self->zdir = -self->model->speed/2;
                walk |= 1;
            }
            else if((rnum & 7) > 5){
                // Move down
                self->zdir = self->model->speed/2;
                walk |= 1;
            }
    
     		if(walk && !self->model->nomove){
     			if(self->zdir > 0 && self->model->animation[ANI_UP]){
     				ent_set_anim(self, ANI_UP, 0);
     			}
     			else if(self->zdir < 0 && self->model->animation[ANI_DOWN]){
     				ent_set_anim(self, ANI_DOWN, 0);
     			}
    			else if(walk<0 && self->model->animation[ANI_BACKWALK]){
    				ent_set_anim(self, ANI_BACKWALK, 0);
    			}
     			else ent_set_anim(self, ANI_WALK, 0);
     			// if(walk<0) self->animating = -1;
     		}
    		else{
    			if(self->model->animation[ANI_FAINT] && self->health <= self->maxhealth / 4) ent_set_anim(self, ANI_FAINT, 0);
    			else ent_set_anim(self, ANI_IDLE, 0);
    			self->xdir = self->zdir = 0;
    		}
        }
        //end of if

        self->stalltime = time + GAME_SPEED - self->aggression;
    }// end of move section

    if((other && other == find_ent_here(self, self->x, self->z, TYPE_ITEM)) && //won't pickup an item that is not previous one
        !other->blink &&
		(diff(self->a - (self->animation->seta[self->animpos] >= 0) * self->animation->seta[self->animpos] , other->a)<0.1)){
        npc_pickupitem(other);
    }

    npc_trymove(self->xdir, self->zdir);
}


entity * smartspawn(s_spawn_entry * props){   // 7-1-2005 Entire section replaced with lord balls code
    entity *e = NULL;

    int dodrop;

    if(!props || (props->spawn2p && count_ents(TYPE_PLAYER)<2) || (props->spawn3p && count_ents(TYPE_PLAYER)<3)|| (props->spawn4p && count_ents(TYPE_PLAYER)<4)) return NULL; //4player	// Now you can make it so enemies/obstacles/etc only spawn if there are 2 players

	e = spawn(props->x + (int)advancex, props->z, props->a, props->name);

    if(e==NULL) return NULL;

    // Alias?
    if(props->alias[0]) strncpy(e->name, props->alias, MAX_NAME_LEN);

	if(props->itemalias[0]) strncpy(e->itemalias, props->itemalias, MAX_NAME_LEN);
	if(props->itemmap) e->itemmap = props->itemmap;
	if(props->itemhealth) e->itemhealth = props->itemhealth;

    if(props->health != 0){
        e->health = props->health;
        e->maxhealth = props->health;
    }

    if(props->mp != 0){
		e->mp = props->mp;
		e->maxmp = props->mp;
	}

    if(props->health2p != 0 && count_ents(TYPE_PLAYER) > 1){	// Now health can be increased for 2 players
		e->health = props->health2p;
		e->maxhealth = props->health2p;
	}
    if(props->health3p != 0 && count_ents(TYPE_PLAYER)>2){	// Now health can be increased for 3 players
		e->health = props->health3p;
		e->maxhealth = props->health3p;  //4player
	}
    if(props->health4p != 0 && count_ents(TYPE_PLAYER)>3){	// Now health can be increased for 4 players
		e->health = props->health4p;
		e->maxhealth = props->health4p; //4player
	}	

    if(props->score != 0) e->score = props->score;	// Overwrite score if exists in the level's. txt file
    if(props->multiple != 0) e->multiple = props->multiple;	// Overwrite multiple if exists in the level's .txt file

    ent_set_colourmap(e, props->colourmap);
    e->map = e->colourmap;	// Used for restoring the map after changing it
    
	if(props->aggression) e->aggression = props->aggression;  // Aggression can be changed with spawn points now
    if(props->itemtrans) e->itemtrans = props->itemtrans;
    if(props->alpha) e->screen = props->alpha;

	// Feb 26, 2005 - Store the original map to be able to restore with dying flash
	if(props->dying){
		e->dying = props->dying;	// Feb 26, 2005 - Used to define which colourmap is used for the dying flash
		e->per1 = props->per1;	// Mar 21, 2005 - Used to store custom percentages
		e->per2 = props->per2;	// Mar 21, 2005 - Used to store custom percentages
	}

	if(props->nolife) e->nolife = props->nolife;	// Overwrite whether live is visible or not

    if(props->boss && level && level->bossmusic[0]){
        music(level->bossmusic, 1);
    }

    e->direction = props->flip;

    switch(e->type){
    case TYPE_ITEM:
        //			e->direction = 1;	//  so items are not flipped
        e->boss = props->boss;	// item bosses?
        break;

    case TYPE_PLAYER:
        e->direction = (level->scrolldir != SCROLL_LEFT);
        e->takedamage = player_takedamage;

        if(time){
            // Mid-level spawn
            if(e->model->animation[ANI_RESPAWN]){
                ent_set_anim(e, ANI_RESPAWN, 0);
                e->think = player_spawn;
            } else {
    			if(level->spawn[e->playerindex][2] > e->a) e->a = level->spawn[e->playerindex][2]; //4player
    
                e->jumping = 1;
                ent_set_anim(e, ANI_JUMP, 0);
                e->think = player_jump;
            }

			if(e->model->makeinv > 0){	// Spawn invincible code
				e->invincible = 1;
				e->blink = 1;
				e->invinctime = time + e->model->makeinv;
				e->arrowon = 1;	// Display the image above the player
			}
        }
        else{

			//ent_set_anim(e, ANI_IDLE, 0);
            if(e->model->animation[ANI_SPAWN]) {
                ent_set_anim(e, ANI_SPAWN, 0);
                e->think = player_spawn;
            }
            else {
                ent_set_anim(e, ANI_IDLE, 0);
                e->think = player_think;
            }
        }

        break;
        
    case TYPE_NPC: // use NPC(or A.I. player) instread of an enemy subtype or trap subtype, for further A.I. use
       //copy a lot from enemy code below
        strncpy(e->item, props->item, MAX_NAME_LEN); //yes, a NPC can hold an item
        e->if2p = props->if2p;
        e->if3p = props->if3p; //4player
        e->if4p = props->if4p; //4player        
        e->boss = props->boss; // NPC boss? Nevermind.

       	if(!e->model->speed && !e->model->nomove) e->model->speed = 1;
    	else if(e->model->nomove) e->model->speed = 0;

    	if(!e->multiple) e->multiple = 5;

        if(e->model->subtype == SUBTYPE_NOTGRAB) e->nograb = 1;

        if(e->model->cantgrab) e->nograb = 1;

        e->takedamage = npc_takedamage; 

        if(stricmp(props->weapon,"")){// spawn with a weapon, a NPC can also have weapons
            e->weapent = spawn(props->x + (int)advancex, 10000, props->a, props->weapon);  
            if(!e->weapent) return NULL;
            set_weapon(e, e->weapent->model->weapnum, -1);
        }

        if(e->model->animation[ANI_SPAWN]) e->think = npc_spawn; 
		else{
            e->think = npc_drop; //npc_drop
            dodrop = (level && (level->scrolldir==SCROLL_UP || level->scrolldir==SCROLL_DOWN));

            if((dodrop && e->x > -30 && e->x < 350) || (props->x > 0 && props->x < 320)){
				// PSP Video Update from 240 to vRes1
                e->a += vRes1 + randf(40);						

                if(e->health < 1){
                    // Funny: drop dead right away
                    ent_set_anim(e, ANI_FALL, 0);
                    e->xdir = (e->direction)?(-1.2):1.2;
                    e->think = npc_fall; 
                }
            }
            dodrop = 0;//reset the temp value for enemy code
        }
        break;

    case TYPE_ENEMY:
        strncpy(e->item, props->item, MAX_NAME_LEN);
        e->if2p = props->if2p;
        e->if3p = props->if3p; //4player
        e->if4p = props->if4p; //4player        
        e->boss = props->boss;
        if(e->model->subtype == SUBTYPE_BIKER){
            e->takedamage = biker_takedamage;
            e->think = biker_drive;
            e->nograb = 1;
            e->attacking = 1;

            if(e->x < 0) e->direction = 0;
            else e->direction = 1;

            e->xdir = 2;
            break;
        }
        // define new subtypes
        else if(e->model->subtype == SUBTYPE_ARROW){
            e->takedamage = enemy_takedamage;
            e->health = 1;

            if(!e->model->speed && !e->model->nomove) e->model->speed = 2;	// Set default speed to 2 for arrows
            else if(e->model->nomove) e->model->speed = 0;

            e->think = arrow_fly;
            e->nograb = 1;
            e->attacking = 1;
            break;
        }
        else{	// Must just be a regular enemy, set defaults accordingly

	       	if(!e->model->speed && !e->model->nomove) e->model->speed = 1;
        	else if(e->model->nomove) e->model->speed = 0;

        	if(!e->multiple) e->multiple = 5;
		}

        if(e->model->subtype == SUBTYPE_NOTGRAB) e->nograb = 1;

        if(e->model->cantgrab) e->nograb = 1;	//27-12-2004 if can't grab is on, character is ungrabbable

        e->takedamage = enemy_takedamage;

        if(stricmp(props->weapon,"")){// spawn with a weapon, 2007-2-12 by  UTunnels
            e->weapent = spawn(props->x + (int)advancex, 10000, props->a, props->weapon);  
            if(!e->weapent) return NULL;
            set_weapon(e, e->weapent->model->weapnum, -1);
        } 

        // 1-10-05  no more enemies dropping in unless they don't have a ANI_SPWN
        if(e->model->animation[ANI_SPAWN]) e->think = enemy_spawn;
		else{
            e->think = enemy_drop;
            dodrop = (level && (level->scrolldir==SCROLL_UP || level->scrolldir==SCROLL_DOWN));

            if((dodrop && e->x > -30 && e->x < 350) || (props->x > 0 && props->x < 320)){
				// PSP Video Update from 240 to vRes1
                e->a += vRes1 + randf(40);						

                if(e->health < 1){
                    // Funny: drop dead right away
                    ent_set_anim(e, ANI_FALL, 0);
                    e->xdir = (e->direction)?(-1.2):1.2;
                    e->think = enemy_fall;
                }
            }
        }
        break;
        // define trap type
    case TYPE_TRAP:
        e->think = trap_think;
        e->takedamage =  enemy_takedamage;
        break;
        //

    case TYPE_OBSTACLE:
        strncpy(e->item, props->item, MAX_NAME_LEN);
        e->if2p = props->if2p;
        e->if3p = props->if3p; //4player
        e->if4p = props->if4p; //4player        
        e->takedamage = obstacle_takedamage;
        e->boss = props->boss;	//  LTB 1-13-05   Obstacle bosses
        ent_set_colourmap(e, props->colourmap);
        break;

    case TYPE_STEAMER:
        e->think = steamer_think;
        e->base = e->a;
        break;

	case TYPE_TEXTBOX:	// New type for displaying text purposes
		e->think = text_think;
		freezeall = 1;	// Pause all animations while text is displayed
		break;

    case TYPE_NONE:
        if(e->model->animation[ANI_WALK]){

            if(props->x < (hRes1/2)) e->xdir = e->model->speed + randf(3);
            else e->xdir = -(e->model->speed + randf(3));

            e->think = anything_walk;
            ent_set_anim(e, ANI_WALK, 0);
        }
        break;
    }
    return e;
}   // 7-1-2005 replaced section ends here



void spawnplayer(int index){
    s_spawn_entry p;
	s_model * model = NULL;
    int w;
    index &= 3;

    model = find_model(player[index].name);
	if(!model) return;

    memset(&p, 0, sizeof(s_spawn_entry));
    strncpy(p.name, model->name, MAX_NAME_LEN);
    p.colourmap = player[index].colourmap;

	if(level->scrolldir==SCROLL_LEFT){
		if(level->spawn[index][0]) p.x = hRes1 - level->spawn[index][0];
		else p.x = (hRes1-20) - 30*index;
	}
	else{
		if(level->spawn[index][0]) p.x = level->spawn[index][0];
		else p.x = 20 + 30*index;
	}

	if(level->spawn[index][1]){
		if(!checkhole(advancex + p.x, PLAYER_MIN_Z + level->spawn[index][1])){
			p.z = PLAYER_MIN_Z + level->spawn[index][1];
		}
		else if(!checkhole(advancex + p.x, (level->holes[holez][1] - level->holes[holez][6] + PLAYER_MIN_Z) / 2)){
			p.z = (level->holes[holez][1] - level->holes[holez][6] + PLAYER_MIN_Z) / 2;
		}
		else{
			p.z = (level->holes[holez][1] + PLAYER_MAX_Z) / 2;
		}
	}
	else if(PLAYER_MAX_Z - PLAYER_MIN_Z > 5) p.z = PLAYER_MIN_Z + 5;  // 6-2-2005 players no longer spawn in holes
	else p.z = PLAYER_MIN_Z;

	if(checkhole(advancex + p.x, p.z)){
		p.z = (level->holes[holez][1] * 2 - level->holes[holez][6]) / 2;

		if(level->scrolldir == SCROLL_LEFT)
			p.x = (level->holes[holez][0] * 2 + level->holes[holez][2] + level->holes[holez][3]) / 2 - advancex - 5;
		else
			p.x = (level->holes[holez][0] * 2 + level->holes[holez][4] + level->holes[holez][5]) / 2 - advancex + 5;
	}

    player[index].ent = smartspawn(&p);

    if(player[index].ent==NULL) shutdown("Fatal: unable to spawn player from '%s'", &p.name);

    player[index].ent->playerindex = index;

    if(player[index].spawnhealth) player[index].ent->health = player[index].spawnhealth + 5;
    if(player[index].ent->health > player[index].ent->model->health) player[index].ent->health = player[index].ent->model->health;

		//mp little recorver after a level by tails
	if(player[index].spawnmp) player[index].ent->mp = player[index].spawnmp + 2;
	if(player[index].ent->mp > player[index].ent->model->mp) player[index].ent->mp = player[index].ent->model->mp;

    for(w=0; w<MAX_ANIS; w++){
        player[index].animation[w] = model->animation[w];
    }

	set_weapon(NULL, level->setweap, index);
}





void time_over(){
	if(level->type == 1) level_completed = 1;		//	Feb 25, 2005 - Used for bonus levels so a life isn't taken away if time expires.level->type == 1 means bonus level, else regular
	else{
		if(!player[0].ent && !player[1].ent) endgame = 1;

		if(player[0].ent && player[0].ent->takedamage){
			self = player[0].ent;
			self->takedamage(self, 1000, 1, 0);
		}
		if(player[1].ent && player[1].ent->takedamage){
			self = player[1].ent;
			self->takedamage(self, 1000, 1, 0);
		}
		if(player[2].ent && player[2].ent->takedamage){  //4player
			self = player[2].ent;
			self->takedamage(self, 1000, 1, 0);
		}
		if(player[3].ent && player[3].ent->takedamage){  //4player
			self = player[3].ent;
			self->takedamage(self, 1000, 1, 0);
		}		

		if(smp_timeover >= 0) sound_play_sample(smp_timeover, 0, savedata.effectvol,savedata.effectvol, 100);

		timeleft = level->settime * COUNTER_SPEED;	// Feb 24, 2005 - This line moved here to set custom time
		showtimeover = 1;
	}
}







// ----------------------- Update functions ------------------------------

void update_scroller(){
	float to;
	int sc1,sc2,sc3,sc4=0; //4player
	int sz1,sz2,sz3,sz4=0; //z auto-scroll
	int numplay=0; //4player    	

	if(time < advancetime || freezeall) return;	// Added freezeall so backgrounds/scrolling don't update if animations are frozen

    //advancetime = time + (GAME_SPEED/100);	// Changed so scrolling speeds up for faster players
    advancetime = time - ((player[0].ent && (player[0].ent->model->speed >= 12 || player[0].ent->model->runspeed >= 12)) ||
    	(player[1].ent && (player[1].ent->model->speed >= 12 || player[1].ent->model->runspeed >= 12)));	// Changed so if your player is faster the backgrounds scroll faster

    if(level_completed) return;

	if(current_spawn>=level->numspawns && !findent(TYPE_ENEMY)){
		if(!findent(TYPE_ENDLEVEL) && ((!findent(TYPE_ITEM) && !findent(TYPE_OBSTACLE) && level->type) || level->type != 1)){	// Feb 25, 2005 - Added so obstacles
			level_completed = 1;												// can be used for bonus levels
		}
	}
    else if(count_ents(TYPE_ENEMY) < groupmin){
        while(count_ents(TYPE_ENEMY) < groupmax &&
        	current_spawn<level->numspawns &&
        	levelpos >= level->spawnpoints[current_spawn].at
		){

            if(level->spawnpoints[current_spawn].wait){
                level_waiting = 1;
                go_time = 0;
            }
            else if(level->spawnpoints[current_spawn].groupmin || level->spawnpoints[current_spawn].groupmax){
                groupmin = level->spawnpoints[current_spawn].groupmin;
                groupmax = level->spawnpoints[current_spawn].groupmax;
            }
            else if(level->spawnpoints[current_spawn].nojoin > 0){
                nojoin = 1;
            }
            else if(level->spawnpoints[current_spawn].nojoin < 0){
                nojoin = 0;
            }
            else smartspawn(&level->spawnpoints[current_spawn]);
            ++current_spawn;
        }
    }

    if(level_waiting){
        // Wait for all enemies to be defeated
        if(!findent(TYPE_ENEMY)){

	        level_waiting = 0;
	        timeleft = level->settime * COUNTER_SPEED;	// Feb 24, 2005 - This line moved here to set custom time
	        go_time = time + 3*GAME_SPEED;
		}
    }

    if(level->scrolldir==SCROLL_RIGHT || level->scrolldir==SCROLL_BOTH){

		if(player[0].ent){
			sc1=(player[0].ent->x);
			numplay++;
		}
		else sc1=0;

		if(player[1].ent){
			sc2=(player[1].ent->x);
			numplay++;
		}
		else sc2=0;

		if(player[2].ent){
			sc3=(player[2].ent->x);
			numplay++;
		}
		else sc3=0;

		if(player[3].ent){
			sc4=(player[3].ent->x);
			numplay++;
		}
		else sc4=0;

		if((sc1+sc2+sc3+sc4) == 0) return;

		to = ((sc1+sc2+sc3+sc4)/numplay);

		if(numplay==1 || numplay==2 || numplay==3 || numplay==4) to -= (hRes1/2);

        if(!level_waiting){

			if(to > advancex){
				if(to > advancex+1) to = advancex+1;
				advancex = to;

				if(level->width - levelpos > hRes1) levelpos = advancex;
				else levelpos++;
			}

			if(level->scrolldir==SCROLL_BOTH){	// Can't go back to the beginning

				if(to < advancex){
					if(to < advancex-1) to = advancex-1;
					advancex = to;
				}
			}

			if(advancex > level->width-hRes1) advancex = level->width-hRes1;
			if(advancex < 0) advancex = 0;

			if(level->width - levelpos > hRes1) levelpos = advancex;
			else levelpos++;
		}
    }
    else if(level->scrolldir==SCROLL_LEFT && !level_waiting){

        if(player[0].ent){
			sc1=(player[0].ent->x);
			numplay++;
		}
		else sc1=0;

		if(player[1].ent){
			sc2=(player[1].ent->x);
			numplay++;
		}
		else sc2=0;

		if(player[2].ent){
			sc3=(player[2].ent->x);
			numplay++;
		}
		else sc3=0;

		if(player[3].ent){
			sc4=(player[3].ent->x);
			numplay++;
		}
		else sc4=0;

		if((sc1+sc2+sc3+sc4) == 0) return;

		to = ((sc1+sc2+sc3+sc4)/numplay);
        
		if(numplay==1 || numplay==2 || numplay==3 || numplay==4) to -= (hRes1/2);

		if(to < advancex){
			if(to < advancex-1) to = advancex-1;
			advancex = to;
		}
		if(advancex > level->width-hRes1) advancex = level->width-hRes1;
		if(advancex < 0) advancex = 0;
		
		if(level->width - levelpos > hRes1) levelpos = (level->width-hRes1) - advancex;
		else levelpos++;
    }
    else if(!level_waiting){
        advancey += 0.5;
        levelpos = advancey;
    }

    // z auto-scroll, 2007 - 2 - 10 by UTunnels
	if(level->scrolldir!=SCROLL_UP && level->scrolldir!=SCROLL_DOWN){
		numplay = 0;
		if(player[0].ent){
			sz1=(player[0].ent->z);
			numplay++;
		}
		else sz1=0;

		if(player[1].ent){
			sz2=(player[1].ent->z);
			numplay++;
		}
		else sz2=0;

		if(player[2].ent){
			sz3=(player[2].ent->z);
			numplay++;
		}
		else sz3=0;

		if(player[3].ent){
			sz4=(player[3].ent->z);
			numplay++;
		}
		else sz4=0;

		if((sz1+sz2+sz3+sz4) == 0) return;

		to = ((sz1+sz2+sz3+sz4)/numplay);

		if(numplay==1 || numplay==2 || numplay==3 || numplay==4) to -= (vRes1/2);

		if(to > advancey){
			if(to > advancey+1) to = advancey+1;
			advancey = to;
		}

		if(to < advancey){
			if(to < advancey-1) to = advancey-1;
			advancey = to;
		}

		if(advancey > panel_height - 16 -vRes1) advancey = panel_height - 16 -vRes1;
		if(advancey < 4) advancey = 4;
	}
    //end of z auto-scroll
}




void draw_scrolled_bg(){
    int i = 0;
	int j = 0;
    int inta = 0;
	int intz = 0;
    int poop = 0;
    int index = 0;
	int fix_y = 0;
    static int neon_count = 0;
    static int rockpos = 0;
    static int rockoffssine[32] = {
                                  2, 2, 3, 4, 5, 6, 7, 7,
                                  8, 8, 9, 9, 9, 9, 8, 8,
                                  7, 7, 6, 5, 4, 3, 2, 2,
                                  1, 1, 0, 0, 0, 0, 1, 1
                                };   // normal rock    
    static int rockoffsshake[32] = {
                                  2, 2, 2, 2, 2, 2, 2, 2,
                                  2, 2, 0, 4, 2, 0, 4, 2,
                                  2, 2, 2, 2, 2, 2, 2, 2,
                                  2, 2, 0, 4, 2, 0, 4, 2,
                                };   // slow, constant jarring rock, like on a train 
    static int rockoffsrumble[32] = {
                                  2, 2, 3, 3, 2, 2, 3, 3,
                                  2, 2, 3, 3, 2, 3, 2, 3,
                                  2, 2, 3, 3, 2, 2, 3, 3,
                                  2, 2, 3, 3, 2, 3, 2, 3,
                                };   // fast, constant rumbling, like in/on a van or trailer
    if(advancex < 0) advancex = 0;

    if(background){
        switch(level->scrolldir){
        case SCROLL_DOWN:
            inta = (advancey+0.5)/2;
            inta %= background->height;
            for(i=-inta; i<vRes1; i+=background->height) copyscreen_o(vscreen, background, 0, i);
            break;
        case SCROLL_UP:
            inta = (advancey+0.5)/2;
            inta %= background->height;
            for(i=inta-background->height; i<vRes1; i+=background->height) copyscreen_o(vscreen, background, 0, i);
            break;
        default:
            inta = (advancex+0.5)/2;
			intz = (advancey+0.5)/2 - 2;
            if(level && level->rocking) inta += (time/(GAME_SPEED/30));
            if(level && level->bgspeed > 0) inta += (time/(30 / level->bgspeed) * 2);
            inta %= background->width;
			intz %= background->height;

            if(!level->bgdir){
            	for(i=-inta; i<hRes1; i+=background->width){
					for(j=-intz;j<vRes1; j+=background->height) copyscreen_o(vscreen, background, i, j);
            	}
			}
			else{
				for(i=inta-background->width; i<hRes1; i+=background->width){
					for(j=-intz;j<vRes1; j+=background->height) copyscreen_o(vscreen, background, i, j);
				}
			}
		}


        // Append bg with texture?
        if(texture && level){
            inta = advancex/2;
            if(level->rocking){
                inta += (time/(GAME_SPEED/30));
                texture_plane(vscreen, 0,background->height, vscreen->width,BGHEIGHT-background->height, inta*256, 10, texture);
            }
            else texture_wave(vscreen, 0,background->height, vscreen->width, BGHEIGHT-background->height, inta,0, texture, time, 5);
        }
    }

    if(level==NULL) return;

    if(level->rocking){
        rockpos = (time/(GAME_SPEED/8)) & 31;
        if(level->rocking == 1)		 gfx_y_offset = quake - 4 - rockoffssine[rockpos];
        else if(level->rocking == 2) gfx_y_offset = quake - 4 - rockoffsshake[rockpos];
        else if(level->rocking == 3) gfx_y_offset = quake - 4 - rockoffsrumble[rockpos];
    }
    else if(time){
		if(quake >= 0) gfx_y_offset = quake - 4;
		else		   gfx_y_offset = quake + 4;
    }

    // z auto-scroll, also affects entities 2007 - 2 - 11 by UTunnels
	// warning: some entities e.g. type-none would use this value
	if(level->scrolldir!=SCROLL_UP && level->scrolldir!=SCROLL_DOWN) gfx_y_offset -= advancey - 4;

    // Draw 3 layers: screen, normal and neon
    if(panels_loaded && panel_width){
        if(time>=neon_time && !freezeall){	// Added freezeall so neon lights don't update if animations are frozen
            for(i=0; i<8; i++) neontable[128+i] = 128 + ((i+neon_count) & 7);
            neon_time = time + (GAME_SPEED/3);
            neon_count += 2;
        }

        if(level->scrolldir==SCROLL_UP || level->scrolldir==SCROLL_DOWN) inta = 0;
        else inta = advancex;

        poop = inta / panel_width;
        inta %= panel_width;
        for(i=-inta; i<=hRes1 && poop>=0 && poop<level->numpanels; i+=panel_width){
            index = level->order[poop];
            if(panels[index].sprite_normal) spriteq_add(i,gfx_y_offset, PANEL_Z, panels[index].sprite_normal, 0, NULL);
            if(panels[index].sprite_neon)   spriteq_add(i,gfx_y_offset, NEONPANEL_Z, panels[index].sprite_neon, SFX_REMAP, neontable);
            if(panels[index].sprite_screen) spriteq_add(i,gfx_y_offset, SCREENPANEL_Z, panels[index].sprite_screen, SFX_BLEND, lut_screen);
            poop++;
        }
    }

    for(i=0; i<level->numholes; i++) spriteq_add(level->holes[i][0]-advancex,level->holes[i][1] - level->holes[i][6] + 4 + gfx_y_offset, HOLE_Z, sprites[1][holesprite], 0, NULL);

    if(frontpanels_loaded){
        
        if(level->scrolldir==SCROLL_UP || level->scrolldir==SCROLL_DOWN) inta = 0;
        else{
            inta = advancex * 1.4;
            fix_y = advancey - 4;
        }
        poop = inta / frontpanels[0]->width;
        inta %= frontpanels[0]->width;
        for(i=-inta; i<=hRes1; i+=frontpanels[0]->width){
            poop %= frontpanels_loaded;
            spriteq_add(i,gfx_y_offset + fix_y, FRONTPANEL_Z, frontpanels[poop], 0, NULL);
            poop++;
        }
    }

    if(quake!=0 && time>=quaketime){
        quake /= 2;
        quaketime = time + (GAME_SPEED/25);
    }
}





void update(int ingame, int usevwait){

    int p, slowmo = 0;
    unsigned long newtime = 0;
	unsigned long interval = 0;

    display_ents();

    if(ingame){
        draw_scrolled_bg();
        predrawstatus();
        slowmo = level_completed;
    }
    else if(background) copyscreen(vscreen, background);

    spriteq_draw(vscreen);
    if(ingame) drawstatus();

    if(bothnewkeys & FLAG_SCREENSHOT) screenshot(vscreen, pal, 1);

    // Debug stuff, should not appear on screenshot
    if(debug_time==0xFFFFFFFF) debug_time = time + GAME_SPEED * 5;
    if(time<debug_time && debug_msg[0]){
        spriteq_clear();
        font_printf(0,230, 0, debug_msg);
        spriteq_draw(vscreen);
    }
    else{
        debug_msg[0] = 0;
#ifdef DEBUG_MODE
        if(levelpos) debug_printf("Position: %i, width: %i, spawn: %i, offsets: %i/%i", levelpos, level->width, current_spawn, quake, gfx_y_offset);
#endif
    }


    if(usevwait) vga_vwait();
    if(rescreen){
        scalescreen(rescreen, vscreen);
        video_copy_screen(rescreen);
    }
    else video_copy_screen(vscreen);

    spriteq_clear();

    control_update(playercontrolpointers, maxplayers);


    bothkeys = 0;
    bothnewkeys = 0;

    for(p=0; p<maxplayers; p++){
        player[p].keys = playercontrolpointers[p]->keyflags;
        player[p].newkeys = playercontrolpointers[p]->newkeyflags;
        player[p].playkeys |= player[p].newkeys;
        player[p].playkeys &= player[p].keys;

        bothkeys |= player[p].keys;
        bothnewkeys |= player[p].newkeys;
    }


    interval = timer_getinterval(GAME_SPEED);
    if(interval > GAME_SPEED) interval = GAME_SPEED/200;
    if(interval > GAME_SPEED/4) interval = GAME_SPEED/4;

    if(slowmo && !level->noslow && !tospeedup){
		if(slo==2) newtime = time + interval;
    }
    else newtime = time + interval;

    slo++;
    if(slo==3) slo = 0;
	if(pause) newtime = time;
    if(newtime > time + 100) newtime = time + 100;

	while(time < newtime){
        update_ents();

        if(ingame){

            update_scroller();

            if(!freezeall){
				if(level->settime > 0 || (!player[0].ent && !player[1].ent && !player[2].ent && !player[3].ent)){	// Now select player time limit works //4player
            		if(timeleft>0) --timeleft;
            		else if(!player[0].joining && !player[1].joining && !player[2].joining && !player[3].joining) time_over();	// Now exits the game if no one selects a character //4player
				}
			}
        }

        ++time;
    }

#if DC
	check_music_opened();
#endif
    sound_update_music();
}




// ----------------------------------------------------------------------



// Simple palette fade
void fade_out(){
    int i, j = 0;
    int b, g = 0;
    unsigned long interval = 0;

    for(i=0, j=0; j<64; ){
        while(j<=i){
            b = ((savedata.brightness+256) * (64-j) / 64) - 256;
            g = 256 - ((savedata.gamma+256) * (64-j) / 64);
            vga_vwait();
            palette_set_corrected(pal, g,savedata.gamma,savedata.gamma, b,b,b);
            j++;
        }

        if(rescreen){
            scalescreen(rescreen, vscreen);
            video_copy_screen(rescreen);
        }
        else video_copy_screen(vscreen);

        sound_update_music();

        if(!musicoverlap) sound_volume_music(savedata.musicvol*(64-j)/64, savedata.musicvol*(64-j)/64);

        interval = timer_getinterval(fade);
        if(interval > fade) interval = fade/60;
        if(interval > fade/4) interval = fade/4;
        i += interval;
    }

    if(!musicoverlap) sound_close_music();

    clearscreen(vscreen);

    if(rescreen){
        scalescreen(rescreen, vscreen);
        video_copy_screen(rescreen);
    }
    else video_copy_screen(vscreen);

    vga_vwait();
    palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
}



void apply_controls(){
    int p;

	for(p=0; p<4; p++){
#if DOS || SDL
        control_setkey(playercontrolpointers[p], FLAG_ESC, 	  CONTROL_ESC);
#endif
		control_setkey(playercontrolpointers[p], FLAG_MOVEUP,     savedata.keys[p][SDID_MOVEUP]);
		control_setkey(playercontrolpointers[p], FLAG_MOVEDOWN,   savedata.keys[p][SDID_MOVEDOWN]);
		control_setkey(playercontrolpointers[p], FLAG_MOVELEFT,   savedata.keys[p][SDID_MOVELEFT]);
		control_setkey(playercontrolpointers[p], FLAG_MOVERIGHT,  savedata.keys[p][SDID_MOVERIGHT]);
		control_setkey(playercontrolpointers[p], FLAG_SPECIAL,    savedata.keys[p][SDID_SPECIAL]);
		control_setkey(playercontrolpointers[p], FLAG_ATTACK,     savedata.keys[p][SDID_ATTACK]);
		control_setkey(playercontrolpointers[p], FLAG_JUMP,       savedata.keys[p][SDID_JUMP]);
		control_setkey(playercontrolpointers[p], FLAG_START,      savedata.keys[p][SDID_START]);
		control_setkey(playercontrolpointers[p], FLAG_SCREENSHOT, savedata.keys[p][SDID_SCREENSHOT]);
	}
}



// ----------------------------------------------------------------------

#ifndef PSP
void display_credits(void){
	unsigned long finishtime = time + 8 * GAME_SPEED;
	int done = 0;

	fade_out();

	unload_background();
    font_unload(0);
    font_unload(1);
	font_unload(2);
	font_unload(4);

	if(!font_load(0, "menu/font1", packfile)) shutdown("Unable to load font #1!\n");
	if(!font_load(1, "menu/font2", packfile)) shutdown("Unable to load font #2!\n");
	if(!font_load(2, "menu/font3", packfile)) shutdown("Unable to load font #3!\n");

	load_background("menu/logo", 0, 0);

	if(!video_set_mode(background->width, background->height)) shutdown("Unable to set video mode: %ix%i!\n", background->width, background->height);
	pal[0] = pal[1] = pal[2] = 0;
	palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);

	bothnewkeys = 0;

	while(!done){

		font_printf(140, 3, 2, "Credits");
		font_printf(15, 35, 1, "Beats Of Rage");
		font_printf(28, 45, 0, "SenileTeam");

		font_printf(15,65,  1, "OpenBOR");
		font_printf(28,75,  0, "SumolX");
		font_printf(84,75,  0, "CGRemakes");
		font_printf(28,85,  0, "Fugue");
		font_printf(84,85,  0, "uTunnels");
		font_printf(28,95,  0, "Kirby");
		font_printf(84,95,  0, "LordBall");
		font_printf(28,105, 0, "Tails");
			 
		font_printf(15,125, 1, "Consoles");
		font_printf(28,135, 0, "DreamCast");
		font_printf(84,135, 0, "Neil Corlett");
		font_printf(28,145, 0, "Sony PSP");
		font_printf(84,145, 0, "SumolX");
		font_printf(28,155, 0, "Windows");
		font_printf(84,155, 0, "Nazo");
		font_printf(28,165, 0, "GamePark");
		font_printf(84,165, 0, "Lemon");
		font_printf(28,175, 0, "MS XBoX");
		font_printf(84,175, 0, "XPort");

		font_printf(15,195, 1, "Menu Design");
		font_printf(28,205, 0, "KingHerb");
		font_printf(84,205, 0, "SumolX");

		font_printf(197,155, 2, "www.LavaLit.com");
		font_printf(190,165, 2, "www.SenileTeam.com");
		
		update(0,0);
		
		done |= (time > finishtime);
		done |= (bothnewkeys & (FLAG_START+FLAG_ESC));
    }
	fade = 75;
	fade_out();
}
#endif


void shutdown(char *msg, ...){

	char buf[2048] = "";
	va_list arglist;
	
	va_start(arglist, msg);
	vsprintf(buf, msg, arglist);
	va_end(arglist);

	savesettings();

#ifndef PSP
	display_credits();
#endif

	printf("\n********* Shutting Down *********\n");

#if PSP
	// How much memory is available prior to shutdown?
	printf("Memory  Available: %u Bytes\n",ramAvailable());
#endif

    printf("Release level data...\n");
    unload_levelorder();
    unload_level();

    printf("Release graphics data...\n");
    freescreen(vscreen);
    freescreen(rescreen);
    freesprites();
    font_unload(0);
    font_unload(1);
    font_unload(2);
    font_unload(3);    // 5-1-2005 Last font wasn't being unloaded

    printf("Release game data...\n");
    free_ents();
    free_models();
    free_modelcache();

    printf("Release timer...\n");
    timer_exit();

    printf("Release input hardware...\n");
    control_exit();

    printf("Release sound system...\n");
    sound_exit();

#if PSP || DC
	printf("Release FileCaching System...\n");
	pak_term();
#endif
	
	printf("************* Done **************\n\n");
    printf(buf);

	exit(0);
}


#ifndef PSP
void guistartup(void){
    int i;

    if((vscreen = allocscreen(hRes1, vRes1)) == NULL) shutdown("Not enough memory!\n");
    clearscreen(vscreen);

	if(!font_load(0, "menu/font1", packfile)) shutdown("Unable to load font #1!\n");
	if(!font_load(1, "menu/font2", packfile)) shutdown("Unable to load font #2!\n");
	if(!font_load(2, "menu/font3", packfile)) shutdown("Unable to load font #3!\n");

    timer_init();

    if(!alloc_ents()) shutdown("Not enough memory for game objects!\n");
	
	control_init(0);
    apply_controls();

    if(rescreen){
        if(!video_set_mode(rescreen->width, rescreen->height)){
            shutdown("Unable to set video mode: %ix%i!\n", rescreen->width, rescreen->height);
        }
    }
    else if(!video_set_mode(hRes1, vRes1)) shutdown("Unable to set video mode: 320x240!\n");

    for(i=0; i<256; i++) neontable[i] = i;
}
#endif

void startup(void){
	int i;

#if PSP || DC
	printf("FileCaching System init...\n");
	if(!pak_init()) shutdown("PAK Error Maybe File Not Found\n");
#endif

#if PSP
	if(savedata.pspcpuspeed<0) savedata.pspcpuspeed = 2;
	if(savedata.pspcpuspeed>2) savedata.pspcpuspeed = 0;
	switch(savedata.pspcpuspeed){
		case 0:
			scePowerSetClockFrequency(222, 222, 111);
			break;
		case 1:
			scePowerSetClockFrequency(266, 266, 133);
			break;
		case 2:
			scePowerSetClockFrequency(333, 333, 166);
			break;
	}
#endif

	load_stringtable(stringtable, "data/langpack.txt");
	loadHighScoreFile();
	clearSavedGame();

#if PSP || SDL
	// Load Video Mode from file 'data/video.txt'
	load_videomode(0);
	if(savedata.fps) fpsCounter = 1;
#endif

	printf("Screen allocation...\n");
	if((vscreen = allocscreen(hRes1, vRes1)) == NULL){
		shutdown("Not enough memory!\n");
	}
	clearscreen(vscreen);

	printf("Loading font...\n");
	if(!font_load(0, "data/sprites/font", packfile)) shutdown("Unable to load font #1!\n");
	if(!font_load(1, "data/sprites/font2", packfile)) shutdown("Unable to load font #2!\n");
	if(!font_load(2, "data/sprites/font3", packfile)) shutdown("Unable to load font #3!\n");
	if(!font_load(3, "data/sprites/font4", packfile)) shutdown("Unable to load font #4!\n");


	printf("Timer init...\n");
	timer_init();

	printf("Initilize Sound...\n");
	if(savedata.usesound && sound_init(64)){
	    if(!sound_start_playback(savedata.soundstereo,savedata.soundbits,savedata.soundrate)) printf("Warning: can't play sound at %u Hz!\n", savedata.soundrate);

#ifndef XBOX
		SB_setvolume(SB_MASTERVOL, 15);
		SB_setvolume(SB_VOICEVOL, savedata.soundvol);
#endif
		load_special_sounds();
	}
	else shutdown("Unable to Initilize Sound.\n");

	printf("Object engine init...\n");
	if(!alloc_ents()) shutdown("Not enough memory for game objects!\n");

	printf("Loading sprites...\n");
	load_special_sprites();		// Changed to conserve memory

	printf("Loading models...\n");
	load_models();

	printf("Loading level order...\n");
	load_levelorder();

	printf("Input init...\n");
	control_init(savedata.usejoy);
	apply_controls();

	printf("Video mode...\n");
	if(rescreen){
		if(!video_set_mode(rescreen->width, rescreen->height)) shutdown("Unable to set video mode: %ix%i!\n", rescreen->width, rescreen->height);
	}
	else if(!video_set_mode(hRes1, vRes1)) shutdown("Unable to set video mode: %d x %d!\n", hRes1, vRes1);

	for(i=0; i<256; i++) neontable[i] = i;

	savesettings();
}







// ----------------------------------------------------------------------------


// Returns 0 on error, -1 on escape
int playgif(char *filename, int x, int y){
	int code;
	int delay;
	unsigned long milliseconds;
	unsigned long nextframe;
	unsigned long lasttime;
	int done;
	int frame = 0;
	int synctosound = 0;


	// Clear background
	unload_background();
	background = allocscreen(hRes2, vRes2);
	if(background==NULL) shutdown("Out of memory! Function Call 'playgif'");

	if(!video_set_mode(hRes2, vRes2)) shutdown("Out of memory! Function Call 'playgif'");
	clearscreen(vscreen);
	
	if(!anigif_open(filename, packfile, pal)) return 0;

	time = 0;
	lasttime = 0;
	milliseconds = 0;
	nextframe = 0;
	delay = 100;
	code = ANIGIF_DECODE_RETRY;
	done = 0;
	synctosound = (sound_getinterval() != 0xFFFFFFFF);

	while(!done){
		if(milliseconds >= nextframe){
			if(code != ANIGIF_DECODE_END){
				while((code = anigif_decode(background, &delay, x, y)) == ANIGIF_DECODE_RETRY);
				// if(code == ANIGIF_DECODE_FRAME){
				// Set time for next frame
				nextframe += delay * 10;
				// }
			}
			else done = 1;
		}
		if(code == ANIGIF_DECODE_END) break;

		if(frame==0){
			vga_vwait();
			pal[0] = pal[1] = pal[2] = 0;
			palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
			update(0,0);
		}
		else update(0,1);

		++frame;

		if(synctosound){
			milliseconds += sound_getinterval();
			if(milliseconds==0xFFFFFFFF) synctosound = 0;
		}
		if(!synctosound) milliseconds += (time-lasttime) * 1000 / GAME_SPEED;
		lasttime = time;

		if(bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)) done = 1;
	}
	anigif_close();

	if(bothnewkeys & (FLAG_ESC | FLAG_ANYBUTTON)) return -1;
	return 1;
}




void playscene(char *filename){

	int handle;
	char *buf;
	unsigned int size;
	int pos;
	char * command = NULL;
	char giffile[256];
	int x=0, y=0;
	int closing = 0;


	// Read file
	if((handle=openpackfile(filename,packfile)) < 0) return;
	size = seekpackfile(handle,0,SEEK_END);
	seekpackfile(handle,0,SEEK_SET);

	buf = (char*)tracemalloc("playscene",size+1);
	if(buf == NULL){
		closepackfile(handle);
		return;
	}
	if(readpackfile(handle, buf, size) != size){
		if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
		closepackfile(handle);
		return;
	}
	buf[size] = 0;		// Terminate string (important!)
	closepackfile(handle);


	// Now interpret the contents of buf line by line
	pos = 0;
	while(buf[pos]){
		command = findarg(buf+pos, 0);
		if(command[0]){
			if(!closing && stricmp(command, "music")==0){
				music(findarg(buf+pos, 1), atoi(findarg(buf+pos, 2)));
			}
			else if(!closing && stricmp(command, "animation")==0){
				strcpy(giffile, findarg(buf+pos, 1));
				x = atoi(findarg(buf+pos, 2));
				y = atoi(findarg(buf+pos, 3));
				if(playgif(giffile, x, y) == -1) closing = 1;
			}
			else if(stricmp(command, "silence")==0){
				sound_close_music();
			}
		}
		// Go to next non-blank line
		while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
		while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
	}
	if(buf != NULL){
		tracefree(buf);
		buf = NULL;
	}
}




// ----------------------------------------------------------------------------




void gameover(){
	int done = 0;
	char tmpBuff[128] = {""};

	unload_background();

	background = allocscreen(hRes2, vRes2);
	if(background==NULL) shutdown("Out of memory! Function Call 'gameover'");
	clearscreen(background);

	if(!video_set_mode(hRes2, vRes2)) shutdown("Out of memory! Function Call 'gameover'");
	clearscreen(vscreen);
	standard_palette();

	music("data/music/gameover.bor", 0);

	time = 0;
	while(!done){
		// ltb 1-17-05 check to see if scene exists if so use it instead of text
		// New alternative scene path for PSP
		if(strcmp(customScenes,"empty") == 0){
            if(openpackfile("data/scenes/gameover.txt", packfile) >=0) playscene("data/scenes/gameover.txt"); // 1-17-05
			else font_printf(113+hOffset,110+vOffset+yOffset, 3, "%s",stringtable[9]); // Game over
		}
		else{	
			strcpy(tmpBuff,customScenes);
			strcat(tmpBuff,"gameover.txt");
            if(openpackfile(tmpBuff, packfile) >=0) playscene(tmpBuff); // 1-17-05
			else font_printf(113+hOffset,110+vOffset+yOffset, 3, "%s",stringtable[9]); // Game over
		}
		
		done |= (time>GAME_SPEED*8 && !sound_query_music(NULL,NULL));
        done |= (bothnewkeys & (FLAG_ESC|FLAG_ANYBUTTON));
		update(0,0);
	}

	unload_background();
}




void hallfame(int addtoscore){
	int done = 0;
	int topten[10] = { 0,0,0,0,0,0,0,0,0,0 };
	unsigned long score;
	char name[MAX_NAME_LEN+1];
	int i, p, y;
	char tmpBuff[128] = {""};
	s_model *model = NULL;

	unload_background();

	background = allocscreen(hRes2, vRes2);
	if(background==NULL) shutdown("Out of memory! Function Call 'ShowComplete'");
	clearscreen(background);

	if(!video_set_mode(hRes2, vRes2)) shutdown("Out of memory! Function Call 'ShowComplete'");
	clearscreen(vscreen);
	standard_palette();

	if(hiscorebg){
		// New alternative background path for PSP
		if(strcmp(customBackgrounds,"empty") == 0) load_background("data/bgs/hiscore", 0, 0);
		else{	
			strcpy(tmpBuff,customBackgrounds);
			strcat(tmpBuff,"hiscore");
			load_background(tmpBuff, 0, 0);
		} 
	}

	if(addtoscore){

		for(p = 0; p < maxplayers; p++){

			model = find_model(player[p].name);

			if(player[p].score > savescore.highsc[9]){
				savescore.highsc[9] = player[p].score;
				strcpy(savescore.hscoren[9], model->name);
				topten[9] = 1;

				for(i = 8; i >= 0 && player[p].score > savescore.highsc[i]; i--){
					score = savescore.highsc[i];
					strcpy(name, savescore.hscoren[i]);
					savescore.highsc[i] = player[p].score;
					strcpy(savescore.hscoren[i], model->name);
					topten[i] = 1;
					savescore.highsc[i + 1] = score;
					strcpy(savescore.hscoren[i + 1], name);
					topten[i + 1] = 0;
				}
			}
		}
	}

	time = 0;

	while(!done){
		y = 56;
		if(!hiscorebg) font_printf(113+hOffset,y-36+vOffset+yOffset, 3, "%s",stringtable[72]); // Hall of fame

		for(i = 0; i < 10; i++){
			font_printf(113+hOffset,y+vOffset+yOffset,topten[i]," %i - %s - %06lu",i+1,savescore.hscoren[i],savescore.highsc[i]);
			y += 16;
		}

		update(0,0);
		done |= (time>GAME_SPEED*8);
		done |= (bothnewkeys & (FLAG_START+FLAG_ESC));
	}

	unload_background();
}




// Level completed, show bonus stuff
void showcomplete(int num){
	int done = 0;
	int clearbonus[4] = { 10000, 10000, 10000, 10000 };
	int lifebonus[4];
	int i, j, k;
	unsigned long nexttime = 0;
	unsigned long finishtime = 0;
	int chan = 0;
	char tmpBuff[128] = {""};

	unload_background();

	background = allocscreen(hRes2, vRes2);
	if(background==NULL) shutdown("Out of memory! Function Call 'ShowComplete'");
	clearscreen(background);

	if(!video_set_mode(hRes2, vRes2)) shutdown("Out of memory! Function Call 'ShowComplete'");
	clearscreen(vscreen);
	standard_palette();

	if(completebg){
		// New alternative background path for PSP
		if(strcmp(customBackgrounds,"empty") == 0) load_background("data/bgs/complete", 0, 0);
		else{	
			strcpy(tmpBuff,customBackgrounds);
			strcat(tmpBuff,"complete");
			load_background(tmpBuff, 0, 0);
		} 
	}

	music("data/music/complete.bor", 0);

	for(i=0; i<maxplayers; i++) lifebonus[i] = player[i].lives * 1000;

	update(0,0);

	time = 0;
	while(!done){
		if(!scomplete[5]) font_printf(hOffset+xOffset+scomplete[0],vOffset+yOffset+scomplete[1], 3, "%s %i %s",stringtable[67], num,stringtable[68]); //Stage %i complete!
		else{
			font_printf(hOffset+xOffset+scomplete[0],vOffset+yOffset+scomplete[1], 3, "Stage");
			font_printf(hOffset+xOffset+scomplete[2],vOffset+yOffset+scomplete[3], 3, "%i",num);
			font_printf(hOffset+xOffset+scomplete[4],vOffset+yOffset+scomplete[5], 3, "Complete");
		}

        font_printf(hOffset+xOffset+cbonus[0],vOffset+yOffset+cbonus[1], 0, "%s",stringtable[69]);//Clear bonus
		for(i=0, j=2, k=3; i < maxplayers; i++, j=j+2, k=k+2) if(player[i].lives > 0) font_printf(hOffset+xOffset+cbonus[j],vOffset+yOffset+cbonus[k], 0, "%06i", clearbonus[i]);
	
        font_printf(hOffset+xOffset+lbonus[0],vOffset+yOffset+lbonus[1], 0, "%s",stringtable[70]);//Life bonus
		for(i=0, j=2, k=3; i < maxplayers; i++, j=j+2, k=k+2) if(player[i].lives > 0) font_printf(hOffset+xOffset+lbonus[j],vOffset+yOffset+lbonus[k], 0, "%06i", lifebonus[i]);

        font_printf(hOffset+xOffset+tscore[0],vOffset+yOffset+tscore[1], 0, "%s",stringtable[71]);//Total score
		for(i=0, j=2, k=3; i < maxplayers; i++, j=j+2, k=k+2) if(player[i].lives > 0) font_printf(hOffset+xOffset+tscore[j],vOffset+yOffset+tscore[k], 0, "%06i", player[i].score);

		while(time > nexttime){
			if(!finishtime)	finishtime = time + 4 * GAME_SPEED;

			for(i=0; i<maxplayers; i++){
				if(player[i].lives > 0){
					if(clearbonus[i] > 0){
						addscore(i, 10);
						clearbonus[i] -= 10;
						finishtime = 0;
					}
					else if(lifebonus[i] > 0){
						addscore(i, 10);
						lifebonus[i] -= 10;
						finishtime = 0;
					}
				}
			}

			if(!finishtime && !(nexttime&15)){
				sound_stop_sample(chan);
				chan = sound_play_sample(smp_beep, 0, savedata.effectvol/2,savedata.effectvol/2, 100);
			}

			nexttime++;
		}

		if(bothnewkeys & (FLAG_ANYBUTTON|FLAG_ESC)) done = 1;
		if(finishtime && time>finishtime) done = 1;

		update(0,0);
	}

	// Add remainder of score, incase player skips counter
    for(i=0; i<maxplayers; i++){
		if(player[i].lives > 0){
			addscore(i, clearbonus[i]);
			addscore(i, lifebonus[i]);
		}
	}

	unload_background();
}





int playlevel(char *filename){
	int i;

	kill_all();

	for(i=0; i<maxplayers; i++){
		savelevel[saveslot].pLives[i] = player[i].lives;
		savelevel[saveslot].pCredits[i] = player[i].credits;
		savelevel[saveslot].pScores[i] = player[i].score;
	}
	savelevel[saveslot].credits = credits;
	saveGameFile();
	saveHighScoreFile();

    printf("Loading level '%s'\n", filename);

	load_level(filename);
	time = 0;
	
	// Fixes the start level executing last button bug
	for(i=0; i<maxplayers; i++){
		if(player[i].lives > 0){
			player[i].newkeys = player[i].playkeys = 0;	
			spawnplayer(i);
		}
	}

    while(!endgame){
        update(1,0);
        if(level_completed) endgame |= (!findent(TYPE_ENEMY) || level->type || findent(TYPE_ENDLEVEL));	// Ends when all enemies die or a bonus level
    }
    fade_out();

   	for(i=0; i<maxplayers; i++){
		if(player[i].ent){
			player[i].spawnhealth = player[i].ent->health;
			player[i].spawnmp = player[i].ent->mp;
			set_weapon(NULL, 0, i);
		}
	}

    if(!musicoverlap) sound_close_music();

    kill_all();
    unload_level();

    return (player[0].lives > 0 || player[1].lives > 0 || player[2].lives > 0|| player[3].lives > 0); //4player
}




int selectplayer(int p1on, int p2on, int p3on, int p4on){

	entity * example[4] = {NULL,NULL,NULL,NULL};
    int i,x;
    int cmap[4] = {0,1,2,3};
    int tperror = 0;
    int exit = 0;
    int ready[4] = {0,0,0,0};
    int escape = 0;
    int players_busy = 0;
    int players_ready = 0;
    int immediate[4];
	char tmpBuff[128] = {""};
    
	selectScreen = 1;

	if(loadGameFile()){
		bonus = 0;
		for(i=0; i<MAX_DIFFICULTIES; i++) if(savelevel[i].times_completed > 0) bonus += savelevel[i].times_completed;
	}

	if(unlockbg && bonus){
		// New alternative background path for PSP
		if(strcmp(customBackgrounds,"empty") == 0) load_background("data/bgs/unlockbg", 1, 0);
		else{	
			strcpy(tmpBuff,customBackgrounds);
			strcat(tmpBuff,"unlockbg");
			load_background(tmpBuff, 1, 0);
		}
	}
	else{
		// New alternative background path for PSP
		if(strcmp(customBackgrounds,"empty") == 0) load_background("data/bgs/select", 1, 0);
		else{	
			strcpy(tmpBuff,"");
			strcpy(tmpBuff,customBackgrounds);
			strcat(tmpBuff,"select");
			load_background(tmpBuff, 1, 0);
		}
	}

    kill_all();

    music("data/music/menu.bor", 1);

    memset(&player[0], 0, sizeof(s_player));
    memset(&player[1], 0, sizeof(s_player));
	memset(&player[2], 0, sizeof(s_player)); //4player
	memset(&player[3], 0, sizeof(s_player)); //4player

    immediate[0] = p1on;
    immediate[1] = p2on;
	immediate[2] = p3on; //4player
	immediate[3] = p4on; //4player
    

    while(!(exit || escape)){
        players_busy = 0;
        players_ready = 0;
        for(i=0; i<maxplayers; i++){
            // you can't have that character!
            if((tperror == i+1) && (ready[i] == 0)) font_printf(75+hOffset,123,0,"%s %d %s",stringtable[64],i+1,stringtable[65]); // Player x Choose a Different Character! //4player

			if(!ready[i]){
                if(player[i].lives <= 0 && ((player[i].newkeys & FLAG_ANYBUTTON) || immediate[i])){

				    if(!noshare) credits = CONTINUES;
				    else{
						player[i].credits = CONTINUES;
						player[i].hasplayed = 1;
					}

                    if(!creditscheat){
						if(noshare) --player[i].credits;
						else --credits;
					}

                    player[i].lives = PLAYER_LIVES;

					if(!psmenu[i][0] && !psmenu[i][1]){
						if(maxplayers > 2) example[i] = spawn((111-(maxplayers*14))+((i*(320-(166/maxplayers))/maxplayers)+hOffset),230+yOffset+vOffset,0, nextplayermodel(NULL)->name); //4player
						else example[i] = spawn(83+(hOffset/2)+(i*(155+hOffset)),230+yOffset+vOffset,0, nextplayermodel(NULL)->name);
					}
					else example[i] = spawn(psmenu[i][0], psmenu[i][1], 0, nextplayermodel(NULL)->name);
					

					if(example[i]==NULL) shutdown("Failed to create player selection object!");

                    example[i]->direction = spdirection[i];					// Select Player Direction for select player screen
                    player[i].colourmap = i;  // 6-2-2005 make player 2 different colour automatically
                    ent_set_colourmap(example[i], i);

                    if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
                }
                else if(player[i].newkeys & FLAG_MOVELEFT && example[i]){	// Added && example[i] so it wouldn't crash

                    if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);

                    ent_set_model(example[i], prevplayermodel(example[i]->model)->name);
                    cmap[i] = i;
                    ent_set_colourmap(example[i], cmap[i]);
                    tperror = 0;	// all is good
                }
                else if(player[i].newkeys & FLAG_MOVERIGHT && example[i]){	// Added && example[i] so it wouldn't crash

                    if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);

                    ent_set_model(example[i], nextplayermodel(example[i]->model)->name);
                    cmap[i] = i;
                    ent_set_colourmap(example[i], cmap[i]);
                    tperror = 0;	// all is good
                }
                // oooh pretty colors! - selectable color scheme for player characters
                else if(player[i].newkeys & FLAG_MOVEUP && colourselect && example[i]){  // Added && example[i] so it wouldn't crash
                    do{
						cmap[i]++;
						if(cmap[i] > example[i]->model->maps_loaded) cmap[i] = 0;
					}
					while(
						example[i]->model->fmap &&
	                   	example[i]->model->colourmap[cmap[i] - 1] == example[i]->model->colourmap[example[i]->model->fmap - 1]
	                );

                    ent_set_colourmap(example[i], cmap[i]);
                }
                else if(player[i].newkeys & FLAG_MOVEDOWN && colourselect && example[i]){ // 	// Added && example[i] so it wouldn't crash
                    do{
						cmap[i]--;
						if(cmap[i] < 0) cmap[i] = example[i]->model->maps_loaded;
					}
					while(
						example[i]->model->fmap &&
	                   	example[i]->model->colourmap[cmap[i] - 1] == example[i]->model->colourmap[example[i]->model->fmap - 1]
	                );

                    ent_set_colourmap(example[i], cmap[i]);
                }
                //
                else if(player[i].newkeys & FLAG_ANYBUTTON){

                    if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

                    strcpy(player[i].name, example[i]->model->name);
                    player[i].colourmap = cmap[i];

                    // reports error if players try to use the same character and sameplay mode is off
					if(sameplayer){
						for(x=0; x<maxplayers; x++){
							if((strncmp(player[i].name,player[x].name,strlen(player[i].name)) == 0) && (i != x)){
								tperror = i+1;
								break;
							}
						}
					}

					if (!tperror){ //4player
                        time = 0;
                        // yay you picked me!
                        if(example[i]->model->animation[ANI_PICK]) ent_set_anim(example[i], ANI_PICK, 0);
                        //
                        while((!ready[i]) && (((ready[0] == 1) || (ready[1] == 1) || (ready[2] == 1)) || ((example[0]==NULL) || (example[1]==NULL)|| (example[2]==NULL)))){  //4player
                            update(0,0);
                            ready[i] |= (time>GAME_SPEED*2);
                        }
                        ready[i] = 1;
                    }
                }
            }
			else{
				if(!psmenu[i][2] && !psmenu[i][3]){
					if(maxplayers > 2) font_printf((95-(maxplayers*14))+((i*(320-(166/maxplayers))/maxplayers)+hOffset),225+yOffset+vOffset,0,"%s",stringtable[66]);
					else font_printf(67+(hOffset/2)+(i*(155+hOffset)),225+yOffset+vOffset,0,"%s",stringtable[66]);
				}
				else font_printf(psmenu[i][2], psmenu[i][3], 0, "%s", stringtable[66]);
			}
	
            if(example[i] != NULL) players_busy++;
            if(ready[i]) players_ready++;
        }

        if(players_busy && players_busy==players_ready) exit = 1;

        update(0,0);

        if(bothnewkeys & FLAG_ESC) escape = 1;
    }

	selectScreen = 0;	// No longer at the select screen
    kill_all();
    sound_close_music();


    return (!escape);
}




void playgame(int p1on, int p2on, int p3on, int p4on,  unsigned which_set, int useSavedGame){
	int current_level = 0;
	int stage = 1;
	int i;

    if(which_set>=num_difficulties) return;
    // shutdown("Illegal set chosen: index %i (there are only %i sets)!", which_set, num_difficulties);

    allow_secret_chars = ifcomplete[which_set];
    PLAYER_LIVES = difflives[which_set];
    musicoverlap=diffoverlap[which_set];
    fade=custfade[which_set];
    CONTINUES = diffcreds[which_set];
    magic_type = typemp[which_set];
    if(PLAYER_LIVES == 0) PLAYER_LIVES = 3;
    if(CONTINUES == 0) CONTINUES = 5;
    if(fade == 0) fade = 24;
    sameplayer = same[which_set];

	if(useSavedGame){                            
		current_level = savelevel[saveslot].level;
		stage = savelevel[saveslot].stage;

		for(i=0; i<maxplayers; i++){
			player[i].lives = savelevel[saveslot].pLives[i];
			player[i].score = savelevel[saveslot].pScores[i];
		}
	}

	// Auto update window position for 4x3 mods to display in widescreen
	if(videoMode < 3 && savedata.videoFile && maxplayers == 2){
		shiftpos[0] = hOffset / 2 + xOffset / 2;
		shiftpos[1] = hOffset + hOffset / 2 + xOffset + xOffset / 2;
	}
	else if(videoMode < 3 && savedata.videoFile && maxplayers == 4 && !ctrlmaxplayers){
		shiftpos[0] = hOffset / 2 + xOffset / 2;
		shiftpos[1] = hOffset + hOffset / 2 + xOffset + xOffset / 2;
		shiftpos[2] = hOffset / 2 + xOffset / 2;
		shiftpos[3] = hOffset + hOffset / 2 + xOffset + xOffset / 2;
	}
	else if(videoMode < 3 && savedata.videoFile && maxplayers == 4 && ctrlmaxplayers){
		shiftpos[0] = hOffset / 4 + xOffset / 4;
		shiftpos[1] = hOffset / 2 + xOffset / 2;
		shiftpos[2] = hOffset + hOffset / 4 + xOffset + xOffset / 4;
		shiftpos[3] = hOffset + hOffset / 2 + xOffset + xOffset / 2;
	}

    if(selectplayer(p1on, p2on, p3on, p4on)){
		for(i=0; i<maxplayers; i++) if(select_scene[i]) playscene(select_scene[i]);

		while(current_level < num_levels[which_set]){
            if(branch_name[0]) { // branch checking
                for(i=0; i<num_levels[which_set]; i++){
                    if(stricmp(levelorder[which_set][i]->branchname, branch_name)==0){
                        current_level = i;
                        branch_name[0] = 0;// clear up so we won't stuck here
                        break;
                    }
                }
            }
			PLAYER_MIN_Z = levelorder[which_set][current_level]->z_coords[0];
			PLAYER_MAX_Z = levelorder[which_set][current_level]->z_coords[1];
			BGHEIGHT = levelorder[which_set][current_level]->z_coords[2];

			if(levelorder[which_set][current_level]->is_scene){
				playscene(levelorder[which_set][current_level]->filename);
			}
			else if(!playlevel(levelorder[which_set][current_level]->filename)){
				  if(player[0].lives <= 0 && player[1].lives <= 0 && player[2].lives <= 0 && player[3].lives <= 0){
					gameover();
					hallfame(1);
					for(i=0; i<maxplayers; i++){
						player[i].hasplayed = 0;
						player[i].weapnum = 0;
						set_weapon(NULL, 0, i);
					}
				}
				break;
			}
			if(levelorder[which_set][current_level]->gonext){
				showcomplete(stage);
				for(i=0; i<maxplayers; i++){
					player[i].spawnhealth = 0;
					player[i].spawnmp = 0;
				}
				++stage;
            	savelevel[saveslot].stage = stage;
			}
			current_level++;
         	savelevel[saveslot].level = current_level;
         	//2007-2-24, gonext = 2, end game
         	if(levelorder[which_set][current_level-1]->gonext == 2){
                current_level = num_levels[which_set];
            }
		}

		if(current_level >= num_levels[which_set]){
         	bonus += savelevel[saveslot].times_completed++; 
			saveGameFile();
			saveHighScoreFile();
			fade_out();
			hallfame(1);
		}
    }
    sound_close_music();
}





int choose_difficulty(){
	int quit = 0;
	int selector = 0;
	int i;

	bothnewkeys = 0;
	
	if(loadGameFile()){
		bonus = 0;
		for(i=0; i<MAX_DIFFICULTIES; i++) if(savelevel[i].times_completed > 0) bonus += savelevel[i].times_completed;
	}

	while(!quit){
		font_printf(129+hOffset,140+vOffset, 2, "%s",stringtable[60]);//Difficulty
		for(i=0; i<num_difficulties; i++){
			if(bonus >= ifcomplete[i]) font_printf(129+hOffset,160+i*10+vOffset, (selector==i), set_names[i]);
			else{
				if(ifcomplete[i]>1) font_printf(129+hOffset,130+i*10+vOffset, (selector==i), "%s %s %i %s", set_names[i],stringtable[61], ifcomplete[i],stringtable[62]); //- finish game, times to unlock
				font_printf(129+hOffset,160+i*10+vOffset, (selector==i), "%s %s", set_names[i],stringtable[63]); //- finish game to unlock
			}
		}

		font_printf(129+hOffset,165+i*10+vOffset, (selector==i), "%s",stringtable[8]);//Back
		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;
			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;
			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(selector<0) selector = i;
		if(selector>i) selector = 0;

		if(bothnewkeys & FLAG_ANYBUTTON){

			if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

			if(selector==i) quit = 1;
			else if(bonus >= ifcomplete[selector]){
				saveslot = selector;
				strncpy(savelevel[saveslot].dName, set_names[saveslot], MAX_NAME_LEN+1);
				return saveslot;
			}
		}
	}
	bothnewkeys = 0;
	return -1;
}



int load_saved_game(){
	int quit = 0;
	int selector = 0;
	int savedStatus = 0;
	char name[256] = {""};
		
	bothnewkeys = 0;
	
	if((savedStatus = loadGameFile())) getPakName(name,0);

	for(saveslot=0; saveslot<MAX_DIFFICULTIES; saveslot++) if(savelevel[saveslot].level) break;

	while(!quit){
		font_printf(60+hOffset,90+vOffset+yOffset, 2, "Load Game");
		font_printf(60+hOffset,110+vOffset+yOffset, 0, "Saved File:");
		if(savedStatus) font_printf(200+hOffset,110+vOffset+yOffset, 0, "%s",name);
		else font_printf(200+hOffset,110+vOffset+yOffset, 0, "Not Found");

		if(savedStatus){
			font_printf(60+hOffset,120+vOffset+yOffset, (selector==0), "Mode:");
			font_printf(200+hOffset,120+vOffset+yOffset, (selector==0), "%s", savelevel[saveslot].dName);
			font_printf(60+hOffset,130+vOffset+yOffset, 0, "Stage:");
			font_printf(200+hOffset,130+vOffset+yOffset, 0, "%d", savelevel[saveslot].stage);
			font_printf(60+hOffset,140+vOffset+yOffset, 0, "Level:");
			font_printf(200+hOffset,140+vOffset+yOffset, 0, "%d", savelevel[saveslot].level);
			font_printf(60+hOffset,150+vOffset+yOffset, 0, "Credits:");
			font_printf(200+hOffset,150+vOffset+yOffset, 0, "%d", savelevel[saveslot].credits);
			font_printf(60+hOffset,160+vOffset+yOffset, 0, "Player 1 Lives:");
			font_printf(200+hOffset,160+vOffset+yOffset, 0, "%d", savelevel[saveslot].pLives[0]);
			font_printf(60+hOffset,170+vOffset+yOffset, 0, "Player 2 Lives:");
			font_printf(200+hOffset,170+vOffset+yOffset, 0, "%d", savelevel[saveslot].pLives[1]);
			font_printf(60+hOffset,180+vOffset+yOffset, 0, "Player 3 Lives:");
			font_printf(200+hOffset,180+vOffset+yOffset, 0, "%d", savelevel[saveslot].pLives[2]);
			font_printf(60+hOffset,190+vOffset+yOffset, 0, "Player 4 Lives:");
			font_printf(200+hOffset,190+vOffset+yOffset, 0, "%d", savelevel[saveslot].pLives[3]);
			font_printf(60+hOffset,210+vOffset+yOffset, (selector==1), "Start Game");
		}
		font_printf(60+hOffset,220+vOffset+yOffset, (selector==2), "Back");
		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(selector == 0 && (bothnewkeys & FLAG_MOVELEFT)){
			while(1){
				--saveslot;
				if(saveslot<0) saveslot = MAX_DIFFICULTIES - 1;
				if(savelevel[saveslot].level) break;
			}
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(selector == 0 && (bothnewkeys & FLAG_MOVERIGHT)){
			while(1){
				++saveslot;
				if(saveslot>MAX_DIFFICULTIES - 1) saveslot = 0;
				if(savelevel[saveslot].level) break;
			}
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(savedStatus){
			if(selector<0) selector = 2;
			if(selector>2) selector = 0;
		}
		else selector = 2;

		if(selector > 0 && (bothnewkeys & FLAG_ANYBUTTON)){
			sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
			switch(selector){
				case 1:
					return saveslot;
					break;
				case 2:
                    quit = 1;
					break;
			}
		}
	}
	bothnewkeys = 0;
	return -1;
}



#if PSP || SDL
void load_videomode(int set){					// Load Video Mode from file
	char * filename = "data/video.txt";

	int handle;
	char *buf;
	unsigned int size;
	int pos;

	char * command;

	strcpy(customScenes,"empty");		// Defaults to empty If not specified in video.txt
	strcpy(customBackgrounds,"empty");	// Defaults to empty If not specified in video.txt
	strcpy(customLevels,"empty");		// Defaults to empty If not specified in video.txt

	// Read file
	if((handle=openpackfile(filename,packfile)) < 0){
		if(!savedata.videoFile){
			hRes1		= 320;		// Gameplay Video
			vRes1		= 240;		// Gameplay Video
			hRes2		= 320;		// Scenes, Titles, Background, Select Screen
			vRes2		= 240;		// Scenes, Titles, Background, Select Screen
			hOffset		= 0;		// Offset for X-Axis Text
			xOffset		= 0;		// Offset for X-Axis Text for VideoMode 0
			vOffset		= 0;		// Offset for Y-Axis Text
			yOffset		= 0;		// Offset for Y-Axis Text for VideoMode 2
			dOffset		= 231;		// Offset for Debug Text
			shiftpos[0] = 0;
			shiftpos[1] = 0;
			shiftpos[2] = 0;
			shiftpos[3] = 0;
			if(set){
				freescreen(vscreen);
				if((vscreen = allocscreen(hRes1, vRes1)) == NULL) shutdown("Not enough memory!\n");
			}
			return;
		}
		else goto VIDEOMODES;
	}
	size = seekpackfile(handle,0,SEEK_END);
	seekpackfile(handle,0,SEEK_SET);

	buf = (char*)tracemalloc("load_videomode",size+1);
	if(buf==NULL){
		closepackfile(handle);
		shutdown("Not enough memory to allocate %i-byte buffer for file %s", size, filename);
	}
	if(readpackfile(handle, buf, size) != size){
		if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
		closepackfile(handle);
		shutdown("Read error accessing file %s", filename);
	}
	buf[size] = 0;		// Terminate string (important!)
	closepackfile(handle);

    // Now interpret the contents of buf line by line
    pos = 0;
    while(pos<size){
        command = findarg(buf+pos, 0);
        if(command[0]){
			if(stricmp(command, "forcemode")==0){
                forceVideoMode =  atoi(findarg(buf+pos, 1));
				savedata.videoFile = 1;
            }
        }
        // Go to next line
        while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
        while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
    }
	if(!forceVideoMode && !savedata.videoFile){
		if(buf != NULL){
			tracefree(buf);
			buf = NULL;
		}
		return;
	}

	// Now interpret the contents of buf line by line
    pos = 0;
    while(pos<size){
        command = findarg(buf+pos, 0);
        if(command[0]){
			if(stricmp(command, "forcemode")==0){
                // Placed here just so we don't shutdown
            }
			else if(stricmp(command, "video")==0){
                videoMode = atoi(findarg(buf+pos, 1));
            }
			else if(stricmp(command, "scenes")==0){
                strncpy(customScenes, findarg(buf+pos, 1), 127);
            }
			else if(stricmp(command, "backgrounds")==0){
                strncpy(customBackgrounds, findarg(buf+pos, 1), 127);
            }
			else if(stricmp(command, "levels")==0){
                strncpy(customLevels, findarg(buf+pos, 1), 127);
            }
            else{
				if(buf != NULL){
					tracefree(buf);
					buf = NULL;
				}
                shutdown("Command '%s' not understood in file '%s'!", command, filename);
            }
        }

        // Go to next line
        while(buf[pos] && buf[pos]!='\n' && buf[pos]!='\r') ++pos;
        while(buf[pos]=='\n' || buf[pos]=='\r') ++pos;
    }

	if(buf != NULL){
		tracefree(buf);
		buf = NULL;
	}

VIDEOMODES:
	switch (videoMode)
	{
		case 0:
			hRes1        = 480;
			vRes1        = 240;
			hRes2        = 320;
			vRes2        = 240;
			hOffset      = 0;
			xOffset		 = 80;
			vOffset      = 0;
			yOffset      = 0;
			dOffset      = 231;
			PLAYER_MIN_Z = 160;	
			PLAYER_MAX_Z = 232;	
			BGHEIGHT     = 160;
			break;

		case 1:
			hRes1        = 480;
			vRes1        = 240;
			hRes2        = 480;
			vRes2        = 240;
			hOffset      = 80;
			xOffset		 = 0;
			vOffset      = 0;
			yOffset      = 0;
			dOffset      = 231;
			PLAYER_MIN_Z = 160;	
			PLAYER_MAX_Z = 232;	
			BGHEIGHT     = 160;
			break;

		case 2:
			hRes1        = 480;
			vRes1        = 240;
			hRes2        = 480;
			vRes2        = 272;
			hOffset      = 80;
			xOffset		 = 0;
			vOffset      = 0;
			yOffset      = 20;		// Only used for this videoMode;
			dOffset      = 231;
			PLAYER_MIN_Z = 160;	
			PLAYER_MAX_Z = 232;	
			BGHEIGHT     = 160;
			break;

		case 3:
			hRes1        = 480;
			vRes1        = 272;
			hRes2        = 480;
			vRes2        = 272;
			hOffset      = 80;
			xOffset		 = 0;
			vOffset      = 20;
			yOffset      = 0;
			dOffset      = 263;
			PLAYER_MIN_Z = 182;	
			PLAYER_MAX_Z = 264;	
			BGHEIGHT     = 182;
			break;

		default:
			shutdown("Invalid video mode: %d in 'data/video.txt', 0 - 3 only supported",videoMode);
			break;
	}
	if(set){
		freescreen(vscreen);
		if((vscreen = allocscreen(hRes1, vRes1)) == NULL) shutdown("Not enough memory!\n");
	}
	printf("Loading video.txt...\n");
}
#endif


// ----------------------------------------------------------------------------


// Set key or button safely (with switching)
void safe_set(int *arr, int index, int newkey, int oldkey){
	int i;

	for(i=0; i<9; i++){
		if(arr[i]==newkey) arr[i] = oldkey;
	}

	arr[index] = newkey;
}


void keyboard_setup(int player){
	int quit = 0;
	int selector = 0;
	int setting = -1;
	int k, ok = 0;

	savesettings();

	bothnewkeys = 0;

	while(!quit){
		font_printf(60+hOffset,90+vOffset+yOffset, 2, "%s %i",stringtable[10], player+1); // Setup controls for player
		font_printf(60+hOffset,110+vOffset+yOffset, (selector==0), "%s",stringtable[31]);//Move up
		font_printf(200+hOffset,110+vOffset+yOffset, (selector==0), control_getkeyname(savedata.keys[player][SDID_MOVEUP]));
		font_printf(60+hOffset,120+vOffset+yOffset, (selector==1), "%s",stringtable[32]);//Move down
		font_printf(200+hOffset,120+vOffset+yOffset, (selector==1), control_getkeyname(savedata.keys[player][SDID_MOVEDOWN]));
		font_printf(60+hOffset,130+vOffset+yOffset, (selector==2), "%s",stringtable[33]);//Move left
		font_printf(200+hOffset,130+vOffset+yOffset, (selector==2), control_getkeyname(savedata.keys[player][SDID_MOVELEFT]));
		font_printf(60+hOffset,140+vOffset+yOffset, (selector==3), "%s",stringtable[34]);//Move right
		font_printf(200+hOffset,140+vOffset+yOffset, (selector==3), control_getkeyname(savedata.keys[player][SDID_MOVERIGHT]));
		font_printf(60+hOffset,150+vOffset+yOffset, (selector==4), "%s",stringtable[35]);//Special
		font_printf(200+hOffset,150+vOffset+yOffset, (selector==4), control_getkeyname(savedata.keys[player][SDID_SPECIAL]));
		font_printf(60+hOffset,160+vOffset+yOffset, (selector==5), "%s",stringtable[36]);//Attack
		font_printf(200+hOffset,160+vOffset+yOffset, (selector==5), control_getkeyname(savedata.keys[player][SDID_ATTACK]));
		font_printf(60+hOffset,170+vOffset+yOffset, (selector==6), "%s",stringtable[37]);//Jump
		font_printf(200+hOffset,170+vOffset+yOffset, (selector==6), control_getkeyname(savedata.keys[player][SDID_JUMP]));
		font_printf(60+hOffset,180+vOffset+yOffset, (selector==7), "%s",stringtable[38]);//Start
		font_printf(200+hOffset,180+vOffset+yOffset, (selector==7), control_getkeyname(savedata.keys[player][SDID_START]));
		font_printf(60+hOffset,190+vOffset+yOffset, (selector==8), "%s",stringtable[39]);//Screenshot
		font_printf(200+hOffset,190+vOffset+yOffset, (selector==8), control_getkeyname(savedata.keys[player][SDID_SCREENSHOT]));
		font_printf(60+hOffset,210+vOffset+yOffset, (selector==9), "%s",stringtable[40]);//OK
		font_printf(60+hOffset,220+vOffset+yOffset, (selector==10), "%s",stringtable[41]);//Cancel
		update(0,0);


		if(setting > -1){
			if(bothnewkeys & FLAG_ESC){
				savedata.keys[player][setting] = ok;
				sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 50);
				setting = -1;
			}
			if(setting > -1){
				k = control_scankey();
				if(k){
					safe_set(savedata.keys[player], setting, k, ok);
					sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
					setting = -1;
					// Prevent accidental screenshot
					bothnewkeys = 0;
				}
			}
		}
		else{
			if(bothnewkeys & FLAG_ESC) quit = 1;
			if(bothnewkeys & FLAG_MOVEUP){
				--selector;
				sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
			}
			if(bothnewkeys & FLAG_MOVEDOWN){
				++selector;
				sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
			}
			if(selector<0) selector = 10;
			if(selector>10) selector = 0;

			if(bothnewkeys & FLAG_ANYBUTTON){
				sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
				if(selector==9) quit = 2;
				else if(selector==10) quit = 1;
				else{
					setting = selector;
					ok = savedata.keys[player][setting];
					savedata.keys[player][setting] = 0;
#if PSP || DOS || SDL
					keyboard_getlastkey();
#endif
				}
			}
		}
	}

	if(quit==2){
		apply_controls();
		savesettings();
	}
	else loadsettings();

	update(0,0);
	bothnewkeys = 0;
}







void input_options(){
	int quit = 0;
	int selector = 1; // 0

	bothnewkeys = 0;

	while(!quit){
		font_printf(60+hOffset,90+vOffset+yOffset, 2, "Control Options");

#if PSP
		if(savedata.usejoy) font_printf(60+hOffset,120+vOffset+yOffset, (selector==0), "Analog Pad Enabled");
		else font_printf(60+hOffset,120+vOffset+yOffset, (selector==0), "Analog Pad Disabled");		
#endif
#if DC || DOS || SDL || XBOX
		if(savedata.usejoy){
            font_printf(60+hOffset,120+vOffset+yOffset, (selector==0), "%s",stringtable[26]); //Gamepad enabled
            if(!control_getjoyenabled()){
                font_printf(160+hOffset,120+vOffset+yOffset, (selector==0), "%s",stringtable[27]); //* device is not ready
            }
        }
        else font_printf(60+hOffset,120+vOffset+yOffset, (selector==0), "%s",stringtable[28]); //Gamepad disabled
#endif

		font_printf(60+hOffset,130+vOffset+yOffset, (selector==1), "%s",stringtable[29]); // Setup controls P1...
		font_printf(60+hOffset,140+vOffset+yOffset, (selector==2), "%s",stringtable[30]); // Setup controls P2...
		font_printf(60+hOffset,150+vOffset+yOffset, (selector==3), "Setup Player 3..."); // Setup controls P3...
		font_printf(60+hOffset,160+vOffset+yOffset, (selector==4), "Setup Player 4..."); // Setup controls P4...
		font_printf(60+hOffset,210+vOffset+yOffset, (selector==5), "%s",stringtable[8]); //back
		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;
			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;
			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(selector<0) selector = 5;
		if(selector>5) selector = 0;
		if(bothnewkeys & (FLAG_MOVELEFT|FLAG_MOVERIGHT|FLAG_ANYBUTTON)){

			if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

			switch(selector){
				case 0:
					savedata.usejoy = !savedata.usejoy;
					control_usejoy(savedata.usejoy);
					break;
				case 1:
					keyboard_setup(0);
					break;
				case 2:
					keyboard_setup(1);
					break;
				case 3:
					keyboard_setup(2);
					break;
				case 4:
					keyboard_setup(3);
					break;
				default:
					quit = (bothnewkeys & FLAG_ANYBUTTON);
			}
		}
	}
	savesettings();
	bothnewkeys = 0;
}



void soundvol_options(){

	int quit = 0;
	int selector = 0;
	int dir;

	bothnewkeys = 0;

	while(!quit){
		font_printf(60+hOffset, 90+vOffset+yOffset, 2, "%s",stringtable[42]); //Options

		font_printf(60+hOffset, 120+vOffset+yOffset, (selector==0), "%s",stringtable[43]);//Sound volume:
		font_printf(200+hOffset,120+vOffset+yOffset, (selector==0), "%i", savedata.soundvol);
		font_printf(60+hOffset, 130+vOffset+yOffset, (selector==1), "%s",stringtable[44]);//Sound effects volume:
		font_printf(200+hOffset,130+vOffset+yOffset, (selector==1), "%i", savedata.effectvol);
		font_printf(60+hOffset, 140+vOffset+yOffset, (selector==2), "%s",stringtable[45]);//Background music:
		font_printf(200+hOffset,140+vOffset+yOffset, (selector==2), "%s", (savedata.usemusic ? stringtable[48] : stringtable[49]));
		font_printf(60+hOffset, 150+vOffset+yOffset, (selector==3), "%s",stringtable[46]);//Music volume:
		font_printf(200+hOffset,150+vOffset+yOffset, (selector==3), "%i", savedata.musicvol);
		font_printf(60+hOffset, 160+vOffset+yOffset, (selector==4), "%s",stringtable[47]);//Show music titles:
		font_printf(200+hOffset,160+vOffset+yOffset, (selector==4),"%s", (savedata.showtitles ? stringtable[50] : stringtable[51]));
		font_printf(60+hOffset, 210+vOffset+yOffset, (selector==5), "%s",stringtable[8]);

		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;

			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;

			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(selector<0) selector = 5;
		if(selector>5) selector = 0;

		if(bothnewkeys & (FLAG_MOVELEFT|FLAG_MOVERIGHT|FLAG_ANYBUTTON)){
			dir = 0;

			if(bothnewkeys & FLAG_MOVELEFT) dir = -1;
			else if(bothnewkeys & FLAG_MOVERIGHT) dir = 1;

			if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

			switch(selector){
				case 0:
					savedata.soundvol += dir;
					if(savedata.soundvol < 0) savedata.soundvol = 0;
					if(savedata.soundvol > 15) savedata.soundvol = 15;
#ifndef XBOX
					SB_setvolume(SB_VOICEVOL, savedata.soundvol);
#endif
					break;
				case 1:
					savedata.effectvol += 4*dir;
					if(savedata.effectvol < 0) savedata.effectvol = 0;
					if(savedata.effectvol > 512) savedata.effectvol = 512;
					break;
				case 2:
					if(!dir) break;
					if(!savedata.usemusic){
						savedata.usemusic = 1;
						music("data/music/remix.bor", 1);
					}
					else{
						savedata.usemusic = 0;
						sound_close_music();
					}
					break;
				case 3:
					savedata.musicvol += 4*dir;
					if(savedata.musicvol < 0) savedata.musicvol = 0;
					if(savedata.musicvol > 512) savedata.musicvol = 512;
					sound_volume_music(savedata.musicvol, savedata.musicvol);
					break;
				case 4:
					savedata.showtitles = !savedata.showtitles;
					break;
				default:
					quit = 1;
			}
		}
	}
	savesettings();
	bothnewkeys = 0;
}


void cheatoptions(){	//  LTB 1-13-05 took out sameplayer option
    int quit = 0;
    int selector = 0;
    int dir;

    bothnewkeys = 0;

    while(!quit){
        font_printf(60+hOffset, 90+vOffset+yOffset, 2, "%s",stringtable[73]); // Cheat Options
        font_printf(60+hOffset, 110+vOffset+yOffset, (selector==0), "%s",stringtable[11]); // Brightness:
        font_printf(200+hOffset,110+vOffset+yOffset, (selector==0), "%i", savedata.brightness);
        font_printf(60+hOffset, 120+vOffset+yOffset, (selector==1), "%s",stringtable[12]); // Gamma:
		font_printf(200+hOffset,120+vOffset+yOffset, (selector==1), "%i", savedata.gamma);
        font_printf(60+hOffset, 130+vOffset+yOffset, (selector==2), "%s",stringtable[13]); // Setup input devices...
        font_printf(60+hOffset, 140+vOffset+yOffset, (selector==3), "%s",stringtable[15]); // Sound options...
		font_printf(60+hOffset, 150+vOffset+yOffset, (selector==4), "%s",stringtable[14]); //	Setup Sound Settings...
		font_printf(60+hOffset, 160+vOffset+yOffset, (selector==5), "System Options...");

        if(livescheat){
            font_printf(60+hOffset,170+vOffset+yOffset, (selector==6), "Infinite Lives On");
        }
        else if(!livescheat){
            font_printf(60+hOffset,170+vOffset+yOffset, (selector==6), "Infinite Lives Off");
        }
        if(creditscheat){
            font_printf(60+hOffset,180+vOffset+yOffset, (selector==7), "Infinite Credits On"); // Enemies fall down when you respawn
        }
        else if(!creditscheat){
            font_printf(60+hOffset,180+vOffset+yOffset, (selector==7), "Infinite Credits Off");//Enemies don't fall down when you respawn
        }
		if(healthcheat){
            font_printf(60+hOffset,190+vOffset+yOffset, (selector==8), "Infinite Health On"); // Enemies fall down when you respawn
        }
        else if(!healthcheat){
            font_printf(60+hOffset,190+vOffset+yOffset, (selector==8), "Infinite Health Off");//Enemies don't fall down when you respawn
        }
        
#ifdef XBOX
		font_printf(60,200, (selector==9), "Screen Size/Position");
		font_printf(60,210, (selector==10), "%s",stringtable[8]); // Back
		font_printf(60,220, 0, "Press RThumb to change GFX Filters");
#else
        font_printf(60+hOffset,210+vOffset+yOffset, (selector==9), "%s",stringtable[8]); // Back
#endif

		update(0,0);

        if(bothnewkeys & FLAG_ESC) quit = 1;
        if(bothnewkeys & FLAG_MOVEUP){
            --selector;

            if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
        }
        if(bothnewkeys & FLAG_MOVEDOWN){
            ++selector;

            if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
        }

#if XBOX
		if(selector<0) selector = 10;  // 7-1-2005 //13-1-2005 changed to 11
        if(selector>10) selector = 0;//	7-1-2005 6 changed to 10 //13-1-2005 changed to 11
#else
		if(selector<0) selector = 9;  // 7-1-2005 //13-1-2005 changed to 11
        if(selector>9) selector = 0;//	7-1-2005 6 changed to 10 //13-1-2005 changed to 11
#endif

        if(bothnewkeys & (FLAG_MOVELEFT|FLAG_MOVERIGHT|FLAG_ANYBUTTON)){
            dir = 0;

            if(bothnewkeys & FLAG_MOVELEFT) dir = -1;
            else if(bothnewkeys & FLAG_MOVERIGHT) dir = 1;

            if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

            switch(selector){
				case 0:
					savedata.brightness += 8*dir;
					if(savedata.brightness < -256) savedata.brightness = -256;
					if(savedata.brightness > 256) savedata.brightness = 256;
					vga_vwait();
					palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
					break;
				case 1:
					savedata.gamma += 8*dir;
					if(savedata.gamma < -256) savedata.gamma = -256;
					if(savedata.gamma > 256) savedata.gamma = 256;
					vga_vwait();
					palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
					break;
				case 2:
					input_options();
					break;
				case 3:
					soundvol_options();
					break;
				case 4:
					soundcard_options();
					break;
				case 5:
					system_options();
					break;
				case 6:
					if(!livescheat) livescheat = 1;
					else if(livescheat) livescheat = 0;
					break;
				case 8:
					if(!creditscheat) creditscheat = 1;
					else if(creditscheat) creditscheat = 0;
					break;
				case 9:
					if(!healthcheat) healthcheat = 1;
					else if(healthcheat) healthcheat = 0;
					break;
#if XBOX
				case 10:
					font_printf(85,90, 2, "Use right/left thumbsticks to");
					font_printf(88,100, 2, "change screen size/position");
					update(0,0);
					xbox_resize();
					break;
#endif

				default:
					quit = 1;
            }
        }
    }
    savesettings();
    bothnewkeys = 0;
}


void system_options(){

	int quit = 0;
	int selector = 0;
	int dir;
	int i = 0;
	int x = 0;

#if PSP
	int batteryPercentage = 0;
	int batteryLifeTime = 0;
	int externalPower = 0;
#endif

	bothnewkeys = 0;

	while(!quit){
		font_printf(60+hOffset, 90+vOffset+yOffset, 2, "System Options");

#if PSP
		externalPower = scePowerIsPowerOnline();
		font_printf(60+hOffset, 120+vOffset+yOffset, (selector==0), "CPU Speed:");
		font_printf(200+hOffset,120+vOffset+yOffset, (selector==0), "%d MHz", scePowerGetCpuClockFrequency());
		font_printf(60+hOffset, 130+vOffset+yOffset, 0, "BUS Speed:");
		font_printf(200+hOffset,130+vOffset+yOffset, 0, "%d MHz", scePowerGetBusClockFrequency());
		font_printf(60+hOffset, 140+vOffset+yOffset, 0, "Charge:");
		if(!externalPower){
			batteryPercentage = scePowerGetBatteryLifePercent();
			batteryLifeTime = scePowerGetBatteryLifeTime();
			if(batteryPercentage < 0) font_printf(200+hOffset,140+vOffset+yOffset, 0, "Calculating...");
			else font_printf(200+hOffset,140+vOffset+yOffset, 0, "%d%% Remaining", batteryPercentage);
			font_printf(60+hOffset,150+vOffset+yOffset, 0, "Battery:");
			if(batteryLifeTime < 0) font_printf(200+hOffset,150+vOffset+yOffset, 0, "Calculating...");
			else font_printf(200+hOffset,150+vOffset+yOffset, 0, "%02d:%02d", batteryLifeTime/60,batteryLifeTime-(batteryLifeTime/60*60));
		}
		else{
			font_printf(200+hOffset,140+vOffset+yOffset, 0, "%d%% Completed", scePowerGetBatteryLifePercent());
			font_printf(60+hOffset, 150+vOffset+yOffset, 0, "AC Power:");
			font_printf(200+hOffset,150+vOffset+yOffset, 0, "Connected");
		}
#endif

#if PSP || DC
#if DC
		i = 1;
		x = 40;
#endif
		font_printf(60+hOffset, 160-x+vOffset+yOffset, (selector==(1-i)), "RAM CleanUp:");
		font_printf(200+hOffset,160-x+vOffset+yOffset, (selector==(1-i)), (savedata.memory ? "Forced" : "Automatic"));
		font_printf(60+hOffset, 170-x+vOffset+yOffset, 0, "Free RAM:");
		font_printf(200+hOffset,170-x+vOffset+yOffset, 0, "%lu Bytes",ramAvailable());
#endif
	
#if XBOX
		i = 1;
		x = 50;
		font_printf(60,120, (selector==0), "Screen Size/Position");
		font_printf(60,225, 0, "Press RThumb to change GFX Filters");
#endif

#if SDL || DOS
		i = 2;
		x = 60;
#endif

		font_printf(60+hOffset, 180-x+vOffset+yOffset, (selector==(2-i)), "%s",stringtable[76]);					//	No Of Players
		if(!ctrlmaxplayers) font_printf(200+hOffset, 180-x+vOffset+yOffset, (selector==(2-i)), "%i",maxplayers);  //	No Of Players
		else font_printf(200+hOffset, 180-x+vOffset+yOffset, (selector==(2-i)), "%i by Mod",maxplayers);

		font_printf(60+hOffset,190-x+vOffset+yOffset, (selector==(3-i)), "%s",stringtable[18]);
		if(versusdamage == 0) font_printf(200+hOffset,190-x+vOffset+yOffset, (selector==(3-i)), "Disabled by Mod");
		else if(versusdamage == 1) font_printf(200+hOffset,190-x+vOffset+yOffset, (selector==(3-i)), "Enabled by Mod");
		else{
			if(savedata.mode) font_printf(200+hOffset,190-x+vOffset+yOffset, (selector==(3-i)), "Disabled");//Mode 1 - Players CAN'T attack each other
			else font_printf(200+hOffset,190-x+vOffset+yOffset, (selector==(3-i)), "Enabled");//Mode 2 - Players CAN attack each other
		}

		font_printf(60+hOffset,200-x+vOffset+yOffset, (selector==(4-i)), "Game Speed:");   // 13-1-2005 game speed options GAME_SPEED
		font_printf(200+hOffset,200-x+vOffset+yOffset, (selector==(4-i)), "%i",GAME_SPEED);

		font_printf(60+hOffset, 210+vOffset+yOffset, (selector==(5-i)), stringtable[8]);
		
		
		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}

		if(selector<0) selector = 5-i;
		if(selector>(5-i)) selector = 0;

		if(bothnewkeys & (FLAG_MOVELEFT|FLAG_MOVERIGHT|FLAG_ANYBUTTON)){
			dir = 0;
			if(bothnewkeys & FLAG_MOVELEFT) dir = -1;
			else if(bothnewkeys & FLAG_MOVERIGHT) dir = 1;
			sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

#if PSP || DC || XBOX
			if(selector == 0){
#if PSP
				savedata.pspcpuspeed += dir;
				if(savedata.pspcpuspeed<0) savedata.pspcpuspeed = 2;
				if(savedata.pspcpuspeed>2) savedata.pspcpuspeed = 0;
				switch(savedata.pspcpuspeed){
					case 0:
						scePowerSetClockFrequency(222, 222, 111);
						break;
					case 1:
						scePowerSetClockFrequency(266, 266, 133);
						break;
					case 2:
						scePowerSetClockFrequency(333, 333, 166);
						break;
				}
#endif

#if DC
				if(!dir) break;
				if(!savedata.memory){
					savedata.memory = 1;
					savedata.memoryThreshold = 20000000;
				}
				else{
					savedata.memory = 0;
					savedata.memoryThreshold = 4000000;
				}
#endif

#if XBOX
				font_printf(85,90, 2, "Use right/left thumbsticks to");
				font_printf(88,100, 2, "change screen size/position");
				update(0,0);
				xbox_resize();
#endif
			}

#if PSP
			else if(selector == 1){
					if(!dir) break;
					if(!savedata.memory){
						savedata.memory = 1;
						savedata.memoryThreshold = 20000000;
					}
					else{
						savedata.memory = 0;
						savedata.memoryThreshold = 4000000;
					}
			}
#endif
#endif
			if(selector == (2-i)){
					if(!ctrlmaxplayers){
						if(maxplayers == 2) maxplayers = 4;
						else maxplayers = 2;
					}
			}
			else if(selector == (3-i)){
					if(versusdamage > 1){
						if(!savedata.mode) savedata.mode = 1;	// Option now saves
						else if(savedata.mode) savedata.mode = 0;	// Option now saves
					}
			}
			else if(selector == (4-i)){				// 13-1-2005 Game speed selection
					GAME_SPEED += (dir*10);   // 13-1-2005 Game speed selection
					if(GAME_SPEED == 0) GAME_SPEED = 10;
					if(GAME_SPEED == 1000) cheats=1;
			}
			else if(selector == (5-i)) quit = 1;
		}
	}
	savesettings();
	bothnewkeys = 0;
}


void video_options(){
	int quit = 0;
	int selector = 0;
	int dir;

	bothnewkeys = 0;

	while(!quit){
		font_printf(60+hOffset, 90+vOffset+yOffset, 2, "Video Options");
		font_printf(60+hOffset, 110+vOffset+yOffset, (selector==0), "%s", stringtable[11]); // Brightness:
		font_printf(200+hOffset,110+vOffset+yOffset, (selector==0), "%i", savedata.brightness);
		font_printf(60+hOffset, 120+vOffset+yOffset, (selector==1), "%s", stringtable[12]); // Gamma:
		font_printf(200+hOffset,120+vOffset+yOffset, (selector==1), "%i", savedata.gamma);
		font_printf(60+hOffset, 130+vOffset+yOffset, (selector==2), "%s", stringtable[16]); // Window offset
        font_printf(200+hOffset,130+vOffset+yOffset, (selector==2), "%i", savedata.windowpos); // 10-20 needed for some TV's

#if DC || DOS || GP2X || XBOX
		font_printf(60+hOffset,210+vOffset+yOffset, (selector==3), "%s",stringtable[8]); // Back
		if(selector<0) selector = 3;
		if(selector>3) selector = 0;
#endif
#if SDL || PSP
#ifndef GP2X
		font_printf(60+hOffset, 140+vOffset+yOffset, (selector==3), "FPS Counter:");
		font_printf(200+hOffset,140+vOffset+yOffset, (selector==3), (savedata.fps ? "Enabled" : "Disabled"));
#endif
#endif

#if SDL || PSP
#ifndef GP2X
		font_printf(60+hOffset,150+vOffset+yOffset, (selector==4), "Video Modes:");
		if(forceVideoMode) font_printf(200+hOffset,150+vOffset+yOffset, (selector==4), "Enabled by Mod");
		else font_printf(200+hOffset,150+vOffset+yOffset, (selector==4), "%s", savedata.videoFile ? "Enabled" : "Disabled");
#endif
#endif

#if SDL
#ifndef GP2X
		font_printf(60+hOffset,160+vOffset+yOffset, (selector==5), "Scale Video:");
		if(scaleVideo) font_printf(200+hOffset,160+vOffset+yOffset, (selector==5), "%ix - %ix%i", scaleVideo, hRes1*scaleVideo, vRes1*scaleVideo);
		else font_printf(200+hOffset,160+vOffset+yOffset, (selector==5), "Disabled");

		font_printf(60+hOffset,170+vOffset+yOffset, (selector==6), "Display Mode:");
		font_printf(200+hOffset,170+vOffset+yOffset, (selector==6), "%s", fullscreen ? "Full" : "Window");

		font_printf(60+hOffset,210+vOffset+yOffset, (selector==7), "%s",stringtable[8]); // Back
		if(selector<0) selector = 7;
		if(selector>7) selector = 0;
#endif
#endif

#if PSP
		font_printf(60+hOffset,160+vOffset+yOffset, (selector==5), "Scale Video:");
		if(scaleVideo == 1) font_printf(200+hOffset,160+vOffset+yOffset, (selector==5), "On - Bileaner Filtering");
		else if(scaleVideo == 2) font_printf(200+hOffset,160+vOffset+yOffset, (selector==5), "On - Linear Filtering");
		else font_printf(200+hOffset,160+vOffset+yOffset, (selector==5), "Disabled");

		font_printf(60+hOffset,210+vOffset+yOffset, (selector==6), "%s",stringtable[8]); // Back
		if(selector<0) selector = 6;
		if(selector>6) selector = 0;
#endif
			
		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;
			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;
			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & (FLAG_MOVELEFT|FLAG_MOVERIGHT|FLAG_ANYBUTTON)){
			dir = 0;

			if(bothnewkeys & FLAG_MOVELEFT) dir = -1;
			else if(bothnewkeys & FLAG_MOVERIGHT) dir = 1;

			if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

			switch(selector){
				case 0:
					savedata.brightness += 8*dir;
					if(savedata.brightness < -256) savedata.brightness = -256;
					if(savedata.brightness > 256) savedata.brightness = 256;
					vga_vwait();
					palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
					break;
				case 1:
					savedata.gamma += 8*dir;
					if(savedata.gamma < -256) savedata.gamma = -256;
					if(savedata.gamma > 256) savedata.gamma = 256;
					vga_vwait();
					palette_set_corrected(pal, savedata.gamma,savedata.gamma,savedata.gamma, savedata.brightness,savedata.brightness,savedata.brightness);
					break;
				case 2:
					savedata.windowpos += dir;
					if(savedata.windowpos < -2) savedata.windowpos = -2;
					if(savedata.windowpos > 20) savedata.windowpos = 20;
					break;
#if SDL || PSP
				case 3:
					if(!dir) break;
					if(!savedata.fps){
						savedata.fps = 1;
						fpsCounter = 1;
					}
					else{
						savedata.fps = 0;
						fpsCounter = 0;
					}
					break;
#ifndef SDL
				case 4:
					if(savedata.videoFile) savedata.videoFile = 0;	// DONT use video.txt file
					else savedata.videoFile = 1;					// Use video.txt file
					load_videomode(1);
					break;
				case 5:
					scaleVideo += dir;
					if(scaleVideo > 2) scaleVideo = 0;
					if(scaleVideo < 0) scaleVideo = 2;
					savedata.scalevideo = scaleVideo;
					video_clearscreen(1);
					break;
#endif
#endif
#if SDL
#ifndef GP2X
				case 4:
					if(scaleVideo) break;
					if(savedata.videoFile) savedata.videoFile = 0;	// DONT use video.txt file
					else savedata.videoFile = 1;					// Use video.txt file
					load_videomode(1);
					break;
				case 5:
					if(savedata.videoFile) break;
					scaleVideo += 2;
					if(scaleVideo > 4) scaleVideo = 0;
					video_set_mode(hRes1, vRes1);
					standard_palette();
					savedata.scalevideo = scaleVideo;
					break;
				case 6:
					if(fullscreen || savedata.fullscreen){
						savedata.fullscreen = 0;					// Window Mode
						video_fullscreen_flip();
					}
					else{
						savedata.fullscreen = 1;					// Full Mode
						video_fullscreen_flip();
					}
					break;
#endif
#endif
				default:
					quit = 1;
			}
		}
	}
	savesettings();
	bothnewkeys = 0;
}


void options(){
	int quit = 0;
	int selector = 0;
	int dir;

	bothnewkeys = 0;

	while(!quit){
		font_printf(60+hOffset, 90+vOffset+yOffset, 2, "%s",stringtable[1]); // Options
		font_printf(60+hOffset, 110+vOffset+yOffset, (selector==0), "Video Options...",stringtable[13]);
		font_printf(60+hOffset, 120+vOffset+yOffset, (selector==1), "%s",stringtable[15]); // Sound options...
		font_printf(60+hOffset, 130+vOffset+yOffset, (selector==2), "%s",stringtable[14]); // Setup Sound Settings..
		font_printf(60+hOffset, 140+vOffset+yOffset, (selector==3), "%s",stringtable[13]); // Setup input devices...
		font_printf(60+hOffset, 150+vOffset+yOffset, (selector==4), "System Options...");

        font_printf(60+hOffset,210+vOffset+yOffset, (selector==5), "%s",stringtable[8]); // Back


		if(selector<0) selector = 5;
		if(selector>5) selector = 0;

		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;

			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;

			if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & (FLAG_MOVELEFT|FLAG_MOVERIGHT|FLAG_ANYBUTTON)){
			dir = 0;

			if(bothnewkeys & FLAG_MOVELEFT) dir = -1;
			else if(bothnewkeys & FLAG_MOVERIGHT) dir = 1;

			if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

			switch(selector){
				case 0:
					video_options();
					break;
				case 1:
					soundvol_options();
					break;
				case 2:
					soundcard_options();
					break;
				case 3:
					input_options();
					break;
				case 4:
					system_options();
					break;
				default:
					quit = 1;
			}
		}
	}
	savesettings();
	bothnewkeys = 0;
}

void soundcard_options(){
	int quit = 0;
	int selector = 0;

	savesettings();

	bothnewkeys = 0;

	while(!quit){
		font_printf(60+hOffset,90+vOffset+yOffset, 2, "Sound card options");
		font_printf(60+hOffset,120+vOffset+yOffset, (selector==0), "Frequency:");
		font_printf(200+hOffset,120+vOffset+yOffset, (selector==0), "%i", savedata.soundrate);
		font_printf(60+hOffset,130+vOffset+yOffset, (selector==1), "Bits:");
		font_printf(200+hOffset,130+vOffset+yOffset, (selector==1), "%i", savedata.soundbits);
		font_printf(60+hOffset,150+vOffset+yOffset, (selector==2), "Apply");
		font_printf(60+hOffset,160+vOffset+yOffset, (selector==3), "Discard");
		font_printf(60+hOffset,210+vOffset+yOffset, (selector==4), "Back");
		update(0,0);

		if(bothnewkeys & FLAG_ESC) quit = 1;
		if(bothnewkeys & FLAG_MOVEUP){
			--selector;
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(bothnewkeys & FLAG_MOVEDOWN){
			++selector;
			sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
		}
		if(selector<0) selector = 4;
		selector %= 5;
		if(bothnewkeys & (FLAG_MOVELEFT|FLAG_MOVERIGHT|FLAG_ANYBUTTON)){
			sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);
			switch(selector){
				case 0:
					if(bothnewkeys & FLAG_MOVELEFT) savedata.soundrate >>= 1;
					if(bothnewkeys & FLAG_MOVERIGHT) savedata.soundrate <<= 1;
					if(savedata.soundrate < 11025) savedata.soundrate = 44100;
					if(savedata.soundrate > 44100) savedata.soundrate = 11025;
					break;
				case 1:
					savedata.soundbits = (savedata.soundbits ^ (8+16));
					if(savedata.soundbits!=8 && savedata.soundbits!=16) savedata.soundbits = 8;
					break;
				case 2:
					if(!(bothnewkeys & FLAG_ANYBUTTON)) break;
					// Apply new hardware settings
					sound_stop_playback();
					if(!sound_start_playback(savedata.soundstereo, savedata.soundbits, savedata.soundrate)){
						savedata.soundbits = 8;
						savedata.soundrate = 11025;
						sound_start_playback(savedata.soundstereo, savedata.soundbits, savedata.soundrate);
					}
					music("data/music/remix.bor", 1);
					savesettings();
					break;
				case 3:
					if(bothnewkeys & FLAG_ANYBUTTON) loadsettings();
					break;
				default:
					quit = (bothnewkeys & FLAG_ANYBUTTON);
			}
		}
	}
	loadsettings();
	bothnewkeys = 0;
}




// ----------------------------------------------------------------------------


void openborMain(void){

	int quit = 0;
	int relback = 1;
	int selector = 0;
	unsigned long introtime = 0;
	int started = 0;
	char tmpBuff[128] = {""};

#ifndef PSP
    int done = 0;
	char pakname[128] = {""};
	char listing[32] = {""};
	int paks = 0;
    int list = 0;
	int lOffset=0;
	unsigned long menutime = 0;
#endif

	loadsettings();

#if PSP
	pspMenu(packfile, savedata.pspPakPath, savedata.pspPakPath);
#endif

	printf("OpenBoR V%X.%04X, compile date: " __DATE__ "\n\n", VERSION>>16, VERSION&0xFFFF);
    printf("Weapons Version - WARNING: weapons require more memory\n");
    printf("Mod creators are responsible for memory issues\n\n");


#ifndef PSP

    guistartup();
	load_background("menu/logo", 0, 0);
    paks = findmods();
	
    while(!done){
		if(paks < 1) font_printf(10,24, 1, "No Mods In Paks Folder!");
		if(bothnewkeys & FLAG_ESC){
			shutdown(
				"OpenBoR V%X.%04X, compile date: " __DATE__ "\n"
				"Presented by Team Senile.\n"
				"This Version is unofficial and based on the Senile Source Code.\n"
				"\n"
				"Special thanks to SEGA and SNK.\n\n",
				VERSION>>16,
				VERSION&0xFFFF
			);
		}

        for(list=0; list<paks; list++){
			strncpy(pakname, pakfiles[list+lOffset].filename,128-strlen(pakfiles[list+lOffset].filename));  
			if(list<18){
				strncpy(listing, "", 32);
				strncpy(listing, pakname, 31);
				font_printf(10,24+(11*list), selector==list, "%s", listing);
			}
        }
		if(bothkeys & FLAG_MOVEUP && time >= menutime){
			--selector;
			menutime = time + GAME_SPEED/8;
		}
		if(bothkeys & FLAG_MOVEDOWN && time >= menutime){
			++selector;
			menutime = time + GAME_SPEED/8;
		}

        if(selector>paks-1) selector=paks-1;
            
		if(selector>17){
			if((selector+lOffset)<paks) lOffset++;
			selector=17;
        }
        if(selector<0){
            selector=0;
            if(lOffset>0) lOffset--;
        }                              
		if((bothnewkeys&(FLAG_ANYBUTTON)) && paks > 1){
            strncpy(pakname, pakfiles[selector+lOffset].filename,128-strlen(pakfiles[selector+lOffset].filename));
			getBasePath(packfile, pakname, 1);
			done = 1;
        }
		if(paks==1){
			getBasePath(packfile, pakname, 1);
			done = 1;
        }
		
		font_printf(5,3, 2, "OpenBoR V%X.%04X", VERSION>>16, VERSION&0xFFFF);
		font_printf(265,3, 2, __DATE__);
		font_printf(197,155, 2, "www.LavaLit.com");
		font_printf(190,165, 2, "www.SenileTeam.com");
		font_printf(5,229, 2, "Action : Load");
		font_printf(259,229, 2, "Quit : Escape");

		if(done) font_printf(215,175, 2, "Loading...");
        update(0,0);
    }

	// unload everything just in case.
	selector = 0;
    unload_levelorder();
    unload_level();
    freescreen(vscreen);
    freescreen(rescreen);
    freesprites();
    font_unload(0);
    font_unload(1);
	font_unload(2);
	free_ents();
    free_models();
    free_modelcache();
    timer_exit();
    control_exit();

#ifdef DC
	pak_term();
#endif
#endif

	printf("Game Selected: %s\n\n", packfile);
	startup();

	// Clear background
	unload_background();
	background = allocscreen(hRes2, vRes2);
	if(background==NULL) shutdown("Out of memory! Function Call 'openborMain'");

	if(!video_set_mode(hRes2, vRes2)) shutdown("Out of memory! Function Call 'openborMain'");
	clearscreen(vscreen);

	// New alternative background path for PSP
	if(strcmp(customBackgrounds,"empty") == 0) load_background("data/bgs/logo", 0, 0);
	else{	
		strcpy(tmpBuff,customBackgrounds);
		strcat(tmpBuff,"logo");
		load_background(tmpBuff, 0, 0);
	}

	while(time<GAME_SPEED*6 && !(bothnewkeys&(FLAG_ANYBUTTON|FLAG_ESC))) update(0,0);

	music("data/music/remix.bor", 1);
	
	// New alternative scene path for PSP
	if(strcmp(customScenes,"empty") == 0) playscene("data/scenes/logo.txt");
	else{	
		strcpy(tmpBuff,"");
		strcpy(tmpBuff,customScenes);
		strcat(tmpBuff,"logo.txt");
        playscene(tmpBuff);
	}	

	while(!quit){
		if(time >= introtime){
			// New alternative scene path for PSP
			if(strcmp(customScenes,"empty") == 0) playscene("data/scenes/intro.txt");
			else{	
				strcpy(tmpBuff,"");
				strcpy(tmpBuff,customScenes);
				strcat(tmpBuff,"intro.txt");
				playscene(tmpBuff);
			}
			
			update(0,0);
			introtime = time + GAME_SPEED * 20;
			relback = 1;
			started = 0;
		}

		if(bothnewkeys & FLAG_ESC) quit = 1;

		if(!started){
			if((time%GAME_SPEED) < (GAME_SPEED/2)) font_printf(129+hOffset,140+vOffset+yOffset, 0, "%s",stringtable[59]);
			if(bothnewkeys&(FLAG_ANYBUTTON)){
				started = 1;
				relback = 1;
				freezeall = 0;	// Added incase game is exited while the animations are frozen
			}
		}
		else{
			font_printf(129+hOffset,150+vOffset+yOffset, (selector==0), "%s",stringtable[0]);  // ltb 1-18-05  moved  Menu
			font_printf(129+hOffset,160+vOffset+yOffset, (selector==1), "Load Game");
			font_printf(129+hOffset,170+vOffset+yOffset, (selector==2), "%s",stringtable[1]);  // ltb 1-18-05  moved  Options
			font_printf(129+hOffset,180+vOffset+yOffset, (selector==3), "%s",stringtable[2]);  // ltb 1-18-05  moved  How To Play
			font_printf(129+hOffset,190+vOffset+yOffset, (selector==4), "%s",stringtable[72]);  // ltb 1-18-05  moved  Hall of Fame
			font_printf(129+hOffset,200+vOffset+yOffset, (selector==5), "%s",stringtable[3]);  // ltb 1-18-05  moved  Quit
			if(selector<0) selector = 5;
			if(selector>5) selector = 0;

			if(bothnewkeys) introtime = time + GAME_SPEED * 20;

			if(bothnewkeys & FLAG_MOVEUP){
				--selector;

				if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
			}
			if(bothnewkeys & FLAG_MOVEDOWN){
				++selector;

				if(smp_beep >= 0) sound_play_sample(smp_beep, 0, savedata.effectvol,savedata.effectvol, 100);
			}
			if(bothnewkeys&(FLAG_ANYBUTTON)){

				if(smp_beep2 >= 0) sound_play_sample(smp_beep2, 0, savedata.effectvol,savedata.effectvol, 100);

				switch(selector){
					case 0:
						playgame(player[0].newkeys&(FLAG_ANYBUTTON), player[1].newkeys&(FLAG_ANYBUTTON), player[2].newkeys&(FLAG_ANYBUTTON), player[3].newkeys&(FLAG_ANYBUTTON), choose_difficulty(),0);
						started = 0;
						relback = 1;
						break;
					case 1:
						playgame(player[0].newkeys&(FLAG_ANYBUTTON), player[1].newkeys&(FLAG_ANYBUTTON), player[2].newkeys&(FLAG_ANYBUTTON), player[3].newkeys&(FLAG_ANYBUTTON), load_saved_game(), 1);
						started = 0;
						relback = 1;
						break;
	                case 2:
		                if(!cheats) options();
			            else {
							if(!forcecheatsoff) cheatoptions();
							else options();
						}
	                    break;
					case 3:
						// New alternative scene path for PSP
						if(strcmp(customScenes,"empty") == 0) playscene("data/scenes/howto.txt");
						else{	
							strcpy(tmpBuff,"");
							strcpy(tmpBuff,customScenes);
							strcat(tmpBuff,"howto.txt");
							playscene(tmpBuff);
						}
						relback = 1;
						break;
					case 4:
						hallfame(0);
						relback = 1;
						break;
					default:
						quit = 1;
						break;
				}
				introtime = time + GAME_SPEED * 20;
			}
		}
		if(relback){
			if(started){
				// New alternative background path for PSP
				if(strcmp(customBackgrounds,"empty") == 0) load_background("data/bgs/titleb", 0, 0);
				else{	
					strcpy(tmpBuff,"");
					strcpy(tmpBuff,customBackgrounds);
					strcat(tmpBuff,"titleb");
					load_background(tmpBuff, 0, 0);
				}
			}
			else{
				// New alternative background path for PSP
				if(strcmp(customBackgrounds,"empty") == 0) load_background("data/bgs/title", 0, 0);
				else{	
					strcpy(tmpBuff,"");
					strcpy(tmpBuff,customBackgrounds);
					strcat(tmpBuff,"title");
					load_background(tmpBuff, 0, 0);
				}
			}

			if(!sound_query_music(NULL,NULL)) music("data/music/remix.bor", 1);
			relback = 0;
		}
		update(0,0);

	}

	shutdown(
        "OpenBoR V%X.%04X, compile date: " __DATE__ "\n"
        "Presented by Team Senile.\n"
        "This Version is unofficial and based on the Senile Source Code.\n"
        "\n"
        "Special thanks to SEGA and SNK.\n\n",
        VERSION>>16,
        VERSION&0xFFFF
    );
}

