[Zscript] Actor reacts to player friendly fire

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
User avatar
JUSSOMONE
Posts: 36
Joined: Fri Oct 07, 2022 8:55 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia (Modern GZDoom)

[Zscript] Actor reacts to player friendly fire

Post by JUSSOMONE »

Goodnight, to be more precise I am trying to make a "friendly" actor react to friend fire from the player by speaking against it, and one of the solutions is a hacky way to implement custom damage functions so they will "know" if the damage came from the player.

Here is an example:

Code: Select all

           Pain: --> NORMAL PAIN STATE
    	          BCC1 H 3 A_Pain;
		  BCC1 H 3;
		  Goto Chase_Enemy;
	   Pain.BulletPlayer: --> THE HACKY WAY TO IMPLEMENT
    	          BCC1 H 3 A_Pain;
		  BCC1 H 3 A_PlaySound("Friend/AlertPlayer", 2, 1.0, 0, 1.0);
		  Goto Chase_Enemy;
I know i could to this but, since my mod has a lot of custom damage types the player can cause, it would take some time and i would need to implement a lot of pain states just for a single effect of the actor alerting the player. Is there a universal solution for this? I know there is DamageMobj but since it's a Int function and not a void, I don't know if it has anything to do with this.

Code: Select all

Override int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags, double angle)
	{
		   If(source.player) //If the source is a player
		   { 
			   SetStateLabel("PainAlertPlayer"); --> THIS WOULD BE THE UNIVERSAL PAIN STATE WHATEVER TYPE OF DAMAGE THE PLAYER WOULD CAUSE TO HIS ALLIES.
			   //AND THEN THE ALLY WOULD ALERT THE PLAYER.
		   }
		   Return Super.DamageMobj(inflictor, source, damage, mod, flags, angle);
       }
I've tried, but somehow it didn't work, only the death state but the actors goes into a ghost bug and that is. Thanks in advance.
Jarewill
 
 
Posts: 1853
Joined: Sun Jul 21, 2019 8:54 am

Re: [Zscript] Actor reacts to player friendly fire

Post by Jarewill »

The reason the DamageMobj override doesn't do anything is because it sets the monster into the PainAlertPlayer state, but still deals damage which puts the monster into the Pain state, ignoring the previous one.
If you add Return 0; below the SetStateLabel call it should nullify damage and prevent the Pain state from triggering, however it will also make the monster immune to player attacks.
You should also add a health>0 check next to the player one to prevent the monster from becoming a ghost.

Alternatively if all you want to do is play a sound, then you can just put A_StartSound where SetStateLabel is currently.
User avatar
JUSSOMONE
Posts: 36
Joined: Fri Oct 07, 2022 8:55 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia (Modern GZDoom)

Re: [Zscript] Actor reacts to player friendly fire

Post by JUSSOMONE »

Thanks for the answer, turned out i just needed to give him an inventory dummy to enter a custom pain state after checking the inventory and then alerting the player.

Code: Select all

Pain: 
	  TNT1 A 0 A_JumpIfInventory("PlayerNowEnemy", 3, "PainTraitor", AAPTR_PLAYER1);
          TNT1 A 0 A_JumpIfInventory("AlertPlayer", 1, "PainHey");
    	  BCC1 H 3 A_Pain;
    	  BCC1 H 3;
	  Goto Chase_Enemy;
PainTraitor: 
    	  BCC1 H 3 A_Pain;
    	  BCC1 H 3;
          Goto Chase_Traitor;
PainHey:
	  TNT1 A 0 A_TakeInventory("AlertPlayer", 255);
	  BCC1 H 3 A_Pain;
    	  TNT1 A 0 A_PlaySound("Friend/AlertPlayer", 2, 1.0, 0, 1.0);
	  BCC1 H 3;
          Goto Chase_Enemy;  

Override int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags, double angle)
{
		   If(source.player) //If the source is a player
		   { 
		       GiveInventory("AlertPlayer", 1); --> THE DUMMY
		   }
		   Return Super.DamageMobj(inflictor, source, damage, mod, flags, angle);
}
Although it's crucial to remove the aforementioned dummy so the actor won't be alerting the enemies if the player did the first damage...Thanks for your help, the little detail of knowing the actor would enter the pain state regardless of the SetStateLabel made all the difference, and I think it's less code intensive.
User avatar
JUSSOMONE
Posts: 36
Joined: Fri Oct 07, 2022 8:55 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia (Modern GZDoom)

Re: [Zscript] Actor reacts to player friendly fire

Post by JUSSOMONE »

Hey, sorry for asking again but ultimately my mod crashed due to the new implemented system from my last post, and it has to do with the DamageMobj, it was an VM execution abort , here is the screenshot:

Image

And the code:

Code: Select all


class Bob_Crew : Allies
{ 
    Default
	{
	      Scale          0.3;
	          Tag            "Bob Crew";
		  Obituary       "%o made a bob angry.";
		  
          Health         30;
	      Radius         16;
	      Height         56;
	      Speed          3;
	      //Mass         100;
	      PainChance     256;
		  MaxTargetRange 1024;
		  
		  BloodType      "MarathonHumanBludSplash";
		  DamageFactor   "Plasma", 1.0;
		  DamageFactor   "Fusion", 1.0;
	      DamageFactor   "Fire", 1.75;
	      DamageFactor   "BulletCaliber", 1.0;
	      DamageFactor   "Explosion", 1.0;		  
		  
	      SeeSound       "bob/see";
		  ActiveSound    " ";//EnemySighted 
	      PainSound      "bob/Pain";
	      DeathSound     "bob/death";
		  
		  //DropItem "Clip", 192;
	      MONSTER;
		  +NOTAUTOAIMED;
		  -COUNTKILL;
		  +FIXMAPTHINGPOS;
          +NOBLOCKMONST;
          +FLOORCLIP;
          +QUICKTORETALIATE;
          +NOINFIGHTSPECIES;
		  +DONTHARMCLASS;
          +DONTHARMSPECIES;
	}
	States
	{
	Spawn:
	      TNT1 A 0 NoDelay A_AllySpawn();
		  TNT1 A 0 A_PlaySound("Thing/Teleport", 4, 1.0, 0, 1.0);
		  BTL1 A 2 Bright;
		  BTL1 B 2 Bright;
		  BTL1 C 2 Bright;
		  BTL1 D 2 Bright;
		  BTL1 E 2 Bright;
		  BTL1 F 2 Bright;
		  BCC1 U 2 A_PlaySound("bob/see", 2, 1.0, 0, 1.0);
		  TNT1 A 0 A_AllyNormal();
		  Goto Look_For_Enemies;

//WANDERING AND CHASING

	Idle:	
	      BCC1 AAABBB 1 A_Wander;
		  TNT1 A 0 A_LookEx(0, 0, 1024, 1024, 0, "Look_For_Enemies");
		  BCC1 CCCDDD 1 A_Wander;
		  TNT1 A 0 A_LookEx(0, 0, 1024, 1024, 0, "Look_For_Enemies");
		  BCC1 AAABBB 1 A_Wander;
		  TNT1 A 0 A_LookEx(0, 0, 1024, 1024, 0, "Look_For_Enemies");
		  BCC1 CCCDDD 1 A_Wander;
		  TNT1 A 0 A_LookEx(0, 0, 1024, 1024, 0, "Look_For_Enemies");
		  Loop;
    Inactive:
	Look_For_Enemies:		  
	      TNT1 A 0 A_JumpIfInventory("PlayerNowEnemy", 3, "Turn_Against_Player", AAPTR_PLAYER1);
		  TNT1 A 0 A_AreaIsClear();
		  TNT1 A 0 A_CheckProximity("Enemy_Sighted", "Enemies", 1024, 1, CPXF_ANCESTOR|CPXF_CHECKSIGHT|CPXF_CLOSEST|CPXF_SETTARGET);
		  TNT1 A 0 A_CheckProximity("Enemy_Sighted", "Yeti", 1024, 1, CPXF_ANCESTOR|CPXF_CHECKSIGHT|CPXF_CLOSEST|CPXF_SETTARGET);
		  TNT1 A 0 A_JumpIfCloser(96,"Waits");
		  BCC1 AAABBB 1 A_Chase(" ", " ");
		  BCC1 CCCDDD 1 A_Chase(" ", " ");
		  BCC1 AAABBB 1 A_Chase(" ", " ");
		  BCC1 CCCDDD 1 A_Chase(" ", " ");
		  //TNT1 A 0 A_GiveInventory("IsAreaSecured", 1); //Check if area is clear of enemies
          //TNT1 A 0 A_JumpIfInventory("IsAreaSecured", 100, "Area_Secured");
    	  Loop;
	
	Waits:
	      BCC1 U 1;      
		  Goto Look_For_Enemies;
		  
	Turn_Against_Player:
	      TNT1 A 0 A_TakeInventory("IsAreaSecured", 255);
	      TNT1 A 0 A_PlaySound("bob/NowEnemy", 2, 1.0, 0, 1.0);
		  TNT1 A 0 A_CheckProximity("Chase_Traitor", "PlayerPawn", 16384, 1, CPXF_ANCESTOR|CPXF_CLOSEST|CPXF_SETTARGET);
		  Goto Chase_Traitor;	 
	Chase_Traitor:
	      BCC1 AAABBB 1 A_Chase(" ","Missile_Traitor");
		  BCC1 CCCDDD 1 A_Chase(" "," ");
		  BCC1 AAABBB 1 A_Chase(" ","Missile_Traitor");
		  BCC1 CCCDDD 1 A_Chase(" "," ");
    	  TNT1 A 0 A_JumpIfInTargetLOS("Chase_Traitor",0,JLOSF_DEADNOJUMP|JLOSF_NOSIGHT);//if dead don't jump
		  Goto Look_For_Enemies;
		  
	Enemy_Sighted:
	      TNT1 A 0 A_TakeInventory("IsAreaSecured", 255);
	      TNT1 A 0 A_PlaySound("bob/active", 2, 1.0, 0, 1.0);
		  Goto Chase_Enemy;
	Chase_Enemy:
		  BCC1 AAABBB 1 A_Chase;
		  BCC1 CCCDDD 1 A_Chase(" "," ");
		  BCC1 AAABBB 1 A_Chase;
		  BCC1 CCCDDD 1 A_Chase(" "," ");
		  TNT1 A 0 A_GiveInventory("ForgetTimer", 1); //Check if enemy is still on sight otherwise forget him
		  TNT1 A 0 A_JumpIfInventory("ForgetTimer", 20, "Forget_Target"); 
    	  TNT1 A 0 A_JumpIfInTargetLOS("Chase_Enemy",0,JLOSF_DEADNOJUMP|JLOSF_NOSIGHT);//if dead don't jump
		  Goto Look_For_Enemies;
   
//AREA SECURED AND FORGET
	
	Area_Secured:
	      BCC1 AAABBB 1 A_Wander;
		  BCC1 CCCDDD 1 A_Wander;
		  BCC1 AAABBB 1 A_Wander;
		  BCC1 CCCDDD 1 A_Wander;
		  TNT1 A 0 A_Jump(32, 1);
		  Loop;
	      TNT1 A 0 A_AllySpawn();
	      TNT1 A 0 A_PlaySound("bob/secure", 2, 1.0, 0, 1.0);
	      BCC1 U 35;
	      TNT1 A 0 {self.bUSESPECIAL = 0;}
		  TNT1 A 0 A_PlaySound("Thing/TeleportOut", 4, 1.0, 0, 1.0);
		  BTL1 F 2 Bright;
		  BTL1 E 2 Bright;
		  BTL1 D 2 Bright;
		  BTL1 C 2 Bright;
		  BTL1 B 2 Bright;
		  BTL1 A 2 Bright;
		  TNT1 A 0 A_GiveInventory("BobReinforcement", 1, AAPTR_PLAYER1);
		  Stop;
		  
	Forget_Target:
	      TNT1 A 0 A_TakeInventory("ForgetTimer", 255);
		  TNT1 A 0 A_ClearTarget;
		  Goto Look_For_Enemies;
		  
//ACTIVATION

    Active:
	      TNT1 A 0;
		  Goto Area_Secured;
		  
//ATTACKING 

    Missile:
	      TNT1 A 0 A_JumpIfInTargetInventory("IsPlayer", 1, "Look_For_Enemies");
	      TNT1 A 0 A_FaceTarget;
          TNT1 A 0 A_TakeInventory("ForgetTimer", 255);
		  TNT1 A 0 A_PlaySound("weapons/pistol/fire", 1, 1.0, 0, 1.0);
    	  TNT1 A 0 A_SpawnItemEx("44mmCasingSpawner", 18, 12, 46, 0, 0, 0, 0, SXF_NOCHECKPOSITION|SXF_TRANSFERPITCH);
    	  BCC1 G 6 Bright A_CustomBulletAttack(3, 3, 1, random(20,28), "BobMarathonPuff", 0, CBAF_NORANDOM);
		  BCC1 F 6;
		  BCC1 U 6;
		  TNT1 A 0 A_FaceTarget;
		  TNT1 A 0 A_PlaySound("weapons/pistol/fire", 1, 1.0, 0, 1.0);
    	  TNT1 A 0 A_SpawnItemEx("44mmCasingSpawner", 18, 12, 46, 0, 0, 0, 0, SXF_NOCHECKPOSITION|SXF_TRANSFERPITCH);
    	  BCC1 G 6 Bright A_CustomBulletAttack(3, 3, 1, random(20,28), "BobMarathonPuff", 0, CBAF_NORANDOM);
		  BCC1 F 6;
		  BCC1 U 6;
    	  Goto Chase_Enemy;
		  
	 Missile_Traitor:
	      TNT1 A 0 A_FaceTarget;
          TNT1 A 0 A_TakeInventory("ForgetTimer", 255);
		  TNT1 A 0 A_PlaySound("weapons/pistol/fire", 1, 1.0, 0, 1.0);
    	  TNT1 A 0 A_SpawnItemEx("44mmCasingSpawner", 18, 12, 46, 0, 0, 0, 0, SXF_NOCHECKPOSITION|SXF_TRANSFERPITCH);
    	  BCC1 G 6 Bright A_CustomBulletAttack(3, 3, 1, random(20,28), "MarathonPuff", 0, CBAF_NORANDOM);
		  BCC1 F 6;
		  BCC1 U 6;
		  TNT1 A 0 A_FaceTarget;
		  TNT1 A 0 A_PlaySound("weapons/pistol/fire", 1, 1.0, 0, 1.0);
    	  TNT1 A 0 A_SpawnItemEx("44mmCasingSpawner", 18, 12, 46, 0, 0, 0, 0, SXF_NOCHECKPOSITION|SXF_TRANSFERPITCH);
    	  BCC1 G 6 Bright A_CustomBulletAttack(3, 3, 1, random(20,28), "MarathonPuff", 0, CBAF_NORANDOM);
		  BCC1 F 6;
		  BCC1 U 6;
    	  Goto Chase_Traitor;

//PAIN AND DEATH
		  
    Pain: 
	      TNT1 A 0 A_JumpIfInventory("PlayerNowEnemy", 3, "PainTraitor", AAPTR_PLAYER1);
		  TNT1 A 0 A_JumpIfInventory("AlertPlayer", 1, "PainHey");
    	  BCC1 H 3 A_Pain;
    	  BCC1 H 3;
		  Goto Chase_Enemy;
	PainTraitor: 
    	  BCC1 H 3 A_Pain;
    	  BCC1 H 3;
		  Goto Chase_Traitor;
    PainHey:
	      TNT1 A 0 A_TakeInventory("AlertPlayer", 255);
		  BCC1 H 3 A_Pain;
    	  TNT1 A 0 A_PlaySound("bob/AlertPlayer", 2, 1.0, 0, 1.0);
		  BCC1 H 3;
		  Goto Chase_Enemy;     
	
    Death:
	      TNT1 A 0 A_Log("\cjB.o.b crew down!");
		  TNT1 A 0 {self.bUSESPECIAL = 0;}
	      BCC1 I 1 A_BobSoftJump();
	Falling:
    	  BCC1 I 1 A_CheckFloor("DeathFall");//A_Jumpif( (vel.x + vel.y + vel.z == 0), "DeathFall");
		  Loop;
	DeathFall:
	      TNT1 A 0 A_Stop;
    	  BCC1 J 3 A_PlaySound("Body/fall", 4, 1.0, 0, 1.0);//A_PlaySound("bob/death", 2, 1.0, 0, 1.0, false, frandom(0.8,1.2));//A_Scream; just for fun
    	  BCC1 K 3 A_GiveToTarget("PlayerNowEnemy", 1);
    	  BCC1 L 3;
		  BCC1 M 3;
		  BCC1 N 3 A_NoBlocking;
		  TNT1 A 0 A_DropItem("Clip", 1, 256);
		  TNT1 A 0 A_Jump(192, "DeathNoAllyTalk"); //Chance for allies not declaring ally death
		  TNT1 A 0 A_AllyIsDown();
	DeathNoAllyTalk:
		  BCC1 O -1;
		  Stop;
	Death.Explosion:
	      TNT1 A 0 A_Log("\cjB.o.b crew down! ouch!");
		  TNT1 A 0 {self.bUSESPECIAL = 0;}
	      BCC1 I 1 A_PlaySound("bob/death", 2, 1.0, 0, 1.0, false, frandom(0.7,0.8)); //A_BobHardJump();
	FallingExplosion:
		  BCC1 I 1 A_CheckFloor("ExplosionFall");//A_Jumpif( (vel.x + vel.y + vel.z == 0), "ExplosionFall");
		  Loop;
	ExplosionFall:
	      TNT1 A 0 A_Stop;
	      TNT1 A 0 A_SetScale(0.4, 0.4);
	      BCC1 P 3 A_PlaySound("bloodsplat", 4, 1.0, 0, 1.0);
		  BCC1 Q 3 A_GiveToTarget("PlayerNowEnemy", 1);
		  BCC1 R 3;
		  BCC1 S 3 A_NoBlocking;
		  TNT1 A 0 A_Jump(192, "ExplosionDeathNoAllyTalk"); //Chance for allies not declaring ally death
		  TNT1 A 0 A_AllyIsDown();
	ExplosionDeathNoAllyTalk:
		  BCC1 T -1;
		  Stop;
    }
	Override int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags, double angle)
	{
		   If(source.player) //If the source is a player
		   { 
		       GiveInventory("AlertPlayer", 1);
		   }
		   Return Super.DamageMobj(inflictor, source, damage, mod, flags, angle);
    }
	Override void Tick()
	{
	Super.Tick();
	
	      if (CheckInventory("KillCheck", 1))
	      {
		      A_PlaySound("bob/Trash", 2, 1.0, 0, 1.0);
			  A_TakeInventory("KillCheck", 255);
	      }
		  if (CheckInventory("AllyDown", 1) && level.time%(random(5,18)) == 0)
	      {
		      A_PlaySound("bob/Mandown", 2, 1.0, 0, 1.0);
			  A_TakeInventory("AllyDown", 255);
		  }
    }
}
I know it seems clumsy but the code itself is very well oraganized, it's just the zdoom forums code display function that messes it up...Can you help me? PS.:Sorry for the late message, but I've just launched my mod and I am worried about the crashes the players might found.
Jarewill
 
 
Posts: 1853
Joined: Sun Jul 21, 2019 8:54 am

Re: [Zscript] Actor reacts to player friendly fire

Post by Jarewill »

It looks like the code tries to read the properties of the source actor when it doesn't exist.
If you modify your code to check if source exists first it should stop crashing:
If(source && source.player)
User avatar
JUSSOMONE
Posts: 36
Joined: Fri Oct 07, 2022 8:55 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia (Modern GZDoom)

Re: [Zscript] Actor reacts to player friendly fire

Post by JUSSOMONE »

Ok, instead I tried to put an If(source.player && health>0), and it has been working fine for the time being...since it also checks if the actor is still alive. Do you agree or it may sound a possible problem?
Jarewill
 
 
Posts: 1853
Joined: Sun Jul 21, 2019 8:54 am

Re: [Zscript] Actor reacts to player friendly fire

Post by Jarewill »

You still don't check if the source actor exists, so I'm afraid it will still cause a VM abort in the same situation it triggered it the first time.
User avatar
JUSSOMONE
Posts: 36
Joined: Fri Oct 07, 2022 8:55 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia (Modern GZDoom)

Re: [Zscript] Actor reacts to player friendly fire

Post by JUSSOMONE »

Jarewill wrote: Thu Jan 25, 2024 2:15 pm You still don't check if the source actor exists, so I'm afraid it will still cause a VM abort in the same situation it triggered it the first time.
hmmm...didn't think about that, okay, thanks for the answer.
Post Reply

Return to “Scripting”