Downloads containing Spaceship v1-01.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Anniversary Bash 26 Battle Jazz2Online Battle N/A Download file
JJ2+ Only: Anniversary Bash 25 Battle Jazz2Online Battle N/A Download file
TSF with JJ2+ Only: Asteroid Armada PurpleJazz Battle N/A Download file
JJ2+ Only: Spaceships (v1.01) PurpleJazz Custom / Concept N/A Download file

File preview

#pragma require "Spaceship.j2a"
#pragma require "Spaceship v1-01.asc"
#pragma require "Silence.wav"
#include "Resize v11.asc"
#include "MLLE-Weapons.asc"

namespace Spaceship {
	bool animsLoaded = false;
	uint customAnimID = 0;

	class SPACESHIP {
		float xSpeed, ySpeed, xAcc, yAcc, xMomentum, yMomentum, fireAngle, momentum = 0;
		float maxSpeed = 9.25;
		float maxTurningSpeed = 7;
		float maxSpeedRev = 6;
		float maxTurningSpeedRev = 4.5;
		float maxDiagonalSpeed = 8;
		float rotationAcc = 0;
		
		int health, anim, angle, rotationSpeed, sample, sample2, fireDelay, hitDelay, armor, damageAccumulated, accumulationTime;
		int powerTime = 30*70;
		
		int damageCalc(SPACESHIP@ ship, int animSpeed) {
			if (ship.armor >= animSpeed) {
				return (animSpeed / 3);
			} else {
				return (animSpeed - (ship.armor / 3 * 2));
			}
		}
		
		bool thrust, reverse, rotating, preciseTurn, fire, justHit, wasHit, stopWeaponChange, powerUp, SDPunishment, tntPowerup = false;
		bool hasShip = true;

		SPACESHIP(){}
	};
	array<SPACESHIP> Ships(32);

	const float PI = 3.1415926535f;
	int xPos = 0, yPos = 0, cameraX = 0, cameraY = 0, radarX = 100, radarY = jjSubscreenHeight - 220;
	array<int> closestPlayers(32, -1); //needed even for non-local players
	uint8 currentWeapon = 1;

	bool crosshairFlash = false;
	int crosshairTime = 0;

	bool gameIsActive() {
		return jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME;
	}

	bool canBeHit(jjPLAYER@ play, jjPLAYER@ creator, jjOBJ@ obj) {
		return play !is null && (play !is creator || obj.eventID == OBJECT::BULLET) && play.blink == 0 && play.health > 0 && creator.blink == 0 && obj.state != STATE::EXPLODE && (creator.isEnemy(play) || (play is creator && obj.eventID == OBJECT::BULLET)) && gameIsActive();
	}

	float recoilX(SPACESHIP@ ship, jjOBJ@ obj, float recoil) {
		return ((obj.xSpeed / (ship.thrust? 16:128)) * recoil);
	}

	float recoilY(SPACESHIP@ ship, jjOBJ@ obj, float recoil) {
		return ((obj.ySpeed / (ship.thrust? 16:128)) * recoil);
	}

	int teamColor(jjPLAYER@ play) {
		if (play.team == TEAM::BLUE) return 32;
		if (play.team == TEAM::RED) return 24;
		if (play.team == TEAM::YELLOW) return 40;
		if (play.team == TEAM::GREEN) return 16;
		
		return 32;
	}

	bool isAlly(jjPLAYER@ creator, jjPLAYER@ play) {
		return !creator.isLocal && jjGameMode == GAME::CTF && creator.team == play.team;
	}

	bool active = true;
	
	int maxHealth;
	
	bool showInstructions = false;
	array<bool> pressOnce(3, false);
	int currPage = 1;

	void handleOnLevelLoad() {
		if (!animsLoaded) {
			animsLoaded = true;
			while (jjAnimSets[ANIM::CUSTOM[customAnimID]] != 0)
				++customAnimID;
			customAnimID = ANIM::CUSTOM[customAnimID];
			jjAnimSets[customAnimID].load(0, "Spaceship.j2a");
		}
		
		jjAnimSets[ANIM::DEVAN].load();
		jjAnimSets[ANIM::MENU].load();
		
		jjWeapons[WEAPON::BLASTER].infinite = false;
		jjWeapons[WEAPON::BLASTER].maximum = 25;
		jjWeapons[WEAPON::BLASTER].defaultSample = false;
		
		jjObjectPresets[OBJECT::BLASTERBULLET].xSpeed = jjObjectPresets[OBJECT::BLASTERBULLET].xSpeed * -2.5;
		jjObjectPresets[OBJECT::BLASTERBULLET].ySpeed = 0;
		jjObjectPresets[OBJECT::BLASTERBULLET].xAcc = 0;
		jjObjectPresets[OBJECT::BLASTERBULLET].yAcc = 0;
		jjObjectPresets[OBJECT::BLASTERBULLET].counterEnd = 65;
		jjObjectPresets[OBJECT::BLASTERBULLET].behavior = Blaster();
		jjObjectPresets[OBJECT::BLASTERBULLET].special = jjObjectPresets[OBJECT::BLASTERBULLET].determineCurAnim(ANIM::DEVAN, 0);
		jjObjectPresets[OBJECT::BLASTERBULLET].determineCurFrame();
		jjObjectPresets[OBJECT::BLASTERBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::BLASTERBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::BLASTERBULLET].animSpeed = 15;
		jjObjectPresets[OBJECT::BLASTERBULLET].triggersTNT = true;
		
		jjObjectPresets[OBJECT::BLASTERBULLETPU].xSpeed = jjObjectPresets[OBJECT::BLASTERBULLETPU].xSpeed * -2.5;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].ySpeed = 0;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].xAcc = 0;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].yAcc = 0;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].counterEnd = 65;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].behavior = Blaster();
		jjObjectPresets[OBJECT::BLASTERBULLETPU].special = jjObjectPresets[OBJECT::BLASTERBULLETPU].determineCurAnim(ANIM::DEVAN, 0);
		jjObjectPresets[OBJECT::BLASTERBULLETPU].determineCurFrame();
		jjObjectPresets[OBJECT::BLASTERBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].animSpeed = 30;
		jjObjectPresets[OBJECT::BLASTERBULLETPU].triggersTNT = true;
		
		jjWeapons[WEAPON::BOUNCER].maximum = 100;
		jjWeapons[WEAPON::BOUNCER].defaultSample = false;
		
		jjObjectPresets[OBJECT::BOUNCERBULLET].xSpeed = jjObjectPresets[OBJECT::BOUNCERBULLET].xSpeed * -3;
		jjObjectPresets[OBJECT::BOUNCERBULLET].ySpeed = 0;
		jjObjectPresets[OBJECT::BOUNCERBULLET].xAcc = 0;
		jjObjectPresets[OBJECT::BOUNCERBULLET].yAcc = 0;
		jjObjectPresets[OBJECT::BOUNCERBULLET].counterEnd = 70;
		jjObjectPresets[OBJECT::BOUNCERBULLET].behavior = RapidBlaster();
		jjObjectPresets[OBJECT::BOUNCERBULLET].special = jjObjectPresets[OBJECT::BOUNCERBULLET].determineCurAnim(ANIM::AMMO, 27);
		jjObjectPresets[OBJECT::BOUNCERBULLET].determineCurFrame();
		jjObjectPresets[OBJECT::BOUNCERBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::BOUNCERBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::BOUNCERBULLET].animSpeed = 9;
		
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].xSpeed = jjObjectPresets[OBJECT::BOUNCERBULLETPU].xSpeed * -3;
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].ySpeed = 0;
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].xAcc = 0;
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].yAcc = 0;
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].counterEnd = 70;
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].behavior = RapidBlaster();
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].special = jjObjectPresets[OBJECT::BOUNCERBULLETPU].determineCurAnim(ANIM::AMMO, 60);
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].determineCurFrame();
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::BOUNCERBULLETPU].animSpeed = 18;
		
		jjWeapons[WEAPON::ICE].maximum = 25;
		jjWeapons[WEAPON::ICE].defaultSample = false;
		jjWeapons[WEAPON::ICE].spread = SPREAD::RFPU;
		
		jjObjectPresets[OBJECT::ICEBULLET].xSpeed = jjObjectPresets[OBJECT::ICEBULLET].xSpeed * -3;
		jjObjectPresets[OBJECT::ICEBULLET].ySpeed = 0;
		jjObjectPresets[OBJECT::ICEBULLET].xAcc = 0;
		jjObjectPresets[OBJECT::ICEBULLET].yAcc = 0;
		jjObjectPresets[OBJECT::ICEBULLET].counterEnd = 30;
		jjObjectPresets[OBJECT::ICEBULLET].freeze = 0;
		jjObjectPresets[OBJECT::ICEBULLET].var[7] = 0;
		jjObjectPresets[OBJECT::ICEBULLET].behavior = IceBurster();
		jjObjectPresets[OBJECT::ICEBULLET].special = jjObjectPresets[OBJECT::ICEBULLET].determineCurAnim(ANIM::PICKUPS, 45);
		jjObjectPresets[OBJECT::ICEBULLET].determineCurFrame();
		jjObjectPresets[OBJECT::ICEBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::ICEBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::ICEBULLET].animSpeed = 25;
		
		jjObjectPresets[OBJECT::ICEBULLETPU].xSpeed = jjObjectPresets[OBJECT::ICEBULLETPU].xSpeed * -3;
		jjObjectPresets[OBJECT::ICEBULLETPU].ySpeed = 0;
		jjObjectPresets[OBJECT::ICEBULLETPU].xAcc = 0;
		jjObjectPresets[OBJECT::ICEBULLETPU].yAcc = 0;
		jjObjectPresets[OBJECT::ICEBULLETPU].counterEnd = 30;
		jjObjectPresets[OBJECT::ICEBULLETPU].freeze = 0;
		jjObjectPresets[OBJECT::ICEBULLETPU].var[7] = 0;
		jjObjectPresets[OBJECT::ICEBULLETPU].behavior = IceBurster();
		jjObjectPresets[OBJECT::ICEBULLETPU].special = jjObjectPresets[OBJECT::ICEBULLETPU].determineCurAnim(ANIM::PICKUPS, 45);
		jjObjectPresets[OBJECT::ICEBULLETPU].determineCurFrame();
		jjObjectPresets[OBJECT::ICEBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::ICEBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::ICEBULLETPU].animSpeed = 50;
		jjObjectPresets[OBJECT::ICEBULLETPU].killAnim = jjObjectPresets[OBJECT::RFBULLET].killAnim;
		
		jjWeapons[WEAPON::SEEKER].maximum = 15;
		
		jjObjectPresets[OBJECT::SEEKERBULLET].xSpeed = jjObjectPresets[OBJECT::SEEKERBULLET].xSpeed * -5;
		jjObjectPresets[OBJECT::SEEKERBULLET].ySpeed = jjObjectPresets[OBJECT::SEEKERBULLET].ySpeed * 5;
		jjObjectPresets[OBJECT::SEEKERBULLET].xAcc = 0;
		jjObjectPresets[OBJECT::SEEKERBULLET].yAcc = 0;
		jjObjectPresets[OBJECT::SEEKERBULLET].counterEnd = 160;
		jjObjectPresets[OBJECT::SEEKERBULLET].lightType = jjObjectPresets[OBJECT::BLASTERBULLET].lightType;
		jjObjectPresets[OBJECT::SEEKERBULLET].behavior = HomingMissile();
		jjObjectPresets[OBJECT::SEEKERBULLET].special = jjObjectPresets[OBJECT::SEEKERBULLET].determineCurAnim(ANIM::AMMO, 37);
		jjObjectPresets[OBJECT::SEEKERBULLET].determineCurFrame();
		jjObjectPresets[OBJECT::SEEKERBULLET].killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
		jjObjectPresets[OBJECT::SEEKERBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::SEEKERBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::SEEKERBULLET].animSpeed = 30;
		
		jjObjectPresets[OBJECT::SEEKERBULLETPU].xSpeed = jjObjectPresets[OBJECT::SEEKERBULLETPU].xSpeed * -6;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].ySpeed = jjObjectPresets[OBJECT::SEEKERBULLETPU].ySpeed * 6;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].xAcc = 0;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].yAcc = 0;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].counterEnd = 180;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].lightType = jjObjectPresets[OBJECT::BLASTERBULLET].lightType;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].behavior = HomingMissile();
		jjObjectPresets[OBJECT::SEEKERBULLETPU].special = jjObjectPresets[OBJECT::SEEKERBULLETPU].determineCurAnim(ANIM::AMMO, 42);
		jjObjectPresets[OBJECT::SEEKERBULLETPU].determineCurFrame();
		jjObjectPresets[OBJECT::SEEKERBULLETPU].killAnim = jjObjectPresets[OBJECT::SEEKERBULLETPU].killAnim;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::SEEKERBULLETPU].animSpeed = 60;
		
		jjWeapons[WEAPON::RF].maximum = 10;
		jjWeapons[WEAPON::RF].spread = SPREAD::NORMAL;
		jjWeapons[WEAPON::RF].defaultSample = false;
		
		jjObjectPresets[OBJECT::RFBULLET].xSpeed = jjObjectPresets[OBJECT::RFBULLET].xSpeed * -10;
		jjObjectPresets[OBJECT::RFBULLET].ySpeed = jjObjectPresets[OBJECT::RFBULLET].ySpeed * -10;
		jjObjectPresets[OBJECT::RFBULLET].xAcc = 0;
		jjObjectPresets[OBJECT::RFBULLET].yAcc = 0;
		jjObjectPresets[OBJECT::RFBULLET].counterEnd = 180;
		jjObjectPresets[OBJECT::RFBULLET].lightType = LIGHT::NONE;
		jjObjectPresets[OBJECT::RFBULLET].behavior = SuperMissile();
		jjObjectPresets[OBJECT::RFBULLET].special = jjObjectPresets[OBJECT::RFBULLET].determineCurAnim(ANIM::SONCSHIP, 0);
		jjObjectPresets[OBJECT::RFBULLET].determineCurFrame();
		jjObjectPresets[OBJECT::RFBULLET].killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
		jjObjectPresets[OBJECT::RFBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::RFBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::RFBULLET].animSpeed = 100;
		
		jjObjectPresets[OBJECT::RFBULLETPU].xSpeed = jjObjectPresets[OBJECT::RFBULLETPU].xSpeed * -10;
		jjObjectPresets[OBJECT::RFBULLETPU].ySpeed = jjObjectPresets[OBJECT::RFBULLETPU].ySpeed * -10;
		jjObjectPresets[OBJECT::RFBULLETPU].xAcc = 0;
		jjObjectPresets[OBJECT::RFBULLETPU].yAcc = 0;
		jjObjectPresets[OBJECT::RFBULLETPU].counterEnd = 180;
		jjObjectPresets[OBJECT::RFBULLETPU].lightType = LIGHT::NONE;
		jjObjectPresets[OBJECT::RFBULLETPU].behavior = SuperMissile();
		jjObjectPresets[OBJECT::RFBULLETPU].special = jjObjectPresets[OBJECT::RFBULLETPU].determineCurAnim(ANIM::SONCSHIP, 0);
		jjObjectPresets[OBJECT::RFBULLETPU].determineCurFrame();
		jjObjectPresets[OBJECT::RFBULLETPU].killAnim = jjObjectPresets[OBJECT::SEEKERBULLET].killAnim;
		jjObjectPresets[OBJECT::RFBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::RFBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::RFBULLETPU].animSpeed = 200;
		
		jjWeapons[WEAPON::TOASTER].maximum = 50;
		jjWeapons[WEAPON::TOASTER].multiplier = 8;
		
		jjObjectPresets[OBJECT::TOASTERBULLET].xAcc = 0;
		jjObjectPresets[OBJECT::TOASTERBULLET].yAcc = 0;
		jjObjectPresets[OBJECT::TOASTERBULLET].behavior = Flamethrower();
		jjObjectPresets[OBJECT::TOASTERBULLET].determineCurFrame();
		jjObjectPresets[OBJECT::TOASTERBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::TOASTERBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::TOASTERBULLET].animSpeed = 6;
		
		jjObjectPresets[OBJECT::TOASTERBULLETPU].xAcc = 0;
		jjObjectPresets[OBJECT::TOASTERBULLETPU].yAcc = 0;
		jjObjectPresets[OBJECT::TOASTERBULLETPU].behavior = Flamethrower();
		jjObjectPresets[OBJECT::TOASTERBULLETPU].determineCurFrame();
		jjObjectPresets[OBJECT::TOASTERBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::TOASTERBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::TOASTERBULLETPU].animSpeed = 12;
		
		jjWeapons[WEAPON::TNT].maximum = 10;
		
		jjAnimSets[ANIM::ROBOT].load();
		jjAnimations[jjAnimSets[ANIM::AMMO] + 59] = jjAnimations[jjAnimSets[ANIM::ROBOT] + 0];
		
		jjObjectPresets[OBJECT::TNT].killAnim = jjObjectPresets[OBJECT::RFBULLET].killAnim;
		jjObjectPresets[OBJECT::TNT].special = jjObjectPresets[OBJECT::TNT].determineCurAnim(ANIM::ROBOT, 0);
		jjObjectPresets[OBJECT::TNT].determineCurFrame();
		jjObjectPresets[OBJECT::TNT].lightType = LIGHT::NONE;
		jjObjectPresets[OBJECT::TNT].behavior = SpikeMine();
		jjObjectPresets[OBJECT::TNT].scriptedCollisions = true;
		jjObjectPresets[OBJECT::TNT].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::TNT].animSpeed = 70;
		jjObjectPresets[OBJECT::TNT].counterEnd = 245;
		jjObjectPresets[OBJECT::TNT].isBlastable = false;
		
		jjWeapons[WEAPON::GUN8].maximum = 25;
		jjWeapons[WEAPON::GUN8].spread = SPREAD::NORMAL;
		jjWeapons[WEAPON::GUN8].gradualAim = false;
		jjWeapons[WEAPON::GUN8].defaultSample = false;
		
		jjObjectPresets[OBJECT::FIREBALLBULLET].xSpeed = -15;
		jjObjectPresets[OBJECT::FIREBALLBULLET].ySpeed = 0;
		jjObjectPresets[OBJECT::FIREBALLBULLET].xAcc = 0;
		jjObjectPresets[OBJECT::FIREBALLBULLET].yAcc = 0;
		jjObjectPresets[OBJECT::FIREBALLBULLET].eventID = OBJECT::BLASTERBULLET;
		jjObjectPresets[OBJECT::FIREBALLBULLET].behavior = Laser();
		jjObjectPresets[OBJECT::FIREBALLBULLET].special = jjObjectPresets[OBJECT::FIREBALLBULLET].determineCurAnim(ANIM::AMMO, 30);
		jjObjectPresets[OBJECT::FIREBALLBULLET].determineCurFrame();
		jjObjectPresets[OBJECT::FIREBALLBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::FIREBALLBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::FIREBALLBULLET].animSpeed = 40;
		jjObjectPresets[OBJECT::FIREBALLBULLET].counterEnd = 140;
		jjObjectPresets[OBJECT::FIREBALLBULLET].killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
		
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].xSpeed = -15;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].ySpeed = 0;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].xAcc = 0;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].yAcc = 0;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].eventID = OBJECT::BLASTERBULLETPU;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].behavior = Laser();
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].special = jjObjectPresets[OBJECT::FIREBALLBULLETPU].determineCurAnim(ANIM::AMMO, 30);
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].determineCurFrame();
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].animSpeed = 80;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].counterEnd = 140;
		jjObjectPresets[OBJECT::FIREBALLBULLETPU].killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
		
		jjWeapons[WEAPON::GUN9].maximum = 50;
		jjObjectPresets[OBJECT::ELECTROBULLET].xSpeed = jjObjectPresets[OBJECT::ELECTROBULLET].xSpeed * -4;
		jjObjectPresets[OBJECT::ELECTROBULLET].ySpeed = 0;
		jjObjectPresets[OBJECT::ELECTROBULLET].counterEnd = 200;
		jjObjectPresets[OBJECT::ELECTROBULLET].behavior = ElectroBlaster();
		jjObjectPresets[OBJECT::ELECTROBULLET].scriptedCollisions = true;
		jjObjectPresets[OBJECT::ELECTROBULLET].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::ELECTROBULLET].animSpeed = 15;
		
		jjObjectPresets[OBJECT::ELECTROBULLETPU].xSpeed = jjObjectPresets[OBJECT::ELECTROBULLETPU].xSpeed * -4;
		jjObjectPresets[OBJECT::ELECTROBULLETPU].ySpeed = 0;
		jjObjectPresets[OBJECT::ELECTROBULLETPU].counterEnd = 210;
		jjObjectPresets[OBJECT::ELECTROBULLETPU].behavior = ElectroBlaster();
		jjObjectPresets[OBJECT::ELECTROBULLETPU].scriptedCollisions = true;
		jjObjectPresets[OBJECT::ELECTROBULLETPU].playerHandling = HANDLING::SPECIAL;
		jjObjectPresets[OBJECT::ELECTROBULLETPU].animSpeed = 30;
		
		jjObjectPresets[OBJECT::BOUNCERAMMO3].behavior = 
		jjObjectPresets[OBJECT::ICEAMMO3].behavior = 
		jjObjectPresets[OBJECT::SEEKERAMMO3].behavior = 
		jjObjectPresets[OBJECT::RFAMMO3].behavior = 
		jjObjectPresets[OBJECT::TOASTERAMMO3].behavior = 
		jjObjectPresets[OBJECT::TNTAMMO3].behavior = 
		jjObjectPresets[OBJECT::GUN8AMMO3].behavior = 
		jjObjectPresets[OBJECT::GUN9AMMO3].behavior = AmmoPickup();
		
		jjObjectPresets[OBJECT::BOUNCERAMMO3].scriptedCollisions = 
		jjObjectPresets[OBJECT::ICEAMMO3].scriptedCollisions = 
		jjObjectPresets[OBJECT::SEEKERAMMO3].scriptedCollisions = 
		jjObjectPresets[OBJECT::RFAMMO3].scriptedCollisions = 
		jjObjectPresets[OBJECT::TOASTERAMMO3].scriptedCollisions = 
		jjObjectPresets[OBJECT::TNTAMMO3].scriptedCollisions = 
		jjObjectPresets[OBJECT::GUN8AMMO3].scriptedCollisions = 
		jjObjectPresets[OBJECT::GUN9AMMO3].scriptedCollisions = true;
		
		jjObjectPresets[OBJECT::BOUNCERAMMO3].var[4] = 2;
		jjObjectPresets[OBJECT::ICEAMMO3].var[4] = 3;
		jjObjectPresets[OBJECT::SEEKERAMMO3].var[4] = 4; 
		jjObjectPresets[OBJECT::RFAMMO3].var[4] = 5;
		jjObjectPresets[OBJECT::TOASTERAMMO3].var[4] = 6; 
		jjObjectPresets[OBJECT::TNTAMMO3].var[4] = 7;
		jjObjectPresets[OBJECT::GUN8AMMO3].var[4] = 8;
		jjObjectPresets[OBJECT::GUN9AMMO3].var[4] = 9;
		
		jjObjectPresets[OBJECT::BOUNCERAMMO3].var[5] = 5;
		jjObjectPresets[OBJECT::ICEAMMO3].var[5] = 2;
		jjObjectPresets[OBJECT::SEEKERAMMO3].var[5] = 2; 
		jjObjectPresets[OBJECT::RFAMMO3].var[5] = 1;
		jjObjectPresets[OBJECT::TOASTERAMMO3].var[5] = 3; 
		jjObjectPresets[OBJECT::TNTAMMO3].var[5] = 1;
		jjObjectPresets[OBJECT::GUN8AMMO3].var[5] = 2;
		jjObjectPresets[OBJECT::GUN9AMMO3].var[5] = 3;
		
		jjObjectPresets[OBJECT::CARROT].behavior = HealthPickup();
		jjObjectPresets[OBJECT::CARROT].scriptedCollisions = true;
		jjObjectPresets[OBJECT::CARROT].determineCurAnim(customAnimID, 6);
		jjObjectPresets[OBJECT::CARROT].determineCurFrame();
		jjObjectPresets[OBJECT::CARROT].light = 8;
		
		jjObjectPresets[OBJECT::FULLENERGY].behavior = MegaHealthPickup();
		jjObjectPresets[OBJECT::FULLENERGY].scriptedCollisions = true;
		jjObjectPresets[OBJECT::FULLENERGY].determineCurAnim(customAnimID, 7);
		jjObjectPresets[OBJECT::FULLENERGY].determineCurFrame();
		
		jjObjectPresets[OBJECT::FIRESHIELD].behavior = PowerOrb();
		jjObjectPresets[OBJECT::FIRESHIELD].scriptedCollisions = true;
		jjObjectPresets[OBJECT::FIRESHIELD].playerHandling = HANDLING::PICKUP;
		jjObjectPresets[OBJECT::FIRESHIELD].light = 8;
		jjObjectPresets[OBJECT::FIRESHIELD].triggersTNT = false;
		jjObjectPresets[OBJECT::FIRESHIELD].determineCurAnim(customAnimID, 5);
		jjObjectPresets[OBJECT::FIRESHIELD].determineCurFrame();
		
		jjObjectPresets[OBJECT::PURPLEGEM].determineCurAnim(customAnimID, 1);
		jjObjectPresets[OBJECT::PURPLEGEM].determineCurFrame();
		jjObjectPresets[OBJECT::PURPLEGEM].behavior = ShipExplosion();
		jjObjectPresets[OBJECT::PURPLEGEM].playerHandling = HANDLING::PARTICLE;
		jjObjectPresets[OBJECT::PURPLEGEM].bulletHandling = HANDLING::IGNOREBULLET;
		jjObjectPresets[OBJECT::PURPLEGEM].frameID = 0;
		
		jjObjectPresets[OBJECT::FULLENERGY].light = 8;
		
		jjObjectPresets[OBJECT::PLASMASHIELD].behavior = ShieldPickup();
		jjObjectPresets[OBJECT::PLASMASHIELD].scriptedCollisions = true;
		jjObjectPresets[OBJECT::PLASMASHIELD].playerHandling = HANDLING::PICKUP;
		jjObjectPresets[OBJECT::PLASMASHIELD].light = 8;
		jjObjectPresets[OBJECT::PLASMASHIELD].triggersTNT = false;
		jjObjectPresets[OBJECT::PLASMASHIELD].determineCurAnim(customAnimID, 3);
		jjObjectPresets[OBJECT::PLASMASHIELD].determineCurFrame();
		
		array<OBJECT::Object> Powerups = {OBJECT::BLASTERPOWERUP, OBJECT::BOUNCERPOWERUP, OBJECT::ICEPOWERUP, OBJECT::SEEKERPOWERUP, OBJECT::RFPOWERUP, OBJECT::TOASTERPOWERUP, OBJECT::TNTPOWERUP, OBJECT::GUN8POWERUP, OBJECT::GUN9POWERUP};
		for (uint p = 0; p < Powerups.length(); p++) {
			jjObjectPresets[Powerups[p]].behavior = ShieldPickup();
			jjObjectPresets[Powerups[p]].scriptedCollisions = true;
			jjObjectPresets[Powerups[p]].playerHandling = HANDLING::PICKUP;
			jjObjectPresets[Powerups[p]].light = 8;
			jjObjectPresets[Powerups[p]].triggersTNT = false;
			jjObjectPresets[Powerups[p]].determineCurAnim(customAnimID, 3);
			jjObjectPresets[Powerups[p]].determineCurFrame();
		}
		
		jjObjectPresets[OBJECT::WATERSHIELD].behavior = MegaShieldPickup();
		jjObjectPresets[OBJECT::WATERSHIELD].scriptedCollisions = true;
		jjObjectPresets[OBJECT::WATERSHIELD].playerHandling = HANDLING::PICKUP;
		jjObjectPresets[OBJECT::WATERSHIELD].light = 8;
		jjObjectPresets[OBJECT::WATERSHIELD].triggersTNT = false;
		jjObjectPresets[OBJECT::WATERSHIELD].determineCurAnim(customAnimID, 4);
		jjObjectPresets[OBJECT::WATERSHIELD].determineCurFrame();
	}

	jjOBJ@ assignHealthPickup(uint8 eventID) {	
		jjOBJ@ preset = jjObjectPresets[eventID];
		preset.behavior = HealthPickup();
		preset.scriptedCollisions = true;
		preset.playerHandling = HANDLING::PICKUP;
		preset.triggersTNT = false;
		preset.determineCurAnim(customAnimID, 6);
		preset.determineCurFrame();
		preset.light = 8;
		
		return preset;
	}
	
	jjOBJ@ assignMegaHealthPickup(uint8 eventID) {	
		jjOBJ@ preset = jjObjectPresets[eventID];
		preset.behavior = MegaHealthPickup();
		preset.scriptedCollisions = true;
		preset.playerHandling = HANDLING::PICKUP;
		preset.triggersTNT = false;
		preset.determineCurAnim(customAnimID, 7);
		preset.determineCurFrame();
		preset.light = 8;
		
		return preset;
	}
	
	jjOBJ@ assignShieldPickup(uint8 eventID) {	
		jjOBJ@ preset = jjObjectPresets[eventID];
		preset.behavior = ShieldPickup();
		preset.scriptedCollisions = true;
		preset.playerHandling = HANDLING::PICKUP;
		preset.triggersTNT = false;
		preset.determineCurAnim(customAnimID, 3);
		preset.determineCurFrame();
		preset.light = 8;
		
		return preset;
	}
	
	jjOBJ@ assignMegaShieldPickup(uint8 eventID) {	
		jjOBJ@ preset = jjObjectPresets[eventID];
		preset.behavior = MegaShieldPickup();
		preset.scriptedCollisions = true;
		preset.playerHandling = HANDLING::PICKUP;
		preset.triggersTNT = false;
		preset.determineCurAnim(customAnimID, 4);
		preset.determineCurFrame();
		preset.light = 8;
		
		return preset;
	}
	
	jjOBJ@ assignPowerOrb(uint8 eventID) {	
		jjOBJ@ preset = jjObjectPresets[eventID];
		preset.behavior = PowerOrb();
		preset.scriptedCollisions = true;
		preset.playerHandling = HANDLING::PICKUP;
		preset.triggersTNT = false;
		preset.determineCurAnim(customAnimID, 5);
		preset.determineCurFrame();
		preset.light = 8;
		
		return preset;
	}

	void assignPickupDefaults() {
		jjObjectPresets[OBJECT::CARROT].behavior = HealthPickup();
		jjObjectPresets[OBJECT::CARROT].scriptedCollisions = true;
		jjObjectPresets[OBJECT::CARROT].determineCurAnim(customAnimID, 6);
		jjObjectPresets[OBJECT::CARROT].determineCurFrame();
		jjObjectPresets[OBJECT::CARROT].light = 8;
		
		jjObjectPresets[OBJECT::FULLENERGY].behavior = MegaHealthPickup();
		jjObjectPresets[OBJECT::FULLENERGY].scriptedCollisions = true;
		jjObjectPresets[OBJECT::FULLENERGY].determineCurAnim(customAnimID, 7);
		jjObjectPresets[OBJECT::FULLENERGY].determineCurFrame();
		
		jjObjectPresets[OBJECT::SEEKERPOWERUP].behavior = MegaShieldPickup();
		jjObjectPresets[OBJECT::SEEKERPOWERUP].scriptedCollisions = true;
		jjObjectPresets[OBJECT::SEEKERPOWERUP].playerHandling = HANDLING::PICKUP;
		jjObjectPresets[OBJECT::SEEKERPOWERUP].light = 8;
		jjObjectPresets[OBJECT::SEEKERPOWERUP].triggersTNT = false;
		jjObjectPresets[OBJECT::SEEKERPOWERUP].determineCurAnim(customAnimID, 4);
		jjObjectPresets[OBJECT::SEEKERPOWERUP].determineCurFrame();
		
		array<OBJECT::Object> Shields = {OBJECT::FIRESHIELD, OBJECT::WATERSHIELD, OBJECT::LIGHTNINGSHIELD, OBJECT::LASERSHIELD};
		for (uint s = 0; s < Shields.length(); s++) {
			jjObjectPresets[Shields[s]].behavior = PowerOrb();
			jjObjectPresets[Shields[s]].scriptedCollisions = true;
			jjObjectPresets[Shields[s]].playerHandling = HANDLING::PICKUP;
			jjObjectPresets[Shields[s]].light = 8;
			jjObjectPresets[Shields[s]].triggersTNT = false;
			jjObjectPresets[Shields[s]].determineCurAnim(customAnimID, 5);
			jjObjectPresets[Shields[s]].determineCurFrame();
		}
		
		array<OBJECT::Object> Powerups = {OBJECT::BLASTERPOWERUP, OBJECT::BOUNCERPOWERUP, OBJECT::ICEPOWERUP, OBJECT::RFPOWERUP, OBJECT::TOASTERPOWERUP, OBJECT::TNTPOWERUP, OBJECT::GUN8POWERUP, OBJECT::GUN9POWERUP};
		for (uint p = 0; p < Powerups.length(); p++) {
			jjObjectPresets[Powerups[p]].behavior = ShieldPickup();
			jjObjectPresets[Powerups[p]].scriptedCollisions = true;
			jjObjectPresets[Powerups[p]].playerHandling = HANDLING::PICKUP;
			jjObjectPresets[Powerups[p]].light = 8;
			jjObjectPresets[Powerups[p]].triggersTNT = false;
			jjObjectPresets[Powerups[p]].determineCurAnim(customAnimID, 3);
			jjObjectPresets[Powerups[p]].determineCurFrame();
		}
		
		jjObjectPresets[OBJECT::PURPLEGEM].determineCurAnim(customAnimID, 1);
		jjObjectPresets[OBJECT::PURPLEGEM].determineCurFrame();
		jjObjectPresets[OBJECT::PURPLEGEM].behavior = ShipExplosion();
		jjObjectPresets[OBJECT::PURPLEGEM].playerHandling = HANDLING::PARTICLE;
		jjObjectPresets[OBJECT::PURPLEGEM].bulletHandling = HANDLING::IGNOREBULLET;
		jjObjectPresets[OBJECT::PURPLEGEM].frameID = 0;
	}
	
	void handleAnimationWork() {
		Resize::Resize(
			jjAnimations[jjAnimSets[ANIM::JAZZ] + 53],
			3,
			Resize::Method::Scale2x
		);
		
		Resize::Resize(
			jjAnimations[jjAnimSets[ANIM::SPAZ] + 53],
			3,
			Resize::Method::Scale2x
		);
		
		Resize::Resize(
			jjAnimations[jjAnimSets[ANIM::LORI] + 53],
			3,
			Resize::Method::Scale2x
		);
		
		Resize::Resize(
			jjAnimations[jjAnimSets[ANIM::PICKUPS] + 41],
			2,
			Resize::Method::Scale2x
		);
		
		Resize::Resize(
			jjAnimations[jjAnimSets[ANIM::AMMO] + 30],
			2,
			Resize::Method::Scale2x
		);
		
		Resize::Resize(
			jjAnimations[jjAnimSets[ANIM::SONCSHIP] + 0],
			1.5,
			Resize::Method::Scale2x
		);
		
		Resize::Resize(
			jjAnimations[jjAnimSets[ANIM::MENU] + 1],
			3.5,
			Resize::Method::Scale2x
		);
		
		
		jjANIMATION@ animBlaster = jjAnimations[jjAnimSets[ANIM::DEVAN] + 0];
		for (uint i = 0; i < animBlaster.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animBlaster + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 32 && sprite[x,y] <= 39) sprite[x,y] -= 17;
				}
			}
			sprite.save(frame);
		}
		
		jjANIMATION@ animBurst = jjAnimations[jjAnimSets[ANIM::PICKUPS] + 45];
		for (uint i = 0; i < animBurst.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animBurst + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 32 && sprite[x,y] <= 39) sprite[x,y] += 56;
				}
			}
			sprite.save(frame);
		}
		
		jjANIMATION@ animBurstAmmo = jjAnimations[jjAnimSets[ANIM::AMMO] + 29];
		for (uint i = 0; i < animBurstAmmo.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animBurstAmmo + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 32 && sprite[x,y] <= 39) sprite[x,y] += 56;
				}
			}
			sprite.save(frame);
		}
		
		jjANIMATION@ animBurstAmmoPU = jjAnimations[jjAnimSets[ANIM::AMMO] + 28];
		for (uint i = 0; i < animBurstAmmoPU.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animBurstAmmoPU + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 88 && sprite[x,y] <= 95) sprite[x,y] -= 33;
				}
			}
			sprite.save(frame);
		}
		
		jjANIMATION@ animMissile = jjAnimations[jjAnimSets[ANIM::SONCSHIP] + 0];
		for (uint i = 0; i < animMissile.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animMissile + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 88 && sprite[x,y] <= 95) sprite[x,y] -= 72;
				}
			}
			sprite.save(frame);
		}
		
		
		jjANIMATION@ animLaser = jjAnimations[jjAnimSets[ANIM::AMMO] + 30];
		for (uint i = 0; i < animLaser.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animLaser + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 32 && sprite[x,y] <= 39) sprite[x,y] += 48;
				}
			}
			sprite.save(frame);
		}
		
		jjANIMATION@ animLaserAmmo = jjAnimations[jjAnimSets[ANIM::AMMO] + 62];
		for (uint i = 0; i < animLaserAmmo.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animLaserAmmo + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 88 && sprite[x,y] <= 95) sprite[x,y] -= 8;
				}
			}
			sprite.save(frame);
		}
		
		jjANIMATION@ animLaserAmmoPU = jjAnimations[jjAnimSets[ANIM::AMMO] + 61];
		for (uint i = 0; i < animLaserAmmoPU.frameCount; i++) {
			jjANIMFRAME@ frame = jjAnimFrames[animLaserAmmoPU + i];
			jjPIXELMAP sprite(frame);
			for (uint x = 0; x < sprite.width; ++x) {
				for (uint y = 0; y < sprite.height; ++y) {
					if (sprite[x,y] >= 24 && sprite[x,y] <= 31) sprite[x,y] += 24;
				}
			}
			sprite.save(frame);
		}
	}

	void handleOnLevelBegin() {
		jjAlert("|||Press |||||F2||| for instructions!");
		jjSampleLoad(SOUND::COMMON_LANDPOP, "silence.wav");
	}

	void handleShipCollisions(jjOBJ@ obj, jjPLAYER@ creator) {
		if (!jjLocalPlayers[0].isIdle) {
			for (int i = 0; i < 32; i++) {
				jjPLAYER@ play = jjPlayers[i];
				SPACESHIP@ ship = Ships[i];
				
				if (obj.doesCollide(play, true) && canBeHit(play,creator,obj) && (obj.var[8] & 1 << play.playerID == 0 || obj.eventID != OBJECT::FIREBALLBULLET)) {
					if (obj.special != 100) {
						ship.damageAccumulated += obj.animSpeed;
						ship.accumulationTime = 0;
						obj.special = 100;
					}
					if (obj.eventID == OBJECT::ICEBULLET || obj.eventID == OBJECT::ICEBULLETPU) damageNumber(creator, play, ship.damageCalc(ship, ship.damageAccumulated));
					
					if (!ship.justHit) {
						ship.justHit = true;
						ship.hitDelay = 0;
									
						randomHurtSample(play, 48);
									
						if (!play.isLocal && obj.eventID != OBJECT::ICEBULLET && obj.eventID != OBJECT::ICEBULLETPU) damageNumber(creator, play, ship.damageCalc(ship, ship.damageAccumulated));
									
						if ((obj.eventID == OBJECT::SEEKERBULLET || obj.eventID == OBJECT::SEEKERBULLETPU) && creator.isLocal) obj.state = STATE::EXPLODE;
									
						jjSample(
							play.xPos, play.yPos,
							(obj.eventID == OBJECT::TOASTERBULLET || obj.eventID == OBJECT::TOASTERBULLETPU)? SOUND::BILSBOSS_FIRE :
							(obj.eventID == OBJECT::SEEKERBULLET || obj.eventID == OBJECT::SEEKERBULLETPU || obj.eventID == OBJECT::RFBULLET || obj.eventID == OBJECT::RFBULLETPU || obj.eventID == OBJECT::TNT)? SOUND::COMMON_EXPL_TNT :
							(obj.eventID == OBJECT::ELECTROBULLET || obj.eventID == OBJECT::ELECTROBULLETPU)? SOUND::COMMON_ELECTRICHIT :
							SOUND::ROBOT_BIG2, 63, 12500 + jjRandom()%2500
						);
						if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
					}
				}
			}
		}
	}

	class Blaster : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			
			obj.behave(BEHAVIOR::BULLET, obj.state == STATE::FLY && inView(obj)? false:true);
			obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
			
			if (obj.state == STATE::START) {
				obj.state = STATE::FLY;			
			}
			
			if (obj.state == STATE::FLY) {
				if (obj.counter == 1 && jjPlayers[obj.creatorID].isLocal) jjSample(obj.xPos, obj.yPos, SOUND::DEVILDEVAN_PHASER2, 50, obj.eventID == OBJECT::BLASTERBULLETPU? 11500 : 0);
				if (obj.counter > 1 && inView(obj)) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0], 1, 1, obj.eventID == OBJECT::BLASTERBULLETPU? SPRITE::SINGLEHUE : SPRITE::NORMAL, 49);
			}
			
			if (obj.findNearestPlayer(1000) > -1 && obj.findNearestPlayer(1000) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
			
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj)) {
				ship.xSpeed += recoilX(ship,obj,12);
				ship.ySpeed += recoilY(ship,obj,12);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::BLASTERBULLETPU? jjObjectPresets[OBJECT::BLASTERBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::BLASTERBULLET].animSpeed * 1.3);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				
				jjSample(play.xPos, play.yPos, SOUND::ROBOT_BIG2, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
			}
			return true;
		}
	}

	class RapidBlaster : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			obj.behave(BEHAVIOR::BULLET, obj.state == STATE::FLY && inView(obj)? false:true);
			obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
			
			if (obj.state == STATE::START) {
				obj.state = STATE::FLY;
			}
			
			if (obj.state == STATE::FLY) {
				if (obj.counter == 1 && jjPlayers[obj.creatorID].isLocal) jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PISTOL1, 50, obj.eventID == OBJECT::BOUNCERBULLETPU? 20000 : 0);
				if (obj.counter > 1) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0], 1, 1, obj.eventID == OBJECT::BOUNCERBULLETPU? SPRITE::SINGLEHUE : SPRITE::NORMAL, 88);
			}
			
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
		}
		
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj)) {
				ship.xSpeed += recoilX(ship,obj,7.5);
				ship.ySpeed += recoilY(ship,obj,7.5);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::BOUNCERBULLETPU? jjObjectPresets[OBJECT::BOUNCERBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::BOUNCERBULLET].animSpeed * 1.3);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				jjSample(play.xPos, play.yPos, SOUND::ROBOT_BIG2, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
			}
			return true;
		}
	}

	class IceBurster : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::BULLET, obj.state == STATE::FLY && inView(obj)? false:true);
			obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
			
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			
			if (obj.state == STATE::START) {
				obj.state = STATE::FLY;
			}
			
			if (obj.state == STATE::FLY) {
				if (obj.counter == 2 && jjPlayers[obj.creatorID].isLocal) {
					jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ICECRUSH, 30, obj.eventID == OBJECT::ICEBULLETPU? 10500:0);
					if (creator.ySpeed == 0) {
						if (obj.ySpeed < - 12) obj.ySpeed = -12;
						if (obj.ySpeed > 12) obj.ySpeed = 12;
					}
				}
				if (obj.counter > 1 && inView(obj)) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0], 1, 1, obj.eventID == OBJECT::ICEBULLETPU? SPRITE::SINGLEHUE : SPRITE::NORMAL, 40);
				if (obj.eventID == OBJECT::ICEBULLETPU && jjGameTicks % 3 == 0) {
					jjPARTICLE@ cinders = jjAddParticle(PARTICLE::FIRE);
					cinders.xPos = obj.xPos;
					cinders.yPos = obj.yPos;
				}
			}
			
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
			
		}
		
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj)) {
				ship.xSpeed += recoilX(ship,obj,17.5);
				ship.ySpeed += recoilY(ship,obj,17.5);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::ICEBULLETPU? jjObjectPresets[OBJECT::ICEBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::ICEBULLET].animSpeed * 1.3);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (obj.special != 100) {
					ship.damageAccumulated += obj.animSpeed;
					ship.accumulationTime = 0;
					obj.special = 100;
				}
				if (obj.eventID == OBJECT::ICEBULLET || obj.eventID == OBJECT::ICEBULLETPU) damageNumber(creator, play, ship.damageCalc(ship, ship.damageAccumulated));
				
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				jjSample(play.xPos, play.yPos, SOUND::ROBOT_BIG2, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
			}
			return true;
		}
	}

	class HomingMissile : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			obj.behave(BEHAVIOR::BULLET, obj.state == STATE::FLY && inView(obj)? false:true);
			obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
			++obj.age;
			if (obj.state == STATE::FLY && obj.counter > 1 && inView(obj)) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0], 1, 1, SPRITE::NORMAL);
			
				if (obj.state == STATE::START) {
					obj.points = int(atan2(obj.xSpeed, obj.ySpeed) * 162.974636 /*+ (jjCos(jjRandom() % 512) * 10)*/); //angle
					obj.state = STATE::FLY;
					obj.xSpeed += obj.var[7] / 65536.f;
				} else if (obj.age > 14) { //redirect
					array<float>@ target = null;
					if (obj.creatorType == CREATOR::PLAYER && jjGameMode > GAME::COOP) {
						const int targetPlayerID = closestPlayers[obj.creatorID]; //closest to player who fired this
						if (targetPlayerID >= 0)
							@target = array<float> = { jjPlayers[targetPlayerID].xPos, jjPlayers[targetPlayerID].yPos };
					}
					if (target is null)
						@target = MLLEWeapons::HelpfulBulletFunctions::GetNearestEnemyPosition(obj, 350); //closest to bullet itself... this part is new to the .asc reimplementation
					
					if (target !is null) {
						int difference = ((int(atan2(obj.xPos - target[0], obj.yPos - target[1]) * 162.974636) + 512 - obj.points) & 1023);
						if (difference >= 512) difference -= 1024;
						obj.points += difference / 8;
						obj.xSpeed = 4.75 * jjSin(obj.points);
						obj.ySpeed = 4.75 * jjCos(obj.points);
					}
				}
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj) && obj.state != STATE::EXPLODE) {
				ship.xSpeed += recoilX(ship,obj,55);
				ship.ySpeed += recoilY(ship,obj,55);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::SEEKERBULLETPU? jjObjectPresets[OBJECT::SEEKERBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::SEEKERBULLET].animSpeed * 1.5);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				jjSample(play.xPos, play.yPos, SOUND::COMMON_EXPL_TNT, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
			}
			return true;
		}
	}

	class SuperMissile : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::BULLET, false);
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			jjPLAYER@ play;
			obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
			
			switch (obj.state) {
				case STATE::START:
					obj.state = STATE::FLY;
					obj.lightType = LIGHT::POINT;
					obj.var[2] = 0;
				break;
				
				case STATE::FLY:
					if (obj.counter == 1 && creator.isLocal) {
						jjSample(creator.xPos, creator.yPos, SOUND::INTRO_SHOT1, 0, obj.eventID == OBJECT::RFBULLETPU? 35500 : 0);
					}
					
					if (obj.counter > 1 && inView(obj)) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0], 1, 1, obj.eventID == OBJECT::RFBULLETPU? SPRITE::PALSHIFT : SPRITE::NORMAL, 8);
					
					if (obj.counter % 5 == 0 && !jjLowDetail) {
						jjOBJ@ trail = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(obj.xPos - jjCos(obj.var[0])), int(obj.yPos - jjSin(obj.var[0])))];
						trail.determineCurAnim(ANIM::AMMO, 3);
						trail.lightType = LIGHT::NONE;
						trail.playerHandling = HANDLING::PARTICLE;
						trail.bulletHandling = HANDLING::IGNOREBULLET;
						trail.isBlastable = false;
					}

				break;
				
				case STATE::EXPLODE:
					jjDrawResizedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 5, obj.curFrame + 1, obj.eventID == OBJECT::RFBULLETPU? 2.5:2, obj.eventID == OBJECT::RFBULLETPU? 2.5:2, SPRITE::NORMAL);
				
					if (obj.var[2] == 0) {
						jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BENZIN1);
						Shockwave temp;
						jjOBJ@ blast = jjObjects[jjAddObject(OBJECT::BULLET, obj.xPos, obj.yPos, obj.creatorID, CREATOR::PLAYER, jjVOIDFUNCOBJ(temp.onBehave))];
						obj.var[2] = 1;
						blast.var[2] = 1;
						blast.var[8] = obj.var[8];
						blast.animSpeed = obj.animSpeed;
						blast.eventID = OBJECT::BULLET;
					}
				break;
			}
			
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj) && obj.var[8] & 1 << play.playerID == 0) {
				ship.xSpeed += recoilX(ship,obj,30);
				ship.ySpeed += recoilY(ship,obj,30);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::RFBULLETPU? jjObjectPresets[OBJECT::RFBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::RFBULLET].animSpeed * 1.5);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				obj.var[8] = obj.var[8] | 1 << play.playerID;
				jjSample(play.xPos, play.yPos, SOUND::COMMON_EXPL_TNT, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
			}
			return true;
		}
	}

	class Shockwave : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			
			obj.playerHandling = HANDLING::PARTICLE;
			obj.bulletHandling = HANDLING::IGNOREBULLET;
			
			if (obj.var[2] == 1) {
				obj.lightType = obj.var[2] == 1? LIGHT::RING2 : LIGHT::NONE;
				obj.var[1] = obj.var[1] + 1;
				obj.light += 2;
				obj.var[4] = obj.light * 4;
				obj.var[5] = obj.animSpeed - ((obj.var[4] * obj.animSpeed)/216); 
				
				if (obj.var[4] >= 200) obj.var[6] = 20;
				else if (obj.var[4] >= 100 && obj.var[6] < 200) obj.var[6] = 30;
				else if (obj.var[4] < 100) obj.var[6] = 40;
				
				for (int i = 1; i < jjObjectCount; i++) {
					jjOBJ@ target = jjObjects[i];
					float dx = target.xPos - obj.xPos, dy = target.yPos - obj.yPos;
					if ((target.eventID == OBJECT::TNT || target.behavior == BEHAVIOR::MONITOR || target.behavior == BEHAVIOR::AMMO15 || target.eventID == OBJECT::GUNCRATE || target.eventID == OBJECT::GEMCRATE || target.eventID == OBJECT::CARROTCRATE || target.playerHandling == HANDLING::ENEMY || target.eventID == OBJECT::DESTRUCTSCENERY) && target.var[4] == 0) {
						if (dx * dx + dy * dy < obj.var[4] * obj.var[4]) {
							if (target.eventID != OBJECT::TNT) {
								obj.objectHit(target, target.playerHandling == HANDLING::ENEMY? HANDLING::ENEMY : HANDLING::SPECIAL);
								if (target.playerHandling != HANDLING::ENEMY) target.state = STATE::KILL;
							} else {
								target.state = STATE::EXPLODE;
								target.counter = 18;
							}
							if (target.playerHandling != HANDLING::ENEMY) target.var[4] = 1;
						}
						else target.var[4] = 0;
					}
				}
				
				
				if (gameIsActive()) {
					for (int i = 0; i < 32; i++) {
						jjPLAYER@ play = jjPlayers[i];
						SPACESHIP@ ship = Ships[play.playerID];
						if (canBeHit(play,creator,obj) && obj.var[4] > 0) {
							float dx = play.xPos - obj.xPos, dy = play.yPos - obj.yPos;
							if (dx * dx + dy * dy < obj.var[4] * obj.var[4]) {
								if (obj.var[8] & 1 << i == 0 && play.blink == 0 && obj.var[5] > 0) {
									obj.var[8] = obj.var[8] | 1 << i;
									jjSample(play.xPos, play.yPos, SOUND::ROBOT_HYDROPUF, 63, 12500 + jjRandom()%2500);
									if ((ship.health - ship.damageCalc(ship, obj.var[5])) > 0) {
										if (play.isLocal) {
											ship.health -= ship.damageCalc(ship, obj.var[5]);
											if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.var[5]) * 2;
										}
										randomHurtSample(play, 0);
										
										ship.damageAccumulated += obj.var[5];
										ship.accumulationTime = 0;
										if (!play.isLocal) damageNumber(creator, play, ship.damageCalc(ship, obj.var[5]));
										
										int angle = int(atan2(play.yPos - obj.yPos, play.xPos - obj.xPos) * (512 / PI));
										obj.ySpeed = jjSin(angle) * 32;
										obj.xSpeed = jjCos(angle) * 32;
			
										ship.xSpeed += recoilX(ship,obj,obj.var[6]);
										ship.ySpeed += recoilY(ship,obj,obj.var[6]);

									} else {
										if (play.isLocal) {
											play.hurt(7, true, creator);
										}
										if (creator.isLocal) {
											ship.SDPunishment = true;
										}
									}
									if (!ship.justHit) {
										ship.justHit = true;
										ship.hitDelay = 0;
									}
								}
							}
						}
					}
				}
			}
			
			if (obj.var[1] == 27) {
				obj.var[1] = 0;
				obj.var[2] = 0;
				obj.var[4] = 0;
				obj.delete();
			}
		}
	}


	class Flamethrower : jjBEHAVIORINTERFACE {
		float xSpeed, ySpeed, xPos, yPos;
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::TOASTERBULLET, false);
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			
			if (obj.state == STATE::FLY) {
				obj.var[0] = int(creator.xSpeed);
				if (obj.counter <= 3) {
					xSpeed = obj.xSpeed;
					ySpeed = obj.ySpeed;
				}
				if (obj.counter <= 7) {
					obj.xSpeed = xSpeed * 2;
					obj.ySpeed = ySpeed * 2;
				}
				
				obj.counter++;
				
				if (obj.counter > 1 && inView(obj)) {
					jjDrawSprite(obj.xPos, obj.yPos, ANIM::AMMO, 13, jjGameTicks >> 2, obj.direction, obj.eventID == OBJECT::TOASTERBULLETPU? SPRITE::SINGLEHUE : SPRITE::NORMAL, 32);
				}
				
				if (obj.counter >= 200) obj.delete();
			}
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
		}
		
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj) && obj.state != STATE::EXPLODE) {
				ship.xSpeed += recoilX(ship,obj,8);
				ship.ySpeed += recoilY(ship,obj,8);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::TOASTERBULLETPU? jjObjectPresets[OBJECT::TOASTERBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::TOASTERBULLET].animSpeed * 1.5);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
				jjSample(play.xPos, play.yPos, SOUND::BILSBOSS_FIRE, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
			}
			return true;
		}
	}

	class SpikeMine : jjBEHAVIORINTERFACE {
		bool isPowerup = false;

		void onBehave(jjOBJ@ obj) {
			obj.behave(obj.state == STATE::EXPLODE && obj.counter >= 18 && inView(obj)? BEHAVIOR::BULLET : BEHAVIOR::TNT, false);
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[creator.playerID];
			
			obj.animSpeed = creator is jjTokenOwner? (isPowerup? 210:105) : (isPowerup? 140:70);
			
			if (obj.state != STATE::EXPLODE) {
				obj.xPos = obj.xPos + obj.xSpeed;
				obj.yPos = obj.yPos + obj.ySpeed;
				
				jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, isPowerup? SPRITE::SINGLEHUE : SPRITE::NORMAL, 40);

				if (obj.counter == 1) {
					if (ship.tntPowerup) isPowerup = true;
					else isPowerup = false;
					
					if (creator.isLocal) jjSample(obj.xPos, obj.yPos, SOUND::ROBOT_SHOOT, 0, isPowerup? 17500 : 0);
					obj.xSpeed = creator.xSpeed / -4;
					obj.ySpeed = (creator.ySpeed < 0.2 && creator.ySpeed > -0.2)? 0 : creator.ySpeed / -4;
				}
				if (obj.counter >= 175) {
					if (obj.counter % 20 > 9) jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::TRANSLUCENTSINGLEHUE, 24);
					obj.var[0] = jjSampleLooped(obj.xPos, obj.yPos, SOUND::COMMON_CLOCK, obj.var[0], 0, 0);
				}
				
				if (jjMaskedPixel(int(obj.xPos), int(obj.yPos))) {
					obj.counter = 18;
					obj.state = STATE::EXPLODE;
				}
			} else {
				if (obj.counter == 18) {
					jjDrawResizedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 5, obj.curFrame, 2, 2, SPRITE::NORMAL);
					
					obj.behavior = BEHAVIOR::BULLET;
					
					if (obj.var[2] == 0) {
						jjSample(obj.xPos, obj.yPos, SOUND::COMMON_BENZIN1);
						Shockwave temp;
						jjOBJ@ blast = jjObjects[jjAddObject(OBJECT::BULLET, obj.xPos, obj.yPos, obj.creatorID, CREATOR::PLAYER, jjVOIDFUNCOBJ(temp.onBehave))];
						obj.var[2] = 1;
						blast.var[2] = 1;
						blast.var[8] = obj.var[8];
						blast.animSpeed = obj.animSpeed;
						blast.eventID = OBJECT::BULLET;
					}
				} else {
					jjDrawResizedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, (1 + (obj.counter/4)), (1 + (obj.counter/4)), isPowerup? SPRITE::SINGLEHUE : SPRITE::NORMAL, 40);
				}
			}
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
		}
		
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj) && obj.var[8] & 1 << play.playerID == 0) {
				ship.xSpeed += recoilX(ship,obj,20);
				ship.ySpeed += recoilY(ship,obj,20);
				randomHurtSample(play, 0);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				obj.var[8] = obj.var[8] | 1 << play.playerID;
				obj.counter = 18;
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
				jjSample(play.xPos, play.yPos, SOUND::COMMON_EXPL_TNT, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
			}
			return true;
		}
	}

	class Laser : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			obj.behave(BEHAVIOR::BULLET, obj.state == STATE::EXPLODE && inView(obj)? true:false);
			obj.var[0] = int(atan2(-obj.ySpeed, obj.xSpeed) * (512.f * 0.318309886142228f));
			if (obj.state == STATE::FLY) {
				if (obj.counter == 1 && jjPlayers[obj.creatorID].isLocal) jjSample(obj.xPos, obj.yPos, SOUND::AMMO_LASER, 0, obj.eventID == OBJECT::BLASTERBULLETPU? 12500:0);
				if (obj.ySpeed < -15) obj.ySpeed = -15;
				if (obj.ySpeed > 15) obj.ySpeed = 15;
			}
			
			if (obj.counter > 1 && inView(obj)) jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.var[0], 1, 1, obj.eventID == OBJECT::BLASTERBULLETPU? SPRITE::SINGLEHUE : SPRITE::NORMAL, 48);
			
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
		}
		
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj) && obj.var[8] & 1 << play.playerID == 0) {
				ship.xSpeed += recoilX(ship,obj,40);
				ship.ySpeed += recoilY(ship,obj,40);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::FIREBALLBULLETPU? jjObjectPresets[OBJECT::FIREBALLBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::FIREBALLBULLET].animSpeed * 1.5);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				obj.var[8] = obj.var[8] | 1 << play.playerID;
				jjSample(play.xPos, play.yPos, SOUND::ROBOT_BIG2, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
			}
			return true;
		}
	}

	class ElectroBlaster : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			obj.behave(BEHAVIOR::ELECTROBULLET, obj.counter > 1 && inView(obj)? true:false);
			
			obj.xPos += jjCos(obj.counter*36)*(obj.ySpeed/3);
			obj.yPos += jjSin(obj.counter*36)*(obj.xSpeed/3);
			
			if (obj.findNearestPlayer(500) > -1 && obj.findNearestPlayer(500) != creator.playerID) handleShipCollisions(obj, creator);
			
			if ((creator.charCurr == CHAR::JAZZ || creator.charCurr == CHAR::LORI) && obj.counter == 1 && obj.state != STATE::EXPLODE) {
				obj.xPos = creator.xPos;
				obj.yPos = creator.yPos;
			}
		}
		
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			jjPLAYER@ creator = jjPlayers[obj.creatorID];
			SPACESHIP@ ship = Ships[play.playerID];
			
			if (canBeHit(play,creator,obj) && obj.state != STATE::EXPLODE) {
				ship.xSpeed += recoilX(ship,obj,15);
				ship.ySpeed += recoilY(ship,obj,15);
				randomHurtSample(play, 0);
				
				if (creator is jjTokenOwner) obj.animSpeed = int(obj.eventID == OBJECT::ELECTROBULLETPU? jjObjectPresets[OBJECT::ELECTROBULLETPU].animSpeed * 1.5 : jjObjectPresets[OBJECT::ELECTROBULLET].animSpeed * 1.5);
				
				if ((ship.health - ship.damageCalc(ship, obj.animSpeed)) > 0) {
					if (play.isLocal) {
						ship.health -= ship.damageCalc(ship, obj.animSpeed);
						if (ship.armor > 0) ship.armor -= ship.damageCalc(ship, obj.animSpeed) * 2;
					}
				} else {
					if (play.isLocal) {
						play.hurt(7, true, creator);
					}
				}
				if (!ship.justHit) {
					ship.justHit = true;
					ship.hitDelay = 0;
				}
				jjSample(play.xPos, play.yPos, SOUND::COMMON_ELECTRICHIT, 63, 12500 + jjRandom()%2500);
				if (ship.armor > 0) jjSample(play.xPos, play.yPos, SOUND::COMMON_LAND1, 63, 12500 + jjRandom()%2500);
				obj.state = STATE::EXPLODE;
				obj.scriptedCollisions = false;
			}
			return true;
		}
	}

	bool inView(const jjOBJ@ obj) {
		for (int i = 0; i < jjLocalPlayerCount; i++) {
			const jjPLAYER@ play = jjLocalPlayers[i];
			if (obj.xPos > play.cameraX - 64 && obj.yPos > play.cameraY - 64 && obj.xPos < play.cameraX + jjSubscreenWidth + 64 && obj.yPos < play.cameraY + jjSubscreenHeight + 64)
				return true;
		}
		return false;
	}

	bool shipInView(jjPLAYER@ play) {
		for (int i = 0; i < jjLocalPlayerCount; i++) {
			const jjPLAYER@ player = jjLocalPlayers[i];
			if (play.xPos > player.cameraX - 64 && play.yPos > player.cameraY - 64 && play.xPos < player.cameraX + jjSubscreenWidth + 64 && play.yPos < player.cameraY + jjSubscreenHeight + 64)
				return true;
		}
		return false;
	}

	void damageNumber(jjPLAYER@ creator, jjPLAYER@ play, int num) {
		if (creator.isLocal) {
			jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
			if (text !is null) {
				text.xPos = play.xPos;
				text.yPos = play.yPos;
				text.ySpeed = -0.25;
				text.string.text = "" + num;
			}
			jjSample(creator.xPos, creator.yPos, SOUND::MENUSOUNDS_TYPE, 63, 28500 - (num * 50));
			crosshairFlash = true;
			crosshairTime = 0;
		}
	}

	class AmmoPickup : jjBEHAVIORINTERFACE {
		float getY(const ::jjOBJ@ obj) const {
			int arg = (((obj.objectID << 3) + ::jjGameTicks) << (obj.yPos > ::jjWaterLevel ? 1 : 4)) + (int(obj.xPos) << 4);
			return obj.yPos + ::jjSin(arg) * 4.f;
		}
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::PICKUP, obj.eventID == OBJECT::TNTAMMO3? false:true);
			if (obj.eventID == OBJECT::TNTAMMO3) {
				jjDrawSprite(obj.xPos, getY(obj), ANIM::ROBOT, 0, 0, 0, SPRITE::SINGLEHUE, (jjLocalPlayers[0].powerup[WEAPON::TNT]? 40:72));
			}
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			if (play.ammo[obj.var[4]] < jjWeapons[obj.var[4]].maximum) {
				if (play.ammo[obj.var[4]] + obj.var[5] > jjWeapons[obj.var[4]].maximum) {
					play.ammo[obj.var[4]] = jjWeapons[obj.var[4]].maximum;
				} else {
					if (jjAutoWeaponChange && play.ammo[obj.var[4]] == 0) play.currWeapon = obj.var[4];
					play.ammo[obj.var[4]] = play.ammo[obj.var[4]] + obj.var[5];
				}
				if (play.isLocal) jjSample(play.xPos, play.yPos, SOUND::COMMON_PICKUPW1);
				obj.behavior = BEHAVIOR::EXPLOSION2;
				obj.scriptedCollisions = false;
				obj.frameID = 0;
			}
			return true;
		}
	}

	class HealthPickup : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::PICKUP);
			obj.direction = 1; 
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			SPACESHIP@ ship = Ships[play.playerID];
			
			int amount = ship.health > (maxHealth - int(maxHealth/5))? maxHealth - ship.health : int(maxHealth/5);
			
			if (ship.health < maxHealth && play.blink == 0) {
				sendShipRotationPacket(ship);
				ship.health += amount;
				
				if (play.isLocal) {
					jjSample(play.xPos, play.yPos, SOUND::COMMON_MONITOR);
					jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
					if (text !is null) {
						text.xPos = play.xPos;
						text.yPos = play.yPos;
						text.ySpeed = -0.25;
						text.string.text = "|||||+" + amount;
					}	
				}
				play.invincibility = 70;
				obj.behavior = BEHAVIOR::EXPLOSION2;
				obj.scriptedCollisions = false;
				obj.frameID = 0;
			}
			return true;
		}
	}


	class MegaHealthPickup : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::PICKUP);
			obj.direction = 1;
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			SPACESHIP@ ship = Ships[play.playerID];
			
			int amount = ship.health > maxHealth? (maxHealth*2) - ship.health : maxHealth;
			
			if (ship.health < (maxHealth*2) && play.blink == 0) {
				sendShipRotationPacket(ship);
				ship.health += amount;

				if (play.isLocal) {
					jjSample(play.xPos, play.yPos, SOUND::COMMON_ITEMTRE);
					jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
					if (text !is null) {
						text.xPos = play.xPos;
						text.yPos = play.yPos;
						text.ySpeed = -0.25;
						text.string.text = "|||||+" + amount;
					}
					playHappySample(play);
				}
				play.invincibility = 210;
				obj.behavior = BEHAVIOR::EXPLOSION2;
				obj.scriptedCollisions = false;
				obj.frameID = 0;
			}
			return true;
		}
	}

	class ShieldPickup : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::PICKUP);
			obj.direction = 1; 
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			SPACESHIP@ ship = Ships[play.playerID];
			
			int amount = (ship.armor > (maxHealth / 2))? maxHealth - ship.armor : int(maxHealth/2);
			
			if (ship.armor < maxHealth) {
				sendShipRotationPacket(ship);
				ship.armor += amount;
				
				if (play.isLocal) {
					jjSample(play.xPos, play.yPos, SOUND::INTRO_SHOTGRN);
					jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
					if (text !is null) {
						text.xPos = play.xPos;
						text.yPos = play.yPos;
						text.ySpeed = -0.25;
						text.string.text = "|+" + amount;
					}
				}
				obj.behavior = BEHAVIOR::EXPLOSION2;
				obj.scriptedCollisions = false;
				obj.frameID = 0;
			}
			return true;
		}
	}

	class MegaShieldPickup : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::PICKUP);
			obj.direction = 1; 
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			SPACESHIP@ ship = Ships[play.playerID];
			
			int amount = ship.armor > maxHealth? (maxHealth*2) - ship.armor : maxHealth;
			
			if (ship.armor < (maxHealth * 2)) {
				sendShipRotationPacket(ship);
				ship.armor += amount;
				
				if (play.isLocal) {
					jjSample(play.xPos, play.yPos, SOUND::INTRO_SHOTGRN, 0, 18000);
					jjPARTICLE@ text = jjAddParticle(PARTICLE::STRING);
					if (text !is null) {
						text.xPos = play.xPos;
						text.yPos = play.yPos;
						text.ySpeed = -0.25;
						text.string.text = "|+" + amount;
					}
					playHappySample(play);
				}
				obj.behavior = BEHAVIOR::EXPLOSION2;
				obj.scriptedCollisions = false;
				obj.frameID = 0;
			}
			return true;
		}
	}

	class PowerOrb : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
			obj.behave(BEHAVIOR::PICKUP);
			obj.direction = 1; 
		}
		bool onObjectHit(jjOBJ@ obj, jjOBJ@ bull, jjPLAYER@ play, int force) {
			SPACESHIP@ ship = Ships[play.playerID];
			
			sendShipRotationPacket(ship);
			ship.powerUp = true;
			ship.powerTime = 30*70;
			if (play.isLocal) {
				jjSample(play.xPos, play.yPos, SOUND::BILSBOSS_FIRESTART, 0, 0);
				playWarCry(play);
			}
			obj.behavior = BEHAVIOR::EXPLOSION2;
			obj.scriptedCollisions = false;
			obj.frameID = 0;
			
			return true;
		}
	}

	class ShipExplosion : jjBEHAVIORINTERFACE {
		void onBehave(jjOBJ@ obj) {
		
			obj.frameID++;
			if (obj.frameID == 2 && jjGameTicks > 70) {
				obj.particlePixelExplosion(1);
				jjSample(obj.xPos, obj.yPos, SOUND::INTRO_BOEM2);
			}
			if (obj.frameID == 84) obj.delete();
			
			jjDrawResizedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 5, obj.frameID / 7, 2, 2, SPRITE::NORMAL);
		}
	}

	void playHappySample(jjPLAYER@ play) {
		switch (play.charCurr) {
			case CHAR::JAZZ: jjSample(play.xPos, play.yPos, SOUND::JAZZSOUNDS_JUMMY, 0, 0); break;
			case CHAR::SPAZ: jjSample(play.xPos, play.yPos, SOUND::SPAZSOUNDS_HAPPY, 0, 0); break;
			case CHAR::LORI: jjSample(play.xPos, play.yPos, SOUND::LORISOUNDS_WEHOO, 0, 0); break;
			default: break;
		}
	}

	void playWarCry(jjPLAYER@ play) {
		switch (play.charCurr) {
			case CHAR::JAZZ: jjSample(play.xPos, play.yPos, SOUND::JAZZSOUNDS_IDLE, 0, 0); break;
			case CHAR::SPAZ: jjSample(play.xPos, play.yPos, SOUND::SPAZSOUNDS_HAHAHA, 0, 0); break;
			case CHAR::LORI: jjSample(play.xPos, play.yPos, SOUND::LORISOUNDS_LORIBOOM, 0, 0); break;
			default: break;
		}
	}


	void handleOnPlayer(jjPLAYER@ play) {
		SPACESHIP@ ship = Ships[play.playerID];
		
		if (jjKey[0x71]) {
			if (!showInstructions) {
				if (!pressOnce[0]) {
					showInstructions = true;
					pressOnce[0] = true;
				}
			} else {
				if (!pressOnce[0]) {
					showInstructions = false;
					pressOnce[0] = true;
				}
			}
		} else {
			pressOnce[0] = false;
		}
		if (showInstructions) {
			if (jjKey[0xBC]) {
				if (!pressOnce[1]) {
					if (currPage == 1) {
						currPage = 3;
					} else {
						currPage--;
					}
					pressOnce[1] = true;
				}
			} else {
				pressOnce[1] = false;
			}
			
			if (jjKey[0xBE]) {
				if (!pressOnce[2]) {
					if (currPage == 3) {
						currPage = 1;
					} else {
						currPage++;
					}
					pressOnce[2] = true;
				}
			} else {
				pressOnce[2] = false;
			}
		}
		
		if (ship.hasShip) {
			play.spriteMode = SPRITE::INVISIBLE;
			play.ballTime = 1;
			play.direction = 1;
			play.fly = FLIGHT::NONE;
			play.jumpStrength = 0;
			jjCharacters[play.charCurr].airJump = AIR::NONE;
			
			if (play.blink != 0) {
				ship.health = ship.SDPunishment? 1:maxHealth;
			} else ship.SDPunishment = false;
			
			if (play.health == 0) { 
				ship.health = ship.SDPunishment? 1:maxHealth;
				ship.armor = 0;
			}
			
			if (ship.health > maxHealth && jjGameTicks % (ship.health > int(maxHealth*1.5) && ship.health < (maxHealth*2)? 28:70) == 0 && play.blink == 0 && jjGameState != GAME::STOPPED) {
				ship.health--;
			}
			
			if (ship.armor < 0) ship.armor = 0;
			if (ship.armor > maxHealth && jjGameTicks % (ship.armor > int(maxHealth*1.5) && ship.armor < (maxHealth*2)? 28:70) == 0 && play.blink == 0 && jjGameState != GAME::STOPPED) {
				ship.armor--;
			}
			
			if (jjMaxHealth == 1) {
				ship.health = 1;
				ship.armor = 0;
			}
			
			if (ship.health > (maxHealth*2)) {
				ship.health = (maxHealth*2);
			}
			
			if (ship.armor > (maxHealth*2)) {
				ship.armor = (maxHealth*2);
			}
			
			switch (play.currWeapon) {
				case WEAPON::BLASTER: 
					if (currentWeapon == 1) play.fastfire = jjMaxHealth == 1? 45:14;
				break;
				case WEAPON::BOUNCER:
					if (currentWeapon == 2) play.fastfire = 6;
				break;
				case WEAPON::ICE:
					if (currentWeapon == 3) play.fastfire = 55;
				break;
				case WEAPON::SEEKER: 
					if (currentWeapon == 4) play.fastfire = 73;
				break;
				case WEAPON::RF: 
					if (currentWeapon == 5) play.fastfire = 110;
				break;
				case WEAPON::TOASTER:
					if (currentWeapon == 6) play.fastfire = 4;
				break;
				case WEAPON::TNT:
					if (currentWeapon == 7) play.fastfire = 70;
				break;
				case WEAPON::GUN8:
					if (currentWeapon == 8) play.fastfire = 32;
				break;
				case WEAPON::GUN9:
					if (currentWeapon == 9) play.fastfire = 12;
				break;
				default: break;
			}
			
			while (play.ammo[currentWeapon] == 0 && play.currWeapon != WEAPON::BLASTER) {
				currentWeapon--;
				play.currWeapon = currentWeapon;
			}
			
			if (ship.powerUp) {
				if (jjGameState != GAME::STOPPED) ship.powerTime--;
				
				for (int i = 1; i <= 9; i++) {
					play.powerup[i] = true;
				}
			} else {
				for (int i = 1; i <= 9; i++) {
					play.powerup[i] = false;
				}
			}
			
			if (ship.powerTime <= 0 && ship.powerUp) {
				ship.powerTime = 0;
				ship.powerUp = false;
				if (play.isLocal) jjSample(play.xPos, play.yPos, SOUND::COMMON_SHLDOF3, 0, 0);
			}
			
			play.xAcc = play.yAcc = 0;
			
			if (jjMaxHealth == 1) {
				for (int i = 2; i <= 9; i++) {
					play.ammo[i] = 0;
				}
			}
				
			if (play.health > 0) {
				play.xSpeed = ship.xSpeed;
				play.ySpeed = ship.ySpeed;
				if (ship.ySpeed < 0.05 && ship.ySpeed > -0.05) {
					ship.ySpeed = 0;
				}
				if (ship.ySpeed == 0) {
					play.ySpeed = 0; 
					play.yPos -= 0.125f;
				}
				if (ship.xSpeed < 0.05 && ship.xSpeed > -0.05) {
					ship.xSpeed = 0;
				}
				if (ship.xSpeed == 0) {
					play.xSpeed = 0;
					if (play.keyRight && !jjMaskedPixel(int(play.xPos-16), int(play.yPos))) play.xPos -= (play.keyRun? 0.368f : 0.183f);
					if (play.keyLeft && !jjMaskedPixel(int(play.xPos+16), int(play.yPos))) play.xPos += (play.keyRun? 0.368f : 0.183f);
				}
			}
			
			if (jjGameTicks % 70 < 69) {
				if (ship.rotationSpeed != 0) {
					ship.rotating = true;
				}
			}
			
			if (jjGameTicks % 70 == 69 && (ship.rotating || play.health == 0 || ship.wasHit)) {
				sendShipRotationPacket(ship);
				ship.rotating = false;
				ship.wasHit = false;
			}
			
				
			if (play.ySpeed > (play.keyRun? 8:6)) play.ySpeed = (play.keyRun? 8:6);
			
			if (play.ySpeed < -6 && !play.keyRun) play.ySpeed = -6;
				
			if (!ship.reverse) {
				if (ship.xAcc < 0) ship.xAcc = 0;
				if (ship.yAcc < 0) ship.yAcc = 0;
			}
			
			if (ship.thrust && !ship.reverse) {
				if (!ship.justHit) {
					ship.xSpeed = (jjCos(ship.angle) * ship.xAcc) + ship.xMomentum;
					ship.ySpeed = (-jjSin(ship.angle) * ship.yAcc) + ship.yMomentum;
				}
					
				if (ship.xMomentum > 0) ship.xMomentum -= 0.05;
				if (ship.xMomentum < 0) ship.xMomentum += 0.05;
				
				if (ship.yMomentum > 0) ship.yMomentum -= 0.05;
				if (ship.yMomentum < 0) ship.yMomentum += 0.05;
				
				if (ship.xAcc < (play.keyRun? ship.maxSpeed:ship.maxTurningSpeed)) ship.xAcc += 0.2;
				if (ship.yAcc < (play.keyRun? ship.maxSpeed:ship.maxTurningSpeed)) ship.yAcc += 0.2;
					
				if (ship.rotationSpeed != 0) {
					if (ship.xAcc > (play.keyRun? ship.maxTurningSpeed:4)) ship.xAcc -= 0.25;
					if (ship.yAcc > (play.keyRun? ship.maxTurningSpeed:4)) ship.yAcc -= 0.25;
				}
			} else {
				if (ship.reverse && !ship.thrust) {
					if (!ship.justHit) {
						ship.xSpeed = (jjCos(ship.angle) * ship.xAcc) + ship.xMomentum;
						ship.ySpeed = (-jjSin(ship.angle) * ship.yAcc) + ship.yMomentum;
					}
					
					if (ship.xMomentum > 0) ship.xMomentum -= 0.1;
					if (ship.xMomentum < 0) ship.xMomentum += 0.1;
					
					if (ship.yMomentum > 0) ship.yMomentum -= 0.1;
					if (ship.yMomentum < 0) ship.yMomentum += 0.1;
					
					if (ship.xAcc > (play.keyRun? -ship.maxSpeedRev:-ship.maxTurningSpeedRev)) ship.xAcc -= 0.085;
					if (ship.yAcc > (play.keyRun? -ship.maxSpeedRev:-ship.maxTurningSpeedRev)) ship.yAcc -= 0.085;
						
					if (ship.rotationSpeed != 0) {
						if (ship.xAcc < (play.keyRun? -ship.maxTurningSpeedRev:-3)) ship.xAcc += 0.25;
						if (ship.yAcc < (play.keyRun? -ship.maxTurningSpeedRev:-3)) ship.yAcc += 0.25;
					}
				} else {
					ship.xMomentum = play.xSpeed * 0.75;
					ship.yMomentum = play.ySpeed * 0.75;
					
					if (jjMaskedHLine(int(play.xPos + ship.xSpeed), -32, int(play.yPos)) || jjMaskedHLine(int(play.xPos - ship.xSpeed), 32, int(play.yPos))) {
						ship.xSpeed = ship.xAcc = ship.xMomentum = 0;
					}
					
					if (jjMaskedVLine(int(play.xPos), int(play.yPos + ship.ySpeed), -32) || jjMaskedVLine(int(play.xPos), int(play.yPos - ship.ySpeed), 32)) {
						ship.ySpeed = ship.yAcc = ship.yMomentum = 0;
					}
					
					if (ship.xMomentum > ship.maxSpeed) ship.xMomentum = ship.maxSpeed;
					if (ship.xMomentum < -ship.maxSpeed) ship.xMomentum = -ship.maxSpeed;
					
					if (ship.yMomentum > ship.maxSpeed) ship.yMomentum = ship.maxSpeed;
					if (ship.yMomentum < -ship.maxSpeed) ship.yMomentum = -ship.maxSpeed;
					
					if (ship.xSpeed > 0) ship.xSpeed -= 0.05;
					else if (ship.xSpeed < 0) ship.xSpeed += 0.05;
						 
					if (ship.ySpeed > 0) ship.xSpeed -= 0.05;
					else if (ship.ySpeed < 0) ship.ySpeed += 0.05;
					
					if (ship.xAcc > (ship.xSpeed * (ship.xSpeed > 0? -1:1))) {
						ship.xAcc -= 0.25;
					}
					if (ship.yAcc > (ship.ySpeed * (ship.ySpeed > 0? -1:1))) {
						ship.yAcc -= 0.25;
					}
						
					if (ship.ySpeed > 0 && ship.ySpeed < ship.maxSpeed && ship.yAcc <= 0 ) {
						ship.ySpeed -= 0.025;
					}
					if (play.keyDown) {
						ship.xAcc = ship.yAcc = 0;
					}
				}
			}
			play.buttstomp = 100;
			
			if (play.xSpeed > ship.maxSpeed) play.xSpeed = ship.maxSpeed;
			if (play.xSpeed < -ship.maxSpeed) play.xSpeed = -ship.maxSpeed;
			if (play.ySpeed > ship.maxSpeed) play.ySpeed = ship.maxSpeed;
			if (play.ySpeed < -ship.maxSpeed) play.ySpeed = -ship.maxSpeed;
			
			if (play.xSpeed > ship.maxDiagonalSpeed && play.ySpeed > ship.maxDiagonalSpeed) {
				play.xSpeed = ship.maxDiagonalSpeed;
				play.ySpeed = ship.maxDiagonalSpeed;
			}
		
			if (play.xSpeed > ship.maxDiagonalSpeed && play.ySpeed < -ship.maxDiagonalSpeed) {
				play.xSpeed = ship.maxDiagonalSpeed;
				play.ySpeed = -ship.maxDiagonalSpeed;
			}
			
			if (play.xSpeed < -ship.maxDiagonalSpeed && play.ySpeed > ship.maxDiagonalSpeed) {
				play.xSpeed = -ship.maxDiagonalSpeed;
				play.ySpeed = ship.maxDiagonalSpeed;
			}
			
			if (play.xSpeed < -ship.maxDiagonalSpeed && play.ySpeed < -ship.maxDiagonalSpeed) {
				play.xSpeed = -ship.maxDiagonalSpeed;
				play.ySpeed = -ship.maxDiagonalSpeed;
			}
			
			if (jjMaskedPixel(int(play.xPos) + int(ship.xSpeed), int(play.yPos) + int(ship.ySpeed)) && !ship.thrust) {
				ship.xAcc = ship.yAcc = ship.xMomentum = ship.yMomentum = 0;
			}
			
			if (jjMouseAim && jjAllowsMouseAim) {
				int screenLimitX = (jjLayerWidth[4]*32)-400;
				int screenLimitY = (jjLayerHeight[4]*32)-400;
				int screenCentreX = (int(play.xPos) <= 400)? int(play.xPos) : int(play.xPos) >= screenLimitX? int(jjSubscreenWidth / 2) + int(abs(screenLimitX - int(play.xPos))) : int(jjSubscreenWidth / 2);
				int screenCentreY = (int(play.yPos) <= 400)? int(play.yPos) : int(play.yPos) >= screenLimitY? int(jjSubscreenHeight / 2) + int(abs(screenLimitY - int(play.yPos))) : int(jjSubscreenHeight / 2);
				
				ship.fireAngle = int(atan2(jjMouseY - screenCentreY, -(jjMouseX - screenCentreX)) * (512 / PI)) - 512;

			} else {
				ship.fireAngle = ship.angle;
			}
			
			if (play.keyFire && !ship.fire) {
				play.fireBullet(WEAPON::CURRENT, true, true, ((ship.fireAngle+256+(play.currWeapon == WEAPON::TOASTER? 512:0))/512) * -PI);
				ship.fire = true;
				currentWeapon = play.currWeapon;
			}
			
			if (play.keyFire && play.currWeapon == WEAPON::TOASTER && play.ammo[WEAPON::TOASTER] > 0) {
				ship.sample2 = jjSampleLooped(play.xPos, play.yPos, SOUND::COMMON_FLAMER, ship.sample2);
			}
			
			if (ship.fire) {
				ship.fireDelay++;
				if (ship.fireDelay >= play.fastfire) {
					ship.fire = false;
					ship.fireDelay = 0;
					ship.stopWeaponChange = false;
				}
				if (play.currWeapon != currentWeapon) {
					play.currWeapon = currentWeapon;
					ship.stopWeaponChange = true;
				}
			} else {
				ship.stopWeaponChange = false;
				if (jjGameTicks % 18 == 0) {
					if (play.ammo[WEAPON::BLASTER] < jjWeapons[WEAPON::BLASTER].maximum) {
						play.ammo[WEAPON::BLASTER] = play.ammo[WEAPON::BLASTER] + 1;
					}
				}
			}
			
			if (play.ammo[WEAPON::BLASTER] < 1 && play.currWeapon != WEAPON::BLASTER) {
				play.ammo[WEAPON::BLASTER] = 1;
			}
			
			if (jjKey[0x01] && jjKey[0x12]) {
				radarX = int(jjMouseX);
				radarY = int(jjMouseY);
			}
			
			if (jjKey[0x02] && jjKey[0x12]) {
				radarX = 100;
				radarY = jjSubscreenHeight - 220;
			}
			
			if (crosshairFlash) {
				crosshairTime++;
				if (crosshairTime > 21) crosshairFlash = false;
			}
			
		} else {
			play.spriteMode = SPRITE::PLAYER;
			jjCharacters[play.charCurr].airJump = AIR::DOUBLEJUMP;
			play.jumpStrength = -10;
		}
		active = play.isInGame;
		
		if (jjMaxHealth == 1) {
			play.currWeapon = currentWeapon = 1;
		}
	}

	void spawnFireTrail(jjPLAYER@ play, SPACESHIP@ ship) {
		jjOBJ@ trail = jjObjects[jjAddObject(OBJECT::EXPLOSION, int(play.xPos - jjCos(ship.angle)), int(play.yPos - jjSin(ship.angle)))];
		trail.determineCurAnim(ANIM::AMMO, !ship.reverse? 3:2);
		trail.lightType = ship.reverse? LIGHT::NONE : LIGHT::POINT;
		trail.playerHandling = HANDLING::PARTICLE;
		trail.bulletHandling = HANDLING::IGNOREBULLET; 
		trail.isBlastable = false;
	}

	void damagedShipTrail(jjPLAYER@ play) {
		jjPARTICLE@ cinders = jjAddParticle(PARTICLE::SMOKE);
		cinders.xPos = (play.xPos - 16) + jjRandom()%32;
		cinders.yPos = (play.yPos - 16) + jjRandom()%32;
		cinders.ySpeed = -1;
	}

	void randomHurtSample(jjPLAYER@ play, int volume) {
		SOUND::Sample sample = SOUND::JAZZSOUNDS_HEY2;
		switch (jjRandom()%4) {
			case 0: sample = play.charCurr == CHAR::JAZZ? SOUND::JAZZSOUNDS_HEY2 : play.charCurr == CHAR::SPAZ? SOUND::SPAZSOUNDS_AUTSCH1 : SOUND::LORISOUNDS_HURT0; break;
			case 1: sample = play.charCurr == CHAR::JAZZ? SOUND::JAZZSOUNDS_HEY3 : play.charCurr == CHAR::SPAZ? SOUND::SPAZSOUNDS_AUTSCH2 : SOUND::LORISOUNDS_HURT1; break;
			case 2: sample = play.charCurr == CHAR::JAZZ? SOUND::JAZZSOUNDS_HEY4 : play.charCurr == CHAR::SPAZ? SOUND::SPAZSOUNDS_AUTSCH1 : SOUND::LORISOUNDS_HURT2; break;
			case 3: sample = play.charCurr == CHAR::JAZZ? SOUND::JAZZSOUNDS_JAZZV4 : play.charCurr == CHAR::SPAZ? SOUND::SPAZSOUNDS_OOOH : SOUND::LORISOUNDS_HURT7; break;
		}
		jjSample(play.xPos, play.yPos, sample, volume, 0);
	}

	void handleOnMain() {
		for (int i = 1; i <= 9; i++) {
			if (jjWeapons[i].allowed)
				jjWeapons[i].allowedPowerup = true;
		}
		
		switch (jjMaxHealth) {
			case 1: maxHealth = 1; break;
			case 2: maxHealth = 50; break;
			case 3: maxHealth = jjGameMode == GAME::CTF? 100:70; break;
			case 4: maxHealth = jjGameMode == GAME::CTF? 125:85; break;
			case 5: maxHealth = jjGameMode == GAME::CTF? 150:100; break;
			case 6: maxHealth = jjGameMode == GAME::CTF? 175:150; break;
			case 7: maxHealth = 200; break;
		}
		if (!jjLocalPlayers[0].isIdle) {
			for (int i = 0; i < 32; i++) {
				jjPLAYER@ play = jjPlayers[i];
				if (!play.isInGame)
					continue;
				SPACESHIP@ ship = Ships[i];
				
				if (ship.justHit) {
					ship.hitDelay++;
					if (ship.hitDelay >= 7) {
						ship.justHit = false;
						ship.wasHit = true;
					}
				}
				if (ship.hasShip) {
				
					if (play.powerup[WEAPON::TNT]) {
						ship.tntPowerup = true;
					} else {
						if (!ship.powerUp) {
							ship.tntPowerup = false;
						}
					}
				
					if (ship.damageAccumulated > 0) {
					
						ship.accumulationTime++;
						if (ship.accumulationTime == 210) {
							ship.damageAccumulated = ship.accumulationTime = 0;
						}
					}
					
					play.lightType = ship.health > maxHealth? LIGHT::LASER : (ship.armor > maxHealth? LIGHT::BRIGHT : LIGHT::PLAYER);
					play.light = 12;
					
					if (ship.powerUp) {
						jjDrawRotatedSprite(play.xPos, play.yPos, customAnimID, ship.anim, 0, ship.angle - 256, 1.25, 1.25, ship.justHit? SPRITE::SINGLECOLOR : SPRITE::TRANSLUCENTCOLOR, ship.justHit? 15 : 24 + (jjGameTicks % 7), 4, 4);
					}
					
					if (ship.thrust || ship.reverse) {
						if (!ship.reverse) {
							if (jjGameTicks % (play.keyRun? 4:8) == 0) spawnFireTrail(play, ship);
						} else {
							if (jjGameTicks % (play.keyRun? 8:12) == 0) spawnFireTrail(play, ship);
						}
						if (play.isLocal) ship.sample = jjSampleLooped(play.xPos, play.yPos, SOUND::FAN_FAN, ship.sample, 24, (play.keyRun? 8000:7500));
					} 
					
					if (jjGameTicks % 3 == 0 && ship.health <= int(maxHealth*0.3)) damagedShipTrail(play);
					
					ship.thrust = play.keyUp;
					ship.reverse = play.keyDown;

					ship.angle = ship.angle + ship.rotationSpeed;
					
					if (ship.angle >= 1024) ship.angle = 0;
					else if (ship.angle <= -1024) ship.angle = 0;
					

					if (play.keyRight) {
						ship.rotationSpeed = (ship.preciseTurn || play.keyJump? 0 : int(ship.rotationAcc)) + (play.keyRun && jjGameTicks % 2 == 0? -4 : -3);
						if (ship.rotationAcc > -12) ship.rotationAcc -= 0.5;
						if (ship.rotationAcc > 0) ship.rotationAcc = 0;
					} else if (play.keyLeft) {
						ship.rotationSpeed = (ship.preciseTurn || play.keyJump? 0 : int(ship.rotationAcc)) + (play.keyRun && jjGameTicks % 2 == 0? 4 : 3);
						if (ship.rotationAcc < 12) ship.rotationAcc += 0.5;
						if (ship.rotationAcc < 0) ship.rotationAcc = 0;
					}
					else {
						ship.rotationSpeed = 0;
						ship.rotationAcc = 0;
					}
					
					switch (play.charCurr) {
						case CHAR::JAZZ: ship.anim = 0; break;
						case CHAR::SPAZ: ship.anim = 1; break;
						case CHAR::LORI: ship.anim = 2; break;
						default: ship.anim = 0; break;
					}
				
					if (play.health > 0 && play.isInGame) {
						if (jjGameTicks % 7 == 0 || play.blink == 0) {
							jjDrawRotatedSprite(play.xPos, play.yPos, customAnimID, ship.anim, 0, ship.angle - 256, 1, 1, ship.justHit? SPRITE::SINGLECOLOR : SPRITE::PLAYER, ship.justHit? 15 : play.playerID, 4, 4);
							if (ship.armor > 0) {
								jjDrawSprite(play.xPos, play.yPos, ANIM::MENU, 1, (ship.armor <= int(maxHealth*0.3)? 0 : ship.armor >= int(maxHealth*0.7)? 8 : 3), 0, SPRITE::ALPHAMAP, ship.armor > maxHealth? 34 : ship.armor >= int(maxHealth*0.7)? 80 : ship.armor > int(maxHealth*0.3)? 41 : 24);
							}
						}
					} else if (play.health == 0 && play.isInGame) {
						if (play.blink <= 0) {
							play.blink = 210;
							jjOBJ@ dummy = jjObjects[jjAddObject(OBJECT::PURPLEGEM, play.xPos, play.yPos)];
						}
					}
				}
			}
		}
				
		if (jjGameMode <= GAME::COOP)
			return;
		jjOBJ@ bogus = jjObjects[0];
		bogus.creator = CREATOR::PLAYER;
		for (uint playerID = 0; playerID < 32; ++playerID, ++bogus.creator) {
			const jjPLAYER@ p = jjPlayers[playerID];
			if (p.isInGame) {
				bogus.xPos = p.xPos;
				bogus.yPos = p.yPos;
				closestPlayers[playerID] = MLLEWeapons::HelpfulBulletFunctions::GetNearestEnemyPlayer(bogus, 350);
			}
		}
	}

	void handleOnPlayerInput(jjPLAYER@ play) {
		SPACESHIP@ ship = Ships[play.playerID];

		if (play.keyJump) {
			ship.preciseTurn = true;
			play.keyJump = false;
		} else {
			ship.preciseTurn = false;
		}
	}

	void handleOnReceive(jjSTREAM &in packet, int clientID) {
		if (jjIsServer) {
			int angle, health, armor;
			bool hasShip, powerUp, preciseTurn;
			packet.pop(angle);
			packet.pop(health);
			packet.pop(armor);
			packet.pop(hasShip);
			packet.pop(powerUp);
			packet.pop(preciseTurn);

			jjPLAYER@ play = jjPlayers[clientID];
			
			Ships[play.playerID].angle = angle;
			Ships[play.playerID].health = health;
			Ships[play.playerID].armor = armor;
			Ships[play.playerID].hasShip = hasShip;
			Ships[play.playerID].powerUp = powerUp;
			Ships[play.playerID].preciseTurn = preciseTurn;
			
			updateShipAnglesForClients(clientID, angle, health, armor, hasShip, powerUp, preciseTurn);
		} else {
			int id, angle, health, armor;
			bool hasShip, powerUp, preciseTurn;
			packet.pop(id);
			packet.pop(angle);
			packet.pop(health);
			packet.pop(armor);
			packet.pop(hasShip);
			packet.pop(powerUp);
			packet.pop(preciseTurn);
			
			jjPLAYER@ play = jjPlayers[id];
			
			if (!play.isLocal) {
				Ships[id].angle = angle;
				Ships[id].health = health;
				Ships[id].armor = armor;
				Ships[id].hasShip = hasShip;
				Ships[id].powerUp = powerUp;
				Ships[id].preciseTurn = preciseTurn;
			}
		}
	}

	void sendShipRotationPacket(SPACESHIP@ ship) {
		if (!jjIsServer) {
			jjSTREAM packet;
			packet.push(ship.angle);
			packet.push(ship.health);
			packet.push(ship.armor);
			packet.push(ship.hasShip);
			packet.push(ship.powerUp);
			packet.push(ship.preciseTurn);
			jjSendPacket(packet);
		} else {
			updateShipAnglesForClients(0, ship.angle, ship.health, ship.armor, ship.hasShip, ship.powerUp, ship.preciseTurn);
		}
	}

	void updateShipAnglesForClients(int id, int angle, int health, int armor, bool hasShip, bool powerUp, bool preciseTurn) {
		jjSTREAM packet;
		packet.push(id);
		packet.push(angle);
		packet.push(health);
		packet.push(armor);
		packet.push(hasShip);
		packet.push(powerUp);
		packet.push(preciseTurn);
		jjSendPacket(packet);
	}

	bool handleOnDrawAmmo(jjPLAYER@ play, jjCANVAS@ canvas) {
		SPACESHIP@ ship = Ships[play.playerID];
		int box_X = 64, box_Y = 64;
		int boxWidth = jjSubscreenWidth - (box_X * 2);
		int boxHeight = jjSubscreenHeight - (box_Y * 2);
		
		array<string> controls = {
			"||||Press |||||LEFT||| or |||||RIGHT||| to steer your ship and aim your weapon",
			"",
			"||||Press and hold |||||UP||| to activate your ship's thrusters",
			"",
			"||||Press and hold |||||DOWN||| to go into reverse",
			"",
			"||||Press and hold |||||RUN||| to move faster",
			"",
			"||||Press |||||FIRE||| to shoot",
			"",
			"||||Press and hold |||||JUMP||| while turning for more precise control",
			"",
			"||||Click and drag |||||LEFT MOUSE||| while holding |||||ALT||| to reposition",
			"||||the ship's radar HUD",
			"",
			"||||Click |||||RIGHT MOUSE||| while holding |||||ALT||| to reset the ",
			"||||ship's radar HUD position to default"
		};
		
		array<string> weaponNames = {
			"|1. Blaster",
			"|2. Rapid Blaster",
			"|3. Burster",
			"|4. Homing Missile",
			"|5. Super Missile",
			"|6. Flamethrower",
			"|7. Spike Mine",
			"|8. Laser Cannon",
			"|9. Electro Blaster"
		};
		
		array<string> weaponDamages = {
			"Damage: 15",
			"Damage: 9",
			"Damage: 25-75",
			"Damage: 30",
			"Damage: 100 (1-99 for explosion)",
			"Damage: 6",
			"Damage: 70 (1-69 for explosion)",
			"Damage: 40",
			"Damage: 15"
		};
		
		array<string> weaponMaxAmmo = {
			"Max Ammo: 25 (regenerates)",
			"Max Ammo: 100 (+5 per pickup)",
			"Max Ammo: 25 (+2 per pickup)",
			"Max Ammo: 15 (+2 per pickup)",
			"Max Ammo: 10 (+1 per pickup)",
			"Max Ammo: 50 (+3 per pickup)",
			"Max Ammo: 10 (+1 per pickup)",
			"Max Ammo: 25 (+2 per pickup)",
			"Max Ammo: 50 (+3 per pickup)"
		};
		
		array<string> itemNames = {
			"|||||Health Pack",
			"|||||Mega Health Pack",
			"|Shield",
			"|Mega Shield",
			"||Power Orb"
		};
		array<string> itemDescriptions = {
			"Restores " + (maxHealth / 5) + " HP, but not beyond your maximum.",
			"Restores " + maxHealth + " HP, up to twice your normal maximum HP.",
			"Adds " + maxHealth / 2 + " Shield Points (SP), but not beyond your maximum.",
			"Adds " + maxHealth + " SP, up to twice your normal maximum SP.",
			"Temporarily powers up all of your weapons for 30 seconds (x2)."
		};
		array<int> itemSprites = {
			6,7,3,4,5
		};
			canvas.drawSprite(
				radarX,
				radarY,
				customAnimID,
				8,
				0,
				0,
				SPRITE::TRANSLUCENT
			);
			
			canvas.drawString(
				radarX - 26,
				radarY + 68,
				"" + int(play.xPos)/32 + "," + int(play.yPos)/32,
				STRING::SMALL,
				STRING::PALSHIFT,
				16
			);
			
			for (int i = 0; i < 32; i++) {
				jjPLAYER@ player = jjPlayers[i];
				
				uint8 teamColor;
				uint8 radarColor;
				switch (player.team) {
					case TEAM::BLUE: teamColor = 34; break;
					case TEAM::RED: teamColor = 24; break;
					case TEAM::GREEN: teamColor = 18; break;
					case TEAM::YELLOW: teamColor = 40; break;
					default: teamColor = 24; break;
				}
				
				switch (jjGameMode) {
					case GAME::CTF: radarColor = teamColor; break;
					case GAME::BATTLE: radarColor = jjGameCustom == GAME::PEST? (player.isZombie? (play.isZombie? 73 : 24) : jjDoZombiesAlreadyExist? (play.isZombie? 24:73):24) : jjGameCustom == GAME::RT? (player is jjTokenOwner? 18 : player is jjBottomFeeder? 24 : 73) : 24; break;
					default: radarColor = 24; break;
				}
				
				int radarOffsetX = int(play.xPos - player.xPos) / 30;
				int radarOffsetY = int(play.yPos - player.yPos) / 30;
				
				float dx = player.xPos - play.xPos, dy = player.yPos - play.yPos;
		
				if (dx * dx + dy * dy < 1800 * 1800) {
					if (radarOffsetX < 70 && radarOffsetX > -63 && radarOffsetY < 70 && radarOffsetY > -63 && player.xPos > 0 && player.yPos > 0 && !play.isSpectating && !play.isOut && !play.isConnecting) {
						if (player.flag != 0) {
							canvas.drawResizedSprite(
								((radarX + 2) - radarOffsetX),
								((radarY + 2) - radarOffsetY),
								ANIM::FLAG,
								3,
								jjGameTicks >> 2,
								radarOffsetX > 0? 0.4:-0.4,
								0.4,
								SPRITE::SINGLECOLOR,
								player.team == TEAM::BLUE? 24 : 34
							);
							canvas.drawRectangle(
								((radarX - 4) - radarOffsetX),
								((radarY - 4) - radarOffsetY),
								player.isLocal? 8:6,
								player.isLocal? 8:6,
								player.isLocal? 80 : radarColor,
								SPRITE::NORMAL
							);
						}
						else if (player.flag == 0) {
							canvas.drawRectangle(
								((radarX - 4) - radarOffsetX),
								((radarY - 4) - radarOffsetY),
								player.isLocal? 8:5,
								player.isLocal? 8:5,
								player.isLocal? 80 : radarColor,
								SPRITE::NORMAL
							);
						}
					}
				}
			}
		if (showInstructions) {
			canvas.drawRectangle(
				box_X,
				box_Y,
				boxWidth,
				boxHeight,
				95,
				SPRITE::TRANSLUCENT
			);
			canvas.drawString(
				box_X + 12,
				box_Y + 32,
				"|||Spaceships: A Guide",
				STRING::LARGE,
				STRING::NORMAL
			);
			if (currPage == 1) {
				canvas.drawString(
					box_X + 12,
					box_Y + 96,
					"Script written by PurpleJazz",
					STRING::MEDIUM,
					STRING::PALSHIFT,
					24
				);
				canvas.drawString(
					box_X + 12,
					box_Y + 128,
					"Controls",
					STRING::MEDIUM,
					STRING::NORMAL
				);
				for (uint i = 0; i < controls.length(); i++) {
					canvas.drawString(
						box_X + 12,
						box_Y + 160 + (16*i),
						controls[i],
						STRING::SMALL,
						STRING::NORMAL
					);
				}
			}
			if (currPage == 2) {
				canvas.drawString(
					box_X + 12,
					box_Y + 96,
					"Weapons",
					STRING::MEDIUM,
					STRING::NORMAL
				);
				for (uint i = 0; i < 5; i++) {
					canvas.drawString(

[preview ends here]