Skip to content
Snippets Groups Projects
Commit d4b056f4 authored by dg's avatar dg
Browse files

dumb talented AI

git-svn-id: http://svn.net-core.org/repos/t-engine4@141 51575b47-30f0-44d4-a5cc-537603b46e54
parent df531888
No related branches found
No related tags found
No related merge requests found
Showing
with 122 additions and 60 deletions
......@@ -52,7 +52,7 @@ function _M:computeRarities(type, list, level, ood, filter)
elseif lev > e.level_range[2] then max = 100 / (lev - e.level_range[2])
end
local genprob = max / e.rarity
print("prob", e.name, math.floor(genprob), "max", math.floor(max), e.level_range[1], e.level_range[2], lev, "egoable", e.egos and #e.egos)
print("prob", e.name, math.floor(genprob), "max", math.floor(max), e.level_range[1], e.level_range[2], lev, "egoable", e.egos)
-- Generate and store egos list if needed
if e.egos and not level:getEntitiesList(type.."/"..e.egos) then
......@@ -68,7 +68,12 @@ function _M:computeRarities(type, list, level, ood, filter)
end
end
table.sort(r, function(a, b) return a.genprob < b.genprob end)
print("*DONE", r.total)
for i, ee in ipairs(r) do
print(("entity chance %02d : chance(%04d): %s"):format(i, ee.genprob, ee.e.name))
end
return r
end
......
......@@ -10,7 +10,7 @@ newAI("move_simple", function(self)
end)
newAI("target_simple", function(self)
if self.ai_target.actor and not self.ai_target.actor.dead and rng.percent(90) then return end
if self.ai_target.actor and not self.ai_target.actor.dead and rng.percent(90) then return true end
-- Find closer ennemy and target it
-- Get list of actors ordered by distance
......@@ -40,6 +40,7 @@ newAI("target_player", function(self)
end)
newAI("simple", function(self)
self:runAI("target_simple")
self:runAI("move_simple")
if self:runAI("target_simple") then
self:runAI("move_simple")
end
end)
-- Defines AIs that can use talents, either smartly or "dumbly"
-- Randomly use talents
newAI("dumb_talented", function(self)
-- Find available talents
local avail = {}
local target_dist = core.fov.distance(self.x, self.y, self.ai_target.actor.x, self.ai_target.actor.y)
for i, tid in ipairs(self.talents) do
local t = self:getTalentFromId(tid)
if not self:isTalentCoolingDown(t) and target_dist <= self:getTalentRange(t) and self:preUseTalent(t, true) then
avail[#avail+1] = tid
print(self.name, self.uid, "dumb ai talents can use", t.name)
end
end
if #avail > 0 then
local tid = avail[rng.range(1, #avail)]
self:useTalent(tid)
end
end)
newAI("dumb_talented_simple", function(self)
if self:runAI("target_simple") then
-- One in "talent_in" chance of using a talent
if rng.chance(self.ai_state.talent_in or 6) and not self:runAI("dumb_talented") then
self:runAI("move_simple")
end
end
end)
......@@ -83,7 +83,7 @@ function _M:useTalent(id)
end
if not self:preUseTalent(ab) then return end
local co = coroutine.create(function()
local ret = ab.action(self)
local ret = ab.action(self, ab)
if not self:postUseTalent(ab, ret) then return end
......@@ -100,13 +100,13 @@ function _M:useTalent(id)
if not self:preUseTalent(ab) then return end
local co = coroutine.create(function()
if not self.sustain_talents[id] then
local ret = ab.activate(self)
local ret = ab.activate(self, ab)
if not self:postUseTalent(ab, ret) then return end
self.sustain_talents[id] = ret
else
local ret = ab.deactivate(self, self.sustain_talents[id])
local ret = ab.deactivate(self, ab, self.sustain_talents[id])
if not self:postUseTalent(ab, ret) then return end
......@@ -120,6 +120,7 @@ function _M:useTalent(id)
else
error("Activating non activable or sustainable talent: "..id.." :: "..ab.name.." :: "..ab.mode)
end
return true
end
--- Replace some markers in a string with info on the talent
......@@ -183,7 +184,7 @@ function _M:learnTalent(t_id, force)
end
end
if t.on_learn then t.on_learn(self) end
if t.on_learn then t.on_learn(self, t) end
self.talents[t_id] = true
self.changed = true
......@@ -200,7 +201,7 @@ function _M:unlearnTalent(t_id)
if known_t_id == t_id then self.hotkey[i] = nil end
end
if t.on_unlearn then t.on_unlearn(self) end
if t.on_unlearn then t.on_unlearn(self, t) end
self.talents[t_id] = nil
self.changed = true
......@@ -325,6 +326,13 @@ function _M:isTalentCoolingDown(t)
if self.talents_cd[t.id] and self.talents_cd[t.id] > 0 then return self.talents_cd[t.id] else return false end
end
--- Returns the range of a talent
function _M:getTalentRange(t)
if not t.range then return 1 end
if type(t.range) == "function" then return t.range(self, t) end
return t.range
end
--- Cooldown all talents by one
-- This should be called in your actors "act()" method
function _M:cooldownTalents()
......
......@@ -51,6 +51,12 @@ function _M:init(t, no_default)
t.mana_rating = t.mana_rating or 10
t.stamina_rating = t.stamina_rating or 10
-- Default regen
self.mana_regen = 0.5
self.stamina_regen = 0.5
self.life_regen = 0.1
self.life_rating = 10
-- Default melee barehanded damage
self.combat = { dam=1, atk=1, apr=0, dammod={str=1} }
......@@ -183,7 +189,7 @@ end
-- Check the actor can cast it
-- @param ab the talent (not the id, the table)
-- @return true to continue, false to stop
function _M:preUseTalent(ab)
function _M:preUseTalent(ab, silent)
if not self:enoughEnergy() then return end
if ab.mana and self:getMana() < ab.mana * (100 + self.fatigue) / 100 then
game.logPlayer(self, "You do not have enough mana to cast %s.", ab.name)
......@@ -194,16 +200,18 @@ function _M:preUseTalent(ab)
return
end
if ab.message then
game.logSeen(self, "%s", self:useTalentMessage(ab))
elseif ab.mode == "sustained" and not self:isTalentActive(ab.id) then
game.logSeen(self, "%s activates %s.", self.name:capitalize(), ab.name)
elseif ab.mode == "sustained" and self:isTalentActive(ab.id) then
game.logSeen(self, "%s deactivates %s.", self.name:capitalize(), ab.name)
elseif ab.type[1]:find("^spell/") then
game.logSeen(self, "%s casts %s.", self.name:capitalize(), ab.name)
else
game.logSeen(self, "%s uses %s.", self.name:capitalize(), ab.name)
if not silent then
if ab.message then
game.logSeen(self, "%s", self:useTalentMessage(ab))
elseif ab.mode == "sustained" and not self:isTalentActive(ab.id) then
game.logSeen(self, "%s activates %s.", self.name:capitalize(), ab.name)
elseif ab.mode == "sustained" and self:isTalentActive(ab.id) then
game.logSeen(self, "%s deactivates %s.", self.name:capitalize(), ab.name)
elseif ab.type[1]:find("^spell/") then
game.logSeen(self, "%s casts %s.", self.name:capitalize(), ab.name)
else
game.logSeen(self, "%s uses %s.", self.name:capitalize(), ab.name)
end
end
return true
end
......
......@@ -30,3 +30,9 @@ function _M:tooltip()
local str = mod.class.Actor.tooltip(self)
return str..("\nTarget: %s\nUID: %d"):format(self.ai_target.actor and self.ai_target.actor.name or "none", self.uid)
end
--- Tries to get a target from the NPC
-- This simple returns current AI target for NPCs
function _M:getTarget(typ)
return self.ai_target.actor.x, self.ai_target.actor.y
end
......@@ -30,11 +30,6 @@ function _M:init(t, no_default)
self.color_b=230
-- self.image="player.png"
-- Default regen
self.mana_regen = 0.5
self.stamina_regen = 0.5
self.life_regen = 0.1
self.life_rating = 10
self.fixed_rating = true
self.max_life=85
......
......@@ -6,11 +6,12 @@ newTalent{
tactical = {
ATTACKAREA = 10,
},
action = function(self)
range = 15,
action = function(self, t)
local duration = 5 + self:combatSpellpower(0.1)
local radius = 3
local dam = 4 + self:combatSpellpower(0.6)
local t = {type="ball", range=15, radius=radius}
local t = {type="ball", range=self:getTalentRange(t), radius=radius}
local x, y = self:getTarget(t)
if not x or not y then return nil end
x, y = game.target:pointAtRange(self.x, self.y, x, y, 15)
......
......@@ -6,8 +6,9 @@ newTalent{
tactical = {
ATTACK = 10,
},
action = function(self)
local t = {type="bolt", range=20}
range = 20,
action = function(self, t)
local t = {type="bolt", range=self:getTalentRange(t)}
if self:knowTalent(Talents.T_ARCANE_LANCE) then t.type = "beam" end
local x, y = self:getTarget(t)
if not x or not y then return nil end
......@@ -38,7 +39,7 @@ newTalent{
tactical = {
MANA = 20,
},
action = function(self)
action = function(self, t)
if not self:hasEffect(self.EFF_MANAFLOW) then
self:setEffect(self.EFF_MANAFLOW, 10, {power=5+self:combatSpellpower(0.3)})
end
......@@ -56,10 +57,10 @@ newTalent{
type = {"spell/arcane", 3},
mode = "passive",
require = { stat = { mag=40 }, },
on_learn = function(self)
on_learn = function(self, t)
self.combat_spellpower = self.combat_spellpower + 10
end,
on_unlearn = function(self)
on_unlearn = function(self, t)
self.combat_spellpower = self.combat_spellpower - 10
end,
info = function(self)
......@@ -75,7 +76,7 @@ newTalent{
tactical = {
DEFEND = 10,
},
activate = function(self)
activate = function(self, t)
game.log("IMPLEMENT ME!")
return true
end,
......
......@@ -7,7 +7,7 @@ newTalent{
tactical = {
ESCAPE = 4,
},
action = function(self)
action = function(self, t)
local target = self
if self:knowTalent(Talents.T_TARGETED_TELEPORT) then
......@@ -46,7 +46,7 @@ newTalent{
tactical = {
ESCAPE = 8,
},
action = function(self)
action = function(self, t)
local target = self
if self:knowTalent(Talents.T_TARGETED_TELEPORT) then
......@@ -108,11 +108,11 @@ newTalent{
tactical = {
MOVEMENT = 20,
},
activate = function(self)
activate = function(self, t)
self:attr("prob_travel", 1)
return true
end,
deactivate = function(self)
deactivate = function(self, t)
self:attr("prob_travel", -1)
return true
end,
......@@ -127,7 +127,7 @@ newTalent{
type = {"spell/conveyance",3},
mana = 30,
cooldown = 10,
action = function(self)
action = function(self, t)
--[[
local target = self
local tx, ty = self.x, self.y
......
......@@ -7,12 +7,12 @@ newTalent{
tactical = {
DEFEND = 10,
},
activate = function(self)
activate = function(self, t)
local power = 1 + self:combatSpellpower(0.15)
self.combat.armor = self.combat.armor + power
return {power=power}
end,
deactivate = function(self, p)
deactivate = function(self, t, p)
self.combat.armor = self.combat.armor - p.power
end,
require = { stat = { mag=14 }, },
......@@ -30,8 +30,9 @@ newTalent{
tactical = {
ATTACK = 10,
},
action = function(self)
local t = {type="bolt", range=20}
range = 20,
action = function(self, t)
local t = {type="bolt", range=self:getTalentRange(t)}
local x, y = self:getTarget(t)
if not x or not y then return nil end
self:project(t, x, y, DamageType.SPELLKNOCKBACK, self:spellCrit(8 + self:combatSpellpower(0.6)))
......
......@@ -6,7 +6,7 @@ newTalent{
tactical = {
ATTACKAREA = 3,
},
action = function(self)
action = function(self, t)
local t = {type="ball", range=0, friendlyfire=false, radius=5 + self:combatSpellpower(0.2)}
self:project(t, self.x, self.y, DamageType.LIGHT, 1)
if self:knowTalent(Talents.T_GLOBE_OF_LIGHT) then
......@@ -38,8 +38,9 @@ newTalent{
tactical = {
ATTACK = 10,
},
action = function(self)
local t = {type="bolt", range=20}
range = 20,
action = function(self, t)
local t = {type="bolt", range=self:getTalentRange(t)}
local x, y = self:getTarget(t)
if not x or not y then return nil end
self:project(t, x, y, DamageType.FIREBURN, self:spellCrit(15 + self:combatSpellpower(2.1)))
......@@ -60,8 +61,9 @@ newTalent{
tactical = {
ATTACKAREA = 10,
},
action = function(self)
local t = {type="ball", range=15, radius=math.min(6, 3 + self:combatSpellpower(0.06))}
range = 15,
action = function(self, t)
local t = {type="ball", range=self:getTalentRange(t), radius=math.min(6, 3 + self:combatSpellpower(0.06))}
local x, y = self:getTarget(t)
if not x or not y then return nil end
self:project(t, x, y, DamageType.FIRE, self:spellCrit(28 + self:combatSpellpower(1.2)))
......@@ -82,11 +84,12 @@ newTalent{
tactical = {
ATTACKAREA = 40,
},
action = function(self)
range = 20,
action = function(self, t)
local duration = 5 + self:combatSpellpower(0.25)
local radius = 5
local dam = 15 + self:combatSpellpower(1.6)
local t = {type="ball", range=20, radius=radius}
local t = {type="ball", range=self:getTalentRange(t), radius=radius}
local x, y = self:getTarget(t)
if not x or not y then return nil end
x, y = game.target:pointAtRange(self.x, self.y, x, y, 15)
......
......@@ -6,7 +6,7 @@ newTalent{
tactical = {
HEAL = 10,
},
action = function(self)
action = function(self, t)
self:setEffect(self.EFF_REGENERATION, 10, {power=5 + self:combatSpellpower(0.35)})
return true
end,
......@@ -25,7 +25,7 @@ newTalent{
tactical = {
HEAL = 10,
},
action = function(self)
action = function(self, t)
self:heal(self:spellCrit(10 + self:combatSpellpower(2)), self)
return true
end,
......
......@@ -3,7 +3,7 @@ newTalent{
type = {"spell/water", 1},
mana = 10,
cooldown = 100,
action = function(self)
action = function(self, t)
return true
end,
require = { stat = { mag=10 }, },
......@@ -20,8 +20,9 @@ newTalent{
tactical = {
ATTACK = 10,
},
action = function(self)
local t = {type="hit", range=20}
range = 20,
action = function(self, t)
local t = {type="hit", range=self:getTalentRange(t)}
local x, y = self:getTarget(t)
if not x or not y then return nil end
self:project(t, x, y, DamageType.COLD, self:spellCrit(7 + self:combatSpellpower(1.2)))
......@@ -43,7 +44,7 @@ newTalent{
tactical = {
ATTACKAREA = 10,
},
action = function(self)
action = function(self, t)
local duration = 5 + self:combatSpellpower(0.05)
local radius = 1
local dam = 12 + self:combatSpellpower(0.5)
......@@ -76,7 +77,7 @@ newTalent{
tactical = {
ATTACKAREA = 20,
},
action = function(self)
action = function(self, t)
local duration = 5 + self:combatSpellpower(0.25)
local radius = 3
local dam = 12 + self:combatSpellpower(0.8)
......
local Talents = require("engine.interface.ActorTalents")
newEntity{
group = "dragon",
name = "dragon of death",
......@@ -40,11 +42,13 @@ newEntity{
level_range = {1, 7}, exp_worth = 1,
rarity = 1,
autolevel = "warrior",
ai = "simple",
ai = "dumb_talented_simple",
max_life = resolvers.rngavg(10,20),
max_mana = resolvers.rngavg(10,20),
max_mana = resolvers.rngavg(50,60),
max_stamina = resolvers.rngavg(10,20),
energy = { mod=0.3 },
has_blood = {nb=3, color={50,255,120}},
combat = { dam=5, atk=6, def=2, apr=1, armor=2},
combat = { dam=5, atk=6, def=2, apr=1, armor=2 },
stats = { str=10, dex=7, mag=14, con=10 },
talents = { Talents.T_MANATHRUST, Talents.T_FREEZE, Talents.T_FLAME }
}
......@@ -19,7 +19,7 @@ return {
},
actor = {
class = "engine.generator.actor.Random",
nb_npc = {2, 3},
nb_npc = {20, 30},
ood = {chance=5, range={1, 10}},
adjust_level_to_player = {-1, 2},
},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment