[Lua] Doing things on project creation

Androrc the Orc

Emperor
Joined
Apr 19, 2004
Messages
1,621
Location
Vienna, Austria
Is there a game event or serial event for when a project (Manhattan Program, etc.) is created?

The idea is to make a project increase the happiness of the player(s) of the team that created it, by using the player:ChangeExtraHappinessPerCity() function on project creation.
 
you can handle SpecificCityInfoDirty and see if it has a project.

I know the code to check a player to see if his team has a project. But, I would need to save a variable onto the player saying whether they have already received the bonus or not, and then attach code for applying the bonus if the player hasn't received it yet on PlayerDoTurn.

But, if there is a game event (or serial event) for when a project is completed, that would greatly simplify things, and make me not need to save the extra variable onto the player.
 
Hadn't tryed it yet, but I may require something like what you want...

maybe using Events.NotificationAdded.Add( OnNotificationAdded ) ?

with function OnNotificationAdded( Id, type, toolTip, strSummary, iGameValue, iExtraGameData )

check for type == NotificationTypes.NOTIFICATION_PROJECT_COMPLETED

iGameValue should be the project ID

and player ID is maybe iExtraGameData


else I'll go for a save game data...
 
Hadn't tryed it yet, but I may require something like what you want...

maybe using Events.NotificationAdded.Add( OnNotificationAdded ) ?

with function OnNotificationAdded( Id, type, toolTip, strSummary, iGameValue, iExtraGameData )

check for type == NotificationTypes.NOTIFICATION_PROJECT_COMPLETED

iGameValue should be the project ID

and player ID is maybe iExtraGameData


else I'll go for a save game data...

Thanks, that sounds like a good idea, although perhaps it would be more practical to just use save game data:

Code:
function TheHumanGenomeProjectEffects( iPlayer )
	local player = Players[iPlayer]
	local team = Teams[player:GetTeam()]
	local applied = load( player, TheHumanGenomeProjectApplied ) or false

	if (team:GetProjectCount(GameInfo.Projects["PROJECT_THE_HUMAN_GENOME_PROJECT"].ID) > 0 and applied == false) then
		player:ChangeExtraHappinessPerCity( 1 )
		save( player, TheHumanGenomeProjectApplied, true )
	end
end

GameEvents.PlayerDoTurn.Add( TheHumanGenomeProjectEffects )

I tried this out, but it is not working, unfortunately.
 
what exactly does not work?
GetProjectCount or ChangeExtraHappinessPerCity?

GetProjectCount works because I've used it for another thing and it worked perfectly.

I meant, "applied == false" isn't working as it should, because even when I replace ChangeExtraHappinessPerCity with a function I know works (ChangeGold), nothing is done, so the conditional isn't being fulfilled even when the project is built.
 
more probably something wrong with this:
GameInfo.Projects["PROJECT_THE_HUMAN_GENOME_PROJECT"].ID) > 0
you'd debug this in the firetuner

I don't think that is the problem... I have checked and the project's tag is written the same as in the xml, and this code for another project works:

Code:
---------------------------------------------------------

function DomesdayBookEffects( iPlayer )
	local player = Players[iPlayer]
	local team = Teams[player:GetTeam()]

	if (team:GetProjectCount(GameInfo.Projects["PROJECT_DOMESDAY_BOOK"].ID) > 0) then
		local GoldBonus = player:GetGold() * 5 / 100
		player:ChangeGold( GoldBonus )
		player:ChangeJONSCulture( 6 )
	end
end

GameEvents.PlayerDoTurn.Add( DomesdayBookEffects )
 
GetProjectCount works just fine; I use it HEAVILY in my own mod, in quite a few different ways.

The Lua functions that change Happiness, however, do NOT work. At all. It'll appear to work at first, but at the end of the turn the game re-calculates all happiness and will basically undo whatever temporary changes to Happiness you made.

If you want to make an increase to Happiness that actually persists, you need to do it through a Building. Create a new building class (say, BUILDINGCLASS_HAPPY_PLUS_1), and make sure to set its <NoLimit> flag to true. Then create a building in that class with a negative Cost that adds 1 Happiness, and use the Lua SetNumRealBuildings function to give as many instances of it as you need to your capital, or in the local city in question. I use this mechanism in my own mods for Priest and Empath specialists (who add Happiness), an assortment of unusual Wonder effects, and so on.

Also, doing an "if X == false" check is problematic. Just write it as "if not X" instead; using an == on a boolean is not always going to work, because of how languages cast "true" or "false" into their internal mechanisms. (Some languages treat any negative number as "false", for instance, so it's possible for two values to both mean False without them being equal.)
 
Thanks very much Spatzimaus! I can confirm that this code works:

Code:
include( "SaveUtils" ); MY_MOD_NAME = "Antarctic Lands Mod"; WARN_NOT_SHARED = false

---------------------------------------------------------

function TheHumanGenomeProjectEffects( iPlayer )
	local player = Players[iPlayer]
	local team = Teams[player:GetTeam()]
	local data = load( player, "TheHumanGenomeProjectApplied" ) or false

	if (team:GetProjectCount(GameInfo.Projects["PROJECT_THE_HUMAN_GENOME_PROJECT"].ID) > 0 and not data) then
		player:ChangeGold( 10000 )
		save( player, "TheHumanGenomeProjectApplied", true )
	end
end

GameEvents.PlayerDoTurn.Add( TheHumanGenomeProjectEffects )

So the conditional now works perfectly. There is still the happiness effect left. I think having a building for that wouldn't look very good, since the building would still show up at the city interface, so what I did is make an "invisible" policy (doesn't appear on the policy interface or civilopedia, pretty much like the finishers), and then tried to use:

Code:
player:DoAdoptPolicy(GameInfo.Policies["POLICY_PROJECT_THE_HUMAN_GENOME_PROJECT"].ID)

...to give it to the player, but that didn't work (the player didn't get the policy). So I did a text search in the mods I have installed for policy-related commands, and found "player:SetHasPolicy" in the Ages of Man Base Mod, and then used it to give the policy to the player that created the project:

Code:
include( "SaveUtils" ); MY_MOD_NAME = "Antarctic Lands Mod"; WARN_NOT_SHARED = false

---------------------------------------------------------

function TheHumanGenomeProjectEffects( iPlayer )
	local player = Players[iPlayer]
	local team = Teams[player:GetTeam()]
	local data = load( player, "TheHumanGenomeProjectApplied" ) or false

	if (team:GetProjectCount(GameInfo.Projects["PROJECT_THE_HUMAN_GENOME_PROJECT"].ID) > 0 and not data) then
		player:SetHasPolicy(GameInfo.Policies["POLICY_PROJECT_THE_HUMAN_GENOME_PROJECT"].ID, true)
		save( player, "TheHumanGenomeProjectApplied", true )
	end
end

GameEvents.PlayerDoTurn.Add( TheHumanGenomeProjectEffects )

This worked very well, but there was still the issue that the interface would show the happiness bonus as being "from Social Policies", so I edited the text:

Code:
	<Language_en_US>
		<Update>
			<Where Tag="TXT_KEY_TP_HAPPINESS_POLICIES"/>
			<Set Text="{1_Num} from Social Policies and Projects."/>
		</Update>
	</Language_en_US>

And now it shows that these happiness bonuses are coming from both social policies and from projects.
 
I think having a building for that wouldn't look very good, since the building would still show up at the city interface,

That's why my mod alters CityView.lua to not display any buildings whose class has the <NoLimit> flag turned on AND whose cost is negative. That way, all of my hidden buildings are taken care of without messing up the UI for anything else. (For reference, I also have a +1 Food building, a +1 Production building, a +1 Research building, a +1 Gold building, a +1 Culture building, a -1 Happiness building, a -10 Happiness building, and a few others not worth getting into here. So I depend heavily on hidden bonuses.)

so what I did is make an "invisible" policy (doesn't appear on the policy interface or civilopedia, pretty much like the finishers), and then tried to use:

BAD IDEA. Bad, bad, bad, bad, BAD idea.

Look, the only reason my mod uses a Policy to handle this is that it's designed to do that. Adding a hidden policy causes a TON of problems:

1> The cost equation for adding new policies cares about the number of policies you have. It's currently impossible to make a no-cost policy. So if you give the player a custom policy behind the scenes, then it'll increase the cost of all of his future Policy choices.
In the vanilla game, the equation is something like 25 + (5*N)^1.80. To account for one hidden "extra" policy, I changed it in my mod to something like 16 + (4*N)^2.01, giving approximately the same general progression when N effectively becomes (N+1). (Seriously, try it in a spreadsheet. It's remarkably close.) But this means that in my mods, every player always has one and ONLY one hidden policy; while I have six hidden policies (well, seven, but one can never be used), they trade off with only one ever being active at any one time, because allowing a player to have two of them would break the cost equation again.

2> Each policy needs to be inside a branch. If you add your custom policy to, say, the Tradition branch, then two things happen:
2a> A player can no longer complete that branch (to get the Finisher, or to count it towards a cultural win) unless he has that policy. That means that it's not something you can turn off at a later point.
2b> The AI will see that branch as already "open", and will prefer to gain its next Policy from that branch regardless of Flavor ratings. (The AI is limited to only having two open branches at once, so this'd count as one of the two.)

3> ...but if you try to put it in a NEW branch, like I did, then you run into an entirely different set of issues:
3a> You need to create a policy in that branch so that it can never be considered completed (which'd count towards a Culture win). The AI will attempt to take any extra policy (since it's in an "open" branch), which means you need to add an override to force the AI to NOT take that policy. GameEvents don't work completely for this, so in my mod I've created an override to where if an AI is clearly stuck (not selecting any valid policies even though it's got more than enough Culture), I force them to pick a new one.
Alternatively, you can choose to NOT add anything beyond the one policy, in which case everyone starts with one completed branch. You'd have to then increase the number of branches needed for a culture win from 5 to 6, AND modify the SocialPolicyPopup and VictoryProgress UIs to reflect this.
3b> The UI is hard-coded to use 10 policy boxes of specific names, which means you'd need to alter SocialPolicyPopup.lua in four different places to get it to not show your custom branch. Or, modify both that lua and its xml companion to add an eleventh box, which adds a whole slew of new problems.
3c> And again, the AI will count this new branch as one of its two "open" ones, which is why I've tried to use certain overrides to force the AI to accept having three open branches.

Bottom line: it's fine to have a "carrier" mechanism. Every player gets a certain Policy, every unit gets a certain Promotion, and so on. It actually makes things pretty handy for modders who want to make certain changes to the base ruleset. But you can't just do it blindly; these things always need a significant amount of work to keep from breaking the existing game. The only kind of thing that doesn't add those issues is a Project. But as you may have noticed, Projects have very few actual effects, so you're very limited in what you can do with them.

It's great that you used something from my mod to try and fix your problem, but there's a reason my mod isn't compatible with most others: to make certain things work, I had to modify a whole lot of other things, enough that I pretty much overlap with everyone else's work.

And now it shows that these happiness bonuses are coming from both social policies and from projects.

Correct. The game's UI double-dips on that, as of the last patch, adding happiness bonuses in two different places, and they still haven't fixed it in vanilla. Yet another UI bug I've fixed in my own mod.
 
Policies don't need to be in branches, from what I've seen. The finishers aren't in any branch and they work fine, and the same with the policy I added.

As for the exponential policy cost, that is indeed a bummer... the strange thing though is that the cost increase happens not when the policy is given (even though the policy's effects are applied), but in the next turn. I imagine the game recalculates it each turn. But, if finisher policies don't count for the increase, perhaps creating a "phantom" branch that has the project policy as finisher could do the trick to avoid it being counted for policy cost.

Alternatively, it could be that the policy changes the cost right away, but the UI only updates on the next turn. In that case what causes finisher policies to not count towards the cost (if they don't) could be a function like "SetHasFreePolicy(ePolicy)", which we probably don't have access to through Lua.
 
Policies don't need to be in branches, from what I've seen. The finishers aren't in any branch and they work fine, and the same with the policy I added.

They ARE tied to a branch, through an explicit Finisher entry, and I think the game internally uses that linkage as a substitute for the direct declaration other policies would get. Regardless, you can have some very strange reactions involving the number of "open" branches for the AI if you try bypassing the branch system. I'd tried this at one point, and it just didn't turn out well, although I suppose the devs might have tried fixing it in the meantime.

As for the exponential policy cost, that is indeed a bummer... the strange thing though is that the cost increase happens not when the policy is given (even though the policy's effects are applied), but in the next turn.

Correct. From what I've seen, the recalculation happens either once per turn, or whenever you add a new policy. For the normal gameplay, this is obviously sufficient, since there's nothing in the core game that adds or subtracts policies as you go other than the standard policy selection system. For a mod, it's a problem; in my own mod, I'd originally used an equation like 0 + (5*N)^1.9, and I had the problem that the player would buy a "free" policy on his first turn.

(Side note: the game is also hard-coded to where an AI player can only select one policy per turn, even if he has a ton of culture. This happens often in late-era starts, where you start with enough culture to gain six or seven policies. The AIs will just pick one new policy each turn until they run out of culture. This has also caused me headaches in the past.)

I imagine the game recalculates it each turn. But, if finisher policies don't count for the increase, perhaps creating a "phantom" branch that has the project policy as finisher could do the trick to avoid it being counted for policy cost.

... and now you'd have it seeing a branch as completed, towards a Cultural victory. Which, again, would require updating the policy UI AND the victory progress UI AND the XML for the victory condition. And again, you'd need to update the policy UI to hide your new branch, because otherwise it'd break.

Yes, I've had to think far too much about this. It'd all just be so much easier if the CultureCost entry in the policies table actually did what it was supposed to, to where we really COULD give free hidden policies. Ideally, we'd not only have a working Cost, but also a <CannotBeChosen> flag (like promotions have) and a <Hidden> flag to prevent the UI from showing it.

Alternatively, it could be that the policy changes the cost right away, but the UI only updates on the next turn.

Not from what I've seen. All UI elements, especially the yield numbers in TopPanel, are set to auto-update in a variety of situations, such as moving your mouse over the number at the top of the screen. (This has obviously mattered to me in my own mod, where I add a new yield to be tracked.)

-------------------------
Anyway, feel free to experiment with it. It's just that, based on my past experience, using hidden Policies to add global effects only works if you've already structured your mod around giving this extra policy, and that takes quite a bit of work.
 
Interesting... Anyhow, I haven't noticed any strangeness in AI choice of policies with my modification. In my test (in a duel map), the AI took Tradition and Patronage.

From what I understood of what you said the DoAdoptPolicy function only works if the player actually could adopt a policy normally. I wonder, then, if first using ChangeNumFreePolicies, and then using DoAdoptPolicy would do the trick to avoid the extra cost.

EDIT: Tried that out, it resulted in getting a free policy and the project policy not being adopted.
 
EDIT: Tried that out, it resulted in getting a free policy and the project policy not being adopted.

i see SetHasPolicy having a 2nd argument and that is bool. I wonder if it will decrease next policy cost if applied to one of policies that player has.

My idea is:
1. add a "POLICY_FAKE_POLICIES_PREREQ", being a prerequisite for POLICY_PROJECT_THE_HUMAN_GENOME_PROJECT.
2. SetHasPolicy(POLICY_FAKE_POLICIES_PREREQ, true).
3. ChangeNumFreePolicies(1)
4. DoAdoptPolicy(POLICY_PROJECT_THE_HUMAN_GENOME_PROJECT)
5. SetHasPolicy(POLICY_FAKE_POLICIES_PREREQ, false).

i wonder if it will do the trick :D
 
Spatzimaus, couldnt you share the citiview.lua hiding fake buildings, please?

It's not very complicated. Inside CityView.lua, there's a function "AddBuildingButton" that determines whether it draws a button for any given building. Just put pretty much everything in that function inside something like:
Code:
local bLimit = GameInfo.BuildingClasses[building.BuildingClass].NoLimit;
if (pCity:IsHasBuilding(buildingID) and not bLimit ) then

and if you want to allow NoLimit constructable buildings, throw on a (building.Cost > 0) check as well.
 
It's not very complicated. Inside CityView.lua, there's a function "AddBuildingButton" that determines whether it draws a button for any given building. Just put pretty much everything in that function inside something like:
Code:
local bLimit = GameInfo.BuildingClasses[building.BuildingClass].NoLimit;
if (pCity:IsHasBuilding(buildingID) and not bLimit ) then

and if you want to allow NoLimit constructable buildings, throw on a (building.Cost > 0) check as well.

thanks!
 
Back
Top Bottom