Dynamic Properties/Calculated Properties & Province values


another newbie question, as I’m working my way through my first module. I want to create a few properties which change the economic value of a province and am having difficulties figuring out the best approach. I wanted to get some advice if possible before going too much further.

The map has stationary pieces defined for each province (Alsace, Champagne, Normandy, etc.) with layers representing different national control (Prussian_Control, French_Control), which I’ve harnessed to a global property with a basic economic value. This works fine to show national totals, but I want to be able to incrementally increase the value of individual provinces to reflect population growth (for example, Champagne up from 3 to 4). Additionally, I want to have a set of modifiers which can act as overall multiples to a basic economy (thus French productivity could grow from a base 1 to 2, etc. applied to all French_Control provinces).

I am running into a bit of difficulty setting this up through a dynamic property and wondered if anyone has any suggestions. Possibly the base economic values would be better off as dynamic properties as well. Any advice would be greatly appreciated.



Would you mind adding few images for the readers to easier visualize what your aim is?

Thanks for the quick reply. I’m adding a few images of the project.[attachment=2]Game image.png[/attachment]
This is an excerpt of the map with provinces - each with a layer trait representing different national control.

The following are a bit blurry, but I can talk you through them.
Set Global Property Trait attached to province:
[attachment=1]Global property image.png[/attachment]
Property name is EconTest, incrementing the province at a value of 3. The Global Property EconTest is defined at module level as numeric.
This is fired by a GKC using matching trait Control_Name=$French$ referring back to the layer names:
[attachment=0]Global key command.png[/attachment]

I’ve been experimenting with Dynamic Properties to alter this, and have the following one almost working correctly. I’ll send this in a following message.

The Dynamic Property is as follows:
[attachment=1]Dynamic Property.png[/attachment]
What I was going for here was something that could be used to increase the economic value of an individual province. I set up a Report Action Trait as well as a GKC to try to get this to work:
[attachment=0]Agriculture GKC.png[/attachment]

This is a bit clunky and doesn’t quite work yet, so I’d appreciate any thoughts on how to improve this. So, this was the first goal - being able to adjust individual provincial values by adding increments.

The second goal was to create a general modifier that would multiply the value of an entire country’s economy, again thinking that a Dynamic Property would work.

Let me know if I can send more information, and thanks again for your help,


I see.

Please review my assumptions below:

  • there are two sides that can control a province;
  • each province has a certain economic value;
  • economic value of each of provinces is further modified by a global economic value of a country to which province belongs.

Say, there is Champagne of economic value 4, but when France global economic value is increased by 1, Champagne economic value raises to 7 (= 4 + 1 * 3).

Does global economic value (for each country) have a cap? I assume yes (for the 2nd approach, for 1st it is irrelevant).

There are two ways, if I understand your problem correctly. Both give roughly same effect but one is much more tedious to setup (unless you tinker with buildFile):

  1. First approach would be to use Labels. Say, you have counters for control (France / Prussia) and on these counters you would also display economic value (like $ProvinceValue$*$Control$Value, where $Control$ is either France or Prussia; you would also need to set up Global Properties named FranceValue and PrussiaValue each of them representing global economic value of corresponding country.

  2. Second approach would be to use Layer Level following expression value. Each level in layer would represent one economic value of province. You would have as many values as the ( max cap for global economic value for France + max cap for global economic value for Prussia ) * max cap of economic value of province. I think I implemented something similar in Ici, c’est la France module–there are unmovable counters in each province that represent their allegiance and … and something else. Like, there are two quantities. I’ve made like 80 or so layers for each of these counters and crafted expression that factored to Global Properties into one number which in turn represented Layer Level. Yap, I think it could work too in your case.

Hi again,

regarding assumptions,
*there is the possibility of up to six players that can control a province;
*the other two assumptions are correct.
The global economic value will have a cap. I also see the economic value working as you suggest generally, with your example of Champagne with base value of 4, possibility of incrementing +1 to 5; then with a national global economic value acting as a multiplier, (say, France has a 2, giving 52 for value of Champagne).

Regarding the approaches, I had initially thought about using a control counter which would harness economic values, but haven’t experimented with it yet, and am moving in the direction of the second. I am a bit confused though - I thought I would need to work this through Dynamic Properties rather than directly through layers. I was looking through your module Ici, c’est la France, at some of the uses of layers for allegiance, (SupportLevel1Allegiance01 for example). I can see how this shifts through support level images, but am unsure how these interact with other components. Of course, I may be getting this completely wrong. Am I on the right track looking through your Population track as a starting point for how this could be modelled?

Yes, you should look at POP track and information display (right-hand side of menu bar, button “Show / hide info”). Clicking on it reveals otherwise hidden counters that show allegiances and the other trait name of which I forgot. Adjustments made POP track (through the buttons or by pressing ctrl-A on a marker), change values stored in Global Properties which in turn change level of the layer displayed on “hidden” counters.

What you would do:

  • open POP track,
  • on main map click on “Show / hide info” so that hidden markers show up;
  • on POP track click on +1 PS;
  • observe how “hidden” (now revealed) markers change.

I haven’t had time to read through this thread, but I wanted to chime in with some high level advice. I’ve made a Vassal module for my own game design that has the same level of rules enforcement as a product one might purchase on Steam. I learned 2 EXTREMELY important things about Calculated Properties versus Dynamic/Global Properties and Global Key Commands.

  1. Do not fear massive arithmetic expressions for Calculated Properties. Modern computers are incredibly fast. Thousands of times faster than the when I first started programming in the 80s. Unless Vassal or Java requires massive overhead (lots of code performing junk I have no control over), calculations that are daunting if done by hand, are completed in a nanosecond. I routinely add expressions like this one to every piece, and it doesn’t affect performance one iota. This counts how many pending battles are in all 55 sea zones:

Integer.toString(GetProperty(“Adriatic Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Aegean Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Alboran Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Balearic Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Baltic Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Barcelona_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Barents Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Bay of Biscay_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Bothnian Bay_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Bothnian Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Canadian Coastal Zone_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Celtic Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Central Atlantic Ocean_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Central Mediterranean_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Cilician Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Denmark Strait_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Eastern Black Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Eastern Mediterranean_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Eastern Sea Frontier_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“English Channel_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Fladen Ground_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“German Bight_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Greenland Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Gulf of Cadiz_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Gulf of Finland_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Gulf of Sirte_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Gulf Sea Frontier_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Iberian Coast_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Indian Ocean_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Ionian Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Irish Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Irminger Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Levantine Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Libyan Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Ligurian Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Nordkapp Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“North Atlantic Ocean_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“North Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Northern Caspian_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Norwegian Leads_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Norwegian Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Red Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Sea of Azov_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Skaggerak-Kattegat_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“South Atlantic Ocean_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Southern Bight_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Southern Caspian_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Tyrrhenian Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Western Approaches_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Western Baltic_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Western Black Sea_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“Western Mediterranean_Units”)).replace(“0”,"").length()/2+Integer.toString(GetProperty(“White Sea_Units”)).replace(“0”,"").length()/2

NOTE: The reason why it’s “instantaneous” is that every property is a Global Property that was determined BEFORE this calculation was performed. If those properties had to be determined on the fly, that expression would be a performance trainwreck…

  1. GKCs or any of the new SUM / COUNT operations DO have massive Vassal and Java overhead. Even an operation that affects only one piece can create a perceptible delay. I’d estimate that these functions are several orders of magnitude more CPU intensitve than my ridiculous Calculated Property above. The trick with these operations is to make them “on demand”, meaning they should never be associated with Calculated Properties or anything else that occurs in the background without direct player interaction. I use Dynamic Properties and Tiggers to invoke these types of operations at moments a player doesn’t even notice. For instance, I avoid them when a player is moving a piece. Players expect instantaneous response when they are dragging and dropping a piece. Any lag is immediately noticed. But if a player flips a counter or uses a Send to Location, there is a quarter second delay as their brain is looking at the new image and taking it all in. If you perform a GKC or SUM / COUNT at that instant, players will never notice that delay. Programmer sleight of hand…

My main point is that it doesn’t matter if you use “brute force” to implement your economic system, as long as players don’t perceive a time delay, your code can be ugly as all heck. Players will never know…

Thanks for both of the above replies. I’ll look deeper into the POP track as suggested - I thought this worked really well visually and was a very elegant solution.
In reply to m3tan, I appreciate the advice - particularly before I try to set a global command key to 120 province pieces. I have been working with a calculated property, and can get it to set a value, but I am unable to now get this to work with multiple province pieces.
I set a Calculated Property {(If(Control_Name==“French”,EconTest,0)} with Control_Name referring to the layer trait assigning different countries to control of a province piece, and with EconTest being a Dynamic Property with the value of a single province. I selected the province, hit a control key to trigger a report on the economy, which all worked.

When I tried to go the next step, copying the Calculated Property and Dynamic Property into a second province, it only read the value of the province I had selected. So I renamed the Calculated Properties for each province (Normandy & Champagne) and created a new Calculated Property with the expression Normandy+Champagne, but again when this was triggered, it only read the value of the province I had selected when running the property.

This probably comes down to having no experience with computer coding before this summer, but I wondered if I was missing something simple. I was hoping the next step was to create a long expression like you suggested, incorporating each province, but need to get this working with two provinces still.

Just a quick tip: don’t use the “If(x,y,z)” function if you can possibly avoid it. The ternary operator “x ? y : z” is much more efficient; in particular, it avoids evaluating the incorrect condition, whereas “If()” always evaluates both the true and false conditions.

For example, your above Calculated Property would be {Control_Name=="French" ? EconTest : 0}

Calculated and Dynamic Properties are specific to the Piece they are defined in. That’s why they only work for the selected Piece. If you want a property to be seen by every Piece on a map, you need to define it as a Global Property. Every Piece sees a Global Property, but the downside is that their value can only be changed by Set Global Property. There is no equivalent of a Calculated Property at the global level.

Jrwatts - thanks for the tip on the If function. I will try to avoid it and experiment a bit with the “x ? y : z” function, particularly if it keeps things running more efficiently. As I may end up with a few large Global Properties, I’m sure it will be important to keep them functioning smoothly.

Ah, this makes sense now. I don’t know how many times I tried reworking Calculated and Dynamic Properties, and still couldn’t get them to run on more than one piece. I ended up setting a Dynamic Property in every province piece that could increment population +1, then created a Global Property that could read all these values, while filtering out provinces based on national control. I had over 120 provinces and was able to run this without any lag, so will probably stick with this method.

Thanks to everyone for the assistance - it was much appreciated and helped reduce the learning curve.