Jump to content

Triggers (split from A couple of suggestions)


Recommended Posts

As far as I know, Atlas does not rely on scripting at all - it's written in C++. As for simulation, AI and RMS, those need to be separate - if I download an RMS someone made, I don't want to have to worry that it might be hacking the simulation to give my opponent an unfair advantage, for instance.

It's an open source game. How would you prevent someone from hacking? OK, games will get "out of sync". But I'm sure it's relatively easy to cheat/hack anyway.

BTW: That means that you fear a scenario may include some cheats? I can't really think you would not play a scenario because of this ^^.

It may include:

Trigger event: playerEntersChatString("Hiho")

Trigger condition: chattingPlayer == getPlayerIdByName("FeXoR")

Trigger action: GivePlayerResources(getPlayerIdByName("FeXoR"), 1000000)

How do you want to avoid this? Though since it's a script it will be easy to find such hacks. But I think the best way to go here is trust ^^.

Edited by FeXoR
Link to comment
Share on other sites

It's an open source game. How would you prevent someone from hacking? OK, games will get "out of sync". But I'm sure it's relatively easy to cheat/hack anyway.

:huh: That's a poor argument. That's like saying: since there will always be some people breaking the law, we should not do anything to prevent it. It's not a tenable position.

BTW: That means that you fear a scenario may include some cheats? I can't really think you would not play a scenario because of this ^^.

It may include:

Trigger event: playerEntersChatString("Hiho")

Trigger condition: chattingPlayer == getPlayerIdByName("FeXoR")

Trigger action: GivePlayerResources(getPlayerIdByName("FeXoR"), 1000000)

How do you want to avoid this?

Simple: don't allow actions/effects like that unless every player agree to it. If you just throw all sorts of scripts into one scripting environment, you don't have that kind of fine-grained access control. They can even be overwriting each other's functions unintentionally - it would be a mess.

Edited by zoot
Link to comment
Share on other sites

To make triggers powerful all those functions have to work. Here are some examples for all used functions in the cheat - but this time ones making sense:

(TE: Trigger Event, TC: Trigger Condition, TA: Trigger Action):

TE: playerEntersChatString("-stats"), TC: true, TA: showPlayerBoard("kills", "losses")

TE: playerEntersChatString("-stats"), TC: getPlayerType(chattingPlayer) == "observer", TA: showPlayerBoard("kills", "losses", "food", "lumber", "stone", "mettal", "population", "populationCapActual")

An example for a king of the hill scenario:

TE: turnEnds(); TC: endingTurn % 100 == 0, TE: givePlayerResources(getVariable("kingOfTheHill"), 1))

TE: unitEntersArea("hill"), TC: getPlayerType(getOwnerOf(enteringUnit)) == "activePlayer", TA: setVariable("kingOfTheHill", getOwnerOf(enteringUnit))

Edited by FeXoR
Link to comment
Share on other sites

In WC3 there are e.g. the trigger event "generic unit event" just saying "a unit [action]". The action could e.g an action or "state change" like "attacks" or "gets attacked" or "dies" or "returns resources" or something. Then (for that specific trigger) some variables where set like "triggering unit" or "attacking unit" (if the units action was "attacks" both are the same but they get also set if the units action was "gets attacked"). So you could use those variables for e.g. the condition. You could set own variables and conversion functions where available like intToString(playerNumberOf(ownerOf(pickedUnit))) or something. It's hard to explain because it was mainly a programming language itself:

http://en.wikipedia.org/wiki/JASS

Here's an example of a function that sets the start locations for players on a random map I've written (and the triggers where never "meant" to be able to do that):

NOTE: I got the German version so some words may be in German (Most parts where not translated though, Auslöser = trigger, Ereigniss = event, Bedingungen = condition, Aktionen = action(s)).

NOTE: The trigger is NOT called by an event but from another trigger

Since the post editor refuses to allow indentation here's a text file and a screen-shot as well as the entire map (not sure how

playable it is, it's an older version):

Screen-shot: post-14196-0-29167300-1355970951_thumb.j

Text (not code!): Trigger Example WC3 - RMG - Place Start Locations.txt

As code: (12)FEXOR - RandomMap 2010-6-4 - Triggers.txt

Entire WC3 random map: (12)FEXOR - RandomMap 2010-6-4.zip

So to say it short: You could do close to everything (The minimap refuses to get updated if you change the terrain). If a function missed you could just convert the trigger in question to a script (like above the "(12)FEXOR - RandomMap 2010-6-4 - Triggers.txt") and add the function you need. Here's the documentation of what triggers can manipulate in the engine:

[still searching for it...]

More information can be found:

http://jass.sourceforge.net/doc/

http://www.wc3jass.c...page/Main_Page/

In AoK/AoC you could only add some types of variables and there where no type conversion functionality. That and the fact that no generic events like "a unit ..." could be used (and so somehow "pick" that unit and do something with it in the action) the possibilities where very limited (I not even managed to get a tower defense map to work properly). Instead only specific unit events could be used like a unit already placed in the map and named could be selected and then used for a trigger. Or any unit could activate the trigger but in the condition/action it was impossible to manipulate this unit because it was not stored.

Edited by FeXoR
Link to comment
Share on other sites

I see. But none of that really implies throwing all scripts into a big hodgepodge like suggested above. We can make one trigger API into the UI and another one into the simulation, without compromising their separation on non-trigger maps.

Edited by zoot
Link to comment
Share on other sites

It's not just about security, it's about integrity. What if two different scripts both define an object named 'MyObject' in the same scripting environment? You get some kind of collision, and one of the scripts break. These issues can be avoided by running separate things in separate environments.

Link to comment
Share on other sites

I see. But none of that really implies throwing all scripts into a big hodgepodge like suggested above. We can make one trigger API into the UI and another one into the simulation, without compromising their separation on non-trigger maps.

True. But isn't it a bigger mess if we need to write the same functions several times and maybe all acting differently?

E.g. wall towers placed ingame will always face the same side than one of the appending walls. In RMGEN that's not always the case (as you can see for Iberian civ bonus walls). Still both have to be compatible (They are AFAIK).

It's not just about security, it's about integrity. What if two different scripts both define an object named 'MyObject' in the same scripting environment? You get some kind of collision, and one of the scripts break. These issues can be avoided by running separate things in separate environments.

You say: "keep the name-space small" I say "keep the name-space unique" like e.g. calling every events TE_BlaBlub etc.

Then use the triggers to make random map libraries called like RM_wall_builder.js or something and import them wherever you like.

That way collisions can be avoided commonly. Even if you got a function called getEntity() in several libs you can import them save.

Darn, it's not Python! Bad, bad... still you could name them unique as mentioned above.

You could also bundle the libs in associative arrays/dictionaries when importing them (like Python does) and run each property of the dictionary after import. Ugly as well...

(The darn editor messes around with my pasted text and newlines again! Could anyone fix that???)

EDIT: I think in the end stands the decision how restrictive we whant to be. I don't like encapsulating and restriction. I'm aware of the problems arising with it from harder to debug scripts to the mentioned name-space problem as well as its harder to keep the overview over one big area of code. And I think it IS an argument that you can hack anyway in an open source game relatively easy. That means you have to grand the scripting community at least some trust. And I hope the "bad" maps/AIs/whatever will be sorted out by the community as well so it's a dynamic social process to keep things clean.

As said before I think for now we will not go this ways (making AIs/RMS based on triggers) anyway so we don't need to focus on that. But Triggers should be as close to the engine as possible to be powerful and fast.

In general I forgot something about triggers. They can be active or inactive at the beginning meaning they will be checked at event occurrence or not. The can also be "repeated" or "single shot" while the first will stay active after it fired and the second will turn itself off if fired. It then can be turned on/off again by other triggers actions.

Edited by FeXoR
Link to comment
Share on other sites

True. But isn't it a bigger mess if we need to write the same functions several times and maybe all acting differently?

We don't need to rewrite any functions, we just create a thin wrapper in each context, which then calls the original function.

E.g. wall towers placed ingame will always face the same side than one of the appending walls. In RMGEN that's not always the case (as you can see for Iberian civ bonus walls). Still both have to be compatible (They are AFAIK).

That sounds like an RMS feature request, more than a trigger feature request.

You say: "keep the name-space small" I say "keep the name-space unique" like e.g. calling every events TE_BlaBlub etc.

And what if someone else picked the same namespace? Also, namespacing does not prevent the type of error modes that may arise when two different scripts unknowingly manipulates the same globals: http://en.wikipedia....mputer_science)

EDIT: I think in the end stands the decision how restrictive we whant to be. I don't like encapsulating and restriction. I'm aware of the problems arising with it from harder to debug scripts to the mentioned name-space problem as well as its harder to keep the overview over one big area of code. And I think it IS an argument that you can hack anyway in an open source game relatively easy. That means you have to grand the scripting community at least some trust. And I hope the "bad" maps/AIs/whatever will be sorted out by the community as well so it's a dynamic social process to keep things clean.

It's not about trust, it's just bad design. Honestly, it's a bit like wanting to run everything as root just for the sense of power it grants :P

Edited by zoot
  • Like 1
Link to comment
Share on other sites

Here is a outline of how a trigger reader could work:

https://github.com/z...117879701525d07

If anyone wants to help out, fork dat repo and get on with it.

I don't know that it should be implemented in C++. For comparison, technologies (including template loading) were implemented entirely in JS. The advantage of using JS is it's much more convenient to access simulation and UI state (IMO) rather than passing it back through C++. It's also much more cumbersome to work with JSON on the C++ side of things -- assuming we'd want to use JSON to define triggers/conditions/effects.

For how this might work, we already parse and store the JSON map settings when reading a map, the same could be done with triggers (in a separate object I think) and then it would call some JS init function that initializes the "trigger manager" and parses the triggers object.

Accessing them in Atlas will be messy either way, because AtlasUI runs in a separate thread and uses message passing to communicate with the engine, however it does have a script interface and the ability to parse JSON objects (it does this for map and player settings).

Link to comment
Share on other sites

I don't know that it should be implemented in C++. For comparison, technologies (including template loading) were implemented entirely in JS. The advantage of using JS is it's much more convenient to access simulation and UI state (IMO) rather than passing it back through C++. It's also much more cumbersome to work with JSON on the C++ side of things -- assuming we'd want to use JSON to define triggers/conditions/effects.

For how this might work, we already parse and store the JSON map settings when reading a map, the same could be done with triggers (in a separate object I think) and then it would call some JS init function that initializes the "trigger manager" and parses the triggers object.

Accessing them in Atlas will be messy either way, because AtlasUI runs in a separate thread and uses message passing to communicate with the engine, however it does have a script interface and the ability to parse JSON objects (it does this for map and player settings).

I'm really not that sure about using json to define events/effects/etc. Wouldn't it be better to define the events in C++ and use them in a JS file that has access to the simulation (Something like AI)? This way we don't need to "define" effects. We can directly change the simulation state using its own functions.

Link to comment
Share on other sites

I don't know that it should be implemented in C++. For comparison, technologies (including template loading) were implemented entirely in JS. The advantage of using JS is it's much more convenient to access simulation and UI state (IMO) rather than passing it back through C++. It's also much more cumbersome to work with JSON on the C++ side of things -- assuming we'd want to use JSON to define triggers/conditions/effects.

For how this might work, we already parse and store the JSON map settings when reading a map, the same could be done with triggers (in a separate object I think) and then it would call some JS init function that initializes the "trigger manager" and parses the triggers object.

Accessing them in Atlas will be messy either way, because AtlasUI runs in a separate thread and uses message passing to communicate with the engine, however it does have a script interface and the ability to parse JSON objects (it does this for map and player settings).

After having looked at it, I am not sure how to do it entirely or almost entirely in JS. If it was done in JS, where would it run? If it runs in the simulation, how would it know of UI events? If it runs in the GUI, how would it know about simulation events - to my knowledge the GUI does not receive messages sent out by simulation components?

So unless there is some other way, I am leaning towards having a 'trigger manager' in C++ which both the UI and the simulation invokes to pass events and the like.

Edited by zoot
Link to comment
Share on other sites

Triggers are crucial for me as well. I can't count the hours I spent playing all the AOE campaigns and I think I downloaded & tried the majority of campaigns and scenarios on aok.heavengames.com (as well as making some scenario's myself).

I (personally!) don't care much about skirmish or AI battles, but I do enjoy the occasional Age of Kings LAN party with a couple of friends.

I agree with Mythos that we don't have the manpower to create campaigns/scenarios but I think if we could provide some triggers, we'll be amazed what the community will come up with. I can't wait to get started.

Link to comment
Share on other sites

  • 2 weeks later...

I decided to pursue a JSON-based solution anyway:

https://github.com/z...ompare/triggers

When I insert an element like this in a map's script settings, a trigger is created which shows a message if one of player 1's units is attacked.

So this would just need a few more conditions and effects and it would basically work. Of course, none of it is supported in Atlas, but it would still be good enough for making, say, a tutorial, by hand.

Link to comment
Share on other sites

  • 2 weeks later...

I decided to pursue a JSON-based solution anyway:

https://github.com/z...ompare/triggers

When I insert an element like this in a map's script settings, a trigger is created which shows a message if one of player 1's units is attacked.

I must have missed this. Even though it may not be the solution we end up using, it's still good to see your efforts (y)

Link to comment
Share on other sites

Something like:


def event = new TriggerEvent("unitGetsAttacked");
def condition = new TriggerCondition("playerToIndex(ownerOfUnit(attackedUnit)) == 1");
def effect = new TriggerEffect("sendMessageToPlayer('Player 1 is under attack!', 1)");
addTrigger(new Trigger(event, condition, effect));

I don't know how the code in maps work.

It's for sure bad to give code as a string.

But in principle my idea is like that.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...