Table of Contents
Migration Notes
Foundation 1.9.6
Trading Village
In 1.9.6, we simplified TRADING_VILLAGE
's list of resources by simply requiring a list of assets.
-- OLD WAY -- Register an addition neighbour to trade with. saltTrader:registerAsset({ DataType = "TRADING_VILLAGE", Id = SaltPrefix .. "_VILLAGE_NANTES", Heraldry = "ICON_" .. CommonResourceSalt, Description = SaltPrefix .. "_VILLAGE_NANTES_DESC", VillageName = "Nantes", --RESOURCE QUANTITY PAIR UnlockCost = { { Resource = "GOLD", Quantity = 300 } }, ResourceTradingReplenishDelay = 300.0, --BUYING RESOURCE TRADING INFO BuyingResourceList = { { ResourceMaxAmount = { Resource = "HONEY", Quantity = 30 }, ReplenishingAmount = 10, }, { ResourceMaxAmount = { Resource = "HERBS", Quantity = 30 }, ReplenishingAmount = 10, } }, --SELLING RESOURCE TRADING INFO SellingResourceList = { { ResourceMaxAmount = { Resource = CommonResourceSalt, Quantity = 50 }, ReplenishingAmount = 20, } }, Allegeance = "CLERGY", ResourceNeededToUpgrade = "BERRIES", TradeRouteUpgradeNarrativePanelTemplate = "NARRATIVE_PANEL_TEMPLATE_TRADE_ROUTE_UPGRADE", WorldGuiIcon = "ICON_" .. CommonResourceSalt, UpgradeEventEnvoyProfile = "PROFILE_ENVOY_ROYAL", }) -- NEW WAY -- Register an addition neighbour to trade with. saltTrader:registerAsset({ DataType = "TRADING_VILLAGE", Id = SaltPrefix .. "_VILLAGE_NANTES", Heraldry = "ICON_" .. CommonResourceSalt, Description = SaltPrefix .. "_VILLAGE_NANTES_DESC", VillageName = "Nantes", --RESOURCE QUANTITY PAIR UnlockCost = { { Resource = "GOLD", Quantity = 300 } }, ResourceTradingReplenishDelay = 300.0, --BUYING RESOURCE TRADING INFO BuyingResourceList = { "HONEY", "HERBS" }, --SELLING RESOURCE TRADING INFO SellingResourceList = { CommonResourceSalt }, Allegeance = "CLERGY", ResourceNeededToUpgrade = "BERRIES", TradeRouteUpgradeNarrativePanelTemplate = "NARRATIVE_PANEL_TEMPLATE_TRADE_ROUTE_UPGRADE", WorldGuiIcon = "ICON_" .. CommonResourceSalt, UpgradeEventEnvoyProfile = "PROFILE_ENVOY_ROYAL", })
Foundation 1.9.3
Gendered fields
In 1.9.3, we merged the Nun and Monk profiles and statuses together to avoid duplication. This brought in the notion of “gendered fields”. Those fields are arrays based on GENDER_USAGE
. Fields that needed to be different depending on gender became “gendered fields”.
Note: AGENT_PROFILE
and VILLAGER_STATUS
linked to Nun have been removed. Nuns will have their profile and status converted to the Monk equivalent.
Text fields
- If empty in gender, will take
GENERIC
- If we want the
GENERIC
version:- If empty, will LOG and ERROR and take the
MASCULINE
version
- To convert:
-- OLD WAY AgentProfile = { DataType = "AGENT_PROFILE", ProfileName = "MONK" } -- NEW WAY AgentProfile = { DataType = "AGENT_PROFILE", ProfileNameGendered = { -- MASCULINE "MONK", -- FEMININE "NUN", -- GENERIC "MONASTIC" } }
Character Setup
CHARACTER_SETUP_DATA
per gender- Animations: If NONE in gender, will take the one from
ALL
- Left/Right Hand: If
nil
in gender, will take the one fromALL
- Clothing: If
nil
in gender, will take the one fromALL
- Lists:
- What is in
ALL
will be used by every gender - No more need to duplicate stuff in both genders
- To convert:
-- OLD WAY CharacterSetup = { DataType = "CHARACTER_SETUP", WalkAnimation = "WALKING", IdleAnimation = "GATHER", FemaleHatList = { "PREFAB_HAT_BERRYPICKER1", }, MaleHatList = { "PREFAB_HAT_BERRYPICKER2", } } -- NEW WAY CharacterSetup = { DataType = "CHARACTER_SETUP", CharacterSetupDataGendered = { -- MALE { DataType = "CHARACTER_SETUP_DATA", HatList = { "PREFAB_HAT_BERRYPICKER2" } }, -- FEMALE { DataType = "CHARACTER_SETUP_DATA", HatList = { "PREFAB_HAT_BERRYPICKER1" } }, -- ALL { DataType = "CHARACTER_SETUP_DATA", WalkAnimation = "WALKING", IdleAnimation = "GATHER" -- Things put in lists here will be used by all genders } } }
Assets
- Gendered Fields:
-
- Need to do conversion manually
-
- Will automatically bring the value in
ProfileFunction
to theALL
value - You can set it up manually if you want
-
- Will automatically bring the value in
ProfileName
to theALL
value - You can set it up manually if you want
-
- Will automatically bring the value in
ProfileNamePlural
to theALL
value - You can set it up manually if you want
-
- Gendered Fields:
-
- Need to do conversion manually
-
- Will automatically bring the value in
StatusName
to theALL
value - You can set it up manually if you want
-
- Will automatically bring the value in
StatusDescription
to theALL
value - You can set it up manually if you want
-
- Will automatically bring the value in
Title
to theALL
value - You can set it up manually if you want
-
Data
- Gendered Fields:
-
- If
nil
in gender, will use quota inALL
. - Can use same quota for each gender, it will only evaluate villagers of that gender for the quota.
- If quota from
ALL
, will evaluate all villagers
-
Foundation 1.9.0.7
Sub-buildings and building functions
Buildings can now have sub-buildings, each sub-building having one or multiple building parts. To achieve this, each building can have a list of sub-buildings (BUILDING.SubAssetBuildingList
). Each sub-building can also have a building function used for all its parts (BUILDING.AssetBuildingFunction
).
Sub-buildings are available as soon as their parent building is available by feeding the list above. But like building parts, they can be unlocked later on using the game action GAME_ACTION_UNLOCK_BUILDING_LIST
in unlockables, and feeding the field BUILDING_PROGRESS.AdditionalBuildingUnlockList
.
An example of sub-buildings is given in the Example 02
mod, in the scripts in the scripts/mithril_factory directory
.
Building part list
The building part set list (BUILDING.BuildingPartSetList
) has been replaced with a simpler part list, without splitting parts into sets (BUILDING.AssetBuildingPartList
). Building parts will now be sorted in the construction UI by their category, instead of the set they belong to.
Unlockables
With the whole rework of the progression system for 1.9, unlockables have been reworked to improve the way content is unlocked throughout the game.
Unlockable types (UNLOCKABLE
and its sub-types) can now have a list of prerequisite unlockables that must be unlocked before this one (UNLOCKABLE.PrerequisiteUnlockableList
). The way content is unlocked is now with specialized game actions (GAME_ACTION
), and a single unlockable can have multiple game actions (UNLOCKABLE.ActionList
).
Examples are given in the Example 02
mod, in the monument.lua
and wall.lua
scripts.
-- OLD UNLOCKABLE mod:registerAsset({ DataType = "UNLOCKABLE_BUILDING", Id = "UNLOCKABLE_MITHRIL_FACTORY", Name = "UNLOCKABLE_MITHRIL_FACTORY_NAME", Description = "UNLOCKABLE_MITHRIL_FACTORY_DESC", EstateInfluenceCostList = { { Estate = "LABOUR", Quantity = 5 } }, RelatedProp = "MITHRIL_FACTORY_MONUMENT" }) -- NEW UNLOCKABLE mod:registerAsset({ DataType = "UNLOCKABLE", Id = "UNLOCKABLE_MITHRIL_FACTORY", Name = "UNLOCKABLE_MITHRIL_FACTORY_NAME", Description = "UNLOCKABLE_MITHRIL_FACTORY_DESC", DataCost = { ResourceCollection = { { Resource = "GOLD_COINS", Quantity = 50 }, } }, ActionList = { { DataType = "GAME_ACTION_UNLOCK_BUILDING_LIST", BuildingProgressData = { AssetBuildingList = { "MITHRIL_FACTORY_MONUMENT", }, }, }, }, })
Estates and progression
Progression, both globally and within estates, having been greatly reworked with 1.9. With this, came changes to the structure of progress tiers. Progress tiers are now grouped together in a new assets of type PROGRESS_PATH
, each progress path containing a list of PROGRESS_TIER_DATA
. One progress path can be set for each estate (ESTATE.AssetProgressPath
), and one is also set for the common progress path (BALANCING.AssetCommonProgressPath
).
Unlockables are now added directly to progress tiers (PROGRESS_TIER_DATA.UnlockableList
), and the conditions to unlock this unlockable are determined by the the progress tier’s conditions (PROGRESS_TIER_DATA.ConditionList
).
-- OLD PROGRESSION mithrilFactoryMod:overrideAsset({ Id = "KINGDOM", EstateProgressList = { -- Add a new estate progress tier Action = "APPEND", { DataType = "ESTATE_PROGRESS_TIER", SplendorRequired = 5, VillagerStatusRequired = { Status = "COMMONER", Quantity = 10 }, UpgradeList = { "UNLOCKABLE_SCI_FI_BRIDGE", "UNLOCKABLE_SCI_FI_WALL" } } } }) -- NEW PROGRESS TIER mithrilFactoryMod:overrideAsset({ Id = "PROGRESS_TIER_COMMON_T2", UnlockableList = { Action = "APPEND", "UNLOCKABLE_SCI_FI_BRIDGE", "UNLOCKABLE_SCI_FI_WALL", }, })
Quests
Quests have been greatly reworked, where instead of having a single QUEST_FUNCTION
, they can have multiple objectives (QUEST.ObjectiveList
), failure conditions (QUEST.FailureConditionList
), success actions (QUEST.QuestSuccessActionList
), and fail actions (QUEST.QuestFailActionList
).
All of those are in the form of game conditions (GAME_CONDITION
and all its sub-types), which allows for a better granularity in the structure of quests.
An example of quest is given in the Example 01 mod
, in the events.lua
script.
-- OLD QUEST myMod:registerAsset({ DataType = "QUEST", Id = "SUPER_QUEST", Name = "THE_GREAT_QUEST", Description = "THE_GREAT_QUEST", QuestFunction = { DataType = "QUEST_FUNCTION_DELIVER_RESOURCES", Estate = "KINGDOM", ResourceListToDeliver = {{ Resource = "WOOD", Quantity = 10 }}, DaysCount = 60 }, QuestRewardList = { QuestRewardResources({{ Resource = "ASTEROID_ROCK", Quantity = 5 }}) } }) -- NEW QUEST myMod:registerAsset({ DataType = "QUEST", Id = "SUPER_QUEST", Name = "THE_GREAT_QUEST", Description = "THE_GREAT_QUEST_DESCRIPTION", ObjectiveList = { { DataType = "GAME_CONDITION_ACCUMULATE_IN_STORAGE", ResourceListToAccumulate = { { Resource = "WOOD", Quantity = 10 }, }, }, { DataType = "GAME_CONDITION_EXECUTE_ACTION_LIST", IsNeedExecution = true, ActionList = { { DataType = "GAME_ACTION_DELIVER_RESOURCE", AmountToPay = { { Resource = "WOOD", Quantity = 10 }, }, IsDeliveryAction = true, }, }, }, }, FailureConditionList = { { DataType = "GAME_CONDITION_DAY_COUNT", DaysToComplete = 30, }, }, QuestSuccessActionList = { { DataType = "GAME_ACTION_GIVE_RESOURCE_LIST", BudgetCategory = "QUESTS", ResourceCollection = { { Resource = "ASTEROID_ROCK", Quantity = 5 }, }, }, }, QuestFailActionList = {}, })
Events
Similarly to quests, events have been reworked to trigger a list of actions (EVENT.ActionList
) instead of having to select a single choice when the event triggers. This helps to decouple events from their potential UI, which can now be triggered by the action GAME_ACTION_SHOW_NARRATIVE_PANEL
and the new asset ASSET_NARRATIVE_PANEL
.
An example of event is given in the Example 01 mod
, in the events.lua
script.
Foundation 1.8.0.0
Resource bundles
Building part costs have been changed to allow for building in steps, with the builder bringing a bundle of resources for each step.
To allow this, BUILDING_PART_COST.RessourcesNeeded
has been replaced with BUILDING_PART_COST.ResourceNeededList
, a list of lists of RESOURCE_QUANTITY_PAIR
. Each entry in the outer list corresponds to a bundle.
The number of construction steps built for each bundle is proportional to a bundle's quantity of resources. For example, if a part needs two resource bundles, one with 30 planks and the other with 10 planks, 75% of the construction steps will be done with the first bundle, and the rest with the second bundle.
Foundation 1.7.1
Texture loading
Some TEXTURE
properties have been changed, depending on their use, to a new type: ATLAS_CELL
.
Therefore, we added a way to explicitly choose the type of an asset to load, when calling registerAssetId
. Specifying the type is optional, TEXTURE
will be inferred for texture assets if absent.
This third parameter is currently only used for image assets.
mod:registerAssetId("test_img1.png", "TEST_IMAGE_1") -- without type specified, loads as a TEXTURE asset mod:registerAssetId("test_img2.png", "TEST_IMAGE_2", "TEXTURE") -- loads as a TEXTURE asset mod:registerAssetId("test_img3.png", "TEST_IMAGE_3", "ATLAS_CELL") -- loads as an ATLAS_CELL asset
Technical Info: Atlas Cells are images packed in a large unique texture (commonly called atlas). This is especially useful for the GUI engine, allowing to draw most of the interface without having to switch the active binded texture.
Foundation 1.6.0.0522
BUILDING and MONUMENT asset types
Monuments have been reworked, and the MONUMENT
type has been merged into BUILDING
. This means there are a few changes to the way buildings and monuments are created.
Buildings
A building's only building part should be specified with the property AssetCoreBuildingPart
instead of BuildingPartSetList
. For the game to not mistake a building for a monument, the property BuildingPartSetList
shoud be left empty.
local myBuilding = { DataType = "BUILDING", ... } if (foundation.getGameVersion == nil or version.cmp(foundation.getGameVersion(), "1.6") < 0) then myBuilding.BuildingPartSetList = { { Name = "DEFAULT", BuildingPartList = { "CORE_BUILDING_PART" } } } else myBuilding.AssetCoreBuildingPart = "CORE_BUILDING_PART" end mod:register(myBuilding)
Monuments
Monuments are now created around a single building part, placed before all the others. This part is the one selected just after selecting the monument in the building selection menu. This part has to be specified in the property AssetCoreBuildingPart
. All other parts still are in BuildingPartSetList
.
The core building part, if removed, will remove the whole monument. Like for the market, the core building part can be used for preview only, and be invisible once built by setting BUILDING_PART.IsVisibleWhenBuilt
to false
.
local myMonument = { ... } if (foundation.getGameVersion == nil or version.cmp(foundation.getGameVersion(), "1.6") < 0) then myMonument.DataType = "BUILDING" myMonument.BuildingPartSetList = { { Name = "DEFAULT", BuildingPartList = { "CORE_BUILDING_PART", "ADDITIONAL_PART_1", "ADDITIONAL_PART_2", "ADDITIONAL_PART_3" } } else myMonument.DataType = "BUILDING" myMonument.AssetCoreBuildingPart = "CORE_BUILDING_PART" myMonument.BuildingPartSetList = { { Name = "DEFAULT", BuildingPartList = { "ADDITIONAL_PART_1", "ADDITIONAL_PART_2", "ADDITIONAL_PART_3" } } end mod:register(myMonument)
Bridge mover
The way bridges are placed has been modified, and requires a small mover change. A bridge mover (BUILDING_PART_MOVER_BRIDGE
) was previously needed on a bridge's root part, and on the end part.
Now, to have a functional bridge, you need instead to place a bridge mover on the start part and end part, and remove the mover of the core part.
Before:
- CorePart -
BUILDING_PART_MOVER_BRIDGE
- StartPart -
BUILDING_PART_MOVER
- EndPart -
BUILDING_PART_MOVER_BRIDGE
Now:
- CorePart -
BUILDING_PART_MOVER
- StartPart -
BUILDING_PART_MOVER_BRIDGE
- EndPart -
BUILDING_PART_MOVER_BRIDGE
Slope constructor
Improvements have been made to the bridge, and you now have to setup bridge end parts (StartPart
and EndPart
) with a BUILDING_CONSTRUCTOR_SLOPE
.
Building zone and basement
To improve the number of basement spawned, scalable building parts (with constructor BUILDING_CONSTRUCTOR_SCALER
or BUILDING_CONSTRUCTOR_BASEMENT
) now need a non-empty building zone that fits the size of your basement filler. If no building zone is set, basements will not be placed.
The BUILDING_ZONE type has also been reworked to allow zone polygons per building part.
Foundation 1.4.5.1009
Building Part Function
For assets from type BUILDING_PART
, the attribute BuildingFunction
is now replaced with AssetBuildingFunction
. Thus, the building function must now be declared as an asset of type BUILDING_FUNCTION
.
Migration example for the Example 02 mod:
- building_parts.lua
mod:register({ DataType = "BUILDING_PART", ... -- OLD -- BuildingFunction = { -- DataType = "BUILDING_FUNCTION_WORKPLACE", -- WorkerCapacity = 4, -- RelatedJob = { Job = "MITHRIL_MINER", Behavior = "WORK_BEHAVIOR" }, -- ResourceProduced = { -- { Resource = "MITHRIL_ORE", Quantity = 5 } -- } -- }, -- NEW BuildingFunction = "MITHRIL_MINE_FUNCTION", ... }) -- ALSO NEW mod:register({ DataType = "BUILDING_FUNCTION_WORKPLACE", Id = "MITHRIL_MINE_FUNCTION", WorkerCapacity = 4, RelatedJob = { Job = "MITHRIL_MINER", Behavior = "WORK_BEHAVIOR" }, ResourceProduced = { { Resource = "MITHRIL_ORE", Quantity = 5 } } })
Resource Type List
The enumeration RESOURCE_TYPE
has been removed, and has been replaced by plain strings. This change means that modders can now define their own new types to work with their custom systems. The list of resource types has been moved to another page.
Another change is that resource, markets, granaries and warehouses can now have multiple resource types. This, combined with new customizable resource types, will be useful to customize where resources can be stocked and sold. This means that the property ResourceType
from RESOURCE
has been renamed to ResourceTypeList
.
For example, before this change, the wine was a resource of type LUXURY_FOOD
. Now, it has the types GRANARY
and TAVERN
. The granary is configured to only store resources of type GRANARY
, and the tavern can only sell resources of type TAVERN
. Thus, wine will only be stored in granaries, and sold in taverns. The luxury stall, with its types LUXURY
and LUXURY_FOOD
, will not be able to sell wine anymore.
The complete list of core resource types can be found: https://www.polymorph.games/foundation/modding/api/resource_type
Migration example:
mod:register({ DataType = "RESOURCE", ... -- OLD -- ResourceType = "LUXURY_FOOD", -- NEW ResourceTypeList = { "GRANARY", "TAVERN" } ... })
Foundation 1.3.1.0802
Building Part Sets
In order to categorize the different parts of a building in the construction window, buildings and monuments now have building part sets.
For assets from type ASSET_BUILDING
, the attribute BuildingPartList
is now replaced with BuildingPartSetList
(a list of BUILDING_PART_SET
).
Migration example for the Example 02 mod:
- monument.lua
mod:register({ DataType = "MONUMENT", ... -- Old -- BuildingPartList = { -- "MITHRIL_FACTORY_CORE_ROOT_PART", -- "MITHRIL_FACTORY_EXTENSION_A_ROOT_PART", -- "MITHRIL_FACTORY_EXTENSION_B_ROOT_PART", -- "MITHRIL_FACTORY_DOOR_A_PART", -- "MITHRIL_FACTORY_DOOR_B_PART", -- "MITHRIL_FACTORY_DECORATION_A_PART", -- "MITHRIL_FACTORY_DECORATION_B_PART" -- }, -- New BuildingPartSetList = { { Name = "PRODUCTION_CORE", BuildingPartList = { "MITHRIL_FACTORY_CORE_ROOT_PART" } }, { Name = "PRODUCTION_EXTENSION", BuildingPartList = { "MITHRIL_FACTORY_EXTENSION_A_ROOT_PART", "MITHRIL_FACTORY_EXTENSION_B_ROOT_PART" } }, { Name = "ENTRANCE", BuildingPartList = { "MITHRIL_FACTORY_DOOR_A_PART", "MITHRIL_FACTORY_DOOR_B_PART" } }, { Name = "DECORATION", BuildingPartList = { "MITHRIL_FACTORY_DECORATION_A_PART", "MITHRIL_FACTORY_DECORATION_B_PART" } } }, ... })
Event Choices
The event assets have been reworked to give the possiblity to have multiple actions triggered when a choice is made. The class EVENT
now has a new attribute ChoiceList
(a list of EVENT_CHOICE
), replacing the attribute ActionList
.
Migration example for the Example 01 mod, where the action to give a quest has been replaced by a choice that gives two quests:
- events.lua
myMod:register({ DataType = "EVENT", ... -- Old -- ActionList = { -- { -- DataType = "EVENT_ACTION_GIVE_QUEST", -- ShortName = "SUPER_EVENT_ACCEPT_QUEST", -- Quest = "SUPER_QUEST" -- }, -- { -- DataType = "EVENT_ACTION_IGNORE", -- ShortName = "KINGDOM_DECLINE", -- } -- }, -- New ChoiceList = { { ShortName = "EVENT_CHOICE_GIVE_TWO_QUESTS", EventActionList = { { DataType = "EVENT_ACTION_GIVE_QUEST", Quest = "SUPER_QUEST" }, { DataType = "EVENT_ACTION_GIVE_QUEST", Quest = "GREAT_QUEST" } } }, { ShortName = "KINGDOM_DECLINE", EventActionList = { { DataType = "EVENT_ACTION_IGNORE" } } }, }, ... })
Foundation 1.2.2.3105
New mod.json
In order to only load mods when enabled, Foundation will now need a new description file for each mod. It will declare all the things Foundation would need to know about the mod without parsing the lua file.
- mod.json
{ "Name": "Simple Example Mod", "Author": "Leo", "Description": "A very simple mod example", "Version": "2.0.0", "AdditionalLanguageList": [ { "Name": "Klingon", "Code": "tlh" } ] }
createMod() function is simplified
Since the new mod.json
file already contains all basic information about the mod, the foundation.createMod()
function does not take any parameter anymore.
- mod.lua
local myMod = foundation.createMod()
GENERAL_DATA asset type has been removed
GENERAL_DATA
asset type has been removed and all its properties has been moved to BALANCING
.
However, modders can now partially override Foundation main balancing (DEFAULT_BALANCING
), instead of being forced to replace all balancing properties. see Asset override for more information.