// Ion run.
// -Object gets zapped with amout of charge.
// -Interal energy gets converted and added to charge.
// +If object is an conductor
//   -Object passes remaining charge onto enemies [High amount]
// -Object passes remaining charge onto conductors [Medium amount]
// +If object isn't a conductor
//   -Object passes remaining charge onto nearby objects [Low amount]
// +If object is an conductor
//   -Object vents remaining ion upwards if there's room.
// -Object Converts remaining charge into damage.

////////Main Ion globals/////////
$Ion::ThunderSound[0] = thunderCrash1;
$Ion::ThunderSound[1] = thunderCrash2;
$Ion::ThunderSound[2] = thunderCrash3;
$Ion::ThunderSound[3] = thunderCrash4;
$Ion::ThunderSoundCount = 4;
$Ion = 1;

$Ion::ConductorCharge = 0; // The amount of energy a conductor makes (extra)
$Ion::StopIon = 0; //Stops ion in it's tracks.
$Ion::Damage = 1;
$Ion::MaxIon = 50;

$Ion::MaxPass = 4;

////////Ionstorm globals/////////

$IonStorm::Radius = 500; //Max distance a beam will hit from target.
$IonStorm::Height = 1000; //Height from which beams fire.
$IonStorm::PlayerSeek = 0.25; //Change a beam will target a player.

////////Main Ion Datablocks///////

datablock AudioProfile(IonFireSound) {
	filename  = "fx/misc/shield.wav";
	description = AudioClose3d;
	preload = true;
};

datablock ParticleData(PrebeamParticle) {
	dragCoefficient = 0;
	gravityCoefficient = -0.9;
	windCoefficient = 0;
	inheritedVelFactor = 0;
	constantAcceleration = 0;
	lifetimeMS = 12000;
	lifetimeVarianceMS = 6000;
	useInvAlpha = 0;
	spinRandomMin = 0;
	spinRandomMax = 0;
	textureName = "special/bluespark.PNG";
	times[0] = 0.75;
	times[1] = 2;
	colors[0] = "0.5 0.5 0.9 1";
	colors[1] = " 0 0 0 0";
	sizes[0] = 1;
	sizes[1] = 1;
};

datablock ParticleEmitterData(PrebeamEmitter) {
	ejectionPeriodMS = 45;
	periodVarianceMS = 10;
	ejectionVelocity = 2;
	velocityVariance = 2;
	ejectionOffset   = 8;
	thetaMin = 140;
	thetaMax = 160;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvances = 0;
	orientParticles= 1;
	orientOnVelocity = 1;
	particles = "PrebeamParticle";
};

datablock ParticleData(ShockSparks) {
	dragCoefficient   = 1;
	gravityCoefficient  = 0.0;
	inheritedVelFactor  = 0.2;
	constantAcceleration = 0.2;
	lifetimeMS      = 200;
	lifetimeVarianceMS  = 0;
	textureName     = "special/blueSpark";
	useInvAlpha = 0;

	spinRandomMin = -20;

	spinRandomMax = 20;

	colors[0]   = "0.8 0.8 1.0 1.0";
	colors[1]   = "0.8 0.8 1.0 0.5";
	colors[2]   = "0.8 0.8 1.0 0.0";
	sizes[0]   = 2.55;
	sizes[1]   = 2.65;
	sizes[2]   = 2.85;
	times[0]   = 0.0;
	times[1]   = 0.5;
	times[2]   = 1.0;

};

datablock ParticleEmitterData(ShockSparksEmitter) {
	ejectionPeriodMS = 5;
	periodVarianceMS = 0;
	ejectionVelocity = 20;
	velocityVariance = 15;
	ejectionOffset  = 1.0;
	thetaMin     = 0;
	thetaMax     = 50;
	phiReferenceVel = 0;
	phiVariance   = 360;
	overrideAdvances = false;
	orientParticles = true;
	//  lifetimeMS    = 100;
	particles = "ShockSparks";
};

datablock ParticleData(ShockFlareParticle) {
	dragCoeffiecient   = 0.0;
	gravityCoefficient  = 0.0;
	inheritedVelFactor  = 0.0;

	lifetimeMS      = 500;
	lifetimeVarianceMS  = 0;

	spinRandomMin = 0.0;
	spinRandomMax = 0.0;
	windcoefficient = 0;
	textureName     = "skins/jetflare03";

	colors[0]   = "0.3 0.3 1.0 0.9";
	colors[1]   = "0.3 0.3 1.0 0.5";
	colors[2]   = "0.3 0.3 1.0 0.9";
	colors[3]   = "0.3 0.3 1.0 0.5";

	sizes[0]   = 5;
	sizes[1]   = 5;
	sizes[2]   = 5;
	sizes[3]   = 5;

	times[0]   = 0.25;
	times[1]   = 0.25;
	times[2]   = 0.25;
	times[3]   = 1;
};

datablock ParticleEmitterData(ShockFlareEmitter) {
	lifetimeMS    = 10;
	ejectionPeriodMS = 100;
	periodVarianceMS = 0;

	ejectionVelocity = 0.1;
	velocityVariance = 0.0;
	ejectionoffset = 0;
	thetaMin     = 0.0;
	thetaMax     = 0.0;

	orientParticles = false;
	orientOnVelocity = false;

	particles = "ShockFlareParticle";
};

datablock ParticleData(ShockFlareParticle2) {
	dragCoeffiecient   = 0.0;
	gravityCoefficient  = 0.0;
	inheritedVelFactor  = 1.0;

	lifetimeMS      = 500;
	lifetimeVarianceMS  = 0;

	spinRandomMin = 0.0;
	spinRandomMax = 0.0;
	windcoefficient = 0;
	textureName     = "skins/jetflare03";

	colors[0]   = "0.3 0.3 1.0 0.9";
	colors[1]   = "0.3 0.3 1.0 0.5";
	colors[2]   = "0.3 0.3 1.0 0.9";
	colors[3]   = "0.3 0.3 1.0 0.5";

	sizes[0]   = 50;
	sizes[1]   = 50;
	sizes[2]   = 50;
	sizes[3]   = 50;

	times[0]   = 0.25;
	times[1]   = 0.25;
	times[2]   = 0.25;
	times[3]   = 1;
};

datablock ParticleEmitterData(ShockFlareEmitter2) {
	lifetimeMS    = 10;
	ejectionPeriodMS = 100;
	periodVarianceMS = 0;

	ejectionVelocity = 0.1;
	velocityVariance = 0.0;
	ejectionoffset = 0;
	thetaMin     = 0.0;
	thetaMax     = 0.0;

	orientParticles = false;
	orientOnVelocity = false;

	particles = "ShockFlareParticle2";
};

datablock ParticleData(InfParticle) {
	dragCoefficient   = 0;
	WindCoefficient = 0;
	gravityCoefficient  = 0.0;
	inheritedVelFactor  = 1;
	constantAcceleration = 0;
	lifetimeMS      = 2000;
	lifetimeVarianceMS  = 0;
	textureName     = "special/lightning1frame2";
	useInvAlpha = 0;

	spinRandomMin = -800;

	spinRandomMax = 800;

	spinspeed = 50;
	colors[0]   = "0.8 0.8 1.0 1.0";
	colors[1]   = "0.8 0.8 1.0 1.0";
	colors[2]   = "0.8 0.8 1.0 0.0";
	sizes[0]   = 1;
	sizes[1]   = 3;
	sizes[2]   = 2;
	times[0]   = 0.0;
	times[1]   = 0.5;
	times[2]   = 1.0;
};

datablock ParticleEmitterData(InfEmitter) {
	ejectionPeriodMS = 50;
	periodVarianceMS = 0;
	ejectionVelocity = 0.5;
	velocityVariance = 1;
	ejectionOffset  = 0.5;
	thetaMin     = 0;
	thetaMax     = 180;
	phiReferenceVel = 0;
	phiVariance   = 360;
	overrideAdvances = false;
	orientParticles = false;
	//  lifetimeMS    = 100;
	particles = "InfParticle";
};

datablock ItemData(ShockLight0) : DeployedLight {
	lightType = "PulsingLight";
	lightColor= "0.5 0.5 1";
	lightTime = "50";
	lightRadius = "4";
};

datablock ItemData(ShockLight1) : DeployedLight {
	lightType = "PulsingLight";
	lightColor= "1 1 1";
	lightTime = "50";
	lightRadius = "3";
};

datablock ItemData(ShockLight2) : DeployedLight {
	//lightType = "PulsingLight";
	lightColor= "0.5 0.5 1";
	//lightTime = "50";
	lightRadius = "2";
};

datablock ExplosionData(ShockExplosion) {
	//explosionShape = "energy_explosion.dts";
	soundProfile  = ShockLanceHitSound;

	emitter[0] = ShockFlareEmitter;
	emitter[1] = ShockSparksEmitter;
	particleDensity = 100;
	particleRadius = 2.50;
	faceViewer      = false;
};

datablock ExplosionData(ShockExplosion2) {
	explosionShape = "disc_explosion.dts";
	soundProfile  =  LightningHitSound;

	faceViewer   = true;

	sizes[0]   = "0.5 0.5 0.5";
	sizes[1]   = "0.3 0.3 0.3";
	sizes[2]   = "0.1 0.4 0.1";
	times[0]   = 0.0;
	times[1]   = 0.5;
	times[2]   = 1.0;

	emitter[0] = "InfEmitter";
	emitter[1] = ShockFlareEmitter;
	particleDensity = 500;
	particleRadius = 15;
	shakeCamera = true;
	camShakeFreq = "10.0 11.0 10.0";
	camShakeAmp = "20.0 20.0 20.0";
	camShakeDuration = 0.5;
	camShakeRadius = 20.0;
};

datablock SniperProjectileData(ShockBeam) {
	directDamage    = 0;
	hasDamageRadius   = false;
	indirectDamage   = 0.0;
	damageRadius    = 0.0;
	velInheritFactor  = 1.0;
	//sound 	    = SniperRifleProjectileSound;
	sound  = IonFireSound;
	explosion      = "ShockExplosion";
	//splash       = SniperSplash;
	//directDamageType  = $DamageType::Lightning;
 
    ImageSource         = "IonRifleImage";

	maxRifleRange    = 2000;
	rifleHeadMultiplier = 1.3;
	beamColor      = "0.4 0.4 0.9";
	fadeTime      = 1.0;

	startBeamWidth = 1.0;
	endBeamWidth	   = 0.1;
	pulseBeamWidth  = 0.1;
	beamFlareAngle 	 = 45.0;
	minFlareSize    = 10.0;
	maxFlareSize    = 500.0;
	pulseSpeed     = 5;
	pulseLength     = 0.5;
	textureName[0]   = "special/flare";
	//textureName[1]   = "skins/beampulse";
	textureName[1] = "special/laserrip04";
	textureName[2] = "special/laserrip04";
	textureName[3] = "special/laserrip04";
	textureName[4] = "special/laserrip04";
	textureName[5] = "special/laserrip04";
	textureName[6] = "special/laserrip04";
	textureName[7] = "special/laserrip04";
	textureName[8] = "special/laserrip04";
	textureName[9] = "special/laserrip04";
	textureName[10] = "special/laserrip02";
	textureName[11] = "special/Shocklance_effect02";

	lightRadius     = 20.0;
	lightColor     = "0.0 0.0 5.0";

	emitter = ELFSparksEmitter;
};

datablock SniperProjectileData(ShockBeam2) :ShockBeam {
	hasDamageRadius   = true;
	indirectDamage   = 5.0;
	damageRadius    = 20.0;
	fadeTime      = 2.0;
	startBeamWidth = 8.0;
	endBeamWidth	   = 1;
	pulseBeamWidth  = 0.1;
	beamFlareAngle 	 = 30;
	minFlareSize    = 50.0;
	maxFlareSize    = 500.0;
	pulseSpeed     = 5;
	pulseLength     = 0.05;
 
    ImageSource         = "IonRifleImage";

	textureName[0]   = "special/flare";
	//textureName[1]   = "skins/beampulse";
	textureName[1] = "special/laserrip04";
	textureName[2] = "special/laserrip04";
	textureName[3] = "special/laserrip04";
	textureName[4] = "special/laserrip04";
	textureName[5] = "special/laserrip04";
	textureName[6] = "special/laserrip04";
	textureName[7] = "special/laserrip04";
	textureName[8] = "special/laserrip04";
	textureName[9] = "special/laserrip04";
	textureName[10] = "special/laserrip02";
	textureName[11] = "special/Shocklance_effect02";

	lightRadius     = 20.0;
	lightColor     = "0.0 0.0 1.0";
	lightRadius     = 20.0;
	lightColor     = "0.0 0.0 1.0";

	beamColor      = "0.5 0.5 0.8";
	maxRifleRange    = 5000;
	explosion      = "ShockExplosion2";
};

////////Main Ion functions///////

function ShapeBase::zap(%obj,%charge) {
    if(isStaticShape(%obj) && !Deployables.isMember(%obj)) {
       return;
    }
	zap(%obj,%charge);
}

///What happens when an object has gotten zapped
function zap(%obj,%charge,%pos) {
	if (isObject(%obj)) {
		%obj.zapSched = "";
		%pos = %obj.getWorldboxCenter();
		%Cco = 1; //All interal energy gets converted to charge.
		%energy = %obj.getEnergyLevel() * %Cco + %obj.getCharge();
		%obj.setEnergyLevel(%obj.getEnergyLevel() * (1 - %Cco));

		if (isConductor(%obj)) {
			%energy = %energy + $Ion::ConductorCharge;
			%obj.smartIon = 1;
			%conductor = 1;
		}
	}

	%charge = %charge + %energy;
	if ($Ion::StopIon || (ShockGroup.getCount > $Ion::MaxIon)
       || (isStaticShape(%obj) && !Deployables.isMember(%obj))) //Phantom139 - Lets block out
		return "";                     //map pieces from being ioned
	if (%charge > 0 && %conductor)
		%charge = findPassEnemy(%charge,%obj);
	if (%charge > 0)
		%charge = findPassCond(%pos,%charge,%obj);
	if (%charge > 0 && !%conductor)
		%charge = findPassObj(%pos,%charge,%obj);
	if (%obj)
		%obj.afterPass(%charge);

//	totalcharge(%pos,100);
}

///// What happens after the object spread his charge
function ShapeBase::afterPass(%obj,%chargeLeft) {
	%obj.zapped = 0;
	%objClass = %obj.getDatablock().classname;
	%maxCharge = %obj.getSizeFactor("maxCharge");

	if (isConductor(%obj) && %chargeLeft > 100) {
		%dir = realVec(%obj,"0 0 1");
		%pos = %obj.getWorldBoxCenter();

		%erres = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%dir,2000)),$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType,%obj);
		if (!%erres)
			%dist = 2000;
		else
		%dist = vectorDist(%pos,getWords(%erres,1,3));

		if (%dist>50) {
			%p = discharge2(vectorAdd(%pos,vectorScale(%dir,2)),%dir,%dist);
			if (%chargeLeft>0)
			%p.setEnergyPercentage(limit(%chargeLeft / %maxCharge,0.5,1));
			addToShock(%p);
			%p.schedule(1000,"delete");
			%dump = 300;
		}
	}
	if (!isConductor(%obj))
		%obj.smartIon = 0;

	%chargeLeft = %chargeLeft - %dump;
	%totalDamage = limit(%chargeLeft - %maxCharge,0);
	if (%totalDamage > 0) {
		if ($Ion::Damage)
			%obj.getDataBlock().schedule(500,"damageObject",%obj,0,pos(%obj),%totalDamage / 100,$DamageType::Lightning);
//		radiusExplosion(%obj,%obj.getWorldboxCenter(),(%totalDamage*%maxCharge) / 50000,(%totalDamage * %maxCharge) / 5000,3000,%obj,$DamageType::Lightning);
	}
	%obj.charge = limit(%chargeLeft - %totalDamage,0);
	%obj.chargeTime = getSimTime();
}

///Look for other objects to spread charge to
function findPassObj(%pos,%charge,%obj) {
	if (%charge > 0)
		%maxRadius = limit(%charge / 2,0,100);

	initContainerRadiusSearch(%pos,%maxRadius,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType);
	%startCharge = %charge;
	while ((%damage = containerSearchNext()) != 0 && !%nopass && %charge > %startCharge * 0.25) {
		// Pass a certain amount.
		%maxPass = %startCharge * 0.25;
		// Pass charge to suitable reciever
		%left = %damage.passCharge(%pos,%maxPass,%obj);
		%charge = %charge - (%maxPass - %left);
		if (%charge != %startCharge)
			%count = %count + 1;
		if (%count > $Ion::MaxPass) //Only pass to 4 object at a time (lag issues);
			return %charge;
	}
	return %charge;
}

///Look for enemy's to spread charge to
function findPassEnemy(%charge,%obj) {
	%pos = %obj.getWorldBoxCenter();
	%maxRadius = limit(%charge * 2,0,100);
	initContainerRadiusSearch(%pos,%maxRadius,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType);

	while ((%damage = containerSearchNext()) != 0 && !%nopass && %charge > 0) {
		if (%damage.team != %obj.team)
			%charge = %damage.passCharge(%pos,%charge,%obj);
		//Zap non owner connected objects.
		else if (%obj.getOwner().evil && (%damage.getOwner() != %obj.getOwner() && %damage.client != %obj.getOwner()))
			%charge = %damage.passCharge(%pos,%charge,%obj);
	}
	return %charge;
}

///Look for conductors to spread charge to
function findPassCond(%pos,%charge,%obj) {
	if (%charge > 0)
	%maxRadius = limit(%charge / 2,0,100);
	initContainerRadiusSearch(%pos,%maxRadius,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType);

	while ((%damage = containerSearchNext()) != 0 && !%nopass && %charge > 0) {
		// Pass a certain amount
		%maxPass = %charge * 0.9;
		// Pass charge to suitable reciever
		%left = %damage.passCond(%pos,%maxPass,%obj);
		%charge = %charge - (%maxPass - %left);
	}
	return %charge;
}

function GameBase::isEnemy(%obj,%target) {
	//echo("isenemy");
	if (!isObject(%obj) || !isObject(%target))
		return "";
	if (%obj.team != %target.team) ///Other teams default to enemy.
		return 1;
	if (%obj.getOwner().evil) {
		if (%obj.getOwner() != %target.client && %obj.getOwner() != %target.getOwner())
			return 1;
	}
	return "";
}

///Pass charge to non conductors
function GameBase::passCharge(%obj,%pos,%charge,%emit) {
	// console spam fix
	if (isObject(%emit))
		%emitIsEnemy = %emit.isEnemy(%obj);
	else
		%emitIsEnemy = 0;
	if (((getSimtime() - %obj.zapTime < 5000) || %obj.getEnergyPct() < 0.25 || GetRandom() * 10 < 1) && !%emitIsEnemy)
		return %charge;

	if (((GetSimtime() - %obj.zapTime < 500)) && %emitIsEnemy)
	return %charge;

	if (%obj.zapSched || %obj == %emit)
		return %charge;

	if (isConductor(%emit))
		%maxRange = %charge * 2;
	else {
		if (%obj.team != %emit.team && %emit.smartIon) //Only pass to team members
			return %charge;
		if (%charge > 0) {
			%maxRange = %charge / 2;
			%miss = mPow(%basicDist,0.5) + %charge / 500;
		}
	}
	%basicDist = vectorDist(%pos,%obj.getWorldBoxCenter());

	if (!(%result = zapLos(%pos,%obj,%miss,%emit)))
		return %charge;

	%firePos = getWords(%result,1,3);
	%hitPos = getWords(%result,4,6);

	%dist = vectorDist(%firePos,%hitPos);
	%dir = vectorNormalize(vectorSub(%hitPos,%firePos));

	if ((%dist > %maxRange || getWord(%return,0) == 1)) {
		%passCharge = 0;
		%loss = limit(mPow(%dist,3),0,50);
	}
	else {
		%passCharge = limit(%charge - mPow(%dist,2),1,%charge);
		%loss = 0; // mPow(%dist,2);
	}

	%passCharge = limit(%passCharge,0,%charge);
	%charge = limit(%charge - (%passCharge + %loss),0);

	if (%dist > %maxRange) {
		%vis = 1;
		%p = discharge2(%firePos,%dir,%dist);
	}
	else {
		%vis = limit(mPow(%passCharge,1 / 3) / 10,0,1);
		%p = discharge(%firePos,%dir);
		createLifeLight(%hitPos,1,1000);

		if (%passCharge) {
			%obj.lastDamagedBy = %emit;
			%obj.smartIon = %emit.smartIon; //Ion remains smart
			%obj.setZap(%passCharge,%hitPos);
		}
	}
	// TODO - 100% always, is this right?
	%vis = 1;
	%p.setEnergyPercentage(%vis);
	addToShock(%p);
	%p.schedule(1000,"delete");

	return %charge;
}

///Passes the charge to the given conductor
function GameBase::passCond(%obj,%pos,%charge,%emit) {
	if (%emit)
		%emitClass = %obj.getDatablock().classname;
	if (!isConductor(%obj))
		return %charge;
	if (%obj.zapSched && getRandom() * 10 > 2)
		return %charge;
	if ((getSimTime() - %obj.zapTime < 1000 && isConductor(%emit)) || %obj == %emit)
		return %charge;
	// Absorb team ion only
	if (isObject(%emit) && %obj.team != %emit.team)
		return %charge;
	//  Don't absorb charge from non allied stuff.
	if (%obj.getOwner().evil && (%obj.getOwner() != %emit.getOwner() && %emit.client != %obj.getOwner()))
		return %charge;

	%basicDist = vectorDist(%pos,%obj.getWorldBoxCenter());

	%miss = mPow(%basicDist,0.5) + %totalCharge / 100;

	if (!(%result = zapLos(%pos,%obj,%miss,%emit)))
		return %charge;

	%firePos = getWords(%result,1,3);
	%hitPos = getWords(%result,4,6);

	%dist = vectorDist(%firePos,%hitPos);
	%dir = vectorNormalize(vectorSub(%hitPos,%firePos));

	if (%dist > 200 && %charge > 1000) {
		%passCharge = 0;
		%loss = mPow(%dist,3);
	}
	else {
		%passCharge = limit(%charge - %dist,0,%charge);
		%loss = mPow(%dist,2);
	}

	%passCharge = limit(%passCharge,0,%charge);
	%charge = limit(%charge - (%passCharge + %loss),0);

	if (%dist > 200 && %charge > 1000) {
		%vis = 1;
		%p = discharge2(%firePos,%dir,%dist);
	}
	else {
		%vis = limit(mPow(%passCharge,1 / 3) / 10,0,1);
		%p = discharge(%firePos,%dir);
		createLifeLight(%hitPos,1,1000);

		if (%passCharge) {
			%obj.lastDamagedBy = %emit;
			%obj.setZap(%passCharge,%hitPos);
		}
	}
	// TODO - 100% always, is this right?
	%vis = 1;
	%p.setEnergyPercentage(%vis);
	addToShock(%p);
	%p.schedule(1000,"delete");

	return %charge;
}

///Put the charge on the target
function GameBase::setZap(%obj,%charge,%hitPos) {
	%group = nameToID(ShockGroup);
	if (!%obj.zapSched && !(%group > 0 && %group.getCount() > 50)) {
		%lagTime = ShockGroup.getCount() * 1000 + (getRandom() + 1);
		%energyTime = %charge * 100 + (getRandom() + 1);
		%otherTime = getRandom()* 0.5 + 1;
		%condTime = 1 + isConductor(%obj) * 5 + isPlayer(%obj) * 50;
		%time = limit(((%lagtime + %energyTime) * %otherTime) / %condTime,0);
		%objClass = %obj.getDatablock().getName();

		if (%obj.client)
			%obj.emp(getSimTime() + limit(%time - 500,0,240000));
		else if ($MTC_Loaded == true) {
			if (%obj.isMTC()) {
				%obj.base.emp(limit(%time * 10,6000));
				%wasMTC = true;
			}
		}
		if (!%wasMTC && !%obj.client) {
			if ((getRandom()* ShockGroup.getCount()) < 50)
				createLifeEmitter(%hitPos,InfEmitter,limit(%time - 500,0,240000));
//				createLifeLight(%hitPos,"0",limit(%time - 500,0,240000));
		}
		%obj.zapTime = getSimTime() + %time;
		%obj.zapSched = %obj.schedule(%time,"zap",0);
	}
	%obj.playShieldEffect("0 0 1");
	%obj.charge = %obj.getCharge() + %passCharge;
	%obj.chargeTime = getSimTime();
}

function createLifeLight(%pos,%type,%time) {
	%light = new Item() {
		datablock = ShockLight @ %type;
		static = true;
		position = %pos;
	};
	%light.schedule(%time,"delete");
}

///Checks to see if there's back and forth contact between the two
function zapLos(%pos,%target,%miss,%obj) {
	%vec = getRandom() * 2 - 1 SPC GetRandom() * 2 - 1 SPC GetRandom() * 2 - 1;
	%dest = %target.getEdge(%vec);
	%res = containerRayCast(%dest,%pos,$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType | $TypeMasks::VehicleObjectType,%target);

	if (%res != %obj && %obj)
		return "";
	else if (!%res)
		%firePos = %pos;
	else
		%firePos = getWords(%res,1,3);

	%dir = vectorMiss(vectorNormalize(vectorSub(%dest,%firepos)),%miss);
	%res2 = containerRayCast(%firePos,vectorAdd(%firePos,vectorScale(%dir,2000)),$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticObjectType,%obj);

	if (!%res2)
		%hitPos = vectorAdd(%firePos,vectorScale(%dir,2000));
	else
		%hitPos = getWords(%res2,1,3);

	if (%res2 != %target)
		return 1 SPC %firePos SPC %hitPos;
	else
		return 2 SPC %firePos SPC %hitPos;
}

///Returns the current charge
function ShapeBase::getCharge(%obj) {
	%oldCharge = %obj.charge;
	%halfTime = 5 * 60 * 1000; //5 minutes before the charges halves
	%timePassed = limit((getSimTime() - %obj.chargeTime),100) / %halfTime;
	return %oldCharge * mPow(0.5,%timePassed);
}

///Returns the totalCharge at an radius around an location
function totalCharge(%pos,%radius) {
	initContainerRadiusSearch(%pos,%radius,$TypeMasks::PlayerObjectType | $TypeMasks::StaticShapeObjectType);
	while ((%damage = containerSearchNext()) != 0)
		%total = %total + %damage.getCharge();
	return %total;
}

///Returns the physical statistics of an object
function ShapeBase::getSizeFactor(%obj,%type) {
	%objClass = %obj.getDatablock().classname;
	//[[Physical factors]]
	%factor1 = mPow(%obj.getVolume(),1 / 3) / 2; // Normalized to players
	%factor2 = %obj.getMaxDamage(); //Object Density
	%factor3 = VectorDot(%obj.getSurface(),"2 2 2"); //Object's total surface
	//[[Energy factors]]
	%factor3 = %obj.getMaxEnergy() / 100; //Energy Capacity
	%factor4 = %obj.getDatablock().rechargeRate; //Energy management
	//[[Object specific]]
	%factor5 = isConductor(%obj); //Sensor absorbtion
	%factor6 = %objClass $= "armor"; //Player living tissue.
	%factor7 = %obj.powerRadius / 100; //Energy production

	if (%type $= "maxCharge")
		return (%factor1 * %factor2) * 100 + (%factor3 * %factor4) * 100 + %factor5 * 2000 - %factor6 * 100 + %factor7 * 100;
}

///Retuns objects who will absorb charge (conductors) and return it to enemies.
	function isConductor(%obj) {
	if (isObject(%obj)) {
		if (%obj.getDataBlock().className $= "sensor")
			return 1;
		else if (%obj.getDataBlock().className $= "energizer")
			return 1;
	}
	return "";
	}

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

/// Standard ion beam
function discharge(%pos,%vec) {
	%p = new SniperProjectile() {
		dataBlock = ShockBeam;
		initialDirection = %vec;
		initialPosition = %pos;
	};
	serverPlay3D(%pos,"IonFireSound");
	createLifeLight(%pos,2,500);
	createLifeEmitter(%pos,ShockFlareEmitter,450);
	return %p;
}

/// Large ion beam
function discharge2(%pos,%vec,%dist,%obj) {
	if (!%dist) {
		%res = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%vec,2000)),$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType,%obj);
		if (%res)
			%hitloc = vectorAdd(getWords(%res,1,3),vectorScale(%dir,-1));
		else
			%hitLoc = vectorAdd(%pos,vectorScale(%vec,2000));
	}
	else
		%hitLoc = vectorAdd(%pos,vectorScale(%vec,%dist));

	zap(0,200,%hitLoc);

	if (getRandom()* 10 < 10 )
		schedule(1000,0,"Serverplay3D",$Ion::ThunderSound[mFloor(getRandom() * $Ion::ThunderSoundCount)],%hitLoc);

//	radiusExplosion(0,vectorAdd(%hitLoc,vectorScale(%vec,-2)),5,0.25,5000,0,$DamageType::Lightning);

	%p = new SniperProjectile() {
		dataBlock = ShockBeam2;
		initialDirection = %vec;
		initialPosition = %pos;
	};
	createLifeLight(%pos,1,500);
	createLifeEmitter(%pos,ShockFlareEmitter2,750);
	Serverplay3D(LightningHitSound,%pos);
	return %p;
}

///Failed Elf beam experiment
function discharge3(%pos,%vec,%target) {
	%pos = vectorAdd(%pos,"0 0 4");
	%vec = vectorMiss(vectorNormalize(vectorSub(pos(%target),%pos)),100);
	%up = vectorCross(%vec,vectorCross("0 0 1",%vec));
	%left = vectorCross(%vec,%up);

	%rot = "0 0 1 3.14";

	%emitter = new StaticShape() {
		position = %pos;
		rotation = %rot;
		datablock = "LightningTarget";
	};

//	echo(%target);

	//ElfProjectile()
	%p = new ElfProjectile() {
		dataBlock = BasicElf;
		initialDirection = %vec;
		initialPosition = %pos;
		sourceObject = %emitter;
		damageFactor = 1;
		sourceSlot = 0;
		targetObject= %target;
	};
//	%p.setEnergyLevel(1);
	%emitter.setRotation(fullRot(%up,%left));
	%emitter.schedule(5000,"delete");
	%p.schedule(5000,"delete");
	return discharge(%pos,%vec);
}

///Adds the beams to the ShockGroup for lag issues.
function addToShock(%obj) {
	%group = nameToID("MissionCleanup/ShockGroup");
	if (%group <= 0) {
		%group = new SimGroup("ShockGroup");
		MissionCleanup.add(%group);
	}
	%group.add(%obj);
}

//Should be the effect that happens to moving objects when zapped.
function GameBase::zapLight(%obj,%time) {
	return "";
	// Aparently only 1 light can be mounted to an object at a time.. odd.
	if (%obj.turret && %disabled) {
		%obj.turret.mountImage("zapLight2",3,false);
		%obj.turret.schedule(%time,"unmountImage",3);
	}
	else if (%obj.client) {
		%obj.mountImage("zapLight2",3,false);
		%obj.schedule(%time,"unmountImage",3);
//		%deplObj.playThread($PowerThread,"Power");
	}
}

function Player::emp(%obj,%time) {
	cancel(%obj.empSched);

	if (isObject(%obj.zap))
		%obj.zap.setTransform(%obj.getTransform());
	else
		%obj.zapObject();
	%obj.setEnergylevel(0);
	if (%time > getSimTime() && (getSimTime() - %time < 240000))
		%obj.empSched = %obj.schedule(510,"emp",%time);
	else {
		%obj.stopZap();
		%obj.zap2Object();
		%obj.schedule(500,"stopZap");
	}
}

///////////////// IonStorm Functions //////////////////

///Creates ionstorm with a beam every %repeat time period for %times times.
function ionStorm(%times,%repeat) {
	cancel($IonStorm);
	ionStormBlast();
	if (%repeat < 1000)
		%repeat = 1000;
	%times = %times - 1;
	if (%times > 0)
		$IonStorm = schedule(%repeat,0,"Ionstorm",%times,%repeat);
}

///A large beam from the air.
function ionStormBlast(%target,%radius) {
//	echo("ionStormBlast");
	if (isObject(%target.player))
		%pos = pos(%target.player);
	else if (isObject(%target))
		%pos = pos(%target);
	else {
		if (getRandom() < $IonStorm::PlayerSeek)
			%pos = pos(randomPlayer());
		else
			%pos = pos(randomDeployable());
	}

	if (%radius)
		%dist = getRandom() * %radius;
	else
		%dist = getRandom() * $IonStorm::Radius;

	%angle = getRandom() * 2 * $Pi;
	%loc = getTerrainHeight2(vectorAdd(mCos(%angle) * %dist SPC mSin(%angle) * %dist SPC "0",%pos));
	createLifeEmitter(%loc,PrebeamEmitter,5000);
	schedule(5000,0,"ionStormBeam",vectorAdd(%loc,"0 0" SPC $IonStorm::Height));
}

///The actual beam
function ionStormBeam(%loc) {
	%p = discharge2(%loc,"0 0 -1");
	%p.setEnergyPercentage(1);
	addToShock(%p);
	%p.schedule(1000,"delete");
}

/////////////////////////////////////////////
////////////////IONMISSILE///////////////////
/////////////////////////////////////////////

datablock ParticleData(IonJetParticle) {
	dragCoefficient      = 0.0;
	gravityCoefficient   = 0;
	inheritedVelFactor   = 0.2;
	constantAcceleration = 0.0;
	lifetimeMS           = 500;
	lifetimeVarianceMS   = 0;
	textureName          = "particleTest";
	colors[0]     = "0.4 0.4 0.9 1.0";
	colors[1]     = "0.5 0.65 1 1";
	sizes[0]      = 1.40;
	sizes[1]      = 1.15;
};

datablock ParticleEmitterData(IonJetEmitter) {
	ejectionPeriodMS = 3;
	periodVarianceMS = 0;
	ejectionVelocity = 6.0;
	velocityVariance = 5.0;
	ejectionOffset   = 0.0;
	overrideAdvances = false;
	thetaMin         = 110;
	thetaMax         = 180;
	phiReferenceVel  = 0;
	phiVariance      = 360;
	particles = "IonJetParticle";
};

datablock SeekerProjectileData(IonMissile):ShoulderMissile {
	hasDamageRadius     = true;
	indirectDamage      = 0.5;
	damageRadius        = 6.0;
	radiusDamageType    = $DamageType::Missile;
	kickBackStrength    = 5;
	explosion           = "ShockExplosion2";
	baseEmitter         = IonJetEmitter;
	delayEmitter        = ELFSparksEmitter;
 
    ImageSource         = "IonLauncherImage";

	lifetimeMS          = 120000;
	muzzleVelocity      = 10.0;
	maxVelocity         = 120.0;
	turningSpeed        = 110.0;
	acceleration        = 110.0;

	hasLight    = true;
	lightRadius = 5.0;
	lightColor  = "0.5 0.5 1";
};

function IonMissile::onExplode(%data,%proj,%pos,%mod) {
	zap(0,200,%pos);
	Parent::onExplode(%data,%proj,%pos,%mod);
}
