Simple Charged weapon example?

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!)
Okgo5555
Posts: 66
Joined: Thu Mar 23, 2017 11:18 am
Discord: Okgo5555

Simple Charged weapon example?

Post by Okgo5555 »

I am currently working on polishing some of my weapons that I have been using to expand my overall dexterity with DECORATE/ZScript etc... Heretofore, I have only ever messed with some rudimentary HTML and CSS. So, the "Basics" of coding are not my background. I have managed to learn many neat tricks on my own and have been incrementally improving over the course of- UGH- 7 years. So, before I ask this question, I want everyone to know that YES I AM AWARE THAT DECORATE SOMEWHAT OBSOLETE. I'm still trying to polish my learnings that I started back in 2014.

I downloaded the hand grenade from the realm667, but I I can't seem to make sense of the way the charging mechanism works with the way that DECORATE is laid out. My project involves reworked Vanilla-ish weapons that have multiple fire modes. I would like the work in something that helps the Pistol emulate the behavior of the EMG from D2016 with it's alt-fire. I understand that custominventory and A_GiveInventory is involved in somehow, but I would really like a totally bare bones example that I could build on. Even if someone can point me to a good example that would be easier to reverse engineer for a noob like me that has a hard time of reading abstract coding and understanding the linearity (or non-linearity) in the syntax sometimes.

Thanks in advance.
XASSASSINX
Posts: 361
Joined: Tue Dec 20, 2016 4:53 pm
Location: MURICAA BROTHER! Just kidding, Brazil.

Re: Simple Charged weapon example?

Post by XASSASSINX »

Hello, to make a charging weapon you may use the AltHold and Hold fire states. As to how decorate is laid out, it follows this: (Using a monster example)

Code: Select all

    Spawn: 
       CRG2 AB 10 A_Look
       Loop 
CRG2 is the frame that will be displayed
AB is the frame letter that you define in your .WAD or .PK3
10 is the time-value in tics (1/35th of a second) that each frames take place
A_Look is the action function that will be used in that frame.

You can use the Hold and AltHold state to make a charging pistol, you can read more about the states here. https://zdoom.org/wiki/Classes:Weapon
There is also another wiki-page teaching how to make a basic first weapon, and WildWeasel (one of the folks from the forum here) has a tutorial too.
User avatar
Sir Robin
Posts: 397
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Simple Charged weapon example?

Post by Sir Robin »

Did you ever find a good example? I'm also looking for a tutorial or example for charging weapons. Not finding much in the wiki or this forum.
XASSASSINX
Posts: 361
Joined: Tue Dec 20, 2016 4:53 pm
Location: MURICAA BROTHER! Just kidding, Brazil.

Re: Simple Charged weapon example?

Post by XASSASSINX »

Oh hello. Well, not really, but they aren't hard to come by. Try out Flakes Doom (which is the first one that comes to my mind), it's a great weapon mod, your starting weapon is a plasma pistol similar to the one in Doom 4. Hey, if you wanna some tutorials or help doing some DECORATE (or ZScript, but i'm not that good with it being honest with you), DM me or ask cause i like to help OR ask the folks around here. Oh acually, as i type this i just realized, I DID make a charging weapon with charge-up states in DECORATE not so long ago. The code is kinda big, so i'm not gonna post here, but in sum, it checks if you have ammo for the Charge sequence, if yes, it charges up and gives you a timed loop for the next charging sequence.
User avatar
Sir Robin
Posts: 397
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Simple Charged weapon example?

Post by Sir Robin »

Yeah somebody sent me a hand grenade that has a charge attack. It worked like you describe - when charging it would add a dummy ammo item to the player inventory, when released it would check how many of that ammo the player had, to know how much charge it had. Then it would clear that inventory item for the next charge attack.

I could do something like that, but it seems kinda hacky, and I wanted to see if there was a cleaner way to do it with zscript or whatever.
XASSASSINX
Posts: 361
Joined: Tue Dec 20, 2016 4:53 pm
Location: MURICAA BROTHER! Just kidding, Brazil.

Re: Simple Charged weapon example?

Post by XASSASSINX »

if there was a cleaner way to do it with zscript or whatever.
I've seen it done with ACS. I believe the weapon was called "Soul Renderer", and it is on Realm667. As for the ZScript part, well, ZScript allow disposable integers on the fly (Like real world progamming languages), so y'know, you could call a variable in a loop, and while this function is active, keep incrementing the intenger, so in a way, the same thing as the dummy inventory.
Kzer-Za
Posts: 507
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

Re: Simple Charged weapon example?

Post by Kzer-Za »

Well, I'm currently making a standalone grenade mod, and I can share the code. Its alt fire is not finished yet, but charging happens in the primary fire, so it can give you an idea.

Code: Select all

class KZ_GrenadeWeapon : DoomWeapon
{
	Default
	{
		Weapon.AmmoUse 1;
		Weapon.AmmoGive 3;
		Weapon.AmmoType "KZ_GrenadeAmmo";
		Weapon.SelectionOrder 0xffffffff;
		Weapon.MinSelectionAmmo1 21;
		+Weapon.NOALERT;
		Tag "$TAG_KZ_GRENADEWEAPON";
		Weapon.SlotNumber 8;
	}
	States
	{
	Ready:
		TNT1 A 0 A_JumpIfInventory("KZ_DoQuickThrow", 1, "Fire");
		GRHO A 1 A_WeaponReady;
		Loop;
	Deselect:
		GRHO A 1 A_Lower(12);
		Loop;
	Select:
		GRHO A 1 A_Raise(12);
		Loop;
	Fire:
		TNT1 A 0 {invoker.ExtraForce = 0;}
		GRTH ABCD 1;
		// throw with medium force if using a quick throw
		TNT1 A 0 A_JumpIfInventory("KZ_DoQuickThrow", 1, "FullThrow");
	Hold:
		GRTH E 1 IncreaseForce();
		TNT1 A 1 A_ReFire("Hold");
	FullThrow:
		TNT1 A 0 A_StartSound("Grenade_Pin");
		GRTH EEF 1;
	UnpinnedThrow:
		GRTH G 1;
		TNT1 A 0 A_StartSound("Grenade_Toss");
		GRTH HI 1;
		TNT1 A 0 ThrowGrenade();
		GRTH JKLM 1;
		TNT1 A 0 A_TakeInventory("KZ_DoQuickThrow", 1);
		TNT1 A 1 A_ReFire("Fire");
		Goto Ready;
// alt fire
	AltFire:
		TNT1 A 0 AltFireChoose();
	RemovePin:
		TNT1 A 0 A_StartSound("Grenade_Pin");
		GRTH EEF 1;
		TNT1 A 0 {invoker.isPinRemoved = true;}
		TNT1 A 1 A_ReFire("Ready");
	}
	
	// weapon functions
	int ExtraForce;
	bool isPinRemoved;
	int GrenadeTimer;
	
	override void PostBeginPlay()
	{
		super.PostBeginPlay();
		
		GrenadeTimer = 35;
	}
	
	action void IncreaseForce()
	{
		invoker.ExtraForce++;
		if (invoker.ExtraForce > 40)
		{
			invoker.ExtraForce = 40;
			Console.Printf("Max throw force");
		}
	}
	
	action void ThrowGrenade()
	{
		let proj = A_FireProjectile("KZ_GrenadeThrown", useammo: true);
		if (proj)
		{
			if (FindInventory("KZ_DoQuickThrow") == null)
			{
				proj.Speed = 3 + invoker.ExtraForce;
				proj.Vel3DFromAngle(proj.Speed, angle, pitch);
			}
			if (invoker.GrenadeTimer != 35)
			{
				proj.ReactionTime = invoker.GrenadeTimer;
			}
			invoker.isPinRemoved = false;
		}
	}
	
	action state AltFireChoose()
	{
		if (invoker.isPinRemoved)
		{
			Console.Printf("Pin is already removed");
			return ResolveState("UnpinnedThrow");
		}
		// otherwise
		Console.Printf("Pin is not removed yet");
		return ResolveState("RemovePin");
	}
}
User avatar
Sir Robin
Posts: 397
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Simple Charged weapon example?

Post by Sir Robin »

Awesome! Thanks so much for the example, that's exactly what I was looking for.

What I'm going to do is change this into a generic class called TossableWeapon, then I can have weapons like grenades inherit from that.
I see you cap the charge at 40. I'll put that as a variable, call it ExtraForceMax, then set it to 40 in defaults. Then on the status bar I can do something like: If (player weapon is tossableweapon) and (weapon extraforce > 0) then display charge percent as (100 * ExtraForce / ExtraForceMax)
I'll also move that "KZ_GrenadeThrown" into a variable, so it can be changed when inherited
I see you check and clear KZ_QuickThrow, but never set it. Where does that come from? I'm guessing that has something to do with your unfinished altfire?
I see you set the GrenadeTimer value in PostBeginPlay() instead of in the defaults. Why is that? What's the difference?
Kzer-Za
Posts: 507
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

Re: Simple Charged weapon example?

Post by Kzer-Za »

Sir Robin wrote:I see you check and clear KZ_QuickThrow, but never set it. Where does that come from? I'm guessing that has something to do with your unfinished altfire?
I see you set the GrenadeTimer value in PostBeginPlay() instead of in the defaults. Why is that? What's the difference?
Hi! If you mean KZ_DoQuickThrow, that's not a variable but an item that is given to the player via a hotkey, to make him quickly switch to the grenade weapon if it is not already selected and immediately go to the Fire state. Then in the function ThrowGrenade the player's inventory is checked for this item again, to make him throw the grenade with it's default speed (which is defined in the grenade projectile class, of course). You can see that proj.Speed is adjusted by invoker.ExtraForce only if (FindInventory("KZ_DoQuickThrow") == null).

Regarding the GrenadeTimer and PostBeginPlay question: you can set in the Default section only the properties that all the actors have (https://zdoom.org/wiki/Actor_properties). Since there is no GrenadeTimer property, I can't set it, so I declare a variable. But a variable cannot be assigned a value outside of a function. So if you declare a variable inside a function, you can do it like this: int a = 5;. But that variable will be accessible only inside that function. For the variable to be accessible from all functions of the actor, you must declare it inside the actor. But since you can assign this variable a value only inside a function, it means that you have to do it in PostBeginPlay, since this function is called right after the actor is spawned.

And regarding the names: it is better if you get rid of the prefix KZ_. The thing is, if a class with the same name is defined more than once, the engine is gonna throw an error when you try to launch the game. And since I want to prevent a situation when my classes are named the same as a class from some other mod, I give my class names a prefix of the name of the mod that I make. In this case I plan to name it simply "Kzer-Za's Standalone Grenades", so I chose prefix "KZ_". Also sometimes I give these prefixes to functions, simply as a reminder to myself that it is not a function from the engine, i.e. available for all actors. If it seems to me that I won't forget in the future that this function was made by me, I don't give it a prefix :)
User avatar
Sir Robin
Posts: 397
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Simple Charged weapon example?

Post by Sir Robin »

Awesome, thanks for sticking around and answering my questions. I've got it working pretty good now. And I name all my stuff UW_ for the same non-colliding reasons.
User avatar
Sir Robin
Posts: 397
Joined: Wed Dec 22, 2021 7:02 pm
Graphics Processor: Intel (Modern GZDoom)
Location: Medellin, Colombia

Re: Simple Charged weapon example?

Post by Sir Robin »

Kzer-Za wrote:Regarding the GrenadeTimer and PostBeginPlay question: you can set in the Default section only the properties that all the actors have (https://zdoom.org/wiki/Actor_properties). Since there is no GrenadeTimer property, I can't set it, so I declare a variable. But a variable cannot be assigned a value outside of a function. So if you declare a variable inside a function, you can do it like this: int a = 5;. But that variable will be accessible only inside that function. For the variable to be accessible from all functions of the actor, you must declare it inside the actor. But since you can assign this variable a value only inside a function, it means that you have to do it in PostBeginPlay, since this function is called right after the actor is spawned.
Thanks for all your help and taking time to explain these things. FYI I just found this page describing how to tag a variable as a property so you can set it's default value in the defaults section without having to do that PostBeginPlay thing: [wiki=ZScript_custom_properties]ZScript Custom Properties[/wiki]

Return to “Scripting”