From eaf20f1158e155b0f7976a9752ae0274f464f253 Mon Sep 17 00:00:00 2001 From: Gerkiz Date: Sat, 2 Oct 2021 21:04:15 +0200 Subject: [PATCH] mtn v3 - fix of broken core feature --- maps/mountain_fortress_v3/locomotive.lua | 57 ++- maps/planet_prison.lua | 6 +- maps/planet_prison/mod/ai.lua | 210 ----------- utils/ai.lua | 346 +++++++++++++++++++ {maps/planet_prison/mod => utils}/common.lua | 10 +- {maps/planet_prison/mod => utils}/timers.lua | 0 6 files changed, 407 insertions(+), 222 deletions(-) delete mode 100644 maps/planet_prison/mod/ai.lua create mode 100644 utils/ai.lua rename {maps/planet_prison/mod => utils}/common.lua (98%) rename {maps/planet_prison/mod => utils}/timers.lua (100%) diff --git a/maps/mountain_fortress_v3/locomotive.lua b/maps/mountain_fortress_v3/locomotive.lua index 10d9296e..534a53a9 100644 --- a/maps/mountain_fortress_v3/locomotive.lua +++ b/maps/mountain_fortress_v3/locomotive.lua @@ -18,6 +18,7 @@ local Task = require 'utils.task' local Token = require 'utils.token' local MapFunctions = require 'tools.map_functions' local SpamProtection = require 'utils.spam_protection' +local AI = require 'utils.ai' local format_number = require 'util'.format_number @@ -33,6 +34,13 @@ local sin = math.sin local cos = math.cos local ceil = math.ceil +local clear_items_upon_surface_entry = { + ['small-electric-pole'] = true, + ['medium-electric-pole'] = true, + ['big-electric-pole'] = true, + ['substation'] = true +} + local shopkeeper = '[color=blue]Shopkeeper:[/color]\n' local space = { @@ -1450,10 +1458,18 @@ local function spawn_biter() local position = loco_surface.find_non_colliding_position('market', center_position, 128, 0.5) local biters = { + 'character', + 'small-biter', + 'medium-biter', 'big-biter', 'behemoth-biter', + 'character', + 'small-spitter', + 'medium-spitter', 'big-spitter', - 'behemoth-spitter' + 'behemoth-spitter', + 'compilatron', + 'character' } local size_of = #biters @@ -1461,9 +1477,21 @@ local function spawn_biter() if not position then return end - this.locomotive_biter = loco_surface.create_entity({name = biters[random(1, size_of)], position = position, force = 'player', create_build_effect_smoke = false}) - this.locomotive_biter.ai_settings.allow_destroy_when_commands_fail = false - this.locomotive_biter.ai_settings.allow_try_return_to_spawner = false + + local chosen_ent = biters[random(1, size_of)] + + if chosen_ent == 'character' then + local data = { + force = 'player', + surface = loco_surface.index, + command = 1, + tick = 60, + repeat_function = true + } + AI.add_job_to_task(data) + end + + this.locomotive_biter = loco_surface.create_entity({name = chosen_ent, position = position, force = 'player', create_build_effect_smoke = false}) rendering.draw_text { text = ({'locomotive.shoo'}), @@ -1476,6 +1504,11 @@ local function spawn_biter() alignment = 'center', scale_with_zoom = false } + + if not chosen_ent == 'character' then + this.locomotive_biter.ai_settings.allow_destroy_when_commands_fail = false + this.locomotive_biter.ai_settings.allow_try_return_to_spawner = false + end end local function create_market(data, rebuild) @@ -1852,7 +1885,12 @@ local function shoo(event) } ) if locomotive_biter and locomotive_biter.valid then - locomotive_biter.die() + local explosion = { + name = 'massive-explosion', + position = locomotive_biter.position + } + surface.create_entity(explosion) + locomotive_biter.destroy() WPT.set().locomotive_biter = nil end return @@ -1879,6 +1917,15 @@ local function on_player_changed_surface(event) return end + local item = player.cursor_stack + if item and item.valid_for_read then + local name = item.name + if clear_items_upon_surface_entry[name] then + player.cursor_stack.clear() + end + end + + if player.surface.name == 'nauvis' then local pos = surface.find_non_colliding_position('character', game.forces.player.get_spawn_position(surface), 3, 0, 5) if pos then diff --git a/maps/planet_prison.lua b/maps/planet_prison.lua index ae282020..b21fe643 100644 --- a/maps/planet_prison.lua +++ b/maps/planet_prison.lua @@ -6,12 +6,12 @@ local Session = require 'utils.datastore.session_data' local Event = require 'utils.event' local Server = require 'utils.server' local MapFuntions = require 'tools.map_functions' -local CommonFunctions = require 'maps.planet_prison.mod.common' +local CommonFunctions = require 'utils.common' local LayersFunctions = require 'maps.planet_prison.mod.layers' -local AIFunctions = require 'maps.planet_prison.mod.ai' +local AIFunctions = require 'utils.ai' local Blueprints = require 'maps.planet_prison.mod.bp' local AfkFunctions = require 'maps.planet_prison.mod.afk' -local Timers = require 'maps.planet_prison.mod.timers' +local Timers = require 'utils.timers' local ClaimsFunctions = require 'maps.planet_prison.mod.claims' local MapConfig = require 'maps.planet_prison.config' local Token = require 'utils.token' diff --git a/maps/planet_prison/mod/ai.lua b/maps/planet_prison/mod/ai.lua deleted file mode 100644 index 38dbcbe8..00000000 --- a/maps/planet_prison/mod/ai.lua +++ /dev/null @@ -1,210 +0,0 @@ -local CommonFunctions = require 'maps.planet_prison.mod.common' - -local Public = {} -local remove = table.remove - -Public.command = { - --[[ - @param args nil - --]] - noop = 0, - --[[ - @param args nil - --]] - seek_and_destroy_player = 1, - --[[ - @param args = { - agents, // All movable agents - positions, // Table of positions to attack - } - --]] - attack_objects = 2 -} - -local function _get_direction(src, dest) - local src_x = CommonFunctions.get_axis(src, 'x') - local src_y = CommonFunctions.get_axis(src, 'y') - local dest_x = CommonFunctions.get_axis(dest, 'x') - local dest_y = CommonFunctions.get_axis(dest, 'y') - - local step = { - x = nil, - y = nil - } - - local precision = CommonFunctions.rand_range(1, 10) - if dest_x - precision > src_x then - step.x = 1 - elseif dest_x < src_x - precision then - step.x = -1 - else - step.x = 0 - end - - if dest_y - precision > src_y then - step.y = 1 - elseif dest_y < src_y - precision then - step.y = -1 - else - step.y = 0 - end - - return CommonFunctions.direction_lookup[step.x][step.y] -end - -local function _move_to(ent, trgt, min_distance) - local state = { - walking = false - } - - local distance = CommonFunctions.get_distance(trgt.position, ent.position) - if min_distance < distance then - local dir = _get_direction(ent.position, trgt.position) - if dir then - state = { - walking = true, - direction = dir - } - end - end - - ent.walking_state = state - return state.walking -end - -local function refill_ammo(ent) - if not ent or not ent.valid then - return - end - local weapon = ent.get_inventory(defines.inventory.character_guns)[ent.selected_gun_index] - if weapon and weapon.valid_for_read then - local selected_ammo = ent.get_inventory(defines.inventory.character_ammo)[ent.selected_gun_index] - if selected_ammo then - if not selected_ammo.valid_for_read then - if weapon.name == 'shotgun' then - ent.insert({name = 'shotgun-shell', count = 20}) - end - if weapon.name == 'pistol' then - ent.insert({name = 'firearm-magazine', count = 20}) - end - end - end - end -end - -local function _shoot_at(ent, trgt) - ent.shooting_state = { - state = defines.shooting.shooting_enemies, - position = trgt.position - } -end - -local function _shoot_stop(ent) - ent.shooting_state = { - state = defines.shooting.not_shooting, - position = {0, 0} - } -end - -local function _do_job_seek_and_destroy_player(surf) - for _, player in pairs(game.players) do - if player and player.valid and player.character then - local search_info = { - name = 'character', - position = player.character.position, - radius = 20, - force = 'enemy' - } - - local ents = surf.find_entities_filtered(search_info) - if ents and #ents > 0 then - for _, e in pairs(ents) do - refill_ammo(e) - if not _move_to(e, player.character, CommonFunctions.rand_range(5, 10)) then - _shoot_at(e, player.character) - else - _shoot_stop(e) - end - end - end - end - end -end - -local function _do_job_attack_objects(surf, args) - local agents = args.agents - if #agents == 0 then - return - end - - local objects = args.objects - local target, closest, agent, query - for i = #agents, 1, -1 do - agent = agents[i] - if not agent.valid then - remove(agents, i) - goto continue - end - - if game.tick % i ~= 0 then - goto continue - end - - query = { - position = agent.position, - radius = 15, - type = { - 'projectile', - 'beam' - }, - force = { - 'enemy', - 'player', - 'neutral' - }, - invert = true - } - closest = surf.find_entities_filtered(query) - if #closest ~= 0 then - target = CommonFunctions.get_closest_neighbour(agent.position, closest) - else - if #objects == 0 then - _shoot_stop(agent) - goto continue - end - - target = CommonFunctions.get_closest_neighbour(agent.position, objects) - end - - if target == nil or not target.valid then - goto continue - end - - if not _move_to(agent, target, CommonFunctions.rand_range(5, 15)) then - _shoot_at(agent, target) - else - _shoot_stop(agent) - end - - ::continue:: - end -end - ---[[ -do_job - Perform non-stateful operation on all enemy "character" entities. -@param surf - LuaSurface, on which everything is happening. -@param command - Command to perform on all non-player controllable characters. ---]] -Public.do_job = function(surf, command, args) - if args == nil then - args = {} - end - - if command == Public.command.seek_and_destroy_player then - _do_job_seek_and_destroy_player(surf) - elseif command == Public.command.attack_objects then - _do_job_attack_objects(surf, args) - end -end - -return Public diff --git a/utils/ai.lua b/utils/ai.lua new file mode 100644 index 00000000..8244c487 --- /dev/null +++ b/utils/ai.lua @@ -0,0 +1,346 @@ +local Event = require 'utils.event' +local CommonFunctions = require 'utils.common' +local Token = require 'utils.token' +local Task = require 'utils.task' +local Global = require 'utils.global' + +local this = { + timers = {} +} + +Global.register( + this, + function(tbl) + this = tbl + end +) + +local Public = {} +local remove = table.remove + +Public.command = { + --[[ + @param args nil + --]] + noop = 0, + --[[ + @param args nil + --]] + seek_and_destroy_player = 1, + --[[ + @param args = { + agents, // All movable agents + positions, // Table of positions to attack + } + --]] + attack_objects = 2 +} + +local function _get_direction(src, dest) + local src_x = CommonFunctions.get_axis(src, 'x') + local src_y = CommonFunctions.get_axis(src, 'y') + local dest_x = CommonFunctions.get_axis(dest, 'x') + local dest_y = CommonFunctions.get_axis(dest, 'y') + + local step = { + x = nil, + y = nil + } + + local precision = CommonFunctions.rand_range(1, 10) + if dest_x - precision > src_x then + step.x = 1 + elseif dest_x < src_x - precision then + step.x = -1 + else + step.x = 0 + end + + if dest_y - precision > src_y then + step.y = 1 + elseif dest_y < src_y - precision then + step.y = -1 + else + step.y = 0 + end + + return CommonFunctions.direction_lookup[step.x][step.y] +end + +local function _move_to(ent, trgt, min_distance) + local state = { + walking = false + } + + local distance = CommonFunctions.get_distance(trgt.position, ent.position) + if min_distance < distance then + local dir = _get_direction(ent.position, trgt.position) + if dir then + state = { + walking = true, + direction = dir + } + end + end + + ent.walking_state = state + return state.walking +end + +local function refill_ammo(ent) + if not ent or not ent.valid then + return + end + local weapon = ent.get_inventory(defines.inventory.character_guns)[ent.selected_gun_index] + if weapon and weapon.valid_for_read then + local selected_ammo = ent.get_inventory(defines.inventory.character_ammo)[ent.selected_gun_index] + if selected_ammo then + if not selected_ammo.valid_for_read then + if weapon.name == 'shotgun' then + ent.insert({name = 'shotgun-shell', count = 5}) + end + if weapon.name == 'pistol' then + ent.insert({name = 'firearm-magazine', count = 5}) + end + end + end + end +end + +local function _shoot_at(ent, trgt) + ent.shooting_state = { + state = defines.shooting.shooting_selected, + position = trgt.position + } +end + +local function _shoot_stop(ent) + ent.shooting_state = { + state = defines.shooting.not_shooting, + position = {0, 0} + } +end + +local function set_noise_hostile_hook(ent) + if not ent or not ent.valid then + return + end + local weapon = ent.get_inventory(defines.inventory.character_guns)[ent.selected_gun_index] + if weapon and weapon.valid_for_read then + return + end + + if CommonFunctions.rand_range(1, 5) == 1 then + ent.insert({name = 'shotgun', count = 1}) + ent.insert({name = 'shotgun-shell', count = 5}) + end +end + +local function _do_job_seek_and_destroy_player(data) + local surf = data.surface + local force = data.force + local players = game.connected_players + + if type(surf) == 'number' then + surf = game.surfaces[surf] + if not surf or not surf.valid then + this.timers[game.tick] = nil + return + end + end + + for _, player in pairs(players) do + if player and player.valid and player.character then + local position = data.position or player.character.position + local search_info = { + name = 'character', + position = position, + radius = 30, + force = force or 'enemy' + } + + local ents = surf.find_entities_filtered(search_info) + if ents and #ents > 0 then + for _, e in pairs(ents) do + if e and e.valid then + if e.player == nil then + set_noise_hostile_hook(e) + refill_ammo(e) + if not _move_to(e, player.character, CommonFunctions.rand_range(5, 10)) then + _shoot_at(e, player.character) + else + _shoot_stop(e) + end + end + else + if data.repeat_function then + this.timers[data.new] = nil + end + end + end + else + if data.repeat_function then + this.timers[data.new] = nil + end + end + end + end +end + +local function _do_job_attack_objects(data) + local surf = data.surface + local force = data.force + local position = data.position + + if type(surf) == 'number' then + surf = game.surfaces[surf] + if not surf or not surf.valid then + if data.repeat_function then + this.timers[data.new] = nil + end + return + end + end + + local search_info = { + name = 'character', + position = position, + radius = 30, + force = force or 'enemy' + } + + local ents = surf.find_entities_filtered(search_info) + if ents and #ents > 0 then + local target, closest, agent, query + for i = #ents, 1, -1 do + agent = ents[i] + if not agent.valid then + remove(ents, i) + goto continue + end + + if game.tick % i ~= 0 then + goto continue + end + + query = { + position = agent.position, + radius = 15, + type = { + 'projectile', + 'beam' + }, + force = { + 'enemy', + 'player', + 'neutral' + }, + invert = true + } + closest = surf.find_entities_filtered(query) + if #closest ~= 0 then + target = CommonFunctions.get_closest_neighbour(agent.position, closest) + else + goto continue + end + + if target == nil or not target.valid then + goto continue + end + + if not _move_to(agent, target, CommonFunctions.rand_range(5, 15)) then + _shoot_at(agent, target) + else + _shoot_stop(agent) + end + + ::continue:: + end + else + if data.repeat_function then + this.timers[data.new] = nil + end + end +end + +local do_job_token + +do_job_token = + Token.register( + function(data) + local surf = data.surface + local command = data.command + + if type(surf) == 'number' then + surf = game.surfaces[surf] + if not surf or not surf.valid then + this.timers[game.tick] = nil + return + end + end + + if command == Public.command.seek_and_destroy_player then + _do_job_seek_and_destroy_player(data) + elseif command == Public.command.attack_objects then + _do_job_attack_objects(data) + end + end +) + +function Public.add_job_to_task(data) + if not type(data) == 'table' then + return + end + + if not data.tick then + return + end + + if not this.timers[game.tick + data.tick] then + this.timers[game.tick + data.tick] = data + end +end + +--[[ +do_job - Perform non-stateful operation on all chosen force "character" entities. +@param surf - LuaSurface, on which everything is happening. +@param command - Command to perform on all non-player controllable characters. +--]] +Public.do_job = function(surf, command, args, force) + if args == nil then + args = {} + end + + if command == Public.command.seek_and_destroy_player then + _do_job_seek_and_destroy_player(surf, force) + elseif command == Public.command.attack_objects then + _do_job_attack_objects(surf, args) + end +end + +Event.add( + defines.events.on_tick, + function() + local tick = game.tick + + if not this.timers[tick] then + return + end + local data = this.timers[tick] + if not data then + return + end + + if data.repeat_function then + this.timers[tick + data.tick] = data + this.timers[tick + data.tick].previous = tick + data.new = tick + data.tick + end + + Task.set_timeout_in_ticks(data.tick, do_job_token, data) + + this.timers[tick] = nil + end +) + +return Public diff --git a/maps/planet_prison/mod/common.lua b/utils/common.lua similarity index 98% rename from maps/planet_prison/mod/common.lua rename to utils/common.lua index c1d873d5..56bf1ed0 100644 --- a/maps/planet_prison/mod/common.lua +++ b/utils/common.lua @@ -542,10 +542,12 @@ Public.get_closest_neighbour = function(position, objects) local object, dist for i = #objects, 1, -1 do object = objects[i] - dist = Public.get_distance(position, object) - if dist < min_dist then - closest = object - min_dist = dist + if object and not object.player then + dist = Public.get_distance(position, object) + if dist < min_dist then + closest = object + min_dist = dist + end end end diff --git a/maps/planet_prison/mod/timers.lua b/utils/timers.lua similarity index 100% rename from maps/planet_prison/mod/timers.lua rename to utils/timers.lua