Upgrades

For AI and campaign script related discussions and questions
Per
Warzone 2100 Team Member
Warzone 2100 Team Member
Posts: 3776
Joined: 03 Aug 2006, 19:39

Upgrades

Post by Per » 26 Mar 2013, 23:23

I am working on changing the way upgrades work. What I currently have is javascript code to set upgrades directly, instead of having them referred to from research.ini and detailed in functions.txt. I considered putting inline javascript in the research.ini file, but ended up thinking that might be a bad idea, so now I am putting everything into rules.js. It will be a lot of info, so I'm not entirely sure how to structure it to make it easy to read.

The upgrades are set for each component and each player, making it much more fine grained than before.

Example code:

Code: Select all

// give all my units 500% armour bonus
for (var i in Upgrades[me].Body)
{
    Upgrades[me].Body[i].Armour = 500;
}

// give player 5's Retribution body a 500% armour bonus
Upgrades[5].Body['Retribution'].Armour = 500;
Related changes: Setting rules.js's 'me' global to -1, allowing it to receive all events. Changing eventResearched() to always have three parameters, where the second may be null (used to be undefined) when inapplicable, and the third parameter is receiving player.

Currently all upgrades are percentages. I am wondering whether some or all should instead be real values (eg if you want to change the armour value of one chassis from 3 to 7, it is slightly harder to calculate the right % value to do this than to just add 4).

If you have any ideas or other input on this, feel free to opine below.

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 26 Mar 2013, 23:42

If it's a lot of info, maybe have an upgrades.js that's included by rules.js?

If rules.js 'me' is now -1, how does rules.js know what human player it's related to? I assume we will have to focus on 'selectedPlayer' (the player the human is playing as on the local network peer) in rules.js?

As most current mods that alter rules.js assume 'me' will be same as 'selectedPlayer' it would be useful to mention in 3.2 release notes (and even comment at top of rules.js) the changed value of 'me'.

Percentages are good for modifiers, but for base values it would be better to use the value rather than a percentage.

I notice the capitalised first letters in your example code above - is the JS API moving more towards OOP or is it just for this specific aspect of the API?

I assume we will be able to now get a list of bodies, etc., simply by iterating the nested object? for (i in Upgrades[5].Body) .... -- That will be most useful :)

Do you have any docs explaining the structure of the nested object yet? Also, in your example code above I notice that you use 'me' - is there a -1 index in the Upgrades array? If so, is there a duplicate of the object it contains in Upgrades[selectedPlayer]?

Will this system be replacing the normal effects of research? For example, will rules.js now be listening to eventResearched() and depending on what tech it sees applying the relevant upgrade via JS (rather than WZ engine doing that)? How will the rules.js know what upgrade to apply -- will it be able to query research.ini?

Specifically, I imagine modders would prefer to keep all the effects listed in research.ini (regardless of whether they get applied via JS or in the WZ engine) rather than having to split some stuff out to rules.js.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

Per
Warzone 2100 Team Member
Warzone 2100 Team Member
Posts: 3776
Joined: 03 Aug 2006, 19:39

Re: Upgrades

Post by Per » 26 Mar 2013, 23:59

Yes, this will be replacing the normal effects of research. I don't know how to avoid splitting things out to rules.js (or some other .js file). The rules.js file have to know the particular research tree.

The capitalised first letter is to emphasise that this is a very different kind of object than anything else. This is very jarring to me, because it breaks so much with everything that has come before. But I see no other, clean way to do it. I also envision a similar 'Stats' object to hold the base values. (I forgot to say that you can of course read the object to get its value, not just set it, but maybe that was assumed.)

The 'me' in the example is just a remnant of old code. Ignore that part. Here is another example of how things would be handled in rules.js (for a single research item):

Code: Select all

function eventResearched(research, structure, player)
{
    if (research.name == "Vehicle Superdense Thermal Armor")
    {
         for (var i in Upgrades[player].Body) // TODO, check if cyborg armour
         {
                 Upgrades[player].Body[i].Thermal += 30;
         }
    }
}
Why do you need to know what human player a 'rules.js' is related to? It seems counter to what it was meant to do. However, you can get this info from 'selectedPlayer'.

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 27 Mar 2013, 00:34

Can you upload a sample of the revised script to pastebin or gist for us to look at? I imagine it's going to be a massive if..else or switch...case statement. While it will only get run when research completes, and thus not cause any performance issues, the size of the JS will surely be cumbersome and prone to human error.

What if the values are still defined in the .ini and get passed in via the 'research' object (a new .value property, or in fact, a property for each defined property for the associated section of the ini file)? That way we don't have to "hard-code" values in to the JS. I know the JS is easy to edit, so not really "hard coded", but I just feel that it would be much easier to work with the ini format for defining values.

Code: Select all

function eventResearched(research, lab, player) {
  if (research.name == "Vehicle Superdense Thermal Armor") {
     Upgrades[player].Body.forEach( function upgradeThermalArmour(body) {
        body.Thermal += research.value;
     });
  }
}
I imagine the final code would be more complex though, as we'd have to filter out cyborg bodies? Are you planning on making cyborg components less weird in their implementation as part of these changes?

Also, instead of having 'me' being set to -1, would it be better to have a new writeable global 'receiveAllEvents' (or whatever) which defaults to false (except in rules.js where it defaults to true). When it's value is true, the script will receive all players' events, when false only the 'me' player. This would mean that 'me' is always the player associated with the script, and avoid much confusion.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 27 Mar 2013, 00:38

...although me = -1 would still be useful for 'extra' scripts?
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

Per
Warzone 2100 Team Member
Warzone 2100 Team Member
Posts: 3776
Joined: 03 Aug 2006, 19:39

Re: Upgrades

Post by Per » 27 Mar 2013, 00:56

I haven't made much .js code yet, still just testing out stuff to see if anything looks nice enough.
aubergine wrote:What if the values are still defined in the .ini and get passed in via the 'research' object (a new .value property, or in fact, a property for each defined property for the associated section of the ini file)? That way we don't have to "hard-code" values in to the JS. I know the JS is easy to edit, so not really "hard coded", but I just feel that it would be much easier to work with the ini format for defining values.
What would you put in the .ini file, though? A value absent its usage would be really weird and non-useful, I think. What we have now is a function:value pair, at least (even though that pair is in another file). But that means having to hard-code what these functions mean, again, which is what I wanted us to get away from.

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 27 Mar 2013, 01:16

What if there was a 'context' property in the research.ini sections which, if defined, gives a sort of 'path' to what should be upgraded.

Rough example:

Code: Select all

function upgradeContext(research, player, category, item, property) {
  item
     ? Upgrades[player][category][item][property] += research.value;
     : Upgrades[player][category].forEach( function(item) {
         item[property] += research.value;
       } );
}

function eventResearched(research, lab, player) {
  if (research.context) {
    // eg. "Body.*.Thermal" (applies to all bodies) or "Body.Viper.Thermal" (only Viper body)
    research.context = research.context.split(".");
    if (research.context.length < 3) throw new Error("Invalid context specified for "+research.name);
    upgradeContext(
       research,
       player,
       research.context[0],
       research.context[1] == "*" ? null : research.context[1],
       research.context[2]
    );
  } else switch (research.name) {
    ... tech-specific effects ...
  }
}
Doing it this way means that for a lot of research items you can just specify a context and not need loads of bloat in the JS. But it also allows you to omit the context and have something far more specific defined in the JS.

How will weapon upgrades be applied - eg. ROF upgrade for machine gun, which could affect multiple machine gun weapons (and perhaps even some things derived from machine guns?)

How are team/alliance researches handled? Or will eventResearched() be called once for each player in a team (seems like overkill considering how much code is going to be in that event)?

Also, what did you think regarding my idea of having a 'receiveAllEvents' global (to avoid 'me' being -1 in rules.js)?
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

Per
Warzone 2100 Team Member
Warzone 2100 Team Member
Posts: 3776
Joined: 03 Aug 2006, 19:39

Re: Upgrades

Post by Per » 27 Mar 2013, 01:32

I don't see what the 'context' is going to look like on the .ini end of things, but I see that the required code on the .js side of things is already nasty and will get worse once all the corner cases crawl in. Seems cleaner to just use massive if...else if structures, then.

All component types suffer from the same problem - do we want to assume clear cut categories at the lowest level that we can apply values to -- or do we want to push this up to where the mod maker can change things. As you see above, I chose the latter for bodies, and intend to do the same for weapons. So the choice of what each mg upgrade will apply to would be made in the script. This will be a large amount of code but at least it will be consistent and very moddable. If you have better ideas, I am open to them.

eventResearched() will have to be called once for every player who gets a tech in a team.

Still thinking about the -1 == me.

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 27 Mar 2013, 02:45

Considering the size of the code that will be in eventResearched() why not call eventResearched just once (passing in the player who researched it) and then let the code in the event decide whether to apply that to team mates? This way you could choose to, for example, only give researches to allies in fixed teams but not dynamic alliances.

Instead of a massive if..else or switch..case, an alternative to handling research could be to create an object where the properties are names of techs and the values are functions.

Code: Select all

var applyResearch = {}
applyResearch["Vehicle Superdense Thermal Armor"] = function(research, lab, player) {
  ...
}
// etc...

function eventResearched(research, lab, player) {
  (research.name in applyResearch)
     ? applyResearch[research.name](research, lab, player)
     : throw new Error("Unable to apply research for: "+research.name);
}
One benefit here is that you could use the same function for multiple technologies and have the function do something different based on the research.name to reduce code bloat.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 27 Mar 2013, 02:56

Also, maybe groupSizes array in 3.2 should be renamed GroupSizes to indicate it's not a normal global?
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 27 Mar 2013, 03:03

Will AI scripts have read-only access to Upgrades object?
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

Per
Warzone 2100 Team Member
Warzone 2100 Team Member
Posts: 3776
Joined: 03 Aug 2006, 19:39

Re: Upgrades

Post by Per » 27 Mar 2013, 13:31

All scripts currently have the same access to everything.

User avatar
Shadow Wolf TJC
Regular
Regular
Posts: 1047
Joined: 16 Apr 2011, 05:12
Location: Raleigh, NC

Re: Upgrades

Post by Shadow Wolf TJC » 27 Mar 2013, 15:35

This thread sure got hot soon after it was started, so forgive me if my suggestions seem outdated:
Per wrote:I am working on changing the way upgrades work. What I currently have is javascript code to set upgrades directly, instead of having them referred to from research.ini and detailed in functions.txt. I considered putting inline javascript in the research.ini file, but ended up thinking that might be a bad idea, so now I am putting everything into rules.js. It will be a lot of info, so I'm not entirely sure how to structure it to make it easy to read.

The upgrades are set for each component and each player, making it much more fine grained than before.

Example code:

Code: Select all

// give all my units 500% armour bonus
for (var i in Upgrades[me].Body)
{
    Upgrades[me].Body[i].Armour = 500;
}

// give player 5's Retribution body a 500% armour bonus
Upgrades[5].Body['Retribution'].Armour = 500;
Related changes: Setting rules.js's 'me' global to -1, allowing it to receive all events. Changing eventResearched() to always have three parameters, where the second may be null (used to be undefined) when inapplicable, and the third parameter is receiving player.
This kind of change would also open up new opportunities for modding outside of merely applying upgrades upon researching stuff. Like, for example, certain units gain bonuses while a certain building is present.
Currently all upgrades are percentages. I am wondering whether some or all should instead be real values (eg if you want to change the armour value of one chassis from 3 to 7, it is slightly harder to calculate the right % value to do this than to just add 4).

If you have any ideas or other input on this, feel free to opine below.
I wouldn't mind at all if upgrades were made into real values (like +1 armor per upgrade, +4 damage-per-shot to Twin Machineguns, etc.) instead of percentage values, assuming that every possible component or structure were upgraded on a case-by-case basis. I could always apply percentage upgrades by multiplying upgrade values by some percentage of themselves. :wink:

By the way, if we're going to handle upgrades on a case-by-case basis using JavaScript, then we may want to outsource the code to an .inc file that is included in rules.js.
Creator of Warzone 2100: Contingency!
Founder of Wikizone 2100: http://wikizone2100.wikia.com/wiki/Wikizone_2100

User avatar
Duha
Trained
Trained
Posts: 287
Joined: 25 Mar 2012, 20:05
Location: SPb, Russia

Re: Upgrades

Post by Duha » 27 Mar 2013, 16:46

Code: Select all

function eventResearched(research, structure, player)
{
    doUpgrade(research,  player);
}

# hash of handlers   res.name: function 
researchHandlers = {
    "Vehicle Superdense Thermal Armor":  upgrade('Body' , 'Thermal', 30)       # + 30
    "Vehicle Superdense OtherT Armor":      upgrade('Body' , 'Thermal',  '30%') #  * 1.3
}

# find fuction and call it with params  if not funct do nothing.
function doUpgrade(research, player) {  researchHandlers[research.name](player) }

# return simple upgrade function
function upgrade(selector1, selector2, param) {return function (player) { update code here }}
Other way:
simpleUpgrade function can be placed to ini.
Function names can be placed in ini and described in research.js
http://addons.wz2100.net/ developer

User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Upgrades

Post by aubergine » 27 Mar 2013, 17:33

@Duha: Having JS functions in .ini would be grim IMHO. But I like your general idea of researchHandlers. We could also use things like function currying to cut down on code bloat.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO

Post Reply