New scenario need some help with understanding events.

Place to post guides, observations, things related to modding that are not mods themselves.
Post Reply
User avatar
quake513
Burner Inserter
Burner Inserter
Posts: 9
Joined: Sun Feb 05, 2017 5:38 pm
Contact:

New scenario need some help with understanding events.

Post by quake513 »

Trying to make a scenario with multiple dimensions I've just about got the transportation between the dimensions solved using teleporters to simplify stuff. However I know you can add events to create messages and some other items without having to do a whole lot of scripting.
If I understand that correctly.

I'm looking to see if anyone can point me in the direction where I can find more information regarding how to use the Lua in the map editor to create events and triggers and what type of commands I can use without having to go too deep into scripting.

I am at the very beginning stages of learning scripting and Lua code.
I have over 1500 hours in Factorio, and now have my kids starting to play. I'm trying to build a custom map for them to play on.

Any help would be appreciate it.

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

Re: New scenario need some help with understanding events.

Post by Pi-C »

quake513 wrote:
Thu Aug 24, 2023 12:41 am
However I know you can add events …
Most modders don't add events, but react to events by adding handlers for specific events to their code.

Basically, if the game wants to tell all active mods that something has happened, it will "raise" an event. Such an event has an internal ID (numeric) that is mapped to a human-readable name in defines.events. Whenever an event is raised, it is guaranteed to have the two parameters "name" (the numeric ID) and "tick" (when the event was raised). Additionally, there will be at least one parameter that is specific to each event (for example, "entity" for defines.events.on_entity_color_changed, or "player_index" for defines.events.on_player_joined_game). Here is a list of all vanilla events with their parameters.

Events work a bit like radio: Whenever an event is raised, this is broadcast globally, to all mods. However, only mods that have turned on their radio and have tuned in to the right channel will notice that the event has been raised. Mods can "listen" to a specific event by calling LuaBootstrap::on_event(ID, handler, filters). Whenever the event with the given numeric ID is raised, the mod's handler (a function) for that event will be run with the event data as argument. Some (not all!) events can be filtered, but even if an event supports filters, they are optional.

Some mods define their own "custom events" by using LuaBootstrap::generate_event_name():

Code: Select all

local my_event_id = script.generate_event_name()
This will return a unique numeric ID for your event. As this ID will be generated as a consequence of your mod's code being run by the game, it may be different when a saved game is loaded with added/removed mods (mods loaded before your mod will also generate their event IDs before your mod). Therefore, you must make sure to re-generate the ID in response to LuaBootstrap::on_configuration_changed.

Once you have a unique ID for your custom event you must publish it, so other mods can use it to listen to your event. This is commonly done via the remote interface:

Code: Select all

my_event_id = script.generate_event_name()

remote.add_interface("YOUR_INTERFACE_NAME", {
	get_event_id = function()
		return my_event_id
	end,
	
	something_else = function()
		…
	end,
})
Now, other mods can register to your custom event by asking yours for its event ID:

Code: Select all

local custom_id
if remote.interfaces["YOUR_INTERFACE_NAME"] then
	custom_id = remote.call("YOUR_INTERFACE_NAME", "get_event_id")
	script.on_event(custom_id, function(event)
		-- Logs the parameters of your custom event
		log(serpent.block(event))
	end)
end
However, nothing will happen yet because the event will never be raised! The game has provided a unique event ID, but of course it can't know when it should raise the event, or what parameters it should pass on. That's entirely your responsibility, but the game provides a tool you can use: LuaBootstrap::raise_event(my_event_id, event_data), where "my_event_id" is the ID you've generated for your custom event, and "event_data" is a table with whatever parameters you want to include. (Note: event_data.name and event_data.tick will be automatically added by the game. The game will also add event_data.mod_name, so other mods can tell which mod raised the event.)

Events are only relevant during the control stage (roughly speaking, when you've started a new game or loaded/joined a game using Factorio's main menu). Triggers are somewhat different: They will be set off during the control stage, but they must be defined in the data stage. Here is an example from my WIP-version of "Bio Industries", where we create an "ammo" item that will set off a ScriptTriggerEffectItem when our cannon fires this ammo:

Code: Select all

  ammo_type = {
    category = BI.additional_categories.Bio_Cannon.cannon_ammo.name,
    target_type = "direction",
    action = {
      { 
        type = "direct",
        trigger_target_mask = { BI.additional_categories.Bio_Cannon.trigger_target.name },
        filter_enabled = true,
        action_delivery = {
          type = "projectile",
          projectile = "bi-bio-cannon-ammo-proto",
          starting_speed = 0.2,
          direction_deviation = 0.5,
          max_range = 90,
          min_range = 20,
          source_effects = {
            type = "script",
            effect_id = "BI_cannon-ammo-proto_create_pollution",
          },
        }
      }
    }
  },
The important thing is this:

Code: Select all

          source_effects = {
            type = "script",
            effect_id = "BI_cannon-ammo-proto_create_pollution",
          },
Now, whenever the ammo is fired in the game, this will raise the on_script_trigger_effect event, and we can act whenever the event has been raised for the defined effect_id:

Code: Select all

script.on_event(defines.events.on_script_trigger_effect, function(event)
	if event.effect_id == "BI_cannon-ammo-proto_create_pollution" then
		-- Do stuff
	end
end)
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

User avatar
quake513
Burner Inserter
Burner Inserter
Posts: 9
Joined: Sun Feb 05, 2017 5:38 pm
Contact:

Re: New scenario need some help with understanding events.

Post by quake513 »

thanks for the help.

how would i change a player's force?

i have tried the following but it does not seem to work.
Is there a way to see what force a player is in?

code 1)
-- Find the player by name
local target_player_name = "quake513" -- Replace with the actual player name
local target_force_name = "Oilver" -- Replace with the actual target force name

local target_player = game.players[target_player_name]
local target_force = game.forces[target_force_name]

-- Change the player's force
if target_player and target_force then
target_player.force = target_force
else
-- Handle case if player or force is not found
-- You might want to log a message or handle this differently
end


Code 2)
-- Get the player index for the player you want to modify
local player_index = 1 -- Replace with the actual player index

-- Get the target force you want to assign to the player
local target_force_name = "target_force_name" -- Replace with the actual target force name

-- Change the player's force
game.players[player_index].force = game.forces[target_force_name]

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

Re: New scenario need some help with understanding events.

Post by Pi-C »

quake513 wrote:
Sat Aug 26, 2023 1:04 am
how would i change a player's force?

Is there a way to see what force a player is in?
The class LuaControl is your friend here, more precisely: LuaControl::force. You can use a string (force name) or a LuaForce when assigning a force to a LuaPlayer or LuaEntity, while reading it will always give you a LuaForce.
i have tried the following but it does not seem to work.
Why, is there any error message? Did you check the log file? Do player and force exist? There's nothing you can do about a non-existant player, but if the force is missing, you can create it with LuaGameScript::create_force.
code 1)
-- Find the player by name
local target_player_name = "quake513" -- Replace with the actual player name
local target_force_name = "Oilver" -- Replace with the actual target force name

local target_player = game.players[target_player_name]
local target_force = game.forces[target_force_name]
According to the explanation of LuaGameScript.players, it's cheaper to use LuaGameScript::get_player() if you want to get no more than one player. You can pass on either an integer (LuaPlayer::index) or a string (LuaPlayer::name) to the function. I'd do it like this:

Code: Select all

-- Find the player by name
local target_player_name = "quake513" -- Replace with the actual player name
local target_player = game.get_player(target_player_name)

local target_force_name = "Oilver" -- Replace with the actual target force name
local target_force = game.forces[target_force_name]

-- Target force doesn't exist!
if not (target_force and target_force.valid) then
	-- Create force
	target_force = game.create_force(target_force_name)
  
  	-- Set up relationship with other forces
  	for f, force in pairs(game.forces) do
  		if force.name == "enemy" then
  			target_force.set_cease_fire(force, false)
  			target_force.set_friend(force, false)
  		elseif force.name == "neutral" then
  			target_force.set_cease_fire(force, true)
  			target_force.set_friend(force, false) 			-- Not sure if this is really necessary!
  		else 
  			target_force.set_friend(force, true)
  		end
  	end
end   
  
-- Change the player's force
if target_player and target_player.valid then
    target_player.force = target_force
else
    -- Handle case if player is not found
    -- You might want to log a message or handle this differently
end
Last edited by Pi-C on Wed Aug 30, 2023 7:09 am, edited 1 time in total.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

User avatar
quake513
Burner Inserter
Burner Inserter
Posts: 9
Joined: Sun Feb 05, 2017 5:38 pm
Contact:

Re: New scenario need some help with understanding events.

Post by quake513 »

thanks for the help got it to work.
first how are ya'll getting the white box for the code? ( in the Froms)

this is the code that i was able to get to work as a command.
Code:
/c local player = game.players["user_name"]; local target_force = game.forces["force_name"]; if player and target_force then player.force = target_force else game.print("Player or target force not found.") end

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

Re: New scenario need some help with understanding events.

Post by Pi-C »

quake513 wrote:
Wed Aug 30, 2023 2:38 am
thanks for the help got it to work.
You're welcome!
first how are ya'll getting the white box for the code? ( in the Froms)
When you're writing/editing a post, there is a line of icons at the top of the window you're writing in:
icons.png
icons.png (23.61 KiB) Viewed 642 times
The highlighted icon will create this box. Of course, the icons are meant only as a shortcut. What really happens is they surround marked text with tags "[X]" for opening and "[/X]" for closing. I've used 'X' as a placeholder because I'm not aware whether/how one could show the real tag without using it. Replace 'X' with "code" to get the code window. You must turn on BBCode in your user settings for this to work:
control_panel.png
control_panel.png (42.15 KiB) Viewed 642 times
this is the code that i was able to get to work as a command.
Code:
/c local player = game.players["user_name"]; local target_force = game.forces["force_name"]; if player and target_force then player.force = target_force else game.print("Player or target force not found.") end
It's usually good practice to make sure that an object exists AND is still valid before doing something with it. Otherwise, there's a good chance you'll get an error like "attempt to index local 'player' (a nil value)" at some point. Also, if you like trying out stuff regarding your own player with console commands, you can use

Code: Select all

p = game.player
(Please note that LuaGameScript::player is ONLY available for console commands, not in your regular code!)
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 discussion”