| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  | function get_any_stack_size(name) | 
					
						
							| 
									
										
										
										
											2024-11-11 11:43:16 +01:00
										 |  |  |     local item = prototypes.item[name] | 
					
						
							| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  |     if item ~= nil then | 
					
						
							|  |  |  |         return item.stack_size | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2024-11-11 11:43:16 +01:00
										 |  |  |     item = prototypes.equipment[name] | 
					
						
							| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  |     if item ~= nil then | 
					
						
							|  |  |  |         return item.stack_size | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     -- failsafe | 
					
						
							|  |  |  |     return 1 | 
					
						
							| 
									
										
										
										
											2021-07-04 15:25:56 +02:00
										 |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | -- from https://stackoverflow.com/a/40180465 | 
					
						
							|  |  |  | -- split("a,b,c", ",") => {"a", "b", "c"} | 
					
						
							|  |  |  | function split(s, sep) | 
					
						
							|  |  |  |     local fields = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sep = sep or " " | 
					
						
							|  |  |  |     local pattern = string.format("([^%s]+)", sep) | 
					
						
							|  |  |  |     string.gsub(s, pattern, function(c) fields[#fields + 1] = c end) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return fields | 
					
						
							| 
									
										
										
										
											2023-03-15 17:03:33 +01:00
										 |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function random_offset_position(position, offset) | 
					
						
							| 
									
										
										
										
											2024-11-11 11:43:16 +01:00
										 |  |  |     return {x=position.x+math.random(-offset, offset), y=position.y+math.random(-offset, offset)} | 
					
						
							| 
									
										
										
										
											2023-03-15 17:03:33 +01:00
										 |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function fire_entity_at_players(entity_name, speed) | 
					
						
							| 
									
										
										
										
											2024-11-30 06:53:28 +01:00
										 |  |  |     local entities = {} | 
					
						
							| 
									
										
										
										
											2023-03-15 17:03:33 +01:00
										 |  |  |     for _, player in ipairs(game.forces["player"].players) do | 
					
						
							| 
									
										
										
										
											2024-11-30 06:53:28 +01:00
										 |  |  |         if player.character ~= nil then | 
					
						
							|  |  |  |             table.insert(entities, player.character) | 
					
						
							| 
									
										
										
										
											2023-03-15 17:03:33 +01:00
										 |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2024-11-30 06:53:28 +01:00
										 |  |  |     return fire_entity_at_entities(entity_name, entities, speed) | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function fire_entity_at_entities(entity_name, entities, speed) | 
					
						
							|  |  |  |     for _, current_entity in ipairs(entities) do | 
					
						
							|  |  |  |         local target = current_entity | 
					
						
							|  |  |  |         if target.health == nil then | 
					
						
							|  |  |  |             target = target.position | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         current_entity.surface.create_entity{name=entity_name, | 
					
						
							|  |  |  |             position=random_offset_position(current_entity.position, 128), | 
					
						
							|  |  |  |             target=target, speed=speed} | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2024-11-11 11:43:16 +01:00
										 |  |  | end | 
					
						
							| 
									
										
										
										
											2025-01-26 22:14:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-20 00:17:19 +01:00
										 |  |  | local teleport_requests = {} | 
					
						
							|  |  |  | local teleport_attempts = {} | 
					
						
							|  |  |  | local max_attempts = 100 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function attempt_teleport_player(player, attempt) | 
					
						
							|  |  |  |     -- global attempt storage as metadata can't be stored | 
					
						
							|  |  |  |     if attempt == nil then | 
					
						
							|  |  |  |         attempt = teleport_attempts[player.index] | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         teleport_attempts[player.index] = attempt | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if attempt > max_attempts then | 
					
						
							|  |  |  |         player.print("Teleport failed: No valid position found after " .. max_attempts .. " attempts!") | 
					
						
							|  |  |  |         teleport_attempts[player.index] = 0 | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     local surface = player.character.surface | 
					
						
							|  |  |  |     local prototype_name = player.character.prototype.name | 
					
						
							|  |  |  |     local original_position = player.character.position | 
					
						
							|  |  |  |     local candidate_position = random_offset_position(original_position, 1024) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     local non_colliding_position = surface.find_non_colliding_position( | 
					
						
							|  |  |  |         prototype_name, candidate_position, 0, 1 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if non_colliding_position then | 
					
						
							|  |  |  |         -- Request pathfinding asynchronously | 
					
						
							|  |  |  |         local path_id = surface.request_path{ | 
					
						
							|  |  |  |             bounding_box = player.character.prototype.collision_box, | 
					
						
							|  |  |  |             collision_mask = { layers = { ["player"] = true } }, | 
					
						
							|  |  |  |             start = original_position, | 
					
						
							|  |  |  |             goal = non_colliding_position, | 
					
						
							|  |  |  |             force = player.force.name, | 
					
						
							|  |  |  |             radius = 1, | 
					
						
							|  |  |  |             pathfind_flags = {cache = true, low_priority = true, allow_paths_through_own_entities = true}, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         -- Store the request with the player index as the key | 
					
						
							|  |  |  |         teleport_requests[player.index] = path_id | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         attempt_teleport_player(player, attempt + 1) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function handle_teleport_attempt(event) | 
					
						
							|  |  |  |     for player_index, path_id in pairs(teleport_requests) do | 
					
						
							|  |  |  |         -- Check if the event matches the stored path_id | 
					
						
							|  |  |  |         if path_id == event.id then | 
					
						
							|  |  |  |             local player = game.players[player_index] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if event.path then | 
					
						
							|  |  |  |                 if player.character then | 
					
						
							|  |  |  |                     player.character.teleport(event.path[#event.path].position)  -- Teleport to the last point in the path | 
					
						
							|  |  |  |                     -- Clear the attempts for this player | 
					
						
							|  |  |  |                     teleport_attempts[player_index] = 0 | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             attempt_teleport_player(player, nil) | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end | 
					
						
							| 
									
										
										
										
											2025-01-26 22:14:39 +01:00
										 |  |  | function spill_character_inventory(character) | 
					
						
							|  |  |  |     if not (character and character.valid) then | 
					
						
							|  |  |  |         return false | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     -- grab attrs once pre-loop | 
					
						
							|  |  |  |     local position = character.position | 
					
						
							|  |  |  |     local surface = character.surface | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     local inventories_to_spill = { | 
					
						
							|  |  |  |         defines.inventory.character_main, -- Main inventory | 
					
						
							|  |  |  |         defines.inventory.character_trash, -- Logistic trash slots | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for _, inventory_type in pairs(inventories_to_spill) do | 
					
						
							|  |  |  |         local inventory = character.get_inventory(inventory_type) | 
					
						
							|  |  |  |         if inventory and inventory.valid then | 
					
						
							|  |  |  |             -- Spill each item stack onto the ground | 
					
						
							|  |  |  |             for i = 1, #inventory do | 
					
						
							|  |  |  |                 local stack = inventory[i] | 
					
						
							|  |  |  |                 if stack and stack.valid_for_read then | 
					
						
							|  |  |  |                     local spilled_items = surface.spill_item_stack{ | 
					
						
							|  |  |  |                         position = position, | 
					
						
							|  |  |  |                         stack = stack, | 
					
						
							|  |  |  |                         enable_looted = false, -- do not mark for auto-pickup | 
					
						
							|  |  |  |                         force = nil, -- do not mark for auto-deconstruction | 
					
						
							|  |  |  |                         allow_belts = true, -- do mark for putting it onto belts | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     if #spilled_items > 0 then | 
					
						
							|  |  |  |                         stack.clear() -- only delete if spilled successfully | 
					
						
							|  |  |  |                     end | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end |