Improved caller-to-target relationship

Like feature suggestions, but you've actually written code to make it happen. More likely to make it into the game than some random request in feature suggestions.

Moderator: Developers

Improved caller-to-target relationship

Postby FDARI » Tue Apr 03, 2012 12:45 pm

Every now and then I have wanted to have one (A) actor trigger an action on another actor (B), where it is also required that B knows of A. With a_radiusgive, the notion gained new relevance, as that feature gives us a viable way to affect actors we ourselves don't know.

I thought of a way. The explanation might be a little complex, but once you get it I think it is both simple and powerful.

Good things:
It lets you refer to the caller of a specific action without messing with the pointers of other actors at all.
It does not need to be saved (the stack is always empty at the start of a tick).

One thing it does not do: Let newly spawned actors see (as GLOBAL) the actor that spawned them. It doesn't work that way unless you force them to act DURING the spawn call, and they normally just appear and hang around until the next tick.

I added a new AAPTR-selector: AAPTR_GLOBAL. (complex wordsy) Most of the time, AAPTR_GLOBAL will return NULL/nobody. However, it can be set to the caller at the beginning of a function call (push to a stack), and restored to its previous value at the end (pop from stack). Currently, this can only be set from decorate. How? With a new keyword that can affect any state/frame: GLOBAL.

Let me show you (sample excerpt; something forces everything nearby to take its side):
Code: Select allExpand view
SomeActorFrame:
TNT1 A 2 GLOBAL A_RadiusGive("code_item", 200, RGF_MONSTERS)
Code: Select allExpand view
pickup:
// pickup state of code_item CustomInventory;
    TNT1 A 0 A_CopyFriendliness(AAPTR_GLOBAL)
    stop
When a code pointer is called with GLOBAL modifier, AAPTR_GLOBAL will refer to the caller of that action until that action is complete. Events triggered by and executed during this action can temporarily override AAPTR_GLOBAL, but only for their own duration. The moment the second modifying action completes, AAPTR_GLOBAL reverts to the original value.

So... Global keyword added to decorate. If nothing is executing with GLOBAL-modifier, AAPTR_GLOBAL is NULL. If something is currently executing with GLOBAL-modifier, the latest started action that is still running defines the GLOBAL-actor. (If the GLOBAL actor dies, the value for that actor becomes NULL; it's a valid value in this case, and can actually be used to test for the continued existence of an original caller.)

:: If I did explain that poorly, or overly complexly, would anybody please help clarify ? :: I really like this feature (suggestion) ::

Other changes introduced:
A_JumpIfTargetInLOS-flag; JLOSF_CHECKGLOBAL: When a non-player calls A_JumpIfTargetInLOS, the global pointer (AAPTR_GLOBAL) provides the target.
A_JumpIfTargetInLOS-flag; JLOSF_NOPLAYERBEHAVIOUR: Players behave just the way monsters do, never using aim/linetarget. (Normally they use their aim/linetarget, no matter what you specify.)

JLOSF_CHECKGLOBAL|JLOSF_NOPLAYERBEHAVIOUR: Check the global pointer for any actor
JLOSF_CHECKGLOBAL: Check the global actor for non-players, check linetarget for players
No flags: Check stored target for non-players, check linetarget for players
JLOSF_NOPLAYERBEHAVIOUR: Check stored target for any actor

In an A_RadiusGive-situation, this allows you to use such features as field-of-view and JLOSF_ALLYNOJUMP to refine selections and actions based on the giver.


And guys: Do you think any of this is at all potentially useful?
Spoiler: Desire for deprecation - A_JumpIfTargetInLOS vs. A_JumpIfInTargetLOS
Spoiler: Possible extensions of the GLOBAL pointer
Technical note: That DObject* / void* casting can probably be avoided by using some casting-free code down in the cpp-file, where using AActor directly isn't likely to be such a pain. I haven't tested it yet, but I expect I might be required to. (The only function that should clearly stay in .h-file is ClearTick, because it will probably be optimised away in release.)

NOTE/QUESTION: It is now possible for an actor to make a sight check on itself, by checking global on its own global frame; easy to avoid if you don't (somehow) want it though. ("####" "#" 1 GLOBAL A_JumpIfTargetInLOS("ICantTellYouButIKnowItsMe", 0, JLOSF_CHECKGLOBAL). Should there be a safeguard against it?
Attachments
PushGlobalPtr.zip
Patches from zdoom and (barely) acs
(3.11 KiB) Downloaded 27 times
Last edited by FDARI on Thu Apr 05, 2012 5:33 am, edited 1 time in total.
User avatar
FDARI
Bronies eunt domus
 
Joined: 03 Nov 2009

Re: Improved caller-to-target relationship

Postby Major Cooke » Wed Apr 04, 2012 8:28 pm

If I am interpreting this correctly then I think I can simplify it for you. Here's what I "believe" to be what you're saying:
Code: Select allExpand view
ZOMB A 10 GLOBAL A_Print("I'm in the AAPTR_GLOBAL pointer now until I come across a duration which I'm not with the GLOBAL word!")
ZOMB A 0 A_Print("And now I'm not anymore.")


This means these monsters can have code pointer manipulations done on them and, thanks to things like the A_JumpIfTargetInLOS, we "could" potentially use multiple GLOBALs, depending on how we set it up, i.e. two global monsters are in different sectors and won't ever see each other. I say "could" because I don't want to run the risk of assuming and/or breaking something...

-----

Now, this definitely sounds exciting. However, I had a bit of a more flexible idea in mind that can also mingle with the GLOBAL ability.

What if we made... custom actor pointers?

A_MakeActorPointer(AAPTR_USER_<NAME>, from AAPTR_WHO, int flags) - Defines a custom pointer that can be used by the calling actor and store it as a customized pointer. AAPTR_WHO is anything from the regular pointers.

Flags:
Default (0) - Non-global. Only the actor who makes it, or to whom it transfers to others with via A_TransferPointers will have it.
1 - Global. Any actor has access to this pointer, and any of them can immediately modify it to their desire.
2 - Overriding. Normally the actor who accesses any of these at any given time will not allow for other actors to have this pointer manipulated unless the actor belonging to this code pointer stops existing. This basically forces all to take this new actor, when called, and accept it as the code pointer for this field name.


A_ClearActorPointer(AAPTR_USER_<NAME>, int flags)

Wipes out this code pointer, similar to performing A_RearrangePointers(AAPTR_NULL, etc.)

Flags:
0 - Self only.
1 - Globally empties the user pointer.

I'm curious, could this be combined with that so the custominventory object be used to force the object to call GLOBAL on said monster by chance?
User avatar
Major Cooke
That's how my uncle stole christmas three times in a row in a single year. -DBT
 
Joined: 28 Jan 2007

Re: Improved caller-to-target relationship

Postby FDARI » Thu Apr 05, 2012 5:12 am

Let's think about the explanation of this feature again. A "global" frame will make its caller the global actor during the execution of its actor pointer. If the processing of the actor pointer encounters another global frame (By ACS, setting an actor's state to something that contains a global frame; or by giving custom inventory with a global frame), that global frame will in turn make its own caller the global actor. Like any other frame, it is capable of triggering ACS and decorate during its execution (ACS_executeWithResult, give-inventory-functions), so you can have any number of global frames triggered inside eachother. Presently (and perhaps for all time, if ever) the global pointer always refers to the last one started that has not yet completed its action.

The global actor: Is the most recent caller of a global frame that has not yet completed the action in that frame.
The global actor is retrieved by AAPTR_GLOBAL.
Global actor sight checks can be made with JLOSF_CHECKGLOBAL
Sight checks can be made to work equally for players and monsters with JLOSF_NOPLAYERBEHAVIOUR (note: Players only get their pointers populated incidentally, or by explicit action of a modder, so "equally" means same rules, not same results.)

In your code, actor would reach the first frame and find an action pointer with global modifier. It would add itself as a global actor, call and execute A_Print, and then remove itself. The rest processes normally (ZOMB A 10). If the duration were 0, the process would still be the same, only that the next frame would already be encountered.
Code: Select allExpand view
ZOMB A 0 GLOBAL A_GiveInventory("GivenByGlobal", 1, AAPTR_TARGET)
ZOMB A 0 A_GiveInventory("GivenByUnknown", 1, AAPTR_MASTER)
ZOMB A 0 GLOBAL A_RadiusGive("GivenByGlobal", 200, RGF_PLAYERS)
If "GivenByGlobal" is a custom inventory object, its entire pickup process will have access to the giver as the GLOBAL actor, because it is part of the give-inventory process that defines that global actor. Any frames in that item that use the GLOBAL-keyword will not have access to that global actor, because they override with their own caller (the recepient of the item).
User avatar
FDARI
Bronies eunt domus
 
Joined: 03 Nov 2009

Re: Improved caller-to-target relationship

Postby FDARI » Thu Apr 05, 2012 5:29 am

Custom actor pointers: Seems unfeasible, very demanding. Let's speculate.

The current actor pointer availability is by flag values in calls. There is no real naming, and our scopes are (actor-specific) and (global/static). For this to integrate decently, we'd have to do something tricky. Add a new data type to decorate, methinks, that can hold a string (custom pointer name) or an int (flag values). There are some niceties available to flag resolution (context-sensitive resolution: caller is player, caller is not player, caller is nobody) that will be less nuanced (caller is somebody, caller is nobody). If "caller is somebody" it would look for its own named pointers first. Then both somebody and nobody could look for globally available named values.

This, however, isn't all that good. Dynamic allocation of variables in an environment where unexpected things could happen to a long-living caller that should clean up its own memory usage.

Approach b: New decorate variable type (mobj, pointer, something)... Decorate can have global variables and actor user-variables already. The string|int trick might actually still apply, with strings referring to user-variable on calling actor or global variable. Wouldn't be dynamic, but would be more flexible.

It's still not easy, or all that feasible, or necessarily safe and stable. Perhaps I'll write it at some point, if I can get it right. Sounds like a "straight to ToB"-proposition.
User avatar
FDARI
Bronies eunt domus
 
Joined: 03 Nov 2009

Re: Improved caller-to-target relationship

Postby Major Cooke » Mon Apr 23, 2012 7:12 am

FDARI: Due to the new injection of the sqrt ability for DECORATE, it has unfortunately made this patch completely incompatible.

Would it be possible to adjust your patch to the latest zdoom revision?
User avatar
Major Cooke
That's how my uncle stole christmas three times in a row in a single year. -DBT
 
Joined: 28 Jan 2007


Return to Code submissions

Who is online

Users browsing this forum: No registered users and 1 guest