Cyphose's Compendium of Questions

Cyphose

y tho
Joined
Feb 1, 2016
Messages
181
Hey, CivFanatics modders! I'm Cyphose, and I'm new here (well, newly registered anyway--I've been lurking for a few weeks now). Side note: If I dun goofed with some aspect of this topic, just let me know (or delete it or move it or whatever's necessary); genuine newbie here. I recently got into modding and this forum has been invaluable in setting me straight and helping me with a lot of issues. Combined with looking up other mods and reverse-engineering to solve my problems, I've gotten by some fairly complicated stuff (at least for a new modder like me) with relative ease.

I've got a pretty ambitious project to mess with all the existing Civs and change their Uniques around (or add some new ones) that further develop a theme and feel to each one, since I find that, generally speaking, base game Civs aren't that distinct from one another. Obviously that's a lot of work, so the mod is very much a WIP, but it's slowly and steadily being fleshed out.

With that, and my lack of expertise, in mind, no time was wasted in running into a few roadblocks. I try my best to solve them on my own, but sometimes nothing I find really answers my question(s). So instead of flooding the forum every time I'm drawing a blank, I figured I'd keep my questions compact into one topic.

Ok, cool. Enough with the introductions then, onto the questions that have been giving me grief for the last few days or so:

Let's say I want to change Arabia's UA to something like this:

House of Wisdom
Gain :c5science: Science upon adopting a :c5culture: Social Policy equal to 50% of its :c5culture: Culture cost. Enter a :c5goldenage: Golden Age each time an Era is advanced.

Numbers obviously malleable. The question I have here is twofold:
  1. Is there any way to grant a flat Science boost to a Civilization? Tracking the Policy cost was enough of an issue (there's really no single command to just give you Policy cost, so I had to settle on a turn-by-turn calculation), but the more I investigate the Science aspect, the less confident I am that it's feasible. After rifling through every corner of the internet, I've only come across varying degrees of "it's not possible without C++/DLL modding." Is this true, or does it require a fancy workaround through dummy buildings or something similar?
  2. How can you track when someone advances an Era? I know how to check what Era they're currently in, but not when it changes. I suppose it could be done by simply tracking the Techs someone owns, but that would require some incredibly comprehensive and inefficient checks.

And the other major problem I've been looking to solve; let's say I want to change Poland's UA to something a bit more tame, but with cooler potential gameplay impact:

Solidarity
Adopting a new :c5culture: Social Policy tree does not increase the :c5culture: Culture cost of the next Policy.

The question I have here is fairly straightforward: how do I prevent the adoption of a certain Policy (obviously all the opener types in the database) from increasing subsequent Culture costs (essentially making it a "free" Policy)?

Note that these UAs (or anything I end up putting here) are just for display; that might be how it ends up, it might not. I might add things (which subsequently raise questions that I post here), I might take them away. Balance is less of an issue at these early stages in the mod, because I'd rather figure out my limits first. As they say, "you were so busy trying to find out whether you could that you didn't stop to think whether you should" (or something to that effect).

Those are all the questions I have on me for the moment. Again, I try to investigate the answers to these sorts of things myself, but sometimes I reach the end of my rope. I'll try to use this as a last resort so as to not needlessly burden you guys with questions that may have simple answers I didn't take the time to figure out.

Thank you in advance to anyone who takes the time to answer my questions!
 
1) Yes - Via Lua. The thing to remember about Science is it's almost always referred to in the API as Research and that techs (and hence research) are owned by teams, not players.

Code:
function GiveScience(iPlayer, iAmount)
  local pPlayer = Players[iPlayer]
  local pTeam = Teams[pPlayer:GetTeam()]
  local pTeamTechs = pTeam:GetTeamTechs()
  local iCurrentTech = pPlayer:GetCurrentResearch()
  pTeamTechs:ChangeResearchProgress(iCurrentTech, iAmount)
end

2) With the 3.276 update, an event was added (from my DLL) to the core game that does this. See GameEvents.TeamSetEra(teamId, eEra); here

3) Specific policies (all openers say) probably can't be done without a DLL mod.
 
1) That was my mistake! I knew Science was equivalent to Research in the API (and that it's done through TeamTechs rather than Player), but I dismissed the "ChangeResearchProgress" Lua function because I figured "well it shouldn't really matter what tech they're researching, right?" Obviously, though, since adding Science is effectively changing your progress toward the tech you're currently researching, I was just overthinking it.

2) Another thing I overlooked in the API thinking it wasn't relevant; the "SetEra" part made me think it was to change the Era, rather than a hook to find out when the Era changes. Sometimes these names can be misleading.

3) I'd rather stay out of the DLL if at all possible, though perhaps it may become necessary depending on what changes I decide to make in the future. Shame that pretty much everything regarding Policy Culture cost is a bit of a mess to handle with Lua or XML.

Here I thought I had dug myself into a complex problem, but it seems I just have issues with overthinking and overlooking some things. I might (read: probably will) be back at some indeterminate time to ask questions if I find myself in another rut, but until then, there's little else to do but thank you very much for the quick and concise answers! Much appreciated.
 
Actually for 3) you should be able to pull the same trick for granting a free policy via lua after the opener has been granted.

So detect the opener being given (via the player adopted policy event) and then do

Code:
pPlayer:SetNumFreePolicies(1)
pPlayer:SetNumFreePolicies(0)

(which exploits a bug in the DLL to increment the number of free policies ever received)
 
there's really no single command to just give you Policy cost

pPlayer:GetNextPolicyCost() will tell you the cost of the next policy (this info is displayed as you mouseover the top panel culture icon), so all you have to do is track the cost of the previous policy
 
Actually for 3) you should be able to pull the same trick for granting a free policy via lua after the opener has been granted.

So detect the opener being given (via the player adopted policy event) and then do

Code:
pPlayer:SetNumFreePolicies(1)
pPlayer:SetNumFreePolicies(0)

(which exploits a bug in the DLL to increment the number of free policies ever received)
I'm pretty sure PlayerAdoptPolicy doesn't fire for the opener policies (nor the finishers). He's need to use PlayerAdoptPolicyBranch. (and as additional fun, PlayerAdoptPolicyBranch doesn't fire for Ideology-adoption in BNW). Also be aware that too many free policies given to the same player (who is also human*) causes the game to cease sending the "Adopt A Social Policy" Notification.

*doesn't affect the AI one way or the other because they don't get notifications anyway.
 
1) Yes - Via Lua. The thing to remember about Science is it's almost always referred to in the API as Research and that techs (and hence research) are owned by teams, not players.

Code:
function GiveScience(iPlayer, iAmount)
  local pPlayer = Players[iPlayer]
  local pTeam = Teams[pPlayer:GetTeam()]
  local pTeamTechs = pTeam:GetTeamTechs()
  local iCurrentTech = pPlayer:GetCurrentResearch()
  pTeamTechs:ChangeResearchProgress(iCurrentTech, iAmount)
end

2) With the 3.276 update, an event was added (from my DLL) to the core game that does this. See GameEvents.TeamSetEra(teamId, eEra); here

3) Specific policies (all openers say) probably can't be done without a DLL mod.
Won't he also need to check that iCurrentTech is not 'nil' (-1) before attemting to use it in the line
Code:
pTeamTechs:ChangeResearchProgress(iCurrentTech, iAmount)
?



oopsi, sorry for not combining these into one post.
 
Won't he also need to check that iCurrentTech is not 'nil' (-1) before attemting to use it in the line
Code:
pTeamTechs:ChangeResearchProgress(iCurrentTech, iAmount)
?

Yep. For some reason I though that a value of -1 just added it to the overflow bucket, but it doesn't. (Actually I know why I though that, it's because I once modded the code to do just that, but had to remove it for the overflow exploit fix.)

Which means that if iCurrentTech is -1, the code will have to store iAmount somewhere and hook up the PlayerDoTurn (or some such) event and add it back in when a tech is choosen
 
pPlayer:GetNextPolicyCost() will tell you the cost of the next policy (this info is displayed as you mouseover the top panel culture icon), so all you have to do is track the cost of the previous policy
I know--but the stipulation that it has to be the next Policy (rather than just, say, getting the cost when you can adopt one) means you need to constantly track it. I got it done properly, but when I said "there's no simple way," I just meant that it was a bit more complicated than "check the Policy cost when adopting" because you actually have to track Policy cost to do that properly.

I'm pretty sure PlayerAdoptPolicy doesn't fire for the opener policies (nor the finishers). He's need to use PlayerAdoptPolicyBranch...
From some basic testing, this seems to be the case. That would also explain why the Science bonus wasn't firing on my Arabian UA when I first adopted a tree. I had used IGE to do so, so I figured I'd adopt the next one naturally (which wasn't an opener) and that worked, so I figured it was just some weird IGE interaction. Evidently, it was not.

Quick question: where are you finding "PlayerAdoptPolicyBranch?" It's not listed in the modiki Lua API as far as I can tell, but I could just be blind. And assuming I am blind, is there a similar GameEvent for finishing a Policy Branch (or is it grouped into AdoptPolicyBranch for simplicity's sake)?

Which means that if iCurrentTech is -1, the code will have to store iAmount somewhere and hook up the PlayerDoTurn (or some such) event and add it back in when a tech is choosen
Sounds easy enough, though it does add some tedium to giving the Science. Oh well, such is the nature of modding (or, well, any time you're working within preset restrictions).

Also, a few new questions:

Let's say I want to make the Aztec UA something along these lines:
Mandate of the War God
Start with the Piety :c5culture: Social Policy tree unlocked. Melee units heal when nearby enemies are defeated. :c5razing: Razing Cities generates :c5faith: Faith for every :c5citizen: Population razed.
I already know how to give them Piety for free (thanks to you guys). Again with a double question otherwise, though:

  1. Is there any easy way to heal melee units when a nearby enemy dies? I thought of a convoluted way of doing it (through a combination of promotions and tracking nearby units and their owners through SerialEventUnitDestroyed), though I'm not sure if I'm overcomplicating it (or if it'd even work).

  2. Pretty much the entirety of the Razing part eludes me. Is there any way to track if a City is in the process of being Razed? Theoretically, if I could do that, I could add a hook to CityCaptureComplete that gets the city's population and then, each, turn, check for remaining population and assign Faith based on that. But I can't find anything in the API that would track if a City is being Razed; just whether a player can Raze and a command to start Razing.
Once again, thank you for all your help!
 
where are you finding "PlayerAdoptPolicyBranch?" It's not listed in the modiki Lua API as far as I can tell, but I could just be blind. And assuming I am blind, is there a similar GameEvent for finishing a Policy Branch (or is it grouped into AdoptPolicyBranch for simplicity's sake)?
The modiki is very out of date. For BNW, see post #1 (Lua API), #5 (Game Events) and #6 (Lua Events) here


... does add some tedium ...
Welcome to the wonderful world of Civ 5 modding!

Is there any easy way to heal melee units when a nearby enemy dies?
Easy ... no. Detecting unit death is one of the most complex tasks in modding (at least without a modded DLL). Note that most of the SerialEvents only trigger for human players and then only if the map is revealed, as they are typically used by the game core to update the UI.

Is there any way to track if a City is in the process of being Razed?
GameEvents.SetPopulation() tracks population change in cities, the change will be negative on razing - see "Units - Population" for an example

pCity:IsRazing() can be used to differentiate between razing and starving
 
[*] Is there any easy way to heal melee units when a nearby enemy dies? I thought of a convoluted way of doing it (through a combination of promotions and tracking nearby units and their owners through SerialEventUnitDestroyed), though I'm not sure if I'm overcomplicating it (or if it'd even work).
Depends which melee units you want to be healed. Nearby units? And if so, what is "nearby"? All melee tiles with a radius of 1 tile? Maybe 2 tiles? Within workable tile range of the same city? Or maybe you want to heal all of your melee units?

Specificity is key.


Welcome to the wonderful world of Civ 5 modding!
^^ This

Easy ... no. Detecting unit death is one of the most complex tasks in modding (at least without a modded DLL). Note that most of the SerialEvents only trigger for human players and then only if the map is revealed, as they are typically used by the game core to update the UI.
I routinely use Events.EndCombatSim to check for killing an enemy unit, which apparently works even unseen, because... Firaxian consistency. :p If you want to check for if the attacker died, just use "if (iAttackingUnitFinalDamage >= iAttackingUnitMaxHitPoints) then", and if the defender died, "if (iDefendingUnitFinalDamage >= iDefendingUnitMaxHitPoints) then". Since it also passes iAttackingPlayer, iAttackingUnit, iDefendingPlayer and iDefendingUnit parameters, it's easy to tell exactly who died.

That said... It still raises the issue of checking whether or not the unit that died was a melee unit, since, um, trying to find properties of things that no longer exist is a little problematic. Which I guess leaves GameEvents.UnitPrekill.
 
I routinely use Events.EndCombatSim to check for killing an enemy unit, which apparently works even unseen,

does that work in SV mode? And with Quick Combat enabled? I know one of them doesn't
 
I don't play with either, so I wouldn't know...
...that may be worth fixing for, um, a number of my mods... :shifty:
 
As I thought, it doesn't fire in either of those cases (SV Map, or 3D Map with Quick Combat)
 
The modiki is very out of date. For BNW, see post #1 (Lua API), #5 (Game Events) and #6 (Lua Events) here
This is something I'd been afraid of for a fair amount of time (that the reference I was using was out of date). Having an up-to-date version of the API will do wonders in putting me on the right track for a lot of these things, since many of the questions I ask are related to not being able to find a proper hook or function relating to what I want to do. Thank you!

Easy ... no. Detecting unit death is one of the most complex tasks in modding (at least without a modded DLL). Note that most of the SerialEvents only trigger for human players and then only if the map is revealed, as they are typically used by the game core to update the UI.
Another thing I was afraid of. I prefer to code these things all at once (since getting them to work once means I can move on to use their mechanics elsewhere if need be), but I might have to hold off on the melee unit heal (or change it) until I'm a bit more experienced (or if I muster the courage to mod the DLL).

GameEvents.SetPopulation() tracks population change in cities, the change will be negative on razing - see "Units - Population" for an example
pCity:IsRazing() can be used to differentiate between razing and starving
Well, that nicely solves that problem with a few added checks to see who owns the City (and therefore who's Razing it).

Depends which melee units you want to be healed. Nearby units? And if so, what is "nearby"? All melee tiles with a radius of 1 tile? Maybe 2 tiles? Within workable tile range of the same city? Or maybe you want to heal all of your melee units?
In the context of the UA that it comes from, "nearby" means "adjacent" (i.e. within one tile). I would've been more specific in the UA itself, but the in-game wording was running out of room which caused it to be cramped on the quick select options menu.

No new questions for right now, just thought I'd acknowledge that I read these responses and that my questions are, for the most part, answered. Thanks everyone (especially for the up-to-date modding Lua API)!
 
I would've been more specific in the UA itself, but the in-game wording was running out of room which caused it to be cramped on the quick select options menu.
Ooh, I feel your pain there. :)

In that case, no matter event you use to detect unit death, assuming you can somehow figure out what unit came out victorious (we'll call it pUnit) and that it's yours, you could add this code segment:
Code:
local pPlot = pUnit:GetPlot()
for i = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
	local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY, i)
	local pAdjacentUnit = pAdjacentPlot:GetUnit()
	if (not (pAdjacentUnit == nil)) and (pAdjacentUnit:GetOwner() == pUnit:GetOwner()) then
		pAdjacentUnit:SetDamage(-34567893358687346675) 
		-- I suggest you change this number, but whatever you change it to, make it negative, since negative damage == healing
	end
end
 
It's been a while since I've posted here, but that's mostly because I haven't had any lingering questions. I've been confronted with some difficult issues, but for the most part I've been able to solve them on my own or reverse-engineer a similar mod to get what needs to be done. As such, the mod I'm working on is coming along slowly... but surely.

However, I'm obviously not reviving this topic for no reason, so here's the question currently bugging me (note that I make no claims of it being complex, only that I have failed to find a solution--whether that's due to its complexity or my blindness, I guess I'll find out :p):

Say I wanted to make a new Improvement; easy enough on its own (art assets notwithstanding), but it has the stipulation that it can only be built next to a Pasture.. How would I do this?

I've tried looking at the CL's Malaysia mod (which has a UI that can only be built next to Sea Resources) but I can't, for the life of me, find anything within the UI's definitions that would restrict it in such a way. That could just be me not being used to reading SQL (I use XML, but the majority of Malaysia's assets are done through SQL), or it could just be me looking in the wrong place (is it defined in builds? Lua?)... point being: reverse engineering is usually my go-to method to learn something new, but it's not helping me here.

So how does allowing an Improvement to only be constructed next to an existing Improvement work?

That's all for this time; no other pressing concerns as of right now. Thanks in advance!
 
EDIT: Ignore for now while I investigate

EDIT2: Well, I had an explanation prepared, but the code doesn't seem to support it, so... yeah, now I'm confused too. :p
In case you were wondering what it was going to be:
Spoiler :
CL's Malaysia does it with a bit* of sleight-of-hand, undercover code trickery which basically involves making an extra unit which is exactly the same as the normal worker, except with the extra ability to construct Malaysia's UI added. (They purposely didn't add that ability to the normal worker) Then, using a Lua script, whenever a Malaysian worker walks onto a plot adjacent to one with a sea resource, the worker is killed and replaced by the doppelganger worker, and vice versa: when the doppleganger walks off a valid plot, it gets killed and replaced with a normal worker.

*"A bit". That makes it sound like it's an easy thing to make functional. :p
 
You are chasing your tail. The Kampung can be built on any water tile.
  • The adjacency requirement for being next to a sea resource or an Atoll is not implimented anywhere. It is probably text oops-copied from the descriptions for the extra naval production and etc.
  • The lua code for CL Malaysia does in fact consider all the correct stuff for the food->culture and naval bonus stuff but there's nothing anywhere that actually impliments the requirement that a Kampung be next to a sea resource or an atoll.
  • I tested and found that the Kampung can be built anywhere on a coastal or ocean tile that is not adjacent to an existing Kampung.
 
This is what I get for reading the description of something and not actually testing it (that'll teach me to trust tooltips). But that would also very much explain why there's no existing implementation for it!

With this and AW's proposed method in mind, I'm beginning to consider one of two things:
  • AW's solution, while complex, should work in theory and is something within reason that can be coded. Shame that Civ doesn't have a better system for determining something like this, but such is the joy of Civ5 modding.
  • Dropping the adjacency requirement entirely and instead giving it additional bonuses while adjacent to a Pasture. This has a similar effect through making it only worth building next to Pastures, but bypasses the convoluted issues of custom workers and spawning/despawning where necessary. Mind you, getting bonuses while adjacent to Pastures is still a chore to do with Lua and the usage of dummy features, improvements, buildings, etc., but it's also much more straightforward.
I asked my question with a solution somewhat similar to AW's in mind, but in pursuit of finding a simpler solution, I suspect I merely confirmed my fear: that there is no simpler solution. Damn you, Civ5 modding!

Regardless, thanks for all the help! I'm needing to ask these sorts of questions less and less as I continue to learn more, luckily, but as always, I'll be back if something else is being a pain.
 
Top Bottom