HDest-Community / Ugly-as-Sin

Enhancement modules for Hideous Destructor
zlib License
21 stars 17 forks source link

add respirator token #184

Closed FDAapproved closed 2 years ago

FDAapproved commented 2 years ago

Adds an easily-check-able inventory token for whenever a respirator is active (worn) on a player's person, which parodies the status of respirator.activated. I am proposing this change for cross-mod compatibility, since without adding UAS as a dependency there's no easy way to check for this otherwise. If you are worried about the performance impact of this check on active players, staggering the check with (level.time % sometickamount) should alleviate that somewhat.

caligari87 commented 2 years ago

Not opposed to the idea in principle, but this is very much not how I'd like it to be done. The proper solution is using GZDoom's Service class to handle cross-mod communication.

I have not dug into this yet, though I've been meaning to. The wiki page is also a bit confusing and needs revised. In any case, I believe it would look something like the following on UaS:

class UaS_RespiratorStatus : Service {
    // Returns -1 on any invalid condition
    override int GetInt(String request, string stringArg, int intArg, double doubleArg, Object objectArg) {
        // Do casting and nullchecks for player and respirator
        let p = HDPlayerPawn(objectArg); if (!p) return -1;
        let r = UaS_Respirator(p.FindInventory("UaS_Respirator")); if (!r) return -1;

        // Process the request
        if (request ~= "IsWorn") { return int(r.activated); } // Yes this uses an int return for a bool value...
        if (request ~= "AirRemaining") { return int(r.mags[0]); }
        return -1;
    }
}

And then your mod would use a ServiceIterator to find the UaS_RespiratorStatus service and call the GetInt() function. Very rough example (doesn't include pointer caching or proper iteration or nullchecking) but something like:

let i = ServiceIterator.Find("UaS_RespiratorStatus");
service RespStatus = i.Next();
bool RespActive = RespStatus.GetInt("IsWorn", objectArg:owner);

I have not tested, this is mostly theoretical and from memory. If you can make it work I'll gladly accept this, or I'll try to do it myself later.

Sidebar, please use the same brace style UaS does for your submissions 😉 Saves me feeling compelled to fix it later.

FDAapproved commented 2 years ago

Alright; I'll cancel the PB addon push and then see if I can get this to work first. It seems like much more overhead to achieve the same solution, though I'll trust your judgement here.

caligari87 commented 2 years ago

Performance-wise I think it's probably a wash. It's more about robust implementation and proper use of engine features. Inventory tokens have always been a hack borne out of necessity in the DECORATE era, and there's not really any reason to use them anymore..

In this case, using a token would certainly solve the problem with ease, is the respirator active or not? However it requires the respirator code to do double-duty (manage the presence of the token), and it can't be expanded in the future for other things. For example, reporting the available air supply would require a different token which also needs duplicated management code to set its amount. But using the service I can simply add another request type to the service class (with as little as one line, as shown), and it pulls data directly from the original object instead of requiring a separate handling process.

caligari87 commented 2 years ago

I've pushed an update which adds the service on UaS as described above. You can use the following example code in your mod to check whether a particular player number is wearing a respirator (or pass the player object pointer directly). This should work without creating a hard dependency of any kind.

bool IsPlayerUsingRespirator(int pnum) {
    ServiceIterator i = ServiceIterator.Find("UaS_RespiratorStatus");
    service RespStatus;
    while (RespStatus = i.Next()) {
        int RespActive = int(RespStatus.GetInt("IsWorn", objectArg:players[pnum].mo));
        console.printf("player "..pnum.." Resp "..RespActive);
        return RespActive;
    }
    return false;
}

Return values: