Jump to content

Making a mod to connect fuel barrels to a generator


salvixd

Recommended Posts

Hello there fellow non-brains eaters! :lol:

 

Please let me introduce myself, my name is salvixd I'm 23, i've been playing project zomboid for about 280~hours now. After playing for a long time i decied to try and make a mod.

 

Have you ever struggled to go out scavenging, exploring, travelling...etc. Leaving your base with your generator running and once you are back you are out of fuel and all your food is wasted.

It happened to me and after looking a lot. :huh: I have not found any mod that allows me to connect fuel barrels to generators. So i will try to do it by myself so others survivors won't have that issue.

 

The problem is that... I don't know which script is the one that i need to copy and modify for it. I supose the one that allows you to connect the barrels to sinks, but where is it? I don't know.

 

Could some of you please tell me where to look?

 

I would really appreciate it! 

 

Thanks in advance, looking forward your  replies

 

Salvixd 

Link to comment
Share on other sites

Hey there Salvixd! I'm Bulwulff and I've been programming for over 20 years and modded more games than I care to count. From experience lets start here:

 

The first thing to consider is the mechanics -> How exactly will you attach a barrel to a generator? by a hose or pipe? Pipe might make more sense from the games logic point as you could create sprites for the pipes and build them like you would walls or floors. I've thought about doing something like this myself.

 

From a programming standpoint however doing something like a hose would be simpler though still a big challenge, especially if you want to make the hose visible and be able to connect it to barrels or maybe even the gas tank of another vehicle, or other container. If you're not worried about making the hose or pipe itself visible, then from a programming standpoint it would be a modification to the generator lua code - specifically the ISGeneratorInfoWindow.lua so you can add something that shows it's connection, and maybe MOGenerator.lua? It may even be better to write your own code as well.

 

Just something to think on since no one else has responded yet. I know what it's like to get stuck on something you want to make happen.

Link to comment
Share on other sites

On 1/11/2022 at 5:11 AM, bulwulff said:

Hey there Salvixd! I'm Bulwulff and I've been programming for over 20 years and modded more games than I care to count. From experience lets start here:

 

The first thing to consider is the mechanics -> How exactly will you attach a barrel to a generator? by a hose or pipe? Pipe might make more sense from the games logic point as you could create sprites for the pipes and build them like you would walls or floors. I've thought about doing something like this myself.

 

From a programming standpoint however doing something like a hose would be simpler though still a big challenge, especially if you want to make the hose visible and be able to connect it to barrels or maybe even the gas tank of another vehicle, or other container. If you're not worried about making the hose or pipe itself visible, then from a programming standpoint it would be a modification to the generator lua code - specifically the ISGeneratorInfoWindow.lua so you can add something that shows it's connection, and maybe MOGenerator.lua? It may even be better to write your own code as well.

 

Just something to think on since no one else has responded yet. I know what it's like to get stuck on something you want to make happen.

 

Thank you so much Bulwulff for taking a bit of your time to answer!

My idea is to keep off things simple and easy. So to start off with wooden barrels and maybe iron barrels, the ones found around the world. 

 

It would look awesome to create visible pipes and all that... but it's would be my very first mod and I can't be more lost haha.

Although... maybe in a future if i manage to make it. :rolleyes::rolleyes:

 

Well, let's go to the point.

 

I thought about copying the "lua code" used to plumb wooden barrels to sinks, showers, toilets...etc. And "rewrite it" or something to addapt it to storage fuel and the make it able to be connected to a generator. Then as the 100% goes down it will take fuel from the "connected  barrel".

 

Now you have said it would be better to write the lua code itself from zero, you made me think about it what would it be better?:huh::huh:

 

What do you think?

 

Again thank you so much for your information :lol::lol:

 

 

 


 

Link to comment
Share on other sites

  • 1 month later...
On 1/7/2022 at 10:37 AM, salvixd said:

Please let me introduce myself, my name is salvixd I'm 23, i've been playing project zomboid for about 280~hours now. After playing for a long time i decied to try and make a mod.

 

Have you ever struggled to go out scavenging, exploring, travelling...etc. Leaving your base with your generator running and once you are back you are out of fuel and all your food is wasted.

 

 

Have you made any progress with this idea?  This appears like something that many players would like.  Something to consider is looking at other fuel mods and see how they do things.  I almost always play my games with the Fuel API mod now which adds some extra fuel mechanics and big 400 capacity barrels.  Would be nice if you could just connect that barrel to a generator. 

Link to comment
Share on other sites

  • 8 months later...
On 2/15/2022 at 5:06 PM, Spar10 said:

 

Have you made any progress with this idea?  This appears like something that many players would like.  Something to consider is looking at other fuel mods and see how they do things.  I almost always play my games with the Fuel API mod now which adds some extra fuel mechanics and big 400 capacity barrels.  Would be nice if you could just connect that barrel to a generator. 

Hello! Unfortunatetly i haven't been able to do any advances... Maybe someone with more experience might be able to do it... 

Link to comment
Share on other sites

  • 1 month later...

Ah and btw: You can implement such logic by using the GlobalObject event handlers.

Here is an example class of mine where I've added an reuseable global object class to implement different types of water collectors as global objects with the capabilities of the vanilla collectors/barrels. It's a bit more, then you require, but maybe it will still help you to get a better undertanding of the global object system.

 

ISWaterCollector.lua

if not ISExtBuildingObject then require 'ExtBuilding/BuildingObjects/ISExtBuildingObject' end


--- @class ISWaterCollector : ISExtBuildingObject
ISWaterCollector = ISExtBuildingObject:derive('ISWaterCollector')


ISWaterCollector.defaults = {
  hasSpecialTooltip = true,
  isoData = {
    isoName = 'watercollector',    -- used as fallback name for the iso object
    systemName = 'watercollector',    -- used as name for the map object system
    objectModDataKeys = { 'waterAmount', 'waterMax', 'addWaterPerAction' },
  },
  properties = {
    waterAmount = 0,
    waterMax = 100,
    addWaterPerAction = 1
  }
}
ISWaterCollector.registeredRecipes = {}


---
--- Java object constructor - initializes and places a completed water collector
--- @param x number Target cell X coordinate (goes from north to south)
--- @param y number Target cell Y coordinate (goes from west to east)
--- @param z number Target cell level (0 = surface, 7 = highest possible layer)
--- @param north boolean Whether the north sprite was chosen
--- @param sprite string Name of the chosen sprite
---
function ISWaterCollector:create(x, y, z, north, sprite)
  ISExtBuildingObject.create(self, x, y, z, north, sprite)
  self.javaObject:getModData()['waterMax'] = self.waterMax
  self.javaObject:getModData()['waterAmount'] = self.waterAmount
  self.javaObject:getModData()['addWaterPerAction'] = self.addWaterPerAction
  self.javaObject:transmitCompleteItemToServer()
  if getCore():getGameMode() ~= 'Multiplayer' then triggerEvent('OnObjectAdded', self.javaObject) end
end



---
--- Lua object constructor - generates a new water collector object
--- @param player number Target player ID
--- @param recipe table The building definition - used to add/alter class fields/properties/modData
--- @return ISWaterCollector BuildingObject instance
---
function ISWaterCollector:new(player, recipe)
  local o = ISExtBuildingObject.new(self, player, recipe)
  setmetatable(o, self)
  self.__index = self
  return o
end



---
--- Extension of the ghost tile placement validation
--- @param square IsoGridSquare Clicked square object
--- @return boolean True, if building can be placed on current target square
---
function ISWaterCollector:isValid(square)
  -- base rules (valid, walkable, free space, reachable, solid ground, etc)
  if not ISExtBuildingObject.isValid(self, square) then return false end
  -- only on surface
  if not getSpecificPlayer(self.player):getZ() == 0 then return false end
  -- not under stairs
  if buildUtil.stairIsBlockingPlacement(square, true) then return false end
  -- tile must have any exterior, natural ground (except water)
  for i=1, square:getObjects():size() do
    local props = square:getProperties()
    if props:Is(IsoFlagType.water) then return false end
    local obj = square:getObjects():get(i-1)
    local textureName = obj:getTextureName() or 'occupied'
    if (not luautils.stringStarts(textureName, 'floors_exterior_natur')) and (not luautils.stringStarts(textureName, 'blends_natur')) then return false end
  end
  return true
end



---
--- Creates an on hover tooltip for water collectors with an amount bar if near enough
--- @param tooltipUI UIElement Tooltip factory
--- @param square IsoGridSquare Clicked square
---
local function DoSpecialTooltip(tooltipUI, square)
  local oPlayer = getSpecificPlayer(0)
  if not oPlayer or oPlayer:getZ() ~= square:getZ() or oPlayer:DistToSquared(square:getX() + 0.5, square:getY() + 0.5) > 4 then return end
  local oIsoObject = CWaterCollectorSystem.instance:getIsoObjectOnSquare(square)
  if not oIsoObject or not oIsoObject:getModData()['waterMax'] then return end
  local name = getText(oIsoObject:getTable().displayName or ISWaterCollector.defaults.displayName or ISExtBuildingObject.defaults.displayName)
  local font = UIFont.Small
  local fontHeight = getTextManager():getFontFromEnum(font):getLineHeight()
  tooltipUI:setHeight(6 + fontHeight + 6 + fontHeight + 12)
  local textX, textY = 12, 6 + fontHeight + 6
  local barWid, barHgt = 80, 4
  local barX = textX + getTextManager():MeasureStringX(font, getText('IGUI_invpanel_Remaining')) + 12
  local barY = textY + (fontHeight - barHgt) / 2 + 2
  tooltipUI:setWidth(barX + barWid + 12)
  tooltipUI:DrawTextureScaledColor(nil, 0, 0, tooltipUI:getWidth(), tooltipUI:getHeight(), 0, 0, 0, 0.75)
  tooltipUI:DrawTextCentre(getText(name), tooltipUI:getWidth() / 2, 6, 1, 1, 1, 1)
  tooltipUI:DrawText(getText('IGUI_invpanel_Remaining'), textX, textY, 1, 1, 1, 1)
  local percent = oIsoObject:getWaterAmount() / oIsoObject:getModData()['waterMax']
  if percent < 0 then percent = 0 end
  if percent > 1 then percent = 1 end
  local amountWidth = math.floor(barWid * percent)
  if percent > 0 then amountWidth = math.max(amountWidth, 1) end
  tooltipUI:DrawTextureScaledColor(nil, barX, barY, amountWidth, barHgt, 0, 0.6, 0, 0.7)
  tooltipUI:DrawTextureScaledColor(nil, barX + amountWidth, barY, barWid - amountWidth, barHgt, 0.15, 0.15, 0.15, 1)
end


Events.DoSpecialTooltip.Add(DoSpecialTooltip)



if isClient() or getCore():getGameMode() ~= 'Multiplayer' then

  require 'Map/CGlobalObjectSystem'

  --- @class CWaterCollectorSystem : CGlobalObjectSystem
  CWaterCollectorSystem = CGlobalObjectSystem:derive('CWaterCollectorSystem')


  ---
  --- Creates a new JS global object system on client-side
  --- @return  CGlobalObjectSystem New controller instance of this global object system type
  ---
  function CWaterCollectorSystem:new()
    return CGlobalObjectSystem.new(self, ISWaterCollector.defaults.isoData.isoName or ISExtBuildingObject.isoData.isoName)
  end



  ---
  --- Checks, if a given IsoObject is a water collector or not (client-side)
  --- @param isoObject userdata Target buildings JS object
  --- @return boolean True, if the object is linked to this system
  ---
  function CWaterCollectorSystem:isValidIsoObject(isoObject)
    if instanceof(isoObject, ISWaterCollector.defaults.isoData.isoType or ISExtBuildingObject.defaults.isoData.isoType) and #(ISWaterCollector.registeredRecipes) > 0 then
      for i=1, #(ISWaterCollector.registeredRecipes) do if ISWaterCollector.registeredRecipes[i] == isoObject:getName() then return true end end
    end
    return false
  end



  ---
  --- Creates a new global object controller on client-side
  --- @param globalObject CGlobalObject Target global object type
  --- @return CGlobalObject New instance of the target object type
  ---
  function CWaterCollectorSystem:newLuaObject(globalObject)
    return CWaterCollectorGlobalObject:new(self, globalObject)
  end


  CGlobalObjectSystem.RegisterSystemClass(CWaterCollectorSystem)




  require 'Map/CGlobalObject'

  --- @class CWaterCollectorGlobalObject : CGlobalObject
  CWaterCollectorGlobalObject = CGlobalObject:derive('CWaterCollectorGlobalObject')


  ---
  --- Creates a new global object on client-side
  --- @param luaSystem CGlobalObjectSystem Global object controller
  --- @param globalObject CGlobalObject Target global object
  --- @return CGlobalObject New instance of the target global object
  ---
  function CWaterCollectorGlobalObject:new(luaSystem, globalObject)
    return CGlobalObject.new(self, luaSystem, globalObject)
  end




  --- @class ISWaterCollectorMenu
  ISWaterCollectorMenu = {}


  ---
  --- Checks whether any right click hit a watercollector global object and if so,
  --- adds its context menu items (including debug options in debug mode)
  --- @param player int ID of the player who did the right click
  --- @param context ISContextMenu The current context menu object
  --- @param worldobjects table Global objects found on the clicked point
  --- @param test boolean Whether this call is a fetch only call for controller support
  ---
  function ISWaterCollectorMenu.OnFillWorldObjectContextMenu(player, context, worldobjects, test)
    if test and ISWorldObjectContextMenu.Test then return true end
    local found, isoObject = false
    for _,v in ipairs(worldobjects) do
      local square = v:getSquare()
      if square then
        for i=1, square:getObjects():size() do
          local v = square:getObjects():get(i-1)
          if CWaterCollectorSystem.instance:isValidIsoObject(v) then
            isoObject = v
            found = true
            break
          end
        end
      end
      if found then break end
    end
    if not found then return end
    local oPlayer = getSpecificPlayer(player)
    if isoObject and isoObject:getSquare():getBuilding() == oPlayer:getBuilding() then
      -- main option with tooltip
      local name = getText(isoObject:getTable().displayName or ISWaterCollector.defaults.displayName or ISExtBuildingObject.defaults.displayName)
      local subMenu = context:getNew(context)
      local subOption = context:addOptionOnTop(name)
      context:addSubMenu(subOption, subMenu)
      local tooltip = ISWorldObjectContextMenu.addToolTip()  -- make use of the vanilla tooltip pool
      tooltip:setName(name)
      local tx = getTextManager():MeasureStringX(tooltip.font, getText('ContextMenu_WaterName') .. ':') + 20
      tooltip.description = string.format('%s: <SETX:%d> %d / %d', getText('ContextMenu_WaterName'), tx, isoObject:getWaterAmount(), isoObject:getWaterMax())
      tooltip.maxLineWidth = 512
      subOption.toolTip = tooltip
      -- option "pour on ground"
      local optionPour = subMenu:addOption(getText('ContextMenu_Pour_on_Ground'), isoObject, ISWaterCollectorMenu.emptyWaterCollector, oPlayer)
      if not isoObject:hasWater() then
        optionPour.onSelect = nil
        optionPour.notAvailable = true
      end
      -- option "add water from item"
      local oInv = oPlayer:getInventory()
      rainCollectorBarrel = isoObject
      ISWorldObjectContextMenu.addWaterFromItem(test, context, worldobjects, oPlayer, oInv)
      local oldOption = context:getOptionFromName(getText('ContextMenu_AddWaterFromItem'))
      if oldOption ~= nil then
        -- xcopy the option to the correct index
        local newOption
        if context:getOptionFromName('ContextMenu_Drink') ~= nil then
          newOption = context:insertOptionBefore(getText('ContextMenu_Drink'), oldOption.name, oldOption.target, nil)
        else
          newOption = context:insertOptionBefore(getText('ContextMenu_Walk_to'), oldOption.name, oldOption.target, nil)
        end
        context:addSubMenu(newOption, context:getSubMenu(oldOption.subOption))
        context:removeLastOption()    -- the vanilla one was inserted at bottom
      end
      -- add debug options
      if isDebugEnabled() then
        -- if there are no other object debug options, the menu must be recreated
        local debugOption = context:getOptionFromName('Objects')
        if debugOption == nil then
          if context:getOptionFromName('UIs') then
            debugOption = context:insertOptionAfter('UIs', 'Objects', worldobjects)
            debugOption.iconTexture = getTexture('media/ui/BugIcon.png')
          else
            debugOption = context:addDebugOption('Objects', worldobjects)
          end
        end
        local debugSubMenu = ISContextMenu:getNew(context)
        context:addSubMenu( debugOption, debugSubMenu)
        debugSubMenu:addOption(name .. ': Zero Water', isoObject, ISWaterCollectorMenu.OnWaterCollectorZeroWater, oPlayer)
        debugSubMenu:addOption(name .. ': Set Water', isoObject, ISWaterCollectorMenu.OnWaterCollectorSetWater)
      end
    end
  end



  ---
  --- Removes all the water from the water collector
  --- @param obj CGlobalObject Target global object instance
  --- @param oPlayer IsoPlayer Acting player object instance
  ---
  function ISWaterCollectorMenu.emptyWaterCollector(obj, oPlayer)
    if luautils.walkAdj(oPlayer, obj:getSquare()) then
      ISTimedActionQueue.add(ISEmptyRainBarrelAction:new(oPlayer, obj))
    end
  end



  ---
  --- Outsourced part of OnWaterCollectorSetWater - executes the action
  --- @param _ any Target object (nil)
  --- @param button ISButton The clicked button
  --- @param obj CWaterCollectorGlobalObject The global object instance of interest
  ---
  local function OnWaterCollectorConfirm(_, button, obj)
    if button.internal == 'OK' then
      local playerObj = getSpecificPlayer(0)
      local text = button.parent.entry:getText()
      if tonumber(text) then
        local waterAmt = math.min(tonumber(text), obj:getWaterMax())
        waterAmt = math.max(waterAmt, 0.0)
        local args = { x = obj:getX(), y = obj:getY(), z = obj:getZ(), index = obj:getObjectIndex(), amount = waterAmt }
        sendClientCommand(playerObj, 'object', 'setWaterAmount', args)
      end
    end
  end



  ---
  --- Debug option which opens a UI to adjust the water amount of the water collector.
  --- Execution after confirmation is outsourced to a local function for performance
  --- @param obj CWaterCollectorGlobalObject Target global object instance
  ---
  function ISWaterCollectorMenu.OnWaterCollectorSetWater(obj)
    local luaObject = CWaterCollectorSystem.instance:getLuaObjectOnSquare(obj:getSquare())
    if not luaObject then return end
    local modal = ISTextBox:new(0, 0, 280, 180, string.format('Water (0-%d):', obj:getWaterMax()), tostring(obj:getWaterAmount()), nil, OnWaterCollectorConfirm, nil, obj)
    modal:initialise()
    modal:addToUIManager()
  end



  ---
  --- Debug option to set the water amount of the water collector to zero
  --- @param obj CWaterCollectorGlobalObject Target global object instance
  --- @param oPlayer IsoPlayer Acting player object instance
  ---
  function ISWaterCollectorMenu.OnWaterCollectorZeroWater(obj, oPlayer)
    local args = { x = obj:getX(), y = obj:getY(), z = obj:getZ(), index = obj:getObjectIndex(), amount = 0 }
    sendClientCommand(oPlayer, 'object', 'setWaterAmount', args)
  end


  Events.OnFillWorldObjectContextMenu.Add(ISWaterCollectorMenu.OnFillWorldObjectContextMenu)

end




if isServer() or getCore():getGameMode() ~= 'Multiplayer' then
  require 'Map/SGlobalObjectSystem'

  --- @class SWaterCollectorSystem : SGlobalObjectSystem
  SWaterCollectorSystem = SGlobalObjectSystem:derive('SWaterCollectorSystem')


  ---
  --- Creates a new JS global object system on server-side
  ---
  function SWaterCollectorSystem:new()
    return SGlobalObjectSystem.new(self, ISWaterCollector.defaults.isoData.isoName or ISExtBuilding.isoData.isoName)
  end



  ---
  --- Initialises the controller by defining which fields
  --- of the building object are relevant for the controller
  ---
  function SWaterCollectorSystem:initSystem()
    SGlobalObjectSystem.initSystem(self)
    self.system:setModDataKeys(ISWaterCollector.defaults.isoData.modDataKeys or ISExtBuildingObject.defaults.isoData.modDataKeys or {})
    self.system:setObjectModDataKeys(ISWaterCollector.defaults.isoData.objectModDataKeys or ISExtBuildingObject.defaults.isoData.objectModDataKeys or {})
    self:convertOldModData()
  end



  ---
  --- Creates a new global object controller (server-side)
  --- @param globalObject SWaterCollectorGlobalObject Target global object type
  ---
  function SWaterCollectorSystem:newLuaObject(globalObject)
    return SWaterCollectorGlobalObject:new(self, globalObject)
  end



  ---
  --- Checks, if a given IsoObject is a water collector or not (server-side)
  --- @param isoObject userdata Target buildings JS object
  --- @return boolean True, if the object is linked to this system
  ---
  function SWaterCollectorSystem:isValidIsoObject(isoObject)
    if instanceof(isoObject, ISWaterCollector.defaults.isoData.isoType or ISExtBuildingObject.defaults.isoData.isoType) and #(ISWaterCollector.registeredRecipes) > 0 then
      for i=1, #(ISWaterCollector.registeredRecipes) do
        if ISWaterCollector.registeredRecipes[i] == isoObject:getName() then return true end
      end
    end
    return false
  end



  -- TODO: Checken, ob es auch ohne die geht, immerhin wird es hierfür keine oldModData geben
  ---
  --- For backwards compatibility
  --- If the gos_xxx.bin file existed, don't touch GameTime modData in case mods are using it
  ---
  function SWaterCollectorSystem:convertOldModData()
    if self.system:loadedWorldVersion() ~= -1 then return end
  end



  ---
  --- Increases the water amount of the buildings JS objects
  ---
  function SWaterCollectorSystem:refill()
    for i=1, self:getLuaObjectCount() do
      local luaObject = self:getLuaObjectByIndex(i)
      if luaObject and luaObject.waterAmount < luaObject.waterMax then
        luaObject.waterAmount = math.min(luaObject.waterMax, luaObject.waterAmount + luaObject.addWaterPerAction)
        local isoObject = luaObject:getIsoObject()
        if isoObject then
          isoObject:setWaterAmount(luaObject.waterAmount)
          isoObject:transmitModData()
        end
      end
    end
  end



  ---
  --- Listener-Wrapper to invoke the refill method of each water collector instance
  ---
  local function EveryTenMinutes()
    SWaterCollectorSystem.instance:refill()
  end



  ---
  --- Writes the new water amount from global object to this lua object
  --- @param object IsoObject Target buildings JS object instance
  --- @param _ int Previous water amount
  ---
  local function OnWaterAmountChange(object, _)
    if not object then return end
    local luaObject = SWaterCollectorSystem.instance:getLuaObjectAt(object:getX(), object:getY(), object:getZ())
    if luaObject then luaObject.waterAmount = object:getWaterAmount() end
  end


  SGlobalObjectSystem.RegisterSystemClass(SWaterCollectorSystem)
  Events.EveryTenMinutes.Add(EveryTenMinutes)
  Events.OnWaterAmountChange.Add(OnWaterAmountChange)




  require 'Map/SGlobalObject'

  --- @class SWaterCollectorGlobalObject : SGlobalObject
  SWaterCollectorGlobalObject = SGlobalObject:derive('SWaterCollectorGlobalObject')


  ---
  --- Creates a new global object (server-side)
  --- @param luaSystem SGlobalObjectSystem Global object controller
  --- @param globalObject SGlobalObject Target global object
  ---
  function SWaterCollectorGlobalObject:new(luaSystem, globalObject)
    return SGlobalObject.new(self, luaSystem, globalObject)
  end



  ---
  --- Initialises a new global object
  ---
  function SWaterCollectorGlobalObject:initNew()
    self.waterAmount = ISWaterCollector.defaults.properties.waterAmount
    self.waterMax = ISWaterCollector.defaults.properties.waterMax
    self.addWaterPerAction = ISWaterCollector.defaults.properties.addWaterPerAction
  end



  ---
  --- Transfers the current vales from the buildings JS object to the global object
  --- @param isoObject IsoObject Target buildings JS object instance
  ---
  function SWaterCollectorGlobalObject:stateFromIsoObject(isoObject)
    self.waterAmount = isoObject:getWaterAmount()
    self.waterMax = isoObject:getModData().waterMax
    self.addWaterPerAction = isoObject:getModData().addWaterPerAction
    isoObject:getModData().waterMax = self.waterMax
    isoObject:getModData().addWaterPerAction = self.addWaterPerAction
    isoObject:transmitModData()
  end



  ---
  --- Transfers the current values from the global object to the buildings JS object
  --- @param isoObject IsoObject Target buildings JS object instance
  ---
  function SWaterCollectorGlobalObject:stateToIsoObject(isoObject)
    if not self.waterAmount then self.waterAmount = ISWaterCollector.defaults.properties.waterAmount end
    if not self.waterMax then self.waterMax = ISWaterCollector.defaults.properties.waterMax end
    if not self.addWaterPerAction then self.addWaterPerAction = ISWaterCollector.defaults.properties.addWaterPerAction end
    isoObject:setWaterAmount(self.waterAmount)
    isoObject:getModData().waterMax = self.waterMax
    isoObject:getModData().addWaterPerAction = self.addWaterPerAction
    isoObject:transmitModData()
  end

end



---
--- Initialises the global objects of all existing building JS objects while loading the map
--- @param isoObject IsoObject Target building object JS object instance
---
local function loadGlobalObject(isoObject)
  if not instanceof(isoObject, ISWaterCollector.defaults.isoData.isoType or ISExtBuildingObject.defaults.isoData.isoType) then return end
  SWaterCollectorSystem.instance:loadIsoObject(isoObject)
end



---
--- Checks the recipe definitions for any recipe which uses ISWaterCollector as targetClass
--- and adds it to the registry, so we can differ between those recipes in the system classes
--- and gather the overloaded sprite and name.
---
local function registerWaterCollectors(recipes)
  for _,v in pairs(recipes) do
    if v ~= nil then
      if v.targetClass == nil and type(v) == 'table' then
        registerWaterCollectors(v)
      elseif v.targetClass == 'ISWaterCollector' then
        if isServer() or getCore():getGameMode() ~= 'Multiplayer' then
          local sprite = v.sprites.sprite or ISWaterCollector.defaults.sprites.sprite or ISExtBuildingObject.defaults.sprites.sprite
          local priority = v.isoData.mapObjectPriority or ISWaterCollector.defaults.isoData.mapObjectPriority or ISExtBuildingObject.defaults.isoData.mapObjectPriority
          MapObjects.OnLoadWithSprite(sprite, loadGlobalObject, priority)
        end
        table.insert(ISWaterCollector.registeredRecipes, v.isoData.isoName or ISWaterCollector.defaults.isoData.isoName or ISExtBuildingObject.defaults.isoData.isoName)
      end
    end
  end
end
registerWaterCollectors(ExtBuildingContextMenu.BuildingRecipes)

 

 

 

The ISExtBuildingObject is just the base class, where my ISBuildings will get their standard functions/methods and adding tooltips etc.

Here's the part where the default values are set:


-- Generic defaults (those are absolutely mandatory for initialisation)
ISExtBuildingObject.defaults = {
  displayName = 'Unnamed',
  buildTime = 200,
  baseHealth = 200,
  mainMaterial = 'wood',    -- decides which skill lvl determines the extra health (allowed is "wood", "metal", "stone" or "glass")
  hasSpecialTooltip = false,
  breakSound = 'BreakObject',
  craftingBank = 'BuildingGeneric',    -- used sound file while performing the build action (it will alternate with tool sounds of the first two tool requirements defined as modData "keep:" entry. It can be used for regular construction sounds as well as "real" crafting bank sounds.
  sprites = { sprite = 'invisible_01_0' },
  isoData = {
    isoName = 'unnamed',    -- defines the name of the global map object instance, if any. If a global object has several subtypes (like in "watercollector"), this might be used to differ between those subtypes (like "waterwell", "rainbarrel"). If there are no subtypes, then it can simply use the same value as its systemName (name of the associated global object system, which must be unique)
    isoType = 'IsoThumpable',
    mapObjectPriority = 7
  },
  modData = {}
}

 

 

 

And one useage would the my water well implementation, which defines a concrete WaterCollector global object:

if not ExtBuildingContextMenu then ExtBuildingContextMenu = {} end
if not ExtBuildingContextMenu.call then
  ExtBuildingContextMenu.call = function(callback, ...) return callback(...) end
  setmetatable(ExtBuildingContextMenu, {__call = ExtBuildingContextMenu.call})
end


---
--- This table will be used in ExtBuilding_ContextMenu.lua
--- to create all submenus and their recipes of the new build menu
---
ExtBuildingContextMenu.BuildingRecipes = {
  ContextMenu_ExtBuilding_Cat__Technology = {
    {
      targetClass = 'ISWaterCollector',
      displayName = 'ContextMenu_ExtBuilding_Obj__WaterWell',
      tooltipDesc = 'Tooltip_ExtBuilding__WaterWell',
      buildTime = 700,
      baseHealth = 600,
      mainMaterial = 'stone',
      completionSound = 'BuildFenceCairn',
      isoData = { isoName = 'waterwell' },
      properties = { waterAmount = 50, waterMax = 5000, addWaterPerAction = 5, craftingBank = 'Shoveling' },
      sprites = { sprite = 'garteneden_tech_01_0', northSprite = 'garteneden_tech_01_1' },
      modData = {
        ['keep:' .. utils.concatItemTypes({'Hammer'})] = 'Base.Hammer',
        ['keep:' .. utils.concatItemTypes({'Saw'})] = 'Base.Saw',
        ['keep:' .. utils.concatItemTypes({'DigGrave'})] = 'Base.Shovel',
        ['need:Base.Rope'] = 5,
        ['need:Base.Plank'] = 4,
        ['need:Base.Nails'] = 10,
        ['need:Base.Stone'] = 20,
        ['use:Base.Gravelbag'] = 8,
        ['need:Base.BucketEmpty'] = 1,
        ['requires:Woodwork'] = 7,
        ['requires:Fitness'] = 5,
        ['xp:Woodwork'] = 5,
        ['xp:Fitness'] = 5
      },
    },
  },
}

 

 

Link to comment
Share on other sites

  • 10 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...