Page 1 of 8

ZScript "Standard Library" - Brainstorming

PostPosted: Thu Jan 19, 2017 5:31 pm
by Xaser
The idea's been vaguely mentioned before, but it's probably worth opening up a separate discussion channel, so...

Now that ZScript is a thing, there's way less burden on the developers to implement certain types of new features since a vast amount of things that previously required engine development can now be DIY'd in scripting. 'Course, that also means that the burden is now on us as a community to implement these things, and there's liable to be many common problems that folks will wish to solve in script-land.

The Idea: a quasi-official ZScript "Standard Library" would be a cool community project to undertake at some point, and I'm interested to hear what ideas folks may have for such a thing, just to get the ball rolling. I've got a few ideas I'll jot down myself at some point (standardized reloading and synthfire, for instance), but mostly I wanted to open the door for brainstorming.

As a quick word of warning, I'd advise folks interested in actually writing such a library to exercise caution before jumping headlong into things. Doing this correctly is a non-trivial undertaking -- there needs to be a clean, well-documented API and sane versioning/release scheduling, else folks will try and axe-murder you when you publish a version that breaks everything. :P

Anyhow, discuss. Or don't -- I'm a Xaser, not a cop.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Thu Jan 19, 2017 6:04 pm
by Gez
How about a modified item grab function replicating stuff like Smart Scavenger? E.g., you have 47 shells, you move over a shellbox, you know have 50 shells -- but the shellbox is still there and will give up to a full 17 shells the next time. Having such an override function ready to be plugged in would be useful. Same for medikits and why not armors.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Fri Jan 20, 2017 1:20 am
by Nash
I've been preparing a set of minimal ZScript demo WAD set. :D Here's what I have so far:

ZScript examples

Position object to player smoothly
How to iterate through objects
Draw something on the screen

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Fri Jan 20, 2017 4:50 am
by kodi
A radial "hitscan" function would be incredibly useful, something that automatically spawns a single puff on any shootable actor within the radius (sight check optional).

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Fri Jan 20, 2017 1:16 pm
by Major Cooke
Xaser wrote:As a quick word of warning, I'd advise folks interested in actually writing such a library to exercise caution before jumping headlong into things. Doing this correctly is a non-trivial undertaking -- there needs to be a clean, well-documented API and sane versioning/release scheduling, else folks will try and axe-murder you when you publish a version that breaks everything. :P


Structs and/or classes with static functions aught to be fine. For example I use the struct TIterator to ease creation of ThinkerIterator so I can stop looking it up so much.

Note this is moreso tailed towards what I need it for so a lot of stuff can be left out.

Code: Select allExpand view
// Contains the iterator flags and instructions.
Struct TIteratorProperties
{
    enum EItFlags
    
{
        ITF_EXACT =         1,        // Disregard inheritance.
        ITF_NOTID =            1 << 1,    // Ignore actors with a TID.
        ITF_NOSPECIAL =        1 << 2, // Ignore actors with a special.
        
        ITF_SAFE 
=            ITF_NOTID|ITF_NOSPECIAL,
    };
    
    enum EItInstructions
    
{
        ITN_DESTROY =        1,        // Destroy all actors found.
        ITN_LOGCOUNT =        2,        // Displays how many of the actors were found.
    };
}

//------------------------------------------------------------------------------
// TIterator
//
// -- Properties --
// * p             - Contains a copy of accessible flags.
// * caller        - The actor doing the distance check.
//
// -- Functions --
// * (!)ErrorMessage (count)
//        Logs a problem message to console.
//        
// * (!)FindActors (ActorType, instructions, flags, maxcount, dist);
//        Looks for ActorType based on flags and distance, then executes the given
//        instruction.
//
// * Setup (mo)
//        Inserts the caller into the struct so it may run properly.
// 
// * DoFindActors
//        A wrapper function which sets up the iterator itself, and calls 
//        FindActors inside the TIterator struct.
//
// (!) - Private function. Must be called on by another.
//------------------------------------------------------------------------------

Struct TIterator
{
    TIteratorProperties p;
    private Actor caller;
//------------------------------------------------------------------------------
    private void ErrorMessage (int count)
    {
        if (!caller)    return;
        String msg = "";
        Switch (count)
        {
            Case -1:
            {
                msg = "Iterator error: No ActorType specified";
                break;
            }
            Case -3:
            {
                msg = "Iterator error: No instructions passed";
                break;
            }
            Default:    break;
        }
        caller.A_Log(msg);
    }
    
//------------------------------------------------------------------------------
    void Setup(Actor mo)
    {
        if (mo)    caller = mo;
    }
//------------------------------------------------------------------------------
    void DoFindActors (Class<Actor> ActorType, int instructions, int flags = 0, int maxcount = 0, double dist = 0.0)
    {
        if (caller)
        {
            int count = FindActors(ActorType, instructions, flags, maxcount, dist);
            
            if 
(count < 0)
            {
                ErrorMessage (count);
                return;
            }
            
            if 
(flags & p.ITN_LOGCOUNT)
                caller.A_LogInt(count);
        }
    }
//------------------------------------------------------------------------------
    private int FindActors (Class<Actor> ActorType, int instructions, int flags = 0, int maxcount = 0, double dist = 0.0)
    {
        // Indicate something went wrong or its not set up properly.            
        if (!ActorType)        return -1;    // Actor type doesn't exist
        if (!caller)        return -2;    // No caller was passed
        if (!instructions)    return -3;    // No instructions were passed
        
        ThinkerIterator it 
= ThinkerIterator.Create(ActorType);
        Actor mo;
        int count = 0;
        while (mo = Actor(it.Next(flags & p.ITF_EXACT)))
        {
            if (((flags & p.ITF_EXACT) && mo.GetClass() != ActorType) || !(mo is ActorType))
                continue;
                
            
// Ignore actors with special and/or tids unless specified.
            if ((flags & p.ITF_NOTID) && mo.tid)
                continue;
            if ((flags & p.ITF_NOSPECIAL) && mo.special)
                continue;
            
            
// Don't take owned items.
            if (mo is "Inventory")
            {
                let inv = Inventory(mo);
                if (inv.Owner != null)
                    continue;
            }
            
            
// Make sure it's in range.
            if (dist <= 0.0 || caller.Distance3D(mo) <= dist)
            {
                count++;
                
                
// Now perform the instructions.
                if (instructions & p.ITN_DESTROY)
                {
                    mo.Destroy();
                    continue;
                }
            }
        }
        return count;
    }
}

//------------------------------------------------------------------------------
// IteratorBase
//
// Houses all the setup materials for inheriting from. 
// Don't spawn it directly -- Inherit from it and replace
// PostBeginPlay instead.
//------------------------------------------------------------------------------

Class IteratorBase : Actor
{
    
TIterator finder;
    TIteratorProperties p;

    override void PostBeginPlay()
    {
        Destroy();
        return;
    }
    
    Default
    
{
        +NOSECTOR
        
+NOINTERACTION
        
+THRUACTORS
        
+NOBLOCKMAP
    
}
}

//==============================================================================
//==============================================================================
//    Examples
//==============================================================================
//==============================================================================

// Removes ammo on the map and prints the number of actors removed, but only if
// they have no tid or special.
Class AmmoRemover : IteratorBase
{
    override void PostBeginPlay()
    {   
        int instructions 
= p.ITN_DESTROY|p.ITN_LOGCOUNT;
        int flags = p.ITF_SAFE;
        int maxcount = 0;
        double dist = 0.0;
        
        finder
.Setup(self);
        finder.DoFindActors("Ammo",instructions, flags, maxcount, dist);
        Destroy();
        return;
    }
}

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 8:41 am
by ZzZombo
Attach an object to another at certain offset, and angle, relative or absolute.
Copy properties from one actor to another, provided one is subclass of another, that's it, they are assignment-compatible. Create a copy of an actor.
Provide an alternative actor flag system where multiple sources of a flag would work with each other independently. So if two things make an actor invulnerable, removing one doesn't cancel the other.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 12:27 pm
by Enjay
I don't think that I am talented enough to write anything meaningful, but I fully support the idea of this script library. It makes a lot of sense and, if it takes off, I'm sure it will be very useful to a lot of people (myself included). I also suspect that the best/most useful/most frequently used might find their way into official support as part of a library within gzdoom.pk3.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 12:39 pm
by Rachael
Enjay wrote:I also suspect that the best/most useful/most frequently used might find their way into official support as part of a library within gzdoom.pk3.

That is unlikely. Remember the problem you had compiling Stronghold after official sqrt support was added?

Unless Graf can find a way to create exceptions for said library (where GZDoom overrides the library's internal functions instead of erroring out) I don't think it's likely anything will be added in. Unless - of course - said library was careful to prefix its function names, i.e. "ZComLib_FunctionName" etc - whereas GZDoom could opt to omit those prefixes.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 12:45 pm
by Enjay
Fair enough. That makes sense. I hadn't really considered the technical/conflict implications of such a thing. I was just sort of thinking out loud that if someone created a particular code-pointer-like function that was clearly pretty universally useful, that such a thing might be a possible candidate for official inclusion.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 1:13 pm
by Rachael
If someone wants to have that happen, then using prefixes would be a very good idea. Among other things, that would also prevent name clashes when someone actually does, later on, implement a function on their own in their own mod that also uses this library, but would later break because this library picked up on that function and the modder updated it.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 6:47 pm
by Xaser
Yeah, I'd absolutely advocate using a prefix -- or a namespace if that indeed becomes a proper thing following Graf's type system overhaul.

Speaking of, even though this is the bikesheddiest sub-topic, I'm thinking of what to name such a thing. I'd originally imagined calling the library "XT" (for XTended or some shiz) just to give all the functions a nice "XT_" prefix, but something like ZTL (reference to C++'s STL) or SZL (Standard ZScript Library) are obvious-maybes as well.

Either way, a ton of cool ideas thus far! That radial hitscan bit reminds me I've always wanted to make something like "A_ArcAttack" which melees all enemies within a cone, for better sword-y weapons and such. So that's on the mental tally as well.

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 7:43 pm
by Arctangent
Xaser wrote:"A_ArcAttack"

hey don't let everyone just use my signature move like it was nothing

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 7:51 pm
by Major Cooke
I'll see if I can supply a smooth transitioning from-one-spot-to-another using a sine wave, going from position A to B. Gonna be a little tricky...

Also, I say make it absolutely mandatory that authors of code exhaust themselves at documenting their functions and features. To this day I still look at all the undocumented zscript code and ask myself "What the hell does this even do?"

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 9:04 pm
by Rachael
Major Cooke wrote:Also, I say make it absolutely mandatory that authors of code exhaust themselves at documenting their functions and features. To this day I still look at all the undocumented zscript code and ask myself "What the hell does this even do?"

Time to get researching! The world is counting on you! You are our second most knowledgeable person on the subject!

Kidding, of course...

Except about that last part... :twisted:

Re: ZScript "Standard Library" - Brainstorming

PostPosted: Mon Jan 23, 2017 10:34 pm
by Major Cooke
I tried escaping that responsibility with my trusty steed.
Image

But he got into the horse nip. I'm going nowhere except a hospital if I even try to get on him.