Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
What is the standard way to do your own custom events?
E.g. suppose I have some integer value I keep track of that is a custom resource (e.g. the amount of mushrooms the player has collected). I want a trigger to fire off when they reach some quota.
If that quota changes use a real variable event.
Example:
MyRealVariable > 0
That will cause a trigger to fire off whenever the variable is greater than 0.
If it is a certain value then use MyRealVariable == myValue
That will cause it to fire only when it hits that value.
/*
* EVENT_PLAYER is a global player variable.
* so is EVENT_UNIT. Names are made up ofc. --> eventUnit...
*/
set EVENT_UNIT = unit//GetUnit(anywhere)
set EVENT_PLAYER = Player(0)
set EVENT_MY_EVENT = 0
set EVENT_MY_EVENT = 1
/*
* code
*/
and
JASS:
private function fire takes nothing returns boolean
if not IsUnitAlly(EVENT_UNIT, EVENT_PLAYER) then
//blabla blub
endif
return false
endfunction
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "EVENT_MY_EVENT", EQUAL, 1)
call TriggerAddCondition(t, function thistype.fire)
Jeez I should have consulted the API first. I didn't know variable events existed. Incredibly helpful!
But why is the variable in quotes? Is that how you tell JASS to not actually evaluate it?
Also does the variable need to be in all caps or any other special format?
Edit: On second thoughts this event is nearly useless. I assume you can't have TriggerRegisterPlayerVariableEvent?
For example, suppose I have 12 variables, one for each player. In the body of the trigger conditions, how do I infer which player the variable belongs to?
Also, how do I get a variable name out of a variable? Again this isn't too useful if it has to be hardcoded in...
Pseudo-language. You make a container for the function you are going to run. This container can be inserted into various lists, which are fired off from the specific event type runway. The event should be run with event responses in order to deliver data to the target function.
Triggers with TriggerAddCondition/TriggerEvaluate are suitable. Such allow the dynamic call of functions.
Apologies for my ignorance but I cannot understand your explanation. It would help if these questions could be directly answered (sorry for my lack of understanding :[).
1. How do I convert a variable into its string variable name?
I suppose that this is impossible directly. I'd have to make some kind of table or array that stores the variable names. But that wouldn't work, because the optimizer changes the variable names, so the mapping would break.
2. Do array members also count as variables? I am guessing not, so you cannot register array members.
3. Given a variable event, how do I infer which player that variable belongs to? Suppose I have 12 variables keeping track of some arbitrary value for each player. I write a trigger for say variable 5, which is player 5's value. How do I tell that that variable "belongs" to player 5?
Positive. But there is no dynamic variable pointer anyway. I guess you are referring to the TriggerRegisterVariableEvent, there you state the identifier of a global variable as string because you cannot pass variable references in jass. TRVE does not hand out event responses like GetTriggerVariableName() and what would a GetTriggerVariable() return? There is no type for that. So converting variables into their string names makes only sense if you are macroing code.
3. Given a variable event, how do I infer which player that variable belongs to? Suppose I have 12 variables keeping track of some arbitrary value for each player. I write a trigger for say variable 5, which is player 5's value. How do I tell that that variable "belongs" to player 5?
You do not need separate TRVE-variables for that. Those are just meant to run the specific trigger/function. Everything else are event responses (parameters) you can transfer. You can do it like I have shown above, allocate an EventResponse object, attach the various data to it, set it as the 'current' globally, then in the target function immediately read the needed information.
However, I was not referring to TRVE. TRVE is only convenient for GUI users because they can write it in the event-block. Jass offers you more possibilities. Instead of marking the triggers with TRVE and setting the variable to call, you can save the triggers themselves and fire them off with TriggerExecute/TriggerEvaluate. You just have to list them up, which you do in the register function, then traverse this list when the event type occurs. You can even have multiple lists because for example you said you want them player-related. Then you attach a list of triggers to a player each. When the event type occurs, you have the player and take its personal execution list to iterate over. This has a larger overhead in registering and in the fire function but the advantage is that you can make more specific events, you do not have to filter for the player in the target function then.
I tried reading through your tutorial, but came to the same conclusions as the commenters. I did not follow it at all.
Perhaps we could simplify things before abstracting to a high level like in your examples.
But first let me get this off my head.
Is it possible to write something like this (keep in mind it is very high level, but you should get the idea).
JASS:
//I know there is no type in JASS for variable, but the idea is
//you can create variable based events for any variable,
//including those generated dynamically
function createVariableEvent takes integer pid, variable someVariable, integer value returns nothing
//the requisite code for creating the trigger/event
//that fires off when the passed variable is
//at the given value passed into the function
endfunction
So I am guessing it is NOT possible to create variable based events off of dynamically generated variables, i.e. local variables (in fact I don't think there's a way to create new variables which aren't locals, so never mind).
So going back down to a low level description, here is a hypothetical situation. I have 12 variables, each called var_i, where i is the identifier, e.g. var_1, var_2, var_3, etc. I want to call arbitrary code when var_i is equal to some value, call it varValue, only for the corresponding player. I also need to be able to recover the player from the event, so that in the event code I have access to the corresponding player object.
Now how would you write this out concretely in JASS? You mentioned it's not required to use TRVE. So what do I use?
E.g. suppose I have some integer value I keep track of that is a custom resource (e.g. the amount of mushrooms the player has collected). I want a trigger to fire off when they reach some quota.
library SomeShit
globals
private integer array int
endglobals
public function Add takes integer id returns nothing
set int[id] = int[id] + 1
if int[id] == 10 then // Keeping track on variable
call CustomDefeatBJ( Player(id), "Defeat" ) // You can fire "event" here
endif
endfunction
endlibrary
//
// Some code
//
// Your function where you collect mushrooms or whatever
//
//
call SomeShit_Add(GetPlayerId(GetTriggerPlayer()))
//
//Or you just add that above into code at first place if it is something small
//I know there is no type in JASS for variable, but the idea is
//you can create variable based events for any variable,
//including those generated dynamically
function createVariableEvent takes integer pid, variable someVariable, integer value returns nothing
//the requisite code for creating the trigger/event
//that fires off when the passed variable is
//at the given value passed into the function
endfunction
No, you cannot. Well could make the compiler hook on each global variable, to replace its setting/reading by functions, which would then call a common function with the variable as string identifier for example. And then you register those variables in createVariableEvent with the same identifier to see the connection.
You could make the compiler do it for each variable and reference but that's highly inefficient, because every variable that does not need it would be checked all the time.
Do it as Kobas wrote. Do not directly influence the variable's value from outside but make function wrappers for it, to parallel run events and whatnot when changing them. And from only there should you use TRVE/dynamic function calls.
I am still confused. Kobas' example gave a function, but how does it get called? I need an event for it do I not. Otherwise I won't know when to call, i.e. when the variable changes or the amount is equal to the requisite value. What event would I use where I would make that call..?
For the item collecting example it's simple because you have events like unit picks up item.
But suppose I have an integer value that gets randomized and I want to see if a player won the lottery (got some integer value). There isn't an event for that. What event would I use? Or do I just need to periodically check every so often, like "every 5 game seconds check this variable's value?"
What Kobas has shown is not the target function but the launcher.
You make a function "DrawALot" and that one rolls the dices in order to determine if event handlers of type "onWinningLottery" are run. Running these event actions is done dynamically and those are the target functions then.
You do not directly catch the setting of some variable, you write a function that sets both the variable and takes care of what it influences.
Yes, but what is the catalyst. You can't just fire off code magically.
It needs to be in the conditions of some trigger.
Here is a situation.
There is an integer which is randomized. Detect if it is a certain value and execute arbitrary code. What is the event? Do I just periodically check every X seconds for that condition?
JASS:
function main takes nothing returns boolean
...
if i == X then
//arbitrary code
endif
...
endfunction
function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterWHATEVENT???(t, ...)
call TriggerAddCondition(t, Condition(function main))
endfunction
Don't think about jass like you do with C++ or such languages.
Lolz use timer.
JASS:
library X initializer A
function B takes nothing returns nothing
if GetRandomInt(1,2) == 1 then
call BJDebugMsg("1")
else
call BJDebugMsg("2")
endif
endfunction
function A takes nothing returns nothing
call TimerStart(CreateTimer(), 1.00, true, function B)
endfunction
endlibrary
If I understand this correctly, it's going to immediately get a random int and check its value.
But what if I want to only do this checking at an arbitrary point/state in the game?
What about something like this...
JASS:
function main takes nothing returns boolean
local integer pid = GetPlayerId(GetTriggerPlayer())
if variables[pid] == someValue then
...
endif
...
endfunction
function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerStateEvent(t, Player(0), PLAYER_STATE_RESOURCE_GOLD, GREATER_THAN_OR_EQUAL, 0)
call TriggerAddCondition(t, Condition(function main))
endfunction
It will keep running main always, since you can't have negative gold. And it also lets me get "GetTriggerPlayer()" so I can check what value I'd like for a particular player.
I can destroy the trigger and also keep it off until I need it running.
Anyone have a better approach?
Edit: Problem with this---it only fires off if your gold amount changes. Still waiting for better solutions ^^
That trigger of yours will fire only when player red gold become larger than 0.
I will suggest checking gold each 1 second for each player.
JASS:
library TEST initializer Init
globals
integer array Gold
endglobals
function CheckGold takes nothing returns nothing
local integer i = 0
loop
if Gold[i] != GetPlayerState(Player(i), PLAYER_STATE_RESOURCE_GOLD) then
call DoWhateverOnGoldChange(i) //<- Player id as argument
endif
set Gold[i] = GetPlayerState(...)
exitwhen i > 12
set i = i + 1
endloop
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), 1.00, true, function CheckGold )
endfunction
endlibrary
Writing this without checking it in WE so care about exact names.
So highly complicated responses for such a simple question...
Use TriggerRegisterVariableEvent
The variable used for this should only be used for the event.
Then have more global variables that contain the data.
For instance, if your event has something to do with players, then make the global contain a player number.
Set the player number before making the event run.
Example Pseudocode:
Code:
function stuffHappens
call Debug(GetPlayerName(EventData))
endfunction
set EventData = Player(0)
set Event = 1
//stuff happens
set Event = 0//ready for next time
call TriggerRegisterVariableEvent(trigger,Event,EQUAL,1)
call TriggerRegisterAction(trigger,stuffHappens)
If I use TRVE, then do I not need the variable hardcoded in?
Native primitive types do not pass by reference, so setting a local variable to the integer and then passing it to TRVE does nothing.
Though my way doesn't solve that stuff either, but it does allow checking of other values that aren't variables (e.g. array members).
I suppose if I made a wrapper struct around an integer, then I could do it, since that struct and the one it was made by would share the same integer field...
If you want a generic event, then TRVE is for you.
Note that you can pass pretty much any amount of variables through globals.
If you want lots of custom events, then make them functions and store them in a hashtable (because you can't store them in arrays).
You can still pass any amount of variables.
TRVE realizes a list of triggers that get run when the variable changes. Thus the variable represents the event type. But you should conceal the implementation and use a function instead of writing TRVE everywhere.
Also, as addressed before, TRVE lacks some flexibility. It's only suited for static events. In the scenario you described here, you wanted a player-related event. If you do not want to select the player from event responses in the target function but rather assign the event to the player directly, you see that each player should have a list of triggers to be executed. Therefore, each player would require a TRVE-variable. Now imagine the event holder is not something as static as a player but a unit for example that is created during runtime. You cannot allocate new global variables ingame. Furthermore you cannot deregister TRVE without recreating the trigger. Since you do not realize the list yourself, you have less options to handle it.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.