A couple more "quick" inquiries

AW Arcaeca

Deus Vult
Joined
Mar 10, 2013
Messages
2,984
Location
Operation Padlock ground zero
1. Is it possible to add a new civilopedia section, preferably without having to edit the civiliopedia.lua file?

2. Is there an event-hook - seeing as how I haven't been able to find one - which fires when a city is captured? I'm aware of GameEvents.CityCaptureComplete, but I need it fire the same turn the city is captured, not when resistance ends, which, IINM is what that event hook listens for.

3. Is it possible, without DLL, to make a city literally impossible for an enemy to take over? There's always simply giving the city a dummy building with an extraordinarily high <Defense> and/or <CityHitPoints> (if that's what it's called, I don't exactly remember) value, but even that will eventually give out to superior military might. I suppose, if a game event such as described in #2 can be found, one could make some lua that would instantly return the city to its original owner...?

4. Possible to detect if a city is next to a lake with lua? I can imagine using the <Traits> FreeBuilding column to give a dummy to all cities conditional that they meet the geographic constraint of being by a lake (which would theoretically work since FreeBuilding at least checks for geographic constraints - it doesn't give Carthage harbors in landlocked cities - and I could use the column from the floating gardens), but frankly I'm sure there's a less involved way with lua, probably involving PlotIterators to sweep through all tiles adjacent to a city and seeing if at least one is water.

5. Does City.GetGarrisonedUnit work with a civilian unit? I've got a civ in progress with a civilian UU which will have a unique effect on cities as long as it's stationed in one, but I can't figure out whether to use this method or a workaround. Perhaps something like "if (pUnit:GetPlot() == pCity:GetPlot()) then"?

6. A game event, or really any method, that fires when a civilian unit is captured: Does such a think exist? I'm really in the dark for this one; I can't find or even imagine any method of this ilk existing and I can't think of any workaround.

If I can think of any more questions in the near future, hopefully I'll remember to just add them to this thread.

TIA,
AW
 
#2 is incorrect to the best of my knowledge. I use CityCaptureComplete in my Civ to handle resource yield reset functions when a City is taken. This function is run as soon as the City has been taken. It even fires when a City is traded to another player -- the bConquest boolean would return false in this case, I believe.

#3 I wonder if you might be able to just use Lua to reset the City's hitpoints to maximum after every attack, or something.

#4 you can use PlotIterators to iterate the tiles immediately around a City and test for plot:IsLake() I think.

No idea on 1, 5 and 6 off the top of my head. Mainly wanted to respond to #2.
 
Ah, well then, that simplifies things. :)
EDIT: Oh, and related - Basically, the idea in mind is that when Civ X's cities are captured, they're obliterated rather than fall to the enemy. But I'm trying to find a function for obliterating a city, like InitCity but the opposite. There's Player.Raze() but would that start the razing process or, as I would prefer, immediately eliminate the city?

But as for #3, resetting hitpoints after every attack would probably work, but the only combat-related game events I'm aware of that fire on/after an attack and don't deal with the graphics engine (e.g. GameEvents.CombatSimEnded) are RED events. :/

The PlotIterator method makes me wonder how much of this code from JFD's Carthage can be recycled to suit my purposes...
Spoiler :
Code:
iResource = GameInfoTypes.RESOURCE_JFD_SHELLFISH
iRadius = 3

function Sukritact_PlaceResource(pCity)
    local pPlot = pCity:Plot()
    local tPlots = {}
    local iNumtoPlace = 1
    for pLoopPlot in PlotAreaSweepIterator(pPlot, iRadius, SECTOR_NORTH, DIRECTION_CLOCKWISE, DIRECTION_OUTWARDS, CENTRE_EXCLUDE) do
        table.insert(tPlots, pLoopPlot)
    end
	
    for iVal = 1, iNumtoPlace do
		local bPlaced = false
		while (not(bPlaced)) and #tPlots > 0 do
			local iRandom = GetRandom(1, #tPlots)
			local pPlot = tPlots[iRandom]
			if pPlot:GetTerrainType() == GameInfoTypes["TERRAIN_COAST"] and pPlot:GetFeatureType() ~= GameInfoTypes["FEATURE_ATOLL"] and not pPlot:IsLake() and pPlot:GetResourceType() == -1 then
				pPlot:SetResourceType(iResource, 1)
				bPlaced = true
			end
			
			table.remove(tPlots, iRandom)
		end
	end
end
Obviously not exactly the same, but what if we edited it so...
Spoiler :
Code:
iRadius = 1

function CheckForLake(pCity)
    local pPlot = pCity:Plot()
    local tPlots = {}
    local iNumtoPlace = 1
    for pLoopPlot in PlotAreaSweepIterator(pPlot, iRadius, SECTOR_NORTH, DIRECTION_CLOCKWISE, DIRECTION_OUTWARDS, CENTRE_EXCLUDE) do
        table.insert(tPlots, pLoopPlot)
    end
	
    for iVal = 1, iNumtoPlace do
		while #tPlots > 0 do
			local iRandom = math.rand(1, #tPlots)
			local pPlot = tPlots[iRandom]
			if pPlot:IsLake() then
				return true
			end
			
			table.remove(tPlots, iRandom)
		end
	end
end
I'm not exactly the most well-versed with PlotIterators. :lol: Seeing as how I've never needed to use it before. But the idea here ^ is that it the function will now iterate around all plots 1 tile from a city and stop and return true once it finds one that is a lake.
 
The main thing is that it seems like we all use whoward's PlotIterators file, hence the call to "PlotAreaSweepIterator()", though I use "PlotAreaSpiralIterator()" myself.

The problem I have with the above code is that it's needlessly complicated if you're only doing the one check. Why bother storing the plots in a table when you're only going to then check it once and remove it if it doesn't have what you want?

My Civ's NewCityResources() function scans all plots 1-tile around the founding of a new city looking for specific resources using whoward's PlotAreaSpiralIterator() and does the check in real-time. The gist of it is that you just feed it the pPlot of whatever plot your City is on, then tell it to scan a 1-plot radius, and for each plot (pLoopPlot in above code) to check for a lake (pLoopPlot:IsLake()) and if it is, then you can either store that plot or do what you want, and move on.

Additionally, after those plots are stored in the table, he goes testing entries in the table randomly? Perhaps he has a reason for doing so, but your situation certainly does not call for that.

Edit:
I only just noticed and read your edit.
According to what whoward told me last timed I asked about it, Player.Raze() simply razes the City. I don't remember if it initiated a normal raze, or an immediate raze. I suppose it would be trivial to test with Firetuner, so if you want, I can test it in about half an hour and report back with the results.
 
Is the NewCityResources function particularly long? If not, may I politely request you to post it? I avoid Steam Workshop like the plague and as for the alternate download links, I seem to have trouble downloading a file from a website apparently from Uganda.
 
LOL Uganda.
I guess that's what the *.ug domain stands for. They probably picked it up for their URL shortener thing, but my alternate downloads are hosted on Pogoplug.com.

In any case, that function is actually quite long because of all the checks I need to do (and it also refers to some sub-functions I abstracted out) so I wouldn't suggest you try to copy/paste it either; it's more for reference as to how I am using the Plot Iterator.

I'll hop in the game now to check on the Raze() method for you, and I suppose I can write up a quick condensed version of my function for your needs, although I'm not entirely sure what exactly you're aiming to do with it.
 
Okay, just tested the method.
It appears to not be what you are looking for. All it seems to do is initiate the razing of a City, and furthermore, is limited to only Cities which you could normally raze anyway. In effect, the Lua method is a way to "Press the Raze button" in Lua. Or I guess more accurately, the button calls this Lua method.

It won't work on your Capital, any Cities you found, enemy Capitals and City-States.

As far as the Plot Iterator stuff, I don't know what information you have to work with, or what your starting point is, so I will make 2 assumptions:
1) You've got whoward's PlotIterators.lua installed and properly include()'d into your main Lua file.
2) You're starting the check on the founding of a new City using GameEvents.PlayerCityFounded().

Code:
local function IsNewCityAdjacentToLake(iPlayer, iX, iY)
	--Check for player civ and stuff if you want, that's what you can use iPlayer for.
	--Skipping that for this example.
	local pPlot = Map.GetPlot(iX, iY)
	local iRadius = 1
	local sStartSector = SECTOR_NORTH
	local sDirection = DIRECTION_ANTICLOCKWISE
	local sFlow = DIRECTION_OUTWARDS
	local bCenter = CENTRE_EXCLUDE
	for pAreaPlot in PlotAreaSpiralIterator(pPlot, iRadius, sStartSector, sDirection, sFlow, bCenter) do
		--Checking to make sure the plot exists first.
		--Could also insert a check for plot owner, but skipped for this example.
		if pAreaPlot and pAreaPlot:IsLake() do
			--It's a lake! DO STUFF! CELEBRATE!
			--We're done.
			break
		end
	end
end
GameEvents.PlayerCityFounded.Add(IsNewCityAdjacentToLake)

It's essentially exactly the same as above, except I'm not doing what I feel is unnecessary for the task: Adding the plots to a table temporarily only to remove them immediately afterward.
I also included a 'break' in the loop since it sounds like you only wanted to check for the presence of a Lake at all. If you instead have effects you want to do for every Lake plot adjacent to the City, you'll need to remove the 'break' so that it will continue running the loop until it finishes.

Edit:
Reading that code more carefully, I think I understand why JFD used a random element in that scenario. He's trying to place a new resource on a random plot, thus he needs to scan all available plots first, and then choose one randomly.

Edit2:
In light of your reported inability to download from Uganda, I've gone ahead and replaced the download links in my thread with their un-shortened versions pointing to pogoplug.com -- can you please verify that they work for you now? I'd imagine if you have problems, you probably won't be the only one.
 
For adjacent-plot iteration you can simply do this (assuming you already got pPlot defined somewhere):

Code:
for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
	local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), direction)
	if pAdjacentPlot and pAdjacentPlot:IsLake() then 
		--Stuff
	end
end
 
That's probably a more direct way, without having to rely on whoward's functions, but his does have the option of being easily adjusted to scan out to a range of 2 or 3 if desired. Both should work.

I should also mention that there does exist the City:Kill() method that does appear to instantly destroy/raze a City, but it seems to leave behind some graphical bugs.
 
That's probably a more direct way, without having to rely on whoward's functions, but his does have the option of being easily adjusted to scan out to a range of 2 or 3 if desired. Both should work.

Yes yes, it only works for adjacent-plot iteration, it is fairly simple and clean.
If you want anything else, look elsewhere. hahaha :lol:

City:Kill() [...] seems to leave behind some graphical bugs.
Care to expand? I'm curious. :crazyeye:
 
I tend to use whoward's function simply because it lets me be lazier and just define a radius for each type of plot-scan I need done (and my Civ has a lot of plot scanning to do each turn, and even mid-turn.)

The downside is that, well, you do have to rely on someone else's functions (even if they are whoward,) and as I found out the hard way, a certain Really Advanced Setup mod tends to completely screw it up because of the way the guy named his files.

Care to expand? I'm curious. :crazyeye:

The City will instantly appear to become the "city ruins" graphic, and disappear from all available interaction, but the City's banner will remain for whatever reason, obstructing view of the map and doing nothing but generate Lua errors (because you're trying to access nil) whenever you click on it.
 
The problem most likely comes due to the game not properly updating the screen when City:Kill() is used on its own. I've replicated many times, which is why I can say so with confidence; I generally try to test everything I suggest, and will explicitly say so (or along the lines of 'I think...') when I am unsure.

As to why it works for you when you kill them on turn 0, I suspect this is because on turn 0, no one has founded their Cities yet. Everyone is still a settler on turn 0 (at least the start of it.) If you are killing them after they found their cities on turn 0, then I have another hypothesis.

I suspect this has to do with the City Banner being displayed, as I alluded to above.

When you try to click on the leftover banner of a city that's been "killed" in this way, you get an error pointing to CityBannerManager.lua. If you are able to kill Cities without issue on turn 0, I assume this is because on turn 0, you have not actually discovered any of these other Cities yet, so there was no city banner displayed to begin with.
 
Well, I'm curious what exactly you're doing to those poor Cities that require them to be killed off on turn 0, haha.

But yeah, I generally watch Firetuner for the lua log like a hawk these days.
 
A-ha.
I use 'pPlayer:Disband(pCity)' instead of 'pCity:Kill()'. Maybe that's the reason I do not incur in this graphical issue.

It's from a mod I'm making for a while now, Geiseric's Civilization; you play as a second Barbarian Civ, but any city you take is automatically razed (both a boon and hindrance).
 
Ah, I see. Maybe that method has more cleanup coded in than City:Kill().

Either way, I feel bad semi-derailing AW's thread, so I'll not investigate further, haha.
 
I feel bad semi-derailing AW's thread, so I'll not investigate further, haha.

With that in mind... here is your #3 inquiry, AW:
Code:
include("FLuaVector.lua") [COLOR="green"]--may remove this and the 'extra' lines if you don't want a nice human reminder that the city is unconquerable[/COLOR]

function NoConquer(iPlayer, iCityID, iDamage, iPreviousDamage)
	local pPlayer = Players[iPlayer]
	local pCity = pPlayer:GetCityByID(iCityID)
	if pPlayer:GetCivilizationType() == [COLOR="Blue"]yourciv[/COLOR] then
		local pPlot = pCity:Plot()
		pCity:ChangeDamage(iPreviousDamage - iDamage)
		[COLOR="Green"]--next 3 lines are 'extra'[/COLOR]
		if pPlot:IsVisibleToWatchingHuman() then
			Events.AddPopupTextEvent(HexToWorld(ToHexFromGrid(Vector2(pPlot:GetX(), pPlot:GetY()))), "[COLOR_NEGATIVE_TEXT][COLOR="red"]IMMORTAL H4x0r CITY![/COLOR][ENDCOLOR]", 0)
		end
	end
end

Events.SerialEventCitySetDamage.Add(NoConquer)

I'm not sure if it is able to avoid complete capture, so just to be sure, set the City's HP veeeeeeeeeeeeery high, so no 1 attack is capable of taking.
On the other hand, you could test it and return with the answer. :)

#5: The workaround would certainly works, I don't know about the method though.
#6: WHoward has a hook for that in his DLL.
 
Aren't the Events.* and SerialEvent stuff relying on the graphical engine?
I don't think they would trigger for stuff that the player can't see. I could be wrong, though, but that was the understanding I got from my research at the time into the whole "Events vs GameEvents" thing.
 
Top Bottom