Forum rules
Please don't start threads here asking for help. This forum is not for requesting guides, only for posting them. If you need help, the Editing forum is for you.
Here's a guide that no one asked for, but hopefully can prove helpful to someone trying something that can easily turn into a not-exactly-flying spaghetti monster without sufficient planning.
Your weapon controls allow up to 8 buttons. This is quite a lot, but sometimes you want to accomplish things through specific combinations of buttons - without getting trapped in a maze of A_(Clear)Refire hacks trying to trace a unique path for every possibility.
Examples:
Dual-wield (or double-barreled, double-triggered) weapons: each of the two fire buttons handles one weapon, and each weapon (which is on a different overlay from the other, of course) can be fired totally independently of what the other is doing - even if the other fire button is still being held down.
Heavy Weapon Guy-style gun spinup: hold altfire to keep your gun ready to fire at any time.
Slam fire shotgun: hold trigger down, hold altfire to rack the shotgun, gun immediately goes off once you chamber the new round.
Panic shot: shoot in the middle of a reload sequence, spending whatever happens to be in the chamber already.
Check for holding Zoom/Reload/other non-A_Refire-able inputs outside of a ready state.
Daggerfall-style melee weapons: hold button, move mouse in the direction to swing.
Wacky weapon with a "jamming typewriter" interface that prevents the weapon from operating if you mash the buttons too fast.
Spellcasting decision trees.
Crude musical instruments.
You have actually run out of buttons for calling all the various weapon functions you have coded.
I've developed some shortcuts that massively improve my own quality of life while trying to do stuff like this. It occurs to me that my setup is not self-evident and might be good to share.
class ComboFriendlyWeapon : Weapon
{
/*
Here are some flag groups for A_WeaponReady.
The first by default enables all weapon keys at once, letting you do everything.
The second disables all buttons and deselect, leaving only the weapon bobbing
(assuming the player has enabled it). This allows you to have a "fake" ready state
in which you are actually directly checking input instead of the native ready code,
or adding a long static frame that would look really awkward if frozen
while the player walks.
*/
enum ComboWeaponConstants
{
WRF_ALL = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|
WRF_ALLOWUSER1|WRF_ALLOWUSER2|
WRF_ALLOWUSER3|WRF_ALLOWUSER4,
WRF_NONE = WRF_NOFIRE|WRF_DISABLESWITCH,
}
/*
A wrapper for SetPSprite.
Basically SetStateLabel for weapon overlays.
I'm not sure if the player null pointer check is necessary...
*/
action void SetWeaponState(statelabel st,int layer=PSP_WEAPON)
{
if(player) player.setpsprite(layer,invoker.findstate(st));
}
/*
These are shortcuts for checking player input.
Obviously you can just type out the whole thing, but if you're
in the middle of typing out lots of different potential combos
you will have plenty enough to keep track of without trying to
remember exactly what that long string is that you need to get
in order to do the check.
*/
action bool PressingFire(){return player.cmd.buttons & BT_ATTACK;}
action bool PressingAltfire(){return player.cmd.buttons & BT_ALTATTACK;}
action bool PressingReload(){return player.cmd.buttons & BT_RELOAD;}
action bool PressingZoom(){return player.cmd.buttons & BT_ZOOM;}
action bool PressingUser1(){return player.cmd.buttons & BT_USER1;}
action bool PressingUser2(){return player.cmd.buttons & BT_USER2;}
action bool PressingUser3(){return player.cmd.buttons & BT_USER3;}
action bool PressingUser4(){return player.cmd.buttons & BT_USER4;}
/*
EDIT: Have a couple more:
*/
action bool PressingUse(){return player.cmd.buttons & BT_USE;}
action bool JustPressed(int which) // "which" being any BT_* value, mentioned above or not
{
return player.cmd.buttons & which && !(player.oldbuttons & which);
}
action bool JustReleased(int which)
{
return !(player.cmd.buttons & which) && player.oldbuttons & which;
}
/*
You can set up these shortcuts for any player input,
and any arbitrary combination thereof:
"TurnSpeed", "ForwardMove", "CrouchRunning", etc. etc. etc.,
and you can of course add new variables to the weapon to keep track
of certain states at certain times...
(I don't actually use these examples below, as these situations don't come up
in HD enough to call for them, but some of my bullet point examples can use them.)
*/
action double TurnSpeed()
{
return((double(player.cmd.pitch),double(player.cmd.yaw)).length());
}
double LastCheckedTurnSpeed;
action bool JustPressedFire()
{
return (player.cmd.buttons & BT_ATTACK && !(player.oldbuttons & BT_ATTACK));
}
int PreviousTicsButtons[16];
}
class ComboSSG : ComboFriendlyWeapon
{
states
{
ready:
SHT2 A 1
{
/*
If you press Fire, the weapon will flash in the FireLeft state
on layer number 1000. Presumably this will consist of a flash
of the left barrel and code to fire the appropriate bullets and
deplete the appropriate ammo.
*/
if(PressingFire()) SetWeaponState("fireleft", PSP_FLASH);
/*
Same thing with Altfire and FireRight. Note that there is
NOT an "else" here - if you hit both buttons both barrels will go off.
This is why it's important to make sure these flashes are
in different layers.
This might look really bad depending on how your flash sprites are made.
In that case you might want to have a third condition at the very start
of this function that checks for both buttons and returns if the result
would have both barrels going off at exactly the same time, e.g.:
if(PressingFire() && PressingAltfire())
{
SetWeaponState("fireboth", PSP_FLASH);
return;
}
*/
if(PressingAltfire()) SetWeaponState("fireright", PSP_FLASH2);
A_WeaponReady(WRF_NOFIRE); //not "none" as we still want to deselect
}loop;
}
enum SSGNums
{
//you could just type "999" right in the SetWeaponState call,
//but you could regret that later.
PSP_FLASH2 = 999,
}
}
Last edited by Matt on Thu Jun 25, 2020 2:39 pm, edited 11 times in total.