research thechnologies for existing items - PVP

Place to get help with not working mods / modding interface.
mohsh86
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sun Jun 23, 2019 2:41 am
Contact:

research thechnologies for existing items - PVP

Post by mohsh86 »

Appologies if this is the wrong place to post.

Hosting PVP server with a couple of friends. One destroyed their silo and saves were overwritten (default save settings).

Was able to recreate the team with the same name and was assigned same index, player joined the force and spawned on the same base with all items in there.

The only thing that got reset is the research. Wondering if there a script can be written such that:

- for all unique items / buildings in the force -> grab required research.
- set required research status to researched.

Thanks!
Pi-C
Smart Inserter
Smart Inserter
Posts: 1756
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: research thechnologies for existing items - PVP

Post by Pi-C »

mohsh86 wrote: Sat May 06, 2023 11:26 pm The only thing that got reset is the research. Wondering if there a script can be written such that:

- for all unique items / buildings in the force -> grab required research.
- set required research status to researched.
It would be possible if each item (rather: a recipe making it) was unlocked by a single research. But at least in modded games, there may be several techs that unlock the same item.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
mohsh86
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sun Jun 23, 2019 2:41 am
Contact:

Re: research thechnologies for existing items - PVP

Post by mohsh86 »

am playing unmodded game if that helps.

doesn't matter if there are duplicate results. the other code snippet should just set them to researched.
mohsh86
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sun Jun 23, 2019 2:41 am
Contact:

Re: research thechnologies for existing items - PVP

Post by mohsh86 »

The following code researches technologies in tech_names

Code: Select all

local player = game.players["player_name"]
local force = player.force
local tech_names = {"automation", "logistics", "logistics-2", "logistics-3", 
"electric-energy-distribution-1", "electric-energy-accumulators", "oil-processing", 
"advanced-oil-processing", "sulfur-processing", "plastics", "advanced-electronics",
 "modules", "nuclear-power", "rocket-silo"}
for _, tech_name in ipairs(tech_names) do
  force.technologies[tech_name].researched = true
end
question remaining is, how do I populate tech_names with relevant technologies, based on items/buildings the force/team has
Pi-C
Smart Inserter
Smart Inserter
Posts: 1756
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: research thechnologies for existing items - PVP

Post by Pi-C »

mohsh86 wrote: Sun May 07, 2023 8:27 am question remaining is, how do I populate tech_names with relevant technologies, based on items/buildings the force/team has
You could copy this and paste it into the chat console:

Code: Select all

/c
forces = {}
items = {}
craft_items = {}
item_recipes = {}
prerequisites = {}

player_inventories = {
  defines.inventory.character_main,
  defines.inventory.character_guns,
  defines.inventory.character_ammo,
  defines.inventory.character_armor,
  defines.inventory.character_trash
}

for f_name, force in pairs(game.forces) do
  if next(force.players) then
    forces[f_name] = { items = {}, recipes = {}, research_this = {} }
    for p, player in pairs(force.players) do
      char = player.character
      for i, id in pairs(char and char.valid and player_inventories or {}) do
        inv = char.get_inventory(id)
        for item, i in pairs(inv and inv.valid and inv.get_contents() or {}) do
          forces[f_name].items[item] = true
          items[item] = true
        end
      end
    end
  end
end

for s_name, surface in pairs(game.surfaces) do
  for e, entity in pairs(surface.find_entities()) do
    if entity.valid and entity.last_user then
      player = type(entity.last_user) == "table" and entity.last_user or
                                                      game.players[entity.last_user]
      force = player.force
      for i, item in pairs(entity.prototype.items_to_place_this or {}) do
        forces[force.name].items[item.name] = true
        items[item.name] = true
      end

      if entity.has_items_inside() then
        for id = 1, entity.get_max_inventory_index() do
          inv = entity.get_inventory(id)
          for item, i in pairs(inv and inv.valid and inv.get_contents() or {}) do
            forces[force.name].items[item] = true
            items[item] = true
          end
        end
      end
    end
  end
end


for  r_name, recipe in pairs(game.recipe_prototypes) do
  for p, product in pairs(recipe.products) do
    if product.type == "item" then
      item_recipes[r_name] = true
      craft_items[product.name] = craft_items[product.name] or {}
      table.insert(craft_items[product.name], r_name)
    end
  end
end


for i_name, i in pairs(items) do
  if not craft_items[i_name] then
    for f_name, f_data in pairs(forces) do
      f_data.items[i_name] = nil
    end
    items[i_name] = nil

  else
    for f_name, f_data in pairs(forces) do
      for r, recipe in pairs(craft_items[i_name]) do
        f_data.recipes[recipe] = true
      end
    end
  end
end


function find_prerequisites(tech_name, list)
  tech = game.technology_prototypes[tech_name]
  list = list or {}
  for p_name, prerequisite in pairs(tech.prerequisites) do
    if not list[p_name] then
      list[p_name] = true
      find_prerequisites(p_name, list)
    end
  end
  return list
end


for t_name, tech in pairs(game.technology_prototypes) do
  for e, effect in pairs(tech.effects) do
    if effect.type == "unlock-recipe" and item_recipes[effect.recipe] then
      for f_name, f_data in pairs(forces) do
        if f_data.recipes[effect.recipe] then
          f_data.research_this[t_name] = true
          prerequisites[t_name] = prerequisites[t_name] or find_prerequisites(t_name)
        end
      end
    end
  end
end


for f_name, f_data in pairs(forces) do
  research = {}
  for t_name, t in pairs(f_data.research_this) do
    for p_name, p in pairs(prerequisites[t_name] or {}) do
      research[p_name] = true
    end
    research[t_name] = true
  end

  force = game.forces[f_name]
  for t_name, t in pairs(research) do
    force.technologies[t_name].researched = true
  end
end
This is basically the same code, but commented, so it should be easier to read:

Code: Select all

local forces = {}
local items = {}          -- List of all items we'll find
local craft_items = {}    -- List of items that can be produced by a recipe
local item_recipes = {}   -- Recipes that produce an item
local prerequisites = {}  -- List of all prerequisite techs required by a tech

local player_inventories = {
  defines.inventory.character_main,
  defines.inventory.character_guns,
  defines.inventory.character_ammo,
  defines.inventory.character_armor,
  defines.inventory.character_trash
}

local char, inv

-- Find all forces with at least one player
for f_name, force in pairs(game.forces) do
  if next(force.players) then
    forces[f_name] = { items = {}, recipes = {}, research_this = {} }

    -- Store names of items found in player inventories with force
    for p, player in pairs(force.players) do
      char = player.character
      for i, id in pairs(char and char.valid and player_inventories or {}) do
        inv = char.get_inventory(id)
        for item, i in pairs(inv and inv.valid and inv.get_contents() or {}) do
          forces[f_name].items[item] = true
          items[item] = true
        end
      end
    end
  end
end


-- Find all entities on all surfaces
local force, player, items
for s_name, surface in pairs(game.surfaces) do
  for e, entity in pairs(surface.find_entities()) do
    -- Get force this entity belongs to (must have been placed by a player)
    if entity.valid and entity.last_user then
      player = type(entity.last_user) == "table" and entity.last_user or
                                                      game.players[entity.last_user]
      force = player.force

      -- Add names of items used to place this entity to item list of force
      for i, item in pairs(entity.prototype.items_to_place_this or {}) do
        forces[force.name].items[item.name] = true
        items[item.name] = true
      end

      -- Add names of items found inside the entity to item list of force
      if entity.has_items_inside() then
        for id = 1, entity.get_max_inventory_index() do
          inv = entity.get_inventory(id)
          for item, i in pairs(inv and inv.valid and inv.get_contents() or {}) do
            forces[force.name].items[item] = true
            items[item] = true
          end
        end
      end
    end
  end
end

-- Find all recipes that produce items
for  r_name, recipe in pairs(game.recipe_prototypes) do
  for p, product in pairs(recipe.products) do
    -- Ignore fluids!
    if product.type == "item" then
      item_recipes[r_name] = true
      craft_items[product.name] = craft_items[product.name] or {}
      table.insert(craft_items[product.name], r_name)
    end
  end
end

-- Update force data
for i_name, i in pairs(items) do
  -- Remove all items that can't be crafted from force data
  if not craft_items[i_name] then
    for f_name, f_data in pairs(forces) do
      f_data.items[i_name] = nil
    end
    items[i_name] = nil

  -- If force has this item, store names of all recipes producing it with force data
  else
    for f_name, f_data in pairs(forces) do
      for r, recipe in pairs(craft_items[i_name]) do
        f_data.recipes[recipe] = true
      end
    end
  end
end


function find_prerequisites(tech_name, list)
  local tech = game.technology_prototypes[tech_name]
  list = list or {}
  for p_name, prerequisite in pairs(tech.prerequisites) do
    if not list[p_name] then
      list[p_name] = true
      find_prerequisites(p_name, list)
    end
  end
  return list
end

-- Find all techs that unlock any of these recipes
for t_name, tech in pairs(game.technology_prototypes) do
  for e, effect in pairs(tech.effects) do
    if effect.type == "unlock-recipe" and item_recipes[effect.recipe] then

      -- Store this tech for all forces that have the unlock recipe in their data
      for f_name, f_data in pairs(forces) do
        if f_data.recipes[effect.recipe] then
          f_data.research_this[t_name] = true

          -- If this is the first force that needs this tech, store all technologies
          -- required to unlock the tech!
          prerequisites[t_name] = prerequisites[t_name] or find_prerequisites(t_name)
        end
      end
    end
  end
end

-- Research techs and their prerequisites!
for f_name, f_data in pairs(forces) do
  research = {}
  -- One technology may be prerequisite of several other techs. We want to research
  -- as little as possible as each finished research will raise an event, so we
  -- compile a list where each tech will be listed just once.
  for t_name, t in pairs(f_data.research_this) do
    for p_name, p in pairs(prerequisites[t_name]) do
      research[p_name] = true
    end

    -- Add the tech that was actually needed, it may be prerequisite of another tech!
    research[t_name] = true
  end

  force = game.forces[f_name]
  for t_name, t in pairs(research) do
    force.technologies[t_name].researched = true
  end
end
It will look for the names of items in player inventories and in the inventories of any entity on any surface. It also will keep track of items used to place down an entity. Then it will look for the recipes that create items, filter out the recipes unlocked by research, and store these techs along with their prerequisite technologies. Based on what item names are stored with a given force, it will then research the technologies.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Pi-C
Smart Inserter
Smart Inserter
Posts: 1756
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: research thechnologies for existing items - PVP

Post by Pi-C »

Pi-C wrote: Sun May 07, 2023 2:38 pm
This is basically the same code, but commented, so it should be easier to read:

Code: Select all

…
-- Find all entities on all surfaces
local force, player, items
for s_name, surface in pairs(game.surfaces) do
  for e, entity in pairs(surface.find_entities()) do
    -- Get force this entity belongs to (must have been placed by a player)
    if entity.valid and entity.last_user then
      player = type(entity.last_user) == "table" and entity.last_user or
                                                      game.players[entity.last_user]
      force = player.force

Duh, how stupid! Use this instead:

Code: Select all

-- Find all entities on all surfaces
local force, player, items
for s_name, surface in pairs(game.surfaces) do
  for e, entity in pairs(surface.find_entities()) do
    -- Get force this entity belongs to (must have been placed by a player)
    if entity.valid then
      force = entity.force
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
mohsh86
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sun Jun 23, 2019 2:41 am
Contact:

Re: research thechnologies for existing items - PVP

Post by mohsh86 »

Worked out great, Thanks!

One last thing, I've noticed that with the existing items, I won't be able to link things together (for example, extend on existing wall, etc) also, electrical grid doesn't show on map unless I build and place something new. Is it possible to reclaim entities (i.e. become the owner again for entities of force in question).

Cheers!
Pi-C
Smart Inserter
Smart Inserter
Posts: 1756
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: research thechnologies for existing items - PVP

Post by Pi-C »

mohsh86 wrote: Mon May 08, 2023 10:12 am Worked out great, Thanks!
You're welcome!
One last thing, I've noticed that with the existing items, I won't be able to link things together (for example, extend on existing wall, etc) also, electrical grid doesn't show on map unless I build and place something new. Is it possible to reclaim entities (i.e. become the owner again for entities of force in question).
Which force do these entities belong to now? Do they have last_user? You can find out by hovering your cursor of an entity and use this on the command line:

Code: Select all

/c p = game.player; s = p.selected; if s then game.print(string.format("Selected entity: %s (%s)\tForce: %s\tLast user: %s", s.name, s.unit_number, s.force.name, s.last_user and s.last_user.name or "none")) end 
You could look for all entities on all surfaces, ignoring those that have no unit_number (trees, rocks etc.) or are obviously enemies (types "unit", "unit-spawner", and "turret"), and then set entity.force = entity.last_user.force if they have a last_user.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
mohsh86
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sun Jun 23, 2019 2:41 am
Contact:

Re: research thechnologies for existing items - PVP

Post by mohsh86 »

I've recreated the force with the same name (got created with the same index), and I joined it with the same name. and Yes, the last user for all items was me as well.

Really Appreciated it :D

Edit:

I've typed the command while hovering over an item it shows:

force: neutral last user: <my user>
mohsh86
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sun Jun 23, 2019 2:41 am
Contact:

Re: research thechnologies for existing items - PVP

Post by mohsh86 »

This finally worked after lots of talks with chat GPT lol. I need to read more about lua and factorio's API
local player_name = "player_name"
local force_name = "force_name"

for _, surface in pairs(game.surfaces) do
for _, entity in pairs(surface.find_entities_filtered{last_user = player_name, force = "neutral"}) do
entity.force = game.forces[force_name]
end
end
Pi-C
Smart Inserter
Smart Inserter
Posts: 1756
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: research thechnologies for existing items - PVP

Post by Pi-C »

mohsh86 wrote: Tue May 09, 2023 12:04 pm This finally worked after lots of talks with chat GPT lol. I need to read more about lua and factorio's API

Code: Select all

local player_name = "player_name"
local force_name = "force_name"

for _, surface in pairs(game.surfaces) do
  for _, entity in pairs(surface.find_entities_filtered{last_user = player_name, force = "neutral"}) do
    entity.force = game.forces[force_name]
  end
end
Unfortunately, you can't use last_user directly as filter for find_entities_filtered. But once you've got a list of entities, you can check whether they have last_user, and whether the last_user has the same force as the entity:

Code: Select all

local old, new
for s, surface in pairs(game.surfaces) do 
	for e, entity in pairs(surface.find_entities()) do
		old = entity.force
		new = entity.last_user and entity.last_user.force
		if new and new ~= old then
			log(string.format("Entity: %s (%s)\tOld force: %s\tNew force: %s", entity.name, entity.unit_number or "nil", old.name, new.name))
			entity.force = new
		end
	end
end
The invalid filter "last_user = player_name" is discarded, so you've actually searched for all entities that belong to force "neutral".

Code: Select all

local force_name = "force_name"
…
  for … do
    entity.force = game.forces[force_name]
  end
…
That's not exactly wrong, but terribly inefficient! For one thing, you look up the force via its name in each loop iteration. As you're always looking for the same force, It's better to cache it once outside the loop:

Code: Select all

local force_name = "force_name"
local force = game.forces[force_name]
…
  for … do
    entity.force = force
  end
…
But even that is too much, because you can use a LuaForce, a string (force.name), or an integer (force.index) as ForceIdentification:

Code: Select all

local force_name = "force_name"
…
  for … do
    entity.force = force_name
  end
…
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Post Reply

Return to “Modding help”