BUGFIX_DUMMY_POLICIES fixes various issues with using orphaned policies. Thanks to LeeS for assistance with this.
Firaxis assume that EVERY policy is attached to a branch, either directly (Policies.PolicyBranchType is not NULL),
or indirectly (by reference from PolicyBranchTypes.FreePolicy or PolicyBranchTypes.FreeFinishingPolicy)
However, modders (being sneaky little buggers) have discovered that policies don't need to be attached to branches
(that is they are orphans) and can use them as dummy policies as a way of getting access to the bonuses in the
Policies table for civilization unique attributes (UAs) etc. They (mainly me!) have also discovered an exploit
to grant policies without affecting the cost of the next policy (the "pPlayer:SetNumFreePolicies(1); pPlayer:SetNumFreePolicies(0)" magic).
This however leads to a number of issues
- buildings (eg Prora) that give "happiness per X policies" include the orphaned policies in the count X
- the player's overall score includes a (small) amount per policy adopted, this also includes orphaned policies
- orphaned policies are included in the replay data, which can lead to weird roller-coaster looking graphs
- the player's count of remaining polices that can be adopted gets skewed, resulting in the loss of the Adopt Policy notification
We could write a method that calculates if a policy is an orphan (doesn't reference the PolicyBranchType
table either directly or indirectly) but that would then break every existing mod that uses dummy policies and
the free policy exploit.
So the adopted solution is to add a "Dummy" column to the Policies table to explicitly flag a policy as a dummy.
This way, existing mods are unaffected and new mods can use the new database column (along with the new Grant/Revoke/Swap API)
and not have to bother with the free policy exploit. Happiness all round
Standard Lua API method changes
- pPlayer:GetNumPolicies() takes an optional parameter bExcludeOrphans (defaults to the BUGFIX_DUMMY_POLICIES configuration value)
In addition, three new pPlayer Lua API methods are provided - GrantPolicy, RevokePolicy and SwapPolicy
local bGranted = pPlayer:GrantPolicy(iPolicy, bFree)
If pPlayer has NOT already adopted the specified policy, they will be granted it. Use bFree to indicate if this is a free policy (ie should NOT increase the culture cost of the next policy).
Returns true if the policy was granted as a result of this call (ie returns false if pPlayer already has the policy).
local bRevoked = pPlayer:RevokePolicy(iPolicy)
If pPlayer HAS already adopted the specified policy, it will be revoked (ie they will no longer have it). This method preserves the culture cost of the next policy.
Returns true if the policy was revoked as a result of this call (ie returns false if pPlayer does not already have the policy).
local bSwapped = pPlayer:SwapPolicy(iPolicyNew, iPolicyOld)
If pPlayer has NOT already adopted the specified NEW policy AND HAS already adopted the OLD policy, then the old policy is revoked and the new policy granted.
If the old policy was free, the new policy will also be free; consequently this method preserves the culture cost of the next policy.
Returns true if the policies were swapped as a result of this call.
A mod should not mix Grant/Revoke/SwapPolicy methods with SetNumFreePolicies/SetHasPolicy methods for the same (group of) policies.