CodeDump: Strategic Availability

Cobalt_Blue

Chieftain
Joined
Jul 30, 2009
Messages
65
Location
SoCal
Hi all, I just released my first Civ V mod, Strategic Availability (found here: http://forums.civfanatics.com/showthread.php?t=416215), which is designed to be a sort of middle ground between normal play and the "Strategic Balance" resource setting. It turned out to be a pretty small mod, just one lua file.

Now that I'm mostly done, I thought I might I'd dump the code for two reasons:
1) To ask you guys to glance at the code and make sure I didn't do anything monumentally stupid that is going to break other mods or something down the line.
2) I figure the code may help someone somewhere someday with their own modding.

Or any other suggestions, really. Are there any "best practices" established for this yet? Any "worst practices" that you see here?

Anyway, thanks in advance for looking.

Spoiler :
Code:
-- Strategic_Availability
-- Author: Cobalt_Blue
-- DateCreated: 3/21/2011 8:26:41 PM
--------------------------------------------------------------

-------------
---GLOBALS---
-------------

--Flags
runOnceAlready = false;

--Techs
horsebackRidingID = -1;
TXT_HORSEBACK_RIDING = "TECH_HORSEBACK_RIDING";
steelID = -1;
TXT_STEEL = "TECH_STEEL";
steamPowerID = -1;
TXT_STEAM_POWER = "TECH_STEAM_POWER";
combustionID = -1;
TXT_COMBUSTION =  "TECH_COMBUSTION";
electronicsID = -1;
TXT_ELECTRONICS = "TECH_ELECTRONICS";
nuclearFusionID = -1;
TXT_NULCEAR_FUSION = "TECH_NUCLEAR_FUSION";

--Resources
horsesID = -1;
TXT_HORSES = "RESOURCE_HORSE";
ironID = -1;
TXT_IRON = "RESOURCE_IRON";
coalID = -1;
TXT_COAL = "RESOURCE_COAL";
oilID = -1;
TXT_OIL = "RESOURCE_OIL";
aluminumID = -1;
TXT_ALUMINUM = "RESOURCE_ALUMINUM";
uraniumID = -1;
TXT_URANIUM = "RESOURCE_URANIUM";

-------------
--FUNCTIONS--
-------------

--Debugging tool, not used
function Listen(...)
	print("LISTENING")
	print(unpack({...}))
	print("END LISTENING")
end


--Load all relevant ids into global vars
function StrategicAvailability_GetIds()
	--Get Resource IDs
	for i = 0, 63, 1 do
		local pResourceInfo = GameInfo.Resources[i];
		if (pResourceInfo ~= nil) then
			szText = Locale.ConvertTextKey( pResourceInfo.Type);
			if (szText == TXT_HORSES) then
				horsesID = i;
			elseif (szText == TXT_IRON) then
				ironID = i;
			elseif (szText == TXT_COAL) then
				coalID = i;
			elseif (szText == TXT_OIL) then
				oilID = i;
			elseif (szText == TXT_ALUMINUM) then
				aluminumID = i;
			elseif (szText == TXT_URANIUM) then
				uraniumID = i;
			end
		end
	end
	--Get Tech IDs
	for i = 0, 255, 1 do
		local pTechInfo = GameInfo.Technologies[i];
		if (pTechInfo ~= nil) then
			szText = Locale.ConvertTextKey( pTechInfo.Type );
			if (szText == TXT_HORSEBACK_RIDING) then
				horsebackRidingID = i;
			elseif (szText == TXT_STEEL) then
				steelID = i;
			elseif (szText == TXT_STEAM_POWER) then
				steamPowerID = i;
			elseif (szText ==TXT_COMBUSTION) then
				combustionID = i;
			elseif (szText == TXT_ELECTRONICS) then
				electronicsID = i;
			elseif (szText == TXT_NULCEAR_FUSION) then
				nuclearFusionID = i;
			end
		end
	end		
end

--If the player acquired the right tech, increase their resource by one
function StrategicAvailability_TechAcquired(player_id, tech_id)
	local player = Players[player_id];
	local pTechInfo = GameInfo.Technologies[tech_id];
	szText = Locale.ConvertTextKey( pTechInfo.Type );
	
	--print( "player " .. player_id .. ": " .. player:GetName() .. "acquired tech " .. tech_id .. ": " .. szText );
	if ((not player:IsMinorCiv()) and (not player:IsBarbarian())) then
		--Steel/Iron
		if ((tech_id == steelID) and (ironID ~= nil)) then
			player:ChangeNumResourceTotal(ironID,1);
			print("Giving 1 Iron to " .. player:GetName());
		--HorsebackRiding/Horses
		elseif ((tech_id == horsebackRidingID) and (horsesID ~= nil)) then
			player:ChangeNumResourceTotal(horsesID,1);
			print("Giving 1 Horse to " .. player:GetName());
		--SteamPower/Coal
		elseif ((tech_id == steamPowerID) and (coalID ~= nil)) then
			player:ChangeNumResourceTotal(coalID,1);
			print("Giving 1 Coal to " .. player:GetName());
		--Combustion/Oil
		elseif ((tech_id == combustionID) and (oilID ~= nil)) then
			player:ChangeNumResourceTotal(oilID,1);
			print("Giving 1 Oil to " .. player:GetName());
		--Electronics/Aluminum
		elseif ((tech_id == electronicsID) and (aluminumID ~= nil)) then
			player:ChangeNumResourceTotal(aluminumID,1);
			print("Giving 1 Aluminum to " .. player:GetName());
		--NuclearFusion/Uranium
		elseif ((tech_id == nuclearFusionID) and (uraniumID ~= nil)) then
			player:ChangeNumResourceTotal(uraniumID,1);
			print("Giving 1 Uranium to " .. player:GetName());
		end
	end
end

--check for techs once at the start of the game so that advanced starts get proper bonuses
function StrategicAvailability_StartGame(xyz, civID, ctr, a,b,c,d,e,f)	
	if (not runOnceAlready) then
		print("STRATEGIC AVAILABILITY MOD: Starting up!");
		for i = 0, 127, 1 do
			local player = Players[i];
			if (player ~= nil and player:IsAlive()) then
				print(player:GetName().. " : " .. i);
				if ((not player:IsMinorCiv()) and (not player:IsBarbarian())) then
					--Grab techs player already has
					teamID = player:GetTeam();
					teamTechs = Teams[teamID]:GetTeamTechs()

					--STEEL
					if(teamTechs:HasTech(steelID)) then
						print(player:GetName() .. " already has STEEL! Adding one (1) Iron.")
						player:ChangeNumResourceTotal(ironID,1);
					end
					--HORSEBACK RIDING
					if(teamTechs:HasTech(horsebackRidingID)) then
						print(player:GetName() .. " already has HORSEBACK RIDING! Adding one (1) Horse.")
						player:ChangeNumResourceTotal(horsesID,1);
					end
					--STEAM POWER
					if(teamTechs:HasTech(steamPowerID)) then
						print(player:GetName() .. " already has STEAM POWER! Adding one (1) Coal.")
						player:ChangeNumResourceTotal(coalID,1);
					end
					--COMBUSTION
					if(teamTechs:HasTech(combustionID)) then
						print(player:GetName() .. " already has COMBUSTION! Adding one (1) Oil.")
						player:ChangeNumResourceTotal(oilID,1);
					end
					--ELECTRONICS
					if(teamTechs:HasTech(electronicsID)) then
						print(player:GetName() .. " already has ELECTRONICS! Adding one (1) Aluminum.")
						player:ChangeNumResourceTotal(aluminumID,1);
					end
					--NUCLEAR FUSION
					if(teamTechs:HasTech(nuclearFusionID)) then
						print(player:GetName() .. " already has NUCLEAR FUSION! Adding one (1) Uranium.")
						player:ChangeNumResourceTotal(uraniumID,1);
					end			
				end
			end
		end
		runOnceAlready = true;
	end
end



-------------
---STARTUP---
-------------
StrategicAvailability_GetIds();
Events.TechAcquired.Add(StrategicAvailability_TechAcquired);
Events.SerialEventCityCreated.Add(StrategicAvailability_StartGame);

--DEBUG MESSAGES, Making sure all the IDs were found
---
print("IRON:".. ironID);
print("HORSE:".. horsesID);
print("COAL:".. coalID);
print("OIL:".. oilID);
print("ALUMINUM:".. aluminumID);
print("URANIUM:".. uraniumID);
---
print("STEEL:".. steelID);
print("HORSEBACK_RIDING:".. horsebackRidingID);
print("STEAM_POWER:".. steamPowerID);
print("COMBUSTION:".. combustionID);
print("ELECTRONICS:".. electronicsID);
print("NUCLEAR_FUSION:".. nuclearFusionID);
---
 
I haven't looked really closely at the code, but on a quick skim a couple things jump out at me.

First, you are doing way more work than necessary to get your resource and tech IDs. They are all available via the GameInfoTypes table so you can just use, for example GameInfoTypes.RESOURCE_HORSE or GameInfoTypes.TECH_ELECTRONICS to get the horse resource ID and electronics tech ID respectively. These can be used directly in your comparisons so you don't need the global variables or the GetIds() function.

Second, why loop from 0 to 127 when checking players? Until we get DLL access, the max player number is 63 and even then it is cleaner to simply iterate over the Players table directly like so:
Code:
for i, player in ipairs(Players) do
  -- "i" is player num and "player" is Player object
  -- and can now be used just like you had them before:
  if (player ~= nil and player:IsAlive()) then
    print(player:GetName().. " : " .. i)
    -- and so on
  end
end

Third, line-ending semicolons are unnecessary in Lua. I know a lot of the Firaxis code uses them -- probably because the devs are so used to C++ syntax -- and they don't actually hurt anything, but they just bloat the file size. :p
 
I haven't looked really closely at the code, but on a quick skim a couple things jump out at me.

First, you are doing way more work than necessary to get your resource and tech IDs. They are all available via the GameInfoTypes table so you can just use, for example GameInfoTypes.RESOURCE_HORSE or GameInfoTypes.TECH_ELECTRONICS to get the horse resource ID and electronics tech ID respectively. These can be used directly in your comparisons so you don't need the global variables or the GetIds() function.

Second, why loop from 0 to 127 when checking players? Until we get DLL access, the max player number is 63 and even then it is cleaner to simply iterate over the Players table directly like so:
Code:
for i, player in ipairs(Players) do
  -- "i" is player num and "player" is Player object
  -- and can now be used just like you had them before:
  if (player ~= nil and player:IsAlive()) then
    print(player:GetName().. " : " .. i)
    -- and so on
  end
end

Third, line-ending semicolons are unnecessary in Lua. I know a lot of the Firaxis code uses them -- probably because the devs are so used to C++ syntax -- and they don't actually hurt anything, but they just bloat the file size. :p

AWESOME.

1) Ah, I didn't know that. I was kind of worried that my current version might break with locality issues, but I was hoping that since that variable wasn't displayed it would remain the same. I'll put that into the next version for sure. Thanks.

2) Why not? This only runs once, so efficiency isn't an issue, and if in the future someone adds the ability to do more, the mod will still work. HOWEVER, I can see a foreach loop is cleaner. I didn't know that was an available construct. I'll change that as well.

3) Yeah, habit. Too much C++/Java/C# for my own good.

THANK YOU for the feedback! I just downloaded the SDK and started coding yesterday without reading much into it first, so I've got lots to learn obviously :lol:.
 
Just want to say thanks again. Here's the new code (for posterity ;) ). It's much shorter, cleaner.

Spoiler :
Code:
-- Strategic_Availability
-- Author: Cobalt_Blue
-- DateCreated: 3/21/2011 8:26:41 PM
--------------------------------------------------------------

-------------
---GLOBALS---
-------------
STRATEGIC_AVAILABILITY_START_UP_TEXT = "STRATEGIC AVAILABILITY MOD: Starting up!"
runOnceAlready = false

-------------
--FUNCTIONS--
-------------

--If the player acquired the right tech, increase their resource by one
function StrategicAvailability_TechAcquired(player_id, tech_id)
	local player = Players[player_id];
	local pTechInfo = GameInfo.Technologies[tech_id]
	local techText = Locale.ConvertTextKey( pTechInfo.Type )
	
	if ((not player:IsMinorCiv()) and (not player:IsBarbarian())) then
		print( "player " .. player_id .. ": " .. player:GetName() .. "acquired tech " .. tech_id .. ": " .. techText )
		
		--HorsebackRiding/Horses
		if (tech_id == GameInfoTypes.TECH_HORSEBACK_RIDING) then
			player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_HORSE,1)
			print("Giving 1 Horse to " .. player:GetName());
		--Steel/Iron
		elseif (tech_id == GameInfoTypes.TECH_STEEL) then
			player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_IRON,1)
			print("Giving 1 Iron to " .. player:GetName());	
		--SteamPower/Coal
		elseif (tech_id == GameInfoTypes.TECH_STEAM_POWER) then
			player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_COAL,1)
			print("Giving 1 Coal to " .. player:GetName())
		--Combustion/Oil
		elseif (tech_id == GameInfoTypes.TECH_COMBUSTION) then
			player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_OIL,1)
			print("Giving 1 Oil to " .. player:GetName())
		--Electronics/Aluminum
		elseif (tech_id == GameInfoTypes.TECH_ELECTRONICS) then
			player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_ALUMINUM,1)
			print("Giving 1 Aluminum to " .. player:GetName())
		--NuclearFusion/Uranium
		elseif (tech_id == GameInfoTypes.TECH_NUCLEAR_FUSION) then
			player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_URANIUM,1)
			print("Giving 1 Uranium to " .. player:GetName())
		end

	end
end --END StrategicAvailability_TechAcquired()


--Run this routine ONCE ONLY at the start of the game. If starting in later eras, give the appropriate bonuses for already-learned techs.
function StrategicAvailability_StartGame(xyz, civID, ctr, a,b,c,d,e,f)	
	if (not runOnceAlready) then
		print(STRATEGIC_AVAILABILITY_START_UP_TEXT)
		runOnceAlready = true
		
		for i, player in ipairs(Players) do
			if (player ~= nil and player:IsAlive()) then
				print(player:GetName().. " : " .. i)
				if ((not player:IsMinorCiv()) and (not player:IsBarbarian())) then
					--Grab techs player already has
					teamID = player:GetTeam();
					teamTechs = Teams[teamID]:GetTeamTechs()

					--HORSEBACK RIDING
					if(teamTechs:HasTech(GameInfoTypes.TECH_HORSEBACK_RIDING)) then
						print(player:GetName() .. " already has HORSEBACK RIDING! Adding one (1) Horse.")
						player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_HORSE,1)
					end
					--STEEL
					if(teamTechs:HasTech(GameInfoTypes.TECH_STEEL)) then
						print(player:GetName() .. " already has STEEL! Adding one (1) Iron.")
						player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_IRON,1)
					end					
					--STEAM POWER
					if(teamTechs:HasTech(GameInfoTypes.TECH_STEAM_POWER)) then
						print(player:GetName() .. " already has STEAM POWER! Adding one (1) Coal.")
						player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_COAL,1)
					end
					--COMBUSTION
					if(teamTechs:HasTech(GameInfoTypes.TECH_COMBUSTION)) then
						print(player:GetName() .. " already has COMBUSTION! Adding one (1) Oil.")
						player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_OIL,1)
					end
					--ELECTRONICS
					if(teamTechs:HasTech(GameInfoTypes.TECH_ELECTRONICS)) then
						print(player:GetName() .. " already has ELECTRONICS! Adding one (1) Aluminum.")
						player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_ALUMINUM,1)
					end
					--NUCLEAR FUSION
					if(teamTechs:HasTech(GameInfoTypes.TECH_NUCLEAR_FUSION)) then
						print(player:GetName() .. " already has NUCLEAR FUSION! Adding one (1) Uranium.")
						player:ChangeNumResourceTotal(GameInfoTypes.RESOURCE_URANIUM,1)
					end	
					--		
				end
			end
		end --END FOREACH
				
	end
end --END StrategicAvailability_StartGame()


-------------
---STARTUP---
-------------
Events.TechAcquired.Add(StrategicAvailability_TechAcquired)
Events.SerialEventCityCreated.Add(StrategicAvailability_StartGame)
 
Top Bottom