Power Armor Enhancement

Place to get help with not working mods / modding interface.
User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Power Armor Enhancement

Post by Ranakastrasz »

I am trying to write a mod to enhance powered armor in the game.
To do so, I need to solve several problems.

~In order to allow Power Conduit Modules, which transfer power from your standard energy network into your modular armor ~
I need to figure out how to, on game tick, determine if a player is inside a power field, and if so, figure out how to drain xx amount of power from that field. That is, If the player is currently near a power pole, I want to be able to have the player act as a power consumer.
I am unable to locate any existing method to drain power, and am unsure how you might create a dummy energy consumer.



~In order to support coal fired generators, or similar~
I need to figure out how to feed powered armor Fuel-type items. The simplest solution would be simply to search for burnable items in the inventory, but that is not ideal. I would prefer to be able to either flag items somehow, or add a new GUI slot for fuel items.

Worst case, I create a custom "Armor Fuel" Item which you can craft from various burnable stuff.

I have already figured out how to manipulate power levels of modules, so that part is easy in comparison.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

johanwanderer
Fast Inserter
Fast Inserter
Posts: 157
Joined: Fri Jun 26, 2015 11:13 pm

Re: Power Armor Enhancement

Post by johanwanderer »

The only thing I can think of is to attach an accumulator-type object to the player. It will drain energy and allow you to retrieve that energy for other purposes.

JamesOFarrell
Filter Inserter
Filter Inserter
Posts: 402
Joined: Fri May 23, 2014 8:54 am
Contact:

Re: Power Armor Enhancement

Post by JamesOFarrell »

johanwanderer wrote:The only thing I can think of is to attach an accumulator-type object to the player. It will drain energy and allow you to retrieve that energy for other purposes.
This is the answer. Teleport an accumulator around with the player that has no sprite and a ghost collision mask. Keep the energy full and when you want to test or draw power drop the amount of energy and the accumulator will draw power naturally. For the item slot, look at the Force Field mod, Rseding91 wrote an example there for modules that you could easily adapt for coal and burnables.

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

That should solve both problems.

The interface is complicated, but the example should be enough.

Thanks!
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

JamesOFarrell wrote:
johanwanderer wrote:The only thing I can think of is to attach an accumulator-type object to the player. It will drain energy and allow you to retrieve that energy for other purposes.
This is the answer. Teleport an accumulator around with the player that has no sprite and a ghost collision mask. Keep the energy full and when you want to test or draw power drop the amount of energy and the accumulator will draw power naturally. For the item slot, look at the Force Field mod, Rseding91 wrote an example there for modules that you could easily adapt for coal and burnables.
I am unable to figure out how to make the accumulator entirely invulnerable. Currently, I can shoot it, which, presumably, means that even if i set it's selection size to zero, mobs, or splash damage will still damage it. It needs to be entirely inviolate.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

johanwanderer
Fast Inserter
Fast Inserter
Posts: 157
Joined: Fri Jun 26, 2015 11:13 pm

Re: Power Armor Enhancement

Post by johanwanderer »

Ranakastrasz wrote:I am unable to figure out how to make the accumulator entirely invulnerable. Currently, I can shoot it, which, presumably, means that even if i set it's selection size to zero, mobs, or splash damage will still damage it. It needs to be entirely inviolate.
Maybe setting this to false?
https://forums.factorio.com/wiki/inde ... structible

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

That really should have worked.
However, it doesn't seem to do so.
Setting health to zero prevents targeting with a gun, but a grenade still kills it.

Edit: Oh wait, Thats a command, not a prototype property.
Edit2: No, thats a read only property. I have no idea how to set it.
edit3: Ah, I spelled it wrong. Lol. fixed now.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

This is the current script.
Appears to work perfectly for single player. However, multplayer wont work correctly.

The test items dont show up.
Also, the second player has either the original accumulator or both of them following them, and after leaving it is still bound to the second player.

--Snipped--
Last edited by Ranakastrasz on Thu Jul 23, 2015 11:47 pm, edited 1 time in total.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

johanwanderer
Fast Inserter
Fast Inserter
Posts: 157
Joined: Fri Jun 26, 2015 11:13 pm

Re: Power Armor Enhancement

Post by johanwanderer »

I only looked over it briefly (and I don't know what is wrong, besides it's not working), but it seems that you're using the same table to store data for all players, i.e.

Code: Select all

glob.modularArmor.player.unit. ...
I would recommend using an array for the players, i.e.

Code: Select all

local player_thing = glob.modularArmor.players[i]; -- not sure what to call it.
if(nil == player_thing)
then
  player_thing = {}
  glob.modularArmor.players[i] = player_thing;
  -- more inits here
end;
-- now the player_thing table contains per-player stuff, so
player_thing.unit.teleport(player.character.position);

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

That helped. At least part of the problem was that I was treating "player" as the iterator when "i" was the actual iterator.

The intention was that the iterator would act as the index for the table.

Edit:I cant do multiplayer debug messages. It crashes the game as stated here https://forums.factorio.com/forum/vie ... =41&t=5638
Print standalone does nothing, and i have no idea how to open the console.
Using that supposed solution doesn't work either.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

Debug messages solved via rk84's test mode mod's debug message function. Not entirely sure how, but it works. Also the loop actually works in multiplayer, unlike my old one.
It also seems to work perfectly in multiplayer now.
However, it causes desyncs for some of the ticks while running. Unsure why. Its not consistent either.

In Warcraft 3, I knew exactly what kind of things caused desyncs. modifying any game altering property of any handle or creating a handle inside a GetLocalPlayer() = thisPlayer block would always cause it. However, no GetLocalPlayer equivelent exists, and I'm not doing anythng specific to "player" so far as I can see. And I am pretty sure that you cant use "player" in multiplayer anyway without an error.

--Snipped--
Last edited by Ranakastrasz on Thu Jul 23, 2015 11:47 pm, edited 1 time in total.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

Rseding91
Factorio Staff
Factorio Staff
Posts: 13266
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: Power Armor Enhancement

Post by Rseding91 »

You get desyncs because you're setting glob.ticking = 0 on_load and so it's not consistent between save/load and player's joining triggers save -> transfer map -> load on other peer.
If you want to get ahold of me I'm almost always on Discord.

Rseding91
Factorio Staff
Factorio Staff
Posts: 13266
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: Power Armor Enhancement

Post by Rseding91 »

Also you can use the following if you want to make debug mod interfaces that print to the calling player:

Code: Select all

remote.add_interface("ModularArmor", {
    print = function(text)
        game.local_player.print(text)
    end
})
This will however only work if called from a player through the console. If another mod calls the interface or if you call it from your own mod it won't work and will thrown an error.
If you want to get ahold of me I'm almost always on Discord.

johanwanderer
Fast Inserter
Fast Inserter
Posts: 157
Joined: Fri Jun 26, 2015 11:13 pm

Re: Power Armor Enhancement

Post by johanwanderer »

Rseding91 wrote:Also you can use the following if you want to make debug mod interfaces that print to the calling player:

Code: Select all

remote.add_interface("ModularArmor", {
    print = function(text)
        game.local_player.print(text)
    end
})
This will however only work if called from a player through the console. If another mod calls the interface or if you call it from your own mod it won't work and will thrown an error.
I would just loop through all the players in the game and print through any that is valid :)

Code: Select all

-----------------------------------------------------------
-- utility functions
-----------------------------------------------------------
local function ldLC_Debug(s)
  for index, player in pairs(game.players) do
    if(player.valid) then
      player.print(s);
    end;
  end
end

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

Rseding91 wrote:You get desyncs because you're setting glob.ticking = 0 on_load and so it's not consistent between save/load and player's joining triggers save -> transfer map -> load on other peer.
Ah. That explains it. That would be inconsistant.

Setting it in the upper global area instead would work, correct?

~~~~
Already got a functional debug message method.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

Rseding91
Factorio Staff
Factorio Staff
Posts: 13266
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: Power Armor Enhancement

Post by Rseding91 »

Ranakastrasz wrote:
Rseding91 wrote:You get desyncs because you're setting glob.ticking = 0 on_load and so it's not consistent between save/load and player's joining triggers save -> transfer map -> load on other peer.
Ah. That explains it. That would be inconsistant.

Setting it in the upper global area instead would work, correct?

~~~~
Already got a functional debug message method.
No, that's also run when you load the game.

You need to set it in on_init and on_load but only set it if the glob.value is nil (so the first ever time the mod is loaded on that map). After the first time it will never be nil again and will get saved/loaded between connections and restarts.
If you want to get ahold of me I'm almost always on Discord.

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

That is useful information.
I am now trying to make the accumulator invisible. However, doing so is surprisingly difficult. I now have a rather less than helpful crash message, of "An unexpected error has occured"

Code: Select all

data:extend(
{
    {
        type = "accumulator",
        name = "modular-accumulator-dummy",
        icon = "__base__/graphics/icons/basic-accumulator.png",
        flags = {"placeable-off-grid", "not-on-map"},
        -- minable = {hardness = 0.2, mining_time = 0.5, result = "basic-accumulator"},
        max_health = 0,
        -- corpse = "medium-remnants",
        collision_box = {{0, 0}, {0, 0}},
        --selection_box = {{-1, -1}, {1, 1}},
        collision_mask = {"ghost-layer"},
        energy_source =
        {
            type = "electric",
            buffer_capacity = "5MJ",
            usage_priority = "primary-input",
            input_flow_limit = "5MW",
            output_flow_limit = "0kW"
        },
        picture = {
            filename = "",
            priority = "low",
            width = 0,
            height = 0,
            shift = {0.0, 0.0}
        },
        charge_animation =
        {
            filename = "",
            width = 0,
            height = 0,
            line_length = 0,
            frame_count = 1,
            shift = {0, 0},
            animation_speed = 0
        },
        charge_cooldown = 0,
        charge_light = {intensity = 0, size = 0},
        discharge_animation =
        {
            filename = "",
            width = 0,
            height = 0,
            line_length = 0,
            frame_count = 1,
            shift = {0, -0},
            animation_speed = 0
        },
        discharge_cooldown = 0,
        discharge_light = {intensity = 0.0, size = 0},
        working_sound =
        {
            sound =
            {
                filename = "",
                volume = 0
            },
            idle_sound =
            {
                filename = "",
                volume = 0
            },
            max_sounds_per_type = 0
        },
    }
})

My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

That problem is solved. However, if I save and load the game, the global.loaded variable is still nil. This contradicts my expectations, as I expected it to remain true after loading, based on information in the wiki.

Also, I am not certain I understand multiplayer instancing correctly. When a new player joins, will they get a copy of global?

Code: Select all

require "defines"

--[[
    This module handles the gameloop alteration, allowing conduit modules in power armor grid to tranfer energy between your power grid and your armor.

]]--

--[[
Charging Module Icon
Artist: Treetog ArtWork (Available for custom work)
Iconset: Junior Icons (146 icons)
License: Free for personal desktop use only.
Commercial usage: Not allowed
]]--

--[[          Gen , Size, Gen/size
    Conduit :  20kw,  1x1, 20kw  
    Solar 1 :  15kw,  1x1, 15kw  
    Solar 2 : 200kw,  3x3, 22.2kw
    Burner  : 200kw,  2x2, 50kw
    Fusion  :   1Mw,  4x4, 62.5kw
    
    
           Energy , duration
    Wood  :  4 MJ ,  20
    Coal  :  8 MJ ,  40
    Fuel  : 25 MJ , 125

    Fusion: 112.5 MJ, 120
    
    
]]--

--local loaded
local tickRate = 1
local ticksPerSecond = 60
local tickRatio = tickRate/ticksPerSecond -- To avoid division operations in a loop
local conversionRatio = 1.0                         -- Internal armor has values 1/1000th of the normal power network. As of 0.12, it is now a ratio of 10
local conversionAntiRatio = 1.0/conversionRatio        -- avoid division
local accumulatorEnergyCap = 5 * 1000 * 1000       -- 100 slots, 20/slot + 80, 2080 transfer rate, 5mil should be sufficient.. This must match accumulator dummy power capacity.

local ArmorTransferRatePerGridSize = 800
local TransferRatePerConduit = 20*1000
local Debug = true

--[[local fuelValues =
{
    wood =  4*1000*1000,
    Coal =  8*1000*1000,
    Fuel = 25*1000*1000
    
}]]--

--[[remote.addinterface("ModularArmor", {
    reset = function()
		global.ticking = nil
        
        --global.modularArmor = {}
    end
})]]--


function verifySettings()
	if tickRate < 0 then
		tickRate = 0
		throwError("Tick rate must be >= 0.")
	end
end

function onload()  -- this function 
        globalPrint("onLoad")
	if global.loaded == nil then
		global.loaded = true
        globalPrint("loaded")
        if global.modularArmor == nil then
            global.modularArmor = {}
        end
        if global.ticking == nil then
            global.ticking = 0
            game.on_event(defines.events.on_tick, ticker)
        end
        
        
		verifySettings()
	end
end

game.on_load(onload)
game.on_init(onload)


function globalPrint(msg)
  local players = game.players
  for i=1, #players do
    players[i].print(msg)
  end
end


function tableIsEmpty(t)
	if t then
		for k in pairs(t) do
			return false
		end
	end
	return true
end

function ticker() -- run once per tickRate number of gameticks.
	if global.ticking == 0 then
		global.ticking = tickRate - 1
		tick()
	else
		global.ticking = global.ticking - 1
	end
end

function tick()
	local shouldKeepTicking
    local thisPlayer = nil
    local players = game.players
    local surface = game.surfaces['nauvis']
        globalPrint("tick")
    shouldKeepTicking = true -- Due to lack of any alternate method of detecting player's armor state, we have to always tick.

    for i=1, #players do
        thisPlayer = players[i]
        --game.getplayer(1).print(i..' '..player)
        local modularArmor = global.modularArmor[i]
        if modularArmor == nil then
            
            modularArmor = {} -- ensure player has data attached
            global.modularArmor[i] = modularArmor
            modularArmor.unit = surface.create_entity{name = "modular-accumulator-dummy", position = thisPlayer.position, force=game.forces.player}
                -- attach power drain dummy.
            modularArmor.unit.destructible = false -- Make dummy invulnerable.
            modularArmor.unit.energy = accumulatorEnergyCap -- initialize energy levels
            modularArmor.previousEnergy = accumulatorEnergyCap -- and previous energy level from last tick

            if Debug == true then
            
                game.always_day=true -- test mode stuff
                thisPlayer.insert{name="basic-grenade",count=5}
                thisPlayer.insert{name="energy-shield-equipment",count=20}
                thisPlayer.insert{name="energy-shield-mk2-equipment",count=10}
                --thisPlayer.insert{name="energy-shield-module-equipment",count=10}
                --thisPlayer.insert{name="energy-shield-core-equipment",count=5}
                
                thisPlayer.insert{name="power-conduit-equipment",count=40}
                --thisPlayer.insert{name="power-conduit-module-equipment",count=10}
                --thisPlayer.insert{name="power-conduit-core-equipment",count=5}

                thisPlayer.insert{name="solar-panel-equipment",count=20}
                thisPlayer.insert{name="solar-panel-equipment-mk2",count=10}
                thisPlayer.insert{name="basic-actuator-equipment",count=20}

                thisPlayer.insert{name="battery-equipment",count=5}
                thisPlayer.insert{name="battery-mk2-equipment",count=5}
                thisPlayer.insert{name="basic-modular-armor",count=1}
                thisPlayer.insert{name="power-armor",count=1}
                thisPlayer.insert{name="power-armor-mk2",count=1}
                
                thisPlayer.insert{name="small-electric-pole",count=200}
                thisPlayer.insert{name="solar-panel",count=200}
            else
            end
            
        else
            modularArmor.unit.teleport(thisPlayer.character.position) -- Ensure that the power drain dummy is always at the player's position.
        end
        
        
        local armor = thisPlayer.get_inventory(defines.inventory.player_armor)[1] -- Check for armour presence.
        
        if armor.valid_for_read then
            
            local grid = armor.grid
            
            if grid ~= nil then -- Check for grid existance.
            
                 local transferRate = 0 -- Rate of transfer from external network to armor.
            
                transferRate = transferRate + ArmorTransferRatePerGridSize*grid.width*grid.height
            
                local energy = 0 -- Total energy and energy capacity
                local energyCap = 0
               -- local shieldHealth = 0 -- Total shield and shield capacity for auto-balancing.
                --local shieldCap =0
                for i,equipment in ipairs(grid.equipment) do -- Loop through all equipment.
                    if equipment.max_energy ~= 0 then
                        energy = energy + equipment.energy -- If it has energy, add values to total value.
                        energyCap = energyCap + equipment.max_energy
                    else
                    
                    end
                    --if equipment.maxshield ~= 0 then
                    --    shieldHealth = shieldHealth + equipment.shield -- Same with shield.
                    --    shieldCap = shieldCap + equipment.maxshield
                    --else
                        
                    if equipment.name == "power-conduit-equipment" then -- Also count each conduit module.
                        transferRate = transferRate + TransferRatePerConduit
                    end
                end
                
                
                local transferRate = transferRate * tickRatio -- transfer rate as calculated was per second, but this runs every so many game ticks.
                local transferRate = math.min(transferRate,energyCap-energy) -- We cant transfer energy without space to put it into
                
                local accumulatorEnergy = modularArmor.unit.energy - modularArmor.previousEnergy -- How much energy was accumulated.

                local energyToTransfer = math.min(transferRate,accumulatorEnergy*conversionAntiRatio) -- Accumulated energy, or transfer wanted, whichever is lower.
                
                
                
                for i,equipment in ipairs(grid.equipment) do 
                    if equipment.max_energy ~= 0 then
                        local difference = equipment.max_energy - equipment.energy
                        if energyToTransfer > difference then
                            energyToTransfer = energyToTransfer - difference
                            equipment.energy = equipment.max_energy
                        else
                            equipment.energy = equipment.energy + energyToTransfer
                            energyToTransfer = 0
                            break
                        end
                        
                    else
                        
                    end
                end
                
                energyToTransfer = energyToTransfer * conversionRatio
                
                
                modularArmor.unit.energy = accumulatorEnergyCap - transferRate*conversionRatio
                modularArmor.previousEnergy = modularArmor.unit.energy - energyToTransfer*conversionRatio -- The additional accumulated energy over
                
                
                
            else
            end
        else
        end
    end
    
	if not shouldKeepTicking then
		global.ticking = nil
		game.onevent(defines.events.ontick, nil)
	end
end

function activateTicker()
	if not global.ticking then
		global.ticking = tickRate
		game.onevent(defines.events.ontick, ticker)
	end
end
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

orzelek
Smart Inserter
Smart Inserter
Posts: 3911
Joined: Fri Apr 03, 2015 10:20 am
Contact:

Re: Power Armor Enhancement

Post by orzelek »

I'm pretty sure that global is only one.

You might need to store the data per player index or something along those lines.

User avatar
Ranakastrasz
Smart Inserter
Smart Inserter
Posts: 2133
Joined: Thu Jun 12, 2014 3:05 am
Contact:

Re: Power Armor Enhancement

Post by Ranakastrasz »

orzelek wrote:I'm pretty sure that global is only one.

You might need to store the data per player index or something along those lines.
The problem being that the gameloop runs every some number of ticks, which if it isn't the same for all players, it will desync.

Also, later, I will be storing fuel items in the armor for a burner generator, which even more obviously must be the same for all players.
My Mods:
Modular Armor Revamp - V16
Large Chests - V16
Agent Orange - V16
Flare - V16
Easy Refineries - V16

Post Reply

Return to “Modding help”