1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

CodeDump: Strategic Availability

Discussion in 'Civ5 - SDK / LUA' started by Cobalt_Blue, Mar 23, 2011.

  1. Cobalt_Blue

    Cobalt_Blue Chieftain

    Joined:
    Jul 30, 2009
    Messages:
    65
    Gender:
    Female
    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);
    ---
    
     
  2. MouseyPounds

    MouseyPounds Prince

    Joined:
    Nov 8, 2010
    Messages:
    417
    Location:
    Maryland, USA
    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
     
  3. Cobalt_Blue

    Cobalt_Blue Chieftain

    Joined:
    Jul 30, 2009
    Messages:
    65
    Gender:
    Female
    Location:
    SoCal
    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:.
     
  4. Cobalt_Blue

    Cobalt_Blue Chieftain

    Joined:
    Jul 30, 2009
    Messages:
    65
    Gender:
    Female
    Location:
    SoCal
    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)
    
     

Share This Page