
(double-sided wall behind the demon head)
This way the only things that are moved are the entrance door and the wall texts.

Code: Select all
onThink = function(self)
if self.go.x == party.x then
self:turnTowardsParty()
if math.abs(self.go.y - party.y) <= 1 then self:performAction("basicAttack")
else self:performAction("charge") end
elseif self.go.y == party.y then
self:turnTowardsParty()
if math.abs(self.go.x - party.x) <= 1 then self:performAction("basicAttack")
else self:performAction("charge") end
else self:moveTowardsParty() end
return true
end,Code: Select all
mt_oni_1.brain:disable()Code: Select all
{
class = "SwarmBrain",
name = "brain",
sight = 5,
allAroundSight = true,
onThink = function(self)
if self.go.level == party.level and self.go.x == party.x and self.go.y == party.y and self.go.elevation == party.elevation then
return self:performAction("basicAttack")
else
return self:wanderIfPartyNotDetected() or self:pursuit()
end
end,
},Code: Select all
{
class = "UggardianBrain",
name = "brain",
sight = 6,
morale = 100,
seeInvisible = true,
allAroundSight = true, -- so that using sidesAttack makes sense
-- Very simple behaviour modification, just use the new attacks when the party is in a good position to
-- get hit by them.
onThink = function(self)
if self.seesParty and party.level == self.go.level and party.elevation == self.go.elevation
and self.go.map:checkLineOfFire(self.go.x,self.go.y,party.x,party.y,self.go.elevation) then
if (self.partyLeft or self.partyRight) and not (self.partyAhead or self.partyBehind) then
-- cannot use self:turnAttack() to autodetect facing as it only succeeds if party is adjacent
return math.random() < 0.5 and self:performAction("turnAttack",self.partyRight and 1 or -1) or self:performAction("sidesAttack") or self:performAction("turnAttack",self.partyRight and 1 or -1)
-- When either frontal attack could conceivably hit the party, don't favor double attack
-- Note that if double attack fails and ranged attack could hit, builtin brain will do
-- ranged attack
elseif self.partyStraightAhead and math.random() < 0.5 and self:performAction("rangedAttack") then
return true
-- Only use double attack when there's ample space for it and the party is not too close
elseif self.partyAhead and self.partyDistY > 1 and math.abs(self.partyDistX) <= 1 then
local xAxis = self.go.facing%2==0
if xAxis then
if self.go.map:checkLineOfFire(self.go.x+1,self.go.y,self.go.x+1,party.y,self.go.elevation)
and self.go.map:checkLineOfFire(self.go.x-1,self.go.y,self.go.x-1,party.y,self.go.elevation) then
return self:performAction("doubleAttack")
end
else
if self.go.map:checkLineOfFire(self.go.x,self.go.y+1,party.x,self.go.y+1,self.go.elevation)
and self.go.map:checkLineOfFire(self.go.x,self.go.y-1,party.x,self.go.y-1,self.go.elevation) then
return self:performAction("doubleAttack")
end
end
end
end
return false
end,
},Code: Select all
{ -- Note: we use performAction("rangedattack") instead of rangedAttack()
-- because rangedAttack() only shoots if the party is in front of the monster
-- and we want to fire regardless of the party's direction
-- XXX: This could be cleaned up a lot, make some local functions?
-- XXX: This sight radius is exceptionally long
class = "RangedBrain",
name = "brain",
sight = 8,
allAroundSight = true,
morale = 100,
seeInvisible = true,
onThink = function(self)
if self.seesParty then
-- If X blast could hit party, do it, since it's our
-- signature move
if self.partyDiagonal and self:performAction("xBlast") then
return true
elseif self.partyAdjacent then
local free = {}
if not self.blockedFront then
table.insert(free,0)
end
if not self.blockedLeft then
table.insert(free,3)
end
if not self.blockedRight then
table.insert(free,1)
end
if not self.blockedBack then
table.insert(free,2)
end
-- Run away, because artillery is not good in melee.
-- We don't use flee() here because it would turn, which
-- is a waste of time on this monster.
-- ...but attack occasionally.
if (#free==0 or math.random() < 0.15) and self:performAction("rangedAttack") then
return true
elseif #free ~= 0 then
local choice = free[math.random(#free)]
if choice == 0 then
return self:moveForward() or self:wander()
elseif choice == 1 then
return self:strafeRight() or self:wander()
elseif choice == 2 then
return self:moveBackward() or self:wander()
else -- 3
return self:strafeLeft() or self:wander()
end
else
return self:wander() -- turn uselessly like a dork
end
elseif self.partyDistY == 0 or self.partyDistX == 0 then
return self.go.map:checkLineOfFire(self.go.x,self.go.y,party.x,party.y,self.go.elevation) and self:performAction("rangedAttack") or self:wander()
else
-- Let the builtin AI approach sometimes, otherwise there are situations where the party can be completely safe by staying on one square
if math.random() < 0.2 then
return false
end
-- Try to line up for a ranged attack.
local x
if math.abs(self.partyDistY) == math.abs(self.partyDistX) then
x = math.random() < 0.5
else
x = (math.abs(self.partyDistY) > math.abs(self.partyDistX))
end
if x then
if self.partyDistX > 0 then
if (not self.blockedRight) and self:strafeRight() then return true end
else
if (not self.blockedLeft) and self:strafeLeft() then return true end
end
if self.partyDistY > 0 then
if (not self.blockedBack) and self:moveBackward() then return true end
elseif self.partyDistY < 0 then
if (not self.blockedFront) and self:moveForward() then return true end
end
else
if self.partyDistY > 0 then
if (not self.blockedBack) and self:moveBackward() then return true end
else
if (not self.blockedFront) and self:moveForward() then return true end
end
if self.partyDistX > 0 then
if (not self.blockedRight) and self:strafeRight() then return true end
else
if (not self.blockedLeft) and self:strafeLeft() then return true end
end
end
end
end
return self:wander()
end,
},Yes but if you return true without performing any action successfully, the brain will think again on the next frame, it's standard for brains to wait() instead.Jgwman wrote:I did intentionally have the brain never fall back on basic behavior, as it seemed pointless if I implemented my AI correctly (in my case; I can see why it'd be useful), which was why I was returning true in all cases.