mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-28 13:39:42 +00:00
Merge branch 'attack_test' into 'master'
Add test for player's attack See merge request OpenMW/openmw!4326
This commit is contained in:
commit
0cb2b71301
6 changed files with 155 additions and 19 deletions
|
@ -461,6 +461,7 @@ Ubuntu_Clang_tests_Debug:
|
|||
stage: test
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
EXAMPLE_SUITE_REVISION: f51b832e033429a7cdc520e0e48d7dfdb9141caa
|
||||
cache:
|
||||
paths:
|
||||
- .cache/pip
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
git clone --depth=1 https://gitlab.com/OpenMW/example-suite.git
|
||||
mkdir example-suite
|
||||
cd example-suite
|
||||
git init
|
||||
git remote add origin https://gitlab.com/OpenMW/example-suite.git
|
||||
git fetch --depth=1 origin ${EXAMPLE_SUITE_REVISION}
|
||||
git checkout FETCH_HEAD
|
||||
cd ..
|
||||
|
||||
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \
|
||||
scripts/integration_tests.py --omw build/install/bin/openmw --workdir integration_tests_output example-suite/
|
||||
|
|
|
@ -5,7 +5,9 @@ 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)
|
||||
|
@ -159,8 +161,8 @@ testing.registerLocalTest('playerDiagonalWalking',
|
|||
|
||||
testing.registerLocalTest('findPath',
|
||||
function()
|
||||
local src = util.vector3(4096, 4096, 867.237)
|
||||
local dst = util.vector3(4500, 4500, 700.216)
|
||||
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,
|
||||
|
@ -180,7 +182,7 @@ testing.registerLocalTest('findPath',
|
|||
|
||||
testing.registerLocalTest('findRandomPointAroundCircle',
|
||||
function()
|
||||
local position = util.vector3(4096, 4096, 867.237)
|
||||
local position = util.vector3(4096, 4096, 1745.95263671875)
|
||||
local maxRadius = 100
|
||||
local options = {
|
||||
agentBounds = types.Actor.getPathfindingAgentBounds(self),
|
||||
|
@ -193,8 +195,8 @@ testing.registerLocalTest('findRandomPointAroundCircle',
|
|||
|
||||
testing.registerLocalTest('castNavigationRay',
|
||||
function()
|
||||
local src = util.vector3(4096, 4096, 867.237)
|
||||
local dst = util.vector3(4500, 4500, 700.216)
|
||||
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,
|
||||
|
@ -206,14 +208,14 @@ testing.registerLocalTest('castNavigationRay',
|
|||
|
||||
testing.registerLocalTest('findNearestNavMeshPosition',
|
||||
function()
|
||||
local position = util.vector3(4096, 4096, 1000)
|
||||
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, 872.674)
|
||||
local expected = util.vector3(4096, 4096, 1746.27099609375)
|
||||
testing.expectLessOrEqual((result - expected):length(), 1,
|
||||
'Navigation mesh position ' .. testing.formatActualExpected(result, expected))
|
||||
end)
|
||||
|
@ -230,6 +232,115 @@ testing.registerLocalTest('playerMemoryLimit',
|
|||
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,
|
||||
|
|
|
@ -220,7 +220,7 @@ local function testMemoryLimit()
|
|||
end
|
||||
|
||||
local function initPlayer()
|
||||
player:teleport('', util.vector3(4096, 4096, 867.237), util.transform.identity)
|
||||
player:teleport('', util.vector3(4096, 4096, 1745), util.transform.identity)
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
|
@ -277,7 +277,12 @@ tests = {
|
|||
{'playerMemoryLimit', function()
|
||||
initPlayer()
|
||||
testing.runLocalTest(player, 'playerMemoryLimit')
|
||||
end}
|
||||
end},
|
||||
{'player with equipped weapon on attack should damage health of other actors', function()
|
||||
initPlayer()
|
||||
world.createObject('basic_dagger1h', 1):moveInto(player)
|
||||
testing.runLocalTest(player, 'playerWeaponAttack')
|
||||
end},
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -81,6 +81,12 @@ function M.expectEqual(v1, v2, msg)
|
|||
end
|
||||
end
|
||||
|
||||
function M.expectNotEqual(v1, v2, msg)
|
||||
if v1 == v2 then
|
||||
error(string.format('%s: %s == %s', msg or '', v1, v2), 2)
|
||||
end
|
||||
end
|
||||
|
||||
function M.closeToVector(expected, maxDistance)
|
||||
return function(actual)
|
||||
local distance = (expected - actual):length()
|
||||
|
|
|
@ -17,11 +17,15 @@ parser.add_argument("--verbose", action='store_true', help="print all openmw out
|
|||
args = parser.parse_args()
|
||||
|
||||
example_suite_dir = Path(args.example_suite).resolve()
|
||||
example_suite_content = example_suite_dir / "game_template" / "data" / "template.omwgame"
|
||||
if not example_suite_content.is_file():
|
||||
sys.exit(
|
||||
f"{example_suite_content} not found, use 'git clone https://gitlab.com/OpenMW/example-suite/' to get it"
|
||||
)
|
||||
|
||||
content_paths = (
|
||||
example_suite_dir / "game_template" / "data" / "template.omwgame",
|
||||
example_suite_dir / "example_animated_creature" / "data" / "landracer.omwaddon",
|
||||
example_suite_dir / "the_hub" / "data" / "the_hub.omwaddon",
|
||||
)
|
||||
for path in content_paths:
|
||||
if not path.is_file():
|
||||
sys.exit(f"{path} is not found, use 'git clone https://gitlab.com/OpenMW/example-suite/' to get it")
|
||||
|
||||
openmw_binary = Path(args.omw).resolve()
|
||||
if not openmw_binary.is_file():
|
||||
|
@ -43,15 +47,17 @@ def runTest(name):
|
|||
shutil.copyfile(example_suite_dir / "settings.cfg", config_dir / "settings.cfg")
|
||||
test_dir = tests_dir / name
|
||||
with open(config_dir / "openmw.cfg", "w", encoding="utf-8") as omw_cfg:
|
||||
for path in content_paths:
|
||||
omw_cfg.write(f'data="{path.parent}"\n')
|
||||
omw_cfg.writelines(
|
||||
(
|
||||
f'data="{example_suite_dir}{os.sep}game_template{os.sep}data"\n',
|
||||
f'data="{testing_util_dir}"\n',
|
||||
f'data-local="{test_dir}"\n',
|
||||
f'user-data="{userdata_dir}"\n',
|
||||
"content=template.omwgame\n",
|
||||
)
|
||||
)
|
||||
for path in content_paths:
|
||||
omw_cfg.write(f'content={path.name}\n')
|
||||
if (test_dir / "openmw.cfg").exists():
|
||||
omw_cfg.write(open(test_dir / "openmw.cfg").read())
|
||||
elif (test_dir / "test.omwscripts").exists():
|
||||
|
@ -124,6 +130,7 @@ for entry in tests_dir.glob("test_*"):
|
|||
if entry.is_dir():
|
||||
if not runTest(entry.name):
|
||||
status = -1
|
||||
shutil.rmtree(config_dir, ignore_errors=True)
|
||||
shutil.rmtree(userdata_dir, ignore_errors=True)
|
||||
if status == 0:
|
||||
shutil.rmtree(config_dir, ignore_errors=True)
|
||||
shutil.rmtree(userdata_dir, ignore_errors=True)
|
||||
exit(status)
|
||||
|
|
Loading…
Reference in a new issue