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

Building Resources

Discussion in 'Civ5 - Mod Components' started by Whys, Nov 1, 2010.

  1. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Version: 6 (build: 2010.11.20.0000)

    Allows buildings to produce resources. This mod is available in the in-game mod screen by the name of "Building Resources -- Template".

    Template includes Alchemist building which gives 1 gold resource and 1 iron. Additional resources from buildings can be easily added to the XML.

    Includes 2 custom notifications that appear when a resource building is gained or lost and mouseover tooltip displays specific resources and amounts. Left click to go to corresponding location or right click to dismiss.

    Known Issue: when a luxury resource is first gained from a building, empire happiness in TopPanel does not update properly until unit moved or next turn.

    Technical: This mod uses SaveUtils.lua with ShareData.lua for compliant use of serialized ScriptData with deserialized cache shared as super-global. All concurrent mods and lua states that include SaveUtils will operate on the same shared cache.

    A brief tutorial on how to merge this mod with your own mod or mod compilations:
    http://forums.civfanatics.com/showthread.php?t=395995&p=9868011


    BuildingResources.lua
    Spoiler :

    Code:
    -- vymdt.06.2010.11.20.0000
    -- Created by: Whys Alives -Open source
    --===========================================================================
    -- BuildingResources.lua
    --===========================================================================
    --[[
    Allows buildings to produce resources.
    
    Template includes Alchemist building which gives 1 gold resource and 1 iron.
    Additional resources from buildings can be easily added to the XML.
    
    Includes 2 custom notifications that appear when a resource building is
    gained or lost and mouseover tooltip displays specific resources and amounts.
    Left click to go to corresponding location or right click to dismiss.
    
    Known Issue: when a luxury resource is first gained from a building, empire
    happiness in TopPanel does not update properly until unit moved or next turn.
    
    Technical: This mod uses SaveUtils.lua with ShareData.lua for compliant use
    of serialized ScriptData with deserialized cache shared as super-global.  All
    concurrent mods and lua states that include SaveUtils will operate on the
    same shared cache.
    ]]
    include( "SaveUtils" ); MY_MOD_NAME = "BuildingResources--vymdt.06.2010.11.20.0000";
    include( "WhysUtils" );
    --===========================================================================
    --[[
    Adds and removes resources provided by buildings.
    ]]
    function applyBuildingResources()
      --setCacheState( 0 ); --no cache.
      
      --build resource change table.
      local changeTable = {};
      for row in GameInfo.Building_ResourceChange() do
        changeTable[row.BuildingType] = changeTable[row.BuildingType] or {};
        changeTable[row.BuildingType][row.ResourceType] = row.ResourceChange;
      end
    
      --loop thru all live players (human and AI).
      for index,pPlayer in pairs( Players ) do
        if pPlayer ~= nil and pPlayer:IsAlive() then
          
          --previously applied building resources and city locations.
          local applied_prev = load( pPlayer, "applied" ) or {};
          local located_prev = load( pPlayer, "located" ) or {};
    
          --currently applied building resources and city locations.
          local applied_curr = {};
          local located_curr = {};
    
          --loop thru player cities.
          for pCity in pPlayer:Cities() do
            local cityID = pCity:GetID();
            applied_prev[cityID] = applied_prev[cityID]  or {};
            applied_curr[cityID] = {};
            located_curr[cityID] = { pCity:GetX(), pCity:GetY() };
            
            --loop thru relevant building types.
            for buildingType,resourceTable in pairs( changeTable ) do
              local buildingID          = GameInfo.Buildings[buildingType].ID;
              local buildingDescription  = GameInfo.Buildings[buildingType].Description;
    
              --look for matching buildings.
              if pCity:IsHasBuilding( buildingID ) then
                applied_curr[cityID][buildingID] = buildingType;
    
                --apply resources for new buildings.
                if applied_prev[cityID][buildingID] == nil then
                  local toolTip = ""; --for custom notification.
                  
                  --loop thru resource changes.
                  for resourceType,resourceChange in pairs( resourceTable ) do
                    local resourceID          = GameInfo.Resources[resourceType].ID;
                    local resourceDescription  = GameInfo.Resources[resourceType].Description;
                    local resourceIconString  = GameInfo.Resources[resourceType].IconString;
    
                    pPlayer:ChangeNumResourceTotal( resourceID, resourceChange ); --cumulative.
                    if toolTip ~= "" then toolTip = toolTip.."[NEWLINE]"; end
                    toolTip = toolTip.."[ICON_BULLET][COLOR_POSITIVE_TEXT]"
                        ..resourceChange.."[ENDCOLOR] "..resourceIconString.." "
                        ..Locale.ConvertTextKey( resourceDescription );
                  end --END resource loop.
    
                  --activate notification.
                  if pPlayer:GetID() == Game.GetActivePlayer() then
                    toolTip = "[COLOR_POSITIVE_TEXT]Gained[ENDCOLOR] "
                        ..Locale.ConvertTextKey( buildingDescription )
                        ..":[NEWLINE]"..toolTip;
                    LuaEvents.CustomNotification( 1001 --BuildingResourcesGain.
                        , "[COLOR_POSITIVE_TEXT]Resource Gain[ENDCOLOR]", toolTip
                        , { ["location"]      = located_curr[cityID]
                          , ["buildingType"]  = buildingType } );
                  end
                end
                --remove handled building case.
                applied_prev[cityID][buildingID] = nil;
              end
            end --END building loop.
    
            --remove empty city case.
            if len( applied_prev[cityID] ) == 0 then applied_prev[cityID] = nil; end
          end --END city loop.
    
          --save applied building resources and city locations.
          save( pPlayer, "applied", applied_curr );
          save( pPlayer, "located", located_curr );
    
          --unapply resources for lost buildings.
          for cityID,buildingTable in pairs( applied_prev ) do
            for buildingID,buildingType in pairs( buildingTable ) do
              local buildingID          = GameInfo.Buildings[buildingType].ID;
              local buildingDescription  = GameInfo.Buildings[buildingType].Description;
              local toolTip = ""; --for custom notification.
    
              --loop thru resource changes.
              for resourceType,resourceChange in pairs( changeTable[buildingType] ) do
                local resourceID          = GameInfo.Resources[resourceType].ID;
                local resourceDescription  = GameInfo.Resources[resourceType].Description;
                local resourceIconString  = GameInfo.Resources[resourceType].IconString;
    
                pPlayer:ChangeNumResourceTotal( resourceID, -resourceChange ); --cumulative.
                if toolTip ~= "" then toolTip = toolTip.."[NEWLINE]"; end
                toolTip = toolTip.."[ICON_BULLET][COLOR_NEGATIVE_TEXT]"
                    ..resourceChange.."[ENDCOLOR] "..resourceIconString.." "
                    ..Locale.ConvertTextKey( resourceDescription );
              end --END resource loop.
    
              --activate notification.
              if pPlayer:GetID() == Game.GetActivePlayer() then
                toolTip = "[COLOR_NEGATIVE_TEXT]Lost[ENDCOLOR] "
                    ..Locale.ConvertTextKey( buildingDescription )..":[NEWLINE]"
                    ..toolTip;
                LuaEvents.CustomNotification( 1002 --BuildingResourcesLoss.
                    , "[COLOR_NEGATIVE_TEXT]Resource Loss[ENDCOLOR]", toolTip
                    , { ["location"]      = located_prev[cityID]
                      , ["buildingType"]  = buildingType } );
              end
            end --END building loop.
          end --END city loop.
        end
      end --END player loop.
      LuaEvents.BuildingResourcesApplied();
    end
    Events.ActivePlayerTurnStart.Add( applyBuildingResources );
    --===========================================================================
    --END BuildingResources.lua
    --===========================================================================
    -- Created by: Whys Alives -Open source
    

    Template XML & SQL:
    Spoiler :

    CIV5BuildingClasses.xml
    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <GameData>
    	<BuildingClasses>
    		<!-- Table data -->
    		<Row>
    			<Type>BUILDINGCLASS_ALCHEMIST</Type>
    			<DefaultBuilding>BUILDING_ALCHEMIST</DefaultBuilding>
    			<Description>TXT_KEY_BUILDING_ALCHEMIST</Description>
    		</Row>
    	</BuildingClasses>
    </GameData>
    
    CIV5Buildings.xml
    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <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>
    			<ArtDefineTag>ART_DEF_BUILDING_FORGE</ArtDefineTag>
    			<MinAreaSize>-1</MinAreaSize>
    			<ConquestProb>0</ConquestProb>
    			<HurryCostModifier>-1</HurryCostModifier>
    			<IconAtlas>CIV_COLOR_ATLAS_LEGENDS</IconAtlas>
    			<PortraitIndex>3</PortraitIndex>
    		</Row>
    	</Buildings>
    	<Building_ResourceChange>
    		<Row>
    			<BuildingType>BUILDING_ALCHEMIST</BuildingType>
    			<ResourceType>RESOURCE_GOLD</ResourceType>
    			<ResourceChange>1</ResourceChange>
    		</Row>
    		<Row>
    			<BuildingType>BUILDING_ALCHEMIST</BuildingType>
    			<ResourceType>RESOURCE_IRON</ResourceType>
    			<ResourceChange>1</ResourceChange>
    		</Row>
    	</Building_ResourceChange>
    </GameData>
    
    Text.sql:
    Code:
    INSERT INTO Language_en_US ( 'Tag', 'Text' )
    	VALUES ( 'TXT_KEY_BUILDING_ALCHEMIST', 'Alchemist' );
    INSERT INTO Language_en_US ( 'Tag', 'Text' )
    	VALUES ( 'TXT_KEY_BUILDING_ALCHEMIST_HELP', 'Produces 1 gold resource and 1 iron.' );
    INSERT INTO Language_en_US ( 'Tag', 'Text' )
    	VALUES ( 'TXT_KEY_BUILDING_ALCHEMIST_PEDIA', 'The alchemist produces gold and iron.' );
    INSERT INTO Language_en_US ( 'Tag', 'Text' )
    	VALUES ( 'TXT_KEY_BUILDING_ALCHEMIST_STRATEGY', 'Turn lead into gold!' );
    

    This mod uses SaveUtils.lua
    You can find it here: http://forums.civfanatics.com/showthread.php?t=392958

    This mod uses ShareData.lua
    You can find it here: http://forums.civfanatics.com/showthread.php?t=398814

    This mod uses CustomNotificationPanel.lua
    You can find it here: http://forums.civfanatics.com/showthread.php?p=9899079

    This mod uses WhysUtils.lua
    Spoiler :

    Code:
    -- vymdt.01.2010.11.17.0000
    -- Created by: Whys Alives -Open source
    --===========================================================================
    -- WhysUtils.lua
    --===========================================================================
    --[[
    Global Functions: len(), out().
    ]]
    --===========================================================================
    --[[
    Returns length of both tables and strings.  Returns false when given boolean,
    function, userdata, or thread.
    ]]
    function len( p )
      local r = 0; local t = type( p );
      if t == "boolean" or t == "function" or t == "userdata" or t == "thread"
          then r = false; print( "len(): Invalid type: "..t ); --error.
      elseif t == "table"  then for k,v in pairs( p ) do r = r +1; end
      elseif t == "string" then r = p:len();
      end
      return r;
    end
    --===========================================================================
    --[[
    Returns a string representation of any given data type.
    ]]
    function out( p )
      local r = ""; local t = type( p );
      if p ~= nil then
        if t ~= "table" then
          if t == "boolean" or t == "number" or t == "function"
              or t == "userdata"  or t == "thread" then
            r = tostring( p );
          else r = '"'..p..'"';
          end
        else
          r = "{"; local b = false;
          for k,v in pairs( p ) do
            if b then r = r..","; end
            r = r..out( k ).."="..out( v );
            b = true;
          end
          r = r.."}"
        end
      end
      return r;
    end
    --===========================================================================
    --END WhysUtils.lua
    --===========================================================================
    -- Created by: Whys Alives -Open source
    
     
  2. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
  3. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
  4. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
  5. MilkmanDan

    MilkmanDan Chieftain

    Joined:
    Dec 24, 2005
    Messages:
    54
    Nice, I was thinking of making a power plant mod (so factories don't require coal but instead electricity) and shifting hydro, solar to produce electricity and then a coal/oil plant with the same. Although, solar might work better as a tile improvement to make that desert good for something.

    Anyway, this was my stumbling block was how to add resources via building so thanks!
     
  6. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Hmmm... just learned that active player is only human players. So the AI can't use this, only human players. If someone knows how to fix this, do tell.
     
  7. MilkmanDan

    MilkmanDan Chieftain

    Joined:
    Dec 24, 2005
    Messages:
    54
    Since it's only 1 human player (for mods), can you do it as one event but loop over all players?
     
  8. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
  9. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Oops. There's a cumulative issue. More resources each turn. Sigh... fixing it now.

    This only occurs if you have more than one resource type for a building and I think I know what's causing it.
     
  10. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Okay, fixed both issues. Now it works for AI players and multiple resources from a single building no longer causes continual addition.
     
  11. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Darn it. Now it doesn't properly subtract multiple resources from a single building when the building is lost. :p

    Version 3, here I come. :]
     
  12. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Okay, version 3 posted and uploaded. Works with all players (human and AI), allows multiple resource types per building, and adds/removes resources as intended.

    Yay! :)

    Added an iron to the Alchemist to demonstrate multiple resources per building.
     
  13. MilkmanDan

    MilkmanDan Chieftain

    Joined:
    Dec 24, 2005
    Messages:
    54
    Great.

    All in all though I'm surprised just how many things aren't available in the civ v api. No beginning of turn hook for any player?, building limits on what they can produce. honestly, terrain/buildings/improvements should be able to produce ANY meaningful item/resource and to have it otherwise takes more programming work to make it limited.
     
  14. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Civ5 is built on a lot of legacy architecture. They've overcome many of the previously built in limitations, but this initial implementation is still based on old assumptions.

    I'll say this, I've been able to do far more modding in far less time in Civ5 than I ever could for Civ4. I'm actually surprised I finished this mod before someone else. It was a well known staple Civ4 mod.

    Unfortunately, I'm getting the sense that several of the better modders available have thrown their hands up in disgust due to sorely lacking documentation and seemingly basic but apparently missing hooks. While I can understand their frustration, I think for now one should not focus on the inability to implement one's grand idea, but rather focus on what *is* currently doable.

    I have no interest in resources from buildings myself. It plays no role in my grand idea that I continue to work on in the background. But by fulfilling mod requests for things that are doable *now*, it's all experience under my belt for later.
     
  15. Supercheese

    Supercheese Zeppelin Commander

    Joined:
    Aug 2, 2009
    Messages:
    378
    Location:
    Idaho
    I, for one, am very glad that this mod component has been released -- several ideas of mine for Civ5 mini-mods that were formerly impossible are now easy! Huzzah! :goodjob:
     
  16. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    I've noticed that negative values will work as well. But there is no default behavior preventing a negative resource total.
     
  17. elistor

    elistor Warlord

    Joined:
    Jan 12, 2008
    Messages:
    111
    Ohhhh gimmie! :D
    How would you like to be credited for this as I'd be using it in one of my existing mods and it makes another mod i'd put on hold one step closer to workable (now if only i could figure out how to make <HiddenNationality> allow offinsive combat with nations other than barbarian).

    And a better way to describe things is do i need to tell people to download your mod so that mine will work or should i just incorperate your mod into any of mine so that they have it anyway?
     
  18. Whys

    Whys Between the Lines

    Joined:
    Oct 20, 2007
    Messages:
    456
    Merge it however you like, just try and give me credit for the BuildingResources.lua file if you would please. Also keep in mind that this mod uses SaveUtils.lua for saving and loading custom data across saved games and across concurrent mods. That's my work as well.
     
  19. elistor

    elistor Warlord

    Joined:
    Jan 12, 2008
    Messages:
    111
    Thanks! If I'm borrowing someone else code I always try and get the preferred method of crediting beforehand, that way it's less likely to displease anyone or cause other friction. I won't be editing the LUA at all since I haven't taken the time to really learn it yet so all your credit tags in there will stay as is as long as I'm using it and I'll add credit to each file that uses your LUA, which is my standard practice when using someone else work. I may also be adding a Thank you note to my mod's since I keep getting donated art and this code now.
     
  20. Decimatus

    Decimatus King

    Joined:
    Oct 22, 2005
    Messages:
    853
    I assume we can now create a new resource and then create a building that produces it?

    Great addition! :)
     

Share This Page