Compare commits
9 Commits
feature/ai
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f1c31f67e | |||
| c0bab85b2f | |||
| 4f436a3d3f | |||
| 752fe00910 | |||
| 2d29d35f96 | |||
| ec290eb18b | |||
| 958d3bc245 | |||
| 1986228670 | |||
| 403ba5a03f |
@ -6,6 +6,5 @@
|
|||||||
"love.filesystem.load": "loadfile"
|
"love.filesystem.load": "loadfile"
|
||||||
},
|
},
|
||||||
"workspace.ignoreDir": ["dev_utils"],
|
"workspace.ignoreDir": ["dev_utils"],
|
||||||
"diagnostics.ignoredFiles": "Disable",
|
"diagnostics.ignoredFiles": "Disable"
|
||||||
"completion.autoRequire": false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,7 @@ function sprite:draw()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- @return Task<nil>
|
--- @return Task<nil>
|
||||||
function sprite:animate(state, node)
|
function sprite:animate(state)
|
||||||
return function(callback)
|
return function(callback)
|
||||||
if not self.animationGrid[state] then
|
if not self.animationGrid[state] then
|
||||||
print("[SpriteBehavior]: no animation for '" .. state .. "'")
|
print("[SpriteBehavior]: no animation for '" .. state .. "'")
|
||||||
|
|||||||
@ -8,7 +8,7 @@ grid.__index = grid
|
|||||||
--- adds a value to the grid
|
--- adds a value to the grid
|
||||||
--- @param value any
|
--- @param value any
|
||||||
function grid:add(value)
|
function grid:add(value)
|
||||||
grid[tostring(value.position)] = value
|
self.__grid[tostring(value.position)] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param position Vec3
|
--- @param position Vec3
|
||||||
|
|||||||
@ -40,6 +40,7 @@ end
|
|||||||
function level:update(dt)
|
function level:update(dt)
|
||||||
utils.each(self.deadIds, function(id)
|
utils.each(self.deadIds, function(id)
|
||||||
self.characters[id] = nil
|
self.characters[id] = nil
|
||||||
|
self.turnOrder:remove(id)
|
||||||
end)
|
end)
|
||||||
self.deadIds = {}
|
self.deadIds = {}
|
||||||
|
|
||||||
|
|||||||
@ -117,4 +117,29 @@ function turnOrder:add(id)
|
|||||||
self.actedQueue:insert(id) -- новые персонажи по умолчанию попадают в очередь следующего хода
|
self.actedQueue:insert(id) -- новые персонажи по умолчанию попадают в очередь следующего хода
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Удалить персонажа из очереди хода (например, при смерти)
|
||||||
|
--- @param id Id
|
||||||
|
function turnOrder:remove(id)
|
||||||
|
if self.current == id then
|
||||||
|
self.current = self.pendingQueue:pop()
|
||||||
|
if not self.current then
|
||||||
|
self:endRound()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local function filterQueue(q, targetId)
|
||||||
|
local newQ = PriorityQueue.new(initiativeComparator)
|
||||||
|
for _, val in ipairs(q.data) do
|
||||||
|
if val ~= targetId then
|
||||||
|
newQ:insert(val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return newQ
|
||||||
|
end
|
||||||
|
|
||||||
|
self.actedQueue = filterQueue(self.actedQueue, id)
|
||||||
|
self.pendingQueue = filterQueue(self.pendingQueue, id)
|
||||||
|
end
|
||||||
|
|
||||||
return { new = new }
|
return { new = new }
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
--- --TODO: каждый каст должен возвращать объект, который позволит отследить момент завершения анимации спелла
|
--- --TODO: каждый каст должен возвращать объект, который позволит отследить момент завершения анимации спелла
|
||||||
--- Да, это Future/Promise/await/async
|
--- Да, это Future/Promise/await/async
|
||||||
|
|
||||||
local Counter = require 'lib.utils.counter'
|
local task = require 'lib.utils.task'
|
||||||
|
|
||||||
--- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell
|
--- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell
|
||||||
--- @field tag string
|
--- @field tag string
|
||||||
@ -55,11 +55,7 @@ function walk:cast(caster, target)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
return function(callback)
|
return caster:has(Tree.behaviors.tiled):followPath(path)
|
||||||
return caster:has(Tree.behaviors.tiled):followPath(path)(
|
|
||||||
callback
|
|
||||||
) -- <- callback вызовется после followPath
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function walk:update(caster, dt)
|
function walk:update(caster, dt)
|
||||||
@ -103,14 +99,10 @@ function regenerateMana:cast(caster, target)
|
|||||||
Tree.behaviors.positioned.new(caster:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }),
|
Tree.behaviors.positioned.new(caster:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }),
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(callback)
|
return task.wait {
|
||||||
light:has(Tree.behaviors.light):animateColor(Vec3 {})(
|
light:has(Tree.behaviors.light):animateColor(Vec3 {}),
|
||||||
function()
|
sprite:animate("hurt")
|
||||||
light:die()
|
}
|
||||||
end
|
|
||||||
)
|
|
||||||
sprite:animate("hurt")(callback)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local attack = setmetatable({}, spell)
|
local attack = setmetatable({}, spell)
|
||||||
@ -143,20 +135,16 @@ function attack:cast(caster, target)
|
|||||||
|
|
||||||
caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end)
|
caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end)
|
||||||
|
|
||||||
return function(callback)
|
return
|
||||||
local c = Counter(callback)
|
task.wait {
|
||||||
|
sprite:animate("attack"),
|
||||||
c.push()
|
task.wait {
|
||||||
sprite:animate("attack")(c.pop)
|
task.chain(targetCharacter:has(Tree.behaviors.residentsleeper):sleep(200),
|
||||||
|
function() return targetSprite:animate("hurt") end
|
||||||
c.push()
|
),
|
||||||
targetCharacter:has(Tree.behaviors.residentsleeper):sleep(200)(
|
|
||||||
function()
|
|
||||||
targetSprite:animate("hurt")(c.pop)
|
|
||||||
Tree.audio:play(Tree.assets.files.audio.sounds.hurt)
|
Tree.audio:play(Tree.assets.files.audio.sounds.hurt)
|
||||||
end
|
}
|
||||||
)
|
}
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
--- @field private isAlive boolean
|
--- @field private isAlive boolean
|
||||||
--- @field push fun():nil добавить 1 к счетчику
|
--- @field push fun():nil добавить 1 к счетчику
|
||||||
--- @field pop fun():nil убавить 1 у счетчика
|
--- @field pop fun():nil убавить 1 у счетчика
|
||||||
|
--- @field set fun(count: integer): nil установить значение на счетчике
|
||||||
local counter = {}
|
local counter = {}
|
||||||
counter.__index = counter
|
counter.__index = counter
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ local function new(onFinish)
|
|||||||
}
|
}
|
||||||
t.push = function() counter._push(t) end
|
t.push = function() counter._push(t) end
|
||||||
t.pop = function() counter._pop(t) end
|
t.pop = function() counter._pop(t) end
|
||||||
|
t.set = function(count) t.count = count end
|
||||||
|
|
||||||
return setmetatable(t, counter)
|
return setmetatable(t, counter)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -34,3 +34,50 @@
|
|||||||
--- ```
|
--- ```
|
||||||
--- @generic T
|
--- @generic T
|
||||||
--- @alias Task<T> fun(callback: fun(value: T): nil): nil
|
--- @alias Task<T> fun(callback: fun(value: T): nil): nil
|
||||||
|
|
||||||
|
--- Возвращает новый Task, который завершится после завершения всех переданных `tasks`.
|
||||||
|
---
|
||||||
|
--- Значение созданного Task будет содержать список значений `tasks` в том же порядке.
|
||||||
|
---
|
||||||
|
--- См. также https://api.dart.dev/dart-async/Future/wait.html
|
||||||
|
--- @generic T
|
||||||
|
--- @param tasks Task[]
|
||||||
|
--- @return Task<T[]>
|
||||||
|
local function wait(tasks)
|
||||||
|
local count = #tasks
|
||||||
|
local results = {}
|
||||||
|
|
||||||
|
return function(callback)
|
||||||
|
for i, task in ipairs(tasks) do
|
||||||
|
task(
|
||||||
|
function(result)
|
||||||
|
results[i] = result
|
||||||
|
|
||||||
|
count = count - 1
|
||||||
|
if count == 0 then callback(results) end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Последовательно объединяет два `Task` в один.
|
||||||
|
--- @generic T
|
||||||
|
--- @generic R
|
||||||
|
--- @param task Task<T> `Task`, который выполнится первым
|
||||||
|
--- @param onCompleted fun(value: T): Task<R> Конструктор второго `Task`. Принимает результат выполнения первого `Task`
|
||||||
|
--- @return Task<R>
|
||||||
|
local function chain(task, onCompleted)
|
||||||
|
return function(callback)
|
||||||
|
task(function(value)
|
||||||
|
local task2 = onCompleted(value)
|
||||||
|
task2(callback)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
wait = wait,
|
||||||
|
chain = chain
|
||||||
|
}
|
||||||
|
|||||||
4
main.lua
4
main.lua
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
local character = require "lib/character/character"
|
local character = require "lib/character/character"
|
||||||
local testLayout
|
local testLayout
|
||||||
|
local TestRunner = require "test.runner"
|
||||||
|
TestRunner:register(require "test.task")
|
||||||
|
|
||||||
function love.conf(t)
|
function love.conf(t)
|
||||||
t.console = true
|
t.console = true
|
||||||
@ -67,6 +69,8 @@ end
|
|||||||
|
|
||||||
local lt = "0"
|
local lt = "0"
|
||||||
function love.update(dt)
|
function love.update(dt)
|
||||||
|
TestRunner:update(dt) -- закомментировать для отключения тестов
|
||||||
|
|
||||||
local t1 = love.timer.getTime()
|
local t1 = love.timer.getTime()
|
||||||
Tree.controls:poll()
|
Tree.controls:poll()
|
||||||
Tree.level.camera:update(dt) -- сначала логика камеры, потому что на нее завязан UI
|
Tree.level.camera:update(dt) -- сначала логика камеры, потому что на нее завязан UI
|
||||||
|
|||||||
46
test/runner.lua
Normal file
46
test/runner.lua
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
--- @class Test
|
||||||
|
local test = {}
|
||||||
|
function test:run(complete) end
|
||||||
|
|
||||||
|
function test:update(dt) end
|
||||||
|
|
||||||
|
--- @class TestRunner
|
||||||
|
--- @field private tests Test[]
|
||||||
|
--- @field private state "loading" | "running" | "completed"
|
||||||
|
--- @field private completedCount integer
|
||||||
|
local runner = {}
|
||||||
|
runner.tests = {}
|
||||||
|
runner.state = "loading"
|
||||||
|
runner.completedCount = 0
|
||||||
|
|
||||||
|
--- глобальный update для тестов, нужен для тестирования фич, зависимых от времени
|
||||||
|
function runner:update(dt)
|
||||||
|
if self.state == "loading" then
|
||||||
|
print("[TestRunner]: running " .. #self.tests .. " tests")
|
||||||
|
|
||||||
|
for _, t in ipairs(self.tests) do
|
||||||
|
t:run(
|
||||||
|
function()
|
||||||
|
self.completedCount = self.completedCount + 1
|
||||||
|
if self.completedCount == #self.tests then
|
||||||
|
self.state = "completed"
|
||||||
|
print("[TestRunner]: tests completed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
self.state = "running"
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, t in ipairs(self.tests) do
|
||||||
|
if t.update then t:update(dt) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- добавляет тест для прохождения
|
||||||
|
--- @param t Test
|
||||||
|
function runner:register(t)
|
||||||
|
table.insert(self.tests, t)
|
||||||
|
end
|
||||||
|
|
||||||
|
return runner
|
||||||
75
test/task.lua
Normal file
75
test/task.lua
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
local task = require "lib.utils.task"
|
||||||
|
|
||||||
|
local test = {}
|
||||||
|
|
||||||
|
local t0
|
||||||
|
local task1Start, task2Start
|
||||||
|
local task1Callback, task2Callback
|
||||||
|
|
||||||
|
--- @return Task<number>
|
||||||
|
local function task1()
|
||||||
|
return function(callback)
|
||||||
|
task1Start = love.timer.getTime()
|
||||||
|
task1Callback = callback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @return Task<number>
|
||||||
|
local function task2()
|
||||||
|
return function(callback)
|
||||||
|
task2Start = love.timer.getTime()
|
||||||
|
task2Callback = callback
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function test:run(complete)
|
||||||
|
t0 = love.timer.getTime()
|
||||||
|
|
||||||
|
task.wait {
|
||||||
|
task1(),
|
||||||
|
task2()
|
||||||
|
} (function(values)
|
||||||
|
local tWait = love.timer.getTime()
|
||||||
|
local dt = tWait - t0
|
||||||
|
|
||||||
|
local t1 = values[1]
|
||||||
|
local t2 = values[2]
|
||||||
|
|
||||||
|
assert(type(t1) == "number" and type(t2) == "number")
|
||||||
|
assert(t2 > t1)
|
||||||
|
assert(dt >= 2, "dt = " .. dt)
|
||||||
|
|
||||||
|
print("task.wait completed in " .. dt .. " sec", "t1 = " .. t1 - t0, "t2 = " .. t2 - t0)
|
||||||
|
|
||||||
|
|
||||||
|
t0 = love.timer.getTime()
|
||||||
|
task.chain(task1(), function(value)
|
||||||
|
t1 = value
|
||||||
|
assert(t1 - t0 >= 1)
|
||||||
|
return task2()
|
||||||
|
end)(
|
||||||
|
function(value)
|
||||||
|
t2 = value
|
||||||
|
assert(t2 - t0 >= 2)
|
||||||
|
print("task.chain completed in " .. t2 - t0 .. " sec")
|
||||||
|
|
||||||
|
complete()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function test:update(dt)
|
||||||
|
local t = love.timer.getTime()
|
||||||
|
if task1Start and t - task1Start >= 1 then
|
||||||
|
task1Callback(t)
|
||||||
|
task1Start = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if task2Start and t - task2Start >= 2 then
|
||||||
|
task2Callback(t)
|
||||||
|
task2Start = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return test
|
||||||
Loading…
x
Reference in New Issue
Block a user