This document shows intentional specifications of the components provided by Lively Epsilon.
The following are code examples that are run as tests. So they sometimes use mocks that you could see in the code
examples. Statements starting with assert
are assumptions on the code that follows. Their name should be self-explanatory.
The documentation also documents internal functions. Please check if a method is supposed to be used in the API reference before using it. |
BrokerUpgrade
BrokerUpgrade:new()
BrokerUpgrade:new() config.description, getDescription() fails if no player is given
local upgrade = upgradeMock()
assert.has_error(function() upgrade:getDescription() end)
BrokerUpgrade:new() config.description, getDescription() returns nil if no description is set
local upgrade = upgradeMock({description = nil})
assert.is_nil(upgrade:getDescription(player))
BrokerUpgrade:new() config.description, getDescription() returns the description if it is a function
local description = "This is an upate"
local upgrade
upgrade = upgradeMock({description = function(arg1, arg2)
assert.is_same(upgrade, arg1)
assert.is_same(player, arg2)
return description
end})
assert.is_same(description, upgrade:getDescription(player))
BrokerUpgrade:new() config.description, getDescription() returns the description if it is a string
local description = "This is an upgrade"
local upgrade = upgradeMock({description = description})
assert.is_same(description, upgrade:getDescription(player))
BrokerUpgrade:new() config.id, getId() allows to set an id
local upgrade = upgradeMock({
id = "fake_upgrade",
})
assert.is_same("fake_upgrade", upgrade:getId())
BrokerUpgrade:new() config.id, getId() fails if id is a number
assert.has_error(function() upgradeMock({id = 42}) end)
BrokerUpgrade:new() config.id, getId() sets a unique id
local upgrade = upgradeMock({id = nil})
assert.is_string(upgrade:getId())
assert.not_same("", upgrade:getId())
local upgrade2 = upgradeMock({id = nil})
assert.not_same(upgrade:getId(), upgrade2:getId())
BrokerUpgrade:new() config.installMessage, getInstallMessage() fails if no player is given
local upgrade = upgradeMock({installMessage = "Foobar" })
assert.has_error(function() upgrade:getInstallMessage() end)
BrokerUpgrade:new() config.installMessage, getInstallMessage() returns nil if no installMessage is set
local upgrade = upgradeMock({installMessage = nil})
assert.is_nil(upgrade:getInstallMessage(player))
BrokerUpgrade:new() config.installMessage, getInstallMessage() returns the message if it is a function
local message = "Thanks for buying that upgrade"
local upgrade
upgrade = upgradeMock({installMessage = function(arg1, arg2)
assert.is_same(upgrade, arg1)
assert.is_same(player, arg2)
return message
end})
assert.is_same(message, upgrade:getInstallMessage(player))
BrokerUpgrade:new() config.installMessage, getInstallMessage() returns the message if it is a string
local message = "Thanks for buying that upgrade"
local upgrade = upgradeMock({installMessage = message })
assert.is_same(message, upgrade:getInstallMessage(player))
BrokerUpgrade:new() config.name, getName() fails if name is a number
assert.has_error(function() upgradeMock({name = 42}) end)
BrokerUpgrade:new() config.name, getName() fails if no name is given
assert.has_error(function() BrokerUpgrade:new({
onInstall = function() end,
}) end)
BrokerUpgrade:new() config.name, getName() returns the name if it is a string
local name = "Hello World"
local upgrade = upgradeMock({name = name})
assert.is_same(name, upgrade:getName())
BrokerUpgrade:new() config.price, getPrice() deduces the cost from the players reputation points when installed
local upgrade = upgradeMock({price = 42.0})
local player = PlayerSpaceship():setReputationPoints(100)
upgrade:install(player)
assert.is_same(58, player:getReputationPoints())
BrokerUpgrade:new() config.price, getPrice() fails if price is non-numeric
assert.has_error(function()
upgradeMock({price = "foo"})
end)
BrokerUpgrade:new() config.price, getPrice() returns 0 if config.price is not set
local upgrade = upgradeMock()
assert.is_same(0, upgrade:getPrice())
BrokerUpgrade:new() config.price, getPrice() takes the price that was set
local upgrade = upgradeMock({price = 42.0})
assert.is_same(42.0, upgrade:getPrice())
BrokerUpgrade:new() config.requiredUpgrade allows to install an upgrade if the required is installed
local required = upgradeMock({id = "required"})
local upgrade = upgradeMock({requiredUpgrade = "required"})
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(required)
assert.is_true(upgrade:canBeInstalled(player))
BrokerUpgrade:new() config.requiredUpgrade exposes the required upgrade
local required = upgradeMock({id = "required"})
local upgrade = upgradeMock({requiredUpgrade = "required"})
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(required)
assert.is_same("required", upgrade:getRequiredUpgradeString())
BrokerUpgrade:new() config.requiredUpgrade prevents the upgrade from being installed on a ship without Upgrade Tracker
local upgrade = upgradeMock({requiredUpgrade = "foobar"})
local player = PlayerSpaceship()
assert.is_false(upgrade:canBeInstalled(player))
BrokerUpgrade:new() config.requiredUpgrade prevents the upgrade to be installed if the required is not installed
local upgrade = upgradeMock({requiredUpgrade = "foobar"})
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
assert.is_false(upgrade:canBeInstalled(player))
BrokerUpgrade:new() config.unique prevents the upgrade from being installed more than once on a ship
local upgrade = upgradeMock({unique = true})
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
assert.is_true(upgrade:canBeInstalled(player))
upgrade:install(player)
assert.is_false(upgrade:canBeInstalled(player))
BrokerUpgrade:new() config.unique prevents the upgrade from being installed on a ship without Upgrade Tracker
local upgrade = upgradeMock({unique = true})
local player = PlayerSpaceship()
assert.is_false(upgrade:canBeInstalled(player))
BrokerUpgrade:new() fails if first argument is a number
assert.has_error(function() BrokerUpgrade:new(42) end)
BrokerUpgrade:new() fails if there is no install function given
assert.has_error(function() BrokerUpgrade:new(nil, function() end) end)
BrokerUpgrade:new() fails if there is no name given
assert.has_error(function() BrokerUpgrade:new("Foobar") end)
BrokerUpgrade:new() returns a valid BrokerUpgrade
local upgrade = upgradeMock()
assert.is_true(BrokerUpgrade:isUpgrade(upgrade))
BrokerUpgrade:new():canBeInstalled()
BrokerUpgrade:new():canBeInstalled() calls the callback
canBeInstalledCalled = 0
local success, msg = upgrade:canBeInstalled(player)
assert.is_same(1, canBeInstalledCalled)
BrokerUpgrade:new():canBeInstalled() fails if no player is given
canBeInstalledCalled = 0
assert.has_error(function() upgrade:canBeInstalled(42) end)
assert.is_same(0, canBeInstalledCalled)
BrokerUpgrade:new():canBeInstalled() interprets as true, when callback returns nil
local player = PlayerSpaceship()
local upgrade
upgrade = upgradeMock({canBeInstalled = function()
return nil
end})
local success, msg = upgrade:canBeInstalled(player)
assert.is_true(success)
assert.is_nil(msg)
BrokerUpgrade:new():canBeInstalled() passes through a string on failure
local player = PlayerSpaceship()
local upgrade = upgradeMock({canBeInstalled = function()
return false, "foobar"
end})
local success, msg = upgrade:canBeInstalled(player)
assert.is_false(success)
assert.is_same("foobar", msg)
BrokerUpgrade:new():canBeInstalled() removes message if it is attached to a true response
local player = PlayerSpaceship()
local upgrade = upgradeMock({canBeInstalled = function()
return true, "message"
end})
local success, msg = upgrade:canBeInstalled(player)
assert.is_true(success)
assert.is_nil(msg)
BrokerUpgrade:new():canBeInstalled() removes non-string message on failure
local player = PlayerSpaceship()
local upgrade = upgradeMock({canBeInstalled = function()
return false, 42
end})
local success, msg = upgrade:canBeInstalled(player)
assert.is_false(success)
assert.is_nil(msg)
BrokerUpgrade:new():install()
BrokerUpgrade:new():install() calls the canBeInstalled callback and the install callback
canBeInstalledCalled = 0
installCalled = 0
upgrade:install(player)
assert.is_same(1, canBeInstalledCalled)
assert.is_same(1, installCalled)
upgrade:install(player)
assert.is_same(2, canBeInstalledCalled)
assert.is_same(2, installCalled)
BrokerUpgrade:new():install() fails if no player is given
canBeInstalledCalled = 0
installCalled = 0
assert.has_error(function() upgrade:install(42) end)
assert.is_same(0, canBeInstalledCalled)
assert.is_same(0, installCalled)
BrokerUpgrade:new():install() throws error if requirement is not met
local upgrade = upgradeMock({canBeInstalled = function()
return false
end})
assert.has_error(function() upgrade:install(player) end)
Chatter
Chatter:new()
Chatter:new() config.maxRange allows to hear chatter within range
local player = PlayerSpaceship()
mockPlayers(player)
local chatter = Chatter:new({maxRange = 30000})
local ship = CpuShip():setCallSign("John Doe")
player:setPosition(0, 0)
ship:setPosition(2000, 0)
chatter:say(ship, "Hello World")
assert.is_same("John Doe: Hello World", player.shipLogs[1].message)
Chatter:new() config.maxRange can happen that players only hear half of a conversation
local player = PlayerSpaceship()
mockPlayers(player)
local chatter = Chatter:new({maxRange = 30000})
local ship1 = CpuShip():setCallSign("Alice")
local ship2 = CpuShip():setCallSign("Bob")
player:setPosition(0, 0)
ship1:setPosition(2000, 0)
ship2:setPosition(99999, 0)
chatter:converse({
{ship1, "Hey Bob. What was your password again?"},
{ship2, "12345"},
{ship1, "That's the stupidest combination I have ever heard in my life!"},
})
for i=1,15 do Cron.tick(i) end
assert.is_same("Alice: Hey Bob. What was your password again?", player.shipLogs[1].message)
assert.is_same("Alice: That's the stupidest combination I have ever heard in my life!", player.shipLogs[2].message)
Chatter:new() config.maxRange does not allow to hear chatter outside range
local player = PlayerSpaceship()
mockPlayers(player)
local chatter = Chatter:new({maxRange = 30000})
local ship = CpuShip():setCallSign("John Doe")
player:setPosition(0, 0)
ship:setPosition(99999, 0)
chatter:say(ship, "Hello World")
assert.is_same({}, player.shipLogs)
Chatter:new() config.maxRange does not limit range of non-ships
local player = PlayerSpaceship()
mockPlayers(player)
local chatter = Chatter:new({maxRange = 30000})
player:setPosition(0, 0)
chatter:say("John Doe", "Hello World")
assert.is_same("John Doe: Hello World", player.shipLogs[1].message)
Chatter:new() config.maxRange fails if maxRange is not a number
assert.has_error(function()
Chatter:new({maxRange = "foobar"})
end)
assert.has_error(function()
Chatter:new({maxRange = function() end})
end)
Chatter:new() config.maxRange works with multiple players
local player1 = PlayerSpaceship()
local player2 = PlayerSpaceship()
mockPlayers(player1, player2)
local ship = CpuShip():setCallSign("John Doe")
local chatter = Chatter:new({maxRange = 30000})
player1:setPosition(0, 0)
ship:setPosition(2000, 0)
player2:setPosition(99999, 0)
chatter:say(ship, "Hello World")
assert.is_same("John Doe: Hello World", player1.shipLogs[1].message)
assert.is_same({}, player2.shipLogs)
Chatter:new() creates a valid chatter
local chatter = Chatter:new()
assert.is_true(Chatter:isChatter(chatter))
Chatter:new() fails when config is not a table
assert.has_error(function()
Chatter:new(42)
end)
assert.has_error(function()
Chatter:new("foo")
end)
Chatter:new():converse()
Chatter:new():converse() aborts conversation if one speaker got destroyed
local player = PlayerSpaceship()
mockPlayers(player)
local chatter = Chatter:new()
local ship1 = CpuShip():setCallSign("John Doe")
local ship2 = CpuShip():setCallSign("Jack the Ripper")
chatter:converse({
{ship2, "I'm gonna destroy you"},
{ship1, "Oh no"},
{ship2, "Wait. How did you survive that?"},
})
Cron.tick(1)
assert.is_same("Jack the Ripper: I'm gonna destroy you", player.shipLogs[1].message)
ship1:destroy()
for i=1,10,1 do Cron.tick(1) end
assert.is_nil(player.shipLogs[2])
Chatter:new():converse() fails if one of items in the table is not a table
local chatter = Chatter:new()
assert.has_error(function()
chatter:converse({
{"John Doe", "Hello World"},
"Ehm... this breaks",
})
end)
Chatter:new():converse() fails if one of the messages is a number
local chatter = Chatter:new()
assert.has_error(function()
chatter:converse({
{"John Doe", "Hello World"},
{"Arthur Dent", 42},
})
end)
Chatter:new():converse() fails if one of the senders is a number
local chatter = Chatter:new()
assert.has_error(function()
chatter:converse({
{"John Doe", "Hello World"},
{42, "I got the answer"},
})
end)
Chatter:new():converse() fails when messages is nil or a string
local chatter = Chatter:new()
assert.has_error(function()
chatter:converse(nil)
end)
assert.has_error(function()
chatter:converse("This breaks")
end)
Chatter:new():converse() leaves breaks between the speakers
local player = PlayerSpaceship()
local ship = CpuShip():setCallSign("Two")
mockPlayers(player)
local chatter = Chatter:new()
chatter:converse({
{"One", "Saying six words takes three seconds."},
{ship, "Four words - two seconds."}
})
assert.is_same("One: Saying six words takes three seconds.", player.shipLogs[1].message)
Cron.tick(1.5)
assert.is_nil(player.shipLogs[2])
Cron.tick(2)
assert.is_same("Two: Four words - two seconds.", player.shipLogs[2].message)
Chatter:new():converse() sends to all player ships
local player1 = PlayerSpaceship()
local player2 = PlayerSpaceship()
local player3 = PlayerSpaceship()
mockPlayers(player1, player2, player3)
local chatter = Chatter:new()
chatter:converse({{"John Doe", "Hello World"}})
assert.is_same("John Doe: Hello World", player1.shipLogs[1].message)
assert.is_same("John Doe: Hello World", player2.shipLogs[1].message)
assert.is_same("John Doe: Hello World", player3.shipLogs[1].message)
Chatter:new():say()
Chatter:new():say() does not send the message if the ship is invalid
local player = PlayerSpaceship()
local ship = CpuShip():setCallSign("John Doe")
ship:destroy()
mockPlayers(player)
local chatter = Chatter:new()
chatter:say(ship, "Hello World")
assert.is_same({}, player.shipLogs)
Chatter:new():say() fails when message is nil or a number
local chatter = Chatter:new()
assert.has_error(function()
chatter:say("John Doe", nil)
end)
assert.has_error(function()
chatter:say("John Doe", 42)
end)
Chatter:new():say() fails when sender is nil or a number
local chatter = Chatter:new()
assert.has_error(function()
chatter:say(nil, "Hello World")
end)
assert.has_error(function()
chatter:say(42, "Hello World")
end)
Chatter:new():say() sends to all player ships
local player1 = PlayerSpaceship()
local player2 = PlayerSpaceship()
local player3 = PlayerSpaceship()
mockPlayers(player1, player2, player3)
local chatter = Chatter:new()
chatter:say("John Doe", "Hello World")
assert.is_same("John Doe: Hello World", player1.shipLogs[1].message)
assert.is_same("John Doe: Hello World", player2.shipLogs[1].message)
assert.is_same("John Doe: Hello World", player3.shipLogs[1].message)
Chatter:new():say() works with a ship as sender
local player = PlayerSpaceship()
local ship = CpuShip():setCallSign("John Doe")
mockPlayers(player)
local chatter = Chatter:new()
chatter:say(ship, "Hello World")
assert.is_same("John Doe: Hello World", player.shipLogs[1].message)
Chatter:new():say() works with a string as sender
local player = PlayerSpaceship()
mockPlayers(player)
local chatter = Chatter:new()
chatter:say("John Doe", "Hello World")
assert.is_same("John Doe: Hello World", player.shipLogs[1].message)
Chatter:newFactory()
Chatter:newFactory() fails if config.filters is not a numeric table
assert.has_error(function() Chatter:newFactory(1, function() end, {
filters = 1,
}) end)
assert.has_error(function() Chatter:newFactory(1, function() end, {
filters = "string",
}) end)
assert.has_error(function() Chatter:newFactory(1, function() end, {
filters = {foo = "bar"},
}) end)
Chatter:newFactory() fails if first parameter is not a positive integer
assert.has_error(function() Chatter:newFactory(-1, function() end) end)
assert.has_error(function() Chatter:newFactory(0, function() end) end)
assert.has_error(function() Chatter:newFactory(nil, function() end) end)
assert.has_error(function() Chatter:newFactory("string", function() end) end)
assert.has_error(function() Chatter:newFactory(SpaceStation(), function() end) end)
Chatter:newFactory() fails if second parameter is not a function
assert.has_error(function() Chatter:newFactory(1, 1) end)
assert.has_error(function() Chatter:newFactory(1, "string") end)
assert.has_error(function() Chatter:newFactory(1, nil) end)
assert.has_error(function() Chatter:newFactory(1, SpaceStation()) end)
Chatter:newFactory() fails if third parameter is not a table
assert.has_error(function() Chatter:newFactory(1, function() end, 1) end)
assert.has_error(function() Chatter:newFactory(1, function() end, "string") end)
Chatter:newFactory() should create a valid chat
local chat = Chatter:newFactory(1, function(one)
return {
{one, "Hello World"}
}
end)
assert.is_true(Chatter:isChatFactory(chat))
assert.is_same(1, chat:getCardinality())
assert.is_true(chat:areValidArguments(CpuShip()))
Chatter:newFactory():areValidArguments()
Chatter:newFactory():areValidArguments() always returns true if no filter is set
local chat = Chatter:newFactory(1, function() end)
assert.is_true(chat:areValidArguments(SpaceStation()))
assert.is_true(chat:areValidArguments(CpuShip()))
Chatter:newFactory():areValidArguments() returns false if no shipTemplateBased is given
local chat = Chatter:newFactory(1, function() end)
assert.is_false(chat:areValidArguments(1))
assert.is_false(chat:areValidArguments(Asteroid()))
Chatter:newFactory():areValidArguments() uses filter on all arguments and gives other partners to filter function
local oneCalled, twoCalled, threeCalled = 0, 0, 0
local oneArg1
local twoArg1, twoArg2
local threeArg1, threeArg2, threeArg3
local one, two, three = SpaceStation(), CpuShip(), CpuShip()
local chat = Chatter:newFactory(3, function() end, {
filters = {
function(one)
oneCalled = oneCalled + 1
oneArg1 = one
return true
end,
function(two, one)
twoCalled = twoCalled + 1
twoArg1 = two
twoArg2 = one
return true
end,
function(three, two, one)
threeCalled = threeCalled + 1
threeArg1 = three
threeArg2 = two
threeArg3 = one
return true
end,
}
})
assert.is_true(chat:areValidArguments(one, two, three))
assert.is_same(1, oneCalled)
assert.is_same(one, oneArg1)
assert.is_same(1, twoCalled)
assert.is_same(two, twoArg1)
assert.is_same(one, twoArg2)
assert.is_same(1, threeCalled)
assert.is_same(three, threeArg1)
assert.is_same(two, threeArg2)
assert.is_same(one, threeArg3)
Chatter:newFactory():areValidArguments() works with one partner
local chat = Chatter:newFactory(1, function() end, {
filters = {
function(one) return isEeStation(one) end
}
})
assert.is_true(chat:areValidArguments(SpaceStation()))
assert.is_false(chat:areValidArguments(CpuShip()))
Chatter:newFactory():areValidArguments() works with two partners
local chat = Chatter:newFactory(2, function() end, {
filters = {
function(one) return isEeStation(one) end,
function(two, _) return isEeShip(two) end,
}
})
assert.is_true(chat:areValidArguments(SpaceStation()))
assert.is_false(chat:areValidArguments(CpuShip()))
assert.is_false(chat:areValidArguments(CpuShip(), SpaceStation()))
assert.is_true(chat:areValidArguments(SpaceStation(), CpuShip()))
Chatter:newFactory():createChat()
Chatter:newFactory():createChat() gives all arguments to the factory
local factoryCalled = 0
local factoryArg1, factoryArg2
local ship1, ship2 = CpuShip(), CpuShip()
local chat = Chatter:newFactory(1, function(arg1, arg2)
factoryCalled = factoryCalled + 1
factoryArg1, factoryArg2 = arg1, arg2
return {
{arg1, "Hello World"},
{arg2, "Foobar"},
}
end)
local result = chat:createChat(ship1, ship2)
assert.is_same(1, factoryCalled)
assert.is_same(ship1, factoryArg1)
assert.is_same(ship2, factoryArg2)
assert.is_same("table", type(result))
Chatter:newNoise()
Chatter:newNoise() allows to remove chat factories
withUniverse(function()
local player = PlayerSpaceship():setCallSign("player"):setPosition(0, 0)
local ship = CpuShip():setCallSign("ship"):setPosition(1000, 0)
print(typeInspect(player:getObjectsInRange(10000)))
local chat1 = Chatter:newFactory(1, function(thisShip)
return {
{ thisShip, "Hello World"},
}
end)
local chat2 = Chatter:newFactory(1, function(thisShip)
return {
{ thisShip, "You should not see me"},
}
end)
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
noise:addChatFactory(chat2, "chat2")
noise:addChatFactory(chat1, "chat1")
noise:removeChatFactory("chat2")
for _=1,90 do Cron.tick(1) end
assert.is_same({
{ship, "Hello World"},
}, chatter:getLastMessages())
end)
Chatter:newNoise() creates a valid ChatNoise
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
assert.is_true(Chatter:isChatNoise(noise))
Chatter:newNoise() fails when chatter is not a chatter
local chatter = mockChatter()
assert.has_error(function()
Chatter:newNoise(nil)
end)
assert.has_error(function()
Chatter:newNoise("foo")
end)
assert.has_error(function()
Chatter:newNoise(42)
end)
assert.has_error(function()
Chatter:newNoise({})
end)
Chatter:newNoise() tries to prevent repetition of chats
withUniverse(function()
local player = PlayerSpaceship():setCallSign("player"):setPosition(0, 0)
local ship1 = CpuShip():setCallSign("ship"):setPosition(1000, 0)
local ship2 = CpuShip():setCallSign("ship"):setPosition(2000, 0)
-- mock chatter to easily track which chats we have seen
local chat1 = { { ship1, "one"} }
local chat2 = { { ship2, "two"} }
local secondToLastChat
local lastChat
local chatter = mockChatter()
chatter.converse = function(_, conversation)
local pretty = require 'pl.pretty'
secondToLastChat = lastChat
if conversation == chat1 then
lastChat = "norep_chat1"
elseif conversation == chat2 then
lastChat = "norep_chat2"
end
conversation = lastChat
end
assert(Chatter:isChatter(chatter))
local noise = Chatter:newNoise(chatter, {preventRepetition = true})
-- chat for first chat
noise:addChatFactory(Chatter:newFactory(1, function() return chat1 end, {filters = { function() return true end }}), "norep_chat1")
noise:addChatFactory(Chatter:newFactory(1, function() return chat2 end, {filters = { function() return true end }}), "norep_chat2")
for _=1,120 do Cron.tick(1) end
for _=1,60 * 6 * 10 do
Cron.tick(1)
assert.not_same(secondToLastChat, lastChat)
end
end)
Chatter:newNoise() works in a complex scenario
withUniverse(function()
local player = PlayerSpaceship():setCallSign("player"):setPosition(0, 0)
local ship = CpuShip():setCallSign("ship"):setPosition(1000, 0)
local station = SpaceStation():setCallSign("station"):setPosition(0, 1000)
local filterStation = function(thing) return thing == station end
local filterShip = function(thing) return thing == ship end
local filterShipOrStation = function(thing) return filterStation(thing) or filterShip(thing) end
-- mock chatter to easily track which chats we have seen
local chat1 = { {ship, "one"} }
local chat2 = { {station, "two"} }
local chat3WithShip = { {ship, "three"} }
local chat3WithStation = { {station, "three"} }
local chat4 = { {station, "four"}, {ship, "four"} }
local chat5 = { {ship, "five"}, {station, "five"} }
local chat1Seen = 0
local chat2Seen = 0
local chat3SeenWithShip = 0
local chat3SeenWithStation = 0
local chat4Seen = 0
local chat5Seen = 0
local errorsSeen = 0
local chatter = mockChatter()
chatter.converse = function(_, conversation)
local pretty = require 'pl.pretty'
if conversation == chat1 then chat1Seen = chat1Seen + 1
elseif conversation == chat2 then chat2Seen = chat2Seen + 1
elseif conversation == chat3WithShip then chat3SeenWithShip = chat3SeenWithShip + 1
elseif conversation == chat3WithStation then chat3SeenWithStation = chat3SeenWithStation + 1
elseif conversation == chat4 then chat4Seen = chat4Seen + 1
elseif conversation == chat5 then chat5Seen = chat5Seen + 1
else
errorsSeen = errorsSeen + 1
end
end
assert(Chatter:isChatter(chatter))
local noise = Chatter:newNoise(chatter)
-- chat for ship only
noise:addChatFactory(Chatter:newFactory(1,
function(one) if one == ship then return chat1 end end, {
filters = { filterShip }
}), "chat1")
-- chat for station only
noise:addChatFactory(Chatter:newFactory(1,
function(one) if one == station then return chat2 end end, {
filters = { filterStation }
}), "chat2")
-- chat for station or ship
noise:addChatFactory(Chatter:newFactory(1,
function(thisThing)
if thisThing == ship then
return chat3WithShip
elseif thisThing == station then
return chat3WithStation
end
end, {
filters = { filterShipOrStation }
}), "chat3")
-- chat for station and ship
noise:addChatFactory(Chatter:newFactory(2,
function(one, two) if one == station and two == ship then return chat4 end end, {
filters = { filterStation, filterShip }
}), "chat4")
-- chat for ship and station
noise:addChatFactory(Chatter:newFactory(2,
function(one, two) if one == ship and two == station then return chat5 end end, {
filters = { filterShip, filterStation }
}), "chat5")
for _=1,60 * 6 * 10 do Cron.tick(1) end
-- a naive check to see if results are random
assert.is_same(0, errorsSeen)
assert(chat1Seen >= 2 and chat1Seen <= 25, "chat1 should occur between 1 and 25 times. Got " .. chat1Seen)
assert(chat2Seen >= 2 and chat2Seen <= 25, "chat2 should occur between 1 and 25 times. Got " .. chat2Seen)
assert(chat3SeenWithShip >= 1 and chat3SeenWithShip <= 12, "chat3 should occur between 1 and 12 times with ship. Got " .. chat3SeenWithShip)
assert(chat3SeenWithStation >= 1 and chat3SeenWithStation <= 12, "chat3 should occur between 1 and 12 times with station. Got " .. chat3SeenWithStation)
assert(chat4Seen >= 2 and chat4Seen <= 25, "chat4 should occur between 1 and 25 times. Got " .. chat4Seen)
assert(chat5Seen >= 2 and chat5Seen <= 25, "chat5 should occur between 1 and 25 times. Got " .. chat5Seen)
end)
Chatter:newNoise() works with a chat with filters
withUniverse(function()
local player = PlayerSpaceship():setCallSign("player"):setPosition(0, 0)
local ship = CpuShip():setCallSign("ship"):setPosition(1000, 0)
local station = SpaceStation():setCallSign("station"):setPosition(0, 1000)
local chat = Chatter:newFactory(2, function(thisStation, thisShip)
return {
{ thisStation, "Who are you?"},
{ thisShip, "John Doe"},
}
end, {
filters = {
function(thing) return isEeStation(thing) end,
function(thing) return isEeShip(thing) end,
},
})
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
noise:addChatFactory(chat)
for _=1,90 do Cron.tick(1) end
assert.is_same({
{station, "Who are you?"},
{ship, "John Doe"},
}, chatter:getLastMessages())
end)
Chatter:newNoise():addChatFactory()
Chatter:newNoise():addChatFactory() allows to add a chat and returns an id
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
local factory = mockChatFactory()
local id = noise:addChatFactory(factory)
assert.is_same("string", type(id))
assert.not_same("", id)
Chatter:newNoise():addChatFactory() fails if no chat is given
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
assert.has_error(function() noise:addChatFactory(nil) end)
assert.has_error(function() noise:addChatFactory("foo") end)
assert.has_error(function() noise:addChatFactory(42) end)
assert.has_error(function() noise:addChatFactory({}) end)
Chatter:newNoise():addChatFactory() fails if the given id is not a non-empty string
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
local factory = mockChatFactory()
assert.has_error(function() noise:addChatFactory(factory, "") end)
assert.has_error(function() noise:addChatFactory(factory, 42) end)
assert.has_error(function() noise:addChatFactory(factory, {}) end)
Chatter:newNoise():addChatFactory() uses an id if it given
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
local factory = mockChatFactory()
local id = noise:addChatFactory(factory, "foobar")
assert.is_same("foobar", id)
Chatter:newNoise():getChatFactories()
Chatter:newNoise():getChatFactories() allows to remove all ChatFactories
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
local chat1 = mockChatFactory()
local chat2 = mockChatFactory()
local chat3 = mockChatFactory()
noise:addChatFactory(chat1, "one")
noise:addChatFactory(chat2, "two")
noise:addChatFactory(chat3, "three")
for id, _ in pairs(noise:getChatFactories()) do
noise:removeChatFactory(id)
end
assert.is_same({}, noise:getChatFactories())
Chatter:newNoise():getChatFactories() returns all ChatFactories
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
local chat1 = mockChatFactory()
local chat2 = mockChatFactory()
local chat3 = mockChatFactory()
noise:addChatFactory(chat1, "one")
noise:addChatFactory(chat2, "two")
noise:addChatFactory(chat3, "three")
assert.is_same({
one = chat1,
two = chat2,
three = chat3,
}, noise:getChatFactories())
Chatter:newNoise():removeChatFactory()
Chatter:newNoise():removeChatFactory() allows to remove a chat
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
local factory = mockChatFactory()
noise:addChatFactory(factory, "id")
noise:removeChatFactory("id")
Chatter:newNoise():removeChatFactory() allows to remove a chat when it has not been set before
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
noise:removeChatFactory("id")
Chatter:newNoise():removeChatFactory() fails if the given id is not a non-empty string
local chatter = mockChatter()
local noise = Chatter:newNoise(chatter)
assert.has_error(function() noise:removeChatFactory("") end)
assert.has_error(function() noise:removeChatFactory(42) end)
assert.has_error(function() noise:removeChatFactory({}) end)
Comms
Comms:merchantFactory()
Comms:merchantFactory() fails if any of the required configs is missing
for k, _ in pairs(requiredConfig) do
local config = Util.deepCopy(requiredConfig)
config[k] = nil
assert.has_error(function() Comms:merchantFactory(config) end)
end
Comms:merchantFactory() should create a valid Comms:newReply
local merchantComms = Comms:merchantFactory(requiredConfig)
assert.is_true(Comms:isReply(merchantComms))
Comms:missionBrokerFactory()
Comms:missionBrokerFactory() fails if any of the required configs is missing
for k, _ in pairs(requiredConfig) do
local config = Util.deepCopy(requiredConfig)
config[k] = nil
assert.has_error(function() Comms:missionBrokerFactory(config) end)
end
Comms:missionBrokerFactory() should create a valid Comms:newReply
local missionComms = Comms:missionBrokerFactory(requiredConfig)
assert.is_true(Comms:isReply(missionComms))
Comms:newReply()
Comms:newReply() can create a reply
local reply = Comms:newReply("Foobar", function() end)
assert.is_true(Comms:isReply(reply))
assert.is_same("Foobar", reply:getWhatPlayerSays(station, player))
Comms:newReply() can create a reply condition check
local condition = function() return true end
local reply = Comms:newReply("Foobar", nil, condition)
assert.is_true(Comms:isReply(reply))
assert.is_true(reply:checkCondition(station, player))
Comms:newReply() can create a reply with comms screen as return
local screen = Comms:newScreen("Hello World")
local reply = Comms:newReply("Foobar", screen)
assert.is_true(Comms:isReply(reply))
assert.is_same(screen, reply:getNextScreen(station, player))
Comms:newReply() can create a reply with function for name instead of string
local name = function() return "Foobar" end
local reply = Comms:newReply(name, function() end)
assert.is_true(Comms:isReply(reply))
assert.is_same("Foobar", reply:getWhatPlayerSays(station, player))
Comms:newReply() fails if first argument is a number
assert.has_error(function() Comms:newReply(42, function() end) end)
Comms:newReply() fails if second argument is a number
assert.has_error(function() Comms:newReply("Foobar", 42) end)
Comms:newReply() fails if third argument is a number
assert.has_error(function() Comms:newReply("Foobar", nil, 42) end)
Comms:newScreen()
Comms:newScreen() allows to add replies
local screen = Comms:newScreen("Hello World")
:addReply(Comms:newReply("One", function() end))
:addReply(Comms:newReply("Two", function() end))
assert.is_true(Comms:isScreen(screen))
assert.is_same(2, Util.size(screen:getHowPlayerCanReact(station, player)))
Comms:newScreen() can create a comms screen that it validates
local screen = Comms:newScreen("Hello World", {
Comms:newReply("One", function() end),
Comms:newReply("Two", function () end),
})
assert.is_true(Comms:isScreen(screen))
assert.is_same(2, Util.size(screen:getHowPlayerCanReact(station, player)))
Cron
Cron:addDelay()
Cron:addDelay() allows to override the delay of a regular
Cron.regular("foobar", function() end, 3, 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.addDelay("foobar", 1)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(3, Cron.getDelay("foobar"))
Cron.addDelay("foobar", 2)
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron:addDelay() allows to set the delay when using once()
Cron.once("foobar", function() end, 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.addDelay("foobar", 1)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.tick(1)
Cron:addDelay() fails silently when an unknown Cron is set
Cron.addDelay("doesnotexist", 5)
assert.is_nil(Cron.getDelay("doesnotexist"))
Cron:getDelay()
Cron:getDelay() allows to get the delay until the call of function when using once()
Cron.once("foobar", function() end, 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_nil(Cron.getDelay("foobar"))
Cron:getDelay() allows to get the delay until the next call of function when using regular()
Cron.regular("foobar", function() end, 3, 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(3, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(2, Cron.getDelay("foobar"))
Cron:getDelay() returns nil for an undefined cron
assert.is_nil(Cron.getDelay("doesnotexist"))
Cron:now()
Cron:now() gives the current time
local now = Cron.now()
assert.is_true(isNumber(now))
Cron.tick(1)
assert.is_same(1, Cron.now() - now)
Cron:once()
Cron:once() allows to remove a callback by calling one callback
local ids = {
"foo",
"bar",
}
for _, id in pairs(ids) do
Cron.once(id, function()
for _, id in pairs(ids) do
Cron.abort(id)
end
end, 1)
end
assert.not_has_error(function()
Cron.tick(1)
end)
Cron:once() allows to replace a once with regular on call
local called = 0
Cron.once("foobar", function()
called = called + 1
Cron.regular("foobar", function()
called = called + 1
end, 1)
end, 1)
assert.is_same(0, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(3, called)
Cron:once() allows to set a new callback after removing the current one
local i = 0
local fun
fun = function()
i = i+1
if i < 10 then Cron.once(fun, 1) end
end
Cron.once(fun, 1)
assert.not_has_error(function()
for i=1,10 do Cron.tick(1) end
end)
Cron:once() is possible to remove a function with a generated name
local called = false
local cronId = Cron.once(function() called = true end, 5)
assert.is_false(called)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(called)
Cron.abort(cronId)
Cron.tick(1)
assert.is_false(called)
Cron:once() is possible to remove a function with a name
local called = false
Cron.once("foobar", function() called = true end, 5)
assert.is_false(called)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(called)
Cron.abort("foobar")
Cron.tick(1)
assert.is_false(called)
Cron:once() will call a function after a certain time
local called = false
Cron.once(function() called = true end, 5)
assert.is_false(called)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(called)
Cron.tick(1)
assert.is_true(called)
Cron:once() will call a function with a name
local called = false
Cron.once("foobar", function() called = true end, 5)
assert.is_false(called)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(called)
Cron.tick(1)
assert.is_true(called)
Cron:once() will call the function only once
local called = 0
Cron.once(function() called = called + 1 end, 1)
assert.is_same(0, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(1, called)
Cron:once() will get the elapsed time as argument to the function
local calledArg = nil
Cron.once(function(_, arg)
calledArg = arg
end, 5)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1.2)
assert.is_near(5.2, calledArg, 0.0001)
Cron:regular()
Cron:regular() a function can be stopped from outside
local called = 0
local cronId = Cron.regular(function() called = called + 1 end, 1)
assert.is_same(0, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.abort(cronId)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(1, called)
Cron:regular() a function can remove itself
local called = 0
Cron.regular("foobar", function()
if called >= 3 then Cron.abort("foobar") else called = called + 1 end
end, 1)
assert.is_same(0, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(3, called)
Cron.tick(1)
assert.is_same(3, called)
Cron.tick(1)
assert.is_same(3, called)
Cron:regular() allows the callback function to return the interval for the next try
local called = 0
Cron.regular("foobar", function()
called = called + 1
if called == 2 then return 3 end
end, 1)
assert.is_same(0, called)
Cron.tick(0.5)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(3, called)
Cron.tick(1)
assert.is_same(4, called)
Cron:regular() the function gets its generated id as first parameter
local theFirstParameter
Cron.regular(function(cronId)
theFirstParameter = cronId
end, 1)
Cron.tick(1)
assert.not_nil(theFirstParameter)
assert.is_string(theFirstParameter)
assert.not_same("", theFirstParameter)
Cron:regular() the function gets its id as first parameter
local theFirstParameter
Cron.regular("foobar", function(cronId)
theFirstParameter = cronId
end, 1)
Cron.tick(1)
assert.is_same("foobar", theFirstParameter)
Cron:regular() will call a function at a regular interval
local called = 0
Cron.regular("foobar", function() called = called + 1 end, 2)
assert.is_same(0, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(3, called)
Cron.tick(1)
assert.is_same(3, called)
Cron:regular() will call a function at a regular interval with delay
local called = 0
Cron.regular("foobar", function() called = called + 1 end, 2, 2)
assert.is_same(0, called)
Cron.tick(1)
assert.is_same(0, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(1)
assert.is_same(3, called)
Cron:regular() will call a function at every tick
local called = 0
Cron.regular(function() called = called + 1 end)
assert.is_same(0, called)
Cron.tick(10)
assert.is_same(1, called)
Cron.tick(1)
assert.is_same(2, called)
Cron.tick(0.5)
assert.is_same(3, called)
Cron.tick(0.1)
assert.is_same(4, called)
Cron.tick(0.01)
assert.is_same(5, called)
Cron:regular() will get the elapsed time as argument to the function
local called, calledArg = 0, nil
Cron.regular(function(_, arg)
called = called + 1
calledArg = arg
end, 1, 1)
Cron.tick(1)
assert.is_same(1, called)
assert.is_same(1, calledArg)
Cron.tick(1.2)
assert.is_same(2, called)
assert.is_near(1.2, calledArg, 0.0001)
Cron.tick(0.7)
Cron.tick(0.7)
assert.is_same(3, called)
assert.is_near(1.4, calledArg, 0.0001)
Cron:setDelay()
Cron:setDelay() allows to override the delay of a regular
Cron.regular("foobar", function() end, 3, 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.setDelay("foobar", 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(3, Cron.getDelay("foobar"))
Cron.setDelay("foobar", 5)
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron:setDelay() allows to set the delay when using once()
Cron.once("foobar", function() end, 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.setDelay("foobar", 5)
assert.is_same(5, Cron.getDelay("foobar"))
Cron.tick(1)
assert.is_same(4, Cron.getDelay("foobar"))
Cron.tick(1)
Cron:setDelay() fails silently when an unknown Cron is set
Cron.setDelay("doesnotexist", 5)
assert.is_nil(Cron.getDelay("doesnotexist"))
EventHandler
EventHandler:new()
EventHandler:new() config.allowedEvents allows to register and fire events from the whitelist
local eh = EventHandler:new({allowedEvents = {"foo"}})
eh:register("foo", function() end)
eh:fire("foo")
EventHandler:new() config.allowedEvents fails if a different event is registered
local eh = EventHandler:new({allowedEvents = {"foo"}})
assert.has_error(function()
eh:register("bar", function() end)
end)
EventHandler:new() config.allowedEvents fails if the config does not contain a table
assert.has_error(function()
EventHandler:new({allowedEvents = 42})
end)
EventHandler:new() config.allowedEvents fails if the config does not contain strings
assert.has_error(function()
EventHandler:new({allowedEvents = {"foo", "bar", 42}})
end)
EventHandler:new() fails if config is not a table
assert.has_error(function()
EventHandler:new(42)
end)
EventHandler:new():fire()
EventHandler:new():fire() allows subsequent listener to modify the argument
local secret = {name=""}
local eh = EventHandler:new()
eh:register("test", function(self, argument) argument.name = argument.name .. "a" end)
eh:register("test", function(self, argument) argument.name = argument.name .. "b" end)
eh:register("test", function(self, argument) argument.name = argument.name .. "c" end)
eh:fire("test", secret)
assert.is.equal("abc", secret.name)
EventHandler:new():fire() calls all listeners even if one raises an error
local called = false
local eh = EventHandler:new()
eh:register("test", function() error("boom") end)
eh:register("test", function() called = true end)
eh:fire("test")
assert.is_true(called)
EventHandler:new():fire() calls the listeners in the order they where registered
local result = ""
local eh = EventHandler:new()
eh:register("test", function() result = result .. "a" end)
eh:register("test", function() result = result .. "b" end)
eh:register("test", function() result = result .. "c" end)
eh:fire("test")
assert.is.equal("abc", result)
eh:register("test", function() result = result .. "d" end)
eh:register("test", function() result = result .. "e" end)
eh:register("test", function() result = result .. "f" end)
result = ""
eh:fire("test")
assert.is.equal("abcdef", result)
eh:register("test", function() result = result .. "g" end)
eh:register("test", function() result = result .. "h" end)
eh:register("test", function() result = result .. "i" end)
result = ""
eh:fire("test")
assert.is.equal("abcdefghi", result)
EventHandler:new():fire() does not call an event twice if "unique" is set
local called = 0
local eh = EventHandler:new({
unique = true,
})
eh:register("test", function() called = called + 1 end)
eh:fire("test")
assert.is_same(1, called)
eh:fire("test")
assert.is_same(1, called)
EventHandler:new():fire() fails if eventName is not a string
assert.has_error(function()
EventHandler:new():fire(42)
end)
EventHandler:new():fire() fails if no eventName is given
assert.has_error(function()
EventHandler:new():fire()
end)
EventHandler:new():fire() gives an additional argument to the listeners
local secret = "Hello World"
local gottenArgument
local eh = EventHandler:new()
eh:register("test", function(self, argument) gottenArgument = argument end)
eh:fire("test", secret)
assert.is.equal(secret, gottenArgument)
EventHandler:new():fire() passes if no event is registered
EventHandler:new():fire("test")
EventHandler:new():register()
EventHandler:new():register() allow to register a listener
local called = 0
local eh = EventHandler:new()
eh:register("test", function() called = called + 1 end)
eh:fire("test")
assert.is.equal(1, called)
EventHandler:new():register() allow to register multiple listeners to the event
local called = 0
local eh = EventHandler:new()
eh:register("test", function() called = called + 1 end)
eh:register("test", function() called = called + 2 end)
eh:fire("test")
assert.is.equal(3, called)
EventHandler:new():register() allows to set priority of events
local result = ""
local eh = EventHandler:new()
eh:register("test", function() result = result .. "b" end, 20)
eh:register("test", function() result = result .. "c" end, 30)
eh:register("test", function() result = result .. "a" end, 10)
eh:fire("test")
assert.is.equal("abc", result)
EventHandler:new():register() can handle a mix of priorities and registration order
local result = ""
local eh = EventHandler:new()
eh:register("test", function() result = result .. "c" end)
eh:register("test", function() result = result .. "g" end, 50)
eh:register("test", function() result = result .. "a" end, -10)
eh:register("test", function() result = result .. "h" end, 50)
eh:register("test", function() result = result .. "d" end)
eh:register("test", function() result = result .. "b" end, -10)
eh:register("test", function() result = result .. "f" end, 20)
eh:register("test", function() result = result .. "e" end)
eh:fire("test")
assert.is.equal("abcdefgh", result)
EventHandler:new():register() default priority is 0
local result = ""
local eh = EventHandler:new()
eh:register("test", function() result = result .. "b" end)
eh:register("test", function() result = result .. "c" end, 10)
eh:register("test", function() result = result .. "a" end, -10)
eh:fire("test")
assert.is.equal("abc", result)
EventHandler:new():register() fails if eventName is not a string
local eh = EventHandler:new()
assert.has_error(function()
eh:register(42, function() end)
end)
EventHandler:new():register() fails if eventName is not given
local eh = EventHandler:new()
assert.has_error(function()
eh:register()
end)
EventHandler:new():register() fails if handler is not a function
local eh = EventHandler:new()
assert.has_error(function()
eh:register("foo", "invalid")
end)
EventHandler:new():register() fails if handler is not given
local eh = EventHandler:new()
assert.has_error(function()
eh:register("foo")
end)
EventHandler:new():register() fails if priority is not a number
local eh = EventHandler:new()
assert.has_error(function()
eh:register("foo", function() end, "foo")
end)
EventHandler:new():register() issues a warning if an event listener is added for an event that has already been fired and config.unique is set
withLogCatcher(function(logs)
local eh = EventHandler:new({
unique = true,
})
eh:register("test", function() end)
eh:fire("test")
assert.is_nil(logs:popLastWarning())
eh:register("test", function() end)
assert.is_not_nil(logs:popLastWarning())
end)
Fleet
Fleet:new()
Fleet:new() GM can issue an order to a wingman that is not changed if the leader is killed
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5})
ship5:orderDefendLocation(42, 4200)
Cron.tick(1)
assert.is_same("Defend Location", ship5:getOrder())
ship1:destroy()
assert.is_same("Defend Location", ship5:getOrder())
Fleet:new() GM can issue new orders to fleet leader and are carried out if fleet leader is killed
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
Fleet:new({ship1, ship2, ship3})
ship1:orderDefendLocation(42, 4200)
Cron.tick(1)
ship1:destroy()
Cron.tick(1)
assert.is_same("Defend Location", ship2:getOrder())
assert.is_same(42, ship2:getOrderTargetLocationX())
assert.is_same(4200, ship2:getOrderTargetLocationY())
assert.is_same("Fly in formation", ship3:getOrder())
Fleet:new() GM can reintegrate a wingman by setting order to Idle or Roaming
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5})
ship4:orderDefendLocation(42, 4200)
ship5:orderDefendLocation(42, 4200)
Cron.tick(1)
ship4:orderIdle()
ship5:orderRoaming()
Cron.tick(1)
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-1400, ship4:getOrderTargetLocationY())
assert.is_same(0, ship4:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(1400, ship5:getOrderTargetLocationY())
assert.is_same(0, ship5:getOrderTargetLocationX())
Fleet:new() allows to give config
local ship = CpuShip()
local fleet = Fleet:new({ship}, {id = "foobar"})
assert.is_true(Fleet:isFleet(fleet))
Fleet:new() circle formation fills gaps when the leader is killed and the highest ranking ship is left
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5}, {formation = "circle"})
ship1:destroy()
Cron.tick(1)
assert.is_same("Fly in formation", ship3:getOrder())
assert.is_same(354, Util.round(ship3:getOrderTargetLocationY()))
assert.is_same(-354, Util.round(ship3:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-354, Util.round(ship4:getOrderTargetLocationY()))
assert.is_same(-354, Util.round(ship4:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(-354, Util.round(ship5:getOrderTargetLocationY()))
assert.is_same(354, Util.round(ship5:getOrderTargetLocationX()))
Fleet:new() circle formation keep position when a wingman is destroyed
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5}, {formation = "circle"})
ship3:destroy()
Cron.tick(1)
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same(354, Util.round(ship2:getOrderTargetLocationY()))
assert.is_same(354, Util.round(ship2:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship3:getOrder())
assert.is_same(354, Util.round(ship3:getOrderTargetLocationY()))
assert.is_same(-354, Util.round(ship3:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-354, Util.round(ship4:getOrderTargetLocationY()))
assert.is_same(-354, Util.round(ship4:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(-354, Util.round(ship5:getOrderTargetLocationY()))
assert.is_same(354, Util.round(ship5:getOrderTargetLocationX()))
Fleet:new() circle formation lets them fly around the leader
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5}, {formation = "circle"})
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same(354, Util.round(ship2:getOrderTargetLocationY()))
assert.is_same(354, Util.round(ship2:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship3:getOrder())
assert.is_same(354, Util.round(ship3:getOrderTargetLocationY()))
assert.is_same(-354, Util.round(ship3:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-354, Util.round(ship4:getOrderTargetLocationY()))
assert.is_same(-354, Util.round(ship4:getOrderTargetLocationX()))
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(-354, Util.round(ship5:getOrderTargetLocationY()))
assert.is_same(354, Util.round(ship5:getOrderTargetLocationX()))
Fleet:new() creates a valid fleet
local fleet = Fleet:new({CpuShip(), CpuShip()})
assert.is_true(Fleet:isFleet(fleet))
Fleet:new() does not allow to add ships that are already in a fleet
local ship = CpuShip()
Fleet:new({ship})
assert.has_error(function() Fleet:new({ship}) end)
Fleet:new() fails if a station is given
assert.has_error(function() Fleet:new({CpuShip(), SpaceStation()}) end)
Fleet:new() fails if id is not a string
local ship = CpuShip()
assert.has_error(function() Fleet:new({ship}, {id = 42}) end)
Fleet:new() fails if the config is not a table
local ship = CpuShip()
assert.has_error(function() Fleet:new({ship}, "This breaks") end)
Fleet:new() fails silently when fleet is invalid
local ship1 = CpuShip()
local fleet = Fleet:new({ship1})
ship1:destroy()
fleet:orderRoaming()
Fleet:new() gives all ships the fleet trait
local ship1 = CpuShip()
local ship2 = CpuShip()
Fleet:new({ship1, ship2})
assert.is_true(Ship:hasFleet(ship1))
assert.is_true(Ship:hasFleet(ship2))
Fleet:new() issues complex orders to fleet leader
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local station = SpaceStation()
local fleet = Fleet:new({ship1, ship2, ship3})
fleet:orderDefendTarget(station)
assert.is_same("Defend Target", ship1:getOrder())
assert.is_same(station, ship1:getOrderTarget())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
Fleet:new() issues orders to fleet leader
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local fleet = Fleet:new({ship1, ship2, ship3})
fleet:orderRoaming()
assert.is_same("Roaming", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
Fleet:new() orders all ships to fly in formation
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
Fleet:new({ship1, ship2, ship3})
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
Fleet:new() row formation fills gaps when a wingman is killed
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5})
ship3:destroy()
Cron.tick(1)
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same(-700, ship2:getOrderTargetLocationY())
assert.is_same(0, ship2:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-1400, ship4:getOrderTargetLocationY())
assert.is_same(0, ship4:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(700, ship5:getOrderTargetLocationY())
assert.is_same(0, ship5:getOrderTargetLocationX())
Fleet:new() row formation fills gaps when the leader is killed and the highest ranking ship is left
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5})
ship1:destroy()
Cron.tick(1)
assert.is_same("Fly in formation", ship3:getOrder())
assert.is_same(700, ship3:getOrderTargetLocationY())
assert.is_same(0, ship3:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-700, ship4:getOrderTargetLocationY())
assert.is_same(0, ship4:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(1400, ship5:getOrderTargetLocationY())
assert.is_same(0, ship5:getOrderTargetLocationX())
Fleet:new() row formation fills gaps when the leader is killed and the highest ranking ship is right
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5})
ship2:destroy()
ship1:destroy()
Cron.tick(1)
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-700, ship4:getOrderTargetLocationY())
assert.is_same(0, ship4:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(700, ship5:getOrderTargetLocationY())
assert.is_same(0, ship5:getOrderTargetLocationX())
Fleet:new() row formation lets them fly in a nuke-friendly formation
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local ship4 = CpuShip()
local ship5 = CpuShip()
Fleet:new({ship1, ship2, ship3, ship4, ship5})
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same(-700, ship2:getOrderTargetLocationY())
assert.is_same(0, ship2:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship3:getOrder())
assert.is_same(700, ship3:getOrderTargetLocationY())
assert.is_same(0, ship3:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship4:getOrder())
assert.is_same(-1400, ship4:getOrderTargetLocationY())
assert.is_same(0, ship4:getOrderTargetLocationX())
assert.is_same("Fly in formation", ship5:getOrder())
assert.is_same(1400, ship5:getOrderTargetLocationY())
assert.is_same(0, ship5:getOrderTargetLocationX())
Fleet:new() transfers the order if the leader is killed
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local fleet = Fleet:new({ship1, ship2, ship3})
fleet:orderRoaming()
ship1:destroy()
Cron.tick(1)
assert.is_same("Roaming", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
Fleet:new():countShips()
Fleet:new():countShips() counts all valid ships
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local fleet = Fleet:new({ship1, ship2, ship3})
assert.is_same(3, fleet:countShips())
ship1:destroy()
assert.is_same(2, fleet:countShips())
ship3:destroy()
assert.is_same(1, fleet:countShips())
ship2:destroy()
assert.is_same(0, fleet:countShips())
Fleet:new():getId()
Fleet:new():getId() generates an id if none is given
local ship = CpuShip()
local fleet = Fleet:new({ship})
assert.is_true(isString(fleet:getId()))
assert.is_not_same("", fleet:getId())
Fleet:new():getId() returns the given id
local ship = CpuShip()
local fleet = Fleet:new({ship}, {id = "foobar"})
assert.is_same("foobar", fleet:getId())
Fleet:new():getLeader()
Fleet:new():getLeader() returns the highest ranking valid ship
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local fleet = Fleet:new({ship1, ship2, ship3})
assert.is_same(ship1, fleet:getLeader())
ship1:destroy()
assert.is_same(ship2, fleet:getLeader())
Fleet:new():getShips()
Fleet:new():getShips() does not allow to manipulate the result
local ship = CpuShip()
local fleet = Fleet:new({ship})
local ships = fleet:getShips()
table.insert(ships, CpuShip())
assert.is_same({ship}, fleet:getShips())
Fleet:new():getShips() returns all valid ships
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local fleet = Fleet:new({ship1, ship2, ship3})
assert.contains_value(ship1, fleet:getShips())
assert.contains_value(ship2, fleet:getShips())
assert.contains_value(ship3, fleet:getShips())
ship1:destroy()
assert.not_contains_value(ship1, fleet:getShips())
assert.contains_value(ship2, fleet:getShips())
assert.contains_value(ship3, fleet:getShips())
ship2:destroy()
assert.not_contains_value(ship1, fleet:getShips())
assert.not_contains_value(ship2, fleet:getShips())
assert.contains_value(ship3, fleet:getShips())
ship3:destroy()
assert.is_same({}, fleet:getShips())
Fleet:new():isValid()
Fleet:new():isValid() it returns true as long as there are valid ships
local ship1 = CpuShip()
local ship2 = CpuShip()
local ship3 = CpuShip()
local fleet = Fleet:new({ship1, ship2, ship3})
assert.is_true(fleet:isValid())
ship1:destroy()
assert.is_true(fleet:isValid())
ship2:destroy()
assert.is_true(fleet:isValid())
ship3:destroy()
assert.is_false(fleet:isValid())
Fleet:withOrderQueue()
Fleet:withOrderQueue() fails if parameter is not a fleet
assert.has_error(function()
Fleet:withOrderQueue()
end)
assert.has_error(function()
Fleet:withOrderQueue(42)
end)
assert.has_error(function()
Fleet:withOrderQueue(SpaceStation())
end)
assert.has_error(function()
Fleet:withOrderQueue(CpuShip())
end)
Fleet:withOrderQueue() should create a fleet with order queue
local fleet = Fleet:new({CpuShip(), CpuShip(), CpuShip()})
Fleet:withOrderQueue(fleet)
assert.is_true(Fleet:hasOrderQueue(fleet))
Fleet:withOrderQueue():abortCurrentOrder()
Fleet:withOrderQueue():abortCurrentOrder() fleet idles if there is no next order
local fleet = Fleet:new({CpuShip(), CpuShip(), CpuShip()})
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:flyTo(1000, 0, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end
})
fleet:addOrder(order)
assert.is_same("Fly towards", fleet:getLeader():getOrder())
assert.is_same(0, onAbortCalled)
fleet:abortCurrentOrder()
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("user", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Fleet:withOrderQueue():flushOrders()
Fleet:withOrderQueue():flushOrders() fleet does not carry out any new orders after the one is completed
local fleet = Fleet:new({CpuShip(), CpuShip(), CpuShip()})
Fleet:withOrderQueue(fleet)
fleet:getLeader():setPosition(0, 0)
fleet:addOrder(Order:flyTo(1000, 0))
fleet:addOrder(Order:flyTo(2000, 0))
assert.is_same("Fly towards", fleet:getLeader():getOrder())
assert.is_same({1000, 0}, {fleet:getLeader():getOrderTargetLocation()})
Cron.tick(1)
fleet:flushOrders()
fleet:getLeader():setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Fly towards", fleet:getLeader():getOrder())
assert.is_same({1000, 0}, {fleet:getLeader():getOrderTargetLocation()})
Fleet:withOrderQueue():forceOrderNow()
Fleet:withOrderQueue():forceOrderNow() executes a fleet order immediately
local fleet = Fleet:new({CpuShip(), CpuShip(), CpuShip()})
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:flyTo(1000, 0, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end
})
fleet:addOrder(order)
fleet:addOrder(Order:flyTo(2000, 0))
fleet:addOrder(Order:flyTo(3000, 0))
fleet:addOrder(Order:flyTo(4000, 0))
assert.is_same("Fly towards", fleet:getLeader():getOrder())
assert.is_same(0, onAbortCalled)
fleet:forceOrderNow(Order:flyTo(0, 1000))
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("user", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Fly towards", fleet:getLeader():getOrder())
assert.is_same({0, 1000}, {fleet:getLeader():getOrderTargetLocation()})
Generic
Generic:withTags()
Generic:withTags():addTag()
Generic:withTags():addTags()
Generic:withTags():getTags()
Generic:withTags():hasTag()
Generic:withTags():removeTag()
Generic:withTags():removeTags()
Menu
Menu:newItem()
Mission
Mission:allOf()
Mission:allOf() fails if any sub mission fails
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local mission = Mission:allOf(subMission1, subMission2, subMission3)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
assert.is_same("new", subMission3:getState())
mission:accept()
assert.is_same("accepted", mission:getState())
assert.is_same("accepted", subMission1:getState())
assert.is_same("accepted", subMission2:getState())
assert.is_same("accepted", subMission3:getState())
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("started", subMission3:getState())
subMission1:success()
assert.is_same("started", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("started", subMission3:getState())
subMission2:fail()
assert.is_same("failed", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("failed", subMission2:getState())
assert.is_same("failed", subMission3:getState())
Mission:allOf() makes all sub missions fail if the wrapper mission is set to fail
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local mission = Mission:allOf(subMission1, subMission2, subMission3)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
assert.is_same("new", subMission3:getState())
mission:accept()
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("started", subMission3:getState())
subMission2:success()
assert.is_same("successful", subMission2:getState())
mission:fail()
assert.is_same("failed", mission:getState())
assert.is_same("failed", subMission1:getState())
assert.is_same("successful", subMission2:getState())
assert.is_same("failed", subMission3:getState())
Mission:allOf() makes all sub missions successful if the wrapper mission is set to success
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local mission = Mission:allOf(subMission1, subMission2, subMission3)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
assert.is_same("new", subMission3:getState())
mission:accept()
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("started", subMission3:getState())
subMission2:success()
assert.is_same("successful", subMission2:getState())
mission:success()
assert.is_same("successful", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("successful", subMission2:getState())
assert.is_same("successful", subMission3:getState())
Mission:allOf() makes every sub mission a PlayerMission if it is a PlayerMission itself
local subMission1 = Mission:new()
local subMission2 = Mission:new()
Mission:forPlayer(subMission2)
local subMission3 = Mission:new()
local player = PlayerSpaceship()
local mission = Mission:allOf(subMission1, subMission2, subMission3)
Mission:forPlayer(mission)
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_true(Mission:isPlayerMission(subMission1))
assert.is_same(player, subMission1:getPlayer())
-- subMission2 was already a player mission, but the player should be set anyways
assert.is_true(Mission:isPlayerMission(subMission2))
assert.is_same(player, subMission2:getPlayer())
assert.is_true(Mission:isPlayerMission(subMission3))
assert.is_same(player, subMission3:getPlayer())
Mission:allOf() starts all the missions and finishes if all are completed (happy case)
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local mission = Mission:allOf(subMission1, subMission2, subMission3)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
assert.is_same("new", subMission3:getState())
mission:accept()
assert.is_same("accepted", mission:getState())
assert.is_same("accepted", subMission1:getState())
assert.is_same("accepted", subMission2:getState())
assert.is_same("accepted", subMission3:getState())
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("started", subMission3:getState())
subMission1:success()
assert.is_same("started", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("started", subMission3:getState())
subMission3:success()
assert.is_same("started", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("successful", subMission3:getState())
subMission2:success()
assert.is_same("successful", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("successful", subMission2:getState())
assert.is_same("successful", subMission3:getState())
Mission:allOf():allOf()
Mission:allOf():allOf() allows to set config
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local onAcceptCalled = false
local mission = Mission:allOf(subMission1, subMission2, {onAccept = function()
onAcceptCalled = true
end})
mission:accept()
assert.is_true(onAcceptCalled)
Mission:allOf():allOf() creates a valid mission and sets the parent mission
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local mission = Mission:allOf(subMission1, subMission2)
assert.is_true(Mission:isMission(mission))
assert.is_true(Mission:isSubMission(subMission1))
assert.is_same(mission, subMission1:getParentMission())
assert.is_true(Mission:isSubMission(subMission2))
assert.is_same(mission, subMission2:getParentMission())
Mission:allOf():allOf() fails if any sub mission is already part of another mission container
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local subMission4 = Mission:new()
Mission:allOf(subMission1, subMission4)
assert.has_error(function() Mission:allOf(subMission1, subMission2, subMission3) end)
Mission:allOf():allOf() fails if any sub mission is not "new"
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
subMission3:accept()
assert.has_error(function() Mission:allOf(subMission1, subMission2, subMission3) end)
subMission3:start()
assert.has_error(function() Mission:allOf(subMission1, subMission2, subMission3) end)
subMission3:success()
assert.has_error(function() Mission:allOf(subMission1, subMission2, subMission3) end)
Mission:allOf():allOf() fails if no sub missions are given
assert.has_error(function()
Mission:allOf()
end)
Mission:allOf():allOf() fails if no sub missions are given
assert.has_error(function()
Mission:allOf()
end)
Mission:allOf():allOf() fails on invalid parameters
assert.has_error(function() Mission:allOf(42) end)
assert.has_error(function() Mission:allOf({}) end)
assert.has_error(function() Mission:allOf("broken") end)
assert.has_error(function() Mission:allOf(CpuShip()) end)
Mission:chain()
Mission:chain() fails if the first sub mission fails
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local mission = Mission:chain(subMission1, subMission2)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:accept()
assert.is_same("accepted", mission:getState())
assert.is_same("accepted", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("new", subMission2:getState())
subMission1:fail()
assert.is_same("failed", mission:getState())
assert.is_same("failed", subMission1:getState())
-- subMission2 state is undefined
Mission:chain() fails if the second sub mission fails
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local mission = Mission:chain(subMission1, subMission2)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:accept()
assert.is_same("accepted", mission:getState())
assert.is_same("accepted", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("new", subMission2:getState())
subMission1:success()
assert.is_same("started", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("started", subMission2:getState())
subMission2:fail()
assert.is_same("failed", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("failed", subMission2:getState())
Mission:chain() makes every sub mission a PlayerMission if it is a PlayerMission itself
local subMission1 = Mission:new()
local subMission2 = Mission:new()
Mission:forPlayer(subMission2)
local player = PlayerSpaceship()
local mission = Mission:chain(subMission1, subMission2)
Mission:forPlayer(mission)
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_true(Mission:isPlayerMission(subMission1))
assert.is_same(player, subMission1:getPlayer())
subMission1:success()
-- subMission2 was already a player mission, but the player should be set anyways
assert.is_true(Mission:isPlayerMission(subMission2))
assert.is_same(player, subMission2:getPlayer())
Mission:chain() makes the current sub mission fail if the wrapper mission is set to failure
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local mission = Mission:chain(subMission1, subMission2)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:accept()
assert.is_same("accepted", mission:getState())
assert.is_same("accepted", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:fail()
assert.is_same("failed", mission:getState())
assert.is_same("failed", subMission1:getState())
assert.is_same("new", subMission2:getState())
-- behavior on any further sub mission state changes are undefined
Mission:chain() makes the current sub mission successful if the wrapper mission is set to success
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local mission = Mission:chain(subMission1, subMission2)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:accept()
assert.is_same("accepted", mission:getState())
assert.is_same("accepted", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("new", subMission2:getState())
mission:success()
assert.is_same("successful", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("new", subMission2:getState())
-- behavior on any further sub mission state changes are undefined
Mission:chain() starts the missions one after another (happy case)
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local mission = Mission:chain(subMission1, subMission2, subMission3)
assert.is_same("new", mission:getState())
assert.is_same("new", subMission1:getState())
assert.is_same("new", subMission2:getState())
assert.is_same("new", subMission3:getState())
mission:accept()
assert.is_same("accepted", mission:getState())
assert.is_same("accepted", subMission1:getState())
assert.is_same("new", subMission2:getState())
assert.is_same("new", subMission3:getState())
mission:start()
assert.is_same("started", mission:getState())
assert.is_same("started", subMission1:getState())
assert.is_same("new", subMission2:getState())
assert.is_same("new", subMission3:getState())
subMission1:success()
assert.is_same("started", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("started", subMission2:getState())
assert.is_same("new", subMission3:getState())
subMission2:success()
assert.is_same("started", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("successful", subMission2:getState())
assert.is_same("started", subMission3:getState())
subMission3:success()
assert.is_same("successful", mission:getState())
assert.is_same("successful", subMission1:getState())
assert.is_same("successful", subMission2:getState())
assert.is_same("successful", subMission3:getState())
Mission:chain():chain()
Mission:chain():chain() allows to set config
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local onAcceptCalled = false
local mission = Mission:chain(subMission1, subMission2, {onAccept = function()
onAcceptCalled = true
end})
mission:accept()
assert.is_true(onAcceptCalled)
Mission:chain():chain() creates a valid mission and sets the parent mission
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local mission = Mission:chain(subMission1, subMission2)
assert.is_true(Mission:isMission(mission))
assert.is_true(Mission:isSubMission(subMission1))
assert.is_same(mission, subMission1:getParentMission())
assert.is_true(Mission:isSubMission(subMission2))
assert.is_same(mission, subMission2:getParentMission())
Mission:chain():chain() fails if any sub mission is already part of another mission container
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local subMission4 = Mission:new()
Mission:chain(subMission1, subMission4)
assert.has_error(function() Mission:chain(subMission1, subMission2, subMission3) end)
Mission:chain():chain() fails if any sub mission is not "new"
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
subMission3:accept()
assert.has_error(function() Mission:chain(subMission1, subMission2, subMission3) end)
subMission3:start()
assert.has_error(function() Mission:chain(subMission1, subMission2, subMission3) end)
subMission3:success()
assert.has_error(function() Mission:chain(subMission1, subMission2, subMission3) end)
Mission:chain():chain() fails if no sub missions are given
assert.has_error(function()
Mission:chain()
end)
Mission:chain():chain() fails if no sub missions are given
assert.has_error(function()
Mission:chain()
end)
Mission:chain():chain() fails on invalid parameters
assert.has_error(function() Mission:chain(42) end)
assert.has_error(function() Mission:chain({}) end)
assert.has_error(function() Mission:chain("broken") end)
assert.has_error(function() Mission:chain(CpuShip()) end)
Mission:chain():getCurrentMission()
Mission:chain():getCurrentMission() returns the correct currently running mission
local subMission1 = Mission:new()
local subMission2 = Mission:new()
local subMission3 = Mission:new()
local mission = Mission:chain(subMission1, subMission2, subMission3)
assert.is_nil(mission:getCurrentMission())
mission:accept()
assert.is_nil(mission:getCurrentMission())
mission:start()
assert.is_same(subMission1, mission:getCurrentMission())
subMission1:success()
assert.is_same(subMission2, mission:getCurrentMission())
subMission2:success()
assert.is_same(subMission3, mission:getCurrentMission())
subMission3:success()
assert.is_nil(mission:getCurrentMission())
Mission:forPlayer()
Mission:forPlayer() fails if no mission is given
local mission = missionMock()
assert.has_error(function() Mission:forPlayer(nil, player) end)
Mission:forPlayer() fails if the mission has been accepted already
local mission = missionMock()
mission:accept()
assert.has_error(function() Mission:forPlayer(mission, player) end)
Mission:forPlayer() fails if the mission is already a player mission
local mission = missionMock()
Mission:forPlayer(mission, player)
assert.has_error(function() Mission:forPlayer(mission, player) end)
Mission:forPlayer() fails if the player is destroyed
local player = PlayerSpaceship()
local onStartCalled = 0
local mission = Mission:new({
onStart = function(self)
onStartCalled = onStartCalled + 1
end,
})
Mission:forPlayer(mission, player)
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same(1, onStartCalled)
Cron.tick(1)
assert.is_same("started", mission:getState())
player:destroy()
Cron.tick(1)
assert.is_same("failed", mission:getState())
Mission:forPlayer() should create a valid Mission with player
local mission = missionMock()
Mission:forPlayer(mission, player)
assert.is_true(Mission:isPlayerMission(mission))
Mission:forPlayer():accept()
Mission:forPlayer():accept() adds the mission to the mission tracker if the player has one
local player = PlayerSpaceship()
Player:withMissionTracker(player)
local mission = Mission:new()
Mission:forPlayer(mission, player)
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({mission}, player:getStartedMissions())
Mission:forPlayer():accept() also calls the original implementation
local originalCalled = false
local mission = missionMock()
mission.accept = function() originalCalled = true end
Mission:forPlayer(mission, player)
mission:setPlayer(PlayerSpaceship())
mission:accept()
assert.is_true(originalCalled)
Mission:forPlayer():accept() can be called if Player are set
local mission = missionWithPlayerMock()
local player = PlayerSpaceship()
mission:setPlayer(player)
mission:accept()
Mission:forPlayer():accept() can not be called if Player is not set
local mission = missionWithPlayerMock()
assert.has_error(function() mission:accept() end)
Mission:forPlayer():getPlayer()
Mission:forPlayer():getPlayer() returns the set Player
local mission = missionWithPlayerMock()
local player = PlayerSpaceship()
mission:setPlayer(player)
assert.is_same(player, mission:getPlayer())
Mission:forPlayer():setPlayer()
Mission:forPlayer():setPlayer() can not be changed on an accepted mission
local player = PlayerSpaceship()
local mission = Mission:new()
Mission:forPlayer(mission, player)
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:setPlayer(player)
end)
Mission:forPlayer():setPlayer() fails if argument is not a player
local mission = missionWithPlayerMock()
assert.has_error(function()mission:setPlayer(42) end)
Mission:new()
Mission:new() allows to give config
local id = "foobar"
local mission = Mission:new({id = id})
assert.is_same(id, mission:getId())
assert.is_true(Mission:isMission(mission))
Mission:new() fails if acceptCondition is not a function
assert.has_error(function() Mission:new({acceptCondition = 42}) end)
Mission:new() fails if onAccept is not a function
assert.has_error(function() Mission:new({onAccept = 42}) end)
Mission:new() fails if onDecline is not a function
assert.has_error(function() Mission:new({onDecline = 42}) end)
Mission:new() fails if onEnd is not a function
assert.has_error(function() Mission:new({onEnd = 42}) end)
Mission:new() fails if onFailure is not a function
assert.has_error(function() Mission:new({onFailure = 42}) end)
Mission:new() fails if onStart is not a function
assert.has_error(function() Mission:new({onStart = 42}) end)
Mission:new() fails if onSuccess is not a function
assert.has_error(function() Mission:new({onSuccess = 42}) end)
Mission:new() fails if the config is not a table
assert.has_error(function() Mission:new("thisBreaks") end)
Mission:new() should create a valid Mission
local mission = Mission:new()
assert.is_true(Mission:isMission(mission))
Mission:new() state machine allows to call accept on a mission in state new
mission[funcName](mission) -- it should not error
Mission:new() state machine allows to call decline on a mission in state new
mission[funcName](mission) -- it should not error
Mission:new() state machine allows to call fail on a mission in state started
mission[funcName](mission) -- it should not error
Mission:new() state machine allows to call start on a mission in state accepted
mission[funcName](mission) -- it should not error
Mission:new() state machine allows to call success on a mission in state started
mission[funcName](mission) -- it should not error
Mission:new() state machine prevents calls to accept on a mission in state accepted
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to accept on a mission in state declined
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to accept on a mission in state failed
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to accept on a mission in state started
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to accept on a mission in state successful
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to decline on a mission in state accepted
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to decline on a mission in state declined
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to decline on a mission in state failed
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to decline on a mission in state started
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to decline on a mission in state successful
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to fail on a mission in state accepted
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to fail on a mission in state declined
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to fail on a mission in state failed
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to fail on a mission in state new
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to fail on a mission in state successful
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to start on a mission in state declined
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to start on a mission in state failed
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to start on a mission in state new
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to start on a mission in state started
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to start on a mission in state successful
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to success on a mission in state accepted
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to success on a mission in state declined
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to success on a mission in state failed
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to success on a mission in state new
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new() state machine prevents calls to success on a mission in state successful
assert.has_error(function()
mission[funcName](mission)
end)
Mission:new():accept()
Mission:new():accept() calls the onAccept callback
local callbackCalled = false
local mission
mission = newMission({onAccept = function(self)
assert.is_same(mission, self)
callbackCalled = true
end})
mission:accept()
assert.is_true(callbackCalled)
Mission:new():accept() calls the onAccept eventListener
local listenerCalled = false
local calledArg1
local mission = newMission()
mission:addAcceptListener(function(_, arg1)
calledArg1 = arg1
listenerCalled = true
end)
assert.is_false(listenerCalled)
mission:accept()
assert.is_true(listenerCalled)
assert.is_same(mission, calledArg1)
Mission:new():accept() fails if onAccept callback fails
local mission = newMission({onAccept = function() error("boom") end})
assert.has_error(function() mission:accept() end)
Mission:new():accept() fails when acceptCondition callback returns a string
local mission = newMission({acceptCondition = function() return "Just... No" end})
assert.has_error(function() mission:accept() end)
Mission:new():accept() fails when acceptCondition callback returns false
local mission = newMission({acceptCondition = function() return false end})
assert.has_error(function() mission:accept() end)
Mission:new():accept() switches to "accepted" if no callback is set
local mission = newMission()
mission:accept()
assert.is_same("accepted", mission:getState())
Mission:new():canBeAccepted()
Mission:new():canBeAccepted() is true when no config is set
local mission = newMission()
assert.is_true(mission:canBeAccepted())
Mission:new():decline()
Mission:new():decline() calls the onDecline callback
local callbackCalled = false
local mission
mission = newMission({onDecline = function(self)
assert.is_same(mission, self)
callbackCalled = true
end})
mission:decline()
assert.is_true(callbackCalled)
Mission:new():decline() calls the onDecline eventListener
local listenerCalled = false
local calledArg1
local mission = newMission()
mission:addDeclineListener(function(_, arg1)
calledArg1 = arg1
listenerCalled = true
end)
assert.is_false(listenerCalled)
mission:decline()
assert.is_true(listenerCalled)
assert.is_same(mission, calledArg1)
Mission:new():decline() fails if onDecline callback fails
local mission = newMission({onDecline = function() error("boom") end})
assert.has_error(function() mission:decline() end)
Mission:new():fail()
Mission:new():fail() calls the onFailure callback and then the onEnd callback
local onFailure = false
local onEndCalled = false
local mission
mission = startedMission({
onFailure = function(self)
assert.is_same(mission, self)
onFailure = true
end,
onEnd = function(self)
assert.is_same(mission, self)
assert.is_true(onFailure)
onEndCalled = true
end
})
mission:fail()
assert.is_true(onFailure)
assert.is_true(onEndCalled)
Mission:new():fail() calls the onFailure eventListener and then the onEnd eventListener
local onFailureCalled = false
local onFailureCalledArg1
local onEndCalled = false
local onEndCalledArg1
local wasOnFailureCalled = false
local mission = startedMission()
mission:addFailureListener(function(_, arg1)
onFailureCalledArg1 = arg1
onFailureCalled = true
end)
mission:addEndListener(function(_, arg1)
wasOnFailureCalled = onFailureCalled
onEndCalledArg1 = arg1
onEndCalled = true
end)
assert.is_false(onFailureCalled)
assert.is_false(onEndCalled)
mission:fail()
assert.is_true(onFailureCalled)
assert.is_same(mission, onFailureCalledArg1)
assert.is_true(wasOnFailureCalled)
assert.is_true(onEndCalled)
assert.is_same(mission, onEndCalledArg1)
Mission:new():fail() fails if onEnd callback fails
local mission = startedMission({onEnd = function() error("boom") end})
assert.has_error(function() mission:fail() end)
Mission:new():fail() fails if onFailure callback fails
local mission = startedMission({onFailure = function() error("boom") end})
assert.has_error(function() mission:fail() end)
Mission:new():getId()
Mission:new():getId() is set at random if none is given
local mission = Mission:new()
assert.is_true(isString(mission:getId()))
assert.is_not_same("", mission:getId())
Mission:new():getId() uses the given id
local id = "foobar"
local mission = Mission:new({id = id})
assert.is_same(id, mission:getId())
Mission:new():getState()
Mission:new():getState() returns "accepted" for an accepted mission
assert.is_same(accepted, acceptedMission():getState())
Mission:new():getState() returns "declined" for a declined mission
assert.is_same(declined, declinedMission():getState())
Mission:new():getState() returns "failed" for a failed mission
assert.is_same(failed, failedMission():getState())
Mission:new():getState() returns "new" for a new mission
assert.is_same(new, newMission():getState())
Mission:new():getState() returns "started" for a started mission
assert.is_same(started, startedMission():getState())
Mission:new():getState() returns "successful" for a successful mission
assert.is_same(successful, successfulMission():getState())
Mission:new():start()
Mission:new():start() calls the onStart callback
local callbackCalled = false
local mission
mission = acceptedMission({onStart = function(self)
assert.is_same(mission, self)
callbackCalled = true
end})
mission:start()
assert.is_true(callbackCalled)
Mission:new():start() calls the onStart eventListener
local listenerCalled = false
local calledArg1
local mission = acceptedMission()
mission:addStartListener(function(_, arg1)
calledArg1 = arg1
listenerCalled = true
end)
assert.is_false(listenerCalled)
mission:start()
assert.is_true(listenerCalled)
assert.is_same(mission, calledArg1)
Mission:new():start() fails if onDecline callback fails
local mission = acceptedMission({onStart = function() error("boom") end})
assert.has_error(function() mission:start() end)
Mission:new():success()
Mission:new():success() calls the onFailure eventListener and then the onEnd eventListener
local onSuccessCalled = false
local onSuccessCalledArg1
local onEndCalled = false
local onEndCalledArg1
local wasOnSuccessCalled = false
local mission = startedMission()
mission:addSuccessListener(function(_, arg1)
onSuccessCalledArg1 = arg1
onSuccessCalled = true
end)
mission:addEndListener(function(_, arg1)
wasOnSuccessCalled = onSuccessCalled
onEndCalledArg1 = arg1
onEndCalled = true
end)
assert.is_false(onSuccessCalled)
assert.is_false(onEndCalled)
mission:success()
assert.is_true(onSuccessCalled)
assert.is_same(mission, onSuccessCalledArg1)
assert.is_true(wasOnSuccessCalled)
assert.is_true(onEndCalled)
assert.is_same(mission, onEndCalledArg1)
Mission:new():success() calls the onSuccess callback and then the onEnd callback
local onSuccessCalled = false
local onEndCalled = false
local mission
mission = startedMission({
onSuccess = function(self)
assert.is_same(mission, self)
onSuccessCalled = true
end,
onEnd = function(self)
assert.is_same(mission, self)
assert.is_true(onSuccessCalled)
onEndCalled = true
end
})
mission:success()
assert.is_true(onSuccessCalled)
assert.is_true(onEndCalled)
Mission:new():success() fails if onEnd callback fails
local mission = startedMission({onEnd = function() error("boom") end})
assert.has_error(function() mission:success() end)
Mission:new():success() fails if onSuccess callback fails
local mission = startedMission({onSuccess = function() error("boom") end})
assert.has_error(function() mission:success() end)
Mission:registerMissionAcceptListener()
Mission:registerMissionAcceptListener() fires when mission is accepted
local eventCalled = false
local calledArg1
Mission:registerMissionAcceptListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
local mission = Mission:new()
assert.is_false(eventCalled)
mission:accept()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:registerMissionCreationListener()
Mission:registerMissionCreationListener() fires when mission is created
local eventCalled = false
local calledArg1
Mission:registerMissionCreationListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
assert.is_false(eventCalled)
local mission = Mission:new()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:registerMissionDeclineListener()
Mission:registerMissionDeclineListener() fires when mission is declined
local eventCalled = false
local calledArg1
Mission:registerMissionDeclineListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
local mission = Mission:new()
assert.is_false(eventCalled)
mission:decline()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:registerMissionEndListener()
Mission:registerMissionEndListener() fires when mission is fails
local eventCalled = false
local calledArg1
Mission:registerMissionEndListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
local mission = Mission:new()
mission:accept()
mission:start()
assert.is_false(eventCalled)
mission:fail()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:registerMissionEndListener() fires when mission is successful
local eventCalled = false
local calledArg1
Mission:registerMissionEndListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
local mission = Mission:new()
mission:accept()
mission:start()
assert.is_false(eventCalled)
mission:success()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:registerMissionFailureListener()
Mission:registerMissionFailureListener() fires when mission is fails
local eventCalled = false
local calledArg1
Mission:registerMissionFailureListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
local mission = Mission:new()
mission:accept()
mission:start()
assert.is_false(eventCalled)
mission:fail()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:registerMissionStartListener()
Mission:registerMissionStartListener() fires when mission is started
local eventCalled = false
local calledArg1
Mission:registerMissionStartListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
local mission = Mission:new()
mission:accept()
assert.is_false(eventCalled)
mission:start()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:registerMissionSuccessListener()
Mission:registerMissionSuccessListener() fires when mission is successful
local eventCalled = false
local calledArg1
Mission:registerMissionSuccessListener(function(_, arg1)
eventCalled = true
calledArg1 = arg1
end)
local mission = Mission:new()
mission:accept()
mission:start()
assert.is_false(eventCalled)
mission:success()
assert.is_true(eventCalled)
assert.is_same(mission, calledArg1)
Mission:withBroker()
Mission:withBroker() fails automatically if the broker is destroyed
local onStartCalled = 0
local station = SpaceStation()
local mission = Mission:new({
onStart = function(self)
onStartCalled = onStartCalled + 1
end,
})
Mission:withBroker(mission, "Hello World")
mission:setMissionBroker(station)
mission:accept()
mission:start()
assert.is_same(1, onStartCalled)
Cron.tick(1)
assert.is_same("started", mission:getState())
station:destroy()
Cron.tick(1)
assert.is_same("failed", mission:getState())
Mission:withBroker() fails if no mission is given
local mission = missionMock()
assert.has_error(function() Mission:withBroker(nil, "Hello World") end)
Mission:withBroker() fails if no title is given
local mission = missionMock()
assert.has_error(function() Mission:withBroker(mission) end)
Mission:withBroker() fails if the config is not a table
local mission = missionMock()
assert.has_error(function() Mission:withBroker(mission, "Hello World", "thisBreaks") end)
Mission:withBroker() fails if the description is a number
assert.has_error(function() missionWithBrokerMock({description = 42}) end)
Mission:withBroker() fails if the mission has been accepted already
local mission = missionMock()
mission:accept()
assert.has_error(function() Mission:withBroker(mission, "Hello World") end)
Mission:withBroker() fails if the mission is already a story mission
local mission = missionMock()
Mission:withBroker(mission, "Hello World")
assert.has_error(function() Mission:withBroker(mission, "Hello World") end)
Mission:withBroker() should create a valid Mission with story
local mission = missionMock()
Mission:withBroker(mission, "Hello World")
assert.is_true(Mission:isBrokerMission(mission))
Mission:withBroker():accept()
Mission:withBroker():accept() also calls the original implementation
local originalCalled = false
local mission = missionMock()
mission.accept = function() originalCalled = true end
Mission:withBroker(mission, "Hello World")
mission:setMissionBroker(SpaceStation())
mission:accept()
assert.is_true(originalCalled)
Mission:withBroker():accept() can be called if MissionBroker is set
local mission = missionWithBrokerMock()
local station = SpaceStation()
mission:setMissionBroker(station)
mission:accept()
Mission:withBroker():accept() can not be called if no MissionBroker is set
local mission = missionWithBrokerMock()
assert.has_error(function() mission:accept() end)
Mission:withBroker():getAcceptMessage()
Mission:withBroker():getAcceptMessage() returns the message if it is a function
local message = "Thanks for taking that mission"
local mission
mission = missionWithBrokerMock({acceptMessage = function(callMission)
assert.is_same(mission, callMission)
return message
end})
assert.is_same(message, mission:getAcceptMessage())
Mission:withBroker():getAcceptMessage() returns the message if it is a string
local message = "Thanks for taking that mission"
local mission = missionWithBrokerMock({acceptMessage = message })
assert.is_same(message, mission:getAcceptMessage())
Mission:withBroker():getDescription()
Mission:withBroker():getDescription() returns the description if it is a function
local description = "This is a mission"
local mission
mission = missionWithBrokerMock({description = function(callMission)
assert.is_same(mission, callMission)
return description
end})
assert.is_same(description, mission:getDescription())
Mission:withBroker():getDescription() returns the description if it is a string
local description = "This is a mission"
local mission = missionWithBrokerMock({description = description})
assert.is_same(description, mission:getDescription())
Mission:withBroker():getHint(),:setHint()
Mission:withBroker():getHint(),:setHint() allows to remove the hint
local mission = missionWithBrokerMock()
mission:setHint(nil)
assert.is_nil(mission:getHint())
Mission:withBroker():getHint(),:setHint() fails if number is to be set as hint
local mission = missionWithBrokerMock()
assert.has_error(function() mission:setHint(42) end)
Mission:withBroker():getHint(),:setHint() returns nil by default
local mission = missionWithBrokerMock()
assert.is_nil(mission:getHint())
Mission:withBroker():getHint(),:setHint() returns nil if the function returns invalid type
local mission = missionWithBrokerMock()
mission:setHint(function() return 42 end)
assert.is_nil(mission:getHint())
Mission:withBroker():getHint(),:setHint() returns the set hint
local mission = missionWithBrokerMock()
mission:setHint("Use force")
assert.is_same("Use force", mission:getHint())
Mission:withBroker():getHint(),:setHint() returns the set hint by function
local mission = missionWithBrokerMock()
mission:setHint(function(theMission)
assert.is_same(mission, theMission)
return "Use force"
end)
assert.is_same("Use force", mission:getHint())
Mission:withBroker():getMissionBroker()
Mission:withBroker():getMissionBroker() returns the set MissionBroker
local mission = missionWithBrokerMock()
local station = SpaceStation()
mission:setMissionBroker(station)
assert.is_same(station, mission:getMissionBroker())
Mission:withBroker():getTitle()
Mission:withBroker():getTitle() returns the title if it is a function
local title = "Hello World"
local mission = missionMock()
Mission:withBroker(mission, function(callMission)
assert.is_same(mission, callMission)
return title
end)
assert.is_same(title, mission:getTitle())
Mission:withBroker():getTitle() returns the title if it is a string
local title = "Hello World"
local mission = missionMock()
Mission:withBroker(mission, title)
assert.is_same(title, mission:getTitle())
Mission:withTimeLimit()
Mission:withTimeLimit() allows to manipulate the time limit on a running mission
local mission = Mission:new()
Mission:withTimeLimit(mission, 10)
mission:accept()
mission:start()
assert.is_same(0, mission:getElapsedTime())
assert.is_same(10, mission:getRemainingTime())
Cron.tick(1)
assert.is_same(1, mission:getElapsedTime())
assert.is_same(9, mission:getRemainingTime())
mission:setTimeLimit(42)
assert.is_same(1, mission:getElapsedTime())
assert.is_same(41, mission:getRemainingTime())
Cron.tick(1)
assert.is_same(2, mission:getElapsedTime())
assert.is_same(40, mission:getRemainingTime())
mission:modifyTimeLimit(-5)
assert.is_same(2, mission:getElapsedTime())
assert.is_same(35, mission:getRemainingTime())
Cron.tick(1)
assert.is_same(3, mission:getElapsedTime())
assert.is_same(34, mission:getRemainingTime())
mission:modifyTimeLimit(2)
assert.is_same(3, mission:getElapsedTime())
assert.is_same(36, mission:getRemainingTime())
Mission:withTimeLimit() does not start the timer before mission is started
local mission = Mission:new()
Mission:withTimeLimit(mission, 10)
assert.is_same(0, mission:getElapsedTime())
assert.is_same(10, mission:getRemainingTime())
Cron.tick(1)
assert.is_same(0, mission:getElapsedTime())
assert.is_same(10, mission:getRemainingTime())
mission:accept()
assert.is_same(0, mission:getElapsedTime())
assert.is_same(10, mission:getRemainingTime())
Cron.tick(1)
assert.is_same(0, mission:getElapsedTime())
assert.is_same(10, mission:getRemainingTime())
mission:start()
assert.is_same(0, mission:getElapsedTime())
assert.is_same(10, mission:getRemainingTime())
Cron.tick(1)
assert.is_same(1, mission:getElapsedTime())
assert.is_same(9, mission:getRemainingTime())
Mission:withTimeLimit() fails if mission is invalid
assert.has_error(function() Mission:withTimeLimit(nil, 10) end)
assert.has_error(function() Mission:withTimeLimit(PlayerSpaceship(), 10) end)
assert.has_error(function() Mission:withTimeLimit(123, 10) end)
Mission:withTimeLimit() fails if the mission already has a time limit
local mission = Mission:new()
Mission:withTimeLimit(mission, 10)
assert.has_error(function() Mission:withTimeLimit(mission, 10) end)
Mission:withTimeLimit() fails if the mission has been started already
local mission = Mission:new()
mission:accept()
mission:start()
assert.has_error(function() Mission:withTimeLimit(mission, 10) end)
Mission:withTimeLimit() fails to start if no time limit is set
local mission = Mission:new()
Mission:withTimeLimit(mission)
mission:accept()
assert.has_error(function() mission:start() end)
mission:setTimeLimit(10)
mission:start()
Mission:withTimeLimit() makes the mission fail if the time is up
local mission = Mission:new()
Mission:withTimeLimit(mission, 10)
mission:accept()
mission:start()
assert.is_same("started", mission:getState())
for _ = 1,9 do
Cron.tick(1)
assert.is_same("started", mission:getState())
end
Cron.tick(1.5)
assert.is_same("failed", mission:getState())
Mission:withTimeLimit() should create a valid Mission with time limit
local mission = Mission:new()
Mission:withTimeLimit(mission, 10)
assert.is_true(Mission:isTimeLimitMission(mission))
Mission:withTimeLimit():getRemainingTime()
Mission:withTimeLimit():getRemainingTime() never gets negative
local mission = Mission:new()
Mission:withTimeLimit(mission, 0.5)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, mission:getRemainingTime())
Mission:withTimeLimit():modifyTimeLimit()
Mission:withTimeLimit():modifyTimeLimit() can decrease the time limit
local mission = Mission:new()
Mission:withTimeLimit(mission, 10)
mission:modifyTimeLimit(-5)
assert.is_same(5, mission:getRemainingTime())
Mission:withTimeLimit():modifyTimeLimit() can increase the time limit
local mission = Mission:new()
Mission:withTimeLimit(mission, 10)
mission:modifyTimeLimit(10)
assert.is_same(20, mission:getRemainingTime())
Mission:withTimeLimit():modifyTimeLimit() fails on anything but a number
local mission = Mission:new()
Mission:withTimeLimit(mission)
assert.has_error(function() mission:modifyTimeLimit(nil) end)
assert.has_error(function() mission:modifyTimeLimit(PlayerSpaceship()) end)
Mission:withTimeLimit():setTimeLimit()
Mission:withTimeLimit():setTimeLimit() fails on non-positive numbers
local mission = Mission:new()
Mission:withTimeLimit(mission)
assert.has_error(function() mission:setTimeLimit(nil) end)
assert.has_error(function() mission:setTimeLimit(-1) end)
assert.has_error(function() mission:setTimeLimit(0) end)
assert.has_error(function() mission:setTimeLimit(PlayerSpaceship()) end)
Mission:withTimeLimit():setTimeLimit() sets the time limit
local mission = Mission:new()
Mission:withTimeLimit(mission)
mission:setTimeLimit(10)
assert.is_same(10, mission:getRemainingTime())
Missions
Missions:answer()
Missions:answer() can have dynamic questions and answers
local station = SpaceStation():setCallSign("Station")
local player = PlayerSpaceship():setCallSign("Player")
Station:withComms(station)
local questionCallArg1, questionCallArg2
local correctAnswerCallArg1, correctAnswerCallArg2
local wrongAnswerCallArg1, wrongAnswerCallArg2
local mission = Missions:answer(
station,
function(arg1, arg2)
questionCallArg1, questionCallArg2 = arg1, arg2
return "What is your ships call sign?"
end,
"I want to answer your question.", {
correctAnswer = function(arg1, arg2)
correctAnswerCallArg1, correctAnswerCallArg2 = arg1, arg2
return arg2:getCallSign()
end,
correctAnswerResponse = "You are right.",
wrongAnswers = function(arg1, arg2)
wrongAnswerCallArg1, wrongAnswerCallArg2 = arg1, arg2
return {
"Not " .. arg2:getCallSign()
}
end,
wrongAnswerResponse = "You are wrong.",
}
)
assert.is_true(Mission:isMission(mission))
mission:accept()
mission:start()
player:commandOpenTextComm(station)
player:selectComms("I want to answer your question.")
assert.is_same(mission, questionCallArg1)
assert.is_same(player, questionCallArg2)
assert.is_same("What is your ships call sign?", player:getCurrentCommsText())
assert.is_same(mission, correctAnswerCallArg1)
assert.is_same(player, correctAnswerCallArg2)
assert.is_same(mission, wrongAnswerCallArg1)
assert.is_same(player, wrongAnswerCallArg2)
player:commandCloseTextComm(station)
Missions:answer() is possible to enable the right answer through a function
local station = SpaceStation()
Station:withComms(station)
local correctAnswer = nil
local mission = Missions:answer(
station,
"What is my name?",
"I want to answer your question.", {
correctAnswer = function() return correctAnswer end,
correctAnswerResponse = "You are right.",
wrongAnswers = { "Paul", "Klaus", "Hans" },
wrongAnswerResponse = "You are wrong.",
}
)
assert.is_true(Mission:isMission(mission))
mission:accept()
mission:start()
local player = PlayerSpaceship()
player:commandOpenTextComm(station)
player:selectComms("I want to answer your question.")
assert.is_same("What is my name?", player:getCurrentCommsText())
assert.is_true(player:hasComms("Paul"))
assert.is_true(player:hasComms("Klaus"))
assert.is_true(player:hasComms("Hans"))
assert.is_false(player:hasComms("Rumpelstiltskin"))
player:commandCloseTextComm(station)
correctAnswer = "Rumpelstiltskin"
player:commandOpenTextComm(station)
player:selectComms("I want to answer your question.")
assert.is_same("What is my name?", player:getCurrentCommsText())
assert.is_true(player:hasComms("Rumpelstiltskin"))
player:selectComms("Rumpelstiltskin")
player:commandCloseTextComm(station)
Missions:answer() mission fails if the wrong answer is given
local station = SpaceStation()
Station:withComms(station)
local mission = Missions:answer(
station,
"What is the answer to my question?",
"I want to answer your question.", {
correctAnswer = "42",
correctAnswerResponse = "You are right.",
wrongAnswers = { "I don't know", "Yes", "No" },
wrongAnswerResponse = "You are wrong.",
}
)
assert.is_true(Mission:isMission(mission))
mission:accept()
mission:start()
local player = PlayerSpaceship()
player:commandOpenTextComm(station)
assert.is_true(player:hasComms("I want to answer your question."))
player:selectComms("I want to answer your question.")
assert.is_same("What is the answer to my question?", player:getCurrentCommsText())
player:selectComms("Yes")
assert.is_same("You are wrong.", player:getCurrentCommsText())
assert.is_same("failed", mission:getState())
player:commandCloseTextComm(station)
Missions:answer() mission is successful if the correct answer is given
local station = SpaceStation()
Station:withComms(station)
local mission = Missions:answer(
station,
"What is the answer to my question?",
"I want to answer your question.", {
correctAnswer = "42",
correctAnswerResponse = "You are right.",
wrongAnswers = { "I don't know", "Yes", "No" },
wrongAnswerResponse = "You are wrong.",
}
)
assert.is_true(Mission:isMission(mission))
mission:accept()
mission:start()
local player = PlayerSpaceship()
player:commandOpenTextComm(station)
assert.is_true(player:hasComms("I want to answer your question."))
player:selectComms("I want to answer your question.")
assert.is_same("What is the answer to my question?", player:getCurrentCommsText())
player:selectComms("42")
assert.is_same("You are right.", player:getCurrentCommsText())
assert.is_same("successful", mission:getState())
player:commandCloseTextComm(station)
Missions:answer() should create a valid Mission with one station and a guessing game
local station = SpaceStation()
Station:withComms(station)
local mission = Missions:answer(
station,
"What is the answer to my question?",
"I want to answer your question.", {
correctAnswer = "42",
correctAnswerResponse = "You are right.",
wrongAnswers = { "I don't know", "Yes", "No" },
wrongAnswerResponse = "You are wrong.",
}
)
assert.is_true(Mission:isMission(mission))
local player = PlayerSpaceship()
player:commandOpenTextComm(station)
assert.is_false(player:hasComms("I want to answer your question."))
player:commandCloseTextComm(station)
mission:accept()
mission:start()
player:commandOpenTextComm(station)
assert.is_true(player:hasComms("I want to answer your question."))
player:selectComms("I want to answer your question.")
assert.is_same("What is the answer to my question?", player:getCurrentCommsText())
assert.is_true(player:hasComms("42"))
assert.is_true(player:hasComms("I don't know"))
assert.is_true(player:hasComms("Yes"))
assert.is_true(player:hasComms("No"))
player:commandCloseTextComm(station)
Missions:capture()
Missions:capture() can run a successful mission with drop off point
local mission = Missions:capture(SpaceStation(), {
dropOffTarget = SpaceStation()
})
mission:getBearer():setPosition(0,0)
player:setPosition(0,0)
player.isDocked = function(thing) return false end
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
mission:getBearer():destroy()
Cron.tick(1)
mission:getItemObject():destroy()
Cron.tick(1)
assert.is_same("started", mission:getState())
player.isDocked = function(self, thing) return thing == mission:getDropOffTarget() end
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:capture() can run a successful mission without drop off point
local mission = Missions:capture(SpaceStation())
mission:getBearer():setPosition(0,0)
player:setPosition(0,0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
mission:getBearer():destroy()
Cron.tick(1)
mission:getItemObject():destroy()
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:capture() config.onApproach is called when the player first enters around the bearer
local onApproachCalled = 0
local bearer = SpaceStation()
local mission
mission = Missions:capture(bearer, {
approachDistance = 10000,
onApproach = function(callMission, callEnemy)
onApproachCalled = onApproachCalled + 1
assert.is_same(mission, callMission)
assert.is_same(bearer, callEnemy)
end,
})
player:setPosition(20000, 0)
bearer:setPosition(0, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, onApproachCalled)
player:setPosition(10001, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, onApproachCalled)
player:setPosition(9999, 0)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
player:setPosition(10001, 0)
Cron.tick(1)
Cron.tick(1)
player:setPosition(9999, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
Missions:capture() config.onBearerDestruction allows to return a custom itemObject
local bearer = SpaceStation()
local itemObject = CpuShip()
local mission
mission = Missions:capture(bearer, {
onBearerDestruction = function(callMission, lastX, lastY)
assert.is_same(mission, callMission)
assert.is_same(4200, lastX)
assert.is_same(-4200, lastY)
return itemObject
end,
})
bearer:setPosition(4200, -4200)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
bearer:destroy()
Cron.tick(1)
assert.is_nil(mission:getBearer())
assert.is_same(itemObject, mission:getItemObject())
Missions:capture() config.onBearerDestruction is called when the bearer is destroyed
local onBearerDestructionCalled = 0
local bearer = SpaceStation()
local mission
mission = Missions:capture(bearer, {
onBearerDestruction = function(callMission, lastX, lastY)
onBearerDestructionCalled = onBearerDestructionCalled + 1
assert.is_same(mission, callMission)
assert.is_same(4200, lastX)
assert.is_same(-4200, lastY)
end,
})
bearer:setPosition(4200, -4200)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, onBearerDestructionCalled)
assert.is_same(bearer, mission:getBearer())
assert.is_nil(mission:getItemObject())
bearer:destroy()
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onBearerDestructionCalled)
assert.is_nil(mission:getBearer())
assert.is_true(isEeObject(mission:getItemObject()))
Missions:capture() config.onDropOff is called when the player returns the collected item
local onDropOffCalled = 0
local mission
mission = Missions:capture(SpaceStation(), {
dropOffTarget = SpaceStation(),
onDropOff = function(callMission)
onDropOffCalled = onDropOffCalled + 1
assert.is_same(mission, callMission)
end,
})
local dockedToTarget = function(self, thing) return thing == mission:getDropOffTarget() end
local dockedToNil = function() return false end
mission:getBearer():setPosition(0,0)
player:setPosition(0,0)
player.isDocked = dockedToNil
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onDropOffCalled)
mission:getBearer():destroy()
Cron.tick(1)
assert.is_same(0, onDropOffCalled)
player.isDocked = dockedToTarget
Cron.tick(1)
assert.is_same(0, onDropOffCalled)
player.isDocked = dockedToNil
mission:getItemObject():destroy()
Cron.tick(1)
assert.is_same(0, onDropOffCalled)
player.isDocked = dockedToTarget
Cron.tick(1)
assert.is_same(1, onDropOffCalled)
assert.is_same("successful", mission:getState())
Missions:capture() config.onDropOffTargetDestroyed is called
local onDropOffTargetDestroyedCalled = 0
local mission
mission = Missions:capture(SpaceStation(), {
dropOffTarget = SpaceStation(),
onDropOffTargetDestroyed = function(callMission)
onDropOffTargetDestroyedCalled = onDropOffTargetDestroyedCalled + 1
assert.is_same(mission, callMission)
end,
})
mission:getBearer():setPosition(0,0)
player:setPosition(0,0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onDropOffTargetDestroyedCalled)
mission:getBearer():destroy()
Cron.tick(1)
assert.is_same(0, onDropOffTargetDestroyedCalled)
mission:getItemObject():destroy()
Cron.tick(1)
assert.is_same(0, onDropOffTargetDestroyedCalled)
mission:getDropOffTarget():destroy()
Cron.tick(1)
assert.is_same(1, onDropOffTargetDestroyedCalled)
assert.is_same("failed", mission:getState())
Missions:capture() config.onItemDestruction is called when the item is destroyed and the player is too far
local onItemDestructionCalled = 0
local mission
mission = Missions:capture(SpaceStation(), {
onItemDestruction = function(callMission, lastX, lastY)
onItemDestructionCalled = onItemDestructionCalled + 1
assert.is_same(mission, callMission)
assert.is_same(4200, lastX)
assert.is_same(-4200, lastY)
end,
})
mission:getBearer():setPosition(4200, -4200)
player:setPosition(0, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onItemDestructionCalled)
mission:getBearer():destroy()
Cron.tick(1)
local x, y = mission:getItemObject():getPosition()
assert.is_same({4200, -4200}, {x,y})
Cron.tick(1)
assert.is_same(0, onItemDestructionCalled)
mission:getItemObject():destroy()
Cron.tick(1)
assert.is_same(1, onItemDestructionCalled)
Missions:capture() config.onPickup is called when the item is destroyed and the player is close enough
local onPickupCalled = 0
local mission
mission = Missions:capture(SpaceStation(), {
onPickup = function(callMission)
onPickupCalled = onPickupCalled + 1
assert.is_same(mission, callMission)
end,
})
mission:getBearer():setPosition(4200, -4200)
player:setPosition(4100, -4200)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onPickupCalled)
mission:getBearer():destroy()
Cron.tick(1)
local x, y = mission:getItemObject():getPosition()
assert.is_same({4200, -4200}, {x,y})
Cron.tick(1)
assert.is_same(0, onPickupCalled)
mission:getItemObject():destroy()
Cron.tick(1)
assert.is_same(1, onPickupCalled)
Missions:capture() fails if a call back function is given, but returns a person
local mission = Missions:capture(function() return nil end)
mission:setPlayer(player)
assert.has_error(function()
mission:accept()
mission:start()
end)
Missions:capture() fails if a call back function is given, but returns a person
local mission = Missions:capture(function() return personMock() end)
mission:setPlayer(player)
assert.has_error(function()
mission:accept()
mission:start()
end)
Missions:capture() fails if second parameter is a number
assert.has_error(function() Missions:capture(CpuShip(), 3) end)
Missions:capture() fails when itemObject is destroyed when the player is too far away
local mission = Missions:capture(SpaceStation())
mission:getBearer():setPosition(0,0)
player:setPosition(4200,4200)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
mission:getBearer():destroy()
Cron.tick(1)
mission:getItemObject():destroy()
Cron.tick(1)
assert.is_same("failed", mission:getState())
Missions:capture() should create a valid Mission if a callback function is given that returns one ship
local ship = CpuShip()
local mission = Missions:capture(function() return ship end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same(ship, mission:getBearer())
assert.is_nil(mission:getItemObject())
Missions:capture() should create a valid Mission with one ship
local ship = CpuShip()
local mission = Missions:capture(ship)
assert.is_true(Mission:isMission(mission))
assert.is_same(ship, mission:getBearer())
assert.is_nil(mission:getItemObject())
Missions:capture() should create a valid Mission with one station
local station = SpaceStation()
local mission = Missions:capture(station)
assert.is_true(Mission:isMission(mission))
assert.is_same(station, mission:getBearer())
assert.is_nil(mission:getItemObject())
Missions:capture() should fail if a person is given instead of a bearer
assert.has_error(function() Missions:capture(personMock()) end)
Missions:capture() should fail if nil is given instead of a bearer
assert.has_error(function() Missions:capture(nil) end)
Missions:crewForRent()
Missions:crewForRent() config.onCrewReady is called when the crew can be picked up again
local onCrewReadyCalled = 0
local player = PlayerSpaceship()
Player:withMenu(player)
local mission
mission = Missions:crewForRent(CpuShip(), {
duration = 3,
sendCrewLabel = "Hello World",
onCrewReady = function(callMission)
onCrewReadyCalled = onCrewReadyCalled + 1
assert.is_same(mission, callMission)
end,
})
mission:setPlayer(player)
player:setRepairCrewCount(4)
player:setPosition(0,0)
mission:getNeedy():setPosition(0,0)
mission:accept()
mission:start()
assert.is_nil(mission:getTimeToReady())
Cron.tick(1)
player:clickButton("engineering", "Hello World")
Cron.tick(1)
assert.is_same(2, mission:getTimeToReady())
Cron.tick(1)
assert.is_same(1, mission:getTimeToReady())
assert.is_same(0, onCrewReadyCalled)
Cron.tick(1)
assert.is_same(1, onCrewReadyCalled)
Cron.tick(1)
assert.is_same(1, onCrewReadyCalled)
assert.is_same(0, mission:getTimeToReady())
Missions:crewForRent() returning crew should return the crew
local onCrewReturnedCalled = 0
local ship = CpuShip()
local player = PlayerSpaceship()
Player:withMenu(player)
local mission
mission = Missions:crewForRent(ship, {
distance = 1000,
crewCount = 1,
duration = 5,
sendCrewLabel = "Hello World",
returnCrewLabel = "Come Back",
onCrewReturned = function(theMission)
onCrewReturnedCalled = onCrewReturnedCalled + 1
assert.is_same(mission, theMission)
end,
})
mission:setPlayer(player)
player:setPosition(0, 0)
mission:getNeedy():setPosition(0, 0)
player:setRepairCrewCount(1)
mission:accept()
mission:start()
Cron.tick(1)
player:clickButton("engineering", "Hello World")
Cron.tick(5)
Cron.tick(1)
assert.is_same(0, onCrewReturnedCalled)
player:clickButton("engineering", "Come Back")
assert.is_same(1, onCrewReturnedCalled)
Missions:crewForRent() sending crew fails when crew count is too low
local onCrewArrivedCalled = 0
local sendCrewFailedCalled = 0
local ship = CpuShip()
local player = PlayerSpaceship()
Player:withMenu(player)
local mission
mission = Missions:crewForRent(ship, {
distance = 1000,
crewCount = 5,
sendCrewLabel = "Hello World",
onCrewArrived = function(callMission)
onCrewArrivedCalled = onCrewArrivedCalled + 1
end,
sendCrewFailed = function(callMission)
sendCrewFailedCalled = sendCrewFailedCalled + 1
assert.is_same(mission, callMission)
end,
})
mission:setPlayer(player)
player:setRepairCrewCount(4)
player:setPosition(0, 0)
mission:getNeedy():setPosition(500, 0)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onCrewArrivedCalled)
assert.is_same(0, sendCrewFailedCalled)
assert.is_same(4, player:getRepairCrewCount())
assert.is_same(0, mission:getRepairCrewCount())
player:clickButton("engineering", "Hello World")
Cron.tick(1)
assert.is_same(0, onCrewArrivedCalled)
assert.is_same(1, sendCrewFailedCalled)
assert.is_same(4, player:getRepairCrewCount())
assert.is_same(0, mission:getRepairCrewCount())
Missions:crewForRent() sending crew succeeds when crew count is high enough
local onCrewArrivedCalled = 0
local sendCrewFailedCalled = 0
local ship = CpuShip()
local player = PlayerSpaceship()
Player:withMenu(player)
local mission
mission = Missions:crewForRent(ship, {
distance = 1000,
crewCount = 4,
sendCrewLabel = "Hello World",
onCrewArrived = function(callMission)
onCrewArrivedCalled = onCrewArrivedCalled + 1
assert.is_same(mission, callMission)
end,
sendCrewFailed = function(callMission)
sendCrewFailedCalled = sendCrewFailedCalled + 1
end,
})
mission:setPlayer(player)
player:setRepairCrewCount(4)
player:setPosition(0, 0)
mission:getNeedy():setPosition(500, 0)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onCrewArrivedCalled)
assert.is_same(0, sendCrewFailedCalled)
assert.is_same(4, player:getRepairCrewCount())
assert.is_same(0, mission:getRepairCrewCount())
player:clickButton("engineering", "Hello World")
Cron.tick(1)
assert.is_same(1, onCrewArrivedCalled)
assert.is_same(0, sendCrewFailedCalled)
assert.is_same(0, player:getRepairCrewCount())
assert.is_same(4, mission:getRepairCrewCount())
Missions:crewForRent() should create a valid Mission with one ship
local ship = CpuShip()
local mission = Missions:crewForRent(ship)
assert.is_true(Mission:isMission(mission))
assert.is_same(ship, mission:getNeedy())
assert.is_same(0, mission:getRepairCrewCount())
Missions:crewForRent() successful mission
local player = PlayerSpaceship()
Player:withMenu(player)
local mission
mission = Missions:crewForRent(CpuShip(), {
distance = 1000,
crewCount = 4,
duration = 5,
sendCrewLabel = "Hello World",
returnCrewLabel = "Come Back",
})
mission:setPlayer(player)
player:setPosition(0, 0)
mission:getNeedy():setPosition(0, 0)
player:setRepairCrewCount(5)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(5, player:getRepairCrewCount())
player:clickButton("engineering", "Hello World")
assert.is_same(1, player:getRepairCrewCount())
Cron.tick(5)
Cron.tick(1)
assert.is_same(1, player:getRepairCrewCount())
player:clickButton("engineering", "Come Back")
Cron.tick(1)
assert.is_same(5, player:getRepairCrewCount())
assert.is_false(player:hasButton("engineering", "Come Back"))
assert.is_false(player:hasButton("engineering+", "Come Back"))
assert.is_same("successful", mission:getState())
Missions:destroy()
Missions:destroy() can run a successful mission
local enemy1 = SpaceStation()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission
mission = Missions:destroy({enemy1, enemy2, enemy3})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
enemy1:destroy()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
enemy2:destroy()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
enemy3:destroy()
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:destroy() config.onApproach is called when the player first enters 10u around the enemy
local onApproachCalled = 0
local enemy = SpaceStation()
local mission
mission = Missions:destroy(enemy, {onApproach = function(callMission, callEnemy)
assert.is_same(mission, callMission)
assert.is_same(enemy, callEnemy)
onApproachCalled = onApproachCalled + 1
end})
player:setPosition(20000, 0)
enemy:setPosition(0, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, onApproachCalled)
player:setPosition(10001, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, onApproachCalled)
player:setPosition(9999, 0)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
player:setPosition(10001, 0)
Cron.tick(1)
Cron.tick(1)
player:setPosition(9999, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
Missions:destroy() config.onApproach is called when the player first gets closer than 10u to any enemy
local onApproachCalled = 0
local enemy1 = SpaceStation()
local enemy2 = SpaceStation()
local mission
mission = Missions:destroy({enemy1, enemy2}, {onApproach = function(callMission, callEnemy)
assert.is_same(mission, callMission)
assert.is_same(enemy2, callEnemy)
onApproachCalled = onApproachCalled + 1
end})
enemy1:setPosition(0, 0)
enemy2:setPosition(1000, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
player:setPosition(11001, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, onApproachCalled)
player:setPosition(10999, 0)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
player:setPosition(9999, 0)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
Missions:destroy() config.onDestruction is called each time an enemy is destroyed
local enemy1 = SpaceStation()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local enemy4 = WarpJammer()
local enemy5 = ScanProbe()
local callback1Called = 0
local callback2Called = 0
local callback3Called = 0
local callback4Called = 0
local callback5Called = 0
local mission
mission = Missions:destroy({enemy1, enemy2, enemy3, enemy4, enemy5}, {onDestruction = function(callMission, callEnemy)
assert.is_same(mission, callMission)
if callEnemy == enemy1 then callback1Called = callback1Called + 1 end
if callEnemy == enemy2 then callback2Called = callback2Called + 1 end
if callEnemy == enemy3 then callback3Called = callback3Called + 1 end
if callEnemy == enemy4 then callback4Called = callback4Called + 1 end
if callEnemy == enemy5 then callback5Called = callback5Called + 1 end
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, callback1Called)
assert.is_same(0, callback2Called)
assert.is_same(0, callback3Called)
assert.is_same(0, callback4Called)
assert.is_same(0, callback5Called)
enemy1:destroy()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(0, callback2Called)
assert.is_same(0, callback3Called)
assert.is_same(0, callback4Called)
assert.is_same(0, callback5Called)
enemy2:destroy()
enemy3:destroy()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(1, callback2Called)
assert.is_same(1, callback3Called)
assert.is_same(0, callback4Called)
assert.is_same(0, callback5Called)
enemy4:destroy()
enemy5:destroy()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(1, callback2Called)
assert.is_same(1, callback3Called)
assert.is_same(1, callback4Called)
assert.is_same(1, callback5Called)
Missions:destroy() config.onDestruction is only called once if callback errors
local enemy1 = CpuShip()
local enemy2 = CpuShip()
local onDestructionCalled = 0
local mission
mission = Missions:destroy({enemy1, enemy2}, {onDestruction = function(_)
onDestructionCalled = onDestructionCalled + 1
error("Intentional error")
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onDestructionCalled)
enemy1:destroy()
Cron.tick(1)
assert.is_same(1, onDestructionCalled)
-- it should not be called again
Cron.tick(1)
assert.is_same(1, onDestructionCalled)
Missions:destroy() fails if a call back function is given that returns nil
local mission = Missions:destroy(function() return nil end)
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:destroy() fails if a call back function is given, but returns a person
local mission = Missions:destroy(function() return personMock() end)
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:destroy() fails if a call back function is given, but returns a table where one item is a person
local mission = Missions:destroy(function() return {CpuShip(), SpaceStation(), personMock()} end)
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:destroy() fails if second parameter is a number
assert.has_error(function() Missions:destroy(CpuShip(), 3) end)
Missions:destroy() fails if the first parameter is a person
assert.has_error(function() Missions:destroy(personMock()) end)
Missions:destroy() fails if the first parameter is a table where one item is a person
assert.has_error(function() Missions:destroy({CpuShip(), SpaceStation(), personMock()}) end)
Missions:destroy() fails if the first parameter is not given
assert.has_error(function() Missions:destroy() end)
Missions:destroy() should create a valid Mission if a callback function is given that returns mixed space objects
local thing1 = CpuShip()
local thing2 = SpaceStation()
local thing3 = WarpJammer()
local thing4 = ScanProbe()
local mission = Missions:destroy(function() return {thing1, thing2, thing3, thing4} end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(thing1, mission:getValidEnemies())
assert.contains_value(thing2, mission:getValidEnemies())
assert.contains_value(thing3, mission:getValidEnemies())
assert.contains_value(thing4, mission:getValidEnemies())
Missions:destroy() should create a valid Mission if a callback function is given that returns one scan probe
local probe = ScanProbe()
local mission = Missions:destroy(function() return probe end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({ probe }, mission:getValidEnemies())
Missions:destroy() should create a valid Mission if a callback function is given that returns one ship
local ship = CpuShip()
local mission = Missions:destroy(function() return ship end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({ship}, mission:getValidEnemies())
Missions:destroy() should create a valid Mission if a callback function is given that returns one station
local station = SpaceStation()
local mission = Missions:destroy(function() return station end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({station}, mission:getValidEnemies())
Missions:destroy() should create a valid Mission if a callback function is given that returns one warp jammer
local jammer = WarpJammer()
local mission = Missions:destroy(function() return jammer end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({ jammer }, mission:getValidEnemies())
Missions:destroy() should create a valid Mission with mixed space objects
local mission = Missions:destroy({CpuShip(), SpaceStation(), CpuShip(), WarpJammer(), ScanProbe()})
assert.is_true(Mission:isMission(mission))
Missions:destroy() should create a valid Mission with one ScanProbe
local mission = Missions:destroy(ScanProbe())
assert.is_true(Mission:isMission(mission))
Missions:destroy() should create a valid Mission with one WarpJammer
local mission = Missions:destroy(WarpJammer())
assert.is_true(Mission:isMission(mission))
Missions:destroy() should create a valid Mission with one ship
local mission = Missions:destroy(CpuShip())
assert.is_true(Mission:isMission(mission))
Missions:destroy() should create a valid Mission with one station
local mission = Missions:destroy(SpaceStation())
assert.is_true(Mission:isMission(mission))
Missions:destroy():getValidEnemies(),
Missions:destroy():getValidEnemies(), countValidEnemies(), getInvalidEnemies(), countInvalidEnemies(), getEnemies(), countEnemies() return correct values
local enemy1 = SpaceStation()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local enemy4 = WarpJammer()
local enemy5 = ScanProbe()
local mission = Missions:destroy({enemy1, enemy2, enemy3, enemy4, enemy5})
assert.is_same(5, mission:countEnemies())
assert.is_same(5, mission:countValidEnemies())
assert.is_same(0, mission:countInvalidEnemies())
assert.contains_value(enemy1, mission:getEnemies())
assert.contains_value(enemy2, mission:getEnemies())
assert.contains_value(enemy3, mission:getEnemies())
assert.contains_value(enemy4, mission:getEnemies())
assert.contains_value(enemy5, mission:getEnemies())
assert.contains_value(enemy1, mission:getValidEnemies())
assert.contains_value(enemy2, mission:getValidEnemies())
assert.contains_value(enemy3, mission:getValidEnemies())
assert.contains_value(enemy4, mission:getValidEnemies())
assert.contains_value(enemy5, mission:getValidEnemies())
assert.not_contains_value(enemy1, mission:getInvalidEnemies())
assert.not_contains_value(enemy2, mission:getInvalidEnemies())
assert.not_contains_value(enemy3, mission:getInvalidEnemies())
assert.not_contains_value(enemy4, mission:getInvalidEnemies())
assert.not_contains_value(enemy5, mission:getInvalidEnemies())
enemy1:destroy()
assert.is_same(5, mission:countEnemies())
assert.is_same(4, mission:countValidEnemies())
assert.is_same(1, mission:countInvalidEnemies())
assert.contains_value(enemy1, mission:getEnemies())
assert.contains_value(enemy2, mission:getEnemies())
assert.contains_value(enemy3, mission:getEnemies())
assert.contains_value(enemy4, mission:getEnemies())
assert.contains_value(enemy5, mission:getEnemies())
assert.not_contains_value(enemy1, mission:getValidEnemies())
assert.contains_value(enemy2, mission:getValidEnemies())
assert.contains_value(enemy3, mission:getValidEnemies())
assert.contains_value(enemy4, mission:getValidEnemies())
assert.contains_value(enemy5, mission:getValidEnemies())
assert.contains_value(enemy1, mission:getInvalidEnemies())
assert.not_contains_value(enemy2, mission:getInvalidEnemies())
assert.not_contains_value(enemy3, mission:getInvalidEnemies())
assert.not_contains_value(enemy4, mission:getInvalidEnemies())
assert.not_contains_value(enemy5, mission:getInvalidEnemies())
enemy2:destroy()
enemy3:destroy()
assert.is_same(5, mission:countEnemies())
assert.is_same(2, mission:countValidEnemies())
assert.is_same(3, mission:countInvalidEnemies())
assert.contains_value(enemy1, mission:getEnemies())
assert.contains_value(enemy2, mission:getEnemies())
assert.contains_value(enemy3, mission:getEnemies())
assert.contains_value(enemy4, mission:getEnemies())
assert.contains_value(enemy5, mission:getEnemies())
assert.not_contains_value(enemy1, mission:getValidEnemies())
assert.not_contains_value(enemy2, mission:getValidEnemies())
assert.not_contains_value(enemy3, mission:getValidEnemies())
assert.contains_value(enemy4, mission:getValidEnemies())
assert.contains_value(enemy5, mission:getValidEnemies())
assert.contains_value(enemy1, mission:getInvalidEnemies())
assert.contains_value(enemy2, mission:getInvalidEnemies())
assert.contains_value(enemy3, mission:getInvalidEnemies())
assert.not_contains_value(enemy4, mission:getInvalidEnemies())
assert.not_contains_value(enemy5, mission:getInvalidEnemies())
enemy4:destroy()
enemy5:destroy()
assert.is_same(5, mission:countEnemies())
assert.is_same(0, mission:countValidEnemies())
assert.is_same(5, mission:countInvalidEnemies())
assert.contains_value(enemy1, mission:getEnemies())
assert.contains_value(enemy2, mission:getEnemies())
assert.contains_value(enemy3, mission:getEnemies())
assert.contains_value(enemy4, mission:getEnemies())
assert.contains_value(enemy5, mission:getEnemies())
assert.not_contains_value(enemy1, mission:getValidEnemies())
assert.not_contains_value(enemy2, mission:getValidEnemies())
assert.not_contains_value(enemy3, mission:getValidEnemies())
assert.not_contains_value(enemy4, mission:getValidEnemies())
assert.not_contains_value(enemy5, mission:getValidEnemies())
assert.contains_value(enemy1, mission:getInvalidEnemies())
assert.contains_value(enemy2, mission:getInvalidEnemies())
assert.contains_value(enemy3, mission:getInvalidEnemies())
assert.contains_value(enemy4, mission:getInvalidEnemies())
assert.contains_value(enemy5, mission:getInvalidEnemies())
Missions:destroy():getValidEnemies(), countValidEnemies(), getInvalidEnemies(), countInvalidEnemies(), getEnemies(), countEnemies() returns nil it it is called before the ships are created in the callback
local enemy1 = SpaceStation()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission = Missions:destroy(function () return {enemy1, enemy2, enemy3} end)
assert.is_nil(mission:countEnemies())
assert.is_nil(mission:countValidEnemies())
assert.is_nil(mission:countInvalidEnemies())
assert.is_nil(mission:getEnemies())
assert.is_nil(mission:getValidEnemies())
assert.is_nil(mission:getInvalidEnemies())
Missions:destroy():getValidEnemies(), countValidEnemies(), getInvalidEnemies(), countInvalidEnemies(), getEnemies(), countEnemies() should not allow to manipulate the tables
local enemy1 = SpaceStation()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission = Missions:destroy({enemy1, enemy2})
local enemies = mission:getEnemies()
table.insert(enemies, enemy3)
assert.is_same(2, mission:countEnemies())
assert.not_contains_value(enemy3, mission:getEnemies())
Missions:destroyRagingMiner()
Missions:destroyRagingMiner() can run a successful mission
local enemy1 = CpuShip()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission
mission = Missions:destroyRagingMiner({enemy1, enemy2, enemy3})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
enemy1:destroy()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
enemy2:destroy()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
enemy3:destroy()
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:destroyRagingMiner() config.onDestruction is called each time an enemy is destroyed
local enemy1 = CpuShip()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local callback1Called = 0
local callback2Called = 0
local callback3Called = 0
local mission
mission = Missions:destroyRagingMiner({enemy1, enemy2, enemy3}, {onDestruction = function(callMission, callEnemy)
assert.is_same(mission, callMission)
if callEnemy == enemy1 then callback1Called = callback1Called + 1 end
if callEnemy == enemy2 then callback2Called = callback2Called + 1 end
if callEnemy == enemy3 then callback3Called = callback3Called + 1 end
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, callback1Called)
assert.is_same(0, callback2Called)
assert.is_same(0, callback3Called)
enemy1:destroy()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(0, callback2Called)
assert.is_same(0, callback3Called)
enemy2:destroy()
enemy3:destroy()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(1, callback2Called)
assert.is_same(1, callback3Called)
Missions:destroyRagingMiner() fails if a call back function is given that returns nil
assert.has_error(function()
Missions:destroyRagingMiner(function() return nil end)
mission:setPlayer(player)
mission:accept()
mission:start()
end)
Missions:destroyRagingMiner() fails if a call back function is given, but returns a person
assert.has_error(function()
Missions:destroyRagingMiner(function() return personMock() end)
mission:setPlayer(player)
mission:accept()
mission:start()
end)
Missions:destroyRagingMiner() fails if a call back function is given, but returns a table where one item is a person
assert.has_error(function()
Missions:destroyRagingMiner(function() return {CpuShip(), CpuShip(), personMock()} end)
mission:setPlayer(player)
mission:accept()
mission:start()
end)
Missions:destroyRagingMiner() fails if second parameter is a number
assert.has_error(function() Missions:destroyRagingMiner(CpuShip(), 3) end)
Missions:destroyRagingMiner() fails if the first parameter is a person
assert.has_error(function() Missions:destroyRagingMiner(personMock()) end)
Missions:destroyRagingMiner() fails if the first parameter is a table where one item is a person
assert.has_error(function() Missions:destroyRagingMiner({CpuShip(), CpuShip(), personMock()}) end)
Missions:destroyRagingMiner() fails if the first parameter is not given
assert.has_error(function() Missions:destroyRagingMiner() end)
Missions:destroyRagingMiner() should create a valid Mission if a callback function is given that returns multiple ships
local mission = Missions:destroyRagingMiner(function() return {CpuShip(), CpuShip(), CpuShip()} end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
Missions:destroyRagingMiner() should create a valid Mission if a callback function is given that returns one ship
local ship = CpuShip()
local mission = Missions:destroyRagingMiner(function() return ship end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({ship}, mission:getValidEnemies())
Missions:destroyRagingMiner() should create a valid Mission with one ship
local mission = Missions:destroyRagingMiner(CpuShip())
assert.is_true(Mission:isMission(mission))
Missions:destroyRagingMiner():getValidEnemies(),
Missions:destroyRagingMiner():getValidEnemies(), countValidEnemies(), getInvalidEnemies(), countInvalidEnemies(), getEnemies(), countEnemies() return correct values
local enemy1 = CpuShip()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission = Missions:destroyRagingMiner({enemy1, enemy2, enemy3})
assert.is_same(3, mission:countEnemies())
assert.is_same(3, mission:countValidEnemies())
assert.is_same(0, mission:countInvalidEnemies())
assert.contains_value(enemy1, mission:getEnemies())
assert.contains_value(enemy2, mission:getEnemies())
assert.contains_value(enemy3, mission:getEnemies())
assert.contains_value(enemy1, mission:getValidEnemies())
assert.contains_value(enemy2, mission:getValidEnemies())
assert.contains_value(enemy3, mission:getValidEnemies())
assert.not_contains_value(enemy1, mission:getInvalidEnemies())
assert.not_contains_value(enemy2, mission:getInvalidEnemies())
assert.not_contains_value(enemy3, mission:getInvalidEnemies())
enemy1:destroy()
assert.is_same(3, mission:countEnemies())
assert.is_same(2, mission:countValidEnemies())
assert.is_same(1, mission:countInvalidEnemies())
assert.contains_value(enemy1, mission:getEnemies())
assert.contains_value(enemy2, mission:getEnemies())
assert.contains_value(enemy3, mission:getEnemies())
assert.not_contains_value(enemy1, mission:getValidEnemies())
assert.contains_value(enemy2, mission:getValidEnemies())
assert.contains_value(enemy3, mission:getValidEnemies())
assert.contains_value(enemy1, mission:getInvalidEnemies())
assert.not_contains_value(enemy2, mission:getInvalidEnemies())
assert.not_contains_value(enemy3, mission:getInvalidEnemies())
enemy2:destroy()
enemy3:destroy()
assert.is_same(3, mission:countEnemies())
assert.is_same(0, mission:countValidEnemies())
assert.is_same(3, mission:countInvalidEnemies())
assert.contains_value(enemy1, mission:getEnemies())
assert.contains_value(enemy2, mission:getEnemies())
assert.contains_value(enemy3, mission:getEnemies())
assert.not_contains_value(enemy1, mission:getValidEnemies())
assert.not_contains_value(enemy2, mission:getValidEnemies())
assert.not_contains_value(enemy3, mission:getValidEnemies())
assert.contains_value(enemy1, mission:getInvalidEnemies())
assert.contains_value(enemy2, mission:getInvalidEnemies())
assert.contains_value(enemy3, mission:getInvalidEnemies())
Missions:destroyRagingMiner():getValidEnemies(), countValidEnemies(), getInvalidEnemies(), countInvalidEnemies(), getEnemies(), countEnemies() returns nil it it is called before the ships are created in the callback
local enemy1 = CpuShip()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission = Missions:destroyRagingMiner(function () return {enemy1, enemy2, enemy3} end)
assert.is_nil(mission:countEnemies())
assert.is_nil(mission:countValidEnemies())
assert.is_nil(mission:countInvalidEnemies())
assert.is_nil(mission:getEnemies())
assert.is_nil(mission:getValidEnemies())
assert.is_nil(mission:getInvalidEnemies())
Missions:destroyRagingMiner():getValidEnemies(), countValidEnemies(), getInvalidEnemies(), countInvalidEnemies(), getEnemies(), countEnemies() should not allow to manipulate the tables
local enemy1 = CpuShip()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission = Missions:destroyRagingMiner({enemy1, enemy2})
local enemies = mission:getEnemies()
table.insert(enemies, enemy3)
assert.is_same(2, mission:countEnemies())
assert.not_contains_value(enemy3, mission:getEnemies())
Missions:disable()
Missions:disable() can run a successful mission
local ship = CpuShip()
local player = PlayerSpaceship()
local mission
mission = Missions:disable(ship, {
damageThreshold = -0.5,
distanceToFinish = 1000,
})
ship:setPosition(0, 0)
player:setPosition(9999, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same("started", mission:getState())
player:setPosition(500, 0)
Cron.tick(1)
assert.is_same("started", mission:getState())
ship:setSystemHealth("impulse", -1)
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:disable() config.onApproach is called when the player first enters 10u around the target
local onApproachCalled = 0
local onApproachArg1 = nil
local ship = CpuShip()
local mission
mission = Missions:disable(ship, { approachDistance = 10000, onApproach = function(arg1)
onApproachArg1 = arg1
onApproachCalled = onApproachCalled + 1
end})
player:setPosition(20000, 0)
ship:setPosition(0, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onApproachCalled)
player:setPosition(10001, 0)
Cron.tick(1)
assert.is_same(0, onApproachCalled)
player:setPosition(9999, 0)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
assert.is_same(mission, onApproachArg1)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
player:setPosition(10001, 0)
Cron.tick(1)
player:setPosition(9999, 0)
Cron.tick(1)
assert.is_same(1, onApproachCalled)
Missions:disable() config.onDestruction is called when ship is completely destroyed
local onDestructionCalled = 0
local onDestructionArg1 = nil
local ship = CpuShip()
local player = PlayerSpaceship()
local mission
mission = Missions:disable(ship, { onDestruction = function(arg1)
onDestructionArg1 = arg1
onDestructionCalled = onDestructionCalled + 1
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onDestructionCalled)
ship:destroy()
Cron.tick(1)
assert.is_same(1, onDestructionCalled)
assert.is_same(mission, onDestructionArg1)
assert.is_same("failed", mission:getState())
Missions:disable() config.onSurrender closeness is necessary for surrender
local onSurrenderCalled = 0
local onSurrenderArg1 = nil
local ship = CpuShip()
local player = PlayerSpaceship()
local mission
mission = Missions:disable(ship, {
damageThreshold = -0.5,
distanceToFinish = 1000,
onSurrender = function(arg1)
onSurrenderArg1 = arg1
onSurrenderCalled = onSurrenderCalled + 1
end
})
ship:setPosition(0, 0)
player:setPosition(9999, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("impulse", -1)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
player:setPosition(1001, 0)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
player:setPosition(1000, 0)
Cron.tick(1)
assert.is_same(1, onSurrenderCalled)
assert.is_same(mission, onSurrenderArg1)
Missions:disable() config.onSurrender disabled impulse engine is necessary for surrender
local onSurrenderCalled = 0
local onSurrenderArg1 = nil
local ship = CpuShip()
local player = PlayerSpaceship()
local mission
mission = Missions:disable(ship, {
damageThreshold = -0.5,
distanceToFinish = 1000,
onSurrender = function(arg1)
onSurrenderArg1 = arg1
onSurrenderCalled = onSurrenderCalled + 1
end
})
ship:setPosition(0, 0)
player:setPosition(100, 0)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("impulse", 0)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("impulse", -0.49)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("impulse", -0.5)
Cron.tick(1)
assert.is_same(1, onSurrenderCalled)
assert.is_same(mission, onSurrenderArg1)
Missions:disable() config.onSurrender jump drive needs to be destroyed too if a ship has it
local onSurrenderCalled = 0
local onSurrenderArg1 = nil
local ship = CpuShip()
ship:setJumpDrive(true)
local player = PlayerSpaceship()
local mission
mission = Missions:disable(ship, {
damageThreshold = -0.5,
distanceToFinish = 1000,
onSurrender = function(arg1)
onSurrenderArg1 = arg1
onSurrenderCalled = onSurrenderCalled + 1
end
})
mission:setPlayer(player)
mission:accept()
mission:start()
ship:setSystemHealth("impulse", -1)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("jumpdrive", 0)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("jumpdrive", -0.49)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("jumpdrive", -0.5)
Cron.tick(1)
assert.is_same(1, onSurrenderCalled)
assert.is_same(mission, onSurrenderArg1)
Missions:disable() config.onSurrender warp drive needs to be destroyed too if a ship has it
local onSurrenderCalled = 0
local onSurrenderArg1 = nil
local ship = CpuShip()
ship:setWarpDrive(true)
local player = PlayerSpaceship()
local mission
mission = Missions:disable(ship, {
damageThreshold = -0.5,
distanceToFinish = 1000,
onSurrender = function(arg1)
onSurrenderArg1 = arg1
onSurrenderCalled = onSurrenderCalled + 1
end
})
mission:setPlayer(player)
mission:accept()
mission:start()
ship:setSystemHealth("impulse", -1)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("warp", 0)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("warp", -0.49)
Cron.tick(1)
assert.is_same(0, onSurrenderCalled)
ship:setSystemHealth("warp", -0.5)
Cron.tick(1)
assert.is_same(1, onSurrenderCalled)
assert.is_same(mission, onSurrenderArg1)
Missions:disable() fails if no valid ship is given
assert.has_error(function() Missions:disable(nil) end)
assert.has_error(function() Missions:disable(personMock()) end)
assert.has_error(function() Missions:disable(42) end)
assert.has_error(function() Missions:disable(SpaceStation()) end)
Missions:disable() should create a valid Mission with a function returning a ship
local mission = Missions:disable(function() return CpuShip() end)
assert.is_true(Mission:isMission(mission))
Missions:disable() should create a valid Mission with one ship
local mission = Missions:disable(CpuShip())
assert.is_true(Mission:isMission(mission))
Missions:disable():getTarget()
Missions:disable():getTarget() returns the target when no function is used
local ship = CpuShip()
local mission = Missions:disable(ship)
assert.is_same(ship, mission:getTarget())
Missions:disable():getTarget() returns the target when no function is used
local ship = CpuShip()
local player = PlayerSpaceship()
local mission = Missions:disable(function() return ship end)
assert.is_nil(mission:getTarget())
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same(ship, mission:getTarget())
Missions:pickUp()
Missions:pickUp() can run a happy scenario with return to station
local artifact = Artifact()
local supplyDrop = SupplyDrop()
local station = SpaceStation()
local player = PlayerSpaceship()
local onStartCalled, onSuccessCalled, onEndCalled = 0, 0, 0
local onPickUpCalled, onPickUpArg1, onPickUpArg2 = 0, nil, nil
local onAllPickedUpCalled, onAllPickedUpArg1 = 0, nil
local mission = Missions:pickUp({artifact, supplyDrop}, station, {
onStart = function() onStartCalled = onStartCalled + 1 end,
onPickUp = function(arg1, arg2)
onPickUpCalled = onPickUpCalled + 1
onPickUpArg1 = arg1
onPickUpArg2 = arg2
end,
onAllPickedUp = function(arg1)
onAllPickedUpCalled = onAllPickedUpCalled + 1
onAllPickedUpArg1 = arg1
end,
onSuccess = function() onSuccessCalled = onSuccessCalled + 1 end,
onEnd = function() onEndCalled = onEndCalled + 1 end,
})
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(artifact, mission:getPickUps())
assert.contains_value(supplyDrop, mission:getPickUps())
assert.is_same(2, mission:countPickUps())
assert.is_same(station, mission:getDeliveryStation())
assert.is_same(1, onStartCalled)
assert.is_same(0, onPickUpCalled)
assert.is_same(0, onAllPickedUpCalled)
assert.is_same(0, onSuccessCalled)
assert.is_same(0, onEndCalled)
artifact:pickUp(player)
assert.is_same(1, onPickUpCalled)
assert.is_same(mission, onPickUpArg1)
assert.is_same(artifact, onPickUpArg2)
assert.is_same(0, onAllPickedUpCalled)
supplyDrop:pickUp(player)
assert.is_same(2, onPickUpCalled)
assert.is_same(mission, onPickUpArg1)
assert.is_same(supplyDrop, onPickUpArg2)
assert.is_same(1, onAllPickedUpCalled)
assert.is_same(mission, onAllPickedUpArg1)
Cron.tick(1)
assert.is_same(0, onSuccessCalled)
assert.is_same(0, onEndCalled)
player:setDockedAt(station)
Cron.tick(1)
assert.is_same(1, onSuccessCalled)
assert.is_same(1, onEndCalled)
Missions:pickUp() can run a happy scenario without return to station
local artifact = Artifact()
local supplyDrop = SupplyDrop()
local player = PlayerSpaceship()
local onStartCalled, onSuccessCalled, onEndCalled = 0, 0, 0
local onPickUpCalled, onPickUpArg1, onPickUpArg2 = 0, nil, nil
local onAllPickedUpCalled, onAllPickedUpArg1 = 0, nil
local mission = Missions:pickUp({artifact, supplyDrop}, {
onStart = function() onStartCalled = onStartCalled + 1 end,
onPickUp = function(arg1, arg2)
onPickUpCalled = onPickUpCalled + 1
onPickUpArg1 = arg1
onPickUpArg2 = arg2
end,
onAllPickedUp = function(arg1)
onAllPickedUpCalled = onAllPickedUpCalled + 1
onAllPickedUpArg1 = arg1
end,
onSuccess = function() onSuccessCalled = onSuccessCalled + 1 end,
onEnd = function() onEndCalled = onEndCalled + 1 end,
})
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(artifact, mission:getPickUps())
assert.contains_value(supplyDrop, mission:getPickUps())
assert.is_same(2, mission:countPickUps())
assert.is_nil(mission:getDeliveryStation())
assert.is_same(1, onStartCalled)
assert.is_same(0, onPickUpCalled)
assert.is_same(0, onAllPickedUpCalled)
assert.is_same(0, onSuccessCalled)
assert.is_same(0, onEndCalled)
artifact:pickUp(player)
assert.is_same(1, onPickUpCalled)
assert.is_same(mission, onPickUpArg1)
assert.is_same(artifact, onPickUpArg2)
assert.is_same(0, onAllPickedUpCalled)
supplyDrop:pickUp(player)
assert.is_same(2, onPickUpCalled)
assert.is_same(mission, onPickUpArg1)
assert.is_same(supplyDrop, onPickUpArg2)
assert.is_same(1, onAllPickedUpCalled)
assert.is_same(mission, onAllPickedUpArg1)
assert.is_same(1, onSuccessCalled)
assert.is_same(1, onEndCalled)
Missions:pickUp() fails if a pickup disappears
local artifact = Artifact()
local supplyDrop = SupplyDrop()
local player = PlayerSpaceship()
local mission = Missions:pickUp({artifact, supplyDrop})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
artifact:destroy()
Cron.tick(1)
assert.is_same("failed", mission:getState())
Missions:pickUp() fails if a pickup is picked up by a different player
local artifact = Artifact()
local supplyDrop = SupplyDrop()
local player = PlayerSpaceship()
local evilPlayer = PlayerSpaceship()
local onPickUpCalled, onFailureCalled, onEndCalled = 0, 0, 0
local mission = Missions:pickUp({artifact, supplyDrop}, {
onPickUp = function() onPickUpCalled = onPickUpCalled + 1 end,
onFailure = function() onFailureCalled = onFailureCalled + 1 end,
onEnd = function() onEndCalled = onEndCalled + 1 end,
})
mission:setPlayer(player)
mission:accept()
mission:start()
artifact:pickUp(evilPlayer)
assert.is_same(0, onPickUpCalled)
assert.is_same(1, onFailureCalled)
assert.is_same(1, onEndCalled)
Missions:pickUp() fails if deliveryStation disappears
local artifact = Artifact()
local supplyDrop = SupplyDrop()
local station = SpaceStation()
local player = PlayerSpaceship()
local mission = Missions:pickUp({artifact, supplyDrop}, station)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
station:destroy()
Cron.tick(1)
assert.is_same("failed", mission:getState())
Missions:pickUp() fails if first argument is invalid
assert.has_error(function() Mission:pickUp(SpaceStation()) end)
assert.has_error(function() Mission:pickUp(nil) end)
assert.has_error(function() Mission:pickUp(42) end)
assert.has_error(function() Mission:pickUp({SpaceStation(), SupplyDrop()}) end)
assert.has_error(function() Mission:pickUp(function()
return SpaceStation()
end) end)
assert.has_error(function() Mission:pickUp(function()
return nil
end) end)
assert.has_error(function() Mission:pickUp(function()
return 42
end) end)
assert.has_error(function() Mission:pickUp(function()
return {SpaceStation(), SupplyDrop()}
end) end)
Missions:pickUp() should create a valid Mission with a function returning a supply drop
local supplyDrop = SupplyDrop()
local mission = Missions:pickUp(function() return supplyDrop end)
assert.is_true(Mission:isMission(mission))
assert.is_nil(mission:getPickUps())
assert.is_nil(mission:countPickUps())
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({supplyDrop}, mission:getPickUps())
assert.is_same(1, mission:countPickUps())
Missions:pickUp() should create a valid Mission with a function returning a supply drop and artifact
local artifact = Artifact()
local supplyDrop = SupplyDrop()
local mission = Missions:pickUp(function() return {artifact, supplyDrop} end)
assert.is_true(Mission:isMission(mission))
assert.is_nil(mission:getPickUps())
assert.is_nil(mission:countPickUps())
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(artifact, mission:getPickUps())
assert.contains_value(supplyDrop, mission:getPickUps())
assert.is_same(2, mission:countPickUps())
Missions:pickUp() should create a valid Mission with a function returning an artifact
local artifact = Artifact()
local mission = Missions:pickUp(function() return artifact end)
assert.is_true(Mission:isMission(mission))
assert.is_nil(mission:getPickUps())
assert.is_nil(mission:countPickUps())
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({artifact}, mission:getPickUps())
assert.is_same(1, mission:countPickUps())
Missions:pickUp() should create a valid Mission with a supply drop
local supplyDrop = SupplyDrop()
local mission = Missions:pickUp(supplyDrop)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({supplyDrop}, mission:getPickUps())
assert.is_same(1, mission:countPickUps())
Missions:pickUp() should create a valid Mission with a supply drop and artifact
local artifact = Artifact()
local supplyDrop = SupplyDrop()
local mission = Missions:pickUp({artifact, supplyDrop})
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(artifact, mission:getPickUps())
assert.contains_value(supplyDrop, mission:getPickUps())
assert.is_same(2, mission:countPickUps())
Missions:pickUp() should create a valid Mission with an artifact
local artifact = Artifact()
local mission = Missions:pickUp(artifact)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({artifact}, mission:getPickUps())
assert.is_same(1, mission:countPickUps())
Missions:scan()
Missions:scan() can run a successful mission when not all ships are destroyed
local target1 = CpuShip()
local target2 = CpuShip()
local target3 = CpuShip()
local mission
mission = Missions:scan({ target1, target2, target3 })
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
target1:destroy()
target2:destroy()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
target3:fullScannedByPlayer()
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:scan() can run a successful mission with asteroids
local target1 = Asteroid()
local target2 = Asteroid()
local target3 = Asteroid()
local mission
mission = Missions:scan({ target1, target2, target3 })
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
target1:scannedByPlayer()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
target2:scannedByPlayer()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
target3:scannedByPlayer()
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:scan() can run a successful mission with ships
local target1 = CpuShip()
local target2 = CpuShip()
local target3 = CpuShip()
local mission
mission = Missions:scan({ target1, target2, target3 }, {scan = "full"})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
target1:fullScannedByPlayer()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
target2:fullScannedByPlayer()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
target3:fullScannedByPlayer()
Cron.tick(1)
assert.is_same("successful", mission:getState())
Missions:scan() config.onDestruction is called when a target is destroyed
local target1 = CpuShip()
local target2 = CpuShip()
local callback1Called = 0
local callback2Called = 0
local mission
mission = Missions:scan({ target1, target2 }, {onDestruction = function(callMission, callEnemy)
assert.is_same(mission, callMission)
if callEnemy == target1 then callback1Called = callback1Called + 1 end
if callEnemy == target2 then callback2Called = callback2Called + 1 end
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, callback1Called)
assert.is_same(0, callback2Called)
target1:destroy()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(0, callback2Called)
target2:destroy()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(1, callback2Called)
Missions:scan() config.onDestruction the unscanned targets are already updated
local target1 = CpuShip()
local target2 = CpuShip()
local calledArg1, calledArg2, calledUnscannedTargets, calledUnscannedTargetsCount
local mission
mission = Missions:scan({ target1, target2 }, {onDestruction = function(arg1, arg2)
calledArg1 = arg1
calledArg2 = arg2
calledUnscannedTargets = arg1:getUnscannedTargets()
calledUnscannedTargetsCount = arg1:countUnscannedTargets()
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
target1:destroy()
Cron.tick(1)
assert.is_same(mission, calledArg1)
assert.is_same(target1, calledArg2)
assert.is_same(1, calledUnscannedTargetsCount)
assert.is_same({target2}, calledUnscannedTargets)
Missions:scan() config.onScan is called by default simple scan was run
local target = CpuShip()
local callback1Called = 0
local mission
mission = Missions:scan(target, {onScan = function(callMission, callTarget)
callback1Called = callback1Called + 1
assert.is_same(mission, callMission)
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
target:friendOrFoeIdentifiedByPlayer()
Cron.tick(1)
assert.is_same(0, callback1Called)
Cron.tick(1)
target:scannedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
target:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
Missions:scan() config.onScan is called each time a target is scanned
local target1 = CpuShip()
local target2 = CpuShip()
local target3 = CpuShip()
local callback1Called = 0
local callback2Called = 0
local callback3Called = 0
local mission
mission = Missions:scan({ target1, target2, target3 }, {onScan = function(callMission, callTarget)
if callTarget == target1 then callback1Called = callback1Called + 1 end
if callTarget == target2 then callback2Called = callback2Called + 1 end
if callTarget == target3 then callback3Called = callback3Called + 1 end
assert.is_same(mission, callMission)
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, callback1Called)
assert.is_same(0, callback2Called)
assert.is_same(0, callback3Called)
target1:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(0, callback2Called)
assert.is_same(0, callback3Called)
target2:fullScannedByPlayer()
target3:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
assert.is_same(1, callback2Called)
assert.is_same(1, callback3Called)
Missions:scan() config.onScan is called when friend or foe is identified
local target = CpuShip()
local callback1Called = 0
local mission
mission = Missions:scan(target, {scan = "fof", onScan = function(callMission, callTarget)
callback1Called = callback1Called + 1
assert.is_same(mission, callMission)
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, callback1Called)
target:friendOrFoeIdentifiedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
Cron.tick(1)
target:scannedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
target:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
Missions:scan() config.onScan is only called after full scan when a full scan is required
local target = CpuShip()
local callback1Called = 0
local mission
mission = Missions:scan(target, {
scan = "full",
onScan = function(callMission, callTarget)
callback1Called = callback1Called + 1
assert.is_same(mission, callMission)
end }
)
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, callback1Called)
target:friendOrFoeIdentifiedByPlayer()
Cron.tick(1)
assert.is_same(0, callback1Called)
Cron.tick(1)
target:scannedByPlayer()
Cron.tick(1)
assert.is_same(0, callback1Called)
target:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(1, callback1Called)
Missions:scan() config.onScan the unscanned targets are already updated
local target1 = CpuShip()
local target2 = CpuShip()
local calledArg1, calledArg2, calledUnscannedTargets, calledUnscannedTargetsCount
local mission
mission = Missions:scan({ target1, target2 }, {onScan = function(arg1, arg2)
calledArg1 = arg1
calledArg2 = arg2
calledUnscannedTargets = arg1:getUnscannedTargets()
calledUnscannedTargetsCount = arg1:countUnscannedTargets()
end})
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
Cron.tick(1)
target1:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(mission, calledArg1)
assert.is_same(target1, calledArg2)
assert.is_same(1, calledUnscannedTargetsCount)
assert.is_same({target2}, calledUnscannedTargets)
Missions:scan() config.scan can be set to "fof", "simple" or "full" for ships
Missions:scan(CpuShip(), {scan = "fof"})
Missions:scan({CpuShip(), CpuShip()}, {scan = "fof"})
Missions:scan(CpuShip(), {scan = "simple"})
Missions:scan({CpuShip(), CpuShip()}, {scan = "simple"})
Missions:scan(CpuShip(), {scan = "full"})
Missions:scan({CpuShip(), CpuShip()}, {scan = "full"})
-- it does not error
Missions:scan() config.scan can be set to "simple" for asteroids
Missions:scan(Asteroid(), {scan = "simple"})
Missions:scan({Asteroid(), Asteroid()}, {scan = "simple"})
-- it does not error
Missions:scan() config.scan fails if it is an unknown string
assert.has_error(function()
Missions:scan(CpuShip(), {scan = "foobar"})
end)
Missions:scan() config.scan fails if it is set to "fof" and function returns a list where one is not a ship
local mission = Missions:scan(function() return {Asteroid(), CpuShip()} end, {scan = "fof"})
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:scan() config.scan fails if it is set to "fof" and function returns a non-ship
local mission = Missions:scan(function() return Asteroid() end, {scan = "fof"})
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:scan() config.scan fails if it is set to "fof" and function returns multiple non-ships
local mission = Missions:scan(function() return {Asteroid(), Asteroid()} end, {scan = "fof"})
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:scan() config.scan fails if it is set to "fof" for a list where one is not a ship
assert.has_error(function()
Missions:scan({CpuShip(), Asteroid()}, {scan = "fof"})
end)
Missions:scan() config.scan fails if it is set to "fof" for a multiple non-ship
assert.has_error(function()
Missions:scan({Asteroid(), Asteroid()}, {scan = "fof"})
end)
Missions:scan() config.scan fails if it is set to "fof" for a non-ship
assert.has_error(function()
Missions:scan(Asteroid(), {scan = "fof"})
end)
Missions:scan() config.scan fails if it is set to "full" and function returns a list where one is not a ship
local mission = Missions:scan(function() return {Asteroid(), CpuShip()} end, {scan = "full"})
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:scan() config.scan fails if it is set to "full" and function returns a non-ship
local mission = Missions:scan(function() return Asteroid() end, {scan = "full"})
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:scan() config.scan fails if it is set to "full" and function returns multiple non-ships
local mission = Missions:scan(function() return {Asteroid(), Asteroid()} end, {scan = "full"})
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:scan() config.scan fails if it is set to "full" for a list where one is not a ship
assert.has_error(function()
Missions:scan({CpuShip(), Asteroid()}, {scan = "full"})
end)
Missions:scan() config.scan fails if it is set to "full" for a multiple non-ship
assert.has_error(function()
Missions:scan({Asteroid(), Asteroid()}, {scan = "full"})
end)
Missions:scan() config.scan fails if it is set to "full" for a non-ship
assert.has_error(function()
Missions:scan(Asteroid(), {scan = "full"})
end)
Missions:scan() fails if a call back function is given that returns nil
local mission = Missions:scan(function() return nil end)
mission:setPlayer(player)
mission:accept()
assert.has_error(function()
mission:start()
end)
Missions:scan() fails if no player was set
local mission = Missions:scan(CpuShip())
assert.has_error(function()
mission:accept()
mission:start()
end)
Missions:scan() fails if second parameter is a number
assert.has_error(function() Missions:scan(CpuShip(), 3) end)
Missions:scan() fails if the first parameter is not given
assert.has_error(function() Missions:scan() end)
Missions:scan() fails when all ships are destroyed without having scanned one
local target1 = CpuShip()
local target2 = CpuShip()
local target3 = CpuShip()
local mission
mission = Missions:scan({ target1, target2, target3 })
mission:setPlayer(player)
mission:accept()
mission:start()
Cron.tick(1)
target1:destroy()
target2:destroy()
Cron.tick(1)
assert.is_same("started", mission:getState())
Cron.tick(1)
target3:destroy()
Cron.tick(1)
assert.is_same("failed", mission:getState())
Missions:scan() should create a valid Mission if a callback function is given that returns multiple asteroids
local thing1 = Asteroid()
local thing2 = Asteroid()
local thing3 = Asteroid()
local mission = Missions:scan(function() return {thing1, thing2, thing3} end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(thing1, mission:getTargets())
assert.contains_value(thing2, mission:getTargets())
assert.contains_value(thing3, mission:getTargets())
Missions:scan() should create a valid Mission if a callback function is given that returns multiple mixed objects
local thing1 = SpaceStation()
local thing2 = CpuShip()
local thing3 = Asteroid()
local mission = Missions:scan(function() return {thing1, thing2, thing3} end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(thing1, mission:getTargets())
assert.contains_value(thing2, mission:getTargets())
assert.contains_value(thing3, mission:getTargets())
Missions:scan() should create a valid Mission if a callback function is given that returns multiple ships
local thing1 = CpuShip()
local thing2 = CpuShip()
local thing3 = CpuShip()
local mission = Missions:scan(function() return {thing1, thing2, thing3} end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(thing1, mission:getTargets())
assert.contains_value(thing2, mission:getTargets())
assert.contains_value(thing3, mission:getTargets())
Missions:scan() should create a valid Mission if a callback function is given that returns one ship
local ship = CpuShip()
local mission = Missions:scan(function() return ship end)
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same({ship}, mission:getTargets())
Missions:scan() should create a valid Mission with mixed objects
local thing1 = SpaceStation()
local thing2 = CpuShip()
local thing3 = Asteroid()
local mission = Missions:scan({thing1, thing2, thing3})
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(thing1, mission:getTargets())
assert.contains_value(thing2, mission:getTargets())
assert.contains_value(thing3, mission:getTargets())
Missions:scan() should create a valid Mission with multiple asteroids
local thing1 = Asteroid()
local thing2 = Asteroid()
local thing3 = Asteroid()
local mission = Missions:scan({thing1, thing2, thing3})
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(thing1, mission:getTargets())
assert.contains_value(thing2, mission:getTargets())
assert.contains_value(thing3, mission:getTargets())
Missions:scan() should create a valid Mission with multiple ships
local thing1 = CpuShip()
local thing2 = CpuShip()
local thing3 = CpuShip()
local mission = Missions:scan({thing1, thing2, thing3})
assert.is_true(Mission:isMission(mission))
mission:setPlayer(player)
mission:accept()
mission:start()
assert.contains_value(thing1, mission:getTargets())
assert.contains_value(thing2, mission:getTargets())
assert.contains_value(thing3, mission:getTargets())
Missions:scan() should create a valid Mission with one ship
local mission = Missions:scan(CpuShip())
assert.is_true(Mission:isMission(mission))
Missions:scan():getTargets(),
Missions:scan():getTargets(), countTargets(), getScannedTargets(), countScannedTargets(), getUnscannedTargets(), countUnscannedTargets() return correct values
local target1 = CpuShip()
local target2 = CpuShip()
local target3 = CpuShip()
local mission = Missions:scan({ target1, target2, target3 })
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same(3, mission:countTargets())
assert.is_same(3, mission:countUnscannedTargets())
assert.is_same(0, mission:countScannedTargets())
assert.contains_value(target1, mission:getTargets())
assert.contains_value(target2, mission:getTargets())
assert.contains_value(target3, mission:getTargets())
assert.contains_value(target1, mission:getUnscannedTargets())
assert.contains_value(target2, mission:getUnscannedTargets())
assert.contains_value(target3, mission:getUnscannedTargets())
assert.not_contains_value(target1, mission:getScannedTargets())
assert.not_contains_value(target2, mission:getScannedTargets())
assert.not_contains_value(target3, mission:getScannedTargets())
target1:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(3, mission:countTargets())
assert.is_same(2, mission:countUnscannedTargets())
assert.is_same(1, mission:countScannedTargets())
assert.contains_value(target1, mission:getTargets())
assert.contains_value(target2, mission:getTargets())
assert.contains_value(target3, mission:getTargets())
assert.not_contains_value(target1, mission:getUnscannedTargets())
assert.contains_value(target2, mission:getUnscannedTargets())
assert.contains_value(target3, mission:getUnscannedTargets())
assert.contains_value(target1, mission:getScannedTargets())
assert.not_contains_value(target2, mission:getScannedTargets())
assert.not_contains_value(target3, mission:getScannedTargets())
target2:fullScannedByPlayer()
target3:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(3, mission:countTargets())
assert.is_same(0, mission:countUnscannedTargets())
assert.is_same(3, mission:countScannedTargets())
assert.contains_value(target1, mission:getTargets())
assert.contains_value(target2, mission:getTargets())
assert.contains_value(target3, mission:getTargets())
assert.not_contains_value(target1, mission:getUnscannedTargets())
assert.not_contains_value(target2, mission:getUnscannedTargets())
assert.not_contains_value(target3, mission:getUnscannedTargets())
assert.contains_value(target1, mission:getScannedTargets())
assert.contains_value(target2, mission:getScannedTargets())
assert.contains_value(target3, mission:getScannedTargets())
Missions:scan():getTargets(), countTargets(), getScannedTargets(), countScannedTargets(), getUnscannedTargets(), countUnscannedTargets() return correct values when ships are destroyed
local target1 = CpuShip()
local target2 = CpuShip()
local target3 = CpuShip()
local mission = Missions:scan({ target1, target2, target3 })
mission:setPlayer(player)
mission:accept()
mission:start()
assert.is_same(3, mission:countTargets())
assert.is_same(3, mission:countUnscannedTargets())
assert.is_same(0, mission:countScannedTargets())
assert.contains_value(target1, mission:getTargets())
assert.contains_value(target2, mission:getTargets())
assert.contains_value(target3, mission:getTargets())
assert.contains_value(target1, mission:getUnscannedTargets())
assert.contains_value(target2, mission:getUnscannedTargets())
assert.contains_value(target3, mission:getUnscannedTargets())
assert.not_contains_value(target1, mission:getScannedTargets())
assert.not_contains_value(target2, mission:getScannedTargets())
assert.not_contains_value(target3, mission:getScannedTargets())
target1:destroy()
Cron.tick(1)
assert.is_same(3, mission:countTargets())
assert.is_same(2, mission:countUnscannedTargets())
assert.is_same(0, mission:countScannedTargets())
assert.contains_value(target1, mission:getTargets())
assert.contains_value(target2, mission:getTargets())
assert.contains_value(target3, mission:getTargets())
assert.not_contains_value(target1, mission:getUnscannedTargets())
assert.contains_value(target2, mission:getUnscannedTargets())
assert.contains_value(target3, mission:getUnscannedTargets())
assert.not_contains_value(target1, mission:getScannedTargets())
assert.not_contains_value(target2, mission:getScannedTargets())
assert.not_contains_value(target3, mission:getScannedTargets())
target2:fullScannedByPlayer()
Cron.tick(1)
assert.is_same(3, mission:countTargets())
assert.is_same(1, mission:countUnscannedTargets())
assert.is_same(1, mission:countScannedTargets())
assert.contains_value(target1, mission:getTargets())
assert.contains_value(target2, mission:getTargets())
assert.contains_value(target3, mission:getTargets())
assert.not_contains_value(target1, mission:getUnscannedTargets())
assert.not_contains_value(target2, mission:getUnscannedTargets())
assert.contains_value(target3, mission:getUnscannedTargets())
assert.not_contains_value(target1, mission:getScannedTargets())
assert.contains_value(target2, mission:getScannedTargets())
assert.not_contains_value(target3, mission:getScannedTargets())
target2:destroy()
Cron.tick(1)
assert.is_same(3, mission:countTargets())
assert.is_same(1, mission:countUnscannedTargets())
assert.is_same(1, mission:countScannedTargets())
assert.contains_value(target1, mission:getTargets())
assert.contains_value(target2, mission:getTargets())
assert.contains_value(target3, mission:getTargets())
assert.not_contains_value(target1, mission:getUnscannedTargets())
assert.not_contains_value(target2, mission:getUnscannedTargets())
assert.contains_value(target3, mission:getUnscannedTargets())
assert.not_contains_value(target1, mission:getScannedTargets())
assert.contains_value(target2, mission:getScannedTargets())
assert.not_contains_value(target3, mission:getScannedTargets())
Missions:scan():getTargets(), countTargets(), getScannedTargets(), countScannedTargets(), getUnscannedTargets(), countUnscannedTargets() returns nil it it is called before the ships are created in the callback
local enemy1 = CpuShip()
local enemy2 = CpuShip()
local enemy3 = CpuShip()
local mission = Missions:scan(function () return {enemy1, enemy2, enemy3} end)
assert.is_nil(mission:countTargets())
assert.is_nil(mission:countScannedTargets())
assert.is_nil(mission:countUnscannedTargets())
assert.is_nil(mission:getTargets())
assert.is_nil(mission:getScannedTargets())
assert.is_nil(mission:getUnscannedTargets())
Missions:scan():getTargets(), countTargets(), getScannedTargets(), countScannedTargets(), getUnscannedTargets(), countUnscannedTargets() should not allow to manipulate the tables
local target1 = CpuShip()
local target2 = CpuShip()
local target3 = CpuShip()
local mission = Missions:scan({ target1, target2 })
mission:setPlayer(player)
mission:accept()
mission:start()
local targets = mission:getTargets()
table.insert(targets, target3)
assert.is_same(2, mission:countTargets())
assert.not_contains_value(target3, mission:getTargets())
Missions:transportProduct()
Missions:transportProduct() calls onInsufficientStorage if the player ship has no storage as soon as the player docks
local onInsufficientStorageCalled = 0
local player = PlayerSpaceship()
local mission
mission = Missions:transportProduct(from, to, product, {
amount = 42,
onInsufficientStorage = function(theMission)
assert.is_same(mission, theMission)
onInsufficientStorageCalled = onInsufficientStorageCalled + 1
end,
})
Mission:withBroker(mission, "Dummy")
Player:withStorage(player, {maxStorage=100})
mission:setPlayer(player)
mission:setMissionBroker(from)
mission:accept()
mission:start()
player:modifyProductStorage(product, 60)
player.isDocked = function(self, thing)
return thing == from
end
Cron.tick(1)
assert.is_same(1, onInsufficientStorageCalled)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onInsufficientStorageCalled)
Missions:transportProduct() fails if first parameter is not a station
local from = CpuShip()
assert.has_error(function() Missions:transportProduct(from, to, product) end)
Missions:transportProduct() fails if fourth parameter is a number
assert.has_error(function() Missions:transportProduct(from, to, product, 3) end)
Missions:transportProduct() fails if second parameter is not a station
assert.has_error(function() Missions:transportProduct(from, CpuShip, product) end)
Missions:transportProduct() fails if third parameter is a number
assert.has_error(function() Missions:transportProduct(from, to, 3) end)
Missions:transportProduct() fails to accept if mission is not a broker mission
local mission = Missions:transportProduct(from, to, product)
assert.has_error(function() mission:accept() end)
Missions:transportProduct() fails to accept if the player ship has no storage at all
local acceptConditionCalled = false
local player = PlayerSpaceship()
local mission
mission = Missions:transportProduct(from, to, product, {
acceptCondition = function(theMission, theError)
assert.is_same(mission, theMission)
assert.is_same("no_storage", theError)
acceptConditionCalled = true
return "You have no storage"
end
})
Mission:withBroker(mission, "Dummy")
mission:setPlayer(player)
mission:setMissionBroker(from)
local success, message = mission:canBeAccepted()
assert.is_true(acceptConditionCalled)
assert.is_false(success)
assert.is_same("You have no storage", message)
assert.has_error(function() mission:accept() end)
Missions:transportProduct() fails to accept if the player ship has too little storage even if they removed everything
local acceptConditionCalled = false
local player = PlayerSpaceship()
local mission
mission = Missions:transportProduct(from, to, product, {
amount = 42,
acceptCondition = function(theMission, theError)
assert.is_same(mission, theMission)
assert.is_same("small_storage", theError)
acceptConditionCalled = true
return "You have too little storage"
end,
})
Mission:withBroker(mission, "Dummy")
mission:setPlayer(player)
Player:withStorage(player, {maxStorage=40})
mission:setMissionBroker(from)
local success, message = mission:canBeAccepted()
assert.is_true(acceptConditionCalled)
assert.is_false(success)
assert.is_same("You have too little storage", message)
assert.has_error(function() mission:accept() end)
Missions:transportProduct() fails when product is sold or lost
local onProductLostCalled = false
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage=100})
local mission
mission = Missions:transportProduct(from, to, product, {
amount = 42,
onProductLost = function(theMission)
assert.is_same(mission, theMission)
onProductLostCalled = true
end,
})
Mission:withBroker(mission, "Dummy")
mission:setPlayer(player)
mission:setMissionBroker(from)
mission:accept()
mission:start()
player.isDocked = function()
return false
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(mission:isLoaded())
assert.is_false(onProductLostCalled)
player.isDocked = function(self, thing)
return thing == from
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_true(mission:isLoaded())
assert.is_false(onProductLostCalled)
player:modifyProductStorage(product, -10)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(mission:isLoaded())
assert.is_true(onProductLostCalled)
assert.is_same(32, player:getProductStorage(product))
assert.is_same("failed", mission:getState())
Missions:transportProduct() should create a valid Mission
local mission = Missions:transportProduct(from, to, product)
assert.is_true(Mission:isMission(mission))
Missions:transportProduct() successful mission
local onLoadCalled = false
local onUnloadCalled = false
local player = PlayerSpaceship()
local mission
mission = Missions:transportProduct(from, to, product, {
amount = 42,
onLoad = function(theMission)
assert.is_same(mission, theMission)
onLoadCalled = true
end,
onUnload = function(theMission)
assert.is_same(mission, theMission)
onUnloadCalled = true
end,
})
Mission:withBroker(mission, "Dummy")
mission:setPlayer(player)
Player:withStorage(player, {maxStorage=100})
mission:setMissionBroker(from)
mission:accept()
mission:start()
player.isDocked = function()
return false
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(mission:isLoaded())
assert.is_false(onLoadCalled)
assert.is_false(onUnloadCalled)
assert.is_same(0, player:getProductStorage(product))
player.isDocked = function(self, thing)
return thing == from
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_true(mission:isLoaded())
assert.is_true(onLoadCalled)
assert.is_false(onUnloadCalled)
assert.is_same(42, player:getProductStorage(product))
player.isDocked = function()
return false
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
player.isDocked = function(self, thing)
return thing == to
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(mission:isLoaded())
assert.is_true(onLoadCalled)
assert.is_true(onUnloadCalled)
assert.is_same(0, player:getProductStorage(product))
assert.is_same("successful", mission:getState())
Missions:transportToken()
Missions:transportToken() can run a successful mission
local onLoadCalled = false
local onUnloadCalled = false
local from = SpaceStation()
local to = SpaceStation()
local player = PlayerSpaceship()
local mission = Missions:transportToken(from, to, {
onLoad = function() onLoadCalled = true end,
onUnload = function() onUnloadCalled = true end,
})
Mission:withBroker(mission, "Dummy")
mission:setPlayer(player)
mission:setMissionBroker(from)
mission:accept()
mission:start()
player.isDocked = function()
return false
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(mission:isTokenLoaded())
assert.is_false(onLoadCalled)
assert.is_false(onUnloadCalled)
player.isDocked = function(self, thing)
return thing == from
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_true(mission:isTokenLoaded())
assert.is_true(onLoadCalled)
assert.is_false(onUnloadCalled)
player.isDocked = function(self, thing)
return thing == to
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(mission:isTokenLoaded())
assert.is_true(onLoadCalled)
assert.is_true(onUnloadCalled)
assert.is_same("successful", mission:getState())
Missions:transportToken() fails if first parameter is not a station
local from = CpuShip()
local to = SpaceStation()
assert.has_error(function() Missions:transportToken(from, to) end)
Missions:transportToken() fails if second parameter is not a station
local from = SpaceStation()
local to = CpuShip()
assert.has_error(function() Missions:transportToken(from, to) end)
Missions:transportToken() fails if third parameter is a number
local from = SpaceStation()
local to = SpaceStation()
assert.has_error(function() Missions:transportToken(from, to, 3) end)
Missions:transportToken() fails to start if mission is not a broker mission
local from = SpaceStation()
local to = SpaceStation()
local mission = Missions:transportToken(from, to)
assert.has_error(function() mission:accept() end)
Missions:transportToken() should create a valid Mission
local from = SpaceStation()
local to = SpaceStation()
local mission = Missions:transportToken(from, to)
assert.is_true(Mission:isMission(mission))
Missions:visit()
Missions:visit() can run a successful mission
local onVisitCalled = false
local station = SpaceStation()
local player = PlayerSpaceship()
local mission = Missions:visit(station, {
onVisit = function() onVisitCalled = true end,
})
Mission:withBroker(mission, "Dummy")
mission:setPlayer(player)
mission:setMissionBroker(station)
mission:accept()
mission:start()
player.isDocked = function()
return false
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(onVisitCalled)
player.isDocked = function(self, thing)
return thing == station
end
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_true(onVisitCalled)
assert.is_same("successful", mission:getState())
Missions:visit() fails if first parameter is not a station
local station = CpuShip()
assert.has_error(function() Missions:visit(station) end)
Missions:visit() fails if second parameter is a number
local station = SpaceStation()
assert.has_error(function() Missions:visit(station, 3) end)
Missions:visit() fails to start if mission is not a broker mission
local station = SpaceStation()
local mission = Missions:visit(station)
assert.has_error(function() mission:accept() end)
Missions:visit() should create a valid Mission
local station = SpaceStation()
local mission = Missions:visit(station, to)
assert.is_true(Mission:isMission(mission))
Missions:wayPoints()
Missions:wayPoints() can run a successful mission while adding waypoints dynamically
local onWayPointCalled = 0
local player = PlayerSpaceship()
local mission
mission = Missions:wayPoints(nil, {
minDistance = 1000,
onStart = function(self)
self:addWayPoint(10000, 0)
end,
onWayPoint = function(self, x, y)
onWayPointCalled = onWayPointCalled + 1
if isEeObject(x) then
x, y = x:getPosition()
end
if x < 49999 then
x = x + 10000
if x > 25000 then
-- make sure the current position is used and not the one when it was added
local artifact = Artifact():setPosition(x / 2, y / 2)
self:addWayPoint(artifact)
artifact:setPosition(x, y)
else
self:addWayPoint(x, y)
end
end
end,
})
mission:setPlayer(player)
mission:accept()
mission:start()
player:setPosition(0, 0)
Cron.tick(1)
assert.is_same("started", mission:getState())
assert.is_same(0, mission:countVisitedWayPoints())
assert.is_same(0, onWayPointCalled)
player:setPosition(10000, 0)
Cron.tick(1)
assert.is_same("started", mission:getState())
assert.is_same(1, mission:countVisitedWayPoints())
assert.is_same(1, onWayPointCalled)
player:setPosition(20000, 0)
Cron.tick(1)
assert.is_same("started", mission:getState())
assert.is_same(2, mission:countVisitedWayPoints())
assert.is_same(2, onWayPointCalled)
player:setPosition(30000, 0)
Cron.tick(1)
assert.is_same("started", mission:getState())
assert.is_same(3, mission:countVisitedWayPoints())
assert.is_same(3, onWayPointCalled)
player:setPosition(40000, 0)
Cron.tick(1)
assert.is_same("started", mission:getState())
assert.is_same(4, mission:countVisitedWayPoints())
assert.is_same(4, onWayPointCalled)
player:setPosition(50000, 0)
Cron.tick(1)
assert.is_same("successful", mission:getState())
assert.is_same(5, mission:countVisitedWayPoints())
assert.is_same(5, onWayPointCalled)
Missions:wayPoints() can run a successful mission with a static list of waypoints
local onWayPointCalled = 0
local callbackArg2 = nil
local callbackArg3 = nil
local artifact = Artifact():setPosition(10000, 0)
local player = PlayerSpaceship()
local mission
mission = Missions:wayPoints({
{10000, 10000},
artifact,
{0, 0}
}, {
minDistance = 1000,
onWayPoint = function(callMission, arg2, arg3)
onWayPointCalled = onWayPointCalled + 1
callbackArg2, callbackArg3 = arg2, arg3
assert.is_same(mission, callMission)
end,
})
mission:setPlayer(player)
mission:accept()
mission:start()
player:setPosition(0, 0)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(0, mission:countVisitedWayPoints())
assert.is_same(0, onWayPointCalled)
assert.is_same("started", mission:getState())
-- move artifact to verify the current position of the object is used
artifact:setPosition(20000, 0)
player:setPosition(20000, 0)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
-- it should not count, because the waypoints should be visited sequentially
assert.is_same(0, mission:countVisitedWayPoints())
assert.is_same(0, onWayPointCalled)
assert.is_same("started", mission:getState())
player:setPosition(10000, 10000)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, mission:countVisitedWayPoints())
assert.is_same(1, onWayPointCalled)
assert.is_same(10000, callbackArg2)
assert.is_same(10000, callbackArg3)
assert.is_same("started", mission:getState())
player:setPosition(20000, 900)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
-- it should allow to be minDistance away
assert.is_same(2, mission:countVisitedWayPoints())
assert.is_same(2, onWayPointCalled)
assert.is_same(artifact, callbackArg2)
assert.is_same(nil, callbackArg3)
assert.is_same("started", mission:getState())
player:setPosition(200, -300)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(3, mission:countVisitedWayPoints())
assert.is_same(3, onWayPointCalled)
assert.is_same(0, callbackArg2)
assert.is_same(0, callbackArg3)
assert.is_same("successful", mission:getState())
Missions:wayPoints() fails if first parameter does not contain valid data
assert.has_error(function() Missions:wayPoints({ 42, "foobar"}) end)
assert.has_error(function() Missions:wayPoints({ { 42, 42}, { 0, 0}, { "this", "is", "invalid"}}) end)
Missions:wayPoints() fails if first parameter is a number
assert.has_error(function() Missions:wayPoints(42) end)
Missions:wayPoints() should create a valid Mission
local mission = Missions:wayPoints({ { 0, 0}})
assert.is_true(Mission:isMission(mission))
Missions:wayPoints():addWayPoint()
Missions:wayPoints():addWayPoint() allows to add an EE object
local mission = Missions:wayPoints()
mission:addWayPoint(Artifact():setPosition(2000, -2000))
Missions:wayPoints():addWayPoint() allows to add coordinates
local mission = Missions:wayPoints()
mission:addWayPoint(2000, -2000)
Missions:wayPoints():addWayPoint() fails if first parameter is not a number
local mission = Missions:wayPoints()
assert.has_error(function() mission:addWayPoint(nil, 0) end)
assert.has_error(function() mission:addWayPoint("foobar", 0) end)
Missions:wayPoints():addWayPoint() fails if second parameter is not a number
local mission = Missions:wayPoints()
assert.has_error(function() mission:addWayPoint(0) end)
assert.has_error(function() mission:addWayPoint(0, nil) end)
assert.has_error(function() mission:addWayPoint(0, "foobar") end)
assert.has_error(function() mission:addWayPoint(0, SpaceShip()) end)
Order
Order:attack()
Order:attack() fails if enemy is neutral for fleet
local fleet = Fleet:new({
CpuShip():setFactionId(0),
CpuShip():setFactionId(0),
CpuShip():setFactionId(0),
})
local enemy = SpaceStation():setFactionId(1)
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:attack(enemy, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
fleet:addOrder(order)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("no_enemy", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Order:attack() fails if enemy is neutral for ship
local ship = CpuShip():setFactionId(0)
local enemy = SpaceStation():setFactionId(1)
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:attack(enemy, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("no_enemy", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:attack() fails if enemy turns into neutral for fleet
local fleet = Fleet:new({
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
})
local enemy = SpaceStation():setFactionId(2)
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:attack(enemy, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
fleet:addOrder(order)
Cron.tick(1)
assert.is_same("Attack", fleet:getLeader():getOrder())
enemy:setFactionId(1)
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("no_enemy", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Order:attack() fails if enemy turns into neutral for ship
local ship = CpuShip():setFactionId(1)
local enemy = SpaceStation():setFactionId(2)
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:attack(enemy, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
Cron.tick(1)
assert.is_same("Attack", ship:getOrder())
enemy:setFactionId(1)
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("no_enemy", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:attack() fails if no station or ship is given
assert.has_error(function()
Order:attack(nil)
end)
assert.has_error(function()
Order:attack("foo")
end)
assert.has_error(function()
Order:attack(Asteroid())
end)
Order:defend()
Order:defend() target aborts if target is destroyed (fleet)
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
local station = SpaceStation()
station.areEnemiesInRange = function() return false end
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:defend(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
fleet:addOrder(order)
assert.is_same(0, onAbortCalled)
Cron.tick(1)
station:destroy()
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("destroyed", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Order:defend() target aborts if target is destroyed (ship)
local ship = CpuShip()
local station = SpaceStation()
station.areEnemiesInRange = function() return false end
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:defend(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
assert.is_same(0, onAbortCalled)
Cron.tick(1)
station:destroy()
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("destroyed", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:defend() target aborts if target is enemy of fleet
local fleet = Fleet:new({
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
CpuShip():setFactionId(1)
})
local station = SpaceStation():setFactionId(2)
station.areEnemiesInRange = function() return false end
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:defend(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
fleet:addOrder(order)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("is_enemy", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Order:defend() target aborts if target is enemy of ship
local ship = CpuShip():setFactionId(1)
local station = SpaceStation():setFactionId(2)
station.areEnemiesInRange = function() return false end
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:defend(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("is_enemy", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:defend() target carries out the order for 30 seconds by default (ship)
local ship = CpuShip()
local station = SpaceStation()
station.areEnemiesInRange = function() return false end
Ship:withOrderQueue(ship)
local onCompletionCalled = 0
local order = Order:defend(station, {
minDefendTime = 30,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
ship:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", ship:getOrder())
assert.is_same(station, ship:getOrderTarget())
for _=1,29 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", ship:getOrder())
assert.is_same(station, ship:getOrderTarget())
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() target carries out the order for 60 seconds by default (fleet)
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
local station = SpaceStation()
station.areEnemiesInRange = function() return false end
Fleet:withOrderQueue(fleet)
local onCompletionCalled = 0
local order = Order:defend(station, {
minDefendTime = 30,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
fleet:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", fleet:getLeader():getOrder())
assert.is_same(station, fleet:getLeader():getOrderTarget())
for _=1,29 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", fleet:getLeader():getOrder())
assert.is_same(station, fleet:getLeader():getOrderTarget())
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() target carries out the order until there are no enemies in range for 15 seconds (fleet)
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
local station = SpaceStation()
local rangeCalled = nil
station.areEnemiesInRange = function(_, range)
rangeCalled = range
return true
end
Fleet:withOrderQueue(fleet)
local onCompletionCalled = 0
local order = Order:defend(station, {
range = 20000,
minClearTime = 15,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
fleet:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", fleet:getLeader():getOrder())
assert.is_same(station, fleet:getLeader():getOrderTarget())
assert.is_same(20000, rangeCalled)
for _=1,60 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", fleet:getLeader():getOrder())
assert.is_same(station, fleet:getLeader():getOrderTarget())
-- no enemies for 5 seconds - is too short
station.areEnemiesInRange = function() return false end
for _=1,5 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
-- ...an other enemy appears
station.areEnemiesInRange = function() return true end
Cron.tick(1)
assert.is_same(0, onCompletionCalled)
-- ...and goes away
station.areEnemiesInRange = function() return false end
for _=1,14 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() target carries out the order until there are no enemies in range for 15 seconds (ship)
local ship = CpuShip()
local station = SpaceStation()
local rangeCalled = nil
station.areEnemiesInRange = function(_, range)
rangeCalled = range
return true
end
Ship:withOrderQueue(ship)
local onCompletionCalled = 0
local order = Order:defend(station, {
range = 20000,
minClearTime = 15,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
ship:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", ship:getOrder())
assert.is_same(station, ship:getOrderTarget())
assert.is_same(20000, rangeCalled)
for _=1,60 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Target", ship:getOrder())
assert.is_same(station, ship:getOrderTarget())
-- no enemies for 5 seconds - is too short
station.areEnemiesInRange = function() return false end
for _=1,5 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
-- ...an other enemy appears
station.areEnemiesInRange = function() return true end
Cron.tick(1)
assert.is_same(0, onCompletionCalled)
-- ...and goes away
station.areEnemiesInRange = function() return false end
for _=1,14 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() target fails if minClearTime is not a positive number
assert.has_error(function()
Order:defend(SpaceStation(), {
minClearTime = "foo",
})
end)
assert.has_error(function()
Order:defend(SpaceStation(), {
minClearTime = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend(SpaceStation(), {
minClearTime = -42,
})
end)
Order:defend() target fails if minDefendTime is not a positive number
assert.has_error(function()
Order:defend(SpaceStation(), {
minDefendTime = "foo",
})
end)
assert.has_error(function()
Order:defend(SpaceStation(), {
minDefendTime = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend(SpaceStation(), {
minDefendTime = -42,
})
end)
Order:defend() target fails if parameter is an invalid target
assert.has_error(function()
Order:defend(42)
end)
assert.has_error(function()
Order:defend("foo")
end)
assert.has_error(function()
Order:defend(Asteroid())
end)
Order:defend() target fails if range is not a positive number
assert.has_error(function()
Order:defend(SpaceStation(), {
range = "foo",
})
end)
assert.has_error(function()
Order:defend(SpaceStation(), {
range = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend(SpaceStation(), {
range = -42,
})
end)
Order:defend() with location carries out the order for 30 seconds (fleet)
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
fleet:getLeader().areEnemiesInRange = function() return false end
Fleet:withOrderQueue(fleet)
local onCompletionCalled = 0
local order = Order:defend(5000, 0, {
minDefendTime = 30,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
fleet:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", fleet:getLeader():getOrder())
assert.is_same({5000, 0}, {fleet:getLeader():getOrderTargetLocation()})
for _=1,29 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", fleet:getLeader():getOrder())
assert.is_same({5000, 0}, {fleet:getLeader():getOrderTargetLocation()})
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() with location carries out the order for 30 seconds (ship)
local ship = CpuShip()
_G.getObjectsInRadius = function() return {} end
Ship:withOrderQueue(ship)
local onCompletionCalled = 0
local order = Order:defend(5000, 0, {
minDefendTime = 30,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
ship:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", ship:getOrder())
assert.is_same({5000, 0}, {ship:getOrderTargetLocation()})
for _=1,29 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", ship:getOrder())
assert.is_same({5000, 0}, {ship:getOrderTargetLocation()})
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() with location carries out the order until there are no enemies in range for 15 seconds (fleet)
local fleet = Fleet:new({
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
})
local calledRange = nil
_G.getObjectsInRadius = function(_, _, range)
calledRange = range
return {CpuShip():setFactionId(2)}
end
Fleet:withOrderQueue(fleet)
local onCompletionCalled = 0
local order = Order:defend(5000, 0, {
range = 20000,
minClearTime = 15,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
fleet:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", fleet:getLeader():getOrder())
assert.is_same({5000, 0}, {fleet:getLeader():getOrderTargetLocation()})
assert.is_same(20000, calledRange)
for _=1,60 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", fleet:getLeader():getOrder())
assert.is_same({5000, 0}, {fleet:getLeader():getOrderTargetLocation()})
-- no enemies for 5 seconds - is too short
_G.getObjectsInRadius = function() return {} end
for _=1,5 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
-- ...an other enemy appears
_G.getObjectsInRadius = function() return {CpuShip():setFactionId(2)} end
Cron.tick(1)
assert.is_same(0, onCompletionCalled)
-- ...and goes away
_G.getObjectsInRadius = function() return {} end
for _=1,14 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() with location carries out the order until there are no enemies in range for 15 seconds (ship)
local ship = CpuShip():setFactionId(1)
local calledRange = nil
_G.getObjectsInRadius = function(_, _, range)
calledRange = range
return {CpuShip():setFactionId(2)}
end
Ship:withOrderQueue(ship)
local onCompletionCalled = 0
local order = Order:defend(5000, 0, {
range = 20000,
minClearTime = 15,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
ship:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", ship:getOrder())
assert.is_same({5000, 0}, {ship:getOrderTargetLocation()})
assert.is_same(20000, calledRange)
for _=1,60 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Defend Location", ship:getOrder())
assert.is_same({5000, 0}, {ship:getOrderTargetLocation()})
-- no enemies for 5 seconds - is too short
_G.getObjectsInRadius = function() return {} end
for _=1,5 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
-- ...an other enemy appears
_G.getObjectsInRadius = function() return {CpuShip():setFactionId(2)} end
Cron.tick(1)
assert.is_same(0, onCompletionCalled)
-- ...and goes away
_G.getObjectsInRadius = function() return {} end
for _=1,14 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() with location fails if any coordinate is not a number
assert.has_error(function()
Order:defend(nil, 42)
end)
assert.has_error(function()
Order:defend(5000, nil)
end)
assert.has_error(function()
Order:defend("foo", 42)
end)
assert.has_error(function()
Order:defend(5000, "foo")
end)
assert.has_error(function()
Order:defend(SpaceStation(), 42)
end)
assert.has_error(function()
Order:defend(5000, SpaceStation())
end)
Order:defend() with location fails if minClearTime is not a positive number
assert.has_error(function()
Order:defend(0, 0, {
minClearTime = "foo",
})
end)
assert.has_error(function()
Order:defend(0, 0, {
minClearTime = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend(0, 0, {
minClearTime = -42,
})
end)
Order:defend() with location fails if minDefendTime is not a positive number
assert.has_error(function()
Order:defend(0, 0, {
minDefendTime = "foo",
})
end)
assert.has_error(function()
Order:defend(0, 0, {
minDefendTime = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend(0, 0, {
minDefendTime = -42,
})
end)
Order:defend() with location fails if range is not a positive number
assert.has_error(function()
Order:defend(0, 0, {
range = "foo",
})
end)
assert.has_error(function()
Order:defend(0, 0, {
range = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend(0, 0, {
range = -42,
})
end)
Order:defend() without argument carries out the order for 30 seconds by default (fleet)
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
fleet:getLeader().areEnemiesInRange = function() return false end
Fleet:withOrderQueue(fleet)
local onCompletionCalled = 0
local order = Order:defend({
minDefendTime = 30,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
fleet:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", fleet:getLeader():getOrder())
for _=1,29 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", fleet:getLeader():getOrder())
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() without argument carries out the order for 30 seconds by default (ship)
local ship = CpuShip()
ship.areEnemiesInRange = function() return false end
Ship:withOrderQueue(ship)
local onCompletionCalled = 0
local order = Order:defend({
minDefendTime = 30,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
ship:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", ship:getOrder())
for _=1,29 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", ship:getOrder())
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() without argument carries out the order until there are no enemies in range for 15 seconds (fleet)
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
local calledRange = nil
fleet:getLeader().areEnemiesInRange = function(_, range)
calledRange = range
return true
end
Fleet:withOrderQueue(fleet)
local onCompletionCalled = 0
local order = Order:defend({
range = 20000,
minClearTime = 15,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
fleet:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", fleet:getLeader():getOrder())
assert.is_same(20000, calledRange)
for _=1,60 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", fleet:getLeader():getOrder())
-- no enemies for 5 seconds - is too short
fleet:getLeader().areEnemiesInRange = function() return false end
for _=1,5 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
-- ...an other enemy appears
fleet:getLeader().areEnemiesInRange = function() return true end
Cron.tick(1)
assert.is_same(0, onCompletionCalled)
-- ...and goes away
fleet:getLeader().areEnemiesInRange = function() return false end
for _=1,14 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() without argument carries out the order until there are no enemies in range for 15 seconds (ship)
local ship = CpuShip()
local calledRange = nil
ship.areEnemiesInRange = function(_, range)
calledRange = range
return true
end
Ship:withOrderQueue(ship)
local onCompletionCalled = 0
local order = Order:defend({
range = 20000,
minClearTime = 15,
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
})
ship:addOrder(order)
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", ship:getOrder())
assert.is_same(20000, calledRange)
for _=1,60 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
assert.is_same("Stand Ground", ship:getOrder())
-- no enemies for 5 seconds - is too short
ship.areEnemiesInRange = function() return false end
for _=1,5 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
-- ...an other enemy appears
ship.areEnemiesInRange = function() return true end
Cron.tick(1)
assert.is_same(0, onCompletionCalled)
-- ...and goes away
ship.areEnemiesInRange = function() return false end
for _=1,14 do Cron.tick(1) end
assert.is_same(0, onCompletionCalled)
Cron.tick(1)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
Order:defend() without argument fails if minClearTime is not a positive number
assert.has_error(function()
Order:defend({
minClearTime = "foo",
})
end)
assert.has_error(function()
Order:defend({
minClearTime = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend({
minClearTime = -42,
})
end)
Order:defend() without argument fails if minDefendTime is not a positive number
assert.has_error(function()
Order:defend({
minDefendTime = "foo",
})
end)
assert.has_error(function()
Order:defend({
minDefendTime = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend({
minDefendTime = -42,
})
end)
Order:defend() without argument fails if range is not a positive number
assert.has_error(function()
Order:defend({
range = "foo",
})
end)
assert.has_error(function()
Order:defend({
range = SpaceStation(),
})
end)
assert.has_error(function()
Order:defend({
range = -42,
})
end)
Order:dock()
Order:dock() does not wait for missiles, when waitForMissileRestock is false
local ship = CpuShip():setWeaponStorageMax("homing", 8):setWeaponStorage("homing", 0)
local station = SpaceStation()
Ship:withOrderQueue(ship)
local completed = false
ship:addOrder(Order:dock(station, {
waitForMissileRestock = false,
onCompletion = function() completed = true end,
}))
-- ship not docked
Cron.tick(1)
assert.is_false(completed)
-- ship docked but no missiles
ship:setDockedAt(station)
Cron.tick(1)
assert.is_true(completed)
Order:dock() does not wait for repairs, when waitForRepair is false
local ship = CpuShip():setHullMax(100):setHull(50)
local station = SpaceStation()
station:setRepairDocked(true)
Ship:withOrderQueue(ship)
local completed = false
ship:addOrder(Order:dock(station, {
waitForRepair = false,
onCompletion = function() completed = true end,
}))
-- ship not docked
Cron.tick(1)
assert.is_false(completed)
-- ship docked
ship:setDockedAt(station)
Cron.tick(1)
assert.is_true(completed)
Order:dock() does not wait for shields to recharge, when waitForShieldRecharge is false
local ship = CpuShip():setShieldsMax(100, 50, 10):setShields(20, 50, 0)
local station = SpaceStation()
Ship:withOrderQueue(ship)
local completed = false
ship:addOrder(Order:dock(station, {
waitForShieldRecharge = false,
onCompletion = function() completed = true end,
}))
-- ship not docked
Cron.tick(1)
assert.is_false(completed)
-- ship docked but low shields
ship:setDockedAt(station)
Cron.tick(1)
assert.is_true(completed)
Order:dock() fails if no station is given
assert.has_error(function()
Order:dock(nil)
end)
assert.has_error(function()
Order:dock("foo")
end)
assert.has_error(function()
Order:dock(CpuShip())
end)
Order:dock() fails if station is an enemy for fleet
local fleet = Fleet:new({
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
})
local station = SpaceStation():setFactionId(2)
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:dock(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
fleet:addOrder(order)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("enemy_station", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Order:dock() fails if station is an enemy for ship
local ship = CpuShip():setFactionId(1)
local station = SpaceStation():setFactionId(2)
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:dock(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("enemy_station", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:dock() fails if station is destroyed for fleet
local fleet = Fleet:new({CpuShip(), CpuShip(), CpuShip()})
local station = SpaceStation()
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:dock(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
fleet:addOrder(order)
Cron.tick(1)
assert.is_same("Dock", fleet:getLeader():getOrder())
station:destroy()
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("invalid_station", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Order:dock() fails if station is destroyed for ship
local ship = CpuShip()
local station = SpaceStation()
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:dock(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
Cron.tick(1)
assert.is_same("Dock", ship:getOrder())
station:destroy()
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("invalid_station", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:dock() fails if station turns into an enemy for fleet
local fleet = Fleet:new({
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
CpuShip():setFactionId(1),
})
local station = SpaceStation():setFactionId(0)
Fleet:withOrderQueue(fleet)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:dock(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
fleet:addOrder(order)
Cron.tick(1)
assert.is_same("Dock", fleet:getLeader():getOrder())
station:setFactionId(2)
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("enemy_station", abortArg2)
assert.is_same(fleet, abortArg3)
assert.is_same("Idle", fleet:getLeader():getOrder())
Order:dock() fails if station turns into an enemy for ship
local ship = CpuShip():setFactionId(1)
local station = SpaceStation():setFactionId(0)
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:dock(station, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
Cron.tick(1)
assert.is_same("Dock", ship:getOrder())
station:setFactionId(2)
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("enemy_station", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:dock() repairs a docked fleet if the station is friendly and supports it
local ship1 = CpuShip():setHullMax(100):setHull(50)
local ship2 = CpuShip():setHullMax(100):setHull(50)
local ship3 = CpuShip():setHullMax(100):setHull(50)
local fleet = Fleet:new({ship1, ship2, ship3})
local station = SpaceStation()
station:setRepairDocked(true)
Fleet:withOrderQueue(fleet)
local completed = false
fleet:addOrder(Order:dock(station, {
onCompletion = function() completed = true end,
}))
fleet:addOrder(Order:flyTo(1000, 0))
-- fleet leader not docked
Cron.tick(1)
assert.is_false(completed)
assert.is_same("Dock", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
-- fleet leader docked, fleet leader unrepaired
ship1:setDockedAt(station)
Cron.tick(1)
assert.is_false(completed)
assert.is_same("Dock", ship1:getOrder())
assert.is_same("Dock", ship2:getOrder())
assert.is_same(station, ship2:getOrderTarget())
assert.is_same("Dock", ship3:getOrder())
assert.is_same(station, ship3:getOrderTarget())
-- fleet leader docked, fleet leader repaired, wingmen not docked
ship1:setHull(100)
Cron.tick(1)
assert.is_false(completed)
assert.is_same("Dock", ship1:getOrder())
assert.is_same("Dock", ship2:getOrder())
assert.is_same("Dock", ship3:getOrder())
-- fleet leader repaired, wingmen docked
ship2:setDockedAt(station)
ship3:setDockedAt(station)
Cron.tick(1)
assert.is_false(completed)
-- fleet leader repaired, wingmen repaired
ship2:setHull(100)
ship3:setHull(100)
Cron.tick(1)
assert.is_true(completed)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Fly towards", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
Order:dock() repairs a docked ship if the station is friendly and supports it
local ship = CpuShip():setHullMax(100):setHull(50)
local station = SpaceStation()
station:setRepairDocked(true)
Ship:withOrderQueue(ship)
local completed = false
ship:addOrder(Order:dock(station, {
onCompletion = function() completed = true end,
}))
-- ship not docked
Cron.tick(1)
assert.is_false(completed)
-- ship docked but unrepaired
ship:setDockedAt(station)
Cron.tick(1)
assert.is_false(completed)
-- ship docked and repaired
ship:setHull(100)
Cron.tick(1)
assert.is_true(completed)
Order:dock() undocks all fleet ships if the order is aborted
local ship1 = CpuShip():setHullMax(100):setHull(50)
local ship2 = CpuShip():setHullMax(100):setHull(50)
local ship3 = CpuShip():setHullMax(100):setHull(50)
local fleet = Fleet:new({ship1, ship2, ship3})
local station = SpaceStation()
station:setRepairDocked(true)
Fleet:withOrderQueue(fleet)
fleet:addOrder(Order:dock(station))
-- fleet leader docked, fleet leader unrepaired
ship1:setDockedAt(station)
Cron.tick(1)
assert.is_same("Dock", ship1:getOrder())
assert.is_same("Dock", ship2:getOrder())
assert.is_same("Dock", ship3:getOrder())
fleet:abortCurrentOrder()
Cron.tick(1)
assert.is_same("Idle", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
Order:dock() waits for all ships to be repaired, refilled and recharged
local ship1 = CpuShip()
local ship2 = CpuShip():setHullMax(100):setHull(50) -- damaged
local ship3 = CpuShip():setWeaponStorageMax("homing", 8):setWeaponStorage("homing", 0) -- weapons
local ship4 = CpuShip():setShieldsMax(100, 50, 10):setShields(20, 50, 0) -- broken shields
local fleet = Fleet:new({ship1, ship2, ship3, ship4})
local station = SpaceStation()
station:setRepairDocked(true)
Fleet:withOrderQueue(fleet)
local completed = false
fleet:addOrder(Order:dock(station, {
onCompletion = function() completed = true end,
}))
fleet:addOrder(Order:flyTo(1000, 0))
ship1:setDockedAt(station)
ship2:setDockedAt(station)
ship3:setDockedAt(station)
ship4:setDockedAt(station)
assert.is_false(completed)
-- repair ship
ship2:setHull(100)
Cron.tick(1)
Cron.tick(1) --twice for Fleet's cron to catch up
assert.is_false(completed)
assert.is_same("Fly in formation", ship2:getOrder())
-- refill missiles
ship3:setWeaponStorage("homing", 8)
Cron.tick(1)
Cron.tick(1) --twice for Fleet's cron to catch up
assert.is_false(completed)
assert.is_same("Fly in formation", ship3:getOrder())
-- recharge shields
ship4:setShields(100, 50, 10)
Cron.tick(1)
Cron.tick(1) --twice for Fleet's cron to catch up
assert.is_true(completed)
assert.is_same("Fly in formation", ship4:getOrder())
Order:dock() waits for missiles to be refilled
local ship = CpuShip():setWeaponStorageMax("homing", 8):setWeaponStorage("homing", 0)
local station = SpaceStation()
Ship:withOrderQueue(ship)
local completed = false
ship:addOrder(Order:dock(station, {
onCompletion = function() completed = true end,
}))
-- ship not docked
Cron.tick(1)
assert.is_false(completed)
-- ship docked but no missiles
ship:setDockedAt(station)
Cron.tick(1)
assert.is_false(completed)
-- ship docked and refilled
ship:setWeaponStorage("homing", 8)
Cron.tick(1)
assert.is_true(completed)
Order:dock() waits for shields to recharge
local ship = CpuShip():setShieldsMax(100, 50, 10):setShields(20, 50, 0)
local station = SpaceStation()
Ship:withOrderQueue(ship)
local completed = false
ship:addOrder(Order:dock(station, {
onCompletion = function() completed = true end,
}))
-- ship not docked
Cron.tick(1)
assert.is_false(completed)
-- ship docked but low shields
ship:setDockedAt(station)
Cron.tick(1)
assert.is_false(completed)
-- ship docked and shields loaded
ship:setShields(100, 50, 10)
Cron.tick(1)
assert.is_true(completed)
Order:flyTo()
Order:flyTo() config.ignoreEnemies allows to set the distance to trigger for fleet
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(1000, 0)
ship:addOrder(Order:flyTo(0, 0, {
ignoreEnemies = true,
}))
assert.is_same("Fly towards (ignore all)", ship:getOrder())
Order:flyTo() config.ignoreEnemies allows to set the distance to trigger for ship
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
Fleet:withOrderQueue(fleet)
fleet:getLeader():setPosition(1000, 0)
fleet:addOrder(Order:flyTo(0, 0, {
ignoreEnemies = true,
}))
assert.is_same("Fly towards (ignore all)", fleet:getLeader():getOrder())
Order:flyTo() config.ignoreEnemies fails if it is not a boolean
local ship = CpuShip()
Ship:withOrderQueue(ship)
assert.has_error(function()
Order:flyTo(0, 0, {
ignoreEnemies = "foobar",
})
end)
assert.has_error(function()
Order:flyTo(0, 0, {
ignoreEnemies = 42,
})
end)
Order:flyTo() config.minDistance allows to set the distance to trigger for fleet leader
local fleet = Fleet:new({
CpuShip(),
CpuShip(),
CpuShip(),
})
Fleet:withOrderQueue(fleet)
fleet:getLeader():setPosition(1000, 0)
local completed = false
fleet:addOrder(Order:flyTo(0, 0, {
minDistance = 100,
onCompletion = function() completed = true end,
}))
assert.is_false(completed)
fleet:getLeader():setPosition(500, 0)
Cron.tick(1)
assert.is_false(completed)
fleet:getLeader():setPosition(101, 0)
Cron.tick(1)
assert.is_false(completed)
fleet:getLeader():setPosition(99, 0)
Cron.tick(1)
assert.is_true(completed)
Order:flyTo() config.minDistance allows to set the distance to trigger for ship
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(1000, 0)
local completed = false
ship:addOrder(Order:flyTo(0, 0, {
minDistance = 100,
onCompletion = function() completed = true end,
}))
assert.is_false(completed)
ship:setPosition(500, 0)
Cron.tick(1)
assert.is_false(completed)
ship:setPosition(101, 0)
Cron.tick(1)
assert.is_false(completed)
ship:setPosition(99, 0)
Cron.tick(1)
assert.is_true(completed)
Order:flyTo() config.minDistance fails if it is not a number or negative
local ship = CpuShip()
Ship:withOrderQueue(ship)
assert.has_error(function()
Order:flyTo(0, 0, {
minDistance = "foobar",
})
end)
assert.has_error(function()
Order:flyTo(0, 0, {
minDistance = -42,
})
end)
Order:flyTo() fails if x or y are not numbers
assert.has_error(function()
Order:flyTo(nil, 0)
end)
assert.has_error(function()
Order:flyTo("foo", 0)
end)
assert.has_error(function()
Order:flyTo({}, 0)
end)
assert.has_error(function()
Order:flyTo(0, nil)
end)
assert.has_error(function()
Order:flyTo(0, "foo")
end)
assert.has_error(function()
Order:flyTo(0, {})
end)
Order:use()
Order:use() fails if wormHole is not a WormHole
assert.has_error(function()
Order:use(nil)
end)
assert.has_error(function()
Order:use("foo")
end)
assert.has_error(function()
Order:use({})
end)
assert.has_error(function()
Order:use(0)
end)
Order:use() fails if wormHole is not valid
assert.has_error(function()
local ship = CpuShip()
local wormhole = WormHole():destroy()
ship:addOrder(Order:use(wormhole))
end)
Order:use() fails if wormhole disappears
local ship = CpuShip()
local wormhole = WormHole():setPosition(0,0):setTargetPosition(10000, 0)
Ship:withOrderQueue(ship)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:use(wormhole, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end,
})
ship:addOrder(order)
Cron.tick(1)
wormhole:destroy()
Cron.tick(1)
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("invalid_target", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Order:use() fleet breaks up in front of the wormhole and calls the config.onBreakUp callback
local leader = CpuShip()
local ship1 = CpuShip()
local ship2 = CpuShip()
local wormhole = WormHole():setPosition(5000,0):setTargetPosition(99999, 0)
local fleet = Fleet:new({leader, ship1, ship2})
Fleet:withOrderQueue(fleet)
local onBreakUpCalled, breakUpArg1, breakUpArg2 = 0, nil, nil
local order = Order:use(wormhole, {
onBreakUp = function(arg1, arg2)
onBreakUpCalled = onBreakUpCalled + 1
breakUpArg1 = arg1
breakUpArg2 = arg2
end,
})
fleet:addOrder(order)
Cron.tick(1)
assert.is_same(0, onBreakUpCalled)
assert.is_same("Fly towards", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
leader:setPosition(3000, 0)
Cron.tick(1)
assert.is_same("Fly towards (ignore all)", leader:getOrder())
assert.is_same(5000, leader:getOrderTargetLocationX())
assert.is_same(0, leader:getOrderTargetLocationY())
assert.is_same("Fly towards (ignore all)", ship1:getOrder())
assert.is_same(5000, ship1:getOrderTargetLocationX())
assert.is_same(0, ship1:getOrderTargetLocationY())
assert.is_same("Fly towards (ignore all)", ship2:getOrder())
assert.is_same(5000, ship2:getOrderTargetLocationX())
assert.is_same(0, ship2:getOrderTargetLocationY())
assert.is_same(1, onBreakUpCalled)
assert.is_same(order, breakUpArg1)
assert.is_same(fleet, breakUpArg2)
Cron.tick(1)
assert.is_same(1, onBreakUpCalled) -- it is only called once
Order:use() fleet leader waits after jump and regroups
local leader = CpuShip():setCallSign("leader")
local ship1 = CpuShip():setCallSign("ship 1")
local ship2 = CpuShip():setCallSign("ship 2")
local wormhole = WormHole():setPosition(5000,0):setTargetPosition(99999, 0)
local fleet = Fleet:new({leader, ship1, ship2})
Fleet:withOrderQueue(fleet)
local order = Order:use(wormhole)
fleet:addOrder(order)
Cron.tick(1)
assert.is_same("Fly towards", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
leader:setPosition(3000, 0)
Cron.tick(1)
assert.is_same("Fly towards (ignore all)", leader:getOrder())
assert.is_same("Fly towards (ignore all)", ship1:getOrder())
assert.is_same("Fly towards (ignore all)", ship2:getOrder())
-- leader jumps first
leader:setPosition(99999, 0)
Cron.tick(1)
assert.is_same("Stand Ground", leader:getOrder())
assert.is_same("Fly towards (ignore all)", ship1:getOrder())
assert.is_same("Fly towards (ignore all)", ship2:getOrder())
-- wingman jumps second
ship1:setPosition(99999, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Stand Ground", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly towards (ignore all)", ship2:getOrder())
-- second wingman jumps
ship2:setPosition(99999, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Stand Ground", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
Order:use() fleet waits after jump and regroups
local leader = CpuShip():setCallSign("leader")
local ship1 = CpuShip():setCallSign("ship 1")
local ship2 = CpuShip():setCallSign("ship 2")
local ship3 = CpuShip():setCallSign("ship 3")
local wormhole = WormHole():setPosition(5000,0):setTargetPosition(99999, 0)
local fleet = Fleet:new({leader, ship1, ship2, ship3})
Fleet:withOrderQueue(fleet)
local order = Order:use(wormhole)
fleet:addOrder(order)
Cron.tick(1)
assert.is_same("Fly towards", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
leader:setPosition(3000, 0)
Cron.tick(1)
assert.is_same("Fly towards (ignore all)", leader:getOrder())
assert.is_same("Fly towards (ignore all)", ship1:getOrder())
assert.is_same("Fly towards (ignore all)", ship2:getOrder())
assert.is_same("Fly towards (ignore all)", ship3:getOrder())
-- wingman jumps first
ship1:setPosition(99999, 0)
Cron.tick(1)
assert.is_same("Fly towards (ignore all)", leader:getOrder())
assert.is_same("Stand Ground", ship1:getOrder())
assert.is_same("Fly towards (ignore all)", ship2:getOrder())
assert.is_same("Fly towards (ignore all)", ship3:getOrder())
-- leader jumps second
leader:setPosition(99999, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Stand Ground", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly towards (ignore all)", ship2:getOrder())
assert.is_same("Fly towards (ignore all)", ship3:getOrder())
-- second wingman jumps
ship2:setPosition(99999, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Stand Ground", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly towards (ignore all)", ship3:getOrder())
ship3:setPosition(99999, 0)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Stand Ground", leader:getOrder())
assert.is_same("Fly in formation", ship1:getOrder())
assert.is_same("Fly in formation", ship2:getOrder())
assert.is_same("Fly in formation", ship3:getOrder())
Person
Person:byName()
Person:byName() should create a valid Person object by name
local person = Person:byName("John Doe")
assert.is_same("John Doe", person.getFormalName())
assert.is_same("John Doe", person.getNickName())
assert.is_true(Person:isPerson(person))
Person:byName() should create a valid Person object by name with nickname
local person = Person:byName("John Doe", "John")
assert.is_same("John Doe", person.getFormalName())
assert.is_same("John", person.getNickName())
assert.is_true(Person:isPerson(person))
Player
Player:withMenu()
Player:withMenu():addHelmsMenuItem(),
Player:withMenu():addMenuItem()
Player:withMenu():addMenuItem(),
Player:withMenu():drawMenu()
Player:withMenu():removeMenuItem()
Player:withMissionDisplay()
Player:withMissionDisplay() creates a valid mission display
local player = PlayerSpaceship()
Player:withMenu(player)
Player:withMissionTracker(player)
Player:withMissionDisplay(player, defaultConfig)
assert.is_true(Player:hasMissionDisplay(player))
assert.is_true(player:hasButton("relay", "Missions"))
player:clickButton("relay", "Missions")
assert.is_true(player:hasCustomMessage("relay"))
Player:withMissionDisplay() fails if the first argument is a player without storage
assert.has_error(function() Player:withMissionDisplay(PlayerSpaceship(), defaultConfig) end)
Player:withMissionDisplay() fails if the first argument is already a mission display player
local player = PlayerSpaceship()
Player:withMenu(player)
Player:withMissionTracker(player)
Player:withMissionDisplay(player, defaultConfig)
assert.has_error(function() Player:withMissionDisplay(player, defaultConfig) end)
Player:withMissionDisplay() fails if the first argument is not a player
assert.has_error(function() Player:withMissionDisplay(42, defaultConfig) end)
Player:withMissionTracker()
Player:withMissionTracker() creates a valid mission tracker
local player = PlayerSpaceship()
Player:withMissionTracker(player)
assert.is_true(Player:hasMissionTracker(player))
Player:withMissionTracker() fails if the first argument is already a mission tracker player
local player = PlayerSpaceship()
Player:withMissionTracker(player)
assert.has_error(function() Player:withMissionTracker(player) end)
Player:withMissionTracker() fails if the first argument is not a player
assert.has_error(function() Player:withMissionTracker(42) end)
Player:withMissionTracker():addMission()
Player:withMissionTracker():addMission() adds a mission
local player = PlayerSpaceship()
Player:withMissionTracker(player)
local mission = startedMissionWithBrokerMock()
player:addMission(mission)
assert.is_same(1, Util.size(player:getStartedMissions()))
Player:withMissionTracker():addMission() fails if the first parameter is not a mission
local player = PlayerSpaceship()
Player:withMissionTracker(player)
assert.has_error(function() player:addMission(42) end)
Player:withMissionTracker():getStartedMissions()
Player:withMissionTracker():getStartedMissions() does not return missions that do not have the state started
local player = PlayerSpaceship()
Player:withMissionTracker(player)
player:addMission(missionWithBrokerMock())
player:addMission(acceptedMissionWithBrokerMock())
player:addMission(declinedMissionWithBrokerMock())
player:addMission(failedMissionWithBrokerMock())
player:addMission(successfulMissionWithBrokerMock())
assert.is_same(0, Util.size(player:getStartedMissions()))
Player:withMissionTracker():getStartedMissions() manipulating the result set does not add missions
local player = PlayerSpaceship()
Player:withMissionTracker(player)
player:addMission(startedMissionWithBrokerMock())
local missions = player:getStartedMissions()
table.insert(missions, startedMissionWithBrokerMock())
assert.is_same(1, Util.size(player:getStartedMissions()))
Player:withMissionTracker():getStartedMissions() returns all started missions
local player = PlayerSpaceship()
Player:withMissionTracker(player)
player:addMission(startedMissionWithBrokerMock())
player:addMission(startedMissionWithBrokerMock())
player:addMission(startedMissionWithBrokerMock())
assert.is_same(3, Util.size(player:getStartedMissions()))
Player:withPowerPresets()
Player:withPowerPresets() works with default parameters
local player = PlayerSpaceship()
Player:withMenu(player, {backLabel = "Back"})
Player:withPowerPresets(player, Util.mergeTables(defaultConfig, { slots = 4 }))
assert.is_true(Player:hasPowerPresets(player))
assert.is_true(player:hasButton("engineering", "Presets"))
player:clickButton("engineering", "Presets")
assert.is_true(player:hasButton("engineering", "Load"))
assert.is_true(player:hasButton("engineering", "Store"))
player:clickButton("engineering", "Store")
assert.is_true(player:hasButton("engineering", "Store 1"))
assert.is_true(player:hasButton("engineering", "Store 2"))
assert.is_true(player:hasButton("engineering", "Store 3"))
assert.is_true(player:hasButton("engineering", "Store 4"))
assert.is_false(player:hasButton("engineering", "Store 5"))
player:setSystemPower("impulse", 0.5)
player:setSystemCoolant("impulse", 0.42)
player:clickButton("engineering", "Store 1")
player:clickButton("engineering", "Back")
player:setSystemPower("impulse", 1)
player:setSystemCoolant("impulse", 0)
player:clickButton("engineering", "Presets")
player:clickButton("engineering", "Load")
assert.is_true(player:hasButton("engineering", "Load 1"))
assert.is_true(player:hasButton("engineering", "Load 2"))
assert.is_true(player:hasButton("engineering", "Load 3"))
assert.is_true(player:hasButton("engineering", "Load 4"))
assert.is_false(player:hasButton("engineering", "Load 5"))
player:clickButton("engineering", "Load 1")
assert.is_same(0.5, player:getSystemPower("impulse"))
assert.is_same(0.42, player:getSystemCoolant("impulse"))
Player:withQuickDial()
Player:withQuickDial() ignores invalid stations
local player = PlayerSpaceship()
local station = SpaceStation():setCallSign("Outpost 42")
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
player:addQuickDial(station)
station:destroy()
assert.is_true(player:hasButton("relay", "Quick Dial"))
player:clickButton("relay", "Quick Dial")
assert.is_false(player:hasButton("relay", "Outpost 42"))
Player:withQuickDial() works with default parameters
local player = PlayerSpaceship()
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
assert.is_true(player:hasButton("relay", "Quick Dial"))
player:clickButton("relay", "Quick Dial")
player:clickButton("relay", "Back")
Player:withQuickDial():addQuickDial()
Player:withQuickDial():addQuickDial() allows to add fleets
local player = PlayerSpaceship()
local fleet = Fleet:new({
CpuShip():setCallSign("Fleet Leader"),
CpuShip():setCallSign("Wingman")
})
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
player:addQuickDial(fleet)
assert.is_true(player:hasButton("relay", "Quick Dial"))
player:clickButton("relay", "Quick Dial")
assert.is_true(player:hasButton("relay", "Fleet Leader"))
Player:withQuickDial():addQuickDial() allows to add ships
local player = PlayerSpaceship()
local ship = CpuShip():setCallSign("Nostromo")
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
player:addQuickDial(ship)
assert.is_true(player:hasButton("relay", "Quick Dial"))
player:clickButton("relay", "Quick Dial")
assert.is_true(player:hasButton("relay", "Nostromo"))
Player:withQuickDial():addQuickDial() allows to add stations
local player = PlayerSpaceship()
local station = SpaceStation():setCallSign("Outpost 42")
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
player:addQuickDial(station)
assert.is_true(player:hasButton("relay", "Quick Dial"))
player:clickButton("relay", "Quick Dial")
assert.is_true(player:hasButton("relay", "Outpost 42"))
Player:withQuickDial():addQuickDial() fails when adding an invalid thing
local player = PlayerSpaceship()
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
assert.has_error(function()
player:addQuickDial()
end)
assert.has_error(function()
player:addQuickDial(42)
end)
assert.has_error(function()
player:addQuickDial(Asteroid())
end)
Player:withQuickDial():getQuickDials()
Player:withQuickDial():getQuickDials() does not return invalid entities
local player = PlayerSpaceship()
local station = SpaceStation():setCallSign("Outpost 42")
local fleet = Fleet:new({
CpuShip():setCallSign("Fleet Leader"),
CpuShip():setCallSign("Wingman")
})
local ship = CpuShip():setCallSign("Nostromo")
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
player:addQuickDial(station)
player:addQuickDial(fleet)
player:addQuickDial(ship)
assert.contains_value(station, player:getQuickDials())
assert.contains_value(fleet, player:getQuickDials())
assert.contains_value(ship, player:getQuickDials())
ship:destroy()
assert.contains_value(station, player:getQuickDials())
assert.contains_value(fleet, player:getQuickDials())
assert.not_contains_value(ship, player:getQuickDials())
station:destroy()
assert.not_contains_value(station, player:getQuickDials())
assert.contains_value(fleet, player:getQuickDials())
assert.not_contains_value(ship, player:getQuickDials())
Player:withQuickDial():getQuickDials() returns a table with all added quick dials
local player = PlayerSpaceship()
local station = SpaceStation():setCallSign("Outpost 42")
local fleet = Fleet:new({
CpuShip():setCallSign("Fleet Leader"),
CpuShip():setCallSign("Wingman")
})
local ship = CpuShip():setCallSign("Nostromo")
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
assert.not_contains_value(station, player:getQuickDials())
assert.not_contains_value(fleet, player:getQuickDials())
assert.not_contains_value(ship, player:getQuickDials())
player:addQuickDial(station)
assert.contains_value(station, player:getQuickDials())
player:addQuickDial(fleet)
player:addQuickDial(ship)
assert.contains_value(fleet, player:getQuickDials())
assert.contains_value(ship, player:getQuickDials())
player:removeQuickDial(station)
assert.not_contains_value(station, player:getQuickDials())
assert.contains_value(fleet, player:getQuickDials())
assert.contains_value(ship, player:getQuickDials())
Player:withQuickDial():removeQuickDial()
Player:withQuickDial():removeQuickDial() allows to remove a quick dial
local player = PlayerSpaceship()
local station = SpaceStation():setCallSign("Outpost 42")
Player:withMenu(player, {backLabel = "Back"})
Player:withQuickDial(player, defaultConfig)
assert.is_true(Player:hasQuickDial(player))
player:addQuickDial(CpuShip())
player:addQuickDial(CpuShip())
player:addQuickDial(station)
player:addQuickDial(CpuShip())
assert.is_true(player:hasButton("relay", "Quick Dial"))
player:clickButton("relay", "Quick Dial")
assert.is_true(player:hasButton("relay", "Outpost 42"))
player:clickButton("relay", "Back")
player:removeQuickDial(station)
player:clickButton("relay", "Quick Dial")
assert.is_false(player:hasButton("relay", "Outpost 42"))
Player:withStorage()
Player:withStorage() allows to configure the maxStorage
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 1000})
assert.is_true(Player:hasStorage(player))
assert.is_same(1000, player:getMaxStorageSpace())
Player:withStorage() creates a valid storage
local player = PlayerSpaceship()
Player:withStorage(player)
assert.is_true(Player:hasStorage(player))
assert.is_number(player:getMaxStorageSpace())
Player:withStorage() fails if first argument is a number
assert.has_error(function() Player:withStorage(42) end)
Player:withStorage() fails if second argument is a number
assert.has_error(function() Player:withStorage(PlayerSpaceship(), 42) end)
Player:withStorage():getProductStorage(),:getEmptyProductStorage(),:getMaxProductStorage()
Player:withStorage():getProductStorage(),:getEmptyProductStorage(),:getMaxProductStorage() returns the correct value
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 100})
assert.is_same(0, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(100, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(100, player:getMaxProductStorage(product2))
assert.is_same(100, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(100, player:getMaxProductStorage(product3))
assert.is_same(100, player:getEmptyProductStorage(product3))
player:modifyProductStorage(product1, 10)
assert.is_same(10, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(90, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(90, player:getMaxProductStorage(product2))
assert.is_same(90, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(90, player:getMaxProductStorage(product3))
assert.is_same(90, player:getEmptyProductStorage(product3))
player:modifyProductStorage(product2, 10)
assert.is_same(10, player:getProductStorage(product1))
assert.is_same(90, player:getMaxProductStorage(product1))
assert.is_same(80, player:getEmptyProductStorage(product1))
assert.is_same(10, player:getProductStorage(product2))
assert.is_same(90, player:getMaxProductStorage(product2))
assert.is_same(80, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(80, player:getMaxProductStorage(product3))
assert.is_same(80, player:getEmptyProductStorage(product3))
player:modifyProductStorage(product2, -5)
assert.is_same(10, player:getProductStorage(product1))
assert.is_same(95, player:getMaxProductStorage(product1))
assert.is_same(85, player:getEmptyProductStorage(product1))
assert.is_same(5, player:getProductStorage(product2))
assert.is_same(90, player:getMaxProductStorage(product2))
assert.is_same(85, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(85, player:getMaxProductStorage(product3))
assert.is_same(85, player:getEmptyProductStorage(product3))
Player:withStorage():getProductStorage(),:getEmptyProductStorage(),:getMaxProductStorage() they fail when called without argument
local player = PlayerSpaceship()
Player:withStorage(player)
assert.has_error(function() player:getProductStorage() end)
assert.has_error(function() player:getMaxProductStorage() end)
assert.has_error(function() player:getEmptyProductStorage() end)
Player:withStorage():getProductStorage(),:getEmptyProductStorage(),:getMaxProductStorage() works correctly with sized products
local product1 = productMock()
local product2 = productMock()
local product3 = productMock()
product1.getSize = function() return 1 end
product2.getSize = function() return 2 end
product3.getSize = function() return 4 end
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 100})
assert.is_same(0, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(100, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(50, player:getMaxProductStorage(product2))
assert.is_same(50, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(25, player:getMaxProductStorage(product3))
assert.is_same(25, player:getEmptyProductStorage(product3))
player:modifyProductStorage(product1, 10)
assert.is_same(10, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(90, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(45, player:getMaxProductStorage(product2))
assert.is_same(45, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(22, player:getMaxProductStorage(product3))
assert.is_same(22, player:getEmptyProductStorage(product3))
player:modifyProductStorage(product2, 10)
assert.is_same(10, player:getProductStorage(product1))
assert.is_same(80, player:getMaxProductStorage(product1))
assert.is_same(70, player:getEmptyProductStorage(product1))
assert.is_same(10, player:getProductStorage(product2))
assert.is_same(45, player:getMaxProductStorage(product2))
assert.is_same(35, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(17, player:getMaxProductStorage(product3))
assert.is_same(17, player:getEmptyProductStorage(product3))
player:modifyProductStorage(product2, -5)
assert.is_same(10, player:getProductStorage(product1))
assert.is_same(90, player:getMaxProductStorage(product1))
assert.is_same(80, player:getEmptyProductStorage(product1))
assert.is_same(5, player:getProductStorage(product2))
assert.is_same(45, player:getMaxProductStorage(product2))
assert.is_same(40, player:getEmptyProductStorage(product2))
assert.is_same(0, player:getProductStorage(product3))
assert.is_same(20, player:getMaxProductStorage(product3))
assert.is_same(20, player:getEmptyProductStorage(product3))
player:modifyProductStorage(product3, 5)
assert.is_same(10, player:getProductStorage(product1))
assert.is_same(70, player:getMaxProductStorage(product1))
assert.is_same(60, player:getEmptyProductStorage(product1))
assert.is_same(5, player:getProductStorage(product2))
assert.is_same(35, player:getMaxProductStorage(product2))
assert.is_same(30, player:getEmptyProductStorage(product2))
assert.is_same(5, player:getProductStorage(product3))
assert.is_same(20, player:getMaxProductStorage(product3))
assert.is_same(15, player:getEmptyProductStorage(product3))
Player:withStorage():getProductStorage(),:getEmptyProductStorage(),:getMaxProductStorage() works with rockets
local hvli = Product:new("HVLI", {id = "hvli"})
local player = PlayerSpaceship()
Player:withStorage(player)
player:setWeaponStorageMax("hvli", 8)
player:setWeaponStorage("hvli", 6)
assert.is_same(6, player:getProductStorage(hvli))
assert.is_same(8, player:getMaxProductStorage(hvli))
assert.is_same(2, player:getEmptyProductStorage(hvli))
for _, weapon in pairs({"hvli", "homing", "mine", "nuke", "emp"}) do
local rocket = Product:new(weapon, {id = weapon})
player:setWeaponStorageMax(weapon, 0)
player:setWeaponStorage(weapon, 0)
assert.is_same(0, player:getProductStorage(rocket))
assert.is_same(0, player:getMaxProductStorage(rocket))
assert.is_same(0, player:getEmptyProductStorage(rocket))
end
Player:withStorage():getProductStorage(),:getEmptyProductStorage(),:getMaxProductStorage() works with scan probes
local probe = Product:new("Scan Probe", {id = "scanProbe"})
local player = PlayerSpaceship()
Player:withStorage(player)
player:setMaxScanProbeCount(8)
player:setScanProbeCount(6)
assert.is_same(6, player:getProductStorage(probe))
assert.is_same(8, player:getMaxProductStorage(probe))
assert.is_same(2, player:getEmptyProductStorage(probe))
player:setMaxScanProbeCount(0)
player:setScanProbeCount(0)
assert.is_same(0, player:getProductStorage(probe))
assert.is_same(0, player:getMaxProductStorage(probe))
assert.is_same(0, player:getEmptyProductStorage(probe))
Player:withStorage():getStorageSpace(),
Player:withStorage():getStorageSpace(), getEmptyStorageSpace(), getMaxStorageSpace() returns the correct value
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 100})
assert.is_same(0, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(100, player:getEmptyStorageSpace())
player:modifyProductStorage(product1, 10)
assert.is_same(10, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(90, player:getEmptyStorageSpace())
player:modifyProductStorage(product2, 10)
assert.is_same(20, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(80, player:getEmptyStorageSpace())
player:modifyProductStorage(product2, -5)
assert.is_same(15, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(85, player:getEmptyStorageSpace())
Player:withStorage():getStorageSpace(), getEmptyStorageSpace(), getMaxStorageSpace() returns the correct values for sized products
local product1 = productMock()
local product2 = productMock()
local product3 = productMock()
product1.getSize = function() return 1 end
product2.getSize = function() return 2 end
product3.getSize = function() return 4 end
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 100})
assert.is_same(0, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(100, player:getEmptyStorageSpace())
player:modifyProductStorage(product1, 10)
assert.is_same(10, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(90, player:getEmptyStorageSpace())
player:modifyProductStorage(product2, 10)
assert.is_same(30, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(70, player:getEmptyStorageSpace())
player:modifyProductStorage(product2, -5)
assert.is_same(20, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(80, player:getEmptyStorageSpace())
player:modifyProductStorage(product3, 5)
assert.is_same(40, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(60, player:getEmptyStorageSpace())
Player:withStorage():getStoredProducts()
Player:withStorage():getStoredProducts() does not return rockets and probes, because we might not know the correct object to return
local player = PlayerSpaceship()
Player:withStorage(player)
player:setWeaponStorageMax("hvli", 5)
player:setWeaponStorage("hvli", 5)
player:setWeaponStorageMax("homing", 4)
player:setWeaponStorage("homing", 4)
player:setWeaponStorageMax("mine", 3)
player:setWeaponStorage("mine", 3)
player:setWeaponStorageMax("emp", 2)
player:setWeaponStorage("emp", 2)
player:setWeaponStorageMax("nuke", 1)
player:setWeaponStorage("nuke", 1)
player:setMaxScanProbeCount(4)
player:setScanProbeCount(4)
assert.is_same({}, player:getStoredProducts())
Player:withStorage():getStoredProducts() returns all the products that are currently stored
local player = PlayerSpaceship()
Player:withStorage(player)
assert.is_same({}, player:getStoredProducts())
player:modifyProductStorage(product1, 1)
assert.is_same(1, Util.size(player:getStoredProducts()))
assert.contains_value(product1, player:getStoredProducts())
player:modifyProductStorage(product1, 1)
assert.is_same(1, Util.size(player:getStoredProducts()))
assert.contains_value(product1, player:getStoredProducts())
player:modifyProductStorage(product2, 1)
assert.is_same(2, Util.size(player:getStoredProducts()))
assert.contains_value(product2, player:getStoredProducts())
player:modifyProductStorage(product2, -1)
assert.is_same(1, Util.size(player:getStoredProducts()))
assert.contains_value(product1, player:getStoredProducts())
assert.not_contains_value(product2, player:getStoredProducts())
Player:withStorage():modifyProductStorage()
Player:withStorage():modifyProductStorage() allows to handle rockets
local player = PlayerSpaceship()
Player:withStorage(player)
for _, weapon in pairs({"hvli", "homing", "mine", "nuke", "emp"}) do
local rocket = Product:new(weapon, {id = weapon})
player:setWeaponStorageMax(weapon, 4)
player:setWeaponStorage(weapon, 0)
player:modifyProductStorage(rocket, 2)
assert.is_same(2, player:getWeaponStorage(weapon))
end
Player:withStorage():modifyProductStorage() allows to handle scan probes
local probe = Product:new("Scan Probe", {id = "scanProbe"})
local player = PlayerSpaceship()
Player:withStorage(player)
player:setMaxScanProbeCount(4)
player:setScanProbeCount(0)
player:modifyProductStorage(probe, 2)
assert.is_same(2, player:getScanProbeCount())
Player:withStorage():modifyProductStorage() fails if no amount is given
local player = PlayerSpaceship()
Player:withStorage(player)
assert.has_error(function() player:modifyProductStorage(product1, nil) end)
Player:withStorage():modifyProductStorage() fails if no product is given
local player = PlayerSpaceship()
Player:withStorage(player)
assert.has_error(function() player:modifyProductStorage(nil, 10) end)
Player:withStorage():modifyProductStorage() it allows to overload the storage so that important mission items are not lost
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 100})
assert.is_same(0, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(100, player:getEmptyStorageSpace())
assert.is_same(0, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(100, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(100, player:getMaxProductStorage(product2))
assert.is_same(100, player:getEmptyProductStorage(product2))
player:modifyProductStorage(product1, 999)
assert.is_same(999, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(0, player:getEmptyStorageSpace())
assert.is_same(999, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(0, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(0, player:getMaxProductStorage(product2))
assert.is_same(0, player:getEmptyProductStorage(product2))
Player:withStorage():modifyProductStorage() it keeps sure the storage level will not be negative
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 100})
assert.is_same(0, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(100, player:getEmptyStorageSpace())
assert.is_same(0, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(100, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(100, player:getMaxProductStorage(product2))
assert.is_same(100, player:getEmptyProductStorage(product2))
player:modifyProductStorage(product1, -10)
assert.is_same(0, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(100, player:getEmptyStorageSpace())
assert.is_same(0, player:getProductStorage(product1))
assert.is_same(100, player:getMaxProductStorage(product1))
assert.is_same(100, player:getEmptyProductStorage(product1))
assert.is_same(0, player:getProductStorage(product2))
assert.is_same(100, player:getMaxProductStorage(product2))
assert.is_same(100, player:getEmptyProductStorage(product2))
Player:withStorage():setMaxStorageSpace()
Player:withStorage():setMaxStorageSpace() allows to set the maximum storage space
local player = PlayerSpaceship()
Player:withStorage(player, {maxStorage = 100})
assert.is_same(0, player:getStorageSpace())
assert.is_same(100, player:getMaxStorageSpace())
assert.is_same(100, player:getEmptyStorageSpace())
player:setMaxStorageSpace(120)
assert.is_same(0, player:getStorageSpace())
assert.is_same(120, player:getMaxStorageSpace())
assert.is_same(120, player:getEmptyStorageSpace())
Player:withStorageDisplay()
Player:withStorageDisplay() creates a valid storage display
local player = PlayerSpaceship()
Player:withMenu(player)
Player:withStorage(player)
Player:withStorageDisplay(player, defaultConfig)
assert.is_true(Player:hasStorageDisplay(player))
assert.is_true(player:hasButton("engineering", "Storage"))
player:clickButton("engineering", "Storage")
assert.is_true(player:hasCustomMessage("engineering"))
Player:withStorageDisplay() fails if the first argument is a player without storage
local player = PlayerSpaceship()
assert.has_error(function() Player:withStorageDisplay(player, defaultConfig) end)
Player:withStorageDisplay() fails if the first argument is already a storage display player
local player = PlayerSpaceship()
Player:withMenu(player)
Player:withStorage(player)
Player:withStorageDisplay(player, defaultConfig)
assert.has_error(function() Player:withStorageDisplay(player, defaultConfig) end)
Player:withStorageDisplay() fails if the first argument is not a player
assert.has_error(function() Player:withStorageDisplay(42, defaultConfig) end)
Player:withUpgradeDisplay()
Player:withUpgradeDisplay() creates a valid upgrade display
local player = PlayerSpaceship()
Player:withMenu(player)
Player:withUpgradeTracker(player)
Player:withUpgradeDisplay(player, defaultConfig)
assert.is_true(Player:hasUpgradeDisplay(player))
assert.is_true(player:hasButton("engineering", "Upgrades"))
player:clickButton("engineering", "Upgrades")
assert.is_true(player:hasCustomMessage("engineering"))
Player:withUpgradeDisplay() fails if the first argument is a player without storage
assert.has_error(function() Player:withUpgradeDisplay(PlayerSpaceship(), defaultConfig) end)
Player:withUpgradeDisplay() fails if the first argument is already an upgrade display player
local player = PlayerSpaceship()
Player:withMenu(player)
Player:withUpgradeTracker(player)
Player:withUpgradeDisplay(player, defaultConfig)
assert.has_error(function() Player:withUpgradeDisplay(player, defaultConfig) end)
Player:withUpgradeDisplay() fails if the first argument is not a player
assert.has_error(function() Player:withUpgradeDisplay(42, defaultConfig) end)
Player:withUpgradeTracker()
Player:withUpgradeTracker() creates a valid upgrade tracker
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
assert.is_true(Player:hasUpgradeTracker(player))
Player:withUpgradeTracker() fails if the first argument is already an upgrade tracker player
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
assert.has_error(function() Player:withUpgradeTracker(player) end)
Player:withUpgradeTracker() fails if the first argument is not a player
assert.has_error(function() Player:withUpgradeTracker(42) end)
Player:withUpgradeTracker():addUpgrade()
Player:withUpgradeTracker():addUpgrade() adds a upgrade
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
local upgrade = upgradeMock()
player:addUpgrade(upgrade)
assert.is_same(1, Util.size(player:getUpgrades()))
Player:withUpgradeTracker():addUpgrade() fails if the first parameter is not a upgrade
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
assert.has_error(function() player:addUpgrade(42) end)
Player:withUpgradeTracker():getUpgrades()
Player:withUpgradeTracker():getUpgrades() manipulating the result set does not add upgrades
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
local upgrades = player:getUpgrades()
table.insert(upgrades, upgradeMock())
assert.is_same(1, Util.size(player:getUpgrades()))
Player:withUpgradeTracker():getUpgrades() returns all upgrades
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
player:addUpgrade(upgradeMock())
player:addUpgrade(upgradeMock())
assert.is_same(3, Util.size(player:getUpgrades()))
Player:withUpgradeTracker():hasUpgrade()
Player:withUpgradeTracker():hasUpgrade() fails if the given argument is a number
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
assert.has_error(function() player:hasUpgrade(42) end)
Player:withUpgradeTracker():hasUpgrade() returns false if the upgrade is not installed by name
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
player:addUpgrade(upgradeMock())
assert.is_false(player:hasUpgrade("fake"))
Player:withUpgradeTracker():hasUpgrade() returns false if the upgrade is not installed by name
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
player:addUpgrade(upgradeMock())
local upgrade = upgradeMock()
player:addUpgrade(upgrade)
assert.is_true(player:hasUpgrade(upgrade:getId()))
Player:withUpgradeTracker():hasUpgrade() returns false if the upgrade is not installed by object
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
player:addUpgrade(upgradeMock())
assert.is_false(player:hasUpgrade(upgradeMock()))
Player:withUpgradeTracker():hasUpgrade() returns true if the upgrade is installed by object
local player = PlayerSpaceship()
Player:withUpgradeTracker(player)
player:addUpgrade(upgradeMock())
player:addUpgrade(upgradeMock())
local upgrade = upgradeMock()
player:addUpgrade(upgrade)
assert.is_true(player:hasUpgrade(upgrade))
Product
Product:new()
Product:new() allows to set an id
local product = Product:new("Fake", {id = "unobtainium"})
assert.is_same("unobtainium", product:getId())
Product:new() allows to set size
local product = Product:new("Fake", {size = 42})
assert.is_same(42, product:getSize())
Product:new() auto generates an id
local product = Product:new("Fake")
assert.is_string(product:getId())
assert.not_same("", product:getId())
Product:new() fails if first argument is a number
assert.has_error(function() Product:new(42) end)
Product:new() fails if second argument is numeric
assert.has_error(function() Product:new("Fake", 42) end)
Product:new() fails if size is non-numeric
assert.has_error(function() Product:new("Fake", {size = "foo"}) end)
Product:new() returns a valid Product
local product = Product:new("Fake")
assert.is_true(Product:isProduct(product))
assert.is_same("Fake", product:getName())
assert.is_string(product:getId())
assert.not_same("", product:getId())
assert.is_same(1, product:getSize())
Product:new():toId()
Product:new():toId() returns a string if product is given
local product = Product:new("Product", {id = "theId"})
assert.is_same("theId", Product:toId(product))
Product:new():toId() returns string if a string was given
assert.is_same("foobar", Product:toId("foobar"))
Other
Ship
Ship GM interaction GM can carry out orders on behalf of the ship
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0))
ship:addOrder(Order:flyTo(0, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
-- GM interferes
ship:orderRoaming()
Cron.tick(1)
assert.is_same("Roaming", ship:getOrder())
-- ship passes by accident :)
ship:setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
Ship GM interaction recovers after GM interaction
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
-- GM interferes
ship:orderRoaming()
Cron.tick(1)
assert.is_same("Roaming", ship:getOrder())
-- GM resets
ship:orderIdle()
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Ship delay delays the execution of the next command
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
local onCompletionCalled = 0
ship:addOrder(Order:flyTo(1000, 0, {
onCompletion = function() onCompletionCalled = onCompletionCalled + 1 end,
delayAfter = 10,
}))
ship:addOrder(Order:flyTo(0, 1000))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
assert.is_same(0, onCompletionCalled)
ship:setPosition(1000, 0)
Cron.tick(1)
assert.is_same(1, onCompletionCalled)
-- but the command should not have changed yet
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
for _=1,9 do Cron.tick(1) end
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 1000}, {ship:getOrderTargetLocation()})
Ship delay will be respected if addCommand is called during delay
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(0, 0, {
delayAfter = 10,
}))
for _=1,5 do Cron.tick(1) end
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
ship:addOrder(Order:flyTo(1000, 0))
-- it should not change yet
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
for _=1,4 do Cron.tick(1) end
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Ship loop allows to loop orders
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0, {
onCompletion = function(self, ship) ship:addOrder(self) end,
}))
ship:addOrder(Order:flyTo(0, 0, {
onCompletion = function(self, ship) ship:addOrder(self) end,
}))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(0, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
Ship
Ship:Order:tick()
Ship:Order:tick() executes the next order if Order:tick() returns false
local order = mockOrder()
local called = 0
order.getShipExecutor = function()
return {
go = function(_, ship) ship:orderRoaming() end,
tick = function()
called = called + 1
if called > 3 then
return false, "boom"
end
end,
}
end
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:addOrder(order)
ship:addOrder(Order:flyTo(1000, 0))
assert.is_same("Roaming", ship:getOrder())
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
Ship:Order:tick() makes the ship idle if Order:tick() returns false and there is no next order
local order = mockOrder()
local called = 0
order.getShipExecutor = function()
return {
go = function(_, ship) ship:orderRoaming() end,
tick = function()
called = called + 1
if called > 3 then
return false, "boom"
end
end,
}
end
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:addOrder(order)
assert.is_same("Roaming", ship:getOrder())
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same("Idle", ship:getOrder())
ship:addOrder(Order:flyTo(1000, 0))
assert.is_same("Fly towards", ship:getOrder())
Ship:Ship:withOrderQueue()
Ship:Ship:withOrderQueue() fails if parameter is not a ship
assert.has_error(function()
Ship:withOrderQueue()
end)
assert.has_error(function()
Ship:withOrderQueue(42)
end)
assert.has_error(function()
Ship:withOrderQueue(SpaceStation())
end)
assert.has_error(function()
Ship:withOrderQueue(Fleet:new({CpuShip(), CpuShip(), CpuShip()}))
end)
Ship:Ship:withOrderQueue() should create a ship with order queue
local ship = CpuShip()
Ship:withOrderQueue(ship)
assert.is_true(Ship:hasOrderQueue(ship))
Ship:abortCurrentOrder()
Ship:abortCurrentOrder() carries out next order if there is one in the cue
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0))
ship:addOrder(Order:flyTo(2000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
ship:abortCurrentOrder()
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({2000, 0}, {ship:getOrderTargetLocation()})
Ship:abortCurrentOrder() carries out next order without delay if there is one in the cue
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0, {delayAfter = 10}))
ship:addOrder(Order:flyTo(2000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Cron.tick(1)
ship:abortCurrentOrder()
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({2000, 0}, {ship:getOrderTargetLocation()})
Ship:abortCurrentOrder() does nothing if there is no current order
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:abortCurrentOrder()
Ship:abortCurrentOrder() ship idles if there is no next order
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:flyTo(1000, 0, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end
})
ship:addOrder(order)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same(0, onAbortCalled)
ship:abortCurrentOrder()
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("user", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Idle", ship:getOrder())
Ship:addOrder()
Ship:addOrder() carries out an order until the next one is given
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(0, 0))
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 0}, {ship:getOrderTargetLocation()})
ship:addOrder(Order:flyTo(0, 1000))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 1000}, {ship:getOrderTargetLocation()})
Ship:addOrder() fails if parameter is not an order
local ship = CpuShip()
Ship:withOrderQueue(ship)
assert.has_error(function()
ship:addOrder()
end)
assert.has_error(function()
ship:addOrder(42)
end)
assert.has_error(function()
ship:addOrder(CpuShip())
end)
Ship:addOrder() immediately carries out an order if there are no other order queued
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Ship:addOrder() queues orders and carries them out consecutively
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0))
ship:addOrder(Order:flyTo(0, 1000))
ship:addOrder(Order:flyTo(-1000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 1000}, {ship:getOrderTargetLocation()})
ship:setPosition(0, 1000)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({-1000, 0}, {ship:getOrderTargetLocation()})
Ship:addOrder() waits until the first order is issued
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
assert.is_same("Idle", ship:getOrder())
Cron.tick(1)
assert.is_same("Idle", ship:getOrder())
ship:addOrder(Order:flyTo(1000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Ship:behaveAsMiner()
Ship:behaveAsMiner() GM can change the mined asteroid
withUniverse(function()
local station = mockValidStation()
local miner = mockValidMiner()
local asteroid1 = Asteroid()
local asteroid2 = Asteroid()
station:setPosition(0, 0)
miner:setPosition(0, 0)
miner:setDockedAt(station)
asteroid1:setPosition(1000, 0)
asteroid2:setPosition(99999, 0)
local whenMinedCalled = 0
Ship:behaveAsMiner(miner, station, function()
whenMinedCalled = whenMinedCalled + 1
return {
[product] = 42,
}
end)
-- find a close asteroid
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid1, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
-- now it is close and should start mining
miner:setPosition(800, 0)
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid1, miner:getOrderTarget())
assert.is_same("mining", miner:getMinerState())
-- it should spend some time mining, but not finish
for i=1,10 do Cron.tick(1) end
assert.is_same(0, whenMinedCalled)
assert.is_same("mining", miner:getMinerState())
-- now the GM interjects
miner:orderFlyTowardsBlind(asteroid2:getPosition()) -- GM interface does not allow to issue Attack orders on asteroids
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid2, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
-- now it is close and should start mining
miner:setPosition(asteroid2:getPosition())
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid2, miner:getOrderTarget())
assert.is_same("mining", miner:getMinerState())
-- it should spend the whole time mining
for i=1,14 do Cron.tick(1) end
assert.is_same(0, whenMinedCalled)
assert.is_same("mining", miner:getMinerState())
-- now the mining should have finished
Cron.tick(1)
assert.is_same(1, whenMinedCalled)
assert.is_same(42, miner:getProductStorage(product))
assert.is_same("Dock", miner:getOrder())
assert.is_same(station, miner:getOrderTarget())
assert.is_same("home", miner:getMinerState())
end)
Ship:behaveAsMiner() GM can force a miner to go home
withUniverse(function()
local station = mockValidStation()
local miner = mockValidMiner()
local asteroid = Asteroid()
station:setPosition(0, 0)
miner:setPosition(1000, 0)
asteroid:setPosition(1000, 0)
local headingHomeCalled = 0
Ship:behaveAsMiner(miner, station, function()
return { [product] = 42, }
end, {
onHeadingHome = function()
headingHomeCalled = headingHomeCalled + 1
end
})
local asteroid2 = Asteroid()
asteroid2:setPosition(2000, 0)
for i=1,20 do Cron.tick(1) end
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid2, miner:getOrderTarget())
miner:orderDock(station)
Cron.tick(1)
assert.is_same(1, headingHomeCalled)
-- ...GM rethinks...
miner:orderIdle()
Cron.tick(1)
assert.is_same("asteroid", miner:getMinerState())
-- ...but then orders miner home again...
miner:orderDock(station)
Cron.tick(1)
assert.is_same(2, headingHomeCalled)
miner:setPosition(station:getPosition())
miner:setDockedAt(station)
for i=1,20 do Cron.tick(1) end
assert.is_same(0, miner:getProductStorage(product))
assert.is_same(42, station:getProductStorage(product))
-- ...miner goes on as usual after unloading
assert.is_same("Attack", miner:getOrder())
end)
Ship:behaveAsMiner() GM can issue custom orders and reset them using the Idle order
withUniverse(function()
local station = mockValidStation()
local otherStation = SpaceStation()
local miner = mockValidMiner()
local asteroid = Asteroid()
station:setPosition(0, 0)
miner:setPosition(0, 0)
miner:setDockedAt(station)
asteroid:setPosition(1000, 0)
local whenMinedCalled = 0
Ship:behaveAsMiner(miner, station, function()
whenMinedCalled = whenMinedCalled + 1
return {
[product] = 42,
}
end)
-- find a close asteroid
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
-- GM interjects
miner:orderRoaming()
for i=1,15 do
Cron.tick(1)
assert.is_same("unknown", miner:getMinerState())
end
-- GM resets the order
miner:orderIdle()
for i=1,15 do
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
end
-- GM interjects
miner:orderDock(otherStation)
for i=1,15 do
Cron.tick(1)
assert.is_same("unknown", miner:getMinerState())
end
-- GM resets the order
miner:orderIdle()
for i=1,15 do
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
end
end)
Ship:behaveAsMiner() does basically work:)
withUniverse(function()
local station = mockValidStation()
local miner = mockValidMiner()
local asteroid = Asteroid()
station:setPosition(0, 0)
miner:setPosition(0, 0)
asteroid:setPosition(1000, 0)
local whenMinedCalled = 0
local asteroidMinedCalled = 0
local asteroidMinedArg1, asteroidMinedArg2, asteroidMinedArg3
local headingAsteroidCalled = 0
local headingAsteroidArg1, headingAsteroidArg2
local unloadedCalled = 0
local unloadedArg1, unloadedArg2, unloadedArg3
local headingHomeCalled = 0
local headingHomeArg1, headingHomeArg2, headingHomeArg3
Ship:behaveAsMiner(miner, station, function()
whenMinedCalled = whenMinedCalled + 1
return {
[product] = 42,
}
end, {
onHeadingAsteroid = function(arg1, arg2)
headingAsteroidCalled = headingAsteroidCalled + 1
headingAsteroidArg1, headingAsteroidArg2 = arg1, arg2
end,
onAsteroidMined = function(arg1, arg2, arg3)
asteroidMinedCalled = asteroidMinedCalled + 1
asteroidMinedArg1, asteroidMinedArg2, asteroidMinedArg3 = arg1, arg2, arg3
end,
onHeadingHome = function(arg1, arg2, arg3)
headingHomeCalled = headingHomeCalled + 1
headingHomeArg1, headingHomeArg2, headingHomeArg3 = arg1, arg2, arg3
end,
onUnloaded = function(arg1, arg2, arg3)
unloadedCalled = unloadedCalled + 1
unloadedArg1, unloadedArg2, unloadedArg3 = arg1, arg2, arg3
end,
})
-- find a close asteroid
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
assert.is_same(1, headingAsteroidCalled)
assert.is_same(miner, headingAsteroidArg1)
assert.is_same(asteroid, headingAsteroidArg2)
miner:setPosition(100, 0)
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
miner:setPosition(200, 0)
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
-- now it is close and should start mining
miner:setPosition(800, 0)
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
assert.is_same("mining", miner:getMinerState())
-- ...let it mine a little...
for i=1,14 do Cron.tick(1) end
assert.is_same(0, whenMinedCalled)
assert.is_same(0, asteroidMinedCalled)
assert.is_same(0, headingHomeCalled)
assert.is_same("mining", miner:getMinerState())
-- now the mining should have finished
Cron.tick(1)
assert.is_same(1, whenMinedCalled)
assert.is_same(42, miner:getProductStorage(product))
assert.is_same(1, asteroidMinedCalled)
assert.is_same(miner, asteroidMinedArg1)
assert.is_same(asteroid, asteroidMinedArg2)
assert.is_same("table", type(asteroidMinedArg3))
assert.is_same(42, asteroidMinedArg3[product])
assert.is_same(1, headingHomeCalled)
assert.is_same(miner, headingHomeArg1)
assert.is_same(station, headingHomeArg2)
assert.is_same("table", type(headingHomeArg3))
assert.is_same(42, headingHomeArg3[product])
assert.is_same("Dock", miner:getOrder())
assert.is_same(station, miner:getOrderTarget())
assert.is_same("home", miner:getMinerState())
miner:setPosition(200, 0)
Cron.tick(1)
assert.is_same("Dock", miner:getOrder())
assert.is_same(station, miner:getOrderTarget())
assert.is_same("home", miner:getMinerState())
miner:setPosition(0, 0)
miner:setDockedAt(station)
Cron.tick(1)
assert.is_same("Dock", miner:getOrder())
assert.is_same(station, miner:getOrderTarget())
assert.is_same("unloading", miner:getMinerState())
-- ...let it unload a little...
for i=1,14 do Cron.tick(1) end
assert.is_same("Dock", miner:getOrder())
assert.is_same(station, miner:getOrderTarget())
assert.is_same(42, miner:getProductStorage(product))
assert.is_same(0, station:getProductStorage(product))
assert.is_same("unloading", miner:getMinerState())
assert.is_same(0, unloadedCalled)
-- ...now delivery should be complete
Cron.tick(1)
assert.is_same(0, miner:getProductStorage(product))
assert.is_same(42, station:getProductStorage(product))
assert.is_same(1, unloadedCalled)
assert.is_same(miner, unloadedArg1)
assert.is_same(station, unloadedArg2)
assert.is_same("table", type(unloadedArg3))
assert.is_same(42, unloadedArg3[product])
-- cycle starts again
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
end)
Ship:behaveAsMiner() does not mine more asteroids when a maximum time has run out
withUniverse(function()
local station = mockValidStation()
local miner = mockValidMiner()
local asteroid1 = Asteroid()
station:setPosition(0, 0)
miner:setPosition(0, 0)
asteroid1:setPosition(1000, 0)
local whenMinedCalled = 0
Ship:behaveAsMiner(miner, station, function()
whenMinedCalled = whenMinedCalled + 1
return {
[product] = 42,
}
end)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid1, miner:getOrderTarget())
for i=1,999 do Cron.tick(1) end
local asteroid2 = Asteroid()
asteroid2:setPosition(2000, 0)
-- it cares out its order first
miner:setPosition(asteroid1:getPosition())
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid1, miner:getOrderTarget())
for i=1,15 do Cron.tick(1) end
assert.is_same("Dock", miner:getOrder())
assert.is_same(station, miner:getOrderTarget())
end)
Ship:behaveAsMiner() fails if homeStation is not a Station
local miner = mockValidMiner()
assert.has_error(function()
Ship:behaveAsMiner(miner, nil, function() end)
end, "Expected homeStation to be a Station, but got <nil>")
assert.has_error(function()
Ship:behaveAsMiner(miner, 42, function() end)
end, "Expected homeStation to be a Station, but got <number>42")
assert.has_error(function()
Ship:behaveAsMiner(miner, SpaceShip():setCallSign("Test"), function() end)
end, "Expected homeStation to be a Station, but got <SpaceShip>\"Test\"")
Ship:behaveAsMiner() fails if ship does not have storage
local station = mockValidStation()
local miner = CpuShip():setCallSign("Dummy")
assert.has_error(function()
Ship:behaveAsMiner(miner, station, function() end)
end, "Ship Dummy needs to have storage configured")
Ship:behaveAsMiner() fails if ship is destroyed
local station = mockValidStation()
local miner = mockValidMiner()
miner:destroy()
assert.has_error(function()
Ship:behaveAsMiner(miner, station, function() end)
end, "Expected ship to be a valid CpuShip, but got a destroyed one")
Ship:behaveAsMiner() fails if ship is not a ship
local station = mockValidStation()
assert.has_error(function()
Ship:behaveAsMiner(nil, station, function() end)
end, "Expected ship to be a CpuShip, but got <nil>")
assert.has_error(function()
Ship:behaveAsMiner(42, station, function() end)
end, "Expected ship to be a CpuShip, but got <number>42")
assert.has_error(function()
Ship:behaveAsMiner(SpaceStation():setCallSign("Test"), station, function() end)
end, "Expected ship to be a CpuShip, but got <SpaceStation>\"Test\"")
assert.has_error(function()
Ship:behaveAsMiner(SpaceShip():setCallSign("Test"), station, function() end)
end, "Expected ship to be a CpuShip, but got <SpaceShip>\"Test\"")
Ship:behaveAsMiner() fails if station does not have storage
local station = SpaceStation():setCallSign("Home")
local miner = mockValidMiner()
assert.has_error(function()
Ship:behaveAsMiner(miner, station, function() end)
end, "Station Home needs to have storage configured")
Ship:behaveAsMiner() idles when home base is destroyed
withUniverse(function() withLogCatcher(function(logs)
local station = mockValidStation():setCallSign("Home")
local miner = mockValidMiner():setCallSign("Dummy")
local asteroid = Asteroid()
station:setPosition(0, 0)
miner:setPosition(0, 0)
miner:setDockedAt(station)
asteroid:setPosition(1000, 0)
Ship:behaveAsMiner(miner, station, function()
return {
[product] = 42,
}
end)
Cron.tick(1)
assert.is_same("asteroid", miner:getMinerState())
station:destroy()
Cron.tick(1)
assert.is_same(1, logs:countWarnings())
assert.is_same("unknown", miner:getMinerState())
assert.is_same("Dummy has lost its home base. :(", logs:popLastWarning())
assert.is_same(nil, logs:popLastWarning()) -- no further errors
Cron.tick(1)
assert.is_same(nil, logs:popLastWarning()) -- it does not spam
end) end)
Ship:behaveAsMiner() selects a new asteroid if the current target is destroyed
withUniverse(function(logs)
local station = mockValidStation():setCallSign("Home")
local miner = mockValidMiner():setCallSign("Dummy")
local asteroid1 = Asteroid():setCallSign("one")
local asteroid2 = Asteroid():setCallSign("two")
station:setPosition(0, 0)
miner:setPosition(0, 0)
miner:setDockedAt(station)
asteroid1:setPosition(1000, 0)
asteroid2:setPosition(2000, 0)
Ship:behaveAsMiner(miner, station, function()
return {
[product] = 42,
}
end)
Cron.tick(1)
assert.is_same("asteroid", miner:getMinerState())
assert.is_same("Attack", miner:getOrder())
local target = miner:getOrderTarget()
target:destroy()
Cron.tick(1)
assert.is_same("asteroid", miner:getMinerState())
assert.is_same("Attack", miner:getOrder())
assert.not_is_same(target, miner:getOrderTarget())
end)
Ship:behaveAsMiner() selects a new asteroid if the current target is destroyed while mining
withUniverse(function(logs)
local station = mockValidStation():setCallSign("Home")
local miner = mockValidMiner():setCallSign("Dummy")
local asteroid1 = Asteroid():setCallSign("one")
local asteroid2 = Asteroid():setCallSign("two")
station:setPosition(100, 0)
miner:setPosition(0, 100)
miner:setDockedAt(station)
asteroid1:setPosition(0, 0)
asteroid2:setPosition(0, 500)
Ship:behaveAsMiner(miner, station, function()
return {
[product] = 42,
}
end)
Cron.tick(1)
assert.is_same("mining", miner:getMinerState())
assert.is_same("Attack", miner:getOrder())
local target = miner:getOrderTarget()
target:destroy()
Cron.tick(1)
assert.is_same("asteroid", miner:getMinerState())
assert.is_same("Attack", miner:getOrder())
assert.not_is_same(target, miner:getOrderTarget())
end)
Ship:behaveAsMiner() warns when there are no mineable asteroids around the station
withUniverse(function() withLogCatcher(function(logs)
local station = mockValidStation():setCallSign("Home")
local miner = mockValidMiner():setCallSign("Dummy")
local asteroid = Asteroid()
station:setPosition(0, 0)
miner:setPosition(0, 0)
miner:setDockedAt(station)
asteroid:setPosition(99999, 0)
local whenMinedCalled = 0
Ship:behaveAsMiner(miner, station, function()
whenMinedCalled = whenMinedCalled + 1
return {
[product] = 42,
}
end)
assert.is_same(1, logs:countWarnings())
assert.is_same("unknown", miner:getMinerState())
assert.is_same("Dummy did not find any mineable asteroids around Home", logs:popLastWarning())
assert.is_same(nil, logs:popLastWarning()) -- no further errors
Cron.tick(1)
assert.is_same(nil, logs:popLastWarning()) -- it does not spam
-- but it starts as soon as an asteroid is close enough
asteroid:setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Attack", miner:getOrder())
assert.is_same(asteroid, miner:getOrderTarget())
assert.is_same("asteroid", miner:getMinerState())
end) end)
Ship:flushOrders()
Ship:flushOrders() does nothing if there are no further orders
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:abortCurrentOrder()
Ship:flushOrders() ship does not carry out any new orders after the one is completed
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0))
ship:addOrder(Order:flyTo(2000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Cron.tick(1)
ship:flushOrders()
ship:setPosition(1000, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Ship:forceOrderNow()
Ship:forceOrderNow() excutes the order if none was executed before
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
Cron.tick(1)
ship:forceOrderNow(Order:flyTo(1000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
Ship:forceOrderNow() executes a ship order immediately
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
local onAbortCalled, abortArg1, abortArg2, abortArg3 = 0, nil, nil, nil
local order = Order:flyTo(1000, 0, {
onAbort = function(arg1, arg2, arg3)
onAbortCalled = onAbortCalled + 1
abortArg1 = arg1
abortArg2 = arg2
abortArg3 = arg3
end
})
ship:addOrder(order)
ship:addOrder(Order:flyTo(2000, 0))
ship:addOrder(Order:flyTo(3000, 0))
ship:addOrder(Order:flyTo(4000, 0))
assert.is_same("Fly towards", ship:getOrder())
assert.is_same(0, onAbortCalled)
ship:forceOrderNow(Order:flyTo(0, 1000))
assert.is_same(1, onAbortCalled)
assert.is_same(order, abortArg1)
assert.is_same("user", abortArg2)
assert.is_same(ship, abortArg3)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({0, 1000}, {ship:getOrderTargetLocation()})
Ship:forceOrderNow() fails if parameter is not an order
local ship = CpuShip()
Ship:withOrderQueue(ship)
ship:setPosition(0, 0)
ship:addOrder(Order:flyTo(1000, 0))
ship:addOrder(Order:flyTo(2000, 0))
ship:addOrder(Order:flyTo(3000, 0))
ship:addOrder(Order:flyTo(4000, 0))
assert.has_error(function()
ship:forceOrderNow()
end)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({1000, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(1000, 0)
Cron.tick(1)
assert.has_error(function()
ship:forceOrderNow(42)
end)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({2000, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(2000, 0)
Cron.tick(1)
assert.has_error(function()
ship:forceOrderNow(CpuShip())
end)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({3000, 0}, {ship:getOrderTargetLocation()})
ship:setPosition(3000, 0)
Cron.tick(1)
assert.is_same("Fly towards", ship:getOrder())
assert.is_same({4000, 0}, {ship:getOrderTargetLocation()})
Ship:withCaptain(),:getCaptain()
Ship:withCaptain(),:getCaptain() sets the captain
local person = personMock()
local ship = CpuShip()
Ship:withCaptain(ship, person)
assert.is_true(ship:hasCrewAtPosition("captain"))
assert.is_true(ship:hasCaptain())
assert.is_same(person, ship:getCrewAtPosition("captain"))
assert.is_same(person, ship:getCaptain())
Ship:withCrew()
Ship:withCrew() can be called multiple times to add different persons
local person1 = personMock()
local person2 = personMock()
local ship = CpuShip()
Ship:withCrew(ship, {captain = person1})
Ship:withCrew(ship, {science = person2})
assert.is_same(person1, ship:getCrewAtPosition("captain"))
assert.is_same(person2, ship:getCrewAtPosition("science"))
Ship:withCrew() can be called multiple times to override a position
local person1 = personMock()
local person2 = personMock()
local ship = CpuShip()
Ship:withCrew(ship, {captain = person1})
Ship:withCrew(ship, {captain = person2})
assert.is_same(person2, ship:getCrewAtPosition("captain"))
Ship:withCrew() should create a crew
local ship = CpuShip()
Ship:withCrew(ship)
assert.is_true(Ship:hasCrew(ship))
Ship:withCrew() should create a crew with a certain position
local person = personMock()
local ship = CpuShip()
Ship:withCrew(ship, {captain = person})
assert.is_true(ship:hasCrewAtPosition("captain"))
assert.is_same(person, ship:getCrewAtPosition("captain"))
assert.is_false(ship:hasCrewAtPosition("science"))
Ship:withCrew() should fail when the position is not a person
local person = {}
local ship = CpuShip()
assert.has_error(function () Ship:withCrew(ship, {captain = person}) end)
Ship:withCrew() should fail when the position is not a string
local person = personMock()
local ship = CpuShip()
assert.has_error(function () Ship:withCrew(ship, {person}) end)
Ship:withEngineeringOfficer()
Ship:withEngineeringOfficer() sets the engineering officer
local person = personMock()
local ship = CpuShip()
Ship:withEngineeringOfficer(ship, person)
assert.is_true(ship:hasCrewAtPosition("engineering"))
assert.is_true(ship:hasEngineeringOfficer())
assert.is_same(person, ship:getCrewAtPosition("engineering"))
assert.is_same(person, ship:getEngineeringOfficer())
Ship:withEvents()
Ship:withEvents() config.onDockInitiation does not fail if the callback errors
local station = SpaceStation()
local ship = CpuShip()
Ship:withEvents(ship, {
onDockInitiation = function()
error("Boom")
end,
})
station:setPosition(0, 0)
ship:setPosition(2000, 0)
ship:orderDock(station)
assert.not_has_error(function()
Cron.tick(1)
end)
Ship:withEvents() config.onDockInitiation fails if onDockInitiation is not a callback
local ship = CpuShip()
assert.has_error(function()
Ship:withEvents(ship, { onDockInitiation = 42})
end)
Ship:withEvents() config.onDockInitiation is called when ship can not decide between two stations
local called = 0
local station1 = SpaceStation()
local station2 = SpaceStation()
local ship = CpuShip()
local calledArg1, calledArg2
Ship:withEvents(ship, {
onDockInitiation = function(arg1, arg2)
called = called + 1
calledArg1 = arg1
calledArg2 = arg2
end,
})
station1:setPosition(0, 0)
ship:setPosition(2000, 0)
station2:setPosition(4000, 0)
ship:orderDock(station1)
Cron.tick(1)
assert.is_same(1, called)
assert.is_same(ship, calledArg1)
assert.is_same(station1, calledArg2)
ship:orderDock(station2)
Cron.tick(1)
assert.is_same(2, called)
assert.is_same(ship, calledArg1)
assert.is_same(station2, calledArg2)
ship:orderDock(station1)
Cron.tick(1)
assert.is_same(3, called)
assert.is_same(ship, calledArg1)
assert.is_same(station1, calledArg2)
Ship:withEvents() config.onDockInitiation is called when the ship approaches a station with the intention of docking
local called = 0
local station = SpaceStation()
local ship = CpuShip()
local calledArg1, calledArg2
Ship:withEvents(ship, {
onDockInitiation = function(arg1, arg2)
called = called + 1
calledArg1 = arg1
calledArg2 = arg2
end,
})
station:setPosition(0, 0)
ship:setPosition(10000, 0)
ship:orderDock(station)
Cron.tick(1)
assert.is_same(0, called)
ship:setPosition(2000, 0)
Cron.tick(1)
assert.is_same(1, called)
assert.is_same(ship, calledArg1)
assert.is_same(station, calledArg2)
-- it is not called multiple times
Cron.tick(1)
assert.is_same(1, called)
-- it resets after a ship was docked at a station
ship:setDockedAt(station)
Cron.tick(1)
assert.is_same(1, called)
ship:orderIdle()
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(1, called)
ship:orderDock(station)
Cron.tick(1)
assert.is_same(2, called)
Ship:withEvents() config.onDockInitiation resets when ship changes orders
local called = 0
local station = SpaceStation()
local ship = CpuShip()
local calledArg1, calledArg2
Ship:withEvents(ship, {
onDockInitiation = function(arg1, arg2)
called = called + 1
calledArg1 = arg1
calledArg2 = arg2
end,
})
station:setPosition(0, 0)
ship:setPosition(10000, 0)
ship:orderDock(station)
Cron.tick(1)
assert.is_same(0, called)
ship:setPosition(2000, 0)
Cron.tick(1)
assert.is_same(1, called)
ship:orderIdle()
Cron.tick(1)
assert.is_same(1, called)
ship:orderDock(station)
Cron.tick(1)
assert.is_same(2, called)
Ship:withEvents() config.onDocking does not fail if the callback errors
local station = SpaceStation()
local ship = CpuShip()
Ship:withEvents(ship, {
onDocking = function()
error("Boom")
end,
})
ship:orderDock(station)
ship:setDockedAt(station)
assert.not_has_error(function()
Cron.tick(1)
end)
Ship:withEvents() config.onDocking fails if onDocking is not a callback
local ship = CpuShip()
assert.has_error(function()
Ship:withEvents(ship, { onDocking = 42})
end)
Ship:withEvents() config.onDocking is called when ship docks multiple stations
local called = 0
local station1 = SpaceStation()
local station2 = SpaceStation()
local ship = CpuShip()
local calledArg1, calledArg2
Ship:withEvents(ship, {
onDocking = function(arg1, arg2)
called = called + 1
calledArg1, calledArg2 = arg1, arg2
end,
})
Cron.tick(1)
assert.is_same(0, called)
ship:orderDock(station1)
Cron.tick(1)
assert.is_same(0, called)
ship:setDockedAt(station1)
Cron.tick(1)
assert.is_same(1, called)
assert.is_same(calledArg1, ship)
assert.is_same(calledArg2, station1)
ship:orderDock(station2)
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(1, called)
ship:setDockedAt(station2)
Cron.tick(1)
assert.is_same(2, called)
assert.is_same(calledArg1, ship)
assert.is_same(calledArg2, station2)
ship:orderDock(station1)
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(2, called)
ship:setDockedAt(station1)
Cron.tick(1)
assert.is_same(3, called)
Ship:withEvents() config.onDocking is called when the ship docks a station
local called = 0
local station = SpaceStation()
local ship = CpuShip()
Ship:withEvents(ship, {
onDocking = function()
called = called + 1
end,
})
Cron.tick(1)
assert.is_same(0, called)
ship:orderDock(station)
Cron.tick(1)
assert.is_same(0, called)
ship:setDockedAt(station)
Cron.tick(1)
assert.is_same(1, called)
-- it is only called once
Cron.tick(1)
assert.is_same(1, called)
ship:orderIdle()
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(1, called)
ship:orderDock(station)
Cron.tick(1)
assert.is_same(1, called)
-- it triggers again when undocked in between
ship:setDockedAt(station)
Cron.tick(1)
assert.is_same(2, called)
Ship:withEvents() config.onUndocking does not fail if the callback errors
local station = SpaceStation()
local ship = CpuShip()
Ship:withEvents(ship, {
onUndocking = function()
error("Boom")
end,
})
ship:orderDock(station)
ship:setDockedAt(station)
Cron.tick(1)
ship:orderIdle()
ship:setDockedAt(nil)
assert.not_has_error(function()
Cron.tick(1)
end)
Ship:withEvents() config.onUndocking fails if onUndocking is not a callback
local ship = CpuShip()
assert.has_error(function()
Ship:withEvents(ship, { onUndocking = 42})
end)
Ship:withEvents() config.onUndocking is called when ship undocks multiple stations
local called = 0
local station1 = SpaceStation()
local station2 = SpaceStation()
local ship = CpuShip()
local calledArg1, calledArg2
Ship:withEvents(ship, {
onUndocking = function(arg1, arg2)
called = called + 1
calledArg1, calledArg2 = arg1, arg2
end,
})
Cron.tick(1)
assert.is_same(0, called)
ship:orderDock(station1)
Cron.tick(1)
assert.is_same(0, called)
ship:setDockedAt(station1)
Cron.tick(1)
assert.is_same(0, called)
ship:orderDock(station2)
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(1, called)
assert.is_same(calledArg1, ship)
assert.is_same(calledArg2, station1)
ship:setDockedAt(station2)
Cron.tick(1)
assert.is_same(1, called)
ship:orderDock(station1)
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(2, called)
assert.is_same(calledArg1, ship)
assert.is_same(calledArg2, station2)
ship:setDockedAt(station1)
Cron.tick(1)
assert.is_same(2, called)
ship:orderDock(station2)
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(3, called)
assert.is_same(calledArg1, ship)
assert.is_same(calledArg2, station1)
Ship:withEvents() config.onUndocking is called when the ship undocks a station
local called = 0
local station = SpaceStation()
local ship = CpuShip()
Ship:withEvents(ship, {
onUndocking = function()
called = called + 1
end,
})
Cron.tick(1)
assert.is_same(0, called)
ship:orderDock(station)
ship:setDockedAt(station)
Cron.tick(1)
assert.is_same(0, called)
ship:orderIdle()
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(1, called)
-- it is only called once
Cron.tick(1)
assert.is_same(1, called)
ship:orderDock(station)
ship:setDockedAt(station)
Cron.tick(1)
assert.is_same(1, called)
-- it triggers again
ship:orderIdle()
ship:setDockedAt(nil)
Cron.tick(1)
assert.is_same(2, called)
Ship:withEvents() fails if a number is given instead of ship
assert.has_error(function()
Ship:withEvents(42)
end)
Ship:withEvents() includes events from ShipTemplateBased
-- just test onDestruction
local called = 0
local ship = CpuShip()
Ship:withEvents(ship, {
onDestruction = function()
called = called + 1
end,
})
Cron.tick(1)
assert.is_same(0, called)
ship:destroy()
Cron.tick(1)
assert.is_same(1, called)
Ship:withFleet()
Ship:withFleet() fails if ship already has a fleet
local ship = CpuShip()
local fleet = fleetMock({ship})
Ship:withFleet(ship, fleet)
assert.has_error(function() Ship:withFleet(ship, fleet) end)
Ship:withFleet() fails when no fleet is given
local ship = CpuShip()
assert.has_error(function() Ship:withFleet(ship, 42) end)
Ship:withFleet() fails when no ship is given
local ship = CpuShip()
local fleet = fleetMock({ship})
assert.has_error(function() Ship:withFleet(42, fleet) end)
Ship:withFleet() should create a ship with fleet
local ship = CpuShip()
local fleet = fleetMock({ship})
Ship:withFleet(ship, fleet)
assert.is_true(Ship:hasFleet(ship))
Ship:withFleet():getFleet()
Ship:withFleet():getFleet() returns the fleet
local ship = CpuShip()
local fleet = fleetMock({ship})
Ship:withFleet(ship, fleet)
assert.is_same(fleet, ship:getFleet())
Ship:withFleet():getFleetLeader()
Ship:withFleet():getFleetLeader() returns the leader of the fleet
local ship = CpuShip()
local fleet = fleetMock({ship})
fleet.getLeader = function(self) return ship end
Ship:withFleet(ship, fleet)
assert.is_same(ship, ship:getFleetLeader())
Ship:withFleet():isFleetLeader()
Ship:withFleet():isFleetLeader() returns true if ship is the fleet leader
local ship1 = CpuShip()
local ship2 = CpuShip()
local fleet = fleetMock({ship1, ship2})
fleet.getLeader = function(self) return ship1 end
Ship:withFleet(ship1, fleet)
Ship:withFleet(ship2, fleet)
assert.is_true(ship1:isFleetLeader())
assert.is_false(ship2:isFleetLeader())
Ship:withHelmsOfficer()
Ship:withHelmsOfficer() sets the helms officer
local person = personMock()
local ship = CpuShip()
Ship:withHelmsOfficer(ship, person)
assert.is_true(ship:hasCrewAtPosition("helms"))
assert.is_true(ship:hasHelmsOfficer())
assert.is_same(person, ship:getCrewAtPosition("helms"))
assert.is_same(person, ship:getHelmsOfficer())
Ship:withRelayOfficer()
Ship:withRelayOfficer() sets the relay officer
local person = personMock()
local ship = CpuShip()
Ship:withRelayOfficer(ship, person)
assert.is_true(ship:hasCrewAtPosition("relay"))
assert.is_true(ship:hasRelayOfficer())
assert.is_same(person, ship:getCrewAtPosition("relay"))
assert.is_same(person, ship:getRelayOfficer())
Ship:withScienceOfficer()
Ship:withScienceOfficer() sets the science officer
local person = personMock()
local ship = CpuShip()
Ship:withScienceOfficer(ship, person)
assert.is_true(ship:hasCrewAtPosition("science"))
assert.is_true(ship:hasScienceOfficer())
assert.is_same(person, ship:getCrewAtPosition("science"))
assert.is_same(person, ship:getScienceOfficer())
Ship:withWeaponsOfficer()
Ship:withWeaponsOfficer() sets the weapons officer
local person = personMock()
local ship = CpuShip()
Ship:withWeaponsOfficer(ship, person)
assert.is_true(ship:hasCrewAtPosition("weapons"))
assert.is_true(ship:hasWeaponsOfficer())
assert.is_same(person, ship:getCrewAtPosition("weapons"))
assert.is_same(person, ship:getWeaponsOfficer())
ShipTemplateBased
ShipTemplateBased:withComms()
ShipTemplateBased:withComms() can set a hail text
local hail = "Hello World"
local station = SpaceStation()
ShipTemplateBased:withComms(station, {hailText = hail})
assert.is_same(hail, station:getComms(player):getWhatNpcSays(station, player))
ShipTemplateBased:withComms() can set comms
local station = SpaceStation()
ShipTemplateBased:withComms(station, {comms = { commsScreenReplyMock(), commsScreenReplyMock(), commsScreenReplyMock()}})
assert.is_same(3, Util.size(station:getComms(player):getHowPlayerCanReact()))
ShipTemplateBased:withComms() causes hasComms() to be true
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.is_true(ShipTemplateBased:hasComms(station))
ShipTemplateBased:withComms() fails if comms is a number
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withComms(station, {comms = 42}) end)
ShipTemplateBased:withComms() fails if first argument is already a SpaceObject with comms
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() ShipTemplateBased:withComms(station) end)
ShipTemplateBased:withComms() fails if first parameter is a number
assert.has_error(function() ShipTemplateBased:withComms(4) end)
ShipTemplateBased:withComms() fails if one of the comms is not a comms
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withComms(station, {comms = { commsScreenReplyMock(), commsScreenReplyMock(), 42}}) end)
ShipTemplateBased:withComms() fails if second argument is not a table
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withComms(station, 42) end)
ShipTemplateBased:withComms() fails if the hailText is a number
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withComms(station, {hailText = 42})end)
ShipTemplateBased:withComms() sets a comms script
local station = SpaceStation()
local called = false
station.setCommsScript = function() called = true end
ShipTemplateBased:withComms(station)
assert.is_true(called)
ShipTemplateBased:withComms():addComms()
ShipTemplateBased:withComms():addComms() allows to be called with a reply
local station = SpaceStation()
ShipTemplateBased:withComms(station)
station:addComms(commsScreenReplyMock())
assert.is_same(1, Util.size(station:getComms(player):getHowPlayerCanReact()))
ShipTemplateBased:withComms():addComms() fails if a number is given as id
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() station:addComms(commsScreenReplyMock(), 42) end)
ShipTemplateBased:withComms():addComms() fails if no reply is given
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() station:addComms() end)
ShipTemplateBased:withComms():addComms() fails if reply is a number
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() station:addComms(42) end)
ShipTemplateBased:withComms():addComms() generates an id if none is set
local station = SpaceStation()
ShipTemplateBased:withComms(station)
local id = station:addComms(commsScreenReplyMock())
assert.not_nil(id)
assert.is_true(isString(id))
assert.not_same("", id)
ShipTemplateBased:withComms():addComms() uses a given id
local id = "foobar"
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.is_same(id, station:addComms(commsScreenReplyMock(), id))
ShipTemplateBased:withComms():getComms()
ShipTemplateBased:withComms():getComms() does not allow to manipulate internal state
local station = SpaceStation()
ShipTemplateBased:withComms(station, {comms = { commsScreenReplyMock() }})
local comms = station:getComms(player)
table.insert(comms:getHowPlayerCanReact(), commsScreenReplyMock())
assert.is_same(1, Util.size(station:getComms(player):getHowPlayerCanReact()))
ShipTemplateBased:withComms():getComms() fails if it is called with a number
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() station:getComms(42) end)
ShipTemplateBased:withComms():getComms() fails if it is called without argument
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() station:getComms() end)
ShipTemplateBased:withComms():getComms() returns all replies from the constructor and addComms()
local reply1 = commsScreenReplyMock()
local reply2 = commsScreenReplyMock()
local reply3 = commsScreenReplyMock()
local station = SpaceStation()
ShipTemplateBased:withComms(station, {comms = { reply1 }})
station:addComms(reply2)
station:addComms(reply3)
local comms = station:getComms(player)
assert.is_true(Comms:isScreen(comms))
assert.contains_value(reply1, comms:getHowPlayerCanReact())
assert.contains_value(reply2, comms:getHowPlayerCanReact())
assert.contains_value(reply3, comms:getHowPlayerCanReact())
ShipTemplateBased:withComms():overrideComms()
ShipTemplateBased:withComms():overrideComms() allows to override comms once
local station = SpaceStation()
ShipTemplateBased:withComms(station)
local screen = commsScreenMock()
station:overrideComms(screen, true)
assert.is_same(screen, station:getComms(player))
assert.not_same(screen, station:getComms(player))
ShipTemplateBased:withComms():overrideComms() allows to permanently override comms
local station = SpaceStation()
ShipTemplateBased:withComms(station)
local screen = commsScreenMock()
station:overrideComms(screen)
assert.is_same(screen:getWhatNpcSays(station, player), station:getComms(player):getWhatNpcSays(station, player))
assert.is_same(screen:getHowPlayerCanReact(), station:getComms(player):getHowPlayerCanReact())
ShipTemplateBased:withComms():overrideComms() allows to remove override
local station = SpaceStation()
ShipTemplateBased:withComms(station)
local screen = commsScreenMock()
station:overrideComms(screen)
assert.is_same(screen, station:getComms(player))
station:overrideComms(nil)
assert.not_same(screen, station:getComms(player))
ShipTemplateBased:withComms():overrideComms() fails if first argument is not a screen
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() station:overrideComms(42) end)
ShipTemplateBased:withComms():overrideComms() fails if second argument is not boolean
local station = SpaceStation()
ShipTemplateBased:withComms(station)
local screen = commsScreenMock()
assert.has_error(function() station:overrideComms(screen, 42) end)
ShipTemplateBased:withComms():removeComms()
ShipTemplateBased:withComms():removeComms() allows to remove a comms that has been added before
local station = SpaceStation()
local reply = commsScreenReplyMock()
ShipTemplateBased:withComms(station)
local id = station:addComms(reply)
station:addComms(commsScreenReplyMock())
station:addComms(commsScreenReplyMock())
station:removeComms(id)
assert.not_contains_value(reply, station:getComms(player):getHowPlayerCanReact())
ShipTemplateBased:withComms():removeComms() fails if a number is given instead of an id
local station = SpaceStation()
ShipTemplateBased:withComms(station)
assert.has_error(function() station:removeComms(42) end)
ShipTemplateBased:withComms():removeComms() fails silently if an invalid id is given
local station = SpaceStation()
ShipTemplateBased:withComms(station)
station:removeComms("does not exist")
assert.is_same({}, station:getComms(player):getHowPlayerCanReact())
ShipTemplateBased:withComms():setHailText()
ShipTemplateBased:withComms():setHailText() calls a set function and returns a string
local hail = "Hello World"
station:setHailText(function(callStation, callPlayer)
assert.is_same(station, callStation)
assert.is_same(player, callPlayer)
return hail
end)
assert.is_same(hail, station:getComms(player):getWhatNpcSays(station, player))
ShipTemplateBased:withComms():setHailText() calls a set function and returns nil
station:setHailText(function() end)
assert.is_same("", station:getComms(player):getWhatNpcSays(station, player))
ShipTemplateBased:withComms():setHailText() calls a set function and returns nil if the return value is a number
station:setHailText(function() return 42 end)
assert.is_same("", station:getComms(player):getWhatNpcSays(station, player))
ShipTemplateBased:withComms():setHailText() return nil if nil was set
station:setHailText(nil)
assert.is_same("", station:getComms(player):getWhatNpcSays(station, player))
ShipTemplateBased:withComms():setHailText() returns a set string
local hail = "Hello World"
station:setHailText(hail)
assert.is_same(hail, station:getComms(player):getWhatNpcSays(station, player))
ShipTemplateBased:withCrew()
ShipTemplateBased:withCrew() can be called multiple times to add different persons
local person1 = personMock()
local person2 = personMock()
local station = SpaceStation()
ShipTemplateBased:withCrew(station, {commander = person1})
ShipTemplateBased:withCrew(station, {relay = person2})
assert.is_same(person1, station:getCrewAtPosition("commander"))
assert.is_same(person2, station:getCrewAtPosition("relay"))
ShipTemplateBased:withCrew() can be called multiple times to override a position
local person1 = personMock()
local person2 = personMock()
local station = SpaceStation()
ShipTemplateBased:withCrew(station, {commander = person1})
ShipTemplateBased:withCrew(station, {commander = person2})
assert.is_same(person2, station:getCrewAtPosition("commander"))
ShipTemplateBased:withCrew() should create a crew
local station = SpaceStation()
ShipTemplateBased:withCrew(station)
assert.is_true(ShipTemplateBased:hasCrew(station))
ShipTemplateBased:withCrew() should fail when the position is not a person
local station = SpaceStation()
assert.has_error(function () ShipTemplateBased:withCrew(station, {captain = {}}) end)
ShipTemplateBased:withCrew() should fail when the position is not a string
local person = personMock()
local station = SpaceStation()
assert.has_error(function () ShipTemplateBased:withCrew(station, {person}) end)
ShipTemplateBased:withEvents()
ShipTemplateBased:withEvents() config.onBeingAttacked does not fail if the callback errors
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onBeingAttacked = function()
error("Boom")
end,
})
station:setHullMax(100)
station:setHull(100)
station:setShieldsMax(100)
station:setShields(100)
station:setPosition(0, 0)
enemy:setPosition(2000, 0)
Cron.tick(1)
station:setShields(90)
assert.not_has_error(function()
Cron.tick(1)
end)
ShipTemplateBased:withEvents() config.onBeingAttacked fails if onBeingAttacked is not a callback
local station = SpaceStation()
assert.has_error(function()
ShipTemplateBased:withEvents(station, { onBeingAttacked = 42})
end)
ShipTemplateBased:withEvents() config.onBeingAttacked is called after it has not received damage for 2 minutes
local called = 0
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onBeingAttacked = function()
called = called + 1
end,
})
station:setHullMax(100)
station:setHull(100)
station:setShieldsMax(100)
station:setShields(100)
station:setPosition(0, 0)
enemy:setPosition(2000, 0)
called = 0
Cron.tick(1)
assert.is_same(0, called)
station:setShields(90)
Cron.tick(1)
assert.is_same(1, called)
for i=1,120 do Cron.tick(1) end
station:setShields(80)
Cron.tick(1)
assert.is_same(2, called)
ShipTemplateBased:withEvents() config.onBeingAttacked is called when the shipTemplateBased looses hull and enemy is close
local called = 0
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onBeingAttacked = function()
called = called + 1
end,
})
station:setHullMax(100)
station:setHull(100)
station:setShieldsMax(100)
station:setShields(100)
station:setPosition(0, 0)
enemy:setPosition(2000, 0)
called = 0
Cron.tick(1)
assert.is_same(0, called)
station:setHull(90)
Cron.tick(1)
assert.is_same(1, called)
station:setHull(80)
Cron.tick(1)
assert.is_same(1, called)
ShipTemplateBased:withEvents() config.onBeingAttacked is called when the shipTemplateBased looses shield and enemy is close
local called = 0
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onBeingAttacked = function()
called = called + 1
end,
})
station:setHullMax(100)
station:setHull(100)
station:setShieldsMax(100)
station:setShields(100)
station:setPosition(0, 0)
enemy:setPosition(2000, 0)
called = 0
Cron.tick(1)
assert.is_same(0, called)
station:setShields(90)
Cron.tick(1)
assert.is_same(1, called)
station:setShields(80)
Cron.tick(1)
assert.is_same(1, called)
ShipTemplateBased:withEvents() config.onBeingAttacked is called with the shipTemplateBased
local station = SpaceStation()
local enemy = CpuShip()
local calledArg
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onBeingAttacked = function(arg1)
calledArg = arg1
end,
})
station:setHullMax(100)
station:setHull(100)
station:setShieldsMax(100)
station:setShields(100)
station:setPosition(0, 0)
enemy:setPosition(2000, 0)
Cron.tick(1)
station:setShields(90)
Cron.tick(1)
assert.is_same(station, calledArg)
ShipTemplateBased:withEvents() config.onBeingAttacked is not called when hull or shield are damaged, but there is no enemy close
local called = 0
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onBeingAttacked = function()
called = called + 1
end,
})
station:setHullMax(100)
station:setHull(100)
station:setShieldsMax(100)
station:setShields(100)
station:setPosition(0, 0)
enemy:setPosition(99999, 0)
called = 0
Cron.tick(1)
assert.is_same(0, called)
station:setHull(90)
Cron.tick(1)
assert.is_same(0, called)
station:setShields(90)
Cron.tick(1)
assert.is_same(0, called)
ShipTemplateBased:withEvents() config.onDestruction does not fail if the callback errors
local station = SpaceStation()
ShipTemplateBased:withEvents(station, {
onDestruction = function()
error("Boom")
end,
})
station:destroy()
assert.not_has_error(function()
Cron.tick(1)
end)
ShipTemplateBased:withEvents() config.onDestruction fails if onDestruction is not a callback
local station = SpaceStation()
assert.has_error(function()
ShipTemplateBased:withEvents(station, { onDestruction = 42})
end)
ShipTemplateBased:withEvents() config.onDestruction is called when the shipTemplateBased is destroyed
local called = 0
local station = SpaceStation()
ShipTemplateBased:withEvents(station, {
onDestruction = function()
called = called + 1
end,
})
Cron.tick(1)
assert.is_same(0, called)
station:destroy()
Cron.tick(1)
assert.is_same(1, called)
-- it is only called once
Cron.tick(1)
assert.is_same(1, called)
ShipTemplateBased:withEvents() config.onDestruction is called with the destroyed shipTemplateBased
local station = SpaceStation()
local calledArg
ShipTemplateBased:withEvents(station, {
onDestruction = function(arg)
calledArg = arg
end,
})
station:destroy()
Cron.tick(1)
assert.is_same(station, calledArg)
ShipTemplateBased:withEvents() config.onEnemyClear does not fail if the callback errors
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onEnemyClear = function()
error("Boom")
end,
})
station:setPosition(0, 0)
enemy:setPosition(1000, 0)
assert.not_has_error(function()
Cron.tick(1)
end)
ShipTemplateBased:withEvents() config.onEnemyClear does not trigger multiple times when multiple enemies enter and leave
local called = 0
local station = SpaceStation()
local enemy1 = CpuShip()
local enemy2 = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy1) < range or distance(self, enemy2) < range
end
ShipTemplateBased:withEvents(station, {
onEnemyClear = function()
called = called + 1
end,
})
-- don't trigger when enemies are outside scanner range
station:setPosition(0, 0)
enemy1:setPosition(99999, 0)
enemy2:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(0, called)
enemy1:setPosition(20000, 0)
enemy2:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(0, called)
-- do not trigger when one enemy leaves range
enemy2:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(0, called)
-- but trigger if both leave the range
enemy1:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(1, called)
ShipTemplateBased:withEvents() config.onEnemyClear does only trigger when an enemy moves out of range
local called = 0
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onEnemyClear = function()
called = called + 1
end,
})
-- don't trigger when enemy is outside scanner range
station:setPosition(0, 0)
enemy:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(0, called)
-- do not call when enemy moves in range
enemy:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(0, called)
-- does not call as long as enemy stays in range
Cron.tick(1)
assert.is_same(0, called)
-- calls when enemy leaves range
enemy:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(1, called)
-- does not trigger when enemy stays out of range
Cron.tick(1)
assert.is_same(1, called)
-- calls when enemy leaves range again
enemy:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(1, called)
enemy:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(2, called)
ShipTemplateBased:withEvents() config.onEnemyClear fails if onDestruction is not a callback
local station = SpaceStation()
assert.has_error(function()
ShipTemplateBased:withEvents(station, { onEnemyClear = 42})
end)
ShipTemplateBased:withEvents() config.onEnemyClear is called with the shipTemplateBased
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
local calledArg
ShipTemplateBased:withEvents(station, {
onEnemyClear = function(arg)
calledArg = arg
end,
})
station:setPosition(0, 0)
enemy:setPosition(20000, 0)
Cron.tick(1)
enemy:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(station, calledArg)
ShipTemplateBased:withEvents() config.onEnemyDetection does not fail if the callback errors
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onEnemyDetection = function()
error("Boom")
end,
})
station:setPosition(0, 0)
enemy:setPosition(1000, 0)
assert.not_has_error(function()
Cron.tick(1)
end)
ShipTemplateBased:withEvents() config.onEnemyDetection does not trigger multiple times when multiple enemies enter and leave
local called = 0
local station = SpaceStation()
local enemy1 = CpuShip()
local enemy2 = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy1) < range or distance(self, enemy2) < range
end
ShipTemplateBased:withEvents(station, {
onEnemyDetection = function()
called = called + 1
end,
})
-- don't trigger when enemies are outside scanner range
station:setPosition(0, 0)
enemy1:setPosition(99999, 0)
enemy2:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(0, called)
enemy1:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(1, called)
-- do not trigger when second enemy enters range
enemy2:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(1, called)
-- do not trigger when one enemy leaves and enters again
enemy1:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(1, called)
enemy1:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(1, called)
-- trigger again when ships reenter
enemy1:setPosition(99999, 0)
enemy2:setPosition(99999, 0)
Cron.tick(1)
enemy1:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(2, called)
ShipTemplateBased:withEvents() config.onEnemyDetection does only trigger when an enemy moves into range
local called = 0
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
ShipTemplateBased:withEvents(station, {
onEnemyDetection = function()
called = called + 1
end,
})
-- don't trigger when enemy is outside scanner range
station:setPosition(0, 0)
enemy:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(0, called)
-- call when enemy moves in range
enemy:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(1, called)
-- does not call as long as enemy stays in range
Cron.tick(1)
assert.is_same(1, called)
-- does not trigger when enemy leaves range
enemy:setPosition(99999, 0)
Cron.tick(1)
assert.is_same(1, called)
-- does trigger again when enemy reenters scanner range
enemy:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(2, called)
ShipTemplateBased:withEvents() config.onEnemyDetection fails if onDestruction is not a callback
local station = SpaceStation()
assert.has_error(function()
ShipTemplateBased:withEvents(station, { onEnemyDetection = 42})
end)
ShipTemplateBased:withEvents() config.onEnemyDetection is called with the shipTemplateBased
local station = SpaceStation()
local enemy = CpuShip()
station.areEnemiesInRange = function(self, range)
return distance(self, enemy) < range
end
local calledArg
ShipTemplateBased:withEvents(station, {
onEnemyDetection = function(arg)
calledArg = arg
end,
})
station:setPosition(0, 0)
enemy:setPosition(20000, 0)
Cron.tick(1)
assert.is_same(station, calledArg)
ShipTemplateBased:withEvents() fails if a number is given instead of shipTemplateBased
assert.has_error(function()
ShipTemplateBased:withEvents(42)
end)
ShipTemplateBased:withMissionBroker()
ShipTemplateBased:withMissionBroker() allows to set missions
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station, {missions = {missionWithBrokerMock(), missionWithBrokerMock(), missionWithBrokerMock()}})
assert.is_same(3, Util.size(station:getMissions()))
ShipTemplateBased:withMissionBroker() causes hasMissionBroker() to be true
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
assert.is_true(ShipTemplateBased:hasMissionBroker(station))
ShipTemplateBased:withMissionBroker() fails if any of the missions is not a mission with broker
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withMissionBroker(station, {missions = {missionMock}}) end)
ShipTemplateBased:withMissionBroker() fails if first argument is already a SpaceObject with broker
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
assert.has_error(function() ShipTemplateBased:withMissionBroker(station) end)
ShipTemplateBased:withMissionBroker() fails if first argument is not a SpaceObject
assert.has_error(function() ShipTemplateBased:withMissionBroker(42) end)
ShipTemplateBased:withMissionBroker() fails if missions is a number
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withMissionBroker(station, {missions = 42}) end)
ShipTemplateBased:withMissionBroker() fails if second argument is not a table
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withMissionBroker(station, 42) end)
ShipTemplateBased:withMissionBroker():addMission()
ShipTemplateBased:withMissionBroker():addMission() allows to add missions
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
station:addMission(missionWithBrokerMock())
assert.is_same(1, Util.size(station:getMissions()))
station:addMission(missionWithBrokerMock())
assert.is_same(2, Util.size(station:getMissions()))
station:addMission(missionWithBrokerMock())
assert.is_same(3, Util.size(station:getMissions()))
ShipTemplateBased:withMissionBroker():addMission() fails if the argument is a number
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
assert.has_error(function() station:addMission(42) end)
ShipTemplateBased:withMissionBroker():addMission() fails if the mission is not a brokerMission
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
assert.has_error(function() station:addMission(missionMock()) end)
ShipTemplateBased:withMissionBroker():getMissions()
ShipTemplateBased:withMissionBroker():getMissions() returns an empty table if no missions where added
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
assert.is_same(0, Util.size(station:getMissions()))
ShipTemplateBased:withMissionBroker():getMissions() returns any missions added via withMissionBroker() and addMission()
local station = SpaceStation()
local mission1 = missionWithBrokerMock()
local mission2 = missionWithBrokerMock()
ShipTemplateBased:withMissionBroker(station, {missions = {mission1}})
station:addMission(mission2)
local mission1Found = false
local mission2Found = false
for _, mission in pairs(station:getMissions()) do
if mission == mission1 then mission1Found = true end
if mission == mission2 then mission2Found = true end
end
assert.is_true(mission1Found)
assert.is_true(mission2Found)
ShipTemplateBased:withMissionBroker():getMissions() should not allow to manipulate the mission table
local station = SpaceStation()
local mission1 = missionWithBrokerMock()
local mission2 = missionWithBrokerMock()
ShipTemplateBased:withMissionBroker(station, {missions = {mission1}})
table.insert(station:getMissions(), mission2)
assert.is_same(1, Util.size(station:getMissions()))
ShipTemplateBased:withMissionBroker():hasMissions()
ShipTemplateBased:withMissionBroker():hasMissions() returns false if no missions where added
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
assert.is_false(station:hasMissions())
ShipTemplateBased:withMissionBroker():hasMissions() returns true if a mission has been added via addMission()
local station = SpaceStation()
local mission = missionWithBrokerMock()
ShipTemplateBased:withMissionBroker(station)
station:addMission(mission)
assert.is_true(station:hasMissions())
ShipTemplateBased:withMissionBroker():hasMissions() returns true if a mission has been added via withMissionBroker()
local station = SpaceStation()
local mission = missionWithBrokerMock()
ShipTemplateBased:withMissionBroker(station, {missions = {mission}})
assert.is_true(station:hasMissions())
ShipTemplateBased:withMissionBroker():removeMission()
ShipTemplateBased:withMissionBroker():removeMission() allows to remove a mission by its id
local station = SpaceStation()
local mission = missionWithBrokerMock()
ShipTemplateBased:withMissionBroker(station, {missions = {mission}})
assert.is_same(1, Util.size(station:getMissions()))
station:removeMission(mission:getId())
assert.is_same(0, Util.size(station:getMissions()))
ShipTemplateBased:withMissionBroker():removeMission() allows to remove a mission object
local station = SpaceStation()
local mission = missionWithBrokerMock()
ShipTemplateBased:withMissionBroker(station, {missions = {mission}})
assert.is_same(1, Util.size(station:getMissions()))
station:removeMission(mission)
assert.is_same(0, Util.size(station:getMissions()))
ShipTemplateBased:withMissionBroker():removeMission() fails if the argument is a number
local station = SpaceStation()
ShipTemplateBased:withMissionBroker(station)
assert.has_error(function() station:removeMission(42) end)
ShipTemplateBased:withMissionBroker():removeMission() fails silently if the mission is unknown
local station = SpaceStation()
local mission1 = missionWithBrokerMock()
local mission2 = missionWithBrokerMock()
ShipTemplateBased:withMissionBroker(station, {missions={mission1}})
station:removeMission(mission2)
assert.is_same(1, Util.size(station:getMissions()))
ShipTemplateBased:withStorageRooms()
ShipTemplateBased:withStorageRooms() canStoreProduct should return false if the product was not configured
assert.is_false(station:canStoreProduct(Product:new("Test Mock", {id="foo"})))
ShipTemplateBased:withStorageRooms() canStoreProduct should return true if the product was configured
assert.is_true(station:canStoreProduct(product))
ShipTemplateBased:withStorageRooms() causes hasStorage() to be true
assert.is_true(Ship:hasStorage(station))
ShipTemplateBased:withStorageRooms() it keeps constraints on the storage capacity without raising an error
station:modifyProductStorage(product, -9999)
assert.is_same(0, station:getProductStorage(product))
station:modifyProductStorage(product, 9999)
assert.is_same(1000, station:getProductStorage(product))
ShipTemplateBased:withStorageRooms() remembers which products where stored
station:modifyProductStorage(product, 100)
assert.is_same(100, station:getProductStorage(product))
station:modifyProductStorage(product, 42)
assert.is_same(142, station:getProductStorage(product))
station:modifyProductStorage(product, -100)
assert.is_same(42, station:getProductStorage(product))
ShipTemplateBased:withUpgradeBroker()
ShipTemplateBased:withUpgradeBroker() allows to set upgrades
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgradeMock(), upgradeMock(), upgradeMock()}})
assert.is_same(3, Util.size(station:getUpgrades()))
ShipTemplateBased:withUpgradeBroker() causes hasUpgradeBroker() to be true
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
assert.is_true(ShipTemplateBased:hasUpgradeBroker(station))
ShipTemplateBased:withUpgradeBroker() fails if any of the upgrades is not a upgrade with broker
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgradeMock}}) end)
ShipTemplateBased:withUpgradeBroker() fails if first argument is already a SpaceObject with broker
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
assert.has_error(function() ShipTemplateBased:withUpgradeBroker(station) end)
ShipTemplateBased:withUpgradeBroker() fails if first argument is not a SpaceObject
assert.has_error(function() ShipTemplateBased:withUpgradeBroker(42) end)
ShipTemplateBased:withUpgradeBroker() fails if second argument is not a table
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withUpgradeBroker(station, 42) end)
ShipTemplateBased:withUpgradeBroker() fails if upgrades is a number
local station = SpaceStation()
assert.has_error(function() ShipTemplateBased:withUpgradeBroker(station, {upgrades = 42}) end)
ShipTemplateBased:withUpgradeBroker():addUpgrade()
ShipTemplateBased:withUpgradeBroker():addUpgrade() allows to add upgrades
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
station:addUpgrade(upgradeMock())
assert.is_same(1, Util.size(station:getUpgrades()))
station:addUpgrade(upgradeMock())
assert.is_same(2, Util.size(station:getUpgrades()))
station:addUpgrade(upgradeMock())
assert.is_same(3, Util.size(station:getUpgrades()))
ShipTemplateBased:withUpgradeBroker():addUpgrade() fails if the argument is a number
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
assert.has_error(function() station:addUpgrade(42) end)
ShipTemplateBased:withUpgradeBroker():getUpgrades()
ShipTemplateBased:withUpgradeBroker():getUpgrades() returns an empty table if no upgrades where added
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
assert.is_same(0, Util.size(station:getUpgrades()))
ShipTemplateBased:withUpgradeBroker():getUpgrades() returns any upgrades added via withUpgradeBroker() and addUpgrade()
local station = SpaceStation()
local upgrade1 = upgradeMock()
local upgrade2 = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgrade1}})
station:addUpgrade(upgrade2)
local upgrade1Found = false
local upgrade2Found = false
for _, upgrade in pairs(station:getUpgrades()) do
if upgrade == upgrade1 then upgrade1Found = true end
if upgrade == upgrade2 then upgrade2Found = true end
end
assert.is_true(upgrade1Found)
assert.is_true(upgrade2Found)
ShipTemplateBased:withUpgradeBroker():getUpgrades() should not allow to manipulate the upgrade table
local station = SpaceStation()
local upgrade1 = upgradeMock()
local upgrade2 = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgrade1}})
table.insert(station:getUpgrades(), upgrade2)
assert.is_same(1, Util.size(station:getUpgrades()))
ShipTemplateBased:withUpgradeBroker():hasUpgrade()
ShipTemplateBased:withUpgradeBroker():hasUpgrade() raises an error on invalid arguments
local station = SpaceStation()
local upgrade = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgrade}})
assert.has_error(function() station:hasUpgrade(42) end)
assert.has_error(function() station:hasUpgrade(SpaceStation()) end)
assert.has_error(function() station:hasUpgrade(nil) end)
ShipTemplateBased:withUpgradeBroker():hasUpgrade() returns false if no upgrades where added
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
local upgrade = upgradeMock()
assert.is_false(station:hasUpgrade(upgrade))
ShipTemplateBased:withUpgradeBroker():hasUpgrade() returns true if a upgrade has been added via addUpgrade()
local station = SpaceStation()
local upgrade = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station)
station:addUpgrade(upgrade)
assert.is_true(station:hasUpgrade(upgrade))
assert.is_true(station:hasUpgrade(upgrade:getId()))
ShipTemplateBased:withUpgradeBroker():hasUpgrade() returns true if a upgrade has been added via withUpgradeBroker()
local station = SpaceStation()
local upgrade = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgrade}})
assert.is_true(station:hasUpgrade(upgrade))
assert.is_true(station:hasUpgrade(upgrade:getId()))
ShipTemplateBased:withUpgradeBroker():hasUpgrades()
ShipTemplateBased:withUpgradeBroker():hasUpgrades() returns false if no upgrades where added
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
assert.is_false(station:hasUpgrades())
ShipTemplateBased:withUpgradeBroker():hasUpgrades() returns true if a upgrade has been added via addUpgrade()
local station = SpaceStation()
local upgrade = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station)
station:addUpgrade(upgrade)
assert.is_true(station:hasUpgrades())
ShipTemplateBased:withUpgradeBroker():hasUpgrades() returns true if a upgrade has been added via withUpgradeBroker()
local station = SpaceStation()
local upgrade = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgrade}})
assert.is_true(station:hasUpgrades())
ShipTemplateBased:withUpgradeBroker():removeUpgrade()
ShipTemplateBased:withUpgradeBroker():removeUpgrade() allows to remove a upgrade by its id
local station = SpaceStation()
local upgrade = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgrade}})
assert.is_same(1, Util.size(station:getUpgrades()))
station:removeUpgrade(upgrade:getId())
assert.is_same(0, Util.size(station:getUpgrades()))
ShipTemplateBased:withUpgradeBroker():removeUpgrade() allows to remove a upgrade object
local station = SpaceStation()
local upgrade = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades = {upgrade}})
assert.is_same(1, Util.size(station:getUpgrades()))
station:removeUpgrade(upgrade)
assert.is_same(0, Util.size(station:getUpgrades()))
ShipTemplateBased:withUpgradeBroker():removeUpgrade() fails if the argument is a number
local station = SpaceStation()
ShipTemplateBased:withUpgradeBroker(station)
assert.has_error(function() station:removeUpgrade(42) end)
ShipTemplateBased:withUpgradeBroker():removeUpgrade() fails silently if the upgrade is unknown
local station = SpaceStation()
local upgrade1 = upgradeMock()
local upgrade2 = upgradeMock()
ShipTemplateBased:withUpgradeBroker(station, {upgrades={upgrade1}})
station:removeUpgrade(upgrade2)
assert.is_same(1, Util.size(station:getUpgrades()))
Station
Station:withMerchant()
Station:withMerchant() can use a function for the buying price
local station = SpaceStation()
Station:withStorageRooms(station, {
[product] = 1000
})
Station:withMerchant(station, {
[product] = { buyingPrice = function(self) return 42 end }
})
assert.is_same(42, station:getProductBuyingPrice(product))
Station:withMerchant() can use a function for the selling price
local station = SpaceStation()
Station:withStorageRooms(station, {
[product] = 1000
})
Station:withMerchant(station, {
[product] = { sellingPrice = function(self) return 42 end }
})
assert.is_same(42, station:getProductSellingPrice(product))
Station:withMerchant() does not buy above the buyingLimit
local station = SpaceStation()
Station:withStorageRooms(station, {
[product] = 1000
})
Station:withMerchant(station, {
[product] = { buyingPrice = 42, buyingLimit = 100 }
})
station:modifyProductStorage(product, -9999)
assert.is_same(100, station:getMaxProductBuying(product))
station:modifyProductStorage(product, 30)
assert.is_same(70, station:getMaxProductBuying(product))
station:modifyProductStorage(product, 670)
assert.is_same(0, station:getMaxProductBuying(product))
station:modifyProductStorage(product, 300)
assert.is_same(0, station:getMaxProductBuying(product))
Station:withMerchant() does not sell below the sellingLimit
local station = SpaceStation()
Station:withStorageRooms(station, {
[product] = 1000
})
Station:withMerchant(station, {
[product] = { sellingPrice = 42, sellingLimit = 100 }
})
station:modifyProductStorage(product, -9999)
assert.is_same(0, station:getMaxProductSelling(product))
station:modifyProductStorage(product, 30)
assert.is_same(0, station:getMaxProductSelling(product))
station:modifyProductStorage(product, 670)
assert.is_same(600, station:getMaxProductSelling(product))
station:modifyProductStorage(product, 300)
assert.is_same(900, station:getMaxProductSelling(product))
Station:withMerchant() player-dependent offers getMaxProductBuying() filters products
assert.not_nil(stationBuying:getMaxProductBuying(productForFriends, friendlyPlayer))
assert.is_nil(stationBuying:getMaxProductBuying(productForFriends, neutralPlayer))
Station:withMerchant() player-dependent offers getMaxProductSelling() filters products
assert.not_nil(stationSelling:getMaxProductSelling(productForFriends, friendlyPlayer))
assert.is_nil(stationSelling:getMaxProductSelling(productForFriends, neutralPlayer))
Station:withMerchant() player-dependent offers getProductBuyingPrice() filters products
assert.is_same(42, stationBuying:getProductBuyingPrice(productForFriends, friendlyPlayer))
assert.is_nil(stationBuying:getProductBuyingPrice(productForFriends, neutralPlayer))
Station:withMerchant() player-dependent offers getProductSellingPrice() filters products
assert.is_same(42, stationSelling:getProductSellingPrice(productForFriends, friendlyPlayer))
assert.is_nil(stationSelling:getProductSellingPrice(productForFriends, neutralPlayer))
Station:withMerchant() player-dependent offers getProductsBought() filters products
assert.contains_value(productForFriends, stationBuying:getProductsBought(friendlyPlayer))
assert.not_contains_value(productForFriends, stationBuying:getProductsBought(neutralPlayer))
Station:withMerchant() player-dependent offers getProductsSold() filters products
assert.contains_value(productForFriends, stationSelling:getProductsSold(friendlyPlayer))
assert.not_contains_value(productForFriends, stationSelling:getProductsSold(neutralPlayer))
Station:withMerchant() player-dependent offers isBuyingProduct() filters products
assert.is_true(stationBuying:isBuyingProduct(productForFriends, friendlyPlayer))
assert.is_false(stationBuying:isBuyingProduct(productForFriends, neutralPlayer))
Station:withMerchant() player-dependent offers isSellingProduct() filters products
assert.is_true(stationSelling:isSellingProduct(productForFriends, friendlyPlayer))
assert.is_false(stationSelling:isSellingProduct(productForFriends, neutralPlayer))
Station:withMerchant() throws an error if neither buying nor selling price is set
local station = SpaceStation()
Station:withStorageRooms(station, {
[product] = 1000
})
assert.has_error(function()
Station:withMerchant(station, {
[product] = {}
})
end)
Station:withMerchant() when configuring a bought and sold product causes hasMerchant() to be true
assert.is_true(Station:hasMerchant(station))
Station:withMerchant() when configuring a bought and sold product causes isBuyingProduct() to be true
assert.is_true(station:isBuyingProduct(product))
Station:withMerchant() when configuring a bought and sold product causes isSellingProduct() to be true
assert.is_true(station:isSellingProduct(product))
Station:withMerchant() when configuring a bought and sold product does not buy above the maxStorage
station:modifyProductStorage(product, -9999)
assert.is_same(1000, station:getMaxProductBuying(product))
station:modifyProductStorage(product, 300)
assert.is_same(700, station:getMaxProductBuying(product))
station:modifyProductStorage(product, 700)
assert.is_same(0, station:getMaxProductBuying(product))
Station:withMerchant() when configuring a bought and sold product does not sell below 0
station:modifyProductStorage(product, -9999)
assert.is_same(0, station:getMaxProductSelling(product))
station:modifyProductStorage(product, 300)
assert.is_same(300, station:getMaxProductSelling(product))
station:modifyProductStorage(product, 700)
assert.is_same(1000, station:getMaxProductSelling(product))
Station:withMerchant() when configuring a bought and sold product returns a list of bought products
assert.is_same({[product:getId()] = product}, station:getProductsBought(product))
Station:withMerchant() when configuring a bought and sold product returns a list of sold products
assert.is_same({[product:getId()] = product}, station:getProductsSold(product))
Station:withMerchant() when configuring a bought and sold product returns the correct buying price
assert.is_same(21, station:getProductBuyingPrice(product))
Station:withMerchant() when configuring a bought and sold product returns the correct selling price
assert.is_same(42, station:getProductSellingPrice(product))
Station:withMerchant() when configuring a bought product causes hasMerchant() to be true
assert.is_true(Station:hasMerchant(station))
Station:withMerchant() when configuring a bought product causes isBuyingProduct() to be true
assert.is_true(station:isBuyingProduct(product))
Station:withMerchant() when configuring a bought product causes isSellingProduct() to be false
assert.is_false(station:isSellingProduct(product))
Station:withMerchant() when configuring a bought product does not buy above the maxStorage
station:modifyProductStorage(product, -9999)
assert.is_same(1000, station:getMaxProductBuying(product))
station:modifyProductStorage(product, 300)
assert.is_same(700, station:getMaxProductBuying(product))
station:modifyProductStorage(product, 700)
assert.is_same(0, station:getMaxProductBuying(product))
Station:withMerchant() when configuring a bought product returns a list of bought products
assert.is_same({[product:getId()] = product}, station:getProductsBought(product))
Station:withMerchant() when configuring a bought product returns an empty list of sold products
assert.is_same({}, station:getProductsSold(product))
Station:withMerchant() when configuring a bought product returns nil for getMaxProductSelling
assert.is_nil(station:getMaxProductSelling(product))
Station:withMerchant() when configuring a bought product returns no sellingPrice
assert.is_nil(station:getProductSellingPrice(product))
Station:withMerchant() when configuring a bought product returns the correct buying price
assert.is_same(42, station:getProductBuyingPrice(product))
Station:withMerchant() when configuring a sold product causes hasMerchant() to be true
assert.is_true(Station:hasMerchant(station))
Station:withMerchant() when configuring a sold product causes isBuyingProduct() to be false
assert.is_false(station:isBuyingProduct(product))
Station:withMerchant() when configuring a sold product causes isSellingProduct() to be true
assert.is_true(station:isSellingProduct(product))
Station:withMerchant() when configuring a sold product does not sell below 0
station:modifyProductStorage(product, -9999)
assert.is_same(0, station:getMaxProductSelling(product))
station:modifyProductStorage(product, 300)
assert.is_same(300, station:getMaxProductSelling(product))
station:modifyProductStorage(product, 700)
assert.is_same(1000, station:getMaxProductSelling(product))
Station:withMerchant() when configuring a sold product returns a list of sold products
assert.is_same({[product:getId()] = product}, station:getProductsSold(product))
Station:withMerchant() when configuring a sold product returns an empty list of bought products
assert.is_same({}, station:getProductsBought(product))
Station:withMerchant() when configuring a sold product returns nil for getMaxProductBuying
assert.is_nil(station:getMaxProductBuying(product))
Station:withMerchant() when configuring a sold product returns no buyingPrice
assert.is_nil(station:getProductBuyingPrice(product))
Station:withMerchant() when configuring a sold product returns the correct selling price
assert.is_same(42, station:getProductSellingPrice(product))
Station:withProduction()
Station:withProduction() does not produce if any of the consumed products is not available
local station = SpaceStation()
Station:withStorageRooms(station, {
[power] = 1000,
[ore] = 1000,
[glue] = 1000,
[herring] = 1000,
})
Station:withProduction(station, {
{
productionTime = 1,
produces = {
{ product = herring, amount = 5 },
},
consumes = {
{ product = power, amount = 10 },
{ product = ore, amount = 10 },
{ product = glue, amount = 10 },
}
}
})
station:modifyProductStorage(power, 100)
station:modifyProductStorage(ore, 5)
station:modifyProductStorage(glue, 100)
Cron.tick(5)
Cron.tick(5)
Cron.tick(5)
assert.is_same(100, station:getProductStorage(power))
assert.is_same(5, station:getProductStorage(ore))
assert.is_same(100, station:getProductStorage(glue))
assert.is_same(0, station:getProductStorage(herring))
Station:withProduction() does not produce if there is no storage space for products left
local station = SpaceStation()
Station:withStorageRooms(station, {
[power] = 1000,
[herring] = 1000,
})
Station:withProduction(station, {
{
productionTime = 1,
produces = {
{ product = herring, amount = 5 },
},
consumes = {
{ product = power, amount = 10 },
}
}
})
station:modifyProductStorage(power, 1000)
station:modifyProductStorage(herring, 1000)
Cron.tick(5)
Cron.tick(5)
Cron.tick(5)
assert.is_same(1000, station:getProductStorage(power))
assert.is_same(1000, station:getProductStorage(herring))
Station:withProduction() does produce as long as there is any space left
local station = SpaceStation()
Station:withStorageRooms(station, {
[power] = 1000,
[herring] = 1000,
})
Station:withProduction(station, {
{
productionTime = 1,
produces = {
{ product = herring, amount = 5 },
},
consumes = {
{ product = power, amount = 10 },
}
}
})
station:modifyProductStorage(power, 1000)
station:modifyProductStorage(herring, 999)
Cron.tick(5)
Cron.tick(5)
Cron.tick(5)
assert.is_same(990, station:getProductStorage(power))
assert.is_same(1000, station:getProductStorage(herring))
Station:withProduction() produces products in a interval
local station = SpaceStation()
Station:withStorageRooms(station, {
[power] = 1000,
[herring] = 1000,
})
Station:withProduction(station, {
{
productionTime = 5,
produces = {
{ product = herring, amount = 5 },
},
consumes = {
{ product = power, amount = 5 },
}
}
})
station:modifyProductStorage(power, 100)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(100, station:getProductStorage(power))
assert.is_same(0, station:getProductStorage(herring))
Cron.tick(1)
assert.is_same(95, station:getProductStorage(power))
assert.is_same(5, station:getProductStorage(herring))
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_same(95, station:getProductStorage(power))
assert.is_same(5, station:getProductStorage(herring))
Cron.tick(1)
assert.is_same(90, station:getProductStorage(power))
assert.is_same(10, station:getProductStorage(herring))
Station:withProduction() produces with callbacks
local station = SpaceStation()
local wasProduced = false
Station:withStorageRooms(station, {
[power] = 1000,
})
Station:withProduction(station, {
{
productionTime = 5,
produces = function() wasProduced = true end,
consumes = {
{ product = power, amount = 10 },
}
}
})
station:modifyProductStorage(power, 1000)
Cron.tick(5)
assert.is_true(wasProduced)
assert.is_same(990, station:getProductStorage(power))
Station:withProduction() stops producing if station is destroyed
local station = SpaceStation()
local wasProduced = false
Station:withStorageRooms(station, {
[herring] = 1000,
})
Station:withProduction(station, {
{
productionTime = 1,
produces = function() wasProduced = true end,
consumes = {
{ product = herring, amount = 1 },
}
}
})
station:modifyProductStorage(herring, 1000)
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
station:destroy()
wasProduced = false
Cron.tick(1)
Cron.tick(1)
Cron.tick(1)
assert.is_false(wasProduced)
Tools
Tools:ensureComms()
Tools:ensureComms() aborts when player is destroyed
local station = SpaceStation()
Station:withComms(station)
station:setHailText("Hello World")
local blocker = SpaceStation()
Station:withComms(blocker)
blocker:setHailText("Blocking the connection")
local player = PlayerSpaceship()
player:commandOpenTextComm(blocker)
Tools:ensureComms(station, player)
Cron.tick(1)
player:destroy()
player:commandCloseTextComm()
Cron.tick(1)
assert.is_true(player:isCommsInactive())
Tools:ensureComms() aborts when sender is destroyed
local station = SpaceStation()
Station:withComms(station)
station:setHailText("Hello World")
local blocker = SpaceStation()
Station:withComms(blocker)
blocker:setHailText("Blocking the connection")
local player = PlayerSpaceship()
player:commandOpenTextComm(blocker)
Tools:ensureComms(station, player)
Cron.tick(1)
station:destroy()
player:commandCloseTextComm()
Cron.tick(1)
assert.is_true(player:isCommsInactive())
Tools:ensureComms() can open a communication channel
local station = SpaceStation()
Station:withComms(station)
station:setHailText("Hello World")
local player = PlayerSpaceship()
Tools:ensureComms(station, player)
assert.same("Hello World", player:getCurrentCommsText())
Tools:ensureComms() can send a plain message
local station = SpaceStation()
Station:withComms(station)
local player = PlayerSpaceship()
Tools:ensureComms(station, player, "Hello World")
assert.same("Hello World", player:getCurrentCommsText())
Tools:ensureComms() fails if no valid player is given
local station = SpaceStation()
Station:withComms(station)
station:setHailText("Hello World")
assert.has_error(function()
Tools:ensureComms(station, nil)
end)
assert.has_error(function()
Tools:ensureComms(station, 42)
end)
assert.has_error(function()
Tools:ensureComms(station, CpuShip())
end)
Tools:ensureComms() fails if no valid station is given
local player = PlayerSpaceship()
assert.has_error(function()
Tools:ensureComms(nil, player)
end)
assert.has_error(function()
Tools:ensureComms(42, player)
end)
assert.has_error(function()
-- does not have comms
Tools:ensureComms(SpaceStation(), player)
end)
Tools:ensureComms() fails if third parameter is invalid
local station = SpaceStation()
Station:withComms(station)
local player = PlayerSpaceship()
assert.has_error(function()
Tools:ensureComms(station, player, 42)
end)
assert.has_error(function()
Tools:ensureComms(station, player, function() end)
end)
Tools:ensureComms() is send as soon as the communication channel opens
local station = SpaceStation()
Station:withComms(station)
station:setHailText("Hello World")
local blocker = SpaceStation()
Station:withComms(blocker)
blocker:setHailText("Blocking the connection")
local player = PlayerSpaceship()
player:commandOpenTextComm(blocker)
Tools:ensureComms(station, player)
assert.same("Blocking the connection", player:getCurrentCommsText())
Cron.tick(1)
assert.same("Blocking the connection", player:getCurrentCommsText())
player:commandCloseTextComm()
Cron.tick(1)
assert.same("Hello World", player:getCurrentCommsText())
player:commandCloseTextComm()
Cron.tick(1)
assert.is_true(player:isCommsInactive())
Tools:storyComms()
Tools:storyComms() ends when player is destroyed
finally(function() Tools:endStoryComms() end)
local station = SpaceStation()
Station:withComms(station)
local player = PlayerSpaceship()
local comms = Comms:newScreen("screen one")
comms:addReply(Comms:newReply("reply one", function()
Tools:endStoryComms()
return Comms:newScreen("screen two")
end))
Tools:storyComms(station, player, comms)
assert.same("screen one", player:getCurrentCommsText())
-- screen one pops up again if comms is closed
player:commandCloseTextComm()
player:destroy()
Cron.tick(1)
-- comms stays closed
assert.is_true(player:isCommsInactive())
Tools:storyComms() ends when station is destroyed
finally(function() Tools:endStoryComms() end)
local station = SpaceStation()
Station:withComms(station)
local player = PlayerSpaceship()
local comms = Comms:newScreen("screen one")
comms:addReply(Comms:newReply("reply one", function()
Tools:endStoryComms()
return Comms:newScreen("screen two")
end))
Tools:storyComms(station, player, comms)
assert.same("screen one", player:getCurrentCommsText())
-- screen one pops up again if comms is closed
player:commandCloseTextComm()
station:destroy()
Cron.tick(1)
-- comms stays closed
assert.is_true(player:isCommsInactive())
Tools:storyComms() invalid calls fails when an other storyComms() is currently running
finally(function() Tools:endStoryComms() end)
Tools:storyComms(station, player, screen)
assert.has_error(function() Tools:storyComms(station, player, screen) end)
Tools:storyComms() invalid calls fails when called with station without comms
assert.has_error(function() Tools:storyComms(SpaceStation(), player, screen) end)
Tools:storyComms() invalid calls fails when called without player
assert.has_error(function() Tools:storyComms(station, nil, screen) end)
Tools:storyComms() invalid calls fails when called without screen
assert.has_error(function() Tools:storyComms(station, player, nil) end)
Tools:storyComms() invalid calls fails when called without station
assert.has_error(function() Tools:storyComms(nil, player, screen) end)
Tools:storyComms() pops up if it is closed before dialog finished
finally(function() Tools:endStoryComms() end)
local station = SpaceStation()
Station:withComms(station)
local player = PlayerSpaceship()
local comms = Comms:newScreen("screen one")
comms:addReply(Comms:newReply("reply one", function()
local screen2 = Comms:newScreen("screen two")
screen2:addReply(Comms:newReply("reply two", function()
Tools:endStoryComms()
return Comms:newScreen("screen three")
end))
return screen2
end))
Tools:storyComms(station, player, comms)
assert.same("screen one", player:getCurrentCommsText())
-- screen one pops up again if comms is closed
player:commandCloseTextComm()
Cron.tick(1)
assert.same("screen one", player:getCurrentCommsText())
player:selectComms("reply one")
assert.same("screen two", player:getCurrentCommsText())
player:commandCloseTextComm()
-- conversation starts at screen one again
player:commandCloseTextComm()
Cron.tick(1)
assert.same("screen one", player:getCurrentCommsText())
player:selectComms("reply one")
assert.same("screen two", player:getCurrentCommsText())
player:selectComms("reply two")
assert.same("screen three", player:getCurrentCommsText())
player:commandCloseTextComm()
Cron.tick(1)
-- comms stays closed
assert.is_true(player:isCommsInactive())
Tools:storyComms() valid call can be created
finally(function() Tools:endStoryComms() end)
Tools:storyComms(station, player, screen)
Tools:storyComms() valid call can be stopped and started again
finally(function() Tools:endStoryComms() end)
Tools:endStoryComms()
Tools:storyComms(station, player, screen)
Translator
Translator:inspect()
Translator:inspect() fails if translator is not a Translator
assert.has_error(function()
Translator:inspect(nil)
end)
assert.has_error(function()
Translator:inspect(42)
end)
assert.has_error(function()
Translator:inspect({})
end)
Translator:inspect() finds excessive translations
local translator = Translator:new()
translator:register("en", "say_hello", "Hello World")
translator:register("de", "say_hello", "Hallo Welt")
translator:register("de", "say_bye", "Tschau")
translator:useLocale("de")
local missingTranslations, excessiveTranslations = Translator:inspect(translator)
assert.is_same({}, missingTranslations)
assert.is_same({"say_bye"}, excessiveTranslations)
Translator:inspect() finds missing translations
local translator = Translator:new("en")
translator:register("en", "say_hello", "Hello World")
translator:register("en", "say_bye", "Good Bye")
translator:register("de", "say_bye", "Tschau")
translator:useLocale("de")
local missingTranslations, excessiveTranslations = Translator:inspect(translator)
assert.is_same({"say_hello"}, missingTranslations)
assert.is_same({}, excessiveTranslations)
Translator:inspect() sorts the result alphabethically by key
local translator = Translator:new()
translator:register("en", "say_a", "A")
translator:register("en", "say_b", "B")
translator:register("en", "say_c", "C")
translator:register("de", "say_d", "D")
translator:register("de", "say_e", "E")
translator:register("de", "say_f", "F")
translator:useLocale("de")
local missingTranslations, excessiveTranslations = Translator:inspect(translator)
assert.is_same({"say_a", "say_b", "say_c"}, missingTranslations)
assert.is_same({"say_d", "say_e", "say_f"}, excessiveTranslations)
Translator:new()
Translator:new() allows to set the default locale
local translator = Translator:new("de")
translator:register("en", "say_hello", "Hello World")
translator:register("de", "say_hello", "Hallo Welt")
assert.is_same("Hallo Welt", translator:translate("say_hello"))
translator:useLocale("en")
assert.is_same("Hello World", translator:translate("say_hello"))
translator:useLocale("de")
assert.is_same("Hallo Welt", translator:translate("say_hello"))
Translator:new() fails if defaultLocale is not a string
assert.has_error(function()
Translator:new(42)
end)
Translator:new() should basically work with a different locale
local translator = Translator:new()
assert.is_true(Translator:isTranslator(translator))
translator:register("say_hello", "Hello World")
translator:register("de", "say_hello", "Hallo Welt")
assert.is_same("Hello World", translator:translate("say_hello"))
translator:useLocale("de")
assert.is_same("Hallo Welt", translator:translate("say_hello"))
Translator:new() should work when only using one locale
local translator = Translator:new()
assert.is_true(Translator:isTranslator(translator))
translator:register("say_hello", "Hello World")
assert.is_same("Hello World", translator:translate("say_hello"))
Translator:new():register()
Translator:new():register() can take a table of translations should work with a different locale
local translator = Translator:new()
translator:register({
say_hello = "Hello World",
say_bye = "Goodbye",
})
translator:register("de", {
say_hello = "Hallo Welt",
say_bye = "Tschau",
})
translator:useLocale("de")
assert.is_same("Hallo Welt", translator:translate("say_hello"))
Translator:new():register() can take a table of translations should work with the default locale
local translator = Translator:new()
translator:register({
say_hello = "Hello World",
say_bye = "Goodbye",
})
assert.is_same("Hello World", translator:translate("say_hello"))
Translator:new():register() fails if key is missing
local translator = Translator:new()
assert.has_error(function()
translator:register()
end)
assert.has_error(function()
translator:register("say_hello", nil)
end)
Translator:new():register() fails if label is not a string or function
local translator = Translator:new()
assert.has_error(function()
translator:register("say_hello", 42)
end)
assert.has_error(function()
translator:register("say_hello", nil)
end)
Translator:new():register() fails if locale is not a string
local translator = Translator:new()
assert.has_error(function()
translator:register(42, "say_hello", "Hello World")
end)
assert.has_error(function()
translator:register(nil, "say_hello", "Hello World")
end)
Translator:new():translate()
Translator:new():translate() can use a function as translation
local translator = Translator:new()
translator:register("say_hello", function() return "Hello World" end)
assert.is_same("Hello World", translator:translate("say_hello"))
Translator:new():translate() gives the translator and all given arguments to translation function
local translator = Translator:new()
local t = translator.translate
local arg1 = Util.randomUuid()
local arg2 = Util.randomUuid()
local arg3 = Util.randomUuid()
local givenArg1, givenArg2, givenArg3
translator:register("say_hello", function(arg1, arg2, arg3)
givenArg1 = arg1
givenArg2 = arg2
givenArg3 = arg3
return "Hello World"
end)
assert.is_same("Hello World", t("say_hello", arg1, arg2, arg3))
assert.is_same(arg1, givenArg1)
assert.is_same(arg2, givenArg2)
assert.is_same(arg3, givenArg3)
Translator:new():translate() gives the translator and all given arguments to translation function
local translator = Translator:new()
local arg1 = Util.randomUuid()
local arg2 = Util.randomUuid()
local arg3 = Util.randomUuid()
local givenArg1, givenArg2, givenArg3
translator:register("say_hello", function(arg1, arg2, arg3)
givenArg1 = arg1
givenArg2 = arg2
givenArg3 = arg3
return "Hello World"
end)
assert.is_same("Hello World", translator:translate("say_hello", arg1, arg2, arg3))
assert.is_same(arg1, givenArg1)
assert.is_same(arg2, givenArg2)
assert.is_same(arg3, givenArg3)
Translator:new():translate() returns an empty string if a translation errors and all fallbacks error too
local translator = Translator:new()
translator:register("say_hello", function() error("Boom") end)
assert.is_same("", translator:translate("say_hello"))
Translator:new():translate() should allow to use short-hand
local translator = Translator:new()
local t = translator.translate
translator:register("say_hello", "Hello World")
assert.is_same("Hello World", t("say_hello"))
Translator:new():translate() should fail if key does not exist in translation or defaultLocale
local translator = Translator:new()
translator:register("say_hello", "Hello World")
assert.has_error(function()
translator:translate("mistyped")
end)
Translator:new():translate() should fail if key is not a string
local translator = Translator:new()
translator:register("say_hello", "Hello World")
assert.has_error(function()
translator:translate(42)
end, "Expected key to be a string, but got <number>42")
assert.has_error(function()
translator:translate({})
end, "Expected key to be a string, but got <table>(size: 0)")
Translator:new():translate() transliterates german characters into ASCII
local translator = Translator:new()
translator:register("umlaut", "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg")
translator:register("umlaut_func", function()
return "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg"
end)
assert.is_same("Falsches Ueben von Xylophonmusik quaelt jeden groesseren Zwerg", translator.translate("umlaut"))
assert.is_same("Falsches Ueben von Xylophonmusik quaelt jeden groesseren Zwerg", translator.translate("umlaut_func"))
Translator:new():translate() tries fallback locales if function does not return a string
local translator = Translator:new("en")
translator:useLocale("de")
translator:register("en", "say_hello", function() return "Hello World" end)
translator:register("de", "say_hello", function() return 42 end)
assert.is_same("Hello World", translator:translate("say_hello"))
Translator:new():translate() tries fallback locales if function errors
-- this behavior should not be problematic because translations should not have side-effects
local translator = Translator:new("en")
translator:useLocale("de")
translator:register("en", "say_hello", function() return "Hello World" end)
translator:register("de", "say_hello", function() return error("Boom") end)
assert.is_same("Hello World", translator:translate("say_hello"))
Translator:new():translate() uses a fallback when translation is not available
local translator = Translator:new()
assert.is_true(Translator:isTranslator(translator))
translator:register("say_hello", "Hello World")
translator:useLocale("de")
assert.is_same("Hello World", translator:translate("say_hello"))
Translator:new():translate() uses multiple fallbacks when translation is not available
local translator = Translator:new()
translator:useLocale("sp", "de")
translator:register("en", "test", "Hello World")
assert.is_same("Hello World", translator:translate("test"))
translator:register("de", "test", "Hallo Welt")
assert.is_same("Hallo Welt", translator:translate("test"))
translator:register("sp", "test", "Hola Mundo")
assert.is_same("Hola Mundo", translator:translate("test"))
Translator:new():useLocale()
Translator:new():useLocale() fails if any of the arguments is not a string
local translator = Translator:new()
assert.has_error(function()
translator:useLocale(42)
end)
assert.has_error(function()
translator:useLocale("en", "de", 42)
end)
Translator:new():useLocale() uses default locale if no argument is given
local translator = Translator:new()
translator:register("say_hello", "Hello World")
translator:useLocale()
assert.is_same("Hello World", translator:translate("say_hello"))
Translator:printInspection()
Translator:printInspection() look that there is no smoke
-- all good
local translator = Translator:new("en")
translator:register("en", "say_hello", "Hello World")
translator:register("de", "say_hello", "Hallo Welt")
translator:useLocale("de")
Translator:printInspection(translator)
-- only missing
local translator = Translator:new("en")
translator:register("en", "say_hello", "Hello World")
translator:register("en", "say_bye", "Good Bye")
translator:register("de", "say_bye", "Tschau")
translator:useLocale("de")
Translator:printInspection(translator)
-- only excessive
local translator = Translator:new()
translator:register("en", "say_hello", "Hello World")
translator:register("de", "say_hello", "Hallo Welt")
translator:register("de", "say_bye", "Tschau")
translator:useLocale("de")
Translator:printInspection(translator)
-- mixed
local translator = Translator:new()
translator:register("en", "say_a", "A")
translator:register("en", "say_b", "B")
translator:register("en", "say_c", "C")
translator:register("de", "say_d", "D")
translator:register("de", "say_e", "E")
translator:register("de", "say_f", "F")
translator:useLocale("de")
Translator:printInspection(translator)
Util
Util:addVector()
Util:addVector() has the x axis for 0 degree
local x, y = Util.addVector(1000, 0, 0, 1000)
assert.is_same(2000, math.floor(x))
assert.is_same(0, math.floor(y))
Util:addVector() it works counter clockwise
local x, y = Util.addVector(1000, 0, 90, 1000)
assert.is_same(1000, math.floor(x))
assert.is_same(1000, math.floor(y))
Util:addVector() it works with SpaceObject
local ship = CpuShip():setPosition(1337, 42)
local x, y = Util.addVector(ship, 90, 1000)
assert.is_same(1337, math.floor(x))
assert.is_same(1042, math.floor(y))
Util:addVector() it works with degrees
local x, y = Util.addVector(1000, 0, 180, 1000)
assert.is_same(0, math.floor(x))
assert.is_same(0, math.floor(y))
Util:addVector() returns the point when adding a vector of zero length
local x, y = Util.addVector(0, 0, 180, 0)
assert.is_same({0, 0}, {x, y})
Util:angleDiff()
Util:angleDiff() returns correct results
assert.is_same(20, Util.angleDiff(10, 30))
assert.is_same(-20, Util.angleDiff(30, 10))
assert.is_same(20, Util.angleDiff(350, 10))
assert.is_same(-20, Util.angleDiff(10, 350))
Util:angleFromVector()
Util:angleFromVector() has the x axis for 0 degree
local angle, distance = Util.angleFromVector(1000, 0)
assert.is_same(0, math.floor(angle))
assert.is_same(1000, math.floor(distance))
Util:angleFromVector() it works counter clockwise
local angle, distance = Util.angleFromVector(0, 1000)
assert.is_same(90, math.floor(angle))
assert.is_same(1000, math.floor(distance))
Util:angleFromVector() it works with degrees
local angle, distance = Util.angleFromVector(-1000, 0)
assert.is_same(180, math.floor(angle))
assert.is_same(1000, math.floor(distance))
Util:appendTables()
Util:appendTables() can merge three tables
local a = {1, 2}
local b = {3, 4}
local c = {5, 6}
local merged = Util.appendTables(a, b, c)
assert.is_same({1, 2, 3, 4, 5, 6}, merged)
-- ensure the original tables are not overridden
assert.not_same(a, merged)
assert.not_same(b, merged)
assert.not_same(c, merged)
Util:appendTables() does not remove duplicates
local a = {1, 3}
local b = {3, 7}
local merged = Util.appendTables(a, b)
assert.is_same({1, 3, 3, 7}, merged)
-- ensure the original tables are not overridden
assert.not_same(a, merged)
assert.not_same(b, merged)
Util:appendTables() fails if the first argument is not a numeric table
assert.has_error(function() Util.appendTables(42, {1}) end)
assert.has_error(function() Util.appendTables(nil, {1}) end)
Util:appendTables() fails if the second argument is not a table
assert.has_error(function() Util.appendTables({1}, 42) end)
Util:appendTables() returns a new table where all the items of all tables are present
local a = {1, 2}
local b = {3, 4}
local merged = Util.appendTables(a, b)
assert.is_same({1, 2, 3, 4}, merged)
-- ensure the original tables are not overridden
assert.not_same(a, merged)
assert.not_same(b, merged)
Util:deepCopy()
Util:deepCopy() should copy primitive types
local thing = {
foo = "bar",
baz = 42
}
local copied = Util.deepCopy(thing)
thing.foo = "fake"
thing.baz = 12
thing.blu = "some"
assert.equal("bar", copied.foo)
assert.equal(42, copied.baz)
assert.is_nil(copied.blu)
Util:deepCopy() should not copy objects from Empty Epsilon
-- Copying them would cause the object to exists twice in memory.
-- This would cause an inconsistent state and might cause the game to crash
-- because of access to invalid memory segments.
require "spec.mocks"
local thing = {
foo = "bar",
station = SpaceStation()
}
local copied = Util.deepCopy(thing)
thing.station.foo = "bar"
assert.same("bar", copied.station.foo)
Util:distanceToLineSegment()
Util:distanceToLineSegment() can use objects instead of positions
local start = CpuShip():setPosition(300, 0)
local stop = CpuShip():setPosition(1300, 0)
local point = CpuShip():setPosition(1300, 200)
assert.is_same(200, Util.distanceToLineSegment(300, 0, 1300, 0, 1300, 200))
assert.is_same(200, Util.distanceToLineSegment(start, 1300, 0, 1300, 200))
assert.is_same(200, Util.distanceToLineSegment(300, 0, stop, 1300, 200))
assert.is_same(200, Util.distanceToLineSegment(300, 0, 1300, 0, point))
assert.is_same(200, Util.distanceToLineSegment(start, stop, 1300, 200))
assert.is_same(200, Util.distanceToLineSegment(start, 1300, 0, point))
assert.is_same(200, Util.distanceToLineSegment(300, 0, stop, point))
assert.is_same(200, Util.distanceToLineSegment(start, stop, point))
Util:distanceToLineSegment() fails if any argument is not a number
assert.has_error(function() Util.distanceToLineSegment(0, 0, 1000, 0, 0, "") end)
assert.has_error(function() Util.distanceToLineSegment(0, 0, 1000, 0, "", 0) end)
assert.has_error(function() Util.distanceToLineSegment(0, 0, 1000, "", 0, 0) end)
assert.has_error(function() Util.distanceToLineSegment(0, 0, "fo", 0, 0, 0) end)
assert.has_error(function() Util.distanceToLineSegment(0, "", 1000, 0, 0, 0) end)
assert.has_error(function() Util.distanceToLineSegment("", 0, 1000, 0, 0, 0) end)
Util:distanceToLineSegment() fails if start and end are identical
assert.has_error(function() Util.distanceToLineSegment(0, 0, 0, 0, 0, 0) end)
assert.has_error(function() Util.distanceToLineSegment(100, 0, 100, 0, 0, 0) end)
Util:distanceToLineSegment() returns 0 if point is on the line segment
assert.is_same(0, Util.distanceToLineSegment(0, 0, 1000, 0, 0, 0))
assert.is_same(0, Util.distanceToLineSegment(0, 0, 1000, 0, 1000, 0))
assert.is_same(0, Util.distanceToLineSegment(0, 0, 1000, 0, 500, 0))
Util:distanceToLineSegment() returns distance if point is on the line, but outside the segment
assert.is_same(1000, Util.distanceToLineSegment(0, 0, 1000, 0, 2000, 0))
assert.is_same(500, Util.distanceToLineSegment(0, 0, 1000, 0, -500, 0))
Util:distanceToLineSegment() returns distance of a point from a line segment (when closest point is on the line)
assert.is_same(200, Util.distanceToLineSegment(
0, 0,
1000, 0,
500, 200
))
assert.is_same(200, Util.distanceToLineSegment(
0, 0,
1000, 0,
500, -200
))
-- the same shifted to the right
assert.is_same(200, Util.distanceToLineSegment(
300, 0,
1300, 0,
800, 200
))
assert.is_same(200, Util.distanceToLineSegment(
300, 0,
1300, 0,
800, -200
))
-- and now rotate the whole thing
for _, deg in pairs({30, 45, 60, 90, 120, 150}) do
deg = deg / 180 * math.pi
local rotate = function(x, y)
return math.cos(deg) * x - math.sin(deg) * y, math.sin(deg) * x + math.cos(deg) * y
end
local startX, startY = rotate(300, 0)
local endX, endY = rotate(1300, 0)
local x1, y1 = rotate(800, 200)
assert.is_same(200, Util.round(Util.distanceToLineSegment(startX, startY, endX, endY, x1, y1)))
local x2, y2 = rotate(800, -200)
assert.is_same(200, Util.round(Util.distanceToLineSegment(startX, startY, endX, endY, x2, y2)))
end
Util:distanceToLineSegment() returns distance of a point from a line segment (when closest point is the end)
assert.is_same(200, Util.distanceToLineSegment(300, 0, 1300, 0, 1300, 200))
assert.is_same(200, Util.distanceToLineSegment(300, 0, 1300, 0, 1300, -200))
Util:distanceToLineSegment() returns distance of a point from a line segment (when closest point is the start)
assert.is_same(200, Util.distanceToLineSegment(300, 0, 1300, 0, 300, 200))
assert.is_same(200, Util.distanceToLineSegment(300, 0, 1300, 0, 300, -200))
Util:heading()
Util:heading() goes clockwise
local one, two = CpuShip(), CpuShip()
one:setPosition(0, 0)
two:setPosition(-1000, 0)
assert.is_same(270, Util.heading(one, two))
two:setPosition(0, -1000)
assert.is_same(0, Util.heading(one, two))
two:setPosition(1000, 0)
assert.is_same(90, Util.heading(one, two))
Util:heading() raises an error if used incorrectly
assert.has_error(function()
Util.heading()
end)
assert.has_error(function()
Util.heading("foo")
end)
assert.has_error(function()
Util.heading(12, 13, 14)
end)
assert.has_error(function()
Util.heading(12, CpuShip())
end)
Util:heading() takes positive y axis as 180°
local one, two = CpuShip(), CpuShip()
one:setPosition(0, 0)
two:setPosition(0, 1000)
assert.is_same(180, Util.heading(one, two))
Util:heading() works with coordinates as first argument
local ship = CpuShip()
ship:setPosition(-1000, 0)
assert.is_same(270, Util.heading(0, 0, ship))
Util:heading() works with coordinates as second argument
local ship = CpuShip()
ship:setPosition(0, 0)
assert.is_same(270, Util.heading(ship, -1000, 0))
Util:heading() works with coordinates only
assert.is_same(270, Util.heading(0, 0, -1000, 0))
Util:isNumericTable()
Util:isNumericTable() returns false on table that contains mixed indices
assert.is_false(Util.isNumericTable({42, foo = "bar", 2, 3, "bar", baz = 42}))
Util:isNumericTable() returns false on table that only contains string indices
assert.is_false(Util.isNumericTable({one = 1, two = 2, three = 3}))
Util:isNumericTable() returns true on empty table
assert.is_true(Util.isNumericTable({}))
Util:isNumericTable() returns true on table that only contains numerical indices
assert.is_true(Util.isNumericTable({1, 2, 3, 4}))
Util:keys()
Util:keys() fails if no table is given
assert.has_error(function() Util.keys() end)
assert.has_error(function() Util.keys(42) end)
Util:keys() returns the keys of a table in an arbitrary order
local input = {
foo = "bar",
baz = "blubb",
number = 42,
}
local output = Util.keys(input)
assert.is_table(output)
assert.is_same(3, Util.size(output))
assert.contains_value("foo", output)
assert.contains_value("baz", output)
assert.contains_value("number", output)
assert.is_true(Util.isNumericTable(output))
Util:map()
Util:map() fails when first argument is not a table
assert.has_error(function()
Util.map(42, function() end)
end)
Util:map() fails when second argument is not a function
assert.has_error(function()
Util.map({}, 42)
end)
Util:map() makes the keys available in the function
local input = {a=1, b=2, c=3}
local output = Util.map(input, function(value, key) return key .. value end)
assert.is_same({a="a1", b="b2", c="c3"}, output)
assert.not_same(input, output) -- it should not change in-place
Util:map() maps a numberic table
local input = {1, 2, 3, 4}
local output = Util.map(input, function(value) return value+1 end)
assert.is_same({2, 3, 4, 5}, output)
assert.not_same(input, output) -- it should not change in-place
Util:map() maps values and retains keys
local input = {a=1, b=2, c=3}
local output = Util.map(input, function(value) return value+1 end)
assert.is_same({a=2, b=3, c=4}, output)
assert.not_same(input, output) -- it should not change in-place
Util:mergeTables()
Util:mergeTables() can merge three tables
local a = {a = 1, b = 2}
local b = {b = 3, c = 4}
local c = {c = 5, d = 6}
local merged = Util.mergeTables(a, b, c)
assert.is_same({a = 1, b = 3, c = 5, d = 6}, merged)
-- ensure the original tables are not overridden
assert.not_same(a, merged)
assert.not_same(b, merged)
assert.not_same(c, merged)
Util:mergeTables() fails if the first argument is not a table
assert.has_error(function() Util.mergeTables(42, {a = 1}) end)
Util:mergeTables() fails if the second argument is not a table
assert.has_error(function() Util.mergeTables({a = 1}, 42) end)
Util:mergeTables() returns a new table where all items and from the second are present
local a = {a = 1, b = 2}
local b = {c = 3, d = 4}
local merged = Util.mergeTables(a, b)
assert.is_same({a = 1, b = 2, c = 3, d = 4}, merged)
-- ensure the original tables are not overridden
assert.not_same(a, merged)
assert.not_same(b, merged)
Util:mergeTables() the second table overrides the first one
local a = {a = 1, b = 2}
local b = {b = 3, c = 4}
local merged = Util.mergeTables(a, b)
assert.is_same({a = 1, b = 3, c = 4}, merged)
Util:mkString()
Util:mkString() should return a string if lastSeparator is left out
local table = { "one", "two", "three" }
assert.equal(Util.mkString(table, ", "), "one, two, three")
Util:mkString() with lastSeparator parameter should fail when using an associative table
local table = { a = "one", c = "two", b = "three" }
assert.has_error(function()
Util.mkString(table, ", ", " and ")
end)
Util:mkString() with lastSeparator parameter should return a string for a single value
local table = { "one" }
assert.equal(Util.mkString(table, ", ", " and "), "one")
Util:mkString() with lastSeparator parameter should return a string for three values
local table = { "one", "two", "three" }
assert.equal(Util.mkString(table, ", ", " and "), "one, two and three")
Util:mkString() with lastSeparator parameter should return a string for two values
local table = { "one", "two" }
assert.equal(Util.mkString(table, ", ", " and "), "one and two")
Util:mkString() with lastSeparator parameter should return an empty string if table is empty
local table = {}
assert.equal(Util.mkString(table, ", ", " and "), "")
Util:onVector()
Util:onVector() returns a point between point 1 and point 2
local x, y = Util.onVector(1000, 2000, 3000, 4000, 0.5)
assert.is_same({2000, 3000}, {x, y})
Util:onVector() returns point 1 when ratio is 0
local x, y = Util.onVector(1000, 2000, 3000, 4000, 0)
assert.is_same({1000, 2000}, {x, y})
Util:onVector() returns point 2 when ratio is 1
local x, y = Util.onVector(1000, 2000, 3000, 4000, 1)
assert.is_same({3000, 4000}, {x, y})
Util:onVector() works with an object and coordinates
local ship = CpuShip():setPosition(1000, 2000)
local x, y = Util.onVector(ship, 3000, 4000, 0.5)
assert.is_same({2000, 3000}, {x, y})
Util:onVector() works with coordinate and an object
local ship = CpuShip():setPosition(3000, 4000)
local x, y = Util.onVector(1000, 2000, ship, 0.5)
assert.is_same({2000, 3000}, {x, y})
Util:onVector() works with two objects
local ship1 = CpuShip():setPosition(1000, 2000)
local ship2 = CpuShip():setPosition(3000, 4000)
local x, y = Util.onVector(ship1, ship2, 0.5)
assert.is_same({2000, 3000}, {x, y})
Util:random()
Util:random() allows to filter elements
local thing1 = { foo = "bar" }
local thing2 = { baz = "bar" }
local thing3 = { blu = "bla" }
local testDummy = {thing1, thing2, thing3 }
local thing1Seen = false
local thing2Seen = false
local thing3Seen = false
for i=1,16,1 do
local result = Util.random(testDummy, function(k, v)
return k ~= 3
end)
if result == thing1 then thing1Seen = true elseif result == thing2 then thing2Seen = true elseif result == thing3 then thing3Seen = true end
end
assert.is_true(thing1Seen)
assert.is_true(thing2Seen)
assert.is_false(thing3Seen)
Util:random() returns all items from the list at random
local thing1 = { foo = "bar" }
local thing2 = { baz = "bar" }
local testDummy = {thing1, thing2 }
local thing1Seen = false
local thing2Seen = false
for i=1,16,1 do
local result = Util.random(testDummy)
if result == thing1 then thing1Seen = true elseif result == thing2 then thing2Seen = true end
end
assert.is_true(thing1Seen)
assert.is_true(thing2Seen)
Util:random() returns an element from a non-empty list with index
local thing = { foo = "bar" }
assert.is.equal(Util.random({foo = thing}), thing)
Util:random() returns an element from a non-empty list with numerical index
local thing = { foo = "bar" }
assert.is.equal(Util.random({thing}), thing)
Util:random() returns nil if list is empty
assert.is_nil(Util.random({}))
Util:random() returns nil if the filter does not leave any item
assert.is_nil(Util.random({1, 2, 3, 4}, function() return false end))
Util:randomSort()
Util:randomSort() fails if no table is given
assert.has_error(function() Util.randomSort() end)
Util:randomSort() randomly sorts a named list
local input = {a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, k=11, l=12, m=13, n=14, o=15, p=16}
local output = Util.randomSort(input)
assert.is_table(output)
assert.is_same(16, Util.size(output))
assert.contains_value(8, output)
assert.is_true(Util.isNumericTable(output))
assert.not_same(input, output)
Util:randomSort() randomly sorts a numeric list
local input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
local output = Util.randomSort(input)
assert.is_table(output)
assert.is_same(16, Util.size(output))
assert.contains_value(8, output)
assert.is_true(Util.isNumericTable(output))
assert.not_same(input, output)
Util:randomSort() returns different results each time in a numeric list
local input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
local output1 = Util.randomSort(input)
local output2 = Util.randomSort(input)
assert.not_same(output1, output2)
Util:randomSort() returns different results each time in a numeric list
local input = {a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, k=11, l=12, m=13, n=14, o=15, p=16}
local output1 = Util.randomSort(input)
local output2 = Util.randomSort(input)
assert.not_same(output1, output2)
Util:randomUuid()
Util:randomUuid() should not return the same uuid twice
local uuid = Util.randomUuid()
local uuid2 = Util.randomUuid()
assert.not_equal(uuid, uuid2)
Util:randomUuid() should return a 16 digit hex
local uuid = Util.randomUuid()
assert.not_nil(uuid:match("^([0-9a-f]+)$"))
assert.equal(uuid:len(), 16)
Util:round()
Util:round() can correctly round to a base of 10
assert.is_same(0, Util.round(0, 10))
assert.is_same(0, Util.round(1, 10))
assert.is_same(0, Util.round(4, 10))
assert.is_same(0, Util.round(4.9, 10))
assert.is_same(10, Util.round(5.1, 10))
assert.is_same(10, Util.round(6, 10))
assert.is_same(10, Util.round(9, 10))
assert.is_same(10, Util.round(10, 10))
Util:round() can round to different bases
assert.is_same(40, Util.round(42, 5))
assert.is_same(42, Util.round(42, 7))
assert.is_same(40, Util.round(42, 10))
Util:round() rounds mathematically correct for negative numbers
assert.is_same(-42, Util.round(-42))
assert.is_same(-42, Util.round(-42.1))
assert.is_same(-42, Util.round(-42.4))
assert.is_same(-42, Util.round(-42.49))
-- because of float magic do not test -42.5 directly
assert.is_same(-43, Util.round(-42.51))
assert.is_same(-43, Util.round(-42.6))
assert.is_same(-43, Util.round(-42.9))
Util:round() rounds mathematically correct for positive numbers
assert.is_same(42, Util.round(42))
assert.is_same(42, Util.round(42.1))
assert.is_same(42, Util.round(42.4))
assert.is_same(42, Util.round(42.49))
-- because of float magic do not test 42.5 directly
assert.is_same(43, Util.round(42.51))
assert.is_same(43, Util.round(42.6))
assert.is_same(43, Util.round(42.9))
Util:size()
Util:size() correctly determines size of a table with mixed indices
assert.is.same(Util.size({
foo = 42,
bar = "baz",
baz = {},
42,
}), 4)
Util:size() correctly determines size of a table with numerical index
assert.is.same(Util.size({42, "foobar", {}}), 3)
Util:size() correctly determines size of a table with object index
assert.is.same(Util.size({
foo = 42,
bar = "baz",
baz = {}
}), 3)
Util:size() correctly determines size of an empty table
assert.is.same(Util.size({}), 0)
Util:vectorFromAngle()
Util:vectorFromAngle() has the x axis for 0 degree
local x, y = Util.vectorFromAngle(0, 1000)
assert.is_same(1000, math.floor(x))
assert.is_same(0, math.floor(y))
Util:vectorFromAngle() it works counter clockwise
local x, y = Util.vectorFromAngle(90, 1000)
assert.is_same(0, math.floor(x))
assert.is_same(1000, math.floor(y))
Util:vectorFromAngle() it works with degrees
local x, y = Util.vectorFromAngle(180, 1000)
assert.is_same(-1000, math.floor(x))
assert.is_same(0, math.floor(y))
Other
typeInspect()
typeInspect() prints PlayerSpaceShip
local player = PlayerSpaceship():setCallSign("Artemis")
assert.is_same("<PlayerSpaceship>\"Artemis\"", typeInspect(player))
player:destroy()
assert.is_same("<PlayerSpaceship>", typeInspect(player))
typeInspect() prints boolean
assert.is_same("<bool>true", typeInspect(true))
assert.is_same("<bool>false", typeInspect(false))
typeInspect() prints function
assert.is_same("<function>", typeInspect(function() end))
typeInspect() prints nil
assert.is_same("<nil>", typeInspect(nil))
typeInspect() prints number
assert.is_same("<number>42", typeInspect(42))
assert.is_same("<number>-123", typeInspect(-123))
assert.is_same("<number>12.3456", typeInspect(12.3456))
typeInspect() prints strings
assert.is_same("<string>\"foobar\"", typeInspect("foobar"))
assert.is_same("<string>\"This is a very long sting that...\"", typeInspect("This is a very long sting that should be cut off in order to not be too long."))
assert.is_same("<string>\"\"", typeInspect(""))
typeInspect() prints tables with numeric key
assert.is_same("<table>(size: 2)", typeInspect({"foo", "bar"}))
typeInspect() prints tables with string key
assert.is_same("<table>(size: 2)", typeInspect({foo = "bar", baz = 42}))
userCallback()
userCallback() calls the callback and returns the args if no error occurs
local a,b,c = userCallback(function(arg1, arg2, arg3) return arg1 * 7, arg2 .. "bar", not arg3 end, 6, "foo", true)
assert.is_same(42, a)
assert.is_same("foobar", b)
assert.is_false(c)
userCallback() logs an error and returns nil if an error occurs
withLogCatcher(function(logs)
local result = userCallback(function() error("Fail") end)
assert.is_nil(result)
assert.is_not_nil(logs:popLastError())
end)
userCallback() logs an error if an invalid function is given
withLogCatcher(function(logs)
local result = userCallback("foobar")
assert.is_nil(result)
assert.is_not_nil(logs:popLastError())
end)
userCallback() returns if no function is given
withLogCatcher(function(logs)
local result = userCallback(nil)
assert.is_nil(result)
assert.is_nil(logs:popLastError())
end)