Allied AI Follow script Request or examples

Started by zutokaza, October 03, 2015, 03:03:10 AM

Previous topic - Next topic
Hello,

I want an allied AI to follow behind the main character and to fight just like the Enemy AIs when they encounter the hero.

The Enemy AI would have to detect the Allied AI the same way as they detect the hero. Also, some options for the enemies to attack decide on who to attack or randomly attack maybe.

Features for Allied AI:
-Follow behind the hero
-Attack enemies based on distance or an option for the hero to call the Allied AI to attack the Enemy Ai the hero is attacking.
-Random attack decision

Enemy AI feature:
-Able to attack the hero or Allied AI based on Distance or an option for the leader/boss to call the enemy AI to attack the hero/Allied AI the leader/boss is attacking.
-Random attack decision

It can be that I would just have to flip the Enemy script around, but some glitched on TeleTransporters might occur.
Solarus Works on ReactOS Opensource Windows OS

https://www.reactos.org/forum/viewtopic.php?f=2&t=14759&p=120616#p120616

I am currently working on exactly this.
However, my time is really limited during this part of the year.
I do have some code...
But it is still a WIP.
Things to look up on the Solarus API
Custom Entities...
They are the way to go.. However they do require some coding knowledge of Lua.

I'll have my code posted some time next year after my first dev cycle is over.
Good Luck...
Other key word topics on the forum are... companions, allies and AI.

Well, I guess I'll do it when I get the time. Thanks for the tips.
Solarus Works on ReactOS Opensource Windows OS

https://www.reactos.org/forum/viewtopic.php?f=2&t=14759&p=120616#p120616

If you want I'll post the code I have presently. But do remember it is a wip with some funky search methods that will be simplified.
No reason to start from zero!
Let me know and I'll attach it.
PG


Code Attached: This is the WIP for my General ALLY Code. With some coding goals. In the search code I identify an entity by name. However... this part of the code is archaic and "for entities" loops are a lot simpler. The collision technique is with link's sword so there are some built in collision detection with enemies.
local entity = ...
--Entity todo:...
-- PUT BEHAVIOR SCRIPT INTO A SEPARATE FILE...
-- PUT INTERACTION SCRIPT INTO A SEPARATE FILE...
-- update behavior script with variables for targeting #'s (default: 100)
-- local attitude = script with variables for targeting #'s (default: 100)
-- create local strength/health function...
-- create local morale function...
-- create global speed function...
-- game:set_value("p_" .. i .. "_distance_to_hero", entity_slots[i])

--local behavior = require("entities/lib/behavior")
--local interactions = require("entities/lib/interactions")?

-- behavior variables...
local game = entity:get_game()
local map = entity:get_map()
local m = sol.movement.create("target")
local direction = entity:get_direction()

local hero = game:get_map():get_entity("hero")
local hero_x, hero_y, hero_layer = hero:get_position()
local self_x, self_y, self_layer = entity:get_position()

local target_dt = nil
local target_dth = nil
local target_h = nil

local ally_dt = nil
local ally_dth = nil
local ally_h = nil

local rock_dt = nil
local rock_dth = nil
local rock_h = nil

-- BEHAVIOR VARIABLES...
local target_dt_distance = nil
local target_h_distance = nil
local target_dth_distance = nil
local target_a_distance = nil

local ally_dt_distance = nil
local ally_h_distance = nil
local ally_dth_distance = nil

local rock_dt_distance = nil
local rock_h_distance = nil
local rock_dth_distance = nil
local rock_a_distance = nil
-- BEHAVIOR ASSIGNMENTS
-- enemy behaviour sliders
target_dt_distance = 100
target_h_distance = 100
target_dth_distance = 100
target_a_distance = 100
-- ally behaviour sliders
ally_dt_distance = 100
ally_h_distance = 100
ally_dth_distance = 100
-- Projectile behaviour sliders
rock_dt_distance = 100
rock_h_distance = 100
rock_dth_distance = 100
rock_a_distance = 100



-- more on the list to be added later...


local going_enemy = false

local properties = {}
--local shield_sprite = nil
local weapon_sprite = nil

local towards_hero = false
local away_hero = false
local attacked_hero = false
local towards_enemy = false
local near_hero = false
local weapon_attack = nil
local current_xy = {}

local xspeed = 0
local yspeed = 0

local timer, enemy_check, shoot_timer, health_timer

function entity:set_properties(prop)
  properties = prop
  properties.life = 10
  properties.damage = 2
  properties.play_hero_seen_sound = false
  properties.stopped_speed = 0
  properties.normal_speed = 64
  properties.faster_speed = 128
  properties.weapon = "knife"
    -- can be knife, pistol, shotgun, grenade, ect...
  properties.regen = 0
end

function entity:on_created()
--  main_sprite =
  self:create_sprite("allies/a_butterfly")
  self:set_size(16, 16)
  self:set_origin(8, 13)
  x, y, layer = hero:get_position()
  self:set_position(x, y, layer)
  self:get_sprite():set_animation("walking")
--  shield_sprite = self:create_sprite(properties.shield_sprite)
end

function entity:on_restarted()
   --current_xy.x, current_xy.y = self:get_position()
end

--function entity:on_position_changed(x, y)
--  if weapon ~= nil then
--    local dx = x - current_xy.x
--    local dy = y - current_xy.y
--    local weapon_x, weapon_y = weapon:get_position()
--    weapon:set_position(weapon_x + dx, weapon_y + dy)
--  end
--  current_xy.x, current_xy.y = x, y
--end
   
-- PUT THIS BEHAVIOR SCRIPT INTO A SEPARATE FILE...
-- PUT INTERACTION SCRIPT INTO A SEPARATE FILE...
function entity:begin()
--  allies:create(game)
--  item:set_savegame_variable("e102")

timer = sol.timer.start(entity, 500, function()
-- Checks for the presence, and location of enemies and allies on the map in relationship to self, ally and hero. The first look...
-- ENEMY sight... turned into save game variables.
-- Naming System... e: enemy, p: projectile,
-- sw: switch, d: door, s: stairs, n: npc, pi: pickable, b: boss
  print ("INPUT BEGIN")
  print(target_dt)
  print(target_dth)
  print(ally_dt)
  print(ally_dth)
  local entity_slots = {}
  for i = 1, 25 do
    local hero = game:get_map():get_entity("hero")
    local hero_x, hero_y, hero_layer = hero:get_position()
    local self_x, self_y, self_layer = entity:get_position()
 
    local distance_hero = math.abs((hero_x+hero_y)-(self_x+self_y))

    local target = game:get_map():get_entity("e_" .. i .. "")
    local ally = game:get_map():get_entity("a_" .. i .. "")
    local rock = game:get_map():get_entity("p_" .. i .. "")
--    local door = game:get_map():get_entity("d_" .. i .. "")
--    local switch = game:get_map():get_entity("sw_" .. i .. "")
--    local npc = game:get_map():get_entity("n_" .. i .. "")
--    local pickable = game:get_map():get_entity("pi_" .. i .. "")
--    local stairs = game:get_map():get_entity("s_" .. i .. "")
--    local boss = game:get_map():get_entity("b_" .. i .. "")
--    local destructible = game:get_map():get_entity("de_" .. i .. "")

--    local sort_door = door:get_type()
--    local sort_switch = switch:get_type()
--    local sort_npc = npc:get_type()
--    local sort_pickable = pickable:get_type()
--    local sort_stairs = stairs:get_type()
--    local sort_destructible = destructible:get_type()
   
    if target ~= nil then
      entity_slots[i] = target
        local breed = target:get_breed()
        local t_x, t_y, t_layer  = target:get_position()
        local distance_target = math.abs((t_x+t_y)-(self_x+self_y))
        local distance_to_hero = math.abs((hero_x+hero_y)-(t_x+t_y))
        local distance_hero = math.abs((hero_x+hero_y)-(self_x+self_y))
      if distance_target <=  target_dt_distance then
        target_dt = ("e_" .. i .. "")
        print(target_dt)
        print("enemy_detected")
      elseif distance_to_hero <= target_dth_distance then
        target_dth = ("e_" .. i .. "")
        print(target_dth)
        print("enemy_attacking_hero")
      elseif distance_hero <= target_h_distance then
        target_dh = ("hero")
        print(target_dh)
        print("enemy regrouping_with_hero")     
      end  
      -- print("B_targeting_finished...")

    elseif ally ~= nil then
      local sort_ally = ally:get_type()
      local model = ally:get_model()
      local a_x, a_y, a_layer  = ally:get_position()
      local distance_ally = math.abs((a_x+a_y)-(self_x+self_y))
      local distance_to_hero = math.abs((hero_x+hero_y)-(a_x+a_y))
      local distance_hero = math.abs((hero_x+hero_y)-(self_x+self_y))
     
  if distance_ally <= ally_dt_distance then
        ally_dt = ("a_" .. i .. "")
        print(ally_dt)
        -- print("B_ally_targeting_dt")
      elseif distance_to_hero >= ally_dth_distance then
        ally_dth = ("a_" .. i .. "")
        print(ally_dth)
        -- print("B_ally_targeting_dth")
      elseif distance_hero <= ally_h_distance then
        ally_dh = ("hero")
        print(ally_dh)
        print("ally regrouping_with_hero")   
      end
     
elseif rock ~= nil then
      local sort_rock = rock:get_type()
      local p_x, p_y, p_layer  = rock:get_position()
      local distance_rock = math.abs((p_x+p_y)-(self_x+self_y))
      local distance_hero = math.abs((hero_x+hero_y)-(self_x+self_y))       
      local distance_to_hero = math.abs((hero_x+hero_y)-(p_x+p_y))
      if distance_rock <= rock_dt_distance then
        rock_dt = ("p_" .. i .. "")
        print(rock_dt)
        print("B_rock_targeting_dt")
      elseif distance_to_hero >= rock_dt_distance then
        rock_dth = ("p_" .. i .. "")
        print(rock_dth)
        print("B_rock_targeting_dth")
      elseif distance_hero <= rock_h_distance then
        target_dh = ("hero")
        print(rock_h_distance)
        print("regrouping_with_hero")   
      end
      -- print("B_rock_targeting_finished...")
   
    end
  end

  local hero = game:get_map():get_entity("hero")
  local hero_x, hero_y, hero_layer = hero:get_position()
  local npc_x, npc_y, npc_layer = entity:get_position()
  local distance_hero = math.abs((hero_x+hero_y)-(npc_x+npc_y))
   
  local m = sol.movement.create("target")
  local direction = entity:get_direction()
  local health = properties.life
 
  if map:has_entities("e_") then
  if target_dt ~= nil and target_dth == nil and distance_hero < 200 then
  local target_in_range = game:get_map():get_entity(target_dt)
    if target_in_range ~= nil then
    local tir_x, tir_y, tir_layer = target_in_range:get_position()
print("go_enemy")
  entity:go_enemy()
    end
  elseif target_dt ~= nil and target_dth ~= nil and distance_hero < 200 then
    local target_d_to_hero = game:get_map():get_entity(target_dth)
if target_d_to_hero ~= nil then
    local tdth_x, tdth_y, tdth_layer = target_d_to_hero:get_position()
    print("defend_hero")
    entity:defend_hero()
end
  elseif target_dt ~= nil and target_dth ~= nil and distance_hero > 100 then
      print("group_hero")
      entity:go_hero()
     
  elseif map:has_entities("a_") and ally_dt ~= nil and ally_dt ~= "a_1" then
    local ally_in_range = game:get_map():get_entity(ally_dt)
    if ally_in_range ~= nil then
    local air_x, air_y, air_layer = ally_in_range:get_position()
    print("go_ally")
    entity:go_ally()
    end
  elseif ally_dt ~= nil and ally_dth ~= nil then
    local ally_in_range = game:get_map():get_entity(ally_dt)
    if ally_in_range ~= nil then
    local air_x, air_y, air_layer = ally_in_range:get_position()
    print("go_ally")
    entity:go_ally()
    end
  elseif ally_dth ~= nil and target_dth == nil or ally_dth ~= nil and target_dt == nil then
  local ally_d_to_hero = game:get_map():get_entity(ally_dth)
  if ally_d_to_hero ~= nil then
    local adth_x, adth_y, adth_layer = ally_d_to_hero:get_position()
    print("go_hero")
    entity:go_hero()
    end
  elseif rock_dt ~= nil then
  local rock_in_range = game:get_map():get_entity(rock_dt)
    if rock_in_range ~= nil then
local rir_x, rir_y, rir_layer = rock_in_range:get_position()
print("go_dodge")
    entity:dodge()
end
  elseif rock_dth ~= nil and target_dth ~= nil then
  local rock_d_to_hero = game:get_map():get_entity(rock_dth)
    if rock_d_to_hero ~= nil then
local rdth_x, rdth_y, rdth_layer = rock_d_to_hero:get_position()
print("go_block")
    entity:block()
end
  else
    print("else__go_hero")
    entity:go_hero()
    end

  else
    print("NO ENEMIES...else__go_hero")
    entity:go_ally()
      print ("sort_ally")
  print (sort_ally)
    end
    return true
end)
end

function entity:go_hero()
  print("following hero...")
  local hero_x, hero_y, hero_layer = hero:get_position()
  local npc_x, npc_y, npc_layer = entity:get_position()
  local distance_hero = math.abs((hero_x+hero_y)-(npc_x+npc_y))
  if distance_hero > 64 then
  m:set_ignore_obstacles(false)
  m:set_target(hero_x, hero_y, hero_layer)
  m:set_speed(64)
  m:start(entity)
  entity:get_sprite():set_animation("walking")
  else
  m:set_ignore_obstacles(false)
  m:set_target(hero_x, hero_y, hero_layer)
  m:set_speed(0)
  m:start(entity)
  entity:get_sprite():set_animation("stopped")
  end
  going_enemy = false
  away_hero = false
  towards_hero = true
  near_hero = false 
  end

function entity:defend_hero()
  if target_dth ~= nil then
  local target_in_range = game:get_map():get_entity(target_dth)
  local tir_x, tir_y, tir_layer = target_in_range:get_position()
  local npc_x, npc_y, npc_layer = entity:get_position()
  local distance_enemy = math.abs((tir_x+tir_y)-(npc_x+npc_y))
    if distance_enemy > 24 then
  --FUNCTION: GO_ENEMY
  m:set_ignore_obstacles(false)
  m:set_target(tir_x, tir_y, tir_layer)
  m:set_speed(128)
  m:start(entity)
  entity:get_sprite():set_animation("walking")
  going_enemy = true
  away_hero = false
  towards_hero = false
  near_hero = false 
  print("Going enemy")
    else
  --FUNCTION: ATTACK
  weapon_attack = map:create_custom_entity{
  name = "w_knife",
  x = npc_x,
  y = npc_y,
  layer = npc_layer,
  direction = direction,
  sprite = "allies/w_knife",
  }
  self.created = true
  weapon_attack:set_position(npc_x, npc_y, npc_layer)
  print("attacking enemy")
  -- FUNCTION: WEAPON_POWER
  weapon_attack:add_collision_test("sprite", function(weapon_attack, other)
    if other:get_type() == "enemy" then
       other:hurt(8)
       weapon_attack:remove()
    else
       weapon_attack:remove()
    end
  end)
  print("sword swing...")
  --FUNCTION: STOPPED
  m:set_ignore_obstacles(false)
  m:set_target(tir_x, tir_y)
  m:set_speed(0)
  m:start(entity)
  entity:get_sprite():set_animation("stopped")
 
  going_enemy = false
  away_hero = false
  towards_hero = false
  near_enemy = true
  end

  end
end

function entity:go_enemy()
  if target_dt ~= nil and target_dth == nil then
  local target_in_range = game:get_map():get_entity(target_dt)
  local tir_x, tir_y, tir_layer = target_in_range:get_position()
  local npc_x, npc_y, npc_layer = entity:get_position()
  local distance_enemy = math.abs((tir_x+tir_y)-(npc_x+npc_y))
    if distance_enemy > 24 then
  --FUNCTION: GO_ENEMY
  m:set_ignore_obstacles(false)
  m:set_target(tir_x, tir_y, tir_layer)
  m:set_speed(128)
  m:start(entity)
  entity:get_sprite():set_animation("walking")
  going_enemy = true
  away_hero = false
  towards_hero = false
  near_hero = false 
  print("Going enemy")
    else
  --FUNCTION: ATTACK
  weapon_attack = map:create_custom_entity{
  name = "w_knife",
  x = npc_x,
  y = npc_y,
  layer = npc_layer,
  direction = direction,
  sprite = "allies/w_knife",
  }
  self.created = true
  weapon_attack:set_position(npc_x, npc_y, npc_layer)
  print("attacking enemy")
  -- FUNCTION: WEAPON_POWER
  weapon_attack:add_collision_test("sprite", function(weapon_attack, other)
    if other:get_type() == "enemy" then
       other:hurt(8)
       weapon_attack:remove()
    else
       weapon_attack:remove()
    end
  end)
  print("sword swing...")
  --FUNCTION: STOPPED
  m:set_ignore_obstacles(false)
  m:set_target(tir_x, tir_y)
  m:set_speed(0)
  m:start(entity)
  entity:get_sprite():set_animation("stopped")
 
  going_enemy = false
  away_hero = false
  towards_hero = false
  near_enemy = true
  end
  end

end

function entity:go_ally()
-- Buggy code... I have to differentiate between the allies... by identifying this ally. by getting their hud slot... it is much easier.
--  local air_x, air_y, air_layer = ally_dt:get_position()
  local npc_x, npc_y, npc_layer = entity:get_position()
--  local distance_ally = math.abs((air_x+air_y)-(npc_x+npc_y))

  --  if map:get_entities_count("a_") > 1  then
--  m:set_ignore_obstacles(false)
--  m:set_target(air_x + 32, air_y + 32, air_layer)
--  m:set_speed(64)
--  m:start(entity)
--  entity:get_sprite():set_animation("walking")
--  going_enemy = false
--  going_ally = true
--  away_hero = false
--  towards_hero = true
--  near_hero = false 
--  elseif map:get_entities_count("a_") < 2 then
  entity:go_hero()
  end
 
function entity:block()
  local rir_x, rir_y, rir_layer = rock_dt:get_position()
  local npc_x, npc_y, npc_layer = entity:get_position()
  local distance_enemy = math.abs((rir_x+rir_y)-(npc_x+npc_y))
  m:set_ignore_obstacles(false)
  m:set_target(rir_x, rir_y, rir_layer)
  m:set_speed(128)
  m:start(entity)
  entity:get_sprite():set_animation("walking")
  going_enemy = false
  going_ally = true
  away_hero = false
  towards_hero = true
  near_hero = false 

  end

function entity:dodge()
  local rdth_x, rdth_y, rdth_layer = rock_dth:get_position()
  local npc_x, npc_y, npc_layer = entity:get_position()
  local distance_enemy = math.abs((rdth_x+rdth_y)-(npc_x+npc_y))
  m:set_ignore_obstacles(false)
  m:set_target(rdth_x + 128, rdth_y + 128, rdth_layer)
  m:set_speed(128)
  m:start(entity)
  entity:get_sprite():set_animation("walking")
  going_enemy = false
  going_ally = true
  away_hero = false
  towards_hero = true
  near_hero = false 

  end 

function entity:hurt()
end

function entity:health()
end

function entity:on_created()
  self:set_drawn_in_y_order(true)
  self:set_can_traverse("hero", false)
  self:set_traversable_by("hero", true)
  entity:begin()
  end
 
-- Behavior function ends.
 
-- PUT INTERACTION SCRIPT INTO A SEPARATE FILE...

end
 
function entity:on_movement_changed(movement)
  local direction = movement:get_direction4()
  entity:get_sprite():set_direction(direction)
end



The other part is in the game_manager.lua insert in the

function game:on_map_changed(map)

  if game:get_value("a1010") and game:get_value("a1011")
  then
  ally2 = map:create_custom_entity{
      name = "a_2",
      x = x,
      y = x,
      layer = layer,
      direction = direction,
      sprite = "allies/a_pi",
      model = "ally_pi"
    }
  self.created = true
  ally2:set_position(x, y, layer)
  local ally2 = true
  local allynumber = 2
  end


Crude but it is a WIP.
Good luck.
PG