Pazyryk
Deity
- Joined
- Jun 13, 2008
- Messages
- 3,584
It's easy to forget that Events can be invoked as well as subscribed to. When you subscribe to them, you are hooking to a call to the graphics engine or some other engine used specifically for the "active player". For example, Events.SerialEventCityCreated doesn't correspond to city founding exactly; its real meaning is "create a city graphic object" for the active player (the AI players aren't interested in graphics). We all do it, but you should think twice (or thrice) before using these as hooks for game rule logic. Even if you think you understand its firing logic, it is as likely as not to change with some future update to the graphics engine.
So what happens if you invoke Events.SerialEventCityCreated instead of subscribing to it? Well ... basically what you are doing is sending instructions to the graphics engine. You can set a city's art style, era and population (apparent, not real) to anything you want. Try it. It works! Do you want your size 1 ancient cities to appear as giant modern metropolises? You can do it. Do you want city art styles to reflect the original owner rather than the current owner (so that a civ has mixed art styles)? You can do it.
Coding this properly into a mod is tricky. You can change a city graphic, but then the event comes along and fires for some reason (city conquest, game load) and undoes your change. You want to link your graphic change so it follows from the event itself. However, invoking an event from a listener for the same event seems like asking for trouble (perhaps its OK depending on the sequence of things; I haven't tried it). Also, many of these events fire many times sequentially (for example, on game load). The code below accounts for all of this. It caches info from a SerialEventCityCreated listener and then (a bit later) invokes SerialEventCityCreated with an altered value. Some care is needed to avoid an infinite loop since your invoking will trigger your listener. The example below forces all cities to have ArtStyleTypes.ARTSTYLE_ASIAN regardless of what is defined in Civilizations. But you can alter it to have full control over city appearance using whatever logic you like.
I suspect there is a whole treasure chest graphic of possibilities by invoking Events. As I said above, you are sending instructions to the graphics engine. I'm pretty sure that Events.SerialEventImprovementCreated will work analogously to the above, allowing control of improvement art style, era, state (e.g., pillaged), and even the art asset used. But I have no idea what is possible. Can you run a combat sim (an illusory combat that isn't "real") with Events.RunCombatSim? Can you make a unit member run an animation with Events.UnitMemberCombatStateChanged? Only experimentation will tell.
So what happens if you invoke Events.SerialEventCityCreated instead of subscribing to it? Well ... basically what you are doing is sending instructions to the graphics engine. You can set a city's art style, era and population (apparent, not real) to anything you want. Try it. It works! Do you want your size 1 ancient cities to appear as giant modern metropolises? You can do it. Do you want city art styles to reflect the original owner rather than the current owner (so that a civ has mixed art styles)? You can do it.
Coding this properly into a mod is tricky. You can change a city graphic, but then the event comes along and fires for some reason (city conquest, game load) and undoes your change. You want to link your graphic change so it follows from the event itself. However, invoking an event from a listener for the same event seems like asking for trouble (perhaps its OK depending on the sequence of things; I haven't tried it). Also, many of these events fire many times sequentially (for example, on game load). The code below accounts for all of this. It caches info from a SerialEventCityCreated listener and then (a bit later) invokes SerialEventCityCreated with an altered value. Some care is needed to avoid an infinite loop since your invoking will trigger your listener. The example below forces all cities to have ArtStyleTypes.ARTSTYLE_ASIAN regardless of what is defined in Civilizations. But you can alter it to have full control over city appearance using whatever logic you like.
Code:
local g_cityUpdateInfo = {}
local g_cityUpdateNum = 0
local function ListenerSerialEventCityCreated(vHexPos, iPlayer, iCity, artStyleType, eraType, continent, populationSize, size, fogState)
print("ListenerSerialEventCityCreated: ", vHexPos, iPlayer, iCity, artStyleType, eraType, continent, populationSize, size, fogState)
if artStyleType ~= ArtStyleTypes.ARTSTYLE_ASIAN then
g_cityUpdateNum = g_cityUpdateNum + 1
g_cityUpdateInfo[g_cityUpdateNum] = g_cityUpdateInfo[g_cityUpdateNum] or {}
local updateInfo = g_cityUpdateInfo[g_cityUpdateNum]
updateInfo[1] = {x = vHexPos.x, y = vHexPos.y, z = vHexPos.z}
updateInfo[2] = iPlayer
updateInfo[3] = iCity
updateInfo[4] = ArtStyleTypes.ARTSTYLE_ASIAN
updateInfo[5] = eraType
updateInfo[6] = continent
updateInfo[7] = populationSize
updateInfo[8] = size
updateInfo[9] = fogState
--Warning! Infinite loop if new updateInfo causes an update!
end
end
Events.SerialEventCityCreated.Add(ListenerSerialEventCityCreated)
local function UpdateCityGraphics()
if g_cityUpdateNum == 0 then return end
print("Running UpdateCityGraphics; number cached = ", g_cityUpdateNum)
while 0 < g_cityUpdateNum do
local updateInfo = g_cityUpdateInfo[g_cityUpdateNum]
g_cityUpdateNum = g_cityUpdateNum - 1
Events.SerialEventCityCreated(updateInfo[1], updateInfo[2], updateInfo[3], updateInfo[4], updateInfo[5], updateInfo[6], updateInfo[7], updateInfo[8], updateInfo[9])
end
end
Events.SerialEventGameDataDirty.Add(UpdateCityGraphics)
Events.SequenceGameInitComplete.Add(UpdateCityGraphics)
Events.SerialEventCityCaptured.Add(UpdateCityGraphics) --not sure if this happens before or after city art change