I really need help with my mod

Place to get help with not working mods / modding interface.
Post Reply
Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

I really need help with my mod

Post by Smitherino »

I have been creating a mod that overhauls weapons in factorio and I really want to impliment a reload system
the code would go something like this

control.lua
script.on_event({defines.events.on_tick},
function (e)
for index,player in pairs(game.connected_players)
if player.character and [mod ammo in ultility slot] and [mod ammo has 1 bullet left in the current magazine]
then [change the weapons sound to reload.ogg, fire rate to 1/2.8 seconds, remove movemend penalty]
else [do nothing]
end
end
end
end
)

only problem is, i can't find the value anywhere in the game which defines how many bullets are left in a magazine, nor how to change weapon statistics from within a script.
would REALLY appreciate any help!!
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: I really need help with my mod

Post by eradicator »

Smitherino wrote: only problem is, i can't find the value anywhere in the game which defines how many bullets are left in a magazine
Thats easy to answer:
LuaItemStack.ammo
Smitherino wrote:nor how to change weapon statistics from within a script.
Thats's easy to answer too:
You can't.
Not on a per ItemStack basis. Only the whole force can have a bonus (and probably not a negative one). You could try defining a slower variant for every gun prototype and swapping them out on the fly :P.

Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

Re: I really need help with my mod

Post by Smitherino »

thanks for the help... I have found a potential way to implement reloading for factorio weapons, but I am having difficulty coding what I am thinking of.
I now know that .ammo can be used to check ammo remaining in a magazine, but I don't know where to use it in the code.. is it ("firearm-magazine").ammo = 1, or just ammo = 1? I don't know.
I also do know what scripts can be activated every tick (so it should immediately trigger when conditions are met), so my idea was to check for the ammo = 1, and the weapon = pistol... then do this
player.get_inventory(defines.inventory.player_guns).remove{name="pistol", count=1}
player.get_inventory(defines.inventory.player_guns).insert{name="dummy-pistol", count=1}

this replaces the pistol, with the one which will fire the last bullet of the magazine and have the longer cool down and reload sound.
the other problem i encountered was how to switch back to the original weapon. I could go .ammo ~=1, but that would immediately switch the dummy pistol back to the regular one.
I need to find the variable in the game which determines whether a gun is on cool-down or not. If i can put these into an if statement, the i can go .ammo ~= 1 and cooldown = false and weapon = dummy-pistol then
player.get_inventory(defines.inventory.player_guns).remove{name="dummy-pistol", count=1}
player.get_inventory(defines.inventory.player_guns).insert{name="pistol", count=1}

this should seamlessly reload weapons in factorio.. i just don't know how to script it :(
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: I really need help with my mod

Post by eradicator »

Looked at http://lua-api.factorio.com/latest/Classes.html (btw best page to just use ctrl-f on) and don't see anything immediately useful to tell the current cooldown process. You can read the cooldown value from the prototype though. As you're already running on_tick you can just use that and implement the cooldown yourself. I.e. when you swap the weapon store event.tick+cooldown in global, then each tick you'll have to compare that to the current tick. Or maybe wait for the in-combat animation to stop or something, that might make it look somewhat like reloading too.

Btw, weapons are stackable, so count=1 won't always work. And you probably also want to look at which weapon slot is selected to the player can't reload a weapon he's not currently using? Also rockets never go over 1 ammo, the prototype doesn't even define magazine_size so it might not even be valid to try to read that (special cases here ye come :P)

Oh, and it's ItemStack.ammo. So you need to get the item stack. For the gun inventory that's probably something like:

Code: Select all

local inventory = player.get_inventory(defines.inventory.player_guns)
for i=1,#inventory do
  local stack = inventory[i] -- 1,2,3 as the guns inventory has 3 slots. Unless someone changes that..
  if stack.ammo == 1 then  dostuff() end
  end

Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

Re: I really need help with my mod

Post by Smitherino »

alright, thanks to you I have managed to create a reload script:

script.on_event({defines.events.on_tick},
function (e)
--if e.tick % 60 == 0 then
for index,player in pairs(game.connected_players) do
local inventory = player.get_inventory(defines.inventory.player_ammo)
if player.character and player.get_inventory(defines.inventory.player_ammo).get_item_count("firearm-magazine") >= 1 then
local stack = inventory[1 or 2 or 3]
if stack.ammo == 1 and player.get_inventory(defines.inventory.player_guns).get_item_count("pistol") >= 1 then
player.get_inventory(defines.inventory.player_guns).remove{name="pistol", count=1}
player.get_inventory(defines.inventory.player_guns).insert{name="submachine-gun", count=1}
else
end
if stack.ammo ~= 1 and player.get_inventory(defines.inventory.player_guns).get_item_count("submachine-gun") >= 1 then
player.get_inventory(defines.inventory.player_guns).remove{name="submachine-gun", count=1}
player.get_inventory(defines.inventory.player_guns).insert{name="pistol", count=1}
else
end
end
end
end
)

the only problem is if i take ammo out of ammo slot 1 and put it in 2 or 3, it crashes despite: local stack = inventory[1 or 2 or 3]
EDIT: this script doesn't actually reload, it just swaps out weapons (which can be replaced with a dummy reload weapon easily)
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

BenSeidel
Filter Inserter
Filter Inserter
Posts: 584
Joined: Tue Jun 28, 2016 1:44 am
Contact:

Re: I really need help with my mod

Post by BenSeidel »

Instead of using script.on_event({defines.events.on_tick}, try script.on_event({defines.on_player_ammo_inventory_changed}.
It will tell you what player has had their inventory changed in some way.

As for the crash, I don't think that
local stack = inventory[1 or 2 or 3]
does what you think it does. (I think 1 or 2 or 3 -> 1, because 0 is false and 1 is true, so true or true or true -> true, and true is converted to 1)
Try looping through inventory[], so you can check each slot.

Another thing that might be a cool mechanic is if the reload is done using a craft. If you can insert a craft at the head of the player crafting queue (Unsure if this is possible without clearing & re-creating the queue first), when a magazine is empty, craft the reload. When the craft has been completed put the ammo into the players ammo slot. It's a lot more work, but it would be cool (you could also bind the reload to a key so that the user has to press it like in many FPS).

Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

Re: I really need help with my mod

Post by Smitherino »

thanks for the suggestions,

try script.on_event({defines.on_player_ammo_inventory_changed} doesn't seem to register when ammo is drained from a magazine, and thus only triggers when the amount on magazines in the inventory has changed. Since the scrip im using "preemptively" checks how many bullets are in the magazine and swaps out weapons before the actual number of mags have changed, this trigger doesn't work.

As for the crafting, I have nooo idea how to do that, as before a few weeks ago, my modding expertise was limited to copying base game code and changing variables :), I work more as a 3d modeller / 3d artist, so making art for the mod was fairly easy for me.

Due to my limited understanding of lua, and factorio's syntax, I also don't know how to loop through ammo inventory slots. leaving it as "inventory[]" just brings a syntax error (go figure) but i am not sure how to check all 3 ammo slots if the "or" statement doesn't do anything.

I have done some more work on the script, it now uses modded items, and works perfectly (eg. an smg with 30 rounds. you fire 29, on the last round the weapon is swapped to smg-dummy. this weapon fires the last bullet, but has a much longer cool-down with a reload sound. firing the last bullet brings a new, 30rnd mag to the top of the stack making the dummy weapon swap back out the to real thing.. fortunately the cool-down and sound played continues even when the weapon is remove from the inventory, creating a seamless reload script. the reload is visualized on screen by the longer cool down of the weapon, so I don't think that crafting (while a cool idea) is necessarily worth implementing considering how much time it would take a novice coder like me.

thanks in advance.
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: I really need help with my mod

Post by eradicator »

Oh, totally forgot about on_ammo_inventory_changed. Might still be worth a consideration to just switch the logic to "before the first bullet is fired" if on_tick ever costs too much performance, but i'd say that's optimization that can be done in a later step.

My post above actually shows how to loop through an inventory, you just get the size of the inventory "#inventory" and then loop through each slot with an incrementing index. inventory[1or2or3] or similar can never work becaues item stacks always exist even if they're empty. This gives you a nice opportunity to just cache the gun/inventory slots though (presumably in on_player_created, and on_configuration_changed).

Code: Select all

global[player_index] = {guns = get_gun_inventory(), ammo = get_ammo_inventory()}
Now you can just access each players gun and ammo inventories without having to re-get them all the time, this also gives you an opportunity to synchronize the slots, as currently as far as i can see you're just searching for "any slot with ammo/gun" which means that if i have a ammo=1 magazine in slot 2 but the weapon in slot 1 stuff breaks. But with the cache (well, without too anyway, technically :P) you can just:

Code: Select all

for index,player in pairs(game.players) do
  for slot=1,3 do
    local gun = global[index].guns[slot]
    local mag = global[index].ammo[slot]
    if not (gun.valid_for_read and mag.valid_for_read) then break end --empty slots are not valid and would throw an error when trying to read .name or .ammmo or anything
    if gun.name == 'pistol' and mag.name == 'firearm-magazine' and mag.ammo == 1 then
      --dostuff
      end
    end
  end
And magically check only the ammo slot that's actually associated with the pistol.

The crafting thing isn't actually difficult, but as noted you can only add to the end of the queue, and storing the queue would actually be annoying (and unexpected to the player), so i can't recommend that.

Oh, hey. You're a 3D artiest.. do you do models on request :P?

Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

Re: I really need help with my mod

Post by Smitherino »

thanks for the help. I generally don't do models on request, but if it's something simple that i could knock off in an hour or two, I don't see why I cant.. what did you have in mind?
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: I really need help with my mod

Post by eradicator »

Smitherino wrote:thanks for the help. I generally don't do models on request, but if it's something simple that i could knock off in an hour or two, I don't see why I cant.. what did you have in mind?
It was more of a general question, the mod that would need a model is kind of in a too early stage still.

Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

Re: I really need help with my mod

Post by Smitherino »

hmm, with this piece of code
script.on_event({defines.events.on_tick},
function (e)
for index,player in pairs(game.players) do
for slot=1,,3 do
local gun = global[index].guns[slot]
local mag = global[index].ammo[slot]

it says that its indexing "?" a nil value... crash
what exactly is global[index] referring to?
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: I really need help with my mod

Post by DaveMcW »

Smitherino wrote:for slot=1,,3 do
Too many commas.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: I really need help with my mod

Post by eradicator »

Smitherino wrote:it says that its indexing "?" a nil value... crash
what exactly is global[index] referring to?
"global" is just a table that has the speciality of being saved into the savegame, everything else about it is just a normal table. So global[index] refers to whatever you put into it. As i said above you have to initialize the table in on_init/on_player_created/etc or else it is just an empty table. 'indexing "?" a nil value' just means that you tried to read from a table that doesn't exist, i.e in that example you tried to read global[1].guns but global[1] is nil becaues you didn't initialize it, so it fails in the first loop.

Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

Re: I really need help with my mod

Post by Smitherino »

I apologize for my noobishness in advance, This works, but it still only checks in ammo slot 1 it seems... I have tried to use the #inventory stuff, but it just throws up errors, It would be nice to know where the inventory cycling piece of code actually goes in the structure of the script. as for the gun and ammo names.. just ignore them, I plan to have reloading specific to mod weapons so people have the option to play vanilla (adding reloading to vanilla weapons might also be a nice idea for a later date...)

Code: Select all

script.on_event({defines.events.on_tick},
   function (e)
      for index,player in pairs(game.players) do
	   global[index] = {ammo = player.get_inventory(defines.inventory.player_ammo), guns = player.get_inventory(defines.inventory.player_guns)}
		local inventory = player.get_inventory(defines.inventory.player_guns)
		for slot=1,3 do
		local gun = global[index].guns[slot]
		local mag = global[index].ammo[slot]
		if not (gun.valid_for_read and mag.valid_for_read) then break end
         if player.character and player.get_inventory(defines.inventory.player_ammo).get_item_count("notcolt-magazine") >= 1 then
		  local stack = player.get_inventory(defines.inventory.player_ammo)
			if gun.name == 'notcolt' and mag.name == 'notcolt-magazine' and mag.ammo == 1 then
               player.get_inventory(defines.inventory.player_guns).remove{name="notcolt", count=1}
               player.get_inventory(defines.inventory.player_guns).insert{name="notcolt-reload", count=1}
			   end
            if gun.name == 'notcolt-reload' and mag.name == 'notcolt-magazine' and mag.ammo ~= 1 then
               player.get_inventory(defines.inventory.player_guns).remove{name="notcolt-reload", count=1}
               player.get_inventory(defines.inventory.player_guns).insert{name="notcolt", count=1}
			   end
			 end
		  end
      end
   end
)
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: I really need help with my mod

Post by eradicator »

Smitherino wrote:I apologize for my noobishness in advance.
The only accepted apology is to get better :P. There's several things you seem to misunderstand or could do better. Use indentation. It makes it far easier for you (and me or anyone else) to read your code and give advice. You also seem to have difficulties with the difference between an inventory and an item stack, basically an inventory is a table of item stacks. And once you optain a reference (=assign to variable) there is no need to retrieve (get_inventory()) the inventory again and again. I've cleaned up the bit you posted and wrote some comments into it that hopefully help you better understand. I've left the usage of the global table out for now as that can be easily added later and is purely for performance and doesn't change any functionality. Copy into your text editor for better readability ;). Note that plural variables "mags" refer to inventories while singular variables "mag" refer to item stacks in this case.

Code: Select all

script.on_event({defines.events.on_tick},
  function (e)
    for _,player in pairs(game.connected_players) do
      local guns = player.get_inventory(defines.inventory.player_guns) --this would be _read_ from global
      local mags = player.get_inventory(defines.inventory.player_ammo) --magazine _inventory_
      if not (guns and mags) then break end --if there is no guns/mags inventory the player has no character, if there is then all slots must also be valid
      if mags.get_item_count("notcolt-magazine") == 0 then break end --the player has no fitting magazine equipped. checking this is not stricktly nessecary as you have to look at the individual slots later anyway.
      
      for slot=1,#guns do --number of slots in the player guns inventory, guns and mags inventories are always the same size so we only need one.
        local gun = guns[slot] --a single gun/magazine _ItemStack_
        local mag = mags[slot]
        if not (gun.valid_for_read and mag.valid_for_read) then break end --if an ItemStack is not valid for read that means it is an empty stack (empty inventory slots have an empty stack and not "nothing")
        
        if gun.name == 'notcolt' and mag.name == 'notcolt-magazine' and mag.ammo == 1 then
          gun.set_stack{name="notcolt-reload", count=gun.count} --overwrites the current gun with the "reload" gun but keeps the same number of guns using .remove on the whole guns inventory does not guarantee that you remove from the right slot
        elseif gun.name == 'notcolt-reload' and mag.name == 'notcolt-magazine' and mag.ammo ~= 1 then
          gun.set_stack{name="notcolt", count=gun.count} --overwrites the current gun with the "reload" gun but keeps the same number of guns using .remove on the whole guns inventory does not guarantee that you remove from the right slot
        end
      end
    end
  end)

Smitherino
Burner Inserter
Burner Inserter
Posts: 10
Joined: Thu Jun 09, 2016 8:49 am
Contact:

Re: I really need help with my mod

Post by Smitherino »

Thanks for the help regarding this. It took me a while, but I finally have something that works.

Code: Select all

script.on_event({defines.events.on_tick},
  function (e)
    for _,player in pairs(game.connected_players) do
      local guns = player.get_inventory(defines.inventory.player_guns)
      local mags = player.get_inventory(defines.inventory.player_ammo)
      if not (guns and mags) then break end
      
      for slot=1,#guns do
        local gun = guns[slot]
        local mag = mags[slot]
        if not (gun.valid_for_read and mag.valid_for_read) then goto reload end
        
        if mag.ammo == 1 and gun.name == gun.name:gsub("-reload","") and mags.get_item_count(mag.name) >=2 then
          gun.set_stack{name= gun.name .. "-reload", count=gun.count}
        elseif mag.ammo ~= 1 then
          gun.set_stack{name= gun.name:gsub("-reload",""), count=gun.count}
        end
	  ::reload::
      end
   end
end)
This still uses on tick (its the only event that works) but It just runs a generic piece of code that works with every weapon. I think this is pretty close to as optimised as it's going to get.

If anyone else sees this, just paste this code into control.lua, and make a new wepon called [weapon-name]-reload. This code will switch between them, causing it to appear as if the player is reloading.
"You must construct additional pylons"
- Executor Artanis

also I'm making mods :)
https://mods.factorio.com/mods/Impossibru/FirearmsPlus

Iccor56
Long Handed Inserter
Long Handed Inserter
Posts: 65
Joined: Mon Dec 25, 2017 12:29 am
Contact:

Re: I really need help with my mod

Post by Iccor56 »

any idea where i can find a list of what is in "player", "defines" or "game"? i can find lots of class info in the api listings but now how it is put into use in game. and i have zero idea how to look at the live situation.

Post Reply

Return to “Modding help”