Author Topic: Chargeable weapon with changing projectile damage  (Read 3713 times)

I believe SpaceGuy's "Polar star" (or something like that) weapon did this. Basically a weapon that you charge by holding down the fire button and the projectile damage depends on how long you held down the key. The final stage would fire a completely different and the most powerful projectile. I really have no idea how to make this.

This is from SpaceGuy's TF2 Demo Pack, I am sure the code can help you with it by changing the projectile damage.

Code: [Select]
function TF2StickyBombImage::onCharge(%this,%obj,%slot)
{
%time = getSimTime() - %obj.stickyChargeTime;

if(%time >= 200 && !%obj.stickyPlaySound)
{
%obj.stickyPlaySound = 1;
%obj.playAudio(2,TF2StickyBombChargeSound);
}

if(%time >= 4200)
%obj.setImageAmmo(%slot,1);

if(%time >= 200 && isObject(%obj.client))
{
%a = ((%time - 500) > 4000 ? 4000 : (%time - 500));
%b = 4000;
%start = "\c0";
%sep = "\c3";

%done = (%a >= %b) || (%a == 0);
%end = (%a / %b) * 15;
%str = (%a == 0 ? %sep : %start);

for(%i=0;%i<15;%i++)
{
if(%i > %end && !%done)
{
%str = %str @ %sep;
%done = 1;
}
%str = %str @ "|";
}

%obj.client.centerPrint("<br><br><br>" @ %str,0.5);
}
}

function TF2StickyBombImage::onFire(%this,%obj,%slot)
{
%obj.playAudio(2,BowFireSound);

%time = getSimTime() - %obj.stickyChargeTime - 200;
if(%time >= 4000)
%speed = 35;
else
%speed = 15 + 20 * %time / 4000;

if(!isObject(%obj.stickyBombSet))
%obj.stickyBombSet = new SimSet();

while(%obj.stickyBombSet.getCount() >= 8)
{
%oldtime = getSimTime();
%oldest = 0;
for(%i=0;%i<%obj.stickyBombSet.getCount();%i++)
{
%o = %obj.stickyBombSet.getObject(%i);
if(%o.fireTime < %oldtime)
{
%oldtime = %o.fireTime;
%oldest = %i;
}
}
%o = %obj.stickyBombSet.getObject(%oldest);
%o.doBombExplode();
%obj.stickyBombSet.remove(%o);
}

%projectile = %this.projectile;

%vector = %obj.getMuzzleVector(%slot);
%objectVelocity = %obj.getVelocity();
%vector1 = VectorScale(%vector, %speed);
%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
%velocity = VectorAdd(%vector1,%vector2);

%p = new Projectile()
{
dataBlock = %projectile;
initialVelocity = %velocity;
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
fireTime = getSimTime();
};
MissionCleanup.add(%p);
%obj.stickyBombSet.add(%p);
%p.set = %obj.stickyBombSet;

%obj.toolAmmo[%obj.currTool]--;
}

You could check the time of it like

Code: [Select]
function Armor::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
    {
         if(%time >= 4000)
         {
         %damage = %damage * %time / 1000;
Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
         }
    else
    {
    Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
    }
 }

EDIT: Also, Tc572's Power Beam does have a charge too.

Here is the link to his add-on: http://forum.blockland.us/index.php?topic=102880.0
« Last Edit: March 12, 2013, 05:08:27 PM by Advanced Bot »

Interesting. Why armor::damage instead of image::onCharge? Wouldn't it make more sense to use the onCharge since I can call it through the state system? Oh and how do I change the projectile fired? Here's what I have so far.

Code: [Select]
stateName[0] = "Activate";
stateSound[0] = weaponSwitchSound;
stateTimeoutValue[0] = 0.1;
stateTransitionOnTimeout[0] = "Ready";

stateName[1] = "Ready";
stateTransitionOnTriggerDown[1] = "ChargeStart";

stateName[2]                    = "ChargeStart";
stateSound[2] = AudioData_NESL_Charge_Start;
stateTransitionOnTriggerDown[2] = "Charge";

stateName[3]                    = "Charge";
stateScript[3] = "onCharge";
stateSound[3] = AudioData_NESL_Charge_Loop;
stateTimeoutValue[3]            = 5.0;
stateTransitionOnTimeout[3] = "Armed";
stateTransitionOnTriggerUp[3] = "Fire";
stateWaitForTimeout[3] = false;

stateName[4] = "Armed";
stateSound[4] = AudioData_NESL_Charge_Done;
stateTransitionOnTriggerUp[4] = "FireCharged";

stateName[5] = "Fire";
stateFire[5] = true;
stateScript[5] = "onFire";
stateSound[5] = AudioData_NESL_Charge_Shot;
stateTimeoutValue[5] = 0.5;
stateTransitionOnTimeout[5] = "Ready";
stateWaitForTimeout[5] = true;

stateName[6] = "FireCharged"; //How to change the projectile fired?
stateEmitterNode[3] = "emitterNode";
stateEmitterTime[3] = 1.0;
stateEmitter[3] = ParticleData_NESL_Charge_Smoke;
stateFire[6] = true;
stateScript[6] = "onFire";
stateSound[6] = AudioData_NESL_Charge_ShotCharged;
stateTimeoutValue[6] = 3.0;
stateTransitionOnTimeout[6] = "Ready";
stateWaitForTimeout[6] = true;
};

//Armor damage? What?
function Armor::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
{
if(%time < 5000) //Charged less than 5 seconds
{
%damage = %damage * %time / 1000; //If you charge less than 1s you do less dmg
Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
}
else
{
//Fire a different projectile?
}
}

Advanced Bot's script will not work, and only demonstrates a loose grasp on what's needed for your weapon to function properly.

First of all, you'll want to write the weapon's onCharge method. This should store getSimTime() to a variable on the player firing the weapon. Second, you'll want to overwrite the standard onFire (You can find an implementation of this in most weapons that fire with a spread). Your version should first calculate the charge time by subtracting the the value you stored on the player from the current getSimTime(). You can then easily check if it's beyond a threshold and if so, use a different projectile. When creating the projectile, store the charge time on it so you can use it in calculating damage. Finally, you'll overwrite the uncharged projectile's damage method (The standard can be found here.) Your damage method just has to alter the damage done based on the charge time you stored on the projectile earlier.

Give it a go and see what you come up with, and we can go from there.

-stuff-

Here is how to change the projectile, this is from Tc572's Power Beam

Code: [Select]
stateName[0] = "Activate";
stateTimeoutValue[0] = 0.2;
stateTransitionOnTimeout[0] = "Ready";
stateSequence[0] = "ready";
stateSound[0] = BeamswitchSound;

stateName[1] = "Ready";
stateTransitionOnTriggerDown[1] = "FireA";
stateAllowImageChange[1] = true;

stateName[2] = "FireA";
stateTransitionOnTimeout[2] = "FireB";
stateTimeoutValue[2] = 0.15;
stateTransitionOnTriggerUp[2] = "Ready";
stateFire[2] = true;
stateSequence[2] = "fire";
stateScript[2] = "onFire";
stateWaitForTimeout[2] = true;
stateAllowImageChange[2] = false;
stateSound[2] = BeamFireSound;

stateName[3] = "FireB";
stateTransitionOnTimeout[3] = "FireC";
stateTimeoutValue[3] = 0.1;
stateTransitionOnTriggerUp[3] = "Ready";
stateFire[3] = true;
stateSequence[3] = "fire";
stateScript[3] = "onFire";
stateWaitForTimeout[3] = true;
stateAllowImageChange[3] = false;
stateSound[3] = BeamFireSound;

stateName[4] = "FireC";
stateTransitionOnTimeout[4] = "ChargeA";
stateTimeoutValue[4] = 0.1;
StateTransitionOnTriggerUp[4] = "Ready";
stateFire[4] = true;
stateSequence[4] = "fire";
stateScript[4] = "onFire";
stateWaitForTimeout[4] = true;
stateAllowImageChange[4] = false;
stateSound[4] = BeamFireSound;

stateName[5] = "ChargeA";
stateTransitionOnTimeout[5] = "ChargeB";
stateScript[5] = "onCharge";
stateTimeoutValue[5] = 0.6;
stateWaitForTimeout[5] = False;
stateTransitionOnTriggerUp[5] = "AbortCharge";
stateAllowImageChange[5] = false;
stateSound[5] = BeamchargestartAsound;

stateName[6] = "ChargeB";
stateTransitionOnTimeout[6] = "Armed";
stateScript[6] = "onCharge";
stateTimeoutValue[6] = 0.7;
stateWaitForTimeout[6] = False;
stateTransitionOnTriggerUp[6] = "AbortCharge";
stateAllowImageChange[6] = false;
stateSound[6] = BeamchargestartBsound;

stateName[7] = "AbortCharge";
stateTransitionOnTimeout[7] = "Ready";
stateTimeoutValue[7] = 0.1;
stateWaitForTimeout[7] = true;
stateScript[7] = "onAbortCharge";
stateFire[7] = true;
stateSequence[7] = "fire";
stateScript[7] = "onFire";
stateAllowImageChange[7] = false;
stateSound[7] = BeamFireSound;

stateName[8] = "Armed";
stateTransitionOnTriggerUp[8] = "Firecharge";
stateSequence[8] = "Armed";
stateAllowImageChange[8] = false;
stateSound[8] = Chargeloop;

stateName[9] = "Firecharge";
stateTransitionOnTimeout[9] = "Ready";
stateTimeoutValue[9] = 0.4;
stateSequence[9] = "fire";
stateScript[9] = "onFireCharged";
stateWaitForTimeout[9] = true;
stateAllowImageChange[9] = false;
stateSound[9] = ChargebeamfireSound;

};

function BeamImage::onFireCharged(%this,%obj,%slot) //This is what makes it shoot charged projectiles.
{
%projectile = BeamChargedProjectile; //<-- This is where you can change the projectile
%spread = 0.00;
%shellcount = 1;
for(%shell=0; %shell<%shellcount; %shell++)
{
%vector = %obj.getMuzzleVector(%slot);
%objectVelocity = %obj.getVelocity();
%vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
%velocity = VectorAdd(%vector1,%vector2);
%x = (getRandom() - 0.5) * 2 * 3.1415926 * %spread;
%y = (getRandom() - 0.5) * 2 * 3.1415926 * %spread;
%z = (getRandom() - 0.5) * 2 * 3.1415926 * %spread;
%mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z);
%velocity = MatrixMulVector(%mat, %velocity);
%p = new (%this.projectileType)()
{
dataBlock = %projectile;
initialVelocity = %velocity;
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
}
return %p;
}

Try looking at the butterfly knife

This is as far as I got.
Code: [Select]
stateName[0] = "Activate";
stateSound[0] = weaponSwitchSound;
stateTimeoutValue[0] = 0.1;
stateTransitionOnTimeout[0] = "Ready";
stateWaitForTimeout[0] = true;

stateName[1] = "Ready";
stateTransitionOnTriggerDown[1] = "onPreFire";

stateName[2]                    = "Charge";
stateScript[2] = "onCharge";
stateSound[2] = AudioData_NESL_Charge_Loop;
stateTimeoutValue[2]            = 5.0;
stateTransitionOnTimeout[2] = "Armed";
stateTransitionOnTriggerUp[2] = "Fire";
stateWaitForTimeout[2] = false;

stateName[3] = "Armed";
stateSound[3] = AudioData_NESL_Charge_Done;
stateTransitionOnTriggerUp[3] = "FireCharged";

stateName[4] = "Fire";
stateFire[4] = true;
stateScript[4] = "onFire";
stateSound[4] = AudioData_NESL_Charge_Shot;
stateTimeoutValue[4] = 0.5;
stateTransitionOnTimeout[4] = "Ready";
stateWaitForTimeout[4] = true;

stateName[5] = "FireCharged";
stateEmitterNode[3] = "emitterNode";
stateEmitterTime[3] = 1.0;
stateEmitter[3] = ParticleData_NESL_Charge_Smoke;
stateFire[5] = true;
stateScript[5] = "onFireCharged";
stateSound[5] = AudioData_NESL_Charge_ShotCharged;
stateTimeoutValue[5] = 3.0;
stateTransitionOnTimeout[5] = "Ready";
stateWaitForTimeout[5] = true;
};

function ImageData_NESL_Charge::onCharge(%this,%obj,%slot)
{
%obj.NESL_Charge_Time = getSimTime();
}

function ImageData_NESL_Charge::onFire(%this,%obj,%slot)
{
%time = getSimTime() - %obj.NESL_Charge_Time;

if(%time < 5000) //Charged less than 5 seconds
{
%damage = %damage * %time / 1000; //If you charge less than 1s you do less dmg
Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
}
else
{
//How do I call the onFireCharged funtion here?
}
}

function ImageData_NESL_Charge::onFireCharged(%this,%obj,%slot)
{
%projectile = ProjectileData_NESL_Charge_Charged;
}

You don't actually need an onFireCharged; you can change the projectile in the onFire method. The onFireCharged method Advanced Bot provided is a good starting point for your onFire method, just add an if statement to determine %projectile, and add the time to the projectile definition.

Try looking at the butterfly knife
The knife is like the spear, we could want a charged spear animation.

The knife is like the spear, we could want a charged spear animation.
The knife shoots a low damage projectile when released while charging and a high damage projectile when released after charging



Code: [Select]
function ImageData_NESL_Charge::onCharge(%this,%obj,%slot)
{
%obj.NESL_Charge_Time = getSimTime();
}

function ImageData_NESL_Charge::onFire(%this,%obj,%slot)
{
%time = getSimTime() - %obj.NESL_Charge_Time;

if(%time < 5000) //Charged less than 5 seconds
{
%directDamage = %directDamage * %time / 1000; //If you charge less than 1s you do less dmg
%damageType = $DamageType::DmgType_NESL_Charge;
Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
}
else
{
echo("This shouldn't be possible"); //After 5s the image state changes
}
}

function ImageData_NESL_Charge::onFireCharged(%this,%obj,%slot)
{
%projectile = ProjectileData_NESL_Charge_Charged;
}

[img width=300]http://i3.kym-cdn.com/photos/images/original/000/234/765/b7e.jpg[/img]

Code: [Select]
function ImageData_NESL_Charge::onCharge(%this,%obj,%slot)
{
%obj.NESL_Charge_Time = getSimTime();
}

function ImageData_NESL_Charge::onFire(%this,%obj,%slot)
{
%time = getSimTime() - %obj.NESL_Charge_Time;

if(%time < 5000) //Charged less than 5 seconds
{
%directDamage = %directDamage * %time / 1000; //If you charge less than 1s you do less dmg
%damageType = $DamageType::DmgType_NESL_Charge;
Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
}
else
{
echo("This shouldn't be possible"); //After 5s the image state changes
}
}

function ImageData_NESL_Charge::onFireCharged(%this,%obj,%slot)
{
%projectile = ProjectileData_NESL_Charge_Charged;
}
Well for starters you're building the Damage function inside the onFire function. One thing at a time. Start by copying the body of an onFire method out of a weapon with projectile spread, which all have to overwrite it. Paste it as the contents of your onFire method. onCharge is correct, as is how you check the charge time later on, so hang on to that bit.

I'm pretty much just copypasting at this point. I have no clue what any of the things in the functions do.

datablock ProjectileData(ProjectileData_NESL_Charge_Ch arged)
{
   className = "ProjectileData";

   uiName = "NES Super Charge Laser - Charged";
   projectileShapeName = "base/data/shapes/empty.dts";
   scale = "1 1 1";

   lifetime = 2000;
   fadeDelay = 2000;
   armingDelay = 0;

   particleEmitter = EmitterData_NESL_Charge_Proje ctile_Charged;
   particleWaterEmitter = "";
   Explosion = ExplosionData_NESL_Charge_Pro jectile_Charged;
   waterExplosion = "";

   muzzleVelocity = 100;
   velInheritFactor = 1.0;
   isBallistic = false;
   gravityMod = 0;

   directDamage = 250;
   directDamageType = $DamageType::DmgType_NESL_Charge;
   radiusDamageType = $DamageType::DmgType_NESL_ChargeRad;

   impactImpulse = 0;
   verticalImpulse = 0;

   brickExplosionImpact = false;
   brickExplosionRadius = 0.0;
   brickExplosionForce = 0;
   brickExplosionMaxVolume = 0;
   brickExplosionMaxVolumeFloati ng = 0;

   collideWithPlayers = true;
   explodeOnPlayerImpact = true;
   explodeOnDeath = false;

   hasLight = true;
   lightRadius = 3.0;
   lightColor = "1.0 0.1 0.0 1.0";
   hasWaterLight = false;
   waterLightColor = "0.0 0.0 0.0 0.0";

   bounceAngle = 0;
   bounceElasticity = 0;
   bounceFriction = 0;
   minStickVelocity = 0;
};

datablock ShapeBaseImageData(ImageData_NESL_Charge)
{
   className = "WeaponImage";
   emap = false;
   cloakable = false;

   shapeFile = "./charge.dts";
   Projectile = ProjectileData_NESL_Charge;
   projectileType = Projectile;

   Item = ItemData_NESL_Charge;
   armReady = true;
   melee = false;
   firstPersonParticles = true;

   mountPoint = 0;
   offset = "0 0 0";
   rotation = "1 0 0 0";
   eyeOffset = "0 0 0";
   eyeRotation = "1 0 0 0";
   correctMuzzleVector = true;
   accuFire = false;

   doColorShift = true;
   colorShiftColor = ItemData_NESL_Charge.colorShi ftColor;

   ammo = "";
   casing = "";
   shellExitDir = "0 0 0";
   shellExitVariance = 0;
   shellVelocity = 0;
   shellExitOffset = "0 0 0";

   lightType = "NoLight";
   lightColor = "0.0 0.0 0.0 0.0";
   LightTime = 1.0;
   lightRadius = 1.0;

   stateName[0]            = "Activate";
   stateSound[0]            = weaponSwitchSound;
   stateTimeoutValue[0]      = 0.1;
   stateTransitionOnTimeout[0]   = "Ready";
   stateWaitForTimeout[0]      = true;

   stateName[1]               = "Ready";
   stateTransitionOnTriggerDown[1]   = "onPreFire";   

   stateName[2]                    = "Charge";
   stateScript[2]               = "onCharge";
   stateSound[2]               = AudioData_NESL_Charge_Loop;
   stateTimeoutValue[2]            = 5.0;
   stateTransitionOnTimeout[2]      = "Armed";
   stateTransitionOnTriggerUp[2]   = "Fire";
   stateWaitForTimeout[2]         = false;

   stateName[3]               = "Armed";
   stateSound[3]               = AudioData_NESL_Charge_Done;
   stateTransitionOnTriggerUp[3]   = "FireCharged";

   stateName[4]            = "Fire";
   stateFire[4]            = true;
   stateScript[4]            = "onFire";
   stateSound[4]            = AudioData_NESL_Charge_Shot;
   stateTimeoutValue[4]      = 0.5;
   stateTransitionOnTimeout[4]   = "Ready";
   stateWaitForTimeout[4]      = true;

   stateName[5]            = "FireCharged";
   stateEmitterNode[3]         = "emitterNode";
   stateEmitterTime[3]         = 1.0;
   stateEmitter[3]            = ParticleData_NESL_Charge_Smok e;
   stateFire[5]            = true;
   stateScript[5]            = "onFireCharged";
   stateSound[5]            = AudioData_NESL_Charge_ShotCha rged;
   stateTimeoutValue[5]      = 3.0;
   stateTransitionOnTimeout[5]   = "Ready";
   stateWaitForTimeout[5]      = true;
};

function ImageData_NESL_Charge::onCharge(%this,%obj,%slot)
{
   %obj.NESL_Charge_Time = getSimTime();
}

function ImageData_NESL_Charg::onFire(%this,%obj,%slot) //Why do I need an onFire in the first place?
{
   %projectile = %this.projectile;
   %spread = 0.0;
   %shellcount = 1; //What is this?

   for(%shell=0; %shell<%shellcount; %shell++)
   {
      %vector = %obj.getMuzzleVector(%slot);
      %objectVelocity = %obj.getVelocity();
      %vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
      %vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
      %velocity = VectorAdd(%vector1,%vector2);
      //%x = (getRandom() - 0.5) * 10 * 3.1415926 * %spread;
      %x = 0.0;
      %y = 0.0;
      %z = 0.0;
      %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z);
      %velocity = MatrixMulVector(%mat, %velocity);

      %p = new (%this.projectileType)()
      {
         dataBlock = %projectile;
         initialVelocity = %velocity;
         initialPosition = %obj.getMuzzlePoint(%slot);
         sourceObject = %obj;
         sourceSlot = %slot;
         client = %obj.client;
      };
      MissionCleanup.add(%p);
   }
   return %p;
}

function ProjectileData_NESL_Charge::damage(%this,%obj,%col,%fade,%pos,%normal)
{
   %time = getSimTime() - %obj.NESL_Charge_Time;

   if(%this.directDamage <= 0)
      return;

   %damageType = $DamageType::DmgType_NESL_Charge;;
   if(%this.DirectDamageType)
      %damageType = %this.DirectDamageType;

   %scale = getWord(%obj.getScale(), 2);
   %directDamage = mClampF(%this.directDamage, -100, 100) * %scale;

   if(%col.getType() & $TypeMasks::PlayerObjectType)
   {
      %col.damage(%obj, %pos, %directDamage, %damageType);
   }
   else
   {
      %col.damage(%obj, %pos, %directDamage, %damageType);
   }
   
   if(%time < 5000) //Charged less than 5 seconds
   {
      %directDamage = %directDamage * %time / 1000; //If you charge less than 1s you do less dmg
      %damageType = $DamageType::DmgType_NESL_Charge;
   }
   else
   {
      echo("This shouldn't be possible");
   }

   Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
}

function ImageData_NESL_Charge::onFireCharged(%this,%obj,%slot)
{
   %projectile = ProjectileData_NESL_Charge_Ch arged;
}

I'm pretty much just copypasting at this point. I have no clue what any of the things in the functions do.

Code: [Select]
function ImageData_NESL_Charge::onCharge(%this,%obj,%slot)
{
%obj.NESL_Charge_Time = getSimTime();
}

function ImageData_NESL_Charg::onFire(%this,%obj,%slot) //Why do I need an onFire in the first place?
{
%projectile = %this.projectile;
%spread = 0.0;
%shellcount = 1; //What is this?

for(%shell=0; %shell<%shellcount; %shell++)
{
%vector = %obj.getMuzzleVector(%slot);
%objectVelocity = %obj.getVelocity();
%vector1 = VectorScale(%vector, %projectile.muzzleVelocity);
%vector2 = VectorScale(%objectVelocity, %projectile.velInheritFactor);
%velocity = VectorAdd(%vector1,%vector2);
//%x = (getRandom() - 0.5) * 10 * 3.1415926 * %spread;
%x = 0.0;
%y = 0.0;
%z = 0.0;
%mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z);
%velocity = MatrixMulVector(%mat, %velocity);

%p = new (%this.projectileType)()
{
dataBlock = %projectile;
initialVelocity = %velocity;
initialPosition = %obj.getMuzzlePoint(%slot);
sourceObject = %obj;
sourceSlot = %slot;
client = %obj.client;
};
MissionCleanup.add(%p);
}
return %p;
}

function ProjectileData_NESL_Charge::damage(%this,%obj,%col,%fade,%pos,%normal)
{
%time = getSimTime() - %obj.NESL_Charge_Time;

if(%this.directDamage <= 0)
return;

%damageType = $DamageType::DmgType_NESL_Charge;;
if(%this.DirectDamageType)
%damageType = %this.DirectDamageType;

%scale = getWord(%obj.getScale(), 2);
%directDamage = mClampF(%this.directDamage, -100, 100) * %scale;

if(%col.getType() & $TypeMasks::PlayerObjectType)
{
%col.damage(%obj, %pos, %directDamage, %damageType);
}
else
{
%col.damage(%obj, %pos, %directDamage, %damageType);
}

if(%time < 5000) //Charged less than 5 seconds
{
%directDamage = %directDamage * %time / 1000; //If you charge less than 1s you do less dmg
%damageType = $DamageType::DmgType_NESL_Charge;
}
else
{
echo("This shouldn't be possible");
}

Parent::Damage(%this, %obj, %sourceObject, %position, %damage, %damageType);
}

function ImageData_NESL_Charge::onFireCharged(%this,%obj,%slot)
{
%projectile = ProjectileData_NESL_Charge_Charged;
}
You're making good progress, and at least you're trying to learn here. To answer your questions in the comments, you need an onFire so you can store the charge time on the projectile, so you can later check it when doing damage. The %shellCount is used in shotgun style weapons that fire multiple projectiles. You can see directly below it there's a loop, that contains all the actual projectile creation stuff, set to run %shellCount times. You can actually cut this stuff out, but it's not that big of a deal.

Now then, cut out your "%time = getSimTime() - %obj.NESL_Charge_Time;" from the damage function, and place it above those variables at the top of the onFire - at the very top of the method. Then what you do is add a new line to the projectile creation stuff (starting after "%p = new (%this.projectileType)()"), where we'll store %time on the projectile. Write this just like all the rest of the lines in that block, setting a new variable to %time. On to the damage function now.

First of all, you got an extra semicolon at the end of your %damageType line. Second - there's a line in the default damage function that actually forgets you over if you're trying to have a weapon do more than 100 damage: "%directDamage = mClampF(%this.directDamage, -100, 100) * %scale;" I have no idea why this was included, but replace it with "%directDamage = %this.directDamage * %scale;"

Now then, you'll want to retrieve your value for %time. In the context of the damage method, %obj is the projectile itself, so at the top of the method, set %time to %obj.something, where something is the name of the value you stored it to in the projectile creation above. You can now scale direct damage by %time. You have this written inside that if-else block, but as you noted, it shouldn't be possible to have one of the two conditions for it, for the block. Additionally, you already have %damageType set in place above, so you can scrap that line as well as if statement and else block. This leaves one line, the one where you actually scale the damage. Take it, and put it directly under the one we were working on a bit ago, where it gets scaled by the size of the projectile (%scale).

Finally, you'll want to remove the Parent::Damage line, otherwise you'll be calling the default damage function in addition to your own.

Last thing we need to handle is the charged projectile. There's actually a really easy way we can do that - and you sort of have the idea in place already in your onFireCharged. Look at the first onFire method, and note at the top, just after determining %time, it defines %projectile. You can use an if statement to define that conditionally. If time is less than 5000, define it just as it is now, but if it is greater than 5000, set it to your charged projectile. Then you can remove the onFireCharged method, and go back to your image datablock and set stateScript[5] to your single onFire method.

This will cause it to fire the charged projectile, but it looks like it's set to do 250 damage, which as we pointed out earlier, is forgeted over by the default damage implementation. You can fix this just by defining a damage method for your charged projectile, copying the same default damage script you put in the previous one, then doing that same line replacement of the mClampF I mentioned earlier.

Anyway, there's my step-by-step guide. Follow it as best you can and we'll see how it looks afterwards. If you have any more questions, ask.