Downloads containing academy_spells.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Academy Superjazz Battle 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 wavy 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.")},
		{"R", Spell("R", "Disrupting Ray", 	SPELL_DISRUPTING_RAY,	SPELL_STONE_SKIN, 		2,	6, 	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,	7, 	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,	8, 	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,	9, 	1, 	0,	25, 1.25,	6,	true, 	"Deals ||{dmg} ||||damage to all living players in a range (undead are unaffected).")},
		{"D", Spell("D", "Dispel", 			SPELL_DISPEL,			SPELL_NONE,				2,	10, 0, 	0, 	30, 1,		6,	true, 	"Removes all effects from all players in range (including the caster).")},
		{"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.")},
		{"T", Spell("T", "Frost Ring",		SPELL_FROST_RING,		SPELL_NONE,				3,  13,	2,	0,	35,	1.5,	7,	false,	"Deals ||{dmg} ||||damage to all enemies in a large range and freezes them (caster is unaffected).")},
		{"F", Spell("F", "Fireball", 		SPELL_FIREBALL,			SPELL_NONE,				3, 	14, 3,	0, 	35, 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.")},
		{"P", Spell("P", "Precision", 		SPELL_PRECISION,		SPELL_FORGETFULNESS,	3,	15, 0, 	20, 45, 0,		0,	false, 	"Your bullets deal ||2 ||||damage more on all enemies for ||||||{dur} ||seconds. Counters Forgetfulness.")},
		{"Y", Spell("Y", "Frenzy", 			SPELL_FRENZY,			SPELL_NONE,				4, 	16, 0, 	20, 50, 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.")},
		{"C", Spell("C", "Magic Mirror", 	SPELL_MAGIC_MIRROR,		SPELL_NONE,				4,  17, 0, 	20, 55, 0,		0,	false,	"You gain a magic shield that reflects hostile spells on enemies for ||||{dur} ||seconds. Does not protect from self damaging spells. Can be dispelled.")},
		{"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 the caster).")},
		{"U", Spell("U", "Cure", 			SPELL_CURE,				SPELL_NONE,				4, 	19, 0, 	0, 	60, 0,		0,	false, 	"You regain full health (for a little pinch) and all negative effects are removed from you.")},
		{"A", Spell("A", "Armageddon",		SPELL_ARMAGEDDON,		SPELL_NONE,				5, 	20, 5, 	0, 	70, 2,		8,	true, 	"All players in a large range are dealt ||{dmg} ||||damage within a short delay (including the caster).")},
		{"Q", Spell("Q", "Implosion", 		SPELL_IMPLOSION,		SPELL_NONE,				5, 	21, 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", cast<Spell@>(fullSpells["M"])},
		{"O", cast<Spell@>(fullSpells["O"])},
		{"S", cast<Spell@>(fullSpells["S"])},
		{"E", cast<Spell@>(fullSpells["E"])},
		{"H", cast<Spell@>(fullSpells["H"])},
		{"D", cast<Spell@>(fullSpells["D"])},
		{"I", cast<Spell@>(fullSpells["I"])},
		{"G", cast<Spell@>(fullSpells["G"])},
		{"T", cast<Spell@>(fullSpells["T"])},
		{"F", cast<Spell@>(fullSpells["F"])},
		{"U", cast<Spell@>(fullSpells["U"])},
		{"L", cast<Spell@>(fullSpells["L"])},
		{"C", cast<Spell@>(fullSpells["C"])},
		{"A", cast<Spell@>(fullSpells["A"])},
		{"Q", cast<Spell@>(fullSpells["Q"])}
	};

	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_MAGIC_MIRROR:
			{
				furArray = FUR_MAGIC_MIRROR;
			}
			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 determineCastersAndDoAOE(int8[] targets, Spell@ spell, float scaledRadius, int8 originalCasterId) {
		array<int8> magicMirrorPlayerIds = acUtils::getMagicMirrorPlayerIdsFromTargets(targets);
		if (magicMirrorPlayerIds.length() > 0 && spell.enumValue != SPELL_ARMAGEDDON) {
			doAoeFromPlayersWithMagicMirror(magicMirrorPlayerIds, spell, scaledRadius, originalCasterId);
		} else {
			acNetworking::sendAoePacket(originalCasterId, spell.enumValue, originalCasterId);
			if (jjIsServer) {
				doAOE(jjPlayers[originalCasterId], jjPlayers[originalCasterId], spell.enumValue);
			}
		}
	}
	
	void determineCastersAndDoChainLightning(int8[] targets, Spell@ spell, float scaledRadius,
			int8 originalCasterId) {
		array<int8> magicMirrorPlayerIds = acUtils::getMagicMirrorPlayerIdsFromTargets(targets);
		if (magicMirrorPlayerIds.length() > 0) {
			doChainLightningFromPlayersWithMagicMirror(magicMirrorPlayerIds, spell, scaledRadius,
					originalCasterId);
		} else {
			if (jjIsServer) {
				array<ChainLightningTarget@> chainLightningTargets = doChainLightning(originalCasterId,
						targets);
				acNetworking::sendChainLightningPacketToClients(originalCasterId, chainLightningTargets);
				chainLightningTargetGroups.insertLast(chainLightningTargets);
			} else {
				acNetworking::sendChainLightningPacketToServer(originalCasterId, originalCasterId, targets);
			}
		}
	}
	
	void determineCastersAndDoMassEffect(int8[] targets, Spell@ spell, float scaledRadius,
			int8 originalCasterId, uint spellDuration) {
		array<int8> magicMirrorPlayerIds = acUtils::getMagicMirrorPlayerIdsFromTargets(targets);
		if (magicMirrorPlayerIds.length() > 0) {
			doMassEffectFromPlayersWithMagicMirror(magicMirrorPlayerIds, spell, scaledRadius,
					originalCasterId, spellDuration);
		} else {
			acNetworking::sendMassEffectPacket(originalCasterId, spell.enumValue, targets, spellDuration,
					originalCasterId);
			if (jjIsServer) {
				doMassEffect(spell.enumValue, targets, spellDuration);
			}
		}
	}
	
	void doAOE(jjPLAYER@ currentCaster, jjPLAYER@ originalCaster, uint8 spellID) {
		doAOE(currentCaster, originalCaster, spellID, -1, -1);
	}
	
	void doAOE(jjPLAYER@ currentCaster, jjPLAYER@ originalCaster, uint8 spellID, float xPos, float yPos) {
		string spellKey = getSpellKeyByEnumValue(spellID);
		Spell@ spell = cast<Spell@>(spells[spellKey]);
		int damage = spell.baseDamage + players[originalCaster.playerID].spellDamageBonus;
		if (xPos < 0) {
			xPos = currentCaster.xPos;
		}
		if (yPos < 0) {
			yPos = currentCaster.yPos;
		}
		
		switch (spellID) {
			case SPELL_DEATH_RIPPLE:
			{
				activeAreasOfEffect.insertLast(DeathRipple(
						xPos, yPos, spell.radius, damage, 35, currentCaster));
			}
			break;
			case SPELL_FROST_RING:
			{
				activeAreasOfEffect.insertLast(FrostRing(
						xPos, yPos, spell.radius * 3.2, damage, 35, currentCaster));
			}
			break;
			case SPELL_FIREBALL:
			{
				activeAreasOfEffect.insertLast(FireballExplosion(
						xPos, yPos, spell.radius, damage, 70, currentCaster));
			}
			break;
			case SPELL_ARMAGEDDON:
			{
				activeAreasOfEffect.insertLast(Armageddon(
						xPos, yPos, spell.radius, damage, 70, originalCaster));
			}
			break;
			case SPELL_IMPLOSION:
			{
				activeAreasOfEffect.insertLast(Implosion(
						xPos, yPos, spell.radius, damage, 70, currentCaster));
			}
			break;
		}
	}
	
	void doAoeFromPlayersWithMagicMirror(array<int8> magicMirrorPlayerIds, Spell@ spell,
			float scaledRadius, int8 originalCasterId) {
		uint magicMirrorPlayerIdsLength = magicMirrorPlayerIds.length();
		for (uint i = 0; i < magicMirrorPlayerIdsLength; i++) {
			int8[] targets = acUtils::getTargets(jjPlayers[magicMirrorPlayerIds[i]], spell, scaledRadius);
			int8[] newMagicMirrorTargets = acUtils::getMagicMirrorPlayerIdsFromTargets(targets);
			for (uint j = 0; j < newMagicMirrorTargets.length(); j++) {
				magicMirrorPlayerIds.insertLast(newMagicMirrorTargets[j]);
			}
			acUtils::filterOutMagicMirrorTargets(targets, spell.enumValue);
			if (targets.length() == 0) {
				continue;
			}
			acNetworking::sendAoePacket(originalCasterId, spell.enumValue, magicMirrorPlayerIds[i]);
			if (jjIsServer) {
				doAOE(jjPlayers[magicMirrorPlayerIds[i]], jjPlayers[originalCasterId], spell.enumValue);
			}
		}
		acNetworking::sendMagicMirrorAnimationPacket(originalCasterId, magicMirrorPlayerIds);
		if (jjIsServer) {
			acUtils::addMagicMirrorAnimationOnTargetsWithMagicMirror(magicMirrorPlayerIds);
		}
	}
	
	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;
		int chainLightningCounter = 0;
		
		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 doChainLightningFromPlayersWithMagicMirror(array<int8> magicMirrorPlayerIds,
			Spell@ spell, float scaledRadius, int8 originalCasterId) {
		uint magicMirrorPlayerIdsLength = magicMirrorPlayerIds.length();
		for (uint i = 0; i < magicMirrorPlayerIdsLength; i++) {
			int8[] targets = acUtils::getTargets(jjPlayers[magicMirrorPlayerIds[i]], spell, scaledRadius);
			int8[] newMagicMirrorTargets = acUtils::getMagicMirrorPlayerIdsFromTargets(targets);
			for (uint j = 0; j < newMagicMirrorTargets.length(); j++) {
				magicMirrorPlayerIds.insertLast(newMagicMirrorTargets[j]);
			}
			acUtils::filterOutMagicMirrorTargets(targets, spell.enumValue);
			if (targets.length() == 0) {
				continue;
			}
			if (jjIsServer) {
				array<ChainLightningTarget@> chainLightningTargets = doChainLightning(
						magicMirrorPlayerIds[i], targets);
				acNetworking::sendChainLightningPacketToClients(magicMirrorPlayerIds[i],
						chainLightningTargets);
				chainLightningTargetGroups.insertLast(chainLightningTargets);
			} else {
				acNetworking::sendChainLightningPacketToServer(originalCasterId, magicMirrorPlayerIds[i],
						targets);
			}
		}
		acNetworking::sendMagicMirrorAnimationPacket(originalCasterId, magicMirrorPlayerIds);
		if (jjIsServer) {
			acUtils::addMagicMirrorAnimationOnTargetsWithMagicMirror(magicMirrorPlayerIds);
		}
	}
	
	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);
			}
			break;
			case SPELL_BLESS:
			{
				Spell@ spell = cast<Spell@>(spells["B"]);
				@effect = Bless(play, "Bless", spell.enumValue, spell.counterSpell, duration);
			}
			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);
			}
			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;
			}
			break;
			case SPELL_DISRUPTING_RAY:
			{
				Spell@ spell = cast<Spell@>(spells["R"]);
				@effect = DisruptingRay(play, "Disrupting Ray", spell.enumValue, spell.counterSpell, duration,
								false, true);
			}
			break;
			case SPELL_WEAKNESS:
			{
				Spell@ spell = cast<Spell@>(spells["W"]);
				@effect = Weakness(play, "Weakness", spell.enumValue, spell.counterSpell, duration,
								false, true);
			}
			break;
			case SPELL_PRECISION:
			{
				Spell@ spell = cast<Spell@>(spells["P"]);
				@effect = Precision(play, "Precision", spell.enumValue, spell.counterSpell, duration);
			}
			break;
			case SPELL_FORGETFULNESS:
			{
				Spell@ spell = cast<Spell@>(spells["G"]);
				@effect = Forgetfulness(play, "Forgetfulness", spell.enumValue, spell.counterSpell, duration,
								true, true);
			}
			break;
			case SPELL_FRENZY:
			{
				Spell@ spell = cast<Spell@>(spells["Y"]);
				@effect = Frenzy(play, "Frenzy", spell.enumValue, spell.counterSpell, duration);
			}
			break;
			case SPELL_MAGIC_MIRROR:
			{
				Spell@ spell = cast<Spell@>(spells["C"]);
				@effect = MagicMirror(play, "Magic Mirror", spell.enumValue, spell.counterSpell, duration);
			}
			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);
				}
				break;
				case SPELL_DISPEL:
				{
					unDoAllEffects(targetPlayerID);
					affects = false;
				}
				break;
				case SPELL_DISRUPTING_RAY:
				{
					Spell@ spell = cast<Spell@>(spells["R"]);
					@effect = DisruptingRay(play, "Disrupting Ray", spell.enumValue, spell.counterSpell, duration * SECOND,
									false, true);
				}
				break;
				case SPELL_WEAKNESS:
				{
					Spell@ spell = cast<Spell@>(spells["W"]);
					@effect = Weakness(play, "Weakness", spell.enumValue, spell.counterSpell, duration * SECOND,
									false, true);
				}
				break;
				case SPELL_FORGETFULNESS:
				{
					Spell@ spell = cast<Spell@>(spells["G"]);
					@effect = Forgetfulness(play, "Forgetfulness", spell.enumValue, spell.counterSpell, duration * SECOND,
									true, true);
				}
				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 doMassEffectFromPlayersWithMagicMirror(array<int8> magicMirrorPlayerIds, Spell@ spell,
			float scaledRadius, int8 originalCasterId, uint spellDuration) {
		uint magicMirrorPlayerIdsLength = magicMirrorPlayerIds.length();
		for (uint i = 0; i < magicMirrorPlayerIdsLength; i++) {
			int8[] targets = acUtils::getTargets(jjPlayers[magicMirrorPlayerIds[i]], spell, scaledRadius);
			int8[] newMagicMirrorTargets = acUtils::getMagicMirrorPlayerIdsFromTargets(targets);
			for (uint j = 0; j < newMagicMirrorTargets.length(); j++) {
				magicMirrorPlayerIds.insertLast(newMagicMirrorTargets[j]);
			}
			acUtils::filterOutMagicMirrorTargets(targets, spell.enumValue);
			if (targets.length() == 0) {
				continue;
			}
			acNetworking::sendMassEffectPacket(originalCasterId, spell.enumValue, targets, spellDuration,
					magicMirrorPlayerIds[i]);
			if (jjIsServer) {
				doMassEffect(spell.enumValue, targets, spellDuration);
			}
		}
		acNetworking::sendMagicMirrorAnimationPacket(originalCasterId, magicMirrorPlayerIds);
		if (jjIsServer) {
			acUtils::addMagicMirrorAnimationOnTargetsWithMagicMirror(magicMirrorPlayerIds);
		}
	}
	
	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, playerID);
				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;
				determineCastersAndDoMassEffect(targets, spell, scaledRadius, playerID, 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 = -1;
					caster.hurt(distance, true);
				}
			}
			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;
				determineCastersAndDoMassEffect(targets, spell, scaledRadius, playerID, 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;
				determineCastersAndDoMassEffect(targets, spell, scaledRadius, playerID, 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;
				determineCastersAndDoAOE(targets, spell, scaledRadius, playerID);
			}
			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;
				determineCastersAndDoMassEffect(targets, spell, scaledRadius, playerID, 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;
				determineCastersAndDoAOE(targets, spell, scaledRadius, playerID);
			}
			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;
				determineCastersAndDoChainLightning(targets, spell, scaledRadius, playerID);
			}
			break;
			case SPELL_MAGIC_MIRROR:
			{
				acNetworking::sendEffectPacket(playerID, SPELL_MAGIC_MIRROR, playerID, spellDuration);
				if (jjIsServer) {
					doEffect(SPELL_MAGIC_MIRROR, playerID, spellDuration);
				}
			}
			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, playerID);
				if (jjIsServer) {
					doAOE(caster, caster, spell.enumValue);
				}
			}
			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;
				determineCastersAndDoAOE(targets, spell, scaledRadius, playerID);
			}
			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
		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));
		}
	}
}