[ZS] Questions on Two-handed Weapon System

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
Gifty
Posts: 615
Joined: Sat Jun 15, 2013 8:21 pm

[ZS] Questions on Two-handed Weapon System

Post by Gifty »

Hey all. The last few weeks I've been trying to create a Bioshock 2 (or more properly, Clive Barker's Undying)-style guns'n'magic two-handed weapon system using PSPRITES, and following Matt's handy combo weapon example. I've made pretty good progress, getting the spell hand and the weapon hand to fire totally independently of each other. I'm still sort of blindly stumbling my way through with trial and error, as the way Gzdoom handles state flow,refire logic and overlays is still kind of mysterious to me.

Something I'm having trouble with is making weapon switching work within this system; since there's no way (that I know of) to let the player equip two weapons at the same time, I'm faking it with one mega weapon class that simply calls different states for each "weapon" you choose to equip. So, the number keys cycle "spells" while the Reload key cycles your physical weapon. The image below illustrates further.
Untitled.png
Untitled.png (12.12 KiB) Viewed 230 times
Number keys do technically change your active weapon, but these are only extra classes to define more fire states for your "spells" hand. Literally everything else is inherited from the master weapon class. The problem is that none of this weapon switching (either the "spells" hand or the Reload function for gun/sword) seem to be working, and the whole system in general feels pretty finnicky. The machinegun will often get stuck in a non-firing state if you mash keys together too quickly. Psprites are still kind of sorcery to me, and I only basically vaguely how the system works. Could someone more in-the-know check for any glaring flaws in this system? I'm sure there's a much better way to define this.

If the code below is too hard to troubleshoot, I also have a playable test level so you can try it firsthand. Mega thanks for any help!

Code: Select all

class SkrangWeapon : ComboFriendlyWeapon 
{
	//VARIABLES/////////////////////////////////////////////////////////////////////////////////////
	bool weaponToggle;
	bool shootRetry;
	bool swingRetry;
	bool spellRetry;
	bool reloadRetry;
	
	default
	{
		MeleeRange 60;

		Weapon.Bobstyle "alpha";
		Weapon.BobSpeed 1.5;

		+WEAPON.NOAUTOAIM;
	}
	
	enum WeaponLayers
	{
		PSP_SPELLHAND = 1001,
		PSP_WEAPONHAND = 1002,
	};

	//FUNCTIONS/////////////////////////////////////////////////////////////////////////////////////
	override void PostBeginPlay()
	{
		swingRetry = 1;
		shootRetry = 1;
		spellRetry = 1;

		weaponToggle = 1;
		super.postbeginplay();
	}
			
	//STATES////////////////////////////////////////////////////////////////////////////////////////
	states
	{
		Select:
			CONE AA 0 ;
			CONE A 1 a_raise;
			loop;

		Deselect:
			CONE AA 0 ;
			CONE A 1 a_lower;
			loop;

		Fire:
			tnt1 a 0;
			stop;

		Ready:
			tnt1 A 1
			{
				a_overlay(1001, "ReadySpell");
				a_overlay(1002, "ReadyGun");
			}
			stop;

		ReadyGun: //ready weapon hand
			GRGN A 1
			{
				A_WeaponReady(WRF_NOFIRE | WRF_ALLOWRELOAD);

				if (pressingFire() && invoker.shootRetry == 1)
					SetWeaponState("GunFire", PSP_WEAPONHAND);
			}
			loop;	

		ReadySword: //ready sword hand
			FSRD A 1
			{
				A_WeaponReady(WRF_NOFIRE | WRF_ALLOWRELOAD);

				if (pressingFire() && invoker.swingRetry == 1)
					SetWeaponState("SwordSwing", PSP_WEAPONHAND);
				if (pressingUser1())
					SetWeaponState("Block", PSP_WEAPONHAND);
			}
			loop;

		ReadySpell: //ready spell hand
			CONE A 1
			{
				A_WeaponReady(WRF_NOFIRE | WRF_ALLOWRELOAD);

				if (pressingAltFire() && invoker.spellRetry == 1)
					SetWeaponState("SpellFire", PSP_SPELLHAND);
			}
			loop;

		SpellFire:
			TNT1 a 0;
			goto ready;

		GunFire:
			GRGN a 3 bright
			{
				invoker.shootRetry = 0;
				A_FireBullets(5.1, 5.1, -1, 5, "BulletPuff");
				A_StartSound("M3/fire");
				A_gunflash("Gunflash");
			}
			GRGN a 3 bright
			{
				A_FireBullets(5.1, 5.1, -1, 5, "BulletPuff");
				A_StartSound("M3/fire");
				A_gunflash("Gunflash2");
				invoker.shootRetry = 1;
			}
			GRGN a 0 A_StartSound("M3/end");
			goto readyGun;

		SwordSwing:
			tnt1 a 0 { invoker.swingRetry = 0 ;}
			TNT1 a 0
			{
				a_startsound("skeleton/swing");
				a_overlay(4, "RECOIL");
			}
			FSRD fe 1;
			FSRD ddef 1;
			FSRD g 2 a_custompunch(10, 0, 0, "SwordPuff");
			FSRD i 2;
			FSRD j 2 { a_jumpifinventory("StunEffect", 1, "stunned"); }
			FSRD abc 2;
			tnt1 a 0 { invoker.swingRetry = 1 ;}
			goto readySword;

		Block:
			FSRD f 8
			{
				a_clearoverlays(6, 7);
				let Player = SkrangPlayer(invoker.owner);
				if (Player)
					player.IsBlocking = true;
			}
			FSRD d 7
			{
				let Player = SkrangPlayer(invoker.owner);
				if (Player)
					player.IsBlocking = false;
			}
			goto ReadySword;

		Stunned:
			BLOC a 2
			{
				a_takeinventory("stuneffect", 1);
				a_recoil(10);
			}
			BLOC bcde 2;
			goto ReadySword;

		Reload:
			TNT1 a 1
			{
				if (invoker.weaponToggle == 1)
				{
					invoker.weaponToggle = 0;
					Console.Midprint("bigfont", "Sword Selected");
					setWeaponState("readysword", PSP_WEAPONHAND);
				}
				else if (invoker.weaponToggle == 0)
				{
					invoker.weaponToggle = 1;
					Console.Midprint("bigfont", "Machinegun Selected");
					setWeaponState("readygun", PSP_WEAPONHAND);
				}

			}
			goto ready;

		GunFlash:
			FLSH A 1 bright;
			stop;

		GunFlash2:
			FLSH B 1 bright;
			stop;

		Recoil:
			//raise
			tnt1 a 1 a_setpitch(pitch - .07, SPF_INTERPOLATE);
			tnt1 a 1 a_setpitch(pitch - .02, SPF_INTERPOLATE);
			tnt1 a 1 a_setpitch(pitch - .01, SPF_INTERPOLATE);
			tnt1 a 1
			{
				a_setpitch(pitch + .1, SPF_INTERPOLATE);
				a_setangle(angle - 0.1);
			}
			tnt1 a 1
			{
				a_setpitch(pitch + .2, SPF_INTERPOLATE);
				a_setangle(angle - 0.1);
			}
			tnt1 a 1
			{
				a_setpitch(pitch + .15, SPF_INTERPOLATE);
				a_setangle(angle - 0.1);
			}
			tnt1 a 1 a_setpitch(pitch + .25, SPF_INTERPOLATE);
			tnt1 a 1 a_setpitch(pitch + .2, SPF_INTERPOLATE);
			tnt1 a 1 a_setpitch(pitch + .1, SPF_INTERPOLATE);
			//lower
			tnt1 a 1
			{
				a_setpitch(pitch - .2, SPF_INTERPOLATE);
				a_setangle(angle + 0.1);
			}
			tnt1 a 1
			{
				a_setpitch(pitch - .15, SPF_INTERPOLATE);
				a_setangle(angle + 0.1);
			}
			tnt1 a 1
			{
				a_setpitch(pitch - .15, SPF_INTERPOLATE);
				a_setangle(angle + 0.1);
			}
			tnt1 a 1 a_setpitch(pitch - .10, SPF_INTERPOLATE);
			tnt1 a 1 a_setpitch(pitch - .10, SPF_INTERPOLATE);
			tnt1 a 1 a_setpitch(pitch - .05, SPF_INTERPOLATE);
			tnt1 a 1 a_setpitch(pitch - .05, SPF_INTERPOLATE);
			stop; 
	}
}

CLASS EnergySpell : SkrangWeapon
{
	DEFAULT
	{
		Tag "Magic Missile";
	}
	
	STATES
	{
		SpellFire:
			CONE abcd 1;
			CONE e 5 a_fireplasma;
			CONE fgha 2;
			goto ReadySpell;		
	}
}

class FireSpell : SkrangWeapon
{
	DEFAULT
	{
		Tag "Fire Blast";
	}
	
	STATES
	{			
		SpellFire: //fireball spell
			CONE abcd 1; 
		FireballHold:
			CONE D 2 a_fireprojectile("skrangfireball",random(-6,6),0,-6.0,0,0,random(-3,3));			
			CONE e 1;
			CONE f 2 { if(pressingaltfire()) setWeaponState("Fireballhold",PSP_SPELLHAND); }
		FireballEnd:
			CONE gha 2;
			goto ReadySpell;		
	}
}
User avatar
Ac!d
Posts: 345
Joined: Tue Apr 02, 2019 5:13 am
Location: France

Re: [ZS] Questions on Two-handed Weapon System

Post by Ac!d »

Code: Select all

		Ready:
			tnt1 A 1
			{
				a_overlay(1001, "ReadySpell");
				a_overlay(1002, "ReadyGun");
			}
			stop;

		ReadyGun: //ready weapon hand
			GRGN A 1
			{
				A_WeaponReady(WRF_NOFIRE | WRF_ALLOWRELOAD);

				if (pressingFire() && invoker.shootRetry == 1)
					SetWeaponState("GunFire", PSP_WEAPONHAND);
			}
			loop;	

		ReadySword: //ready sword hand
			FSRD A 1
			{
				A_WeaponReady(WRF_NOFIRE | WRF_ALLOWRELOAD);

				if (pressingFire() && invoker.swingRetry == 1)
					SetWeaponState("SwordSwing", PSP_WEAPONHAND);
				if (pressingUser1())
					SetWeaponState("Block", PSP_WEAPONHAND);
			}
			loop;

		Reload:
			TNT1 a 1
			{
				if (invoker.weaponToggle == 1)
				{
					invoker.weaponToggle = 0;
					Console.Midprint("bigfont", "Sword Selected");
					setWeaponState("readysword", PSP_WEAPONHAND);
				}
				else if (invoker.weaponToggle == 0)
				{
					invoker.weaponToggle = 1;
					Console.Midprint("bigfont", "Machinegun Selected");
					setWeaponState("readygun", PSP_WEAPONHAND);
				}

			}
			goto ready;


The problem is in the "Reload" state. When you set "invoker.weaponToggle" to 0 or 1, it's always goes to the "Ready" state with the overlays "ReadySpell" and "ReadyGun"
Here's my solution below. (it's probably not great, but it's works fine)

Code: Select all

		Ready:
			tnt1 A 1
			{
				a_overlay(1001, "ReadySpell");

				if (invoker.weaponToggle == 1)
				{ a_overlay(1002, "ReadyGun"); }
				else if (invoker.weaponToggle == 0)
				{ a_overlay(1002, "ReadySword"); }
			}
			stop;
User avatar
Gifty
Posts: 615
Joined: Sat Jun 15, 2013 8:21 pm

Re: [ZS] Questions on Two-handed Weapon System

Post by Gifty »

Thanks a bunch! That put a pretty good dent in getting the reload system on track, now I just have to come up with a timer system to stop the player from mashing the reload function.
Post Reply

Return to “Scripting”