UE2 - UT2kX Damage Over Time

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
Yes, that's the idea. The function should accept none. Try harcoding values to see if the Actor works as intended. If it works, figure out how to fix your variables.

Code:
Owner.TakeDamage(5, None, , , 'Poisoned');

I've also led you into error by forgetting to put a few commas. The input variables should always correspond with the parameters in the prototype.

For example the following won't compile because there is a comma missing after HitLocation and the compiler will assume that 'SomeDamageType' is the value given for Momentum.

Code:
// Prototype: TakeDamage( int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, name DamageType );
TakeDamage(50, aPawn, vect(0,0,0), 'SomeDamageType' );

Since 'SomeDamageType' isn't a vector, this will produce an error. To avoid this, either put a value or variable for each parameter or enter a space where the parameter should go, like so :

Code:
// Prototype: TakeDamage( int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, name DamageType );
TakeDamage(50, aPawn, vect(0,0,0), , 'SomeDamageType' );
 
Last edited:

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
It still doesnt seem to like the DamageType parameter. I've set it like this as you said
Code:
Owner.TakeDamage(50, None, vect(0,0,0), , 'DamTypeDart' );
and set a variable like this:
Code:
var class<DamageType> DamTypeDart;

also tried

var class<DamTypeDart> DamTypeDart;
but it still doesnt seem to like it. Is there some other type of var class it should be? Tried var() class .... too but it doesnt like that either. I've defined it in the defaultproperties too. Thanks again for your help
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
DamageType is of type name as can be seen in the function prototype/signature :
// Prototype: TakeDamage( int Damage, Pawn InstigatedBy, Vector HitLocation, Vector Momentum, name DamageType );

Names are specific to unrealscript. Set the name variable in defaultproperties or directly in your code. There's an example in the code of the original weapon.
 
Last edited:
Apr 11, 2006
738
0
16
It still doesnt seem to like the DamageType parameter. I've set it like this as you said
Code:
Owner.TakeDamage(50, None, vect(0,0,0), , 'DamTypeDart' );
and set a variable like this:
Code:
var class<DamageType> DamTypeDart;

also tried

var class<DamTypeDart> DamTypeDart;
but it still doesnt seem to like it. Is there some other type of var class it should be? Tried var() class .... too but it doesnt like that either. I've defined it in the defaultproperties too. Thanks again for your help

If you define a variable of type class<DamageType>, then when you refer to that variable name in the TakeDamage function, do not put it in quotation marks.

ex.

var class<DamageType> MyDamageType;
...
TakeDamage(...,MyDamageType);
...
defaultproperties{ MyDamageType=class'DamTypeWhatever'}
 

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
Ok i've corrected that bit, but for some reason it kept coming up with an error, i had a look and realized it was complaining at parameter 4, which is where the Momentum value goes, not the DamageType. It seems to be that it wont accept a blank for that spot. On the ClassicSniperFire they have Momentum*X but since it has no idea what the X is im guessing it wont take that. Heres the code if you need it, nothing has changed really:
Code:
Owner.TakeDamage(20, None, vect(0,0,0), , DamTypeDart);
 

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
Ok i simply put vect(0,0,0) for the momentum and that has corrected the problem. Just working on a few mishaps in the code

EDIT: Ok i've fixed everything here, and the UCC likes it apart from one thing. The Owner.ClientMessage isnt recognized. I had a look at PlayerController events and theres one that says
Code:
event ClientMessage(coerce string S, optional name Type)
but if i try that in the function Spawned()code it says ClientMessage has no effect. I tried a few others, but they said they all had no effects which is strange. Not sure if thats the right page to be looking under though

Lastly the compiled version isnt damaging the owner of it, when fired it spawns a visible (so i know that part of the code is working) actor but does not damage the owner of it. Would Parent.TakeDamage work? Not sure that the Owner part is working.
 
Last edited:

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
Maybe Owner needs to be cast to Pawn. Try Pawn(Owner) to see if ClientMessage works.
 
Last edited:

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
Not sure if Pawn(Other) would work because the message is under DartPoison not the Firing class, and Other isnt defined as the person who was just hit. Perhaps i should put it right next to the Spawn command?
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
Sorry, it's late and I meant Owner. The damage should be coming from your custom Actor. Good night.
 
Last edited:

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
Sorry didnt mean to upset you Azura, I'll try that. Thanks again for your help

EDIT: Yup that did the trick Azura! Thanks
 
Last edited:

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
Success! Everything is working as intended now. The message, damage, and firing is all working! Thanks so much Azura, you've been a great help. Heres the source code if anybody has been following along and wondering:
Code:
//Phoenix's Poison Dart Damage Actor
class DartPoison extends Actor;

var() class<DamageType> DamTypeDart;
var int DamageNum;

simulated function PostBeginPlay()
{
   SetTimer(1.5, true);
   Pawn(Owner).ClientMessage( "You have been hit by a poison dart!" );
}

simulated function Timer()
{
    Pawn(Owner).TakeDamage(20,Instigator,Location,vect(0,0,0),DamTypeDart);
	if (DamageNum >= 5)
    {
        Destroy();
    }
    DamageNum++;
}



defaultproperties
{
   DamTypeDart=Class'DG.DamTypeDart'
   bHidden=true
}
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
Sorry didnt mean to upset you Azura, I'll try that. Thanks again for your help

EDIT: Yup that did the trick Azura! Thanks

Not upset. Just completely drained after going to class until 19h00. Happy to hear it works :D .

P.S: If you want to make sure it produces no errors, you can check if Owner is different from none and that it is a Pawn ( Owner.IsA( 'Pawn' ) ).
 
Last edited:

War_Master

Member
May 27, 2005
702
0
16
Here's the source code of the FlameThrower weapon from the ExcessiveOverkill302 mod as I suggested you to look at long ago. This one works flawlessly and can be used as anything like a poison dart as well. And yes, the code is way different than yours plus, it doesnt set the damage actor to be owned by the player getting hit because the Owner of the damage actor should be the Instigator at all time.

This should be the projectile that hits the enemy which spawns the Timed damage actor. Read the script and take out what you dont need like the effects:
Code:
class FTprojEX extends Projectile;

var xEmitter Trail;
var xEmitter BFlame2, BFlame3, BFlame4;
var Emitter BFlame;
var byte Bounces;
var float DamageAtten;
var() Vector        Addlocation,AddlocationB;
replication
{
    reliable if (bNetInitial && Role == ROLE_Authority)
        Bounces, BFlame, BFlame2;
}

simulated function Destroyed()
{
    if (Trail !=None) Trail.mRegen=False;
	Super.Destroyed();
}

simulated function PostBeginPlay()
{
    local float r;
    
    if ( Level.NetMode != NM_DedicatedServer )
    {
	
        if ( !PhysicsVolume.bWaterVolume )
        {
      	    BFlame=spawn(class'FlameEmitterEX',self);
		    BFlame.SetBase(self);
		    BFlame2=spawn(class'FTmuzzleFlash',self);
			BFlame2.SetRelativeLocation(AddLocationB);	  
        }
    }
  
    Velocity = Vector(Rotation) * (Speed);
    if (PhysicsVolume.bWaterVolume)
        Velocity *= 0.65;

    r = FRand();
    if (r > 0.65)
    {
		bdynamicLight=True;
    }
    if (r > 0.25)
        Bounces = 1;
    else
        Bounces = 0;

    Super.PostBeginPlay();
    
	if ( Instigator != None )
		InstigatorController = Instigator.Controller;
}

simulated function ProcessTouch (Actor Other, vector HitLocation)
{	
	local Vector X, RefNormal, RefDir;
	local bool bisBlocked;

	bisBlocked=False;
	
	if (Other == Instigator) return;
	
	if (Other == Owner) return;

	if (Other.IsA('xPawn') && xPawn(Other).CheckReflect(HitLocation, RefNormal, 0))
	{
		if (Role == ROLE_Authority)
		{
	            X = Normal(Velocity);
		    RefDir = X - 2.0*RefNormal*(X dot RefNormal);
	            RefDir = RefNormal;
		    Spawn(Class, Other,, HitLocation+RefDir*20, Rotator(RefDir));
		    bisBlocked=True;
		}
	        Destroy();
	}
    
	else if ( (FTProjEX(Other) == None) && (Other != Instigator) )
	{
		if (Other != Instigator && Other.isa('Pawn') && !bisBlocked)
		{
			 FryGuy(Pawn(Other),HitLocation);
		}
	
		if ( Other.isa('Vehicle') )
			damage=damage*6;
		
		if ( Role == ROLE_Authority )
			HurtRadius(damage, 220, MyDamageType, MomentumTransfer, HitLocation);
	        Destroy();
	}
}

simulated function FryGuy(Pawn Other, vector hitlocation)
{
	local Flamer SFire, otherfire;
	foreach AllActors(class'Flamer',otherfire)
		if(otherfire.Base == other)
		{
			return;
		}
	SFire=spawn(class'Flamer', Other,,-hitlocation,);
	SFire.ToTorch(other,hitlocation);
}
simulated function Landed( Vector HitNormal )
{
    SetPhysics(PHYS_None);
    LifeSpan = 1.0;
    Explode(Location,HitNormal);
}

simulated function HitWall( vector HitNormal, actor Wall )
{
    local float OldLifeSpan;

    if ( BFlame2 != None )
    {
	BFlame2.mRegen=False;
	BFlame2.Destroy();
    }

    if ( BFlame3 != None )
    {
	OldLifeSpan=bFlame.LifeSpan;
	BFlame3.Destroy();
    }

    if ( (Mover(Wall) != None) && Mover(Wall).bDamageTriggered )
    {
        if ( Level.NetMode != NM_Client )
            Wall.TakeDamage( Damage, instigator, Location,
MomentumTransfer * Normal(Velocity), MyDamageType);
        Destroy();
        return;
    }

    SetPhysics(PHYS_Falling);
    
    if ( Bounces > 0 )
    {
	if ( !PhysicsVolume.bWaterVolume )
	{
		Velocity = 0.25 * (Velocity - 1.5*HitNormal*(Velocity dot HitNormal));
		SetRotation(Rotation*-1);
		Bounces = Bounces - 1;
		BFlame4=spawn(class'FlameThrowerFlames',self);
		bFlame4.SetRelativeLocation(AddLocation);
		bFlame4.LifeSpan=OldLifeSpan;
		 if ( BFlame != None )
		      bFlame.destroy();

	}
        return;
    }

    bBounce = false;
    
    if (Trail != None) 
        Trail.mRegen=False;
    
    if ( BFlame != None )
      bFlame.destroy();// = false;//BFlame.mregen = false;
    
    if ( BFlame4 != None )
	BFlame4.mRegen=False;
    
    Explode(Location, HitNormal);
	
}

simulated function Explode(vector HitLocation, vector HitNormal) 
{
    if ( EffectIsRelevant(Location,false) )
    {
		if ( (ExplosionDecal != None) && (Level.NetMode != NM_DedicatedServer) )
			Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal));
    }
    DelayedHurtRadius(Damage, DamageRadius, MyDamageType, MomentumTransfer, HitLocation );
	Destroy(); 
}
simulated function PhysicsVolumeChange( PhysicsVolume Volume )
{
    if (Volume.bWaterVolume)
    {
		if ( BFlame!=None )
			BFlame.Destroy();
		if ( BFlame2!=None )
			BFlame2.Destroy();
		if ( Trail != None )
				Trail.mRegen=False;

		spawn(class'FTWaterSteamEX',,,Location,);
		Velocity *= 0.05;
    }
}


This is the actual damage actor that will do damage over time until its Lifespan expires and is the crucial script that you need. This should work for anything using variables to be set by the projectile:
Code:
class Flamer extends Actor;

var pawn Other;
var hitFlameBig BFlame;
var int burnDamage;

replication
{
	// The server replicates these variables to the client.
	reliable if( Role == ROLE_Authority )
		bFlame;
}

function ToTorch(actor toTorch, optional vector hitlocation)
{
	Other=Pawn(toTorch);
	
	if ( Other == None )
		return;
	
	setBase(Other);
	if(hitlocation != vect(0,0,0))
		setLocation(hitlocation);
	gotostate('FlameOn');
}

state FlameOn
{
	simulated function Beginstate()
	{
		if ( Other != None )
		{
			BFlame=spawn(class'HitFlameBig',,,,);
			if ( BFlame != None )
			{
				BFlame.Setbase(Other);
				Bflame.Default.LifeSpan=lifespan;
			}
			SetTimer(0.5,True);
		}
	}
	simulated function timer()
	{
		if ( BFlame == None )
			destroy();

		if ( Role == Role_Authority && 
			Other != None && Instigator != None && 
			( ShieldGunEX(Other.Weapon) == None ||
!ShieldGunEX(Other.Weapon).ShieldOn()) )
		{
		     if ( Level.NetMode != NM_Client )
			    Other.TakeDamage(burnDamage, Instigator, Other.location,
vect(0,0,0),class'ExcessiveV302.DamTypeFlameThrowerEX');
		}
		else
		{
		    if ( BFlame != None )
		    {
				Bflame.lifespan = 1.0;
				Bflame.mRegenRange[0]=0;
         		BFlame.mRegenRange[1]=0;
		    }
		}
	}
}


I hope this helps since all it took was a quick and small download of a very popular weapon mod for UT2k4. And btw, this is the best looking flamethrower effect I've seen in the UT games besides the U2 one.
 
Last edited:

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
I had a look at the Excessive version 302, not the overkill one though and it was a lot more complicated than i wanted it to be. I like coding from scratch as i learn more that way then just copying and pasting and changing a few things. That was kind of you to post that code up and i will have a look at it. Simple is the way to go, as i can spawn the effects from the damage actor without it being very long code like the flamethrower.
Thanks again for that War Master, i appreciate it.

-Phoenix
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
The FlameThrower code War_Master posted will be helpful reference if you need some clientside effects. For example a pulsating green filter could appear on the HUD of the player each time the poison does damage. This requires replication which is Epic's way of handling what happens on the server and/or the client in network games.

As a sidenote, it should be possible to reduce the health of a player (aPawn.Health) directly without going through the TakeDamage() function (which probably triggers a hurt sound and red veil in the HUD). This could be used to create a slow and constant drain on health during the lifespan of the poison dart.
 
Last edited:

Phoenix_Wing

Official Kantham Stalker
Mar 28, 2008
386
0
0
California
Yup the TakeDamage() function does cause a hurt sound and red veil, but i think because i haven't defined some hit thing it does cause visual glitches where theres a red line going crazy across the screen. Wouldn't setting the health value of the player make it go down to the value instantly? Or have the timer event trigger every 0.2 seconds for 3 damage? That would look a lot nicer actually. Might add in a little green pulse to the HUD and 3rd person view. Right now its kind of strange actually :D
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
Yup the TakeDamage() function does cause a hurt sound and red veil, but i think because i haven't defined some hit thing it does cause visual glitches where theres a red line going crazy across the screen.

No idea if that's hardcoded or if it can be changed through parameters.

Wouldn't setting the health value of the player make it go down to the value instantly? Or have the timer event trigger every 0.2 seconds for 3 damage? That would look a lot nicer actually. Might add in a little green pulse to the HUD and 3rd person view. Right now its kind of strange actually :D

Yes, it would but you could decrease health one unit at a time without the effect that TakeDamage() has. Or you could do a sort of combination where there is a slow decrease in health and the player registering "hurt" every X units. Timers are best for things that happen after a certain period of time but it is also possible to use the Tick() probe function which is pratically real time or Postrender in the case of a HUD mutator.

Adding some visuals will require some replication and changing what's displayed on the HUD of a player. It's best if you research this on unrealwiki before attempting some changes.
 
Last edited:
Apr 11, 2006
738
0
16
Adding an icon to the player's screen that shows up may be kind of tricky, but can be done.

If you decrease health simply by subtracting health, without using TakeDamage(), and the player is in god mode, they can reach negative health (at which point they become glitched and IIRC are unable to move, but also can't die).

Naturally you can simply check to make sure the damage won't set the player to the negatives first. You can control when the red "pain" flashes occur on the player by using the function NotifyTakeHit.
 

War_Master

Member
May 27, 2005
702
0
16
As Wail of Suicide mentioned, decreasing health can be glitchy and can be prevented by checking if the enemy has more than 0 health in order to prevent the TakeDamage() function since it does damage on shields and armors to prevent damage on the owner.

As for the flashy effect when the timer does damage on you, that depends on the DamageType you're using and since that one was from a flamethrower it is red of course. Check the DamageType from the BioRifle and see how it handles the Green flashy effect when you get hurt by it, I think it is a simple variable in it anyway.

On that script, the timer will tell the actor the delay of each time it should do the damage. If you want to calculate to do 5 hits max, just multiply what is in the SetTimer(0.5,True) by 5 which gives you a 2.5 value, this should be the Lifespan of the actor doing the damage. If you want the hits to be every 0.2 seconds and be a total of 3 hits, the Lifespan of the actor should be 0.6.

I like that script because is pretty much flawless and it doesnt damage the player after he dies because it checks to destroy() itself when the player dies (a mistake that many others have made). There's also the ChaosUT2 poisoning dart projectile which is exactly what you want to do and I recommend to check that one out so your dart works correctly.

Btw, I added the flame projectile script so you see how it sets the damage actor into the player. You don't need all the other code or effects since all you need to check is if what it hits is a Pawn and nothing else.
 

War_Master

Member
May 27, 2005
702
0
16
I forgot to mention, there's also a way to stop the damage actor when a player picks a health or vial pick-up. I remember seeing one that worked like that in UT99 but I cant remember what mod it was or how it was coded to do that. I'll write it down if I ever find it. Maybe it was the ChaosUT for UT99. :eek: