Question about the global table
Question about the global table
I'm working with a new mod project (with no prior Factorio modding experience) and I'm trying to set up something where a pseudo-infinite upgrade technology could possibly increment an integer value to track how many times that technology has been researched, assuming that's necessary.
I'm not 100% sure if that is needed, but basically what I want to do is check how many times that technology has been researched so I can call it from a function to determine, say, the maximum rotational speed of an inserter, based on settings defined in the mod's settings and the progress the player has made in-game.
If that doesn't need a call of the global table, then that's fine, though I would still like to know how I would make such a call for future reference.
If more information is needed, so as to provide better input, I'm willing to give more thorough explanation.
Thank you!
I'm not 100% sure if that is needed, but basically what I want to do is check how many times that technology has been researched so I can call it from a function to determine, say, the maximum rotational speed of an inserter, based on settings defined in the mod's settings and the progress the player has made in-game.
If that doesn't need a call of the global table, then that's fine, though I would still like to know how I would make such a call for future reference.
If more information is needed, so as to provide better input, I'm willing to give more thorough explanation.
Thank you!
Hi hungry, I'm dad!
Re: Question about the global table
See code of https://mods.factorio.com/mod/RocketExplosions
Code: Select all
function get_tech_level (force, tech_name)
local level = 0
local techs = force.technologies
local tech = techs[tech_name] or techs[tech_name..'-1']
if tech and tech.researched then
level = tech.level
local i = 1
local fl = true
while fl do
i=i+1
local tech = techs[tech_name..'-'..i]
if tech and tech.researched then
--game.print ('ok: i: '..i)
level=tech.level
elseif tech then
-- level 7 or not researched
fl = false
if not (techs[tech_name..'-'..(i+1)]) then
-- there is no level 8 at all
level=tech.level
end
else
fl = false
end
end
else
--game.print ('not researched')
return 0
end
return level
end
Re: Question about the global table
Why don't you define an infinite-tech prototype? You'll need to find a formula for the unit count (how many science packs are required per level) and set technology.max_level = "infinite".spiro9 wrote: Mon Apr 25, 2022 2:29 am I'm working with a new mod project (with no prior Factorio modding experience) and I'm trying to set up something where a pseudo-infinite upgrade technology could possibly increment an integer value to track how many times that technology has been researched, assuming that's necessary.
Listen to on_research_finished:I'm not 100% sure if that is needed, but basically what I want to do is check how many times that technology has been researched so I can call it from a function to determine, say, the maximum rotational speed of an inserter, based on settings defined in the mod's settings and the progress the player has made in-game.
Code: Select all
script.on_event(defines.events.on_research_finished, function(event)
local tech = event.research
local level = tech.level
end)
The global table is something that belongs to your mod. It will be accessible in script.on_init, script.on_configuration_changed, migration scripts, and during normal running of the game, but NOT in script.on_load.If that doesn't need a call of the global table, then that's fine, though I would still like to know how I would make such a call for future reference.
You should put things in there that can't be easily restored when you load a game: e.g. who built a particular entity, or what was the last car a player was riding in. Also, you should think about how to structure your data before storing anything in the global table. Obviously, as your mod evolves your needs will change, but it's always harder to reorganize your data later on than doing it right immediately.
As an example, let's say you have a specific tech and want prevent players from using cheats to research it. So you could do this:
Code: Select all
script.on_init(function()
global.forces = global.forces or {}
end)
script.on_event(defines.events.on_research_finished, function(event)
local tech = event.research
local cheated = event.by_script
local level = tech.level
local force = tech.force
global.forces[force.name] = global.forces[force.name] or {}
-- Add new level of researched tech to global table
if not cheated then
global.forces[force.name][tech.name] = tech.level
-- Cheat detected! Some level of the tech has been researched
-- honestly, reset the tech to that level.
elseif global.forces[force.name][tech.name] then
tech.level = global.forces[force.name][tech.name]
-- Cheat detected! No level of the tech has ever been researched
-- by that force, revert the research completely.
else
tech.researched = false
end
end)
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Re: Question about the global table
To clarify, `global` is read-only during `script.on_load`. It is to be used only to restore event subscription states and Lua metatables.Pi-C wrote:The global table is something that belongs to your mod. It will be accessible in script.on_init, script.on_configuration_changed, migration scripts, and during normal running of the game, but NOT in script.on_load.
My mods: Multiple Unit Train Control, Smart Artillery Wagons
Maintainer of Vehicle Wagon 2, Cargo Ships, Honk
Maintainer of Vehicle Wagon 2, Cargo Ships, Honk
-
- Smart Inserter
- Posts: 2768
- Joined: Tue Apr 25, 2017 2:01 pm
- Contact:
Re: Question about the global table
Iirc, too (and somebody please correct me if I'm wrong), each mod's instance of "global" is for that mod only. So there's no worry about needing to use unique keys to avoid mod conflict (though that also means another mod can't access any values you store there).
My Mods: Classic Factorio Basic Oil Processing | Sulfur Production from Oils | Wood to Oil Processing | Infinite Resources - Normal Yield | Tree Saplings (Redux) | Alien Biomes Tweaked | Restrictions on Artificial Tiles | New Gear Girl & HR Graphics
Re: Question about the global table
Thanks, you're right! I've confused 'game' and 'global' here: 'game' can't be accessed at all in on_load, 'global' can only be read.robot256 wrote: Mon Apr 25, 2022 1:01 pm To clarify, `global` is read-only during `script.on_load`. It is to be used only to restore event subscription states and Lua metatables.
@ spiro9: Also 'global' can't be used outside of event handlers. If control.lua is loaded, 'global' will be empty, and if you make any changes to it, they will be overwritten once the file has been loaded completely. You definitely should read about the data lifecycle.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Re: Questions about the global table & tech calls
If I'm calling this from, say, clicking on an inserter to change certain attributes on the fly, what would be the best way to do so and return the level value as something I can pass into a formula built from user-set mod settings?darkfrei wrote: Mon Apr 25, 2022 9:29 am See code of https://mods.factorio.com/mod/RocketExplosions
Code: Select all
function get_tech_level (force, tech_name) local level = 0 local techs = force.technologies local tech = techs[tech_name] or techs[tech_name..'-1'] if tech and tech.researched then level = tech.level local i = 1 local fl = true while fl do i=i+1 local tech = techs[tech_name..'-'..i] if tech and tech.researched then --game.print ('ok: i: '..i) level=tech.level elseif tech then -- level 7 or not researched fl = false if not (techs[tech_name..'-'..(i+1)]) then -- there is no level 8 at all level=tech.level end else fl = false end end else --game.print ('not researched') return 0 end return level end
So like, if I've got a single technology that has a maximum level set (based on a mod's settings, which I've already coded, see below code block), how would I work with that? Passing mod settings into variables should be easy enough, I figure.
Keep in mind I'm primarily referencing and recycling the code from the Inserter Configuration mod, which only seems to have calls for checking an inserter prototype, a functionality that isn't like what I'm trying to accomplish, which is simply stated, a more configurable take on what Bob's Adjustable Inserters does, since that mod has hard-coded limitations I'd like a work-around to. I note that in reading the code used there I see a lot of 'force' calls related to technology, similar to what you sent. I'm not sure how I'd grab that.
Code: Select all
-- Based on Bob's Adjustable Inserters technology format as reference.
data:extend(
{
{
type = "technology",
name = "spiro9_inserter_reach",
icon = "__spiros_inserters__/graphics/tech-icons/reach.png",
icon_size = 128,
effects =
{
},
prerequisites =
{
"logistics",
},
unit =
{
count_formula = "((10*L)^2)/5",
ingredients =
{
{"automation-science-pack", 1},
},
time = 5
},
max_level = settings.startup["s9:inserter-reach-techs"].value,
order = "spiro9_inserter_reach_1",
upgrade = true,
},
}
)
Code: Select all
--[[ Commented out as reference to replace it with custom functionality.
function inserter_utils.get_max_range(inserter)
local prototype = inserter
if prototype.object_name == "LuaEntity" then
prototype = inserter_utils.get_prototype(prototype)
end
local pickup_pos = math2d.position.tilepos(math2d.position.add(prototype.inserter_pickup_position, {0.5, 0.5}))
local drop_pos = math2d.position.tilepos(math2d.position.add(prototype.inserter_drop_position, {0.5, 0.5}))
return math.max(math.abs(pickup_pos.x), math.abs(pickup_pos.y), math.abs(drop_pos.x), math.abs(drop_pos.y))
end ]]
function inserter_utils.get_max_range(inserter)
-- The original function appears to grab the maximum range from the inserter prototype. This is not desired behavior.
-- Change this to follow the variables set by the user!
-- function: max range = base + inserter reach setting + (reach incrementation setting * reach tech level), where base is 2
local setting_irbase = settings.startup["s9:inserter-reach"].value
local reach_increment_final = settings.startup["s9:inserter-reach-inc"].value *
return ( 2 + setting_irbase + reach_increment_final )
end
Hi hungry, I'm dad!
Re: Question about the global table
I'm looking more into this and I think I've found my answer in the *game* table, for what it is I'm looking to do. The event that calls the function I'm rewriting is on_entity_settings_pasted, which has a player_index value.Pi-C wrote: Mon Apr 25, 2022 10:03 amThe global table is something that belongs to your mod. It will be accessible in script.on_init, script.on_configuration_changed, migration scripts, and during normal running of the game, but NOT in script.on_load.
You should put things in there that can't be easily restored when you load a game: e.g. who built a particular entity, or what was the last car a player was riding in. Also, you should think about how to structure your data before storing anything in the global table. Obviously, as your mod evolves your needs will change, but it's always harder to reorganize your data later on than doing it right immediately.
As an example, let's say you have a specific tech and want prevent players from using cheats to research it. So you could do this:
Hope that helps!Code: Select all
script.on_init(function() global.forces = global.forces or {} end) script.on_event(defines.events.on_research_finished, function(event) local tech = event.research local cheated = event.by_script local level = tech.level local force = tech.force global.forces[force.name] = global.forces[force.name] or {} -- Add new level of researched tech to global table if not cheated then global.forces[force.name][tech.name] = tech.level -- Cheat detected! Some level of the tech has been researched -- honestly, reset the tech to that level. elseif global.forces[force.name][tech.name] then tech.level = global.forces[force.name][tech.name] -- Cheat detected! No level of the tech has ever been researched -- by that force, revert the research completely. else tech.researched = false end end)
What I need to know now is whether I can safely call something like local player_obj = game.players[player_index] or whether that might prevent saving like the docs warn about.
Hi hungry, I'm dad!
Re: Question about the global table
Is it okay if I recycle this code with credit? This is my current code, if that's not the case - perhaps you might know how to retool it to have the functionality I need.darkfrei wrote: Mon Apr 25, 2022 9:29 am See code of https://mods.factorio.com/mod/RocketExplosions
Code: Select all
function get_tech_level (force, tech_name) local level = 0 local techs = force.technologies local tech = techs[tech_name] or techs[tech_name..'-1'] if tech and tech.researched then level = tech.level local i = 1 local fl = true while fl do i=i+1 local tech = techs[tech_name..'-'..i] if tech and tech.researched then --game.print ('ok: i: '..i) level=tech.level elseif tech then -- level 7 or not researched fl = false if not (techs[tech_name..'-'..(i+1)]) then -- there is no level 8 at all level=tech.level end else fl = false end end else --game.print ('not researched') return 0 end return level end
Code: Select all
function inserter_utils.get_max_range(inserter, this_force)
-- The original function appears to grab the maximum range from the inserter prototype. This is not desired behavior.
-- Change this to follow the variables set by the user!
-- Initialize a variable of the relevant technology!
local reachtech = this_force.technologies["spiro9_inserter_reach"]
-- function: max range = base + inserter reach setting + (reach incrementation setting * reach tech level), where base is 2
local setting_irbase = settings.startup["s9:inserter-reach"].value
local reach_increment_final = (settings.startup["s9:inserter-reach-inc"].value * reachtech.level)
return ( 2 + setting_irbase + reach_increment_final )
end
Hi hungry, I'm dad!
Re: Question about the global table
Yes, you can make what you want without any limitationsspiro9 wrote: Wed Apr 27, 2022 4:33 am
Is it okay if I recycle this code with credit? This is my current code, if that's not the case - perhaps you might know how to retool it to have the functionality I need.Code: Select all
function inserter_utils.get_max_range(inserter, this_force) -- The original function appears to grab the maximum range from the inserter prototype. This is not desired behavior. -- Change this to follow the variables set by the user! -- Initialize a variable of the relevant technology! local reachtech = this_force.technologies["spiro9_inserter_reach"] -- function: max range = base + inserter reach setting + (reach incrementation setting * reach tech level), where base is 2 local setting_irbase = settings.startup["s9:inserter-reach"].value local reach_increment_final = (settings.startup["s9:inserter-reach-inc"].value * reachtech.level) return ( 2 + setting_irbase + reach_increment_final ) end
The unlimited technology (1-7 was limited and from 8 is unlimited) level is not so easy as it must be.
Re: Question about the global table
It's OK to use that in a local definition. You can also usespiro9 wrote: Wed Apr 27, 2022 1:25 am What I need to know now is whether I can safely call something like local player_obj = game.players[player_index] or whether that might prevent saving like the docs warn about.
Code: Select all
local entity = player.surface.create_entity{name = "car", position = player.position}
global.entities = global.entities or {}
global.entities[entity.unit_number] = entity
Code: Select all
local entity = player.surface.create_entity{name = "car", position = player.position}
global.entities = global.entities or {}
global.entities[entity] = entity.unit_number
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!