Behavior Tree
In RPGs, we can often see a number of monsters gathering around their territory and wandering around, and in the middle of the territory there is usually a boss or leader monster that is protected by all the other monsters. So how does this work? Here is a simple example that may give you some insight into how this works.
Brief description
- when the monster is low on blood, it will automatically run to the boss
- When the boss doesn't attack, it will restore its own blood level first
- If the boss is at a healthy level, it will restore the unit with the lowest blood level among the minions
- when the player does not enter the boss's territory, the monsters will patrol the territory continuously
- when the player enters the territory, the monsters automatically attack the player
- when the player leaves the territory, the monsters return to their constant patrol status
Code
Methods
do
local playerId = 1
local unitLead -- lead actor
local unitHealer -- healer monster
--[[
Function
--]]
-- Game initialisation
function gameInit()
unitLead = cli.actor_unit(7)
unitHealer = cli.actor_unit(6)
local player = cli.player(playerId)
player:camera_focus(unitLead)
player:select(unitLead)
msgPro()
end
-- Message Processor
function msgPro()
local player = cli.player(31)
local monsterUnitGroup = player._base.get_all_unit_id() -- get player owner's all unit's id
local guardKey = 134230223 -- unit guard key
local healerKey = 134257924 -- unit healer key
cli.loop(2, function(timer)
monsterUnitGroup = player._base.get_all_unit_id() -- refresh hostile group
for _, v in Python.enumerate(monsterUnitGroup) do
local unitTemp = cli.actor_unit(v)
if unitTemp:get_key() == guardKey then
guardActionAI(unitTemp)
end
if unitTemp:get_key() == healerKey then
healerActionAI(unitTemp)
end
end
end)
end
-- Behaviour AI of the Near Guard
function guardActionAI(unitGuard)
local unitHealerPoint = unitHealer:get_point()
local unitLeadPoint = unitLead:get_point()
local distance = unitHealerPoint * unitLeadPoint
if distance >= 300 then
local curGuardHp = unitGuard:get("hp_cur") -- get real current HP property
local maxGuardHp = unitGuard:get("hp_max") -- get real max HP property
if curGuardHp > 0.5 * maxGuardHp then
local torritoryCenterPoint = cli.actor_point(1000000002)
local torritoryRange = 800
if torritoryCenterPoint * unitLeadPoint < torritoryRange then
unitGuard:attack(unitLead)
else
local randAngle = math.random() * 360
local randRadius = math.random() * torritoryRange
local x, y = torritoryCenterPoint:offset(randAngle, randRadius * 100):get()
local randTargetPoint = cli.point(x / 100, y / 100, 0)
unitGuard:move(randTargetPoint)
end
else
unitGuard:move(unitHealer:get_point()) -- make guard run to healer's position
end
else
unitGuard:attack(unitHealer:get_point())
end
end
-- Change unit blood volume
local function changeHp(unit, Hp)
if Hp == 0 then return end
local lowestHpUnitCurHp = unit:get("hp_cur")
local lowestHpUnitMaxHp = unit:get("hp_max")
local lowestHpUnitHpTemp = (lowestHpUnitCurHp + Hp) > lowestHpUnitMaxHp and lowestHpUnitMaxHp or (lowestHpUnitCurHp + Hp)
unit:set("hp_cur", lowestHpUnitHpTemp)
local lowestHpUnitPoint = unit:get_point()
local harmTextType
if Hp > 0 then
harmTextType = "Recover"
else
harmTextType = "Phy"
end
Hp = Hp >> 31 == 0 and Hp or ~Hp + 1 -- get abs
if not unit:is_alive() then return end
cli.create_harm_text(lowestHpUnitPoint, harmTextType, tostring(Hp))
end
-- Behavioural AI for treatment soldiers
function healerActionAI(unitHealer)
local curHealerHp = unitHealer:get("hp_cur")
local maxHealerHp = unitHealer:get("hp_max")
if maxHealerHp == curHealerHp then
local lowestHpUnit = getLowestHpUnit()
if not lowestHpUnit then
local torritoryCenterPoint = cli.actor_point(1000000002)
local unitLeadPoint = unitLead:get_point()
if torritoryCenterPoint * unitLeadPoint <= 200 then
unitHealer:attack(unitLead)
else
unitHealer:move(torritoryCenterPoint)
end
else
unitHealer:stop()
local direction = lowestHpUnit:get_point() / unitHealer:get_point() -- Calculate direction
unitHealer:set_facing(direction, 0)
changeHp(lowestHpUnit, 20)
unitHealer:add_animation({
name = "ability_30001",
speed = 1,
init_time = 0,
end_time = -1,
loop = false
}) -- Casting action
end
else
unitHealer:stop()
changeHp(unitHealer, 20)
unitHealer:add_animation({
name = "ability_30001",
speed = 1,
init_time = 0,
end_time = -1,
loop = false
}) -- Casting action
end
end
-- Get the friendly unit with the lowest blood count
function getLowestHpUnit()
local lowestHpUnit = nil
local monsterUnitGroup = cli.player(31)._base.get_all_unit_id()
local lowestHpPercentage = 1
for _, v in Python.enumerate(monsterUnitGroup) do
local lowestHpUnitTemp = cli.actor_unit(v)
local lowestHpUnitCurHp = lowestHpUnitTemp:get("hp_cur")
local lowestHpUnitMaxHp = lowestHpUnitTemp:get("hp_max")
if lowestHpUnitCurHp / lowestHpUnitMaxHp < lowestHpPercentage then
lowestHpPercentage = lowestHpUnitCurHp / lowestHpUnitMaxHp
lowestHpUnit = lowestHpUnitTemp
end
end
return lowestHpUnit
end
Event
cli.game:event("Game-Init", gameInit)
end
Download
link unvailable for now.