"How do I ZScript?"

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!)
Locked
User avatar
phantombeta
Posts: 2084
Joined: Thu May 02, 2013 1:27 am
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia with Vulkan support
Location: Brazil

Re: "How do I ZScript?"

Post by phantombeta »

Vaecrius wrote:Thanks, good catch - I totally forgot to consider that what with my adventures in mousecrash wackyland. (see closed bugs forum)

Another weird problem: I've got this code:

Code: Select all

...
        thinkeriterator it=thinkeriterator.create("HDWeapon");
        HDweapon hdw;
        while(
            hdw=HDWeapon(it.Next())
        ){
            if(hdw.owner==self){
                int blx;
                if(hdw is "Lumberjack"){
                    blx+=100;
                    if(hdw.weaponstatus[1]>=0)blx+=BLOX_BATTERY_LOADED;
                }else if(hdw is "HDPistol"){
...  
and so on, with each "HDWeapon" owned by the caller adding a different value to "blx".

Which works fine, except this ends up with "blx" being some obscenely high number unless I explicitly reset it with "int blx=0". Why is this? Shouldn't that int be re-initialized every time that block is run?
While ACS automatically sets all variables to 0, ZScript might not. I wouldn't be surprised it it had the same behaviour as C/C++ regarding uninitialized variables. (i.e. whatever garbage data was in the memory already becomes the variable's value). Of course, it could always be a bug.

(IMO all languages should initialize variables on definition. As a good example, D does this in a way that helps in finding bugs. Integers are initialized to zero, floating point types are initialized to NaN, etc.)
Pink Baron
Posts: 33
Joined: Wed Mar 16, 2016 4:19 pm

Re: "How do I ZScript?"

Post by Pink Baron »

Hi all!

During my ZScripting some weapon stuff, I came across an obstacle.
Long story short, I need to alter frame letters of single state, akin to A_SetTicks when it comes to their tick duration.

Code: Select all

	// Doom 2 shotgun - vanilla
	Ready:
		SHTG A 1 A_WeaponReady 	// Objective = if user has 20 or less shells, use frame B instead
		Loop
I've already tried the direct assignment (frame = "Expression"), but doesn't work as intended (in fact, it alters owner's sprite). I suppose overlays are used when doing additional sprites and the invocation doesn't help, either:

Code: Select all

	// Same shotgun, same state - does work as expected, except the frame part
	Ready:		
		SHTG A 1
		{
			If (Invoker.Owner)
			{
				Int ShellCount = Invoker.Owner.CountInv("Shell");
				If (ShellCount <= 20) {Frame = 1;} // 0 = A, 1 = B. Does alter user's sprite instead.
			}
			A_WeaponReady();
		}
If anyone can help me doing this without resorting to adding additional states (and thus possibly breaking some compatibility and similar stuff) it will be greatily appreciated.
Blue Shadow
Posts: 4949
Joined: Sun Nov 14, 2010 12:59 am

Re: "How do I ZScript?"

Post by Blue Shadow »

Shouldn't you be doing the frame change on the invoker?
User avatar
Xaser
 
 
Posts: 10772
Joined: Sun Jul 20, 2003 12:15 pm
Contact:

Re: "How do I ZScript?"

Post by Xaser »

@Pink Baron: Playing with the HUD state is slightly more roundabout, but doable. "Frame = 1" will affect the player sprite, as you've found out, but even changing it to "invoker.Frame = 1" will not have the desired effect -- it'll change the shotgun's pickup sprite (which will have no real effect since it's in your inventory).

Without diving too much into the details, HUD states (known as "PSprites" in engine-speak) are stored on the player -- calling "self.player.GetPSprite(PSP_WEAPON)" (assuming "self" is the player, which it is for weapon action functions) will return a reference to the player's current weapon state, which you can set the frame of.

See here for a quick sample where I do exactly this -- just copy that block and replace the "layer" variable in the GetPSprite call with the constant "PSP_WEAPON" (and "psp.frame = 1" of course), and it ought to what you're looking for.

Hope this helps!


[P.S. before someone mentions it: PlayerPawn actors do have a tantalizingly-named "SetPsprite" function, but this actually does a state change, whereas the OP here is looking for changing the frame of the existing state. In case anyone's ever wondering why you'd ever want to do this, take a look at the function I linked -- or DamNums, for that matter. It's a powerful technique and one of the cooler things you can do in ZScript that was unfathomable in DECORATE-land.]
User avatar
phantombeta
Posts: 2084
Joined: Thu May 02, 2013 1:27 am
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia with Vulkan support
Location: Brazil

Re: "How do I ZScript?"

Post by phantombeta »

How/where should I make things like RPG leveling systems or a custom status effects system for players? I remember reading this kind of stuff should be done in the DoEffect function of an inventory item actor, so that it always gets executed for the right PlayerPawn.
(I know how to make a RPG leveling system, I just wanna know how/where I should code it. As a Thinker created by the PlayerPawn? As part of the PlayerPawn's Tick function? In the DoEffect function of an inventory item actor?)
User avatar
Nash
 
 
Posts: 17433
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia
Contact:

Re: "How do I ZScript?"

Post by Nash »

Either on the PlayerPawn's Tick() itself, or on an Inventory's Tick() if you want the character system to be pluggable into NPCs too (so that you don't have to write code twice). For the latter, you'd manipulate the Owner's stats... the Tick() runs on its Owner as long as it's present in their inventory.

I'd usually choose the latter. You can differentiate filter who exactly your code is affecting by using the "is" operator, for example

Code: Select all

if (Owner is "PlayerPawn") // do stuff that only gets applied to players
if (Owner is "MyCustomNPC") // do stuff that only affects MyCustomNPC and their descendents 
For calculating damage, I imagine you make some new weapon functions that will read the character's stats and calculate stuff according to whatever rules you want. Not sure about armour but I'm sure you can look around and see how Armor is done in GZDoom and emulate/expand that to fit your needs.
Pink Baron
Posts: 33
Joined: Wed Mar 16, 2016 4:19 pm

Re: "How do I ZScript?"

Post by Pink Baron »

Xaser wrote:@Pink Baron: Playing with the HUD state is slightly more roundabout, but doable. "Frame = 1" will affect the player sprite, as you've found out, but even changing it to "invoker.Frame = 1" will not have the desired effect -- it'll change the shotgun's pickup sprite (which will have no real effect since it's in your inventory).

Without diving too much into the details, HUD states (known as "PSprites" in engine-speak) are stored on the player -- calling "self.player.GetPSprite(PSP_WEAPON)" (assuming "self" is the player, which it is for weapon action functions) will return a reference to the player's current weapon state, which you can set the frame of.

See here for a quick sample where I do exactly this -- just copy that block and replace the "layer" variable in the GetPSprite call with the constant "PSP_WEAPON" (and "psp.frame = 1" of course), and it ought to what you're looking for.

Hope this helps!


[P.S. before someone mentions it: PlayerPawn actors do have a tantalizingly-named "SetPsprite" function, but this actually does a state change, whereas the OP here is looking for changing the frame of the existing state. In case anyone's ever wondering why you'd ever want to do this, take a look at the function I linked -- or DamNums, for that matter. It's a powerful technique and one of the cooler things you can do in ZScript that was unfathomable in DECORATE-land.]
Thanks a lot - after understanding how layers are handled by the engine, it actually worked! :D

As a thankyou gift, here's a generalized function that does all of the above. It changes weapon's internal sprite to X frame, also can edit any other layer if desired. Works only inside anonymous functions though.

Code: Select all

	// Frame letter changer for weapon states/layers (or SetOverlayFrame, for that matter)(similar to A_SetTicks, but for frame letters this time). 
	// Inspired by Xaser.
	
	// Changes actual weapon's frame to X (where 0 is A, 1 is B, etc). 
	// If InputLayer is filled out, it will edit Y layer instead (see A_Overlay's ZDoom Wiki).
	// NOTE: Use it inside an anonymous function or it won't work
	
	Void SetWeaponFrame (int InputFrame, int InputLayer = PSP_WEAPON)
	{
		If(Owner.Player) // Check if weapon has an owner and it's a player
		{
			// Do retrieve user's (weapon) sprite from specified layer. Default one is PSP_WEAPON, which is used by the engine for the actual weapons
			// "PSprites" are HUD states, and they are stored into players
	
			PSprite WFrame = Owner.Player.GetPSprite(InputLayer);	
			if(WFrame && WFrame.CurState != null) {WFrame.frame = InputFrame;}	// Set input frame to the desired layer
		}
	}
User avatar
phantombeta
Posts: 2084
Joined: Thu May 02, 2013 1:27 am
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia with Vulkan support
Location: Brazil

Re: "How do I ZScript?"

Post by phantombeta »

How do I display text on the screen? I need to port this code from my mod to ZScript: (This is C code, meant to be compiled to ACS bytecode with GDCC)

Code: Select all

void LevelUp (int level, int attrPoints, bool log, string message) {
        SetFont (s"DBIGFONT");

        if (GetUserCVar (PLN, s"S7_MsgsOn")) {
            SetFont (s"DBIGFONT");
            HudMessage (HUDMSG_FADEINOUT | HUDMSG_LAYER_OVERHUD | HUDMSG_LOG * log, 16000, CR_UNTRANSLATED, 0.5k, 0.5k, 3.0k, 0.3k, 0.3k, 0.0k, "Level up!");
            SetFont (s"SMALLFNT");
            if (message == NULL)
                HudMessage_Str (HUDMSG_FADEINOUT | HUDMSG_LAYER_OVERHUD | HUDMSG_LOG * log, 16001, CR_UNTRANSLATED, 0.5k, 0.55k, 3.0k, 0.3k, 0.3k, 0.0k, s"You've reached level %d.\nYou have gained %d attribute points.", level, attrPoints);
            else
                HudMessage_Str (HUDMSG_FADEINOUT | HUDMSG_LAYER_OVERHUD | HUDMSG_LOG * log, 16001, CR_UNTRANSLATED, 0.5k, 0.55k, 3.0k, 0.3k, 0.3k, 0.0k, message);
        }

        PlaySound (0, s"Player/LevelUp", CHAN_UI);
    }
But I have no idea what to replace HudMessage with. The rest is pretty simple and obvious, but I can't find what function to replace HudMessage with.
User avatar
Gutawer
Posts: 469
Joined: Sat Apr 16, 2016 6:01 am
Preferred Pronouns: She/Her

Re: "How do I ZScript?"

Post by Gutawer »

Either use the RenderOverlay method in an EventHandler, or use a ZScript Status Bar (from a quick glance at your code, it looks like a RenderOverlay would be more appropriate here).
TheUndreamedBus
Posts: 2
Joined: Tue Sep 12, 2017 6:13 pm

Re: "How do I ZScript?"

Post by TheUndreamedBus »

Is it at all possible to have a health pack spawn from an enemy that heals for the damage done to the enemy minus the base health of the enemy. I was working on it, but im not sure how (or if you even can) change the defaults of a inventory item dynamically
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

Re: "How do I ZScript?"

Post by Matt »

CustomInventory item that heals an amount based on its amount

Enemy DamageMobj virtual override that tracks the amount of damage done to it

In death state, don't use the dropitem but instead have it spawn the above custominventory item and set its amount to the tracked damage minus the spawnhealth value, something like:

Code: Select all

actor iii=spawn("brutalhealingitem",pos+(0,0,32));
inventory(iii).amount=trackeddamage; 
TheUndreamedBus
Posts: 2
Joined: Tue Sep 12, 2017 6:13 pm

Re: "How do I ZScript?"

Post by TheUndreamedBus »

Thanks for the help, but I guess I should have specified im pretty new to this, most of this is completely alien to me. Maybe im a bit in over my head.
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

Re: "How do I ZScript?"

Post by Matt »

Sorry, I'm feeling a bit lazy and restless atm to come up with the full implementation, though you might want to check out this tutorial here and see if you can tweak that to make the zombieman to set the value of its clip drop to, say, the inverse of its health, to get a feel for how this sort of thing works.


Question of my own: I'm converting some old Decorate code that goes something like

Code: Select all

TNT1 AAAAAAAAAA 0 A_DoStuff
Would it improve performance to change this to

Code: Select all

TNT1 A 0 {for (int i=0;i<10;i++) A_DoStuff();} 
so it doesn't have to waste that time cycling through those states? Or is this sort of thing automatically optimized?

(asking because the former is easier for me to read, at least for less than 10 or if I don't really care about the precise number)


EDIT:
Spoiler:
Multiple frames actually runs very slightly faster! I would never have thought!
Last edited by Matt on Wed Sep 13, 2017 8:36 pm, edited 3 times in total.
User avatar
Jaxxoon R
Posts: 772
Joined: Sun May 04, 2014 7:22 pm

Re: "How do I ZScript?"

Post by Jaxxoon R »

Is there a quick-and-dirty way of detecting if an actor is moving against a solid line? I'm moving the actor by setting its velocity directly.
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

Re: "How do I ZScript?"

Post by Matt »

Like, to bounce it off the line? I can use a TryMove to see if there is a blockingline but someone else will have to reply with how to get the angle (which I'd like to know as well).
Locked

Return to “Scripting”