here is the first release of the AI switcher : http://grimrock.nexusmods.com/mods/254/
Demo in this post from Leki: viewtopic.php?f=14&t=5230&start=100#p58291
Sample dungeon contained in zip, with web and poison shooting spider - really almost the same AI code of Leki ancient ghoul, put on a spider to give you an example
Sorry but now I'm in a hurry, later today I'll update this post with more details I wanted to release it, however, as I'm not so sure I'm able to do it later
Readme.txt dumped here in a hurry:
SETUP
=====
0) unzip or copy the ai folder in your mod directory.
IF YOU'RE USING LOG FRAMEWORK OR GRIMWIDGETS:
--------------------------------------------
1) You must be on the latest version of Log Framework
2) In init.lua, immediately *after* the import of grimwidgets/framework line add:
import "mod_assets/ai/aiswitcher.lua"
IF YOU'RE NOT USING LOG FRAMEWORK OR GRIMWIDGETS:
-------------------------------------------------
1) At the beginning of init.lua, immediately after either the 'import "assets/scripts/standard_assets.lua"' line add:
import "mod_assets/ai/aiswitcher.lua"
2) After this, add a scripting entity to your dungeon with this code:
spawn("aiswitcher_engine", 1, 1, 1, 1):open()
Either way... You're Done
DEFINING MONSTERS
=================
To define a monster for AI switching, use defineAiSwitchMonster instead of defineObject.
You need to change the monster definition this way:
1) All hooks must be strings containing code, instead of functions. In 99% of the cases you just need to add [[ before the code and ]] after (but before the comma).
2) You need a new "brains" subtable which will contain the alternate definitions. You can override every property in brains (as a handy helper, if you override the animations, these will be merged with the original definition).
3) Each element of the "brains" subtable must contain a "condition". Condion can be a function in string form or a table of <condition-name>, <minvalue>, <maxvalue>. condition-name can as now be "distance" to check the distance of the monster to the party, "directdistance", which checks the distance but only if the monster is in the same row or column of the party, and "health" which checks the monster health. If a function in string form is specified, the function receives the monster as an argument and should return true when the condition is satisfied.
4) Elements of "brains" are always checked in sequence (so the first satisfied condition wins). If no condition is satisfied, the default is the brain of the monster (the one outside the brains table).
5) Do NOT add items to monsters with AI switch using the standard editor, as those are lost after an AI change.
EXTRA-HOOKS
===========
A couple of extra hooks are provided for the modder convenience at the monster declaration level:
- onAiSwitch(self, oldindex, newindex): Happens when the AI is about to be switched. oldindex and newindex are indices in the brains array, with 0 being the one of the outer (default) brain.
- onPartyProjectileHit: same as party.onProjectileHit. This is included here, as a lot of monster customizations involve
shooting a projectile as a form of attack
Example:
Code: Select all
defineAiSwitchMonster{
name = "spider",
class = "Monster",
model = "assets/models/monsters/spider.fbx",
meshName = "spider_mesh",
animations = {
idle = "assets/animations/monsters/spider/spider_idle.fbx",
moveForward = "assets/animations/monsters/spider/spider_walk.fbx",
turnLeft = "assets/animations/monsters/spider/spider_turn_left.fbx",
turnRight = "assets/animations/monsters/spider/spider_turn_right.fbx",
attack = "assets/animations/monsters/spider/spider_bite.fbx",
getHitFrontLeft = "assets/animations/monsters/spider/spider_get_hit_front_left.fbx",
getHitFrontRight = "assets/animations/monsters/spider/spider_get_hit_front_right.fbx",
getHitBack = "assets/animations/monsters/spider/spider_get_hit_back.fbx",
getHitLeft = "assets/animations/monsters/spider/spider_get_hit_left.fbx",
getHitRight = "assets/animations/monsters/spider/spider_get_hit_right.fbx",
fall = "assets/animations/monsters/spider/spider_get_hit_front_left.fbx",
},
moveSound = "spider_walk",
attackSound = "spider_attack",
hitSound = "spider_hit",
dieSound = "spider_die",
hitEffect = "hit_goo",
capsuleHeight = 0.2,
capsuleRadius = 0.8,
health = 160,
sight = 6,
attackPower = 23,
accuracy = 10,
movementCoolDown = 1,
noRecoilInterval = { 0.1, 0.4 },
exp = 175,
healthIncrement = 30,
attackPowerIncrement = 5,
brain = "Melee",
onDealDamage = [[function(self, champion, damage)
if math.random() <= 0.3 then
champion:setConditionCumulative("poison", 30)
end
end]],
onDamage = [[function(self, amount, typed)
if (typed == "poison") then
return false
end
end]],
brains =
{
{ -- will change to this brain when the monster is in line with the party, with distance equal to 2
condition = { "directdistance", 2, 2 },
animations = { attack = "mod_assets/animations/spider_shoot.fbx" },
rangedAttack = "poison_bolt",
brain = "Ranged",
-- override ranged attack for this monster! Note the hook is a string
onRangedAttack = [[function(self)
local x, z = getForward(self.facing)
shootProjectile("spider_web", self.level, self.x, self.y, self.facing, 9.0, 7.0, 0.1, -x, 0.7, z, 10, self, true);
return false;
end]],
onPartyProjectileHit = [[function(champ, proj, damage, damtype)
if (proj.name == "spider_web") then
champ:setConditionCumulative("paralyzed", 60);
end
end]],
},
{ -- will change to this brain when the monster is distance 2 to infinite from the party
condition = { "distance", 2, 99999 },
animations = { attack = "mod_assets/animations/spider_shoot.fbx" },
rangedAttack = "poison_bolt",
brain = "Ranged",
},
},
}
defineAnimationEvent{
animation = "mod_assets/animations/spider_shoot.fbx",
event = "ranged_attack",
frame = 12,
}
cloneObject {
name = "spider_web",
baseObject = "rock",
-- change the model here!
}