Difference between revisions of "Trade-goods.plist"

From Elite Wiki
(Secondary market keys)
(Links: Added another and a useful quote from cim)
(18 intermediate revisions by 5 users not shown)
Line 75: Line 75:
  
 
=== market_script ===
 
=== market_script ===
The name of a Javascript file to use as a [[Oolite_Market_Scripts|market script]] for this trade good when calculating main station quantities and prices.
+
The name of a Javascript file to use as a [[Oolite Market Scripts|market script]] for this trade good when calculating main station quantities and prices.
  
 
=== name ===
 
=== name ===
Line 112: Line 112:
  
 
=== short_comment ===
 
=== short_comment ===
A string that will be displayed on the F8 commodity list screen in the optional extra column. This can be modified later by [[Oolite_JavaScript_Reference:_Manifest#setComment|manifest.setShortComment()]]. As the meaning of this value will depend on the OXPs installed, it is unlikely to be useful to specify an initial value in the plist.
+
A string that will be displayed on the F8 commodity list screen in the optional extra column. This can be modified later by [[Oolite_JavaScript_Reference:_Manifest#setShortComment|manifest.setShortComment()]]. As the meaning of this value will depend on the OXPs installed, it is unlikely to be useful to specify an initial value in the plist.
  
 
=== sort_order ===
 
=== sort_order ===
Line 123: Line 123:
 
== Secondary Market Definitions ==
 
== Secondary Market Definitions ==
  
Secondary market definitions are entered in the [[shipdata.plist#market_definition|market_definition]] key in shipdata.plist, which is an array of dictionaries, each defining rules which modify the prices and quantities from the main station market, and possibly adjust the market capacity and legality of the good. The following modifications are applied for each trade good:
+
Secondary market definitions are entered in the [[shipdata.plist#market_definition|market_definition]] key in shipdata.plist, which is an array of dictionaries, each defining rules which modify the prices and quantities from the main station market, and possibly adjust the market capacity and legality of the good. The following modifications are applied for each trade good, assuming no market_script is defined:
  
 
# The market definition array is searched from top to bottom for the first element matching the trade good
 
# The market definition array is searched from top to bottom for the first element matching the trade good
 
# The market quantity is scaled in proportion to the relative capacities for that good in the two markets.
 
# The market quantity is scaled in proportion to the relative capacities for that good in the two markets.
# If a market script is defined for the secondary market, it is then used to modify the prices and quantities further.
 
 
# Otherwise, the price and quantity modifications defined in the plist are used.
 
# Otherwise, the price and quantity modifications defined in the plist are used.
  
Line 135: Line 134:
  
 
* type = 'good': the good with the key in trade-goods.plist exactly matching the name property
 
* type = 'good': the good with the key in trade-goods.plist exactly matching the name property
* type = 'class': any good in trade-goods.plist with an entry in its [[#classes|classes] list exactly matching the name property
+
* type = 'class': any good in trade-goods.plist with an entry in its [[#classes|classes]] list exactly matching the name property
 
* type = 'default': any good. The name property is ignored if present.
 
* type = 'default': any good. The name property is ignored if present.
  
Line 150: Line 149:
 
If present, these keys override the keys of the same name in the basic trade good definition. If omitted, the station will use the same rules as the main station, provided that [[shipdata.plist#market_monitored|its market is monitored at all]]
 
If present, these keys override the keys of the same name in the basic trade good definition. If omitted, the station will use the same rules as the main station, provided that [[shipdata.plist#market_monitored|its market is monitored at all]]
  
==== market_script ====
+
==== price_adder, price_multiplier, price_randomiser ====
If this key is defined, the price_* and quantity_* keys are ignored and the named [[Oolite_Market_Scripts|market script]] is used instead to update the price and quantity values.
 
 
 
==== price_adder, price_multiplier, price_random ====
 
 
These keys modify the price in the secondary market. The formula is (in decicredits):
 
These keys modify the price in the secondary market. The formula is (in decicredits):
 
  rnd = -1..1 // random number in range -1 to +1, slightly biased towards zero
 
  rnd = -1..1 // random number in range -1 to +1, slightly biased towards zero
  secondary_price = MAX(1, (primary_price + price_adder) * (price_multiplier + (price_random * rnd)))
+
  secondary_price = MAX(1, (primary_price + price_adder) * (price_multiplier + (price_randomiser * rnd)))
 
As an exception, if price_adder and price_multiplier are both <= 0, then the result is always 0 regardless of price_random.
 
As an exception, if price_adder and price_multiplier are both <= 0, then the result is always 0 regardless of price_random.
  
==== quantity_adder, quantity_multiplier, quantity_random ====
+
==== quantity_adder, quantity_multiplier, quantity_randomiser ====
 
These keys together with the capacity modify the quantity in the secondary market. The formula is:
 
These keys together with the capacity modify the quantity in the secondary market. The formula is:
 
  rnd = -1..1 // random number in range -1 to +1, slightly biased towards zero
 
  rnd = -1..1 // random number in range -1 to +1, slightly biased towards zero
 
  adjusted_quantity = (local_capacity / primary_capacity) * primary_quantity;
 
  adjusted_quantity = (local_capacity / primary_capacity) * primary_quantity;
  secondary_quantity = (adjusted_quantity + quantity_adder) * (quantity_multiplier + (quantity_random * rnd)))
+
  secondary_quantity = (adjusted_quantity + quantity_adder) * (quantity_multiplier + (quantity_randomiser * rnd)))
 
As an exception, if quantity_adder and quantity_multiplier are both <= 0, then the result is always 0 regardless of quantity_random.
 
As an exception, if quantity_adder and quantity_multiplier are both <= 0, then the result is always 0 regardless of quantity_random.
 +
 +
== Examples ==
 +
Hardly anybody ended up using this! And cim never even put it into his [[New Cargoes]] oxp! Spara wrote a conversion script for the old commodities.plist, and everybody seems to have used that instead for determining markets for new stations.
 +
 +
=== From the core game: Rock Hermits ===
 +
From the shipdata.plist in the config folder
 +
 +
"oolite_template_rock-hermit" =
 +
{ ....
 +
is_carrier = 1;
 +
is_template = 1;
 +
"market_capacity" = 31; // maximum capacity for any good
 +
"market_definition" = (
 +
{
 +
// export cheap mining products
 +
"type" = "class";
 +
"name" = "oolite-mining";
 +
"price_multiplier" = 0.8;
 +
"price_randomiser" = 0.1;
 +
"quantity_multiplier" = 3.5;
 +
"quantity_randomiser" = 3.0;
 +
},
 +
{
 +
// import supplies a bit
 +
"type" = "class";
 +
"name" = "oolite-edible";
 +
"price_multiplier" = 1.05;
 +
"price_randomiser" = 0.05;
 +
"quantity_multiplier" = 0.0;
 +
"capacity" = 15;
 +
},
 +
{
 +
// sometimes need clothes, but usually have enough already
 +
"type" = "class";
 +
"name" = "oolite-wearable";
 +
"price_multiplier" = 0.8;
 +
"price_randomiser" = 0.4;
 +
"quantity_multiplier" = 0.0;
 +
"capacity" = 7;
 +
},
 +
{
 +
// sometimes need new mining equipment
 +
"type" = "class";
 +
"name" = "oolite-machinery";
 +
"price_multiplier" = 0.8;
 +
"price_randomiser" = 0.4;
 +
"quantity_multiplier" = 0.0;
 +
"capacity" = 7;
 +
},
 +
{
 +
// not really interested
 +
"type" = "default";
 +
"price_multiplier" = 0.55;
 +
"price_randomiser" = 0.25;
 +
"quantity_multiplier" = 0.0;
 +
"capacity" = 3;
 +
}
 +
);
 +
"market_monitored" = no;
 +
....
 +
};
 +
=== From Cim's oxp: Risk-based Economy (v.2.0, updated from commodities.plist) ===
 +
{
 +
"food" = {
 +
"price_random" = 0.15;
 +
};
 +
 +
"radioactives" = {
 +
"price_random" = 0.12;
 +
};
 +
 +
"liquor_wines" = {
 +
"price_random" = 0.15;
 +
};
 +
 +
"luxuries" = {
 +
"price_random" = 0.08;
 +
};
 +
 +
"luxuries" = {
 +
"price_random" = 0.08;
 +
};
 +
 +
"computers" = {
 +
"price_random" = 0.12;
 +
};
 +
 +
"machinery" = {
 +
"price_random" = 0.05;
 +
};
 +
 +
"alloys" = {
 +
"price_average" = 158;
 +
"price_economic" = 0.10;
 +
"price_random" = 0.06;
 +
};
 +
 +
"firearms" = {
 +
"price_random" = 0.20;
 +
};
 +
 +
"furs" = {
 +
"price_random" = 0.25;
 +
};
 +
 +
"alien_items" = {
 +
"price_economic" = 0.05;
 +
"price_random" = 0.45;
 +
};
 +
 +
}
 +
 +
=== Setting an orbital main station's slaves on sale to zero ===
 +
[[User:Phkb|Phkb]] provided this example which requires a ''planetinfo.plist'' & a new ''.js file'' - there are other ways to achieve the same result
 +
====  planetinfo.plist ====
 +
(in the "Config" folder)
 +
 +
{
 +
    "0 96" = {market_script = "digebiti_noslaves.js";};
 +
}
 +
 +
==== digebiti_noslaves.js ====
 +
(in the "Scripts" folder) - note the complex name, which prevents confusion with other snippets of script
 +
 +
"use strict";
 +
this.name          = "digebiti_noslaves";
 +
this.author        = "Phkb";
 +
this.copyright      = "(C) 2022 Phkb";
 +
this.licence        = "CC-NC-by-SA 4.0";
 +
this.description    = "Removes slaves for sale in Digebiti";
 +
this.version        = "1.0";
 +
 +
this.updateLocalCommodityDefinition = function(goodDefinition, station, systemID) {
 +
    if (goodDefinition.key == "slaves" && station && station.isMainStation) {
 +
        goodDefinition.quantity = 0;
 +
    }
 +
    return goodDefinition;
 +
}
 +
 +
This method will work, so long as no other OXP changes the market_script property of the Digebiti system in the same way.
 +
 +
== Using the new Trade-goods.plist ==
 +
The original commodities.plist was really not very good - it was based very strongly on the original Elite 84 pricing algorithm, which was written to use peculiarities of 8-bit Maths to its advantage. So one ended up with a series of fairly incomprehensible integers defining each market.
 +
 +
trade-goods.plist was designed to make that simpler, by:
 +
:1) Making it a dictionary rather than an unlabelled array so one could tell which parameter was which
 +
:2) Making the price range for a trade good dependent on more sensible variables, so rather than setting the price range with a bitmask, one set the price and quantity variations dependent on the economy and on random chance.
 +
 +
Compare the v.1.81 trade-goods.plist with a v.1.80 commodities.plist - they were designed to give near-identical market behaviour for the built-in trade goods.
 +
 +
But - that only made it more comprehensible. It didn't really let one do anything particularly different to Elite 84 markets - mostly including the "Rich Industrial -> Poor Agricultural" single spectrum. One can tweak that a bit with peak_export and peak_import to get slightly different shapes (so a good might peak at Poor Industrial rather than Rich Industrial) but it doesn't really give one the option of a third economy type still.
 +
 +
''So how does one introduce a new economic pole, such as a mining industry?''
 +
 +
For that, one needs the scripting support. [[Oolite JavaScript Reference: Market Scripts]]. <br>
 +
One can quite literally do whatever one likes with the prices that way. One could have every *system* have its own separate economy, which varied over time according to events, what the player had previously traded, ''etc. etc.'' to produce something comparable to Elite Dangerous' economic sim, if that's the route one wanted to go down. Obviously the more complexity one adds the harder it gets to script up, but it's now all possible.
 +
 +
[[File:SOTL Altmap F8.png|thumb|right|200px|SOTL Altmap F8 commodities market]]
 +
[[SOTL Altmap]] does have an example of it, which basically throws out the original Elite-like algorithm of trade-goods.plist and starts over. It goes *way* beyond just adding a third economy, too.
 +
 +
Basically in SOTL every trade good is tagged in the trade-goods.plist with a bunch of classes
 +
''e.g.''
 +
classes = ("sotl-ex-salvage","sotl-im-refining","sotl-quantity-high","sotl-priceband-3","sotl-demand-medium","sotl-supply-medium","sotl-volatility-low");
 +
So that means:
 +
- exported by salvage economies
 +
- imported by refining economies
 +
- high quantity, moderate price, low price volatility
 +
 +
Then the market script uses those classes, rather than the standard pricing variables, to define the economic behaviour.
 +
:So line 69-74ish: get the current economy description, convert that to the format used in the class name
 +
:...line 85-90: check for the import and export classes for that economy and see if it's imported or exported
 +
:...lines 100-120: apply various special conditions, so e.g. the sotl-ims-radiation class checks the planet surface radiation levels (custom property in sysinfo) and if the radiation level is high, imports these goods even if the basic economy isn't interested (e.g. "sotl-ceramics-ind" in the plist)
 +
:...and then it uses those calculations (and a bunch more of the classes) in the rest of the script to set the price and quantity depending on whether it's an import, an export, or neither
 +
 +
That's massive overkill for just adding a third mining economy, but one would need to work on a similar principle.
 +
 +
:1) Tag every commodity with classes for (import|export|none)-(agricultural|industrial|mining) as appropriate
 +
:2) Adjust the economy names (there are a few different ways one could do that) so that the 8 economy IDs were allocated to agricultural, industrial and mining economies
 +
:3) Have a price script which like the SOTL one gets the current economy, looks at the classes list to find out if the commodity is imported or exported or neither, and sets the price and quantity accordingly. One could use the standard price_economic, price_average, price_random properties in the plist to give baseline data to the script if one was not planning to do anything too weird.
 +
 +
{Technically one doesn't need the classes - just have the price script contain a list of goods internally for each economy - but this way if other people add extra trade goods in their own OXPs, they can tag them up for mining economy support too}
 +
 +
All fairly straightforward theoretically.
 +
 +
''The biggest impediment to doing a wholesale reworking of the economic system, is just how incompatible it makes lots and lots of OXP's. In order to override the vanilla commodities as in SOTL, requires setting a script for each in trade-goods.plist. Which is fine if it's the only OXP that wants to play around with commodity prices. As soon as two OXP's try to do the same thing, one gets a conflict and no longer knows which OXP pricing model is in play.''
 +
 +
''For SOTL it's fine, because it uses the scenario system to forcibly eject all other OXP's from use while it's running. But if one wants the OXP to go in the general playing pool set of OXP's, it becomes much more complicated. [[User:Phkb|Phkb]] spent a long time trying to work out why his market script wasn't running, only to find that [[UPS Courier]] was quietly adjusting the markets in certain systems via the planetinfo.plist file. And there are a lot of OXP's that want to play in this space, tweaking prices up or down.''
 +
 +
''Hence one usually abandons using any of the script options, and just tweaks the price after all the scripts have finished playing, ''eg'' in [[Smugglers]]. It was the only way to guarantee getting some sort of bump in a price when wanted.''
 +
 +
== Links ==
 +
*[[Oolite JavaScript Reference: Market Scripts]]
 +
*[[Commodities.plist]] - the precursor (which still mostly works!). If it doesn't see [http://www.aegidian.org/bb/viewtopic.php?p=246145#p246145 here] (2015)
 +
*[http://aegidian.org/bb/viewtopic.php?f=2&t=16828 Proposal for 1.82: support for economic changes] ([[User:Cim|Cim]], 2014)
 +
*[http://aegidian.org/bb/viewtopic.php?f=2&t=16735 Cim on introducing the Trade-goods.plist and deprecating the commodities.plist] (2014)
 +
{{QuoteText|Text=Trade goods work is coming along fairly well. As well as plist-coded prices for trade goods, which should handle the simple cases (including a few things not possible with commodities.plist), you can now specify a "market script" which allows you to set prices per trade-good, per station, or per system. If you try to do all three, the following happens: <br>
 +
1) The per-trade good script sets a standard price and quantity for the system's economy and any other data it considers <br>
 +
2) The per-system script can then modify this price and quantity <br>
 +
3) The station takes the price and quantity from the system, and scales the quantity to the capacity of the station market <br>
 +
4) The per-trade good script runs again in "secondary station" mode, to modify the price and quantity again <br>
 +
5) The per-station script runs to make final adjustments to price and quantity <br>
 +
Generally one OXP wouldn't do all three, though.|Source=([http://aegidian.org/bb/viewtopic.php?p=226722#p226722 Cim in Progress Thread (2014)])}}
 +
=== Using the Trade-goods.plist ===
 +
*[http://www.aegidian.org/bb/viewtopic.php?f=4&t=20576 How to get the displayName for a commodity in the Main Station Market?] (2020)
 +
*[http://www.aegidian.org/bb/viewtopic.php?f=6&t=17269 Thinking about Planet Income classification] (2015)
 +
 +
{{QuoteText|Text=
 +
In 1.81 this is a lot easier to OXP - you can introduce as many economies as you like, though you are limited to eight economy symbols (so if you wanted lots of economy types, you might end up with Poor Industrial, Avg Industrial and Rich Industrial sharing a symbol on the map). The trade good pricing scripts can then be used to set up different price categories in different economies (and introduce additional trade goods, or split an existing good into multiple categories, which may be necessary if you're adding more economies).
 +
 +
Resetting economies across the planets is a big change in terms of lines of code, but is also very easy to automate. If OXPers are looking for a scripting tool to use for this sort of automation, then NodeJS or io.js is free and also uses Javascript as its language, so you don't need to learn a new one - and may even be able to share code between your build scripts and your OXP, though I haven't tried that yet.|Source=([http://aegidian.org/bb/viewtopic.php?f=6&t=17269 Cim in Planet Income thread (2015)])}}
 +
 +
[[Category:Oolite]]
 +
[[Category:Oolite scripting]]

Revision as of 19:34, 10 November 2022

Note: this page describes functionality currently only in the 1.81 development code. It may change further before being part of a stable release.

The trade-goods.plist file replaces and extends the old commodities.plist and illegal_goods.plist files from Oolite 1.81 onwards to allow greater flexibility in trade good definitions, including defining entirely new goods.

The file is a dictionary, with keys as commodity keys, and values defining the quantities, prices and other properties of those commodities.

{
	"food" = {
		"name" = "[commodity-name food]";
		"classes" = ("oolite-consumer","oolite-edible","oolite-farming");
		"quantity_unit" = 0;  // 0=t
		"peak_export" = 7; // Poor Ag
		"peak_import" = 0; // Rich Ind
		"price_average" = 50; // decicredits
		// fraction of average ~= 2.75 credits
		"price_economic" = 0.55;
		// fraction of average ~= 0.2 credits
		"price_random" = 0.04;
		"quantity_average" = 13.5; // gets rounded
		"quantity_economic" = 0.52;
		"quantity_random" = 0.04;
		"legality_export" = 0;
		"legality_import" = 0;
		"trumble_opinion" = 1.0;
		"sort_order" = 100;
	};
}

The following keys are accepted within a commodity definition

Property keys

capacity

The maximum amount of this good which can be held at a main station market. The default is 127 units.

classes

An array of good class names. Good classes can be used by secondary stations to set pricing rules for groups of goods.

The following classes are defined by Oolite, with the core goods in those classes also listed. OXPs should usually add these classes as appropriate to custom trade goods they create, and may also define their own classes (which should be given an OXP-specific prefix). Some built-in classes only contain one built-in good, to allow for easier sub-typing by OXPs.

  • oolite-alien: goods which were not made by a Cooperative species (Alien Items)
  • oolite-animalproduct: goods which are obtained from animals but are not themselves animals (Furs)
  • oolite-business: goods which are usually obtained for industrial or other corporate use (Computers, Machinery, Alloys, Minerals, Gold, Platinum)
  • oolite-consumer: goods often obtained for personal use (Food, Textiles, Liquor/Wines, Luxuries, Narcotics, Furs, Gold, Platinum, Gem Stones)
  • oolite-dangerous: goods which may be hazardous (Radioactives, Narcotics, Firearms)
  • oolite-edible: goods which can be eaten reasonably safely (Food, Liquor/Wines)
  • oolite-farming: goods which are produced by farming or similar agricultural processes (Food, Textiles, Liquor/Wines, Furs)
  • oolite-living: goods which contain living creatures (Slaves)
  • oolite-luxury: goods which are luxuries, usually a subset of oolite-consumer (Luxuries)
  • oolite-machinery: goods which are industrial machinery (Machinery)
  • oolite-medical: goods which may have a medical purpose (Narcotics)
  • oolite-metals: goods which are largely or entirely made of metal (Alloys, Gold, Platinum)
  • oolite-military: goods which are of interest to the military (Firearms, Alien Items)
  • oolite-mining: goods which are produced by mining operations or similar extraction processes (Radioactives, Minerals, Gold, Platinum, Gem Stones)
  • oolite-rawmaterials: goods which are raw materials needing refining to be used further (Minerals)
  • oolite-restricted: goods which are subject to restrictions on trade (Slaves, Narcotics, Firearms)
  • oolite-salvage: goods which are often retrieved from space battles (Slaves, Alloys)
  • oolite-shipyard: goods used in the production of space ships (Computers, Alloys)
  • oolite-slaves: goods which are slaves (Slaves)
  • oolite-technological: goods requiring a high-tech process to produce (Computers, Machinery)
  • oolite-thargoid: goods of Thargoid origin (Alien Items)
  • oolite-weapons: goods which are weapons (Firearms)
  • oolite-wearable: goods which are or can be used to make clothes (Textiles, Furs)

comment

A string that will be displayed on the F8 F8 commodity detail screen to describe the commodity. This can be modified later by manifest.setComment()

legality_export

This number will be multiplied by the number of units carried when leaving a main station (or another station which enforces Cooperative market laws). This value will be ORed with the player's bounty.

legality_import

This number will be multiplied by the number of units carried when entering a main station (or another station which enforces Cooperative market laws). This value will be ORed with the player's bounty.

No core game good is illegal to import.

market_script

The name of a Javascript file to use as a market script for this trade good when calculating main station quantities and prices.

name

A string containing the name of the trade good. This may contain []s for descriptions.plist expansion.

peak_export

A number from 0 to 7 identifying the economy which will give the best price for buying this good. Economies closer to this economy than the peak_import will also have below-average prices.

peak_import

A number from 0 to 7 identifying the economy which will give the best price for selling this good. Economies closer to this economy than the peak_export will also have above-average prices.

price_average

The average price of this trade good at a main system station which neither imports nor exports, in decicredits.

price_economic

The proportion of the price affected by the system economy. A value of 0 means that this good does not change in price depending on system economy. A value of 0.3 means that the good would be 0.7 times price_average in an ideal exporting system, and 1.3 times price_average in an ideal importing system. While values greater than 1 may be used, in general much lower values are better.

price_random

The proportion of the price affected by random factors. A value of 0.6 means that the actual price of the good in a system which neither imports nor exports may be between 0.4 and 1.6 times the price_average.

If this value is larger than price_economic, this means that even an ideal trade run from the best exporter to the best importer is not guaranteed to make a profit.

quantity_average

The average quantity of this trade good at a main system station which neither imports nor exports, in decicredits. If the calculated quantity exceeds the capacity, it will be capped.

quantity_economic

The proportion of the quantity affected by the system economy. A value of 0 means that this good does not change in quantity depending on system economy. A value of 0.3 means that the good would be 0.7 times as frequent in an ideal importing system, and 1.3 times as frequent in an ideal exporting system. A value of 1 or greater will make the good never or rarely (depending on quantity_random) available in systems which import the good.

quantity_random

The proportion of the quantity affected by random factors. A value of 0.6 means that the actual quantity of the good in a system which neither imports nor exports may be between 0.4 and 1.6 times the quantity_average.

If this value is greater than 1, then systems may often entirely lack this good. If this value is greater than 1 + quantity_economic, then even an ideal exporter may not have any in stock.

quantity_unit

The size of container one unit of this good represents. 0 = tonne; 1 = kilogram; 2 = gram. The default is zero.

short_comment

A string that will be displayed on the F8 commodity list screen in the optional extra column. This can be modified later by manifest.setShortComment(). As the meaning of this value will depend on the OXPs installed, it is unlikely to be useful to specify an initial value in the plist.

sort_order

A number positioning the item on the F8 screen when goods are sorted in the default order. The core goods have values 100 to 1700 in steps of 100.

trumble_opinion

How likely is a hungry trumble to consider eating this good? Goods with a value of 0 for this will never be eaten by trumbles; other goods may be eaten depending on the value of this parameter and the values for other goods on board.


Secondary Market Definitions

Secondary market definitions are entered in the market_definition key in shipdata.plist, which is an array of dictionaries, each defining rules which modify the prices and quantities from the main station market, and possibly adjust the market capacity and legality of the good. The following modifications are applied for each trade good, assuming no market_script is defined:

  1. The market definition array is searched from top to bottom for the first element matching the trade good
  2. The market quantity is scaled in proportion to the relative capacities for that good in the two markets.
  3. Otherwise, the price and quantity modifications defined in the plist are used.

Secondary Market Matching

This uses the 'type' and 'name' properties. Type has three possible values

  • type = 'good': the good with the key in trade-goods.plist exactly matching the name property
  • type = 'class': any good in trade-goods.plist with an entry in its classes list exactly matching the name property
  • type = 'default': any good. The name property is ignored if present.

'default' should generally only be used for the last entry in the market definition.

Secondary Market Modifications

The following keys in the chosen market definition are then used to modify the good

capacity

The market capacity for this good or set of goods. If omitted, the default capacity for the station will be used.

legality_import, legality_export

If present, these keys override the keys of the same name in the basic trade good definition. If omitted, the station will use the same rules as the main station, provided that its market is monitored at all

price_adder, price_multiplier, price_randomiser

These keys modify the price in the secondary market. The formula is (in decicredits):

rnd = -1..1 // random number in range -1 to +1, slightly biased towards zero
secondary_price = MAX(1, (primary_price + price_adder) * (price_multiplier + (price_randomiser * rnd)))

As an exception, if price_adder and price_multiplier are both <= 0, then the result is always 0 regardless of price_random.

quantity_adder, quantity_multiplier, quantity_randomiser

These keys together with the capacity modify the quantity in the secondary market. The formula is:

rnd = -1..1 // random number in range -1 to +1, slightly biased towards zero
adjusted_quantity = (local_capacity / primary_capacity) * primary_quantity;
secondary_quantity = (adjusted_quantity + quantity_adder) * (quantity_multiplier + (quantity_randomiser * rnd)))

As an exception, if quantity_adder and quantity_multiplier are both <= 0, then the result is always 0 regardless of quantity_random.

Examples

Hardly anybody ended up using this! And cim never even put it into his New Cargoes oxp! Spara wrote a conversion script for the old commodities.plist, and everybody seems to have used that instead for determining markets for new stations.

From the core game: Rock Hermits

From the shipdata.plist in the config folder

	"oolite_template_rock-hermit" = 
	{	....
		is_carrier = 1;
		is_template = 1;
		"market_capacity" = 31; // maximum capacity for any good
		"market_definition" = (
			{
				// export cheap mining products
				"type" = "class";
				"name" = "oolite-mining";
				"price_multiplier" = 0.8;
				"price_randomiser" = 0.1;
				"quantity_multiplier" = 3.5;
				"quantity_randomiser" = 3.0;
			},
			{
				// import supplies a bit
				"type" = "class";
				"name" = "oolite-edible";
				"price_multiplier" = 1.05;
				"price_randomiser" = 0.05;
				"quantity_multiplier" = 0.0;
				"capacity" = 15;
			},
			{
				// sometimes need clothes, but usually have enough already
				"type" = "class";
				"name" = "oolite-wearable";
				"price_multiplier" = 0.8;
				"price_randomiser" = 0.4;
				"quantity_multiplier" = 0.0;
				"capacity" = 7;
			},
			{
				// sometimes need new mining equipment
				"type" = "class";
				"name" = "oolite-machinery";
				"price_multiplier" = 0.8;
				"price_randomiser" = 0.4;
				"quantity_multiplier" = 0.0;
				"capacity" = 7;
			},
			{
				// not really interested
				"type" = "default";
				"price_multiplier" = 0.55;
				"price_randomiser" = 0.25;
				"quantity_multiplier" = 0.0;
				"capacity" = 3;
			}
		);
		"market_monitored" = no;
		....
	};

From Cim's oxp: Risk-based Economy (v.2.0, updated from commodities.plist)

{
	"food" = {
		"price_random" = 0.15;
	};
	
	"radioactives" = {
		"price_random" = 0.12;
	};

	"liquor_wines" = {
		"price_random" = 0.15;
	};
	
	"luxuries" = {
		"price_random" = 0.08;
	};

	"luxuries" = {
		"price_random" = 0.08;
	};

	"computers" = {
		"price_random" = 0.12;
	};

	"machinery" = {
		"price_random" = 0.05;
	};

	"alloys" = {
		"price_average" = 158;
		"price_economic" = 0.10;
		"price_random" = 0.06;
	};

	"firearms" = {
		"price_random" = 0.20;
	};

	"furs" = {
		"price_random" = 0.25;
	};

	"alien_items" = {
		"price_economic" = 0.05;
		"price_random" = 0.45;
	};

}

Setting an orbital main station's slaves on sale to zero

Phkb provided this example which requires a planetinfo.plist & a new .js file - there are other ways to achieve the same result

planetinfo.plist

(in the "Config" folder)

{
    "0 96" = {market_script = "digebiti_noslaves.js";};
}

digebiti_noslaves.js

(in the "Scripts" folder) - note the complex name, which prevents confusion with other snippets of script

"use strict";
this.name           = "digebiti_noslaves";
this.author         = "Phkb";
this.copyright      = "(C) 2022 Phkb";
this.licence        = "CC-NC-by-SA 4.0";
this.description    = "Removes slaves for sale in Digebiti";
this.version        = "1.0";

this.updateLocalCommodityDefinition = function(goodDefinition, station, systemID) {
    if (goodDefinition.key == "slaves" && station && station.isMainStation) {
        goodDefinition.quantity = 0;
    }
    return goodDefinition;
}

This method will work, so long as no other OXP changes the market_script property of the Digebiti system in the same way.

Using the new Trade-goods.plist

The original commodities.plist was really not very good - it was based very strongly on the original Elite 84 pricing algorithm, which was written to use peculiarities of 8-bit Maths to its advantage. So one ended up with a series of fairly incomprehensible integers defining each market.

trade-goods.plist was designed to make that simpler, by:

1) Making it a dictionary rather than an unlabelled array so one could tell which parameter was which
2) Making the price range for a trade good dependent on more sensible variables, so rather than setting the price range with a bitmask, one set the price and quantity variations dependent on the economy and on random chance.

Compare the v.1.81 trade-goods.plist with a v.1.80 commodities.plist - they were designed to give near-identical market behaviour for the built-in trade goods.

But - that only made it more comprehensible. It didn't really let one do anything particularly different to Elite 84 markets - mostly including the "Rich Industrial -> Poor Agricultural" single spectrum. One can tweak that a bit with peak_export and peak_import to get slightly different shapes (so a good might peak at Poor Industrial rather than Rich Industrial) but it doesn't really give one the option of a third economy type still.

So how does one introduce a new economic pole, such as a mining industry?

For that, one needs the scripting support. Oolite JavaScript Reference: Market Scripts.
One can quite literally do whatever one likes with the prices that way. One could have every *system* have its own separate economy, which varied over time according to events, what the player had previously traded, etc. etc. to produce something comparable to Elite Dangerous' economic sim, if that's the route one wanted to go down. Obviously the more complexity one adds the harder it gets to script up, but it's now all possible.

SOTL Altmap F8 commodities market

SOTL Altmap does have an example of it, which basically throws out the original Elite-like algorithm of trade-goods.plist and starts over. It goes *way* beyond just adding a third economy, too.

Basically in SOTL every trade good is tagged in the trade-goods.plist with a bunch of classes e.g.

classes = ("sotl-ex-salvage","sotl-im-refining","sotl-quantity-high","sotl-priceband-3","sotl-demand-medium","sotl-supply-medium","sotl-volatility-low");

So that means:

- exported by salvage economies
- imported by refining economies
- high quantity, moderate price, low price volatility

Then the market script uses those classes, rather than the standard pricing variables, to define the economic behaviour.

So line 69-74ish: get the current economy description, convert that to the format used in the class name
...line 85-90: check for the import and export classes for that economy and see if it's imported or exported
...lines 100-120: apply various special conditions, so e.g. the sotl-ims-radiation class checks the planet surface radiation levels (custom property in sysinfo) and if the radiation level is high, imports these goods even if the basic economy isn't interested (e.g. "sotl-ceramics-ind" in the plist)
...and then it uses those calculations (and a bunch more of the classes) in the rest of the script to set the price and quantity depending on whether it's an import, an export, or neither

That's massive overkill for just adding a third mining economy, but one would need to work on a similar principle.

1) Tag every commodity with classes for (import|export|none)-(agricultural|industrial|mining) as appropriate
2) Adjust the economy names (there are a few different ways one could do that) so that the 8 economy IDs were allocated to agricultural, industrial and mining economies
3) Have a price script which like the SOTL one gets the current economy, looks at the classes list to find out if the commodity is imported or exported or neither, and sets the price and quantity accordingly. One could use the standard price_economic, price_average, price_random properties in the plist to give baseline data to the script if one was not planning to do anything too weird.

{Technically one doesn't need the classes - just have the price script contain a list of goods internally for each economy - but this way if other people add extra trade goods in their own OXPs, they can tag them up for mining economy support too}

All fairly straightforward theoretically.

The biggest impediment to doing a wholesale reworking of the economic system, is just how incompatible it makes lots and lots of OXP's. In order to override the vanilla commodities as in SOTL, requires setting a script for each in trade-goods.plist. Which is fine if it's the only OXP that wants to play around with commodity prices. As soon as two OXP's try to do the same thing, one gets a conflict and no longer knows which OXP pricing model is in play.

For SOTL it's fine, because it uses the scenario system to forcibly eject all other OXP's from use while it's running. But if one wants the OXP to go in the general playing pool set of OXP's, it becomes much more complicated. Phkb spent a long time trying to work out why his market script wasn't running, only to find that UPS Courier was quietly adjusting the markets in certain systems via the planetinfo.plist file. And there are a lot of OXP's that want to play in this space, tweaking prices up or down.

Hence one usually abandons using any of the script options, and just tweaks the price after all the scripts have finished playing, eg in Smugglers. It was the only way to guarantee getting some sort of bump in a price when wanted.

Links

Trade goods work is coming along fairly well. As well as plist-coded prices for trade goods, which should handle the simple cases (including a few things not possible with commodities.plist), you can now specify a "market script" which allows you to set prices per trade-good, per station, or per system. If you try to do all three, the following happens:

1) The per-trade good script sets a standard price and quantity for the system's economy and any other data it considers
2) The per-system script can then modify this price and quantity
3) The station takes the price and quantity from the system, and scales the quantity to the capacity of the station market
4) The per-trade good script runs again in "secondary station" mode, to modify the price and quantity again
5) The per-station script runs to make final adjustments to price and quantity
Generally one OXP wouldn't do all three, though.

(Cim in Progress Thread (2014))

Using the Trade-goods.plist

In 1.81 this is a lot easier to OXP - you can introduce as many economies as you like, though you are limited to eight economy symbols (so if you wanted lots of economy types, you might end up with Poor Industrial, Avg Industrial and Rich Industrial sharing a symbol on the map). The trade good pricing scripts can then be used to set up different price categories in different economies (and introduce additional trade goods, or split an existing good into multiple categories, which may be necessary if you're adding more economies).

Resetting economies across the planets is a big change in terms of lines of code, but is also very easy to automate. If OXPers are looking for a scripting tool to use for this sort of automation, then NodeJS or io.js is free and also uses Javascript as its language, so you don't need to learn a new one - and may even be able to share code between your build scripts and your OXP, though I haven't tried that yet.

(Cim in Planet Income thread (2015))