Downloads containing academy_spells.asc

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

File preview

namespace acSpells {

	dictionary fullSpells = {
		{"M", Spell("M", "Magic Arrow", 	SPELL_MAGIC_ARROW,		SPELL_NONE, 			1,	1, 	1, 	0, 	10, 0,		0,	false, 	"Fires an arrow that flies in a straight line, hitting all objects on its way and dealing ||{dmg} ||||damage on all enemies.")},
		{"O", Spell("O", "Blood Lust", 		SPELL_BLOOD_LUST,		SPELL_NONE,				1,	2, 	0, 	20, 10, 0,		0,	false, 	"You gain a sugar rush and max fast fire for ||||{dur} ||seconds.")},
		{"S", Spell("S", "Slow", 			SPELL_SLOW,				SPELL_NONE, 			1,	3,	0, 	10, 10, 1,		4,	false, 	"All enemies in range lose half of their movement speed for ||||{dur} ||seconds.")},
		{"K", Spell("K", "Stone Skin", 		SPELL_STONE_SKIN,		SPELL_DISRUPTING_RAY,	1,	4, 	0, 	20, 15, 0,		0,	false, 	"You take |1 |||||damage less from enemy bullets for ||||||{dur} ||seconds. Counters Disrupting Ray.")},
		{"B", Spell("B", "Bless", 			SPELL_BLESS,			SPELL_WEAKNESS,			1,	5, 	0, 	20, 15, 0,		0,	false, 	"Your bullets deal ||1 ||||damage more on all enemies for ||||||{dur} ||seconds. Counters Weakness.")},
		{"D", Spell("D", "Dispel", 			SPELL_DISPEL,			SPELL_NONE,				1,	6, 	0, 	0, 	15, 1,		6,	true, 	"Removes all effects from all players in range (including you).")},
		{"R", Spell("R", "Disrupting Ray", 	SPELL_DISRUPTING_RAY,	SPELL_STONE_SKIN, 		2,	7, 	0, 	10, 20, 1.25,	5,	false, 	"All enemies in range become vulnerable and take ||2 ||||damage more from bullets for ||||||{dur} ||seconds. Counters Stone Skin.")},
		{"W", Spell("W", "Weakness", 		SPELL_WEAKNESS,			SPELL_BLESS,			2,	8, 	0, 	10, 20, 1.25,	5,	false, 	"All enemies in range become weak and their bullets deal |1 |||||damage less for ||||||{dur} ||seconds. Counters Bless.")},
		{"E", Spell("E", "Ice Bolt", 		SPELL_ICE_BOLT,			SPELL_NONE,				2,	9, 	2, 	0, 	20, 0,		0,	false, 	"Fires a bolt that flies in a straight line, hitting all objects on its way, dealing ||{dmg} ||||damage on all enemies and freezing them.")},
		{"H", Spell("H", "Death Ripple", 	SPELL_DEATH_RIPPLE,		SPELL_NONE,				2,	10, 1, 	0,	25, 1.25,	6,	true, 	"Deals ||{dmg} ||||damage to all living players in a range (undead are unaffected).")},
		{"I", Spell("I", "Wall of Fire",	SPELL_WALL_OF_FIRE,		SPELL_NONE,				2,	11, 2, 	10, 30, 1.25,	0,	false, 	"Places a passable wall of fire that lasts ||||{dur} ||seconds. All players passing through it are dealt ||||{dmg} ||||damage.")}, //Should be Fire wall, but would be harder to distinguish from Fireball
		{"G", Spell("G", "Forgetfulness", 	SPELL_FORGETFULNESS,	SPELL_PRECISION,		3,	12, 0, 	10, 35, 1.5,	7,	false, 	"All enemies in range lose the ability to shoot bullets for ||||{dur} ||seconds. Counters Precision.")},
		{"P", Spell("P", "Precision", 		SPELL_PRECISION,		SPELL_FORGETFULNESS,	3,	13, 0, 	20, 40, 0,		0,	false, 	"Your bullets deal ||2 ||||damage more on all enemies for ||||||{dur} ||seconds. Counters Forgetfulness.")},
		{"T", Spell("T", "Frost Ring",		SPELL_FROST_RING,		SPELL_NONE,				3,  14,	2,	0,	40,	1.5,	7,	false,	"Deals ||{dmg} ||||damage to all enemies in a range and freezes them (caster is unaffected).")},
		{"F", Spell("F", "Fireball", 		SPELL_FIREBALL,			SPELL_NONE,				3, 	15, 3,	0, 	40, 0,		7,	false, 	"Fires a flaming bolt that flies in a straight line, exploding upon the first impact with a player or a solid wall, dealing ||{dmg} ||||damage in a large radius to all players.")},
		{"U", Spell("U", "Cure", 			SPELL_CURE,				SPELL_NONE,				4, 	16, 0, 	0, 	50, 0,		0,	false, 	"You regain full health (for a little pinch) and all negative effects are removed from you.")},
		{"Y", Spell("Y", "Frenzy", 			SPELL_FRENZY,			SPELL_NONE,				4, 	17, 0, 	20, 55, 0,		0,	false, 	"Your bullets deal ||4 ||||damage more on all enemies, but you also take ||||4 ||||damage more from enemy bullets for ||||||{dur} ||seconds.")},
		{"L", Spell("L", "Chain Lightning",	SPELL_CHAIN_LIGHTNING,	SPELL_NONE,				4, 	18, 4, 	0, 	60, 1.75,	7,	false, 	"All enemies in a large range are dealt ||{dmg} ||||damage. Players whom are in range of the primary targets take ||||half ||||the damage (including you).")},
		{"A", Spell("A", "Armageddon",		SPELL_ARMAGEDDON,		SPELL_NONE,				5, 	19, 5, 	0, 	70, 2,		8,	true, 	"All players in a large range are dealt ||{dmg} ||||damage within a short delay (including you).")},
		{"Q", Spell("Q", "Implosion", 		SPELL_IMPLOSION,		SPELL_NONE,				5, 	20, 7, 	0, 	80, 2,		3,	false, 	"All enemies in a short range are dealt ||{dmg} ||||forced damage within a short delay (ignores blinking).")}
	};

	dictionary normalSpells = {
		{"M", Spell("M", "Magic Arrow", 	SPELL_MAGIC_ARROW,		SPELL_NONE, 			1,	1, 	1, 	0, 	10, 0,		0,	false, 	"Fires an arrow that flies in a straight line, hitting all objects on its way and dealing ||{dmg} ||||damage on all enemies.")},
		{"O", Spell("O", "Blood Lust", 		SPELL_BLOOD_LUST,		SPELL_NONE,				1,	2, 	0, 	20, 10, 0,		0,	false, 	"You gain a sugar rush and max fast fire for ||||{dur} ||seconds.")},
		{"S", Spell("S", "Slow", 			SPELL_SLOW,				SPELL_NONE, 			1,	3,	0, 	10, 10, 1,		4,	false, 	"All enemies in range lose half of their movement speed for ||||{dur} ||seconds.")},
		{"D", Spell("D", "Dispel", 			SPELL_DISPEL,			SPELL_NONE,				1,	6, 	0, 	0, 	15, 1,		6,	true, 	"Removes all effects from all players in range (including you).")},
		{"E", Spell("E", "Ice Bolt", 		SPELL_ICE_BOLT,			SPELL_NONE,				2,	9, 	2, 	0, 	20, 0,		0,	false, 	"Fires a bolt that flies in a straight line, hitting all objects on its way, dealing ||{dmg} ||||damage on all enemies and freezing them.")},
		{"H", Spell("H", "Death Ripple", 	SPELL_DEATH_RIPPLE,		SPELL_NONE,				2,	10, 1, 	0,	25, 1.25,	6,	true, 	"Deals ||{dmg} ||||damage to all living players in a range (undead are unaffected).")},
		{"I", Spell("I", "Wall of Fire",	SPELL_WALL_OF_FIRE,		SPELL_NONE,				2,	11, 2, 	10, 30, 1.25,	0,	false, 	"Places a passable wall of fire that lasts ||||{dur} ||seconds. All players passing through it are dealt ||||{dmg} ||||damage.")}, //Should be Fire wall, but would be harder to distinguish from Fireball
		{"G", Spell("G", "Forgetfulness", 	SPELL_FORGETFULNESS,	SPELL_PRECISION,		3,	12, 0, 	10, 35, 1.5,	7,	false, 	"All enemies in range lose the ability to shoot bullets for ||||{dur} ||seconds.")},
		{"T", Spell("T", "Frost Ring",		SPELL_FROST_RING,		SPELL_NONE,				3,  14,	2,	0,	40,	1.5,	7,	false,	"Deals ||{dmg} ||||damage to all enemies in a range and freezes them (caster is unaffected).")},
		{"F", Spell("F", "Fireball", 		SPELL_FIREBALL,			SPELL_NONE,				3, 	15, 3,	0, 	40, 0,		7,	false, 	"Fires a flaming bolt that flies in a straight line, exploding upon the first impact with a player or a solid wall, dealing ||{dmg} ||||damage in a large radius to all players.")},
		{"U", Spell("U", "Cure", 			SPELL_CURE,				SPELL_NONE,				4, 	16, 0, 	0, 	50, 0,		0,	false, 	"You regain full health (for a little pinch) and all negative effects are removed from you.")},
		{"L", Spell("L", "Chain Lightning",	SPELL_CHAIN_LIGHTNING,	SPELL_NONE,				4, 	18, 4, 	0, 	60, 1.75,	7,	false, 	"All enemies in a large range are dealt ||{dmg} ||||damage. Players whom are in range of the primary targets take ||||half ||||the damage (including you).")},
		{"A", Spell("A", "Armageddon",		SPELL_ARMAGEDDON,		SPELL_NONE,				5, 	19, 5, 	0, 	70, 2,		8,	true, 	"All players in a large range are dealt ||{dmg} ||||damage within a short delay (including you).")},
		{"Q", Spell("Q", "Implosion", 		SPELL_IMPLOSION,		SPELL_NONE,				5, 	20, 7, 	0, 	80, 2,		3,	false, 	"All enemies in a short range are dealt ||{dmg} ||||forced damage within a short delay (ignores blinking).")}
	};

	bool canCastSpell(Player@ asPlayer, SPELL spellEnum) {
		string key = getSpellKeyByEnumValue(spellEnum);
		Spell@ spell = cast<Spell@>(spells[key]);
		return canCastSpell(asPlayer, spell);
	}

	bool canCastSpell(Player@ asPlayer, Spell@ spell) {
		if (demoModeOn || (asPlayer.mana >= spell.baseManaCost && asPlayer.cooldown <= 0)) {
			return true;
		}
		return false;
	}
	
	void castSpell(jjPLAYER@ play, string key) {
		Spell@ spell = cast<Spell@>(spells[key]);
		Player@ asPlayer = players[play.playerID];
		asPlayer.unSetChanneledSpellKey();
		numpadBuffer.removeRange(0, numpadBuffer.length);
		acNetworking::sendChannelingStoppedPacket(play.playerID);
		channelingStarted = false;
		recovering = true;
		
		if (!demoModeOn) {
			asPlayer.cooldown = spell.tier * SECOND;
		}
		
		if (doSpell(key)) {
			int index = ownSpells.find(key);
			if (index >= 0 && !demoModeOn) {
				asPlayer.mana -= spell.baseManaCost;
			}
		}
		else {
			if (key == "I") {
				jjAlert("Can't cast wall of fire here. Try on a flat land on a more open area.");
			}
			else {
				jjAlert("That spell affected no one!");
			}
		}
	}
	
	void changePlayerFur(uint8 effectID, int8 playerID) {
		array<uint8> furArray;
		jjPLAYER@ play = jjPlayers[playerID];
		Player@ asPlayer = players[playerID];
		bool change = true;
		switch (effectID) {
			case SPELL_STONE_SKIN:
			{
				furArray = FUR_STONE_SKIN;
			}
			break;
			case SPELL_BLESS:
			{
				furArray = FUR_BLESS;
			}
			break;
			case SPELL_SLOW:
			{
				furArray = FUR_SLOW;
			}
			break;
			case SPELL_DISRUPTING_RAY:
			{
				furArray = FUR_DISRUPTING_RAY;
			}
			break;
			case SPELL_WEAKNESS:
			{
				furArray = FUR_WEAKNESS;
			}
			break;
			case SPELL_PRECISION:
			{
				furArray = FUR_PRECISION;
			}
			break;
			case SPELL_FORGETFULNESS:
			{
				furArray = FUR_FORGETFULNESS;
			}
			break;
			case SPELL_FRENZY:
			{
				furArray = FUR_FRENZY;
			}
			break;
			case SPELL_UNDO:
			{
				if (asPlayer.activeEffects.length >= 1) { //If there are other active effects, change to the latest
					Effect@ effect = asPlayer.activeEffects[asPlayer.activeEffects.length - 1];
					changePlayerFur(effect.enumValue, playerID);
				}
				else {
					play.fur = asPlayer.originalFur;
				}
				change = false;
			}
			break;
			case SPELL_NONE:
			default:
			{
				if (asPlayer.activeEffects.length >= 1) { //If there are any active effects, change to the original
					play.fur = asPlayer.originalFur;
				}
				change = false;
			}
			break;
		}
		if (change) {
			play.furSet(furArray[0], furArray[1], furArray[2], furArray[3]);
		}
	}
	
	void channel(jjPLAYER@ play) {
		string selectedSpellKey = players[play.playerID].selectedSpellKey;
		if (channelingElapsed < cast<Spell@>(spells[selectedSpellKey]).channelingTime * SECOND) {
			channelingElapsed++;
		}
		else {
			channelingElapsed = 0;
			castSpell(play, selectedSpellKey);
		}
	}
	
	void doAOE(jjPLAYER@ caster, uint8 spellID, float xOrigin, float yOrigin) {
		string spellKey = getSpellKeyByEnumValue(spellID);
		Player@ asPlayer = players[caster.playerID];
		Spell@ spell = cast<Spell@>(spells[spellKey]);
		int damage = spell.baseDamage + asPlayer.spellDamageBonus;
		
		switch (spellID) {
			case SPELL_DEATH_RIPPLE:
			{
				jjSample(xOrigin, yOrigin, SOUND::COMMON_HEAD);
				activeAreasOfEffect.insertLast(DeathRipple(
						xOrigin, yOrigin, spell.radius, damage, 35, caster));
			}
			break;
			case SPELL_FROST_RING:
			{
				jjSample(xOrigin, yOrigin, SOUND::COMMON_STEAM);
				activeAreasOfEffect.insertLast(FrostRing(
						xOrigin, yOrigin, spell.radius * 3.2, damage, 70, caster));
			}
			break;
			case SPELL_FIREBALL:
			{
				jjSample(xOrigin, yOrigin, SOUND::COMMON_BENZIN1);
				jjSample(xOrigin, yOrigin, SOUND::DEVILDEVAN_DRAGONFIRE);
				activeAreasOfEffect.insertLast(FireballExplosion(
						xOrigin, yOrigin, spell.radius, damage, 70, caster));
			}
			break;
			case SPELL_ARMAGEDDON:
			{
				jjSample(xOrigin, yOrigin, SOUND::INTRO_BOEM1);
				activeAreasOfEffect.insertLast(Armageddon(
						xOrigin, yOrigin, spell.radius, damage, 70, caster));
			}
			break;
			case SPELL_IMPLOSION:
			{
				activeAreasOfEffect.insertLast(Implosion(
						xOrigin, yOrigin, spell.radius, damage, 70, caster));
			}
			break;
		}
	}
	
	void doBullet(int bulletID) {
		jjPLAYER@ play = jjLocalPlayers[0];
		int originalAmmo = play.ammo[WEAPON::BLASTER];
		play.ammo[WEAPON::BLASTER] = bulletID;
		int id = play.fireBullet(WEAPON::BLASTER);
		jjObjects[id].behave(BEHAVIOR::DEFAULT, false); //behave once to read bullet count
		play.ammo[WEAPON::BLASTER] = originalAmmo;
	}

	array<ChainLightningTarget@> doChainLightning(int8 casterID, int8[] targets) {
		Player@ asCaster = players[casterID];
		Spell@ spell = cast<Spell@>(spells["L"]);
		array<ChainLightningTarget@> chainLightningTargets;
		
		for (uint i = 0; i < targets.length; i++) {
			int8 targetPlayerID = targets[i];
			chainLightningTargets.insertLast(ChainLightningTarget(
					chainLightningCounter, targetPlayerID,
					spell.baseDamage + asCaster.spellDamageBonus - players[targetPlayerID].magicResist,
					CHAIN_LIGHTNING_DEFAULT_DURATION));
			chainLightningCounter++;
		}
		
		for (uint i = 0; i < chainLightningTargets.length; i++) {
			int8 targetPlayerID = chainLightningTargets[i].playerID;
			
			float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
			int8[] newTargets = acUtils::getTargets(jjPlayers[targetPlayerID], spell, scaledRadius);
			
			for (uint j = 0; j < newTargets.length; j++) {
				bool found = acUtils::findFromChainLightningTargets(chainLightningTargets, newTargets[j]);
				
				if (!found) {
					chainLightningTargets.insertLast(ChainLightningTarget(chainLightningCounter, newTargets[j],
							int8((spell.baseDamage + asCaster.spellDamageBonus - players[newTargets[j]].magicResist) / 2),
							CHAIN_LIGHTNING_DEFAULT_DURATION, chainLightningTargets[i].id));
					chainLightningCounter++;
				}
			}
		}
		
		for (uint i = 0; i < chainLightningTargets.length; i++) {
			if (acUtils::gameIsRunning()) {
				jjPlayers[chainLightningTargets[i].playerID].hurt(chainLightningTargets[i].damage, false, jjPlayers[casterID]);
			}
		}
		
		return chainLightningTargets;
	}
	
	void doEffect(uint8 effectID, int8 targetPlayerID, uint duration, bool multiplyDuration = true) {
		jjPLAYER@ play = jjPlayers[targetPlayerID];
		Player@ asPlayer = players[targetPlayerID];
		if (multiplyDuration) duration *= SECOND;
		
		Effect @effect;
		bool affects = true;
		
		switch (effectID) {
			case SPELL_STONE_SKIN:
			{
				Spell@ spell = cast<Spell@>(spells["K"]);
				@effect = StoneSkin(play, "Stone Skin", spell.enumValue, spell.counterSpell, duration);
				jjSample(play.xPos, play.yPos, SOUND::ORANGE_BOEML);
				jjSample(play.xPos, play.yPos, SOUND::ORANGE_BOEMR);
			}
			break;
			case SPELL_BLESS:
			{
				Spell@ spell = cast<Spell@>(spells["B"]);
				@effect = Bless(play, "Bless", spell.enumValue, spell.counterSpell, duration);
				jjSample(play.xPos, play.yPos, SOUND::ORANGE_SWEEP0R);
			}
			break;
			case SPELL_BLOOD_LUST:
			{
				Spell@ spell = cast<Spell@>(spells["O"]);
				@effect = BloodLust(play, "Blood Lust", spell.enumValue, spell.counterSpell, duration, true);
			}
			break;
			case SPELL_SLOW:
			{
				Spell@ spell = cast<Spell@>(spells["S"]);
				@effect = Slow(play, "Slow", spell.enumValue, spell.counterSpell, duration,
								true, true);
				jjSample(play.xPos, play.yPos, SOUND::DEVILDEVAN_VANISH1);
			}
			break;
			case SPELL_CURE:
			{
				if (asPlayer.activeEffects.length == 0) {
					asPlayer.originalFur = play.fur; //This one works for storing the absolute original fur
				}
				asPlayer.removeNegativeEffects();
				changePlayerFur(SPELL_UNDO, targetPlayerID);
				
				affects = false;
				jjSample(play.xPos, play.yPos, SOUND::ORANGE_SWEEP2L);
				jjSample(play.xPos, play.yPos, SOUND::ORANGE_SWEEP2R);
			}
			break;
			case SPELL_DISRUPTING_RAY:
			{
				Spell@ spell = cast<Spell@>(spells["R"]);
				@effect = DisruptingRay(play, "Disrupting Ray", spell.enumValue, spell.counterSpell, duration,
								false, true);
				jjSample(play.xPos, play.yPos, SOUND::COMMON_ITEMTRE);
			}
			break;
			case SPELL_WEAKNESS:
			{
				Spell@ spell = cast<Spell@>(spells["W"]);
				@effect = Weakness(play, "Weakness", spell.enumValue, spell.counterSpell, duration,
								false, true);
				jjSample(play.xPos, play.yPos, SOUND::COMMON_HOLYFLUT);
			}
			break;
			case SPELL_PRECISION:
			{
				Spell@ spell = cast<Spell@>(spells["P"]);
				@effect = Precision(play, "Precision", spell.enumValue, spell.counterSpell, duration);
				jjSample(play.xPos, play.yPos, SOUND::INTRO_RUN);
			}
			break;
			case SPELL_FORGETFULNESS:
			{
				Spell@ spell = cast<Spell@>(spells["G"]);
				@effect = Forgetfulness(play, "Forgetfulness", spell.enumValue, spell.counterSpell, duration,
								true, true);
				jjSample(play.xPos, play.yPos, SOUND::INTRO_GUNM0);
			}
			break;
			case SPELL_FRENZY:
			{
				Spell@ spell = cast<Spell@>(spells["Y"]);
				@effect = Frenzy(play, "Frenzy", spell.enumValue, spell.counterSpell, duration);
				jjSample(play.xPos, play.yPos, SOUND::INTRO_MONSTER);
			}
			break;
		}
		
		if (affects) {
			if (jjIsServer) {
				if (asPlayer.activeEffects.length == 0) {
					asPlayer.originalFur = play.fur; //This one works for storing the absolute original fur
				}
				changePlayerFur(effectID, targetPlayerID);
			}
			
			players[targetPlayerID].setNewActiveEffect(effect);
		}
	}
	
	void doMassEffect(uint8 effectID, int8[] targets, uint duration) {
		for (uint i = 0; i < targets.length; i++) {
			int8 targetPlayerID = targets[i];
			jjPLAYER@ play = jjPlayers[targetPlayerID];
			Player@ asPlayer = players[targetPlayerID];
			
			Effect @effect;
			bool affects = true;
			
			switch (effectID) {
				case SPELL_SLOW:
				{
					Spell@ spell = cast<Spell@>(spells["S"]);
					@effect = Slow(play, "Slow", spell.enumValue, spell.counterSpell, duration * SECOND,
									true, true);
					jjSample(play.xPos, play.yPos, SOUND::DEVILDEVAN_VANISH1);
				}
				break;
				case SPELL_DISPEL:
				{
					unDoAllEffects(targetPlayerID);
					affects = false;
					jjSample(play.xPos, play.yPos, SOUND::WITCH_MAGIC);
				}
				break;
				case SPELL_DISRUPTING_RAY:
				{
					Spell@ spell = cast<Spell@>(spells["R"]);
					@effect = DisruptingRay(play, "Disrupting Ray", spell.enumValue, spell.counterSpell, duration * SECOND,
									false, true);
					jjSample(play.xPos, play.yPos, SOUND::COMMON_ITEMTRE);
				}
				break;
				case SPELL_WEAKNESS:
				{
					Spell@ spell = cast<Spell@>(spells["W"]);
					@effect = Weakness(play, "Weakness", spell.enumValue, spell.counterSpell, duration * SECOND,
									false, true);
					jjSample(play.xPos, play.yPos, SOUND::COMMON_HOLYFLUT);
				}
				break;
				case SPELL_FORGETFULNESS:
				{
					Spell@ spell = cast<Spell@>(spells["G"]);
					@effect = Forgetfulness(play, "Forgetfulness", spell.enumValue, spell.counterSpell, duration * SECOND,
									true, true);
					jjSample(play.xPos, play.yPos, SOUND::INTRO_GUNM0);
				}
				break;
			}
			
			if (affects) {
				if (jjIsServer) {
					if (asPlayer.activeEffects.length == 0) {
						asPlayer.originalFur = play.fur; //This one works for storing the absolute original fur
					}
					changePlayerFur(effectID, targetPlayerID);
				}
				
				players[targetPlayerID].setNewActiveEffect(effect);
			}
		}
	}
	
	bool doSpell(string key) {
		jjPLAYER@ caster = jjLocalPlayers[0];
		Player@ asPlayer = players[caster.playerID];
		int8 playerID = caster.playerID;
		Spell@ spell = cast<Spell@>(spells[key]);
		uint spellDuration = spell.baseDuration + asPlayer.spellDurationBonus;
		
		switch (spell.enumValue) {
			case SPELL_MAGIC_ARROW:
			{
				doBullet(BULLET_MAGIC_ARROW);
			}
			break;
			case SPELL_STONE_SKIN:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_STONE_SKIN, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_STONE_SKIN, playerID, spellDuration);
				}
			}
			break;
			case SPELL_BLESS:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_BLESS, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_BLESS, playerID, spellDuration);
				}
			}
			break;
			case SPELL_BLOOD_LUST:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_BLOOD_LUST, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_BLOOD_LUST, playerID, spellDuration);
				}
				jjPlayers[playerID].startSugarRush(spellDuration * 70);
			}
			break;
			case SPELL_DISPEL:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendMassEffectPacket(playerID, SPELL_DISPEL, targets, spellDuration);
				if (jjIsServer) {
					doMassEffect(SPELL_DISPEL, targets, spellDuration);
				}
			}
			break;
			case SPELL_SLOW:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendMassEffectPacket(playerID, SPELL_SLOW, targets, spellDuration);
				if (jjIsServer) {
					doMassEffect(SPELL_SLOW, targets, spellDuration);
				}
			}
			break;
			case SPELL_CURE:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_CURE, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_CURE, playerID, spellDuration);
				}
				if (caster.health < jjMaxHealth) {
					int distance = caster.health - jjMaxHealth;
					caster.hurt(distance, true);
				}
			}
			break;
			case SPELL_VISIONS:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_VISIONS, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_VISIONS, playerID, spellDuration);
				}
			}
			break;
			case SPELL_DISRUPTING_RAY:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendMassEffectPacket(playerID, SPELL_DISRUPTING_RAY, targets, spellDuration);
				if (jjIsServer) {
					doMassEffect(SPELL_DISRUPTING_RAY, targets, spellDuration);
				}
			}
			break;
			case SPELL_WEAKNESS:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendMassEffectPacket(playerID, SPELL_WEAKNESS, targets, spellDuration);
				if (jjIsServer) {
					doMassEffect(SPELL_WEAKNESS, targets, spellDuration);
				}
			}
			break;
			case SPELL_ICE_BOLT:
			{
				doBullet(BULLET_ICE_BOLT);
			}
			break;
			case SPELL_DEATH_RIPPLE:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendAOEPacket(playerID, spell.enumValue);
				
				if (jjIsServer) {
					doAOE(caster, spell.enumValue, caster.xPos, caster.yPos);
				}
			}
			break;
			case SPELL_PRECISION:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_PRECISION, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_PRECISION, playerID, spellDuration);
				}
			}
			break;
			case SPELL_WALL_OF_FIRE:
			{
				int direction = caster.direction;
				float xOrigin = caster.xPos + direction * WALL_OF_FIRE_CASTING_RANGE;
				float yOrigin = caster.yPos + 16;
				int height = acUtils::getWallOfFireHeightInTiles(xOrigin, yOrigin);
				if (height < 1) {
					return false;
				}
				
				acNetworking::sendWallOfFirePacket(playerID, xOrigin, yOrigin, height);
				if (jjIsServer) {
					uint duration = (spell.baseDuration + asPlayer.spellDurationBonus) * SECOND;
					doWallOfFire(caster, spell, xOrigin, yOrigin, height, duration);
				}
			}
			break;
			case SPELL_FORGETFULNESS:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendMassEffectPacket(playerID, SPELL_FORGETFULNESS, targets, spellDuration);
				if (jjIsServer) {
					doMassEffect(SPELL_FORGETFULNESS, targets, spellDuration);
				}
			}
			break;
			case SPELL_FROST_RING:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendAOEPacket(playerID, spell.enumValue);
				
				if (jjIsServer) {
					doAOE(caster, spell.enumValue, caster.xPos, caster.yPos);
				}
			}
			break;
			case SPELL_FIREBALL:
			{
				doBullet(BULLET_FIREBALL);
			}
			break;
			case SPELL_FRENZY:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_FRENZY, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_FRENZY, playerID, spellDuration);
				}
			}
			break;
			case SPELL_CHAIN_LIGHTNING:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acUtils::playLightningSound(caster.xPos, caster.yPos);
				
				if (jjIsServer) {
					array<ChainLightningTarget@> chainLightningTargets = doChainLightning(playerID, targets);
					acNetworking::sendChainLightningPacketToClients(playerID, chainLightningTargets);
					chainLightningTargetGroups.insertLast(chainLightningTargets);
				} else {
					acNetworking::sendChainLightningPacketToServer(playerID, targets);
				}
			}
			break;
			case SPELL_ARMAGEDDON:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendAOEPacket(playerID, spell.enumValue);
				
				if (jjIsServer) {
					doAOE(caster, spell.enumValue, caster.xPos, caster.yPos);
				}
			}
			break;
			case SPELL_IMPLOSION:
			{
				float scaledRadius = acUtils::getScaledRadius(spell.radius, ANIM::FLARE, 5);
				int8[] targets = acUtils::getTargets(caster, spell, scaledRadius);
				
				if (targets.length == 0) return false;
				
				acNetworking::sendAOEPacket(playerID, spell.enumValue);
				
				if (jjIsServer) {
					doAOE(caster, spell.enumValue, caster.xPos, caster.yPos);
				}
			}
			break;
		}
		return true;
	}
	
	void doWallOfFire(jjPLAYER@ caster, Spell@ spell, float xOrigin, float yOrigin, int8 height, uint duration) {
		Player@ asPlayer = players[caster.playerID];
		removeWallOfFireByCaster(caster);
		WallOfFire wallOfFire(xOrigin, yOrigin, height, spell.baseDamage + asPlayer.spellDamageBonus, duration, caster); //TODO: CHANGE TO VARIABLE
		jjSample(caster.xPos, caster.yPos, SOUND::DEVILDEVAN_DRAGONFIRE);
		activeWallsOfFire.insertLast(wallOfFire);
	}
	
	string getLongestSpellName() {
		string longestName = "";
		for (uint i = 0; i < ownSpells.length; i++) {
			string spellName = cast<Spell@>(spells[ownSpells[i]]).name;
			string fullName = ownSpells[i] + "   " + spellName + "       ";
			if (fullName.length > longestName.length) longestName = fullName;
		}
		if (longestName.length == 0) longestName = EMPTY_SPELLBOOK;
		return longestName;
	}
	
	int getSpellIndexByKey(string selectedSpellKey) {
		if (selectedSpellKey == "" && ownSpells.length > 0) {
			return -1;
		}
		for (uint i = 0; i < ownSpells.length; i++) {
			Spell@ spell = cast<Spell@>(spells[ownSpells[i]]);
			if (spell.key == selectedSpellKey) {
				return int(i);
			}
		}
		return -2;
	}
	
	string getSpellKeyByEnumValue(uint8 spellEnumValue) {
		string spellKey = "";
		array<string> spellKeys = spells.getKeys();
		for (uint i = 0; i < spellKeys.length; i++) {
			string key = spellKeys[i];
			Spell@ spell = cast<Spell@>(spells[key]);
			if (SPELL(spellEnumValue) == spell.enumValue) {
				spellKey = key;
				break;
			}
		}
		return spellKey;
	}
	
	uint getTotalSpellCountByTier(uint tier) {
		uint spellCount = 0;
		array<string> spellKeys = spells.getKeys();
		
		for (uint i = 0; i < spellKeys.length; i++) {
			string key = spellKeys[i];
			Spell@ spell = cast<Spell@>(spells[key]);
			if (spell.tier == tier) spellCount++;
		}
		
		return spellCount;
	}
	
	Spell@ giveRandomSpellByTier(uint tierChance, uint minimumTier = 1) {
		array<Spell@> spellsInRange;
		array<string> spellKeys = spells.getKeys();
		for (uint i = 0; i < spellKeys.length; i++) {
			string key = spellKeys[i];
			Spell@ spell = cast<Spell@>(spells[key]);
			
			if (spell.tier <= tierChance && spell.tier >= minimumTier) {
				spellsInRange.insertLast(spell);
			}
		}
		return spellsInRange[jjRandom() % spellsInRange.length]; 
	}
	
	void removeSpells() {
		ownSpells.removeRange(0, ownSpells.length);
	}
	
	void removeWallOfFireByCaster(jjPLAYER@ caster) {
		int j = 0;
		int activeWallsOfFireLength = activeWallsOfFire.length();
		for (int i = 0; i < activeWallsOfFireLength; i++) {
			WallOfFire@ wall = activeWallsOfFire[i];
			if (@caster !is wall.caster) {
				@activeWallsOfFire[j] = wall;
				j++;
			}
		}
		activeWallsOfFire.removeRange(j, activeWallsOfFireLength - j);
	}
	
	void unDoEffect(int8 targetPlayerID) {
		if (jjIsServer) {
			changePlayerFur(SPELL_UNDO, targetPlayerID);
		}
	}

	void unDoAllEffects(int8 targetPlayerID) {
		if (jjIsServer) changePlayerFur(SPELL_NONE, targetPlayerID);
		players[targetPlayerID].removeAllActiveEffects();
	}
	
	void updateSpellDescriptions(array<string> spellKeys, Player@ asPlayer) {
		for (uint i = 0; i < spellKeys.length; i++) {
			string key = spellKeys[i];
			Spell@ spell = cast<Spell@>(spells[key]);
			string descriptionWithDmg = jjRegexReplace(spell.description, "\\{dmg\\}",
					"" + (spell.baseDamage + asPlayer.spellDamageBonus));
			spell.description = jjRegexReplace(descriptionWithDmg, "\\{dur\\}",
					"" + (spell.baseDuration + asPlayer.spellDurationBonus));
		}
	}
}