Downloads containing ab20btl07.j2as

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Anniversary Bash 20 Levels Jazz2Online Multiple N/A Download file

File preview

#include "MLLE-Include-1.4.asc"
const bool MLLESetupSuccessful = MLLE::Setup();
#pragma require "ab20btl07-MLLE-Data-2.j2l"
#pragma require "ab20btl07-MLLE-Data-1.j2l"
#pragma require "ab20btl07.j2l"
#pragma require "SEenergyblast.asc"
#include "SEenergyblast.asc"
#include "weaponMega.asc"
#pragma require "weaponMega.asc"
#pragma require "SEfirework.asc"
#include "SEfirework.asc"
#pragma require "TornadoGun.j2a"
#pragma require "weaponMega.j2a"
void onLevelLoad() { 
resetSpread();
ReplaceEggmanAnimation() ;
	EnergyBlastEdit energyBlast;
	energyBlast.loadAnims(jjAnimSets[ANIM::CUSTOM[0]]);
	energyBlast.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BLOW});
	energyBlast.setAsWeapon(4, weaponHook);
	se::firework.loadAnims(jjAnimSets[ANIM::CUSTOM[1]]);
	se::firework.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BOEM1, SOUND::INTRO_BOEM2});
	se::firework.setAsWeapon(5, weaponHook);
    jjObjectPresets[OBJECT::BRIDGE].behavior = Bridge;
	jjObjectPresets[OBJECT::CHIPS].scriptedCollisions = true;
	jjObjectPresets[OBJECT::CHIPS].behavior = SpreadShot();
		jjWeapons[WEAPON::GUN8].spread = SPREAD::NORMAL;
		jjObjectPresets[OBJECT::APPLE].behavior = Eggman();
		for (int i = 1; i < 255; i++) {
		if (jjObjectPresets[i].behavior == BEHAVIOR::PICKUP) {
			jjObjectPresets[i].behavior = CannotBeShotDown;
		}
	}
	jjObjectPresets[OBJECT::RFAMMO3].behavior = CannotBeShotDown;
	jjObjectPresets[OBJECT::SEEKERAMMO3].behavior = CannotBeShotDown;
	jjAnimSets[ANIM::CUSTOM[6]].load(0, "TornadoGun.j2a");
	jjAnimations[jjAnimSets[ANIM::AMMO] + 57] = jjAnimations[jjAnimSets[ANIM::CUSTOM[6]] + 3];
	jjAnimations[jjAnimSets[ANIM::AMMO] + 56] = jjAnimations[jjAnimSets[ANIM::CUSTOM[6]] + 2];
	
	jjObjectPresets[OBJECT::TOASTERBULLET].behavior = TornadoGun();
	jjObjectPresets[OBJECT::TOASTERBULLET].special = jjObjectPresets[OBJECT::TOASTERBULLET].determineCurAnim(ANIM::CUSTOM[6], 0);
	jjObjectPresets[OBJECT::TOASTERBULLET].xSpeed = 4;
	jjObjectPresets[OBJECT::TOASTERBULLET].counterEnd = 110;
	jjObjectPresets[OBJECT::TOASTERBULLET].var[6] = 0;
	jjObjectPresets[OBJECT::TOASTERBULLET].freeze = 0;
	jjObjectPresets[OBJECT::TOASTERBULLET].eventID = OBJECT::FIREBALLBULLET;
	jjObjectPresets[OBJECT::TOASTERBULLET].lightType = jjObjectPresets[OBJECT::FIREBALLBULLET].lightType;
	jjObjectPresets[OBJECT::TOASTERBULLET].killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
	jjObjectPresets[OBJECT::TOASTERBULLET].lightType = LIGHT::POINT;
	
	jjObjectPresets[OBJECT::TOASTERBULLETPU].behavior = TornadoGunPU();
	jjObjectPresets[OBJECT::TOASTERBULLETPU].special = jjObjectPresets[OBJECT::TOASTERBULLETPU].determineCurAnim(ANIM::CUSTOM[6], 1);
	jjObjectPresets[OBJECT::TOASTERBULLETPU].xSpeed = 6;
	jjObjectPresets[OBJECT::TOASTERBULLETPU].counterEnd = 120;
	jjObjectPresets[OBJECT::TOASTERBULLETPU].var[6] = 8;
	jjObjectPresets[OBJECT::TOASTERBULLETPU].freeze = 0;
	jjObjectPresets[OBJECT::TOASTERBULLETPU].eventID = OBJECT::FIREBALLBULLET;
	jjObjectPresets[OBJECT::TOASTERBULLETPU].killAnim = jjObjectPresets[OBJECT::ICEBULLET].killAnim;
	jjObjectPresets[OBJECT::TOASTERBULLETPU].lightType = LIGHT::POINT2;
	
	jjObjectPresets[OBJECT::TOASTERPOWERUP].determineCurAnim(ANIM::CUSTOM[6], 4);
	jjObjectPresets[OBJECT::TOASTERPOWERUP].determineCurFrame();
	
	jjObjectPresets[OBJECT::TOASTERAMMO15].determineCurAnim(ANIM::CUSTOM[6], 5);
	jjObjectPresets[OBJECT::TOASTERAMMO15].determineCurFrame();

	jjWeapons[WEAPON::TOASTER].defaultSample = false;
	jjWeapons[WEAPON::TOASTER].replacedByBubbles = false;
	//jjWeapons[WEAPON::TOASTER].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::TOASTER].style = WEAPON::NORMAL;
	jjWeapons[WEAPON::TOASTER].multiplier = 1;
	
}

se::DefaultWeaponHook weaponHook;
bool gameIsActive() {
	return jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME;
}
class EnergyBlastEdit : se::EnergyBlastWeapon {
	protected void prepareWeaponProfile(::jjWEAPON@ weapon) const override {
		weapon.comesFromBirds = false;
		weapon.comesFromBirdsPowerup = false;
		weapon.defaultSample = false;
		weapon.gradualAim = false;
		weapon.multiplier = 6; //was originally 3
		weapon.replacedByBubbles = false;
		weapon.spread = SPREAD::NORMAL;
		weapon.style = WEAPON::POPCORN;
	}
}
void CannotBeShotDown(jjOBJ@ obj) {
	obj.behave(BEHAVIOR::PICKUP);
	if (obj.state == STATE::FLOATFALL) obj.state = STATE::FLOAT;
}

bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
	return weaponHook.drawAmmo(player, canvas);
}
uint nextBullet = 200;
int timer = 0;

void onLevelBegin() {
    if (!jjIsServer) jjSendPacket(jjSTREAM());
}

void onReceive(jjSTREAM &in packet, int clientID) {
weaponHook.processPacket(packet, clientID);
    if (jjIsServer) {
        updateTime(clientID);
    } else {
        int currTime;
        packet.pop(currTime);
        timer = currTime;
    }
}

void updateTime(int clientID) {
    jjSTREAM packet;
    packet.push(timer);
    jjSendPacket(packet, clientID);
}


void onMain() {
	weaponHook.processMain();
	
}
class SpreadShot : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::PICKUP, false);
		obj.light = 12;
		obj.lightType = LIGHT::BRIGHT;
			obj.xPos=obj.xOrg+0*jjSin(-jjGameTicks*5+int(obj.xOrg)*32/3);
			obj.yPos=obj.yOrg+0*jjCos(-jjGameTicks*5+int(obj.xOrg)*32/3);	}
	void onDraw(jjOBJ@ obj) {
		jjDrawRotatedSprite(obj.xPos, obj.yPos + 10, ANIM::AMMO, 37, jjGameTicks >> 2, angle, 1, 1, SPRITE::NORMAL, 0, 1);
		jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 37, jjGameTicks >> 2, angle, 1, 1, SPRITE::NORMAL, 0, 1);
		jjDrawRotatedSprite(obj.xPos, obj.yPos - 10, ANIM::AMMO, 37, jjGameTicks >> 2, angle, 1, 1, SPRITE::NORMAL, 0, 1);
	}
	bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ p, int force) {
		obj.bulletHandling = HANDLING::IGNOREBULLET;
		obj.behavior = BEHAVIOR::EXPLOSION2;
		obj.scriptedCollisions = false;
		obj.frameID = 0;
		if (p.isLocal) spreadShotCountdown = 15;
		jjSamplePriority(SOUND::COMMON_PICKUP1);
		return true;
	}
}
uint spreadShotCountdown = 0;
uint16 angle = 0;
void resetSpread() {
	jjWeapons[WEAPON::BLASTER].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::BOUNCER].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::ICE].spread = SPREAD::ICE;
	jjWeapons[WEAPON::SEEKER].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::RF].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::TNT].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::TOASTER].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::GUN8].spread = SPREAD::NORMAL;
	jjWeapons[WEAPON::GUN9].spread = SPREAD::NORMAL;
}
void onPlayer(jjPLAYER@ p) {
	weaponHook.processPlayer(p);
	if (spreadShotCountdown > 0 && jjGameTicks % 70 == 0) {
		spreadShotCountdown--;
		if (spreadShotCountdown == 0) {
			resetSpread();
		}
	}
	else if (spreadShotCountdown == 15) {
	/*	for (uint i = 1; i <= 4; i++) {
	
			jjWeapons[i].spread = SPREAD::RFPU;
		}  
		for (uint i = 7; i <= 9; i++) {
	
			jjWeapons[i].spread = SPREAD::RFPU;
		}  
		jjWeapons[6].spread = SPREAD::RFPU;
		*/
		jjWeapons[1].spread = SPREAD::RFPU;
		jjWeapons[2].spread = SPREAD::RFPU;
		jjWeapons[3].spread = SPREAD::RFPU;
		jjWeapons[6].spread = SPREAD::RFPU;
		jjWeapons[8].spread = SPREAD::RFPU;
		jjWeapons[9].spread = SPREAD::RFPU;
	}
	if (spreadShotCountdown > 0) {
		jjTEXTAPPEARANCE normal(STRING::NORMAL);
		
		int x = int(p.cameraX);
		int y = int(p.cameraY);
		
		jjDrawSprite(x + jjResolutionWidth - 72, y + jjResolutionHeight - 270, ANIM::PICKUPS, 87, jjGameTicks >> 2, 0, SPRITE::NORMAL, 0, 1);
		jjDrawString(x + jjResolutionWidth - 55, y + jjResolutionHeight - 274, "" + spreadShotCountdown, STRING::MEDIUM, normal, 0, SPRITE::PALSHIFT, 0, 1);
	}
	if (p.health == 0) {
		spreadShotCountdown == 0;
		resetSpread();
	}
	angle += 12;
}
void onPlayerInput(jjPLAYER@ player) {
	weaponHook.processPlayerInput(player);
}
/*void onReceive(jjSTREAM &in packet, int clientID) {
	weaponHook.processPacket(packet, clientID);
}*/


void Bridge(jjOBJ@ obj) {
    obj.behave(BEHAVIOR::BRIDGE);
    obj.yPos = obj.yOrg - 12;
}



bool LoadWeaponMega3() {
	if (!SafeToReplaceWeapon(WEAPON::ICE))
		return false;
	
	jjWeapons[WEAPON::ICE].style = WEAPON::POPCORN;
	jjWeapons[WEAPON::ICE].spread = SPREAD::TOASTER;
	jjWeapons[WEAPON::ICE].multiplier = 12;
	jjWeapons[WEAPON::ICE].gradualAim = true;
	
	Preset1.behavior = ApplyIceCloud;
	Preset2.behavior = ApplyIceCloud;
	Preset1.special = Preset1.determineCurAnim(AnimSet, 0);
	Preset2.special = Preset2.determineCurAnim(AnimSet, 0);
	Preset1.determineCurFrame(); Preset2.determineCurFrame();
	Preset1.isBlastable = true; Preset2.isBlastable = true;
	//Preset1.var[6] = Preset1.var[6] | 16; //"fireball"
	//Preset2.var[6] = Preset2.var[6] | 16;
	Preset1.counterEnd = 100; //lifetime
	Preset2.counterEnd = 100;
	
	return true;
}
const bool WeaponMega3Loaded = LoadWeaponMega3();

void ApplyIceCloud(jjOBJ@ obj) { obj.behavior = IceCloud(obj); obj.behave(); }
class IceCloud : WeaponMega {
	IceCloud(jjOBJ@ objectOfAttachment) {
		@obj = @objectOfAttachment;
		obj.xSpeed += obj.var[7] / 65536.f;
		if (obj.creatorType == CREATOR::PLAYER)
			obj.ySpeed += int(jjPlayers[obj.creatorID].ySpeed) / 2;
		obj.state = STATE::FLY;
	}
	void onBehave(jjOBJ@) override {
		obj.xPos += obj.xSpeed *= 0.96;
		obj.yPos += obj.ySpeed *= 0.96;
		if (obj.frameID < 3 && ++obj.age & 15 == 15) {
			++obj.frameID; ++obj.curFrame;
		}
		if (--obj.counterEnd == 0 || obj.state == STATE::EXPLODE) {
			int playerID;
			if (  (playerID = getNearestEnemy(400)) >= 0) {
				obj.frameID = 0;
				obj.killAnim = (obj.curAnim += 1) + 1;
				obj.counterEnd = 100;
				obj.freeze = 0;
				const jjPLAYER@ target = jjPlayers[playerID];
				obj.xSpeed = (target.xPos - obj.xPos) / 25;
				obj.ySpeed = (target.yPos - obj.yPos) / 25;
				obj.xAcc = obj.yAcc = obj.var[7] = 0;
				obj.behavior = BEHAVIOR::BULLET;
			} else {
				obj.counter = 0;
				obj.frameID = 0;
				obj.curAnim += 2;
				obj.behavior = BEHAVIOR::EXPLOSION;
			}
			jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ICECRUSH);
		} else if (jjGameTicks & 1 == 0) {
			jjPARTICLE@ part = jjAddParticle(PARTICLE::ICETRAIL);
			if (part !is null) {
				part.yPos = obj.yPos;
				part.xPos = obj.xPos + (jjRandom() & 31) - 15;
			}
		}
	}
	void onDraw(jjOBJ@) override {
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, 1, SPRITE::TRANSLUCENT);
		if (obj.frameID == 3)
			jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, jjAnimations[obj.curAnim + 1], 0, SPRITE::TRANSLUCENTCOLOR, 15);
	}
}



	
	
	bool LoadWeaponMega4() {
	if (!SafeToReplaceWeapon(WEAPON::SEEKER))
		return false;
		
	jjWeapons[WEAPON::GUN9].defaultSample = false;
	jjWeapons[WEAPON::GUN9].replacedByBubbles = true;
	
jjObjectPresets[OBJECT::ELECTROBULLET].behavior = ApplyPathfinder;
	jjObjectPresets[OBJECT::ELECTROBULLETPU].behavior = ApplyPathfinder;
	jjObjectPresets[OBJECT::ELECTROBULLET].special = jjObjectPresets[OBJECT::ELECTROBULLET].determineCurAnim(AnimSet, 0);
	jjObjectPresets[OBJECT::ELECTROBULLETPU].special = jjObjectPresets[OBJECT::ELECTROBULLETPU].determineCurAnim(AnimSet, 1);
	jjObjectPresets[OBJECT::ELECTROBULLET].determineCurFrame(); jjObjectPresets[OBJECT::ELECTROBULLETPU].determineCurFrame();
	jjObjectPresets[OBJECT::ELECTROBULLET].isBlastable = false; jjObjectPresets[OBJECT::ELECTROBULLETPU].isBlastable = false;
	jjObjectPresets[OBJECT::ELECTROBULLET].playerHandling = jjObjectPresets[OBJECT::ELECTROBULLETPU].playerHandling = HANDLING::PLAYERBULLET;
	jjObjectPresets[OBJECT::ELECTROBULLET].counterEnd = 46; jjObjectPresets[OBJECT::ELECTROBULLETPU].counterEnd = 32;
	jjObjectPresets[OBJECT::ELECTROBULLET].var[6] = 16;
	
	return true;
}
const bool WeaponMega4Loaded = LoadWeaponMega4();

void ApplyPathfinder(jjOBJ@ obj) { obj.behavior = Pathfinder(obj); obj.behave(); }
class Pathfinder : WeaponMega {
	array<jjOBJ@> nodes;
	bool waiting = false;
	bool foundAtLeastOneTarget = false;
	uint counter = 0;
	bool powerup;
	Pathfinder(jjOBJ@ objectOfAttachment) {
		@obj = @objectOfAttachment;
		obj.state = STATE::FLY;
		powerup = isPowerup();
		jjSample(obj.xPos, obj.yPos, powerup ? SOUND::AMMO_LASER3 : SOUND::AMMO_LASER2);
	}
	void destroy() {
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ELECTRIC2);
		for (uint i = 0; i < nodes.length; ++i)
			nodes[i].delete();
		obj.delete();
	}
	void onBehave(jjOBJ@) override {
		if (obj.state == STATE::EXPLODE) {
			destroy();
			return;
		}
		obj.frameID = (obj.objectID + jjGameTicks / 3) % 3;
		if (++counter >= obj.counterEnd) {
			if (waiting = !waiting) { //stop moving
				addNode();
				counter = obj.counterEnd / 2;
			} else { //start moving
				if (nodes.length > (uint(powerup ? 6 : 4) / uint(foundAtLeastOneTarget ? 1 : 3)) || obj.state == STATE::DONE) {
					destroy();
					return;
				}
				const int nearestEnemyID = getNearestEnemy(200);
				if (nearestEnemyID >= 0) {
					const jjPLAYER@ target = jjPlayers[nearestEnemyID];
					obj.var[7] = 0;
					const float standardSpeed = (powerup ? 3.f : 2.5f);
					const float dx = target.xPos - obj.xPos, dy = target.yPos - obj.yPos;
					foundAtLeastOneTarget = true;
					if (abs(dx) > abs(dy)) {
						obj.xSpeed = (dx > 0) ? standardSpeed : -standardSpeed;
						obj.ySpeed = 0;
					} else {
						obj.xSpeed = 0;
						obj.ySpeed = (dy > 0) ? standardSpeed : -standardSpeed;
					}
				}
				counter = 0;
			}
		}
		if (!waiting) {
			obj.xPos += obj.xSpeed + obj.var[7] / 65536.f;
			obj.yPos += obj.ySpeed;
			if (obj.var[7] > 0x100000)
				obj.var[7] = obj.var[7] - 0x80000;
			else if (obj.var[7] < -0x100000)
				obj.var[7] = obj.var[7] + 0x80000;
			if (colliding()) {
				counter = obj.counterEnd / 2;
				waiting = true;
				addNode();
				obj.state = STATE::DONE;
			}
		}
	}
	bool colliding() const {
		return maskedPixelAtBullet() && jjEventAtLastMaskedPixel != AREA::ONEWAY && jjEventAtLastMaskedPixel != AREA::HOOK && jjEventAtLastMaskedPixel != AREA::VINE;
	}
	void addNode() {
		jjOBJ@ node = jjObjects[jjAddObject(OBJECT::BEES, obj.xPos, obj.yPos, obj.creatorID, obj.creatorType)];
		node.playerHandling = HANDLING::PLAYERBULLET;
		node.var[6] = 2 | (powerup ? 8 : 0) | 16; //fireball
		node.animSpeed = powerup ? 2 : 1;
		node.curFrame = jjAnimations[obj.curAnim + 2];
		node.state = STATE::FLY;
		node.lightType = LIGHT::BRIGHT;
		node.light = powerup ? 10 : 8;
		nodes.insertLast(node);
		jjSample(obj.xPos, obj.yPos, SOUND::COMMON_ELECTRIC1);
	}
	void onDraw(jjOBJ@) override {
		jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame + obj.frameID);
		for (uint i = 0; i < nodes.length; ++i) {
			const jjOBJ@ node = nodes[i];
			const jjOBJ@ node2 = (i < nodes.length - 1) ? nodes[i + 1] : obj;
			//jjDrawRectangle(node.xPos, node.yPos, int(node2.xPos - node.xPos) + 1, int(node2.yPos - node.yPos) + 1, 15);
			jjDrawSpriteFromCurFrame(node.xPos, node.yPos, node.curFrame + obj.frameID);
		}
	}
}



class TornadoGun : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::BULLET);
		jjPLAYER@ creator = jjPlayers[obj.creatorID];
		if (obj.state == STATE::FLY) {
			if (obj.counter == 1 && creator.isLocal) {
				//jjSample(creator.xPos, creator.yPos, SOUND::INTRO_BLOW, 48, 25000);
				jjSample(creator.xPos, creator.yPos, SOUND::COMMON_BIRDFLY, 48, 25000);
			}
			
			switch (obj.direction) {
				case 1: obj.xSpeed = obj.xSpeed - 0.05; obj.ySpeed = obj.ySpeed - 0.1; break;
				case -1: obj.xSpeed = obj.xSpeed + 0.05; obj.ySpeed = obj.ySpeed - 0.1; break;
			}
			
			float pdx = jjLocalPlayers[0].xPos - obj.xPos, pdy = jjLocalPlayers[0].yPos - obj.yPos;
			if ((pdx * pdx + pdy * pdy < 64 * 64) && !creator.isLocal && (jjLocalPlayers[0].team != creator.team || jjFriendlyFire) && gameIsActive()) {
				jjLocalPlayers[0].ySpeed -= 2;
				jjLocalPlayers[0].xSpeed += 2 * obj.direction;
			}
		}
		if (obj.state == STATE::EXPLODE) {
			if (obj.var[0] == 0) {
				jjSample(obj.xPos, obj.yPos, SOUND::COMMON_SWISH4, 36, 15000);
				obj.var[0] = 1;
			}
		}
	}
}

class TornadoGunPU : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {
		obj.behave(BEHAVIOR::BULLET);
		jjPLAYER@ creator = jjPlayers[obj.creatorID];
		if (obj.state == STATE::FLY) {
			if (obj.counter == 1 && creator.isLocal) {
			//	jjSample(creator.xPos, creator.yPos, SOUND::INTRO_BLOW, 48, 30000);
				jjSample(creator.xPos, creator.yPos, SOUND::COMMON_BIRDFLY, 48, 30000);
			}

			switch (obj.direction) {
				case 1: obj.xSpeed = obj.xSpeed - 0.05; obj.ySpeed = obj.ySpeed - 0.1; break;
				case -1: obj.xSpeed = obj.xSpeed + 0.05; obj.ySpeed = obj.ySpeed - 0.1; break;
			}
			
			float pdx = jjLocalPlayers[0].xPos - obj.xPos, pdy = jjLocalPlayers[0].yPos - obj.yPos;
			if ((pdx * pdx + pdy * pdy < 96 * 96) && !creator.isLocal && (jjLocalPlayers[0].team != creator.team || jjFriendlyFire) && gameIsActive()) {
				jjLocalPlayers[0].ySpeed -= 4;
				jjLocalPlayers[0].xSpeed += 4 * obj.direction;
			}
		}
		if (obj.state == STATE::EXPLODE) {
			if (obj.var[0] == 0) {
				jjSample(obj.xPos, obj.yPos, SOUND::COMMON_SWISH4, 36, 15000);
				obj.var[0] = 1;
			}
		}
	}
}

bool LoadWeaponMega7() {
	if (!SafeToReplaceWeapon(WEAPON::TNT))
		return false;
		
	jjWeapons[WEAPON::TNT].defaultSample = false;
	jjWeapons[WEAPON::TNT].style = WEAPON::CAPPED;
	jjWeapons[WEAPON::TNT].maximum = 18;
	Preset1.behavior = function(obj){obj.behavior = GravityWell(obj); obj.behave();};
	Preset1.special = Preset1.determineCurAnim(AnimSet, 0);
	Preset1.determineCurFrame();
	Preset1.state = STATE::IDLE; //why bother with START, really
	Preset1.var[6] = 16; //fireball-ish
	Preset1.var[6] = 8;
	Preset1.animSpeed = 1; //do damage to objects (as opposed to players)
	Preset1.counter = 0;
	Preset1.xSpeed = 1; //needed to get a direction
	Preset1.playerHandling = HANDLING::PLAYERBULLET;
	Preset1.eventID = OBJECT::BULLET; //to prevent using /tntdamage
	
	
	return true;
}
const bool WeaponMega7Loaded = LoadWeaponMega7();

class GravityWell : WeaponMega {
	GravityWell(jjOBJ@ objectOfAttachment) {
		@obj = @objectOfAttachment;
	}
	void onBehave(jjOBJ@) override {
		obj.frameID = ++obj.counter / 7;
		obj.age = jjSampleLooped(obj.xPos, obj.yPos, SOUND::COMMON_MONITOR, obj.age, 63, 5000);
		
		if (obj.counter > 175) {
			jjSample(obj.xPos, obj.yPos, SOUND::COMMON_SHLDOF3);
			obj.particlePixelExplosion(88);
			obj.delete();
		} else {
			for (uint i = jjObjectCount - 1; i > 0; --i) {
				jjOBJ@ other = jjObjects[i];
				if (other.isActive && other.eventID <= OBJECT::BULLET && other.isBlastable && other.state != STATE::EXPLODE && (other.creatorType != CREATOR::PLAYER || playerIsEnemy(jjPlayers[other.creator]))) {
					float d, dx, dy;
					if (withinRange(other.xPos, other.yPos, d, dx, dy)) {
						const float angle = atan2(dy, dx);
						other.xSpeed += 64 / d * cos(angle);
						other.ySpeed += 64 / d * sin(angle);
					}
				}
			}
			for (uint i = 0; i < 32; ++i) {
				jjPLAYER@ other = jjPlayers[i];
				if (other.isInGame && other.blink == 0 && playerIsEnemy(other)) {
					float d, dx, dy;
					if (withinRange(other.xPos, other.yPos, d, dx, dy)) {
						const float angle = atan2(dy, dx);
						other.xSpeed += 24 / d * cos(angle);
						other.ySpeed += 10 / d * sin(angle);
					}
				}
			}
		}
	}
	void onDraw(jjOBJ@) override {
		if (obj.frameID <= 6) {
			obj.curFrame = jjAnimations[obj.curAnim] + obj.frameID;
			jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame);
		} else {
			while (obj.frameID > 6) obj.frameID -= 3;
			obj.curFrame = jjAnimations[obj.curAnim] + obj.frameID;
			jjDrawRotatedSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, (obj.counter - 7*7) * (obj.xSpeed >= 0 ? -32 : 32));
			obj.lightType = LIGHT::RING2;
			obj.light = 15 - (obj.counter & 7) * 2;
		}
	}
	bool withinRange(float x, float y, float &out d, float &out dx, float &out dy) const {
		dx = obj.xPos - x;
		dy = obj.yPos - y;
		return (d = sqrt(dx * dx + dy * dy)) < (290);
	}
}
class Eggman : jjBEHAVIORINTERFACE {
	void onBehave(jjOBJ@ obj) {	
	float t = (jjGameTicks);
	obj.playerHandling = HANDLING::HURT;
	obj.xPos = obj.xOrg + 12*32*cos(0.25*t/7+40);
	obj.yPos = obj.yOrg + 32*sin(t/7)+32*sin(0.6*t/7+70)+32*sin(0.3*t/7-20);
	
	jjDrawSprite(obj.xPos  , obj.yPos , ANIM::PICKUPS, 1, 1, -3*sin(0.25*t/7+40));
	
	} 
	}
	void ReplaceEggmanAnimation() {
	jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::APPLE].curAnim];
	anim.frameCount = 1; //The Bee enemy's animation has several frames, but the tileset only has material for one. Editing jjANIMATION::frameCount to 1 will ensure that the animation never extends beyond the one frame we're providing.
	jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
	jjPIXELMAP gargoyle(0, 158*32, 2*32, 2*32, 4); //Arguments in order: Leftmost pixel of image in layer; topmost pixel; area width; area height; layer ID (1-indexed). You can check the level for yourself in JCS to see how this matches up with the gargoyle's position in layer 3, although note that JCS uses 1-indexed tile positions, not 0-indexed like AngelScript does.
	gargoyle.save(frame);
	frame.hotSpotX = -frame.width / 2;	//Most hotspot values are negative, and the most common position for a hotspot is in the (negative) center of the frame. That's what this code does.
	frame.hotSpotY = -frame.height / 2;	//Of course, you're welcome to experiment with other hotspot positions for your own specialized needs, but this is fine for a simple Bee enemy.
}