You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/scripts/data/integration_tests/test_lua_api/player.lua

350 lines
15 KiB
Lua

local testing = require('testing_util')
local self = require('openmw.self')
local util = require('openmw.util')
local core = require('openmw.core')
local input = require('openmw.input')
local types = require('openmw.types')
local nearby = require('openmw.nearby')
local camera = require('openmw.camera')
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Controls, false)
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Fighting, false)
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Jumping, false)
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Looking, false)
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Magic, false)
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.VanityMode, false)
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.ViewMode, false)
local function rotate(object, targetPitch, targetYaw)
local endTime = core.getSimulationTime() + 1
while core.getSimulationTime() < endTime do
object.controls.jump = false
object.controls.run = true
object.controls.movement = 0
object.controls.sideMovement = 0
if targetPitch ~= nil then
object.controls.pitchChange = util.normalizeAngle(targetPitch - object.rotation:getPitch()) * 0.5
end
if targetYaw ~= nil then
object.controls.yawChange = util.normalizeAngle(targetYaw - object.rotation:getYaw()) * 0.5
end
coroutine.yield()
end
end
local function rotateByYaw(object, target)
rotate(object, nil, target)
end
local function rotateByPitch(object, target)
rotate(object, target, nil)
end
testing.registerLocalTest('playerYawRotation',
function()
local initialAlphaXZ, initialGammaXZ = self.rotation:getAnglesXZ()
local initialAlphaZYX, initialBetaZYX, initialGammaZYX = self.rotation:getAnglesZYX()
local targetYaw = math.rad(90)
rotateByYaw(self, targetYaw)
testing.expectEqualWithDelta(self.rotation:getYaw(), targetYaw, 0.05, 'Incorrect yaw rotation')
local alpha1, gamma1 = self.rotation:getAnglesXZ()
testing.expectEqualWithDelta(alpha1, initialAlphaXZ, 0.05, 'Alpha rotation in XZ convention should not change')
testing.expectEqualWithDelta(gamma1, targetYaw, 0.05, 'Incorrect gamma rotation in XZ convention')
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
testing.expectEqualWithDelta(alpha2, targetYaw, 0.05, 'Incorrect alpha rotation in ZYX convention')
testing.expectEqualWithDelta(beta2, initialBetaZYX, 0.05, 'Beta rotation in ZYX convention should not change')
testing.expectEqualWithDelta(gamma2, initialGammaZYX, 0.05, 'Gamma rotation in ZYX convention should not change')
end)
testing.registerLocalTest('playerPitchRotation',
function()
local initialAlphaXZ, initialGammaXZ = self.rotation:getAnglesXZ()
local initialAlphaZYX, initialBetaZYX, initialGammaZYX = self.rotation:getAnglesZYX()
local targetPitch = math.rad(90)
rotateByPitch(self, targetPitch)
testing.expectEqualWithDelta(self.rotation:getPitch(), targetPitch, 0.05, 'Incorrect pitch rotation')
local alpha1, gamma1 = self.rotation:getAnglesXZ()
testing.expectEqualWithDelta(alpha1, targetPitch, 0.05, 'Incorrect alpha rotation in XZ convention')
testing.expectEqualWithDelta(gamma1, initialGammaXZ, 0.05, 'Gamma rotation in XZ convention should not change')
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
testing.expectEqualWithDelta(alpha2, initialAlphaZYX, 0.05, 'Alpha rotation in ZYX convention should not change')
testing.expectEqualWithDelta(beta2, initialBetaZYX, 0.05, 'Beta rotation in ZYX convention should not change')
testing.expectEqualWithDelta(gamma2, targetPitch, 0.05, 'Incorrect gamma rotation in ZYX convention')
end)
testing.registerLocalTest('playerPitchAndYawRotation',
function()
local targetPitch = math.rad(-30)
local targetYaw = math.rad(-60)
rotate(self, targetPitch, targetYaw)
testing.expectEqualWithDelta(self.rotation:getPitch(), targetPitch, 0.05, 'Incorrect pitch rotation')
testing.expectEqualWithDelta(self.rotation:getYaw(), targetYaw, 0.05, 'Incorrect yaw rotation')
local alpha1, gamma1 = self.rotation:getAnglesXZ()
testing.expectEqualWithDelta(alpha1, targetPitch, 0.05, 'Incorrect alpha rotation in XZ convention')
testing.expectEqualWithDelta(gamma1, targetYaw, 0.05, 'Incorrect gamma rotation in XZ convention')
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
testing.expectEqualWithDelta(alpha2, math.rad(-56), 0.05, 'Incorrect alpha rotation in ZYX convention')
testing.expectEqualWithDelta(beta2, math.rad(-25), 0.05, 'Incorrect beta rotation in ZYX convention')
testing.expectEqualWithDelta(gamma2, math.rad(-16), 0.05, 'Incorrect gamma rotation in ZYX convention')
end)
testing.registerLocalTest('playerRotation',
function()
local rotation = math.sqrt(2)
local endTime = core.getSimulationTime() + 3
while core.getSimulationTime() < endTime do
self.controls.jump = false
self.controls.run = true
self.controls.movement = 0
self.controls.sideMovement = 0
self.controls.pitchChange = rotation
self.controls.yawChange = rotation
coroutine.yield()
local alpha1, gamma1 = self.rotation:getAnglesXZ()
testing.expectThat(alpha1, testing.isNotNan(), 'Alpha rotation in XZ convention is nan')
testing.expectThat(gamma1, testing.isNotNan(), 'Gamma rotation in XZ convention is nan')
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
testing.expectThat(alpha2, testing.isNotNan(), 'Alpha rotation in ZYX convention is nan')
testing.expectThat(beta2, testing.isNotNan(), 'Beta rotation in ZYX convention is nan')
testing.expectThat(gamma2, testing.isNotNan(), 'Gamma rotation in ZYX convention is nan')
end
end)
testing.registerLocalTest('playerForwardRunning',
function()
local startPos = self.position
local endTime = core.getSimulationTime() + 1
while core.getSimulationTime() < endTime do
self.controls.jump = false
self.controls.run = true
self.controls.movement = 1
self.controls.sideMovement = 0
self.controls.yawChange = 0
coroutine.yield()
end
local direction, distance = (self.position - startPos):normalize()
testing.expectGreaterThan(distance, 0, 'Run forward, distance')
testing.expectEqualWithDelta(direction.x, 0, 0.1, 'Run forward, X coord')
testing.expectEqualWithDelta(direction.y, 1, 0.1, 'Run forward, Y coord')
end)
testing.registerLocalTest('playerDiagonalWalking',
function()
local startPos = self.position
local endTime = core.getSimulationTime() + 1
while core.getSimulationTime() < endTime do
self.controls.jump = false
self.controls.run = false
self.controls.movement = -1
self.controls.sideMovement = -1
self.controls.yawChange = 0
coroutine.yield()
end
local direction, distance = (self.position - startPos):normalize()
testing.expectGreaterThan(distance, 0, 'Walk diagonally, distance')
testing.expectEqualWithDelta(direction.x, -0.707, 0.1, 'Walk diagonally, X coord')
testing.expectEqualWithDelta(direction.y, -0.707, 0.1, 'Walk diagonally, Y coord')
end)
testing.registerLocalTest('findPath',
function()
local src = util.vector3(4096, 4096, 1745)
local dst = util.vector3(4500, 4500, 1745.95263671875)
local options = {
agentBounds = types.Actor.getPathfindingAgentBounds(self),
includeFlags = nearby.NAVIGATOR_FLAGS.Walk + nearby.NAVIGATOR_FLAGS.Swim,
areaCosts = {
water = 1,
door = 2,
ground = 1,
pathgrid = 1,
},
destinationTolerance = 1,
}
local status, path = nearby.findPath(src, dst, options)
testing.expectEqual(status, nearby.FIND_PATH_STATUS.Success, 'Status')
testing.expectLessOrEqual((path[#path] - dst):length(), 1,
'Last path point ' .. testing.formatActualExpected(path[#path], dst))
end)
testing.registerLocalTest('findRandomPointAroundCircle',
function()
local position = util.vector3(4096, 4096, 1745.95263671875)
local maxRadius = 100
local options = {
agentBounds = types.Actor.getPathfindingAgentBounds(self),
includeFlags = nearby.NAVIGATOR_FLAGS.Walk,
}
local result = nearby.findRandomPointAroundCircle(position, maxRadius, options)
testing.expectGreaterThan((result - position):length(), 1,
'Random point ' .. testing.formatActualExpected(result, position))
end)
testing.registerLocalTest('castNavigationRay',
function()
local src = util.vector3(4096, 4096, 1745)
local dst = util.vector3(4500, 4500, 1745.95263671875)
local options = {
agentBounds = types.Actor.getPathfindingAgentBounds(self),
includeFlags = nearby.NAVIGATOR_FLAGS.Walk + nearby.NAVIGATOR_FLAGS.Swim,
}
local result = nearby.castNavigationRay(src, dst, options)
testing.expectLessOrEqual((result - dst):length(), 1,
'Navigation hit point ' .. testing.formatActualExpected(result, dst))
end)
testing.registerLocalTest('findNearestNavMeshPosition',
function()
local position = util.vector3(4096, 4096, 2000)
local options = {
agentBounds = types.Actor.getPathfindingAgentBounds(self),
includeFlags = nearby.NAVIGATOR_FLAGS.Walk + nearby.NAVIGATOR_FLAGS.Swim,
searchAreaHalfExtents = util.vector3(1000, 1000, 1000),
}
local result = nearby.findNearestNavMeshPosition(position, options)
local expected = util.vector3(4096, 4096, 1746.27099609375)
testing.expectLessOrEqual((result - expected):length(), 1,
'Navigation mesh position ' .. testing.formatActualExpected(result, expected))
end)
testing.registerLocalTest('playerMemoryLimit',
function()
local ok, err = pcall(function()
local str = 'a'
while true do
str = str .. str
end
end)
testing.expectEqual(ok, false, 'Script reaching memory limit should fail')
testing.expectEqual(err, 'not enough memory')
end)
testing.registerLocalTest('playerWeaponAttack',
function()
camera.setMode(camera.MODE.ThirdPerson)
local options = {
agentBounds = types.Actor.getPathfindingAgentBounds(self),
}
local duration = 10
local endTime = core.getSimulationTime() + duration
local nextTime = 0
local use = self.ATTACK_TYPE.NoAttack
local attributes = {}
for k, v in pairs(types.Actor.stats.attributes) do
attributes[k] = v(self).base
end
types.Actor.stats.attributes.speed(self).base = 100
types.Actor.stats.attributes.strength(self).base = 1000
types.Actor.stats.attributes.agility(self).base = 1000
local weaponId = 'basic_dagger1h'
local weapon = types.Actor.inventory(self):find(weaponId)
local isWeapon = function(actual)
if actual == nil then
return weaponId .. ' is not found'
end
if actual.recordId ~= weaponId then
return 'found weapon recordId does not match expected: actual=' .. tostring(actual.id)
.. ', expected=' .. weaponId
end
return ''
end
testing.expectThat(weapon, isWeapon)
types.Actor.setEquipment(self, {[types.Actor.EQUIPMENT_SLOT.CarriedRight] = weapon})
coroutine.yield()
testing.expectThat(types.Actor.getEquipment(self, types.Actor.EQUIPMENT_SLOT.CarriedRight), isWeapon)
types.Actor.setStance(self, types.Actor.STANCE.Weapon)
local previousHealth = nil
local targetActor = nil
while true do
local time = core.getSimulationTime()
testing.expectLessOrEqual(time, endTime, 'Did not damage any targets in ' .. duration .. ' seconds')
if targetActor ~= nil and types.Actor.stats.dynamic.health(targetActor).current < previousHealth then
print('Dealt ' .. (previousHealth - types.Actor.stats.dynamic.health(targetActor).current) .. ' damage to ' .. tostring(targetActor))
break
end
local minDistance = nil
for i, actor in ipairs(nearby.actors) do
if actor.id ~= self.id then
local distance = (actor.position - self.position):length()
if minDistance == nil or minDistance > distance then
minDistance = distance
targetActor = actor
end
end
end
testing.expectNotEqual(targetActor, nil, 'No attack targets found')
previousHealth = types.Actor.stats.dynamic.health(targetActor).current
local destination = nil
if minDistance > 100 then
local status, path = nearby.findPath(self.position, targetActor.position, options)
testing.expectEqual(status, nearby.FIND_PATH_STATUS.Success,
'Failed to find path from ' .. tostring(self.position) .. ' to ' .. tostring(targetActor.position))
destination = path[2]
use = self.ATTACK_TYPE.NoAttack
self.controls.run = true
self.controls.movement = 1
else
destination = targetActor.position
if nextTime < time then
if use == 0 then
use = self.ATTACK_TYPE.Any
nextTime = time + 0.5
else
use = self.ATTACK_TYPE.NoAttack
end
end
end
self.controls.use = use
local direction = destination - self.position
direction = direction:normalize()
self.controls.yawChange = util.normalizeAngle(math.atan2(direction.x, direction.y) - self.rotation:getYaw())
self.controls.pitchChange = util.normalizeAngle(math.asin(util.clamp(-direction.z, -1, 1)) - self.rotation:getPitch())
coroutine.yield()
end
for k, v in pairs(types.Actor.stats.attributes) do
v(self).base = attributes[k]
end
end)
return {
engineHandlers = {
onFrame = testing.updateLocal,
},
eventHandlers = testing.eventHandlers
}