Resources From Buildings -- Need Help Polishing

Joined
Oct 20, 2007
Messages
522
Location
Spokane, WA
The following XML and LUA files allow for buildings to set resource types and amounts produced per turn. The demonstration includes an Alchemist building that produces 1 gold resource and additional entries can be easily added to the XML.

Technically, everything works. Build an Alchemist and you get one gold resource. Problem is the necessary function is attached to the player turn start, not building complete or building destroyed. Thus the TopPanel is having trouble properly updating, misreporting empire happiness until either a unit is moved or another turn is taken. Similarly, when you sell the buildling, you have to take another turn before you actually lose the resource, and then another turn before TopPanel properly updates again.

I would like to release this mod, but these issues aren't acceptable. Does anyone know what can be done to improve this?

Spoiler :

Code:
-- vymdt.01.2010.10.28.0000
-- Created by: Ryan F. Mercer -Open source
--===========================================================================
-- BuildingResources
--===========================================================================
--[[
Adds resources from buildings to player with corresponding buildings.
]]

include( "SaveUtils" ); MY_MOD_NAME = "Building Resources";
--===========================================================================
--[[
...
]]
function GetPlayer()
	local iPlayerID = Game.GetActivePlayer();
	if (iPlayerID < 0) then
		print("Error - player index not correct");
		return nil;
	end

	if (not Players[iPlayerID]:IsHuman()) then
		return nil;
	end;

	return Players[iPlayerID];
end
--===========================================================================
--[[
...
]]
function addBuildingResources()
	local pPlayer = GetPlayer();
	local applied = load( pPlayer, "Building Resources" );
	local listing = {};

	for pCity in pPlayer:Cities() do
		local cityID = pCity:GetID();
		listing[cityID] = {};
		applied[cityID] = applied[cityID] or {};
		
		for row in GameInfo.Building_ResourceChange() do
			local building = row.BuildingType;
			local resource = row.ResourceType;
			local change   = row.ResourceChange;
			local buildingID = GameInfo["Buildings"][building]["ID"];
			
			if pCity:IsHasBuilding( buildingID ) then
				listing[cityID][buildingID] = building;
				if applied[cityID][buildingID] == nil then
					pPlayer:ChangeNumResourceTotal( GameInfo["Resources"][resource]["ID"], change ); --cumulative.
				end
				applied[cityID][buildingID] = nil;
			end
		end
		if len( applied[cityID] ) == 0 then applied[cityID] = nil; end
	end

	for cityID,buildings in pairs( applied ) do
		for buildingID,building in pairs( buildings ) do
			
			for row in GameInfo.Building_ResourceChange() do
				local tBuilding = row.BuildingType;
				local tResource = row.ResourceType;
				local tChange   = row.ResourceChange;
				
				if tBuilding == building then
					pPlayer:ChangeNumResourceTotal( GameInfo["Resources"][tResource]["ID"], -tChange ); --cumulative.
					break;
				end
			end
		end
	end
	save( pPlayer, "Building Resources", listing );
end
Events.ActivePlayerTurnStart.Add( addBuildingResources );
--===========================================================================
--END BuildingResources
--===========================================================================
-- Created by: Ryan F. Mercer -Open source

Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Created by ModBuddy on 10/18/2010 10:47:13 AM -->
<GameData>
	<BuildingClasses>
		<!-- Table data -->
		<Row>
			<Type>BUILDINGCLASS_ALCHEMIST</Type>
			<DefaultBuilding>BUILDING_ALCHEMIST</DefaultBuilding>
			<Description>TXT_KEY_BUILDING_ALCHEMIST</Description>
		</Row>
	</BuildingClasses>
</GameData>

Code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Created by ModBuddy on 10/18/2010 10:36:36 AM -->
<GameData>
	<!-- Table definition -->
  <Table name="Building_ResourceChange">
		<Column name="BuildingType" type="text" reference="Buildings(Type)"/>
		<Column name="ResourceType" type="text" reference="Resources(Type)"/>
		<Column name="ResourceChange" type="integer"/>
	</Table>
	<!-- Table data -->
	<Buildings>
		<Row>
			<Type>BUILDING_ALCHEMIST</Type>
			<BuildingClass>BUILDINGCLASS_ALCHEMIST</BuildingClass>
			<Cost>1</Cost>
			<GoldMaintenance>1</GoldMaintenance>
			<Help>TXT_KEY_BUILDING_ALCHEMIST_HELP</Help>
			<Description>TXT_KEY_BUILDING_ALCHEMIST</Description>
			<Civilopedia>TXT_KEY_BUILDING_ALCHEMIST_PEDIA</Civilopedia>
			<Strategy>TXT_KEY_BUILDING_ALCHEMIST_STRATEGY</Strategy> <!-- not used. -->
			<ArtDefineTag>ART_DEF_BUILDING_FORGE</ArtDefineTag>
			<MinAreaSize>-1</MinAreaSize>
			<ConquestProb>0</ConquestProb>
			<HurryCostModifier>-1</HurryCostModifier>
			<IconAtlas>BW_ATLAS_1</IconAtlas>
			<PortraitIndex>28</PortraitIndex>
		</Row>
	</Buildings>
	<Building_ResourceChange>
		<Row>
			<BuildingType>BUILDING_ALCHEMIST</BuildingType>
			<ResourceType>RESOURCE_GOLD</ResourceType>
			<ResourceChange>1</ResourceChange>
		</Row>
	</Building_ResourceChange>
</GameData>
 
I'm new to Lua (but not scripting languages) so forgive me if some of the terms I use aren't proper for Lua.

I'm not sure I understand what all the code is doing now, would you humor me and let me know if this is psuedo-code is accurate?
When the human player's turn starts (activePlayer is only for human players right?):
- load the player-city-building array that was saved last time (activePlayerTurnStart of the previous turn) as "applied"
- create a new array for the player-city-building array for this activePlayerTurnStart as "listing"
- for each player-city-resourcebuilding:
-- add to the listing array
-- add to the applied array if it's not there
-- if the building isn't defined in the applied array (it wasn't built at the start of the prior turn) then add resources for it

- for each player-city-resourcebuilding in the applied array increase the players resources

- save the "listing" array for the player​


I think maybe you want to change:

Code:
for cityID,buildings in pairs( applied ) do

to:

Code:
for cityID,buildings in pairs( listing ) do

That way you are adding resources for the array of player-city-buildings that exist in this turn start and not the set that existed at the start of the prior turn. You are always a turn behind by looping over the "applied" list.

I don't think this is going to fix your problem with the top bar refreshing when you sell a building, but I'm not sure this is really a problem. Maybe a player should not lose resources the same turn they sell a building. Maybe it's correct that they get resources that turn, but then no turns after that. Seems logical to me.

If you do want a player to lose resources the same turn they sell a building then I think you'll need to add a function to remove the building from the stored array and tie it to the Event for that (maybe we don't know what that is though since it's a new feature in the patch which came out after we got the Events).

Concern: It looks to me like the bonuses from these buildings are only given to the human player (or else I'm misunderstanding activePlayer).

Concern 2: It looks like you might be giving double resources the turn after the building is completed. If you change to loop over "listing" then you should be able to remove this (unless I'm misunderstanding when buildings are "completed" in relation to activeplayerturnstart).

I might also have a couple of suggestions (mostly optimization / readability stuff) which I'll PM you if you're interested.
 
Actually, I am applying the resources the turn they show up and nothing is doubling. Buildings complete before the player turn start and by looking in the applied array it becomes clear that it is a new building because it wasn't there the turn before. I loop over the applied array again at the end to see if there are any buildings left over, which are buildings the player no longer has, so subtracts the resources. That all works just fine.

I see your point that leaving the resource until the end of turn isn't really a problem. I guess everything works as intended except for the dang TopPanel not updating instantly, and oh yeah, I guess this should work for computer players too. :]

I was just hoping that there might be a couple events or something that I'm not familiar with that would allow me to execute the necessary code at the moment a building is completed or lost. That might resolve the TopPanel issue, but then again, maybe not.

I appreciate your reply and will look at whatever you want to send me, but why not just post it here?
 
http://forums.civfanatics.com/showthread.php?t=385612
This thread (you have probably seen it) lists the Events we can hook into, but it doesn't include the parameter's that are passed. It's also where I saw that ActivePlayer = the human player (last page).

I don't see any event likely for adding a building through purchasing or selling a building. That's probably because they don't expect that doing so will have any effect until the next turn since yields (and resources) are only added after the turn is over.

I really like the idea for the mod, this will be all kinds of helpful for other mods. It's a really good idea.
 
Back
Top Bottom