Skip to content

Commit

Permalink
tickets: convert to using the Check class
Browse files Browse the repository at this point in the history
The Check class provides the ability to embed documentation that can be
auto generated. The class also provides a wide verity of types it
already checks for without having to write these check functions over
and over again.
  • Loading branch information
jtoppins committed Mar 20, 2023
1 parent e5154bf commit 4878443
Showing 1 changed file with 159 additions and 96 deletions.
255 changes: 159 additions & 96 deletions src/dct/systems/tickets.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,102 +3,182 @@
--- Defines the accounting of a ticket system.

require("math")
local class = require("libs.class")
local class = require("libs.namedclass")
local utils = require("libs.utils")
local Timer = require("dct.libs.Timer")
local Marshallable = require("dct.libs.Marshallable")
local Command = require("dct.libs.Command")
local Check = require("dct.templates.checkers.Check")
local UPDATE_TIME = 120

local function checkvalue(keydata, tbl)
if tbl[keydata.name] >= 0 then
return true
end
return false
end
local difficulty = {
["EASY"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 0.5,
["modifier_reward"] = 1.5,
},
["NORMAL"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 1.0,
["modifier_reward"] = 1.0,
},
["HARD"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 1.5,
["modifier_reward"] = 0.5,
},
["REALISTIC"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 1.0,
["modifier_reward"] = 0,
},
["CUSTOM"] = {},
}

local function checkdifficulty(keydata, tbl)
local val = string.lower(tbl[keydata.name])
local settings = {
["easy"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 0.5,
["modifier_reward"] = 1.5,
local CheckSide = class("CheckSide", Check)
function CheckSide:__init()
Check.__init(self, "Coalition Options", {
["tickets"] = {
["type"] = Check.valuetype.UINT,
["description"] = [[
Number of tickets the side's ticket pool starts with. The Neutral
faction can start with zero tickets all others must have above zero.]],
},
["normal"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 1.0,
["modifier_reward"] = 1.0,
["player_cost"] = {
["type"] = Check.valuetype.UINT,
["default"] = 1,
["description"] = [[
Defines the cost of each player slot.]],
},
["hard"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 1.5,
["modifier_reward"] = 0.5,
["modifier_reward"] = {
["type"] = Check.valuetype.UINT,
["default"] = 1,
["description"] = [[
Defines the multiplicative modifier that is applied to all rewards the
given faction receives.]],
},
["realistic"] = {
["player_cost"] = 1.0,
["modifier_loss"] = 1.0,
["modifier_reward"] = 0,
["modifier_loss"] = {
["type"] = Check.valuetype.UINT,
["default"] = 1,
["description"] = [[
Defines the multiplicative modifier that is applied to all losses the
given faction takes.]],
},
["difficulty"] = {
["type"] = Check.valuetype.TABLEKEYS,
["values"] = difficulty,
["default"] = difficulty.CUSTOM,
["description"] = [[
Defines some predefined settings for player_cost, modifier_reward, and
modifier_loss. If this is set to anything other than `custom` any
explicitly defined values for player_cost, etc will be overwritten.
%VALUES%]],
},
["custom"] = {
["win_message"] = {
["type"] = Check.valuetype.STRING,
["default"] = "winner",
["description"] = [[
]],
},
}
}, [[Each coalition definition is required and can have the
following possible options.]])
end

if settings[val] == nil then
return false
function CheckSide:check(data)
local ok, key, msg = Check.check(self, data)

if not ok then
return false, key, msg
end
for k, v in pairs(settings[val]) do
tbl[k] = v

data.start = data.tickets
data.win_message = string.gsub(data.win_message, '["]', "\\\"")
for k, v in pairs(data.difficulty) do
data[k] = v
end

return true
end

local function sanatize(keydata, tbl)
tbl[keydata.name] = string.gsub(tbl[keydata.name], '["]', "\\\"")
local CheckGoals = class("CheckGoals", Check)
function CheckGoals:__init()
Check.__init(self, "Goals", {
["time"] = {
["default"] = -1,
["type"] = Check.valuetype.INT,
["description"] = [[
The total time, in seconds, the campaign will run before a winner is
determined and a new state is generated. Set to zero to disable the
timer.]],
},
["red"] = {
["type"] = Check.valuetype.TABLE,
["description"] = [[
Table defining the RED coalition's starting tickets, difficulity, and win
message.]],
},
["blue"] = {
["type"] = Check.valuetype.TABLE,
["description"] = [[
Table defining the BLUE coalition's starting tickets, difficulity, and win
message.]],
},
["neutral"] = {
["type"] = Check.valuetype.TABLE,
["description"] = [[
Table defining the NEUTRAL coalition's starting tickets, difficulity, and
win message.]],
},
}, [[
Theater goals define the way in which DCT will evaluate the completion
of the campaign. It is a simple ticket system much like what is present
in many AAA FPS titles.
An Example:
**file location:** `<theater-root>/theater.goals`
```lua
time = 43200 -- 12 hours in seconds
blue = {
tickets = 100,
player_cost = 1,
modifier_reward = 0.5,
modifier_loss = 0.2,
}
neutral = {
tickets = 0,
}
red = {
tickets = 200,
difficulty = "easy",
}
```]])
end

local function checkside(keydata, tbl)
local keys = {
{
["name"] = "tickets",
["type"] = "number",
["check"] = checkvalue,
}, {
["name"] = "player_cost",
["type"] = "number",
["check"] = checkvalue,
["default"] = 1,
}, {
["name"] = "modifier_reward",
["type"] = "number",
["check"] = checkvalue,
["default"] = 1,
}, {
["name"] = "modifier_loss",
["type"] = "number",
["check"] = checkvalue,
["default"] = 1,
}, {
["name"] = "difficulty",
["type"] = "string",
["check"] = checkdifficulty,
["default"] = "custom",
}, {
["name"] = "win_message",
["type"] = "string",
["check"] = sanatize,
["default"] = "winner",
}
}
function CheckGoals:check(data)
local ok, key, msg = Check.check(self, data)

tbl[keydata.name].path = tbl.path
utils.checkkeys(keys, tbl[keydata.name])
tbl[keydata.name].path = nil
tbl[keydata.name].start = tbl[keydata.name].tickets
if not ok then
return false, key, msg
end

local checkside = CheckSide()

for _, side in pairs({"red", "blue", "neutral"}) do
ok, key, msg = checkside:check(data[side])

if not ok then
return false, side.."."..key, msg
end
end
return true
end

local Tickets = class(Marshallable)
local Tickets = class("Tickets", Marshallable)
function Tickets:__init(theater)
Marshallable.__init(self)
self.cfgfile = dct.settings.server.theaterpath..utils.sep..
Expand Down Expand Up @@ -146,30 +226,13 @@ end

function Tickets:readconfig()
local goals = utils.readlua(self.cfgfile)
local keys = {
{
["name"] = "time",
["type"] = "number",
["check"] = checkvalue,
["default"] = -1
}, {
["name"] = "red",
["type"] = "table",
["check"] = checkside,
}, {
["name"] = "blue",
["type"] = "table",
["check"] = checkside,
}, {
["name"] = "neutral",
["type"] = "table",
["check"] = checkside,
}
}

goals.path = self.cfgfile
utils.checkkeys(keys, goals)
goals.path = nil
local checker = CheckGoals()
local ok, key, msg = checker:check(goals)

if not ok then
error(string.format("invalid `%s` %s; file: %s",
tostring(key), tostring(msg), tostring(self.cfgfile)))
end

if goals.time > 0 then
self.timer = Timer(goals.time, timer.getAbsTime)
Expand Down

0 comments on commit 4878443

Please sign in to comment.