mirror of
https://github.com/akaAgar/the-universal-mission-for-dcs-world.git
synced 2026-08-17 03:11:21 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
-- ====================================================================================
|
||||
-- TUM.PLAYERCAREER - HANDLES THE PERSISTENT PILOT CAREER IN SINGLE-PLAYER MISSIONS
|
||||
-- ====================================================================================
|
||||
-- (local const) MAX_RIBBONS
|
||||
-- (local const) MEDAL_BOX_DISPLAY_TIME
|
||||
-- (local const) OBJECTIVES_PER_RIBBON
|
||||
-- (local const) MEDALS
|
||||
-- (local const) RANKS
|
||||
-- (local table) careerStats
|
||||
-- (local) fixIncompleteStats()
|
||||
-- (local) getHighestMedal()
|
||||
-- (local) getRibbonCount()
|
||||
-- TUM.playerCareer.awardScore(score, objectives)
|
||||
-- TUM.playerCareer.createMenu()
|
||||
-- TUM.playerCareer.displayMedalBox(printSummary)
|
||||
-- TUM.playerCareer.getCareerSummary()
|
||||
-- TUM.playerCareer.load()
|
||||
-- TUM.playerCareer.onStartUp()
|
||||
-- TUM.playerCareer.reset()
|
||||
-- TUM.playerCareer.save()
|
||||
-- ====================================================================================
|
||||
|
||||
TUM.playerCareer = {}
|
||||
|
||||
do
|
||||
local MAX_RIBBONS = 40 -- Maximum number of ribbons
|
||||
local MEDAL_BOX_DISPLAY_TIME = 15 -- in seconds
|
||||
local OBJECTIVES_PER_RIBBON = 4 -- How many completed objectives to gain a new ribbon?
|
||||
|
||||
local MEDALS = {
|
||||
{ "Air medal", 200 },
|
||||
{ "Bronze star", 300 },
|
||||
{ "Airman's medal", 400 },
|
||||
{ "Distinguished Flying Cross", 500 },
|
||||
{ "Silver Star for Valor", 600 },
|
||||
{ "Air Force Cross", 700 },
|
||||
{ "Congressional Medal of Honor", 800 },
|
||||
}
|
||||
|
||||
local RANKS = {
|
||||
{ "2d Lt.", "Second lieutenant", 0 },
|
||||
{ "1st Lt.", "First lieutenant", 500 },
|
||||
{ "Capt.", "Captain", 2000 },
|
||||
{ "Maj.", "Major", 8000 },
|
||||
{ "Lt Col.", "Lieutenant colonel", 16000 },
|
||||
{ "Col.", "Colonel", 32000 },
|
||||
}
|
||||
|
||||
local careerStats = {}
|
||||
|
||||
-------------------------------------
|
||||
-- Adds missing fields, if any, to the careerStats table
|
||||
-------------------------------------
|
||||
local function fixIncompleteStats()
|
||||
if not careerStats then careerStats = { } end
|
||||
if not careerStats.bestSortie then careerStats.bestSortie = 0 end
|
||||
if not careerStats.completedObjectives then careerStats.completedObjectives = 0 end
|
||||
if not careerStats.completedSorties then careerStats.completedSorties = 0 end
|
||||
if not careerStats.medals then careerStats.medals = 0 end
|
||||
if not careerStats.medalWounded then careerStats.medalWounded = false end
|
||||
if not careerStats.rank then careerStats.rank = 1 end
|
||||
if not careerStats.score then careerStats.score = 0 end
|
||||
careerStats.version = TUM.VERSION_NUMBER
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns the highest medal a player has obtained for scoring a high number of points during a single sortie
|
||||
-- @return A number (index in the MEDALS table)
|
||||
-------------------------------------
|
||||
local function getHighestMedal()
|
||||
local medal = 0
|
||||
for i=1,#MEDALS do
|
||||
if careerStats.bestSortie >= MEDALS[i][2] then
|
||||
medal = i
|
||||
end
|
||||
end
|
||||
|
||||
return medal
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns the current number a ribbons a player was awarded for completing objectives
|
||||
-- @return A number
|
||||
-------------------------------------
|
||||
local function getRibbonCount()
|
||||
return DCSEx.math.clamp(math.floor(careerStats.completedObjectives / OBJECTIVES_PER_RIBBON), 0, MAX_RIBBONS)
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Awards career points. Only works in single-player missions
|
||||
-- @param score Number of career points to award
|
||||
-- @param objectives Number of completed objectives to award
|
||||
-- @return True if a ribbon, medal or promotion was awarded, false otherwise
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.awardScore(score, objectives)
|
||||
if not DCSEx.io.canReadAndWrite() then return false end -- IO disabled, career and scoring disabled
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return false end -- No career in multiplayer
|
||||
score = math.max(0, math.floor(score or 0))
|
||||
|
||||
fixIncompleteStats()
|
||||
|
||||
local oldRibbonCount = getRibbonCount()
|
||||
careerStats.bestSortie = math.max(careerStats.bestSortie, score)
|
||||
careerStats.score = careerStats.score + score
|
||||
careerStats.completedObjectives = careerStats.completedObjectives + objectives
|
||||
TUM.log("Awarded "..tostring(score).." xp and "..tostring(objectives).." completed objectives to player.")
|
||||
local newRibbonCount = getRibbonCount()
|
||||
|
||||
local somethingWasAwarded = false
|
||||
|
||||
-- Check for promotions
|
||||
if careerStats.rank < #RANKS and careerStats.score >= RANKS[careerStats.rank + 1][3] then
|
||||
careerStats.rank = careerStats.rank + 1
|
||||
somethingWasAwarded = true
|
||||
trigger.action.outText("✪ You have been promoted to the rank of "..RANKS[careerStats.rank][2]..".", MEDAL_BOX_DISPLAY_TIME)
|
||||
end
|
||||
|
||||
-- Check for medals
|
||||
for i=1,#MEDALS do
|
||||
if i > careerStats.medals and score >= MEDALS[i][2] then
|
||||
trigger.action.outText("✪ You have been awarded the "..MEDALS[i][1]..".", MEDAL_BOX_DISPLAY_TIME)
|
||||
careerStats.medals = i
|
||||
somethingWasAwarded = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for ribbons
|
||||
if newRibbonCount > oldRibbonCount then
|
||||
trigger.action.outText("✪ You have been awarded a battle ribbon.", MEDAL_BOX_DISPLAY_TIME)
|
||||
somethingWasAwarded = true
|
||||
end
|
||||
|
||||
TUM.playerCareer.save()
|
||||
|
||||
if somethingWasAwarded then
|
||||
TUM.playerCareer.displayMedalBox(false)
|
||||
end
|
||||
|
||||
return somethingWasAwarded
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Appends the career menu to the F10 menu. Only works in single-player missions
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.createMenu()
|
||||
if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer
|
||||
|
||||
missionCommands.addCommand("✪ View pilot career stats", nil, TUM.playerCareer.displayMedalBox, true)
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Displays the player's medal box and carrer summary. Only works in single-player missions
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.displayMedalBox(printSummary)
|
||||
if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer
|
||||
fixIncompleteStats()
|
||||
|
||||
if printSummary then
|
||||
trigger.action.outText(TUM.playerCareer.getCareerSummary(), MEDAL_BOX_DISPLAY_TIME, true)
|
||||
end
|
||||
|
||||
DCSEx.dcs.outPicture("Pic-MedalBox.png", MEDAL_BOX_DISPLAY_TIME, true, 0, 2, 2, 50, 1)
|
||||
DCSEx.dcs.outPicture("Pic-Rank"..tostring(careerStats.rank)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1)
|
||||
|
||||
local ribbonCount = getRibbonCount()
|
||||
for i=1,ribbonCount do
|
||||
DCSEx.dcs.outPicture("Pic-Ribbon"..tostring(i)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1)
|
||||
end
|
||||
|
||||
for i=1,careerStats.medals do
|
||||
DCSEx.dcs.outPicture("Pic-Medal"..tostring(i)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1)
|
||||
end
|
||||
trigger.action.outSound("UI-Career.ogg")
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns the player career summary as a string. Only works in single-player missions
|
||||
-- @return A string
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.getCareerSummary()
|
||||
if not DCSEx.io.canReadAndWrite() then return "" end -- IO disabled, career and scoring disabled
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return "" end -- No career in multiplayer
|
||||
fixIncompleteStats()
|
||||
|
||||
local playerName = "Player"
|
||||
local players = DCSEx.world.getAllPlayers()
|
||||
if #players > 0 then
|
||||
playerName = players[1]:getPlayerName()
|
||||
end
|
||||
|
||||
local summary = ""
|
||||
summary = summary.."CAREER STATS FOR "..RANKS[careerStats.rank][1]:upper().." "..playerName:upper()..":\n"
|
||||
summary = summary.."=======================\n"
|
||||
summary = summary.."- Rank: "..RANKS[careerStats.rank][2].."\n"
|
||||
summary = summary.."- Best sortie XP: "..DCSEx.string.toStringThousandsSeparator(careerStats.bestSortie).."\n"
|
||||
summary = summary.."- Total career XP: "..DCSEx.string.toStringThousandsSeparator(careerStats.score).."\n"
|
||||
summary = summary.."- Completed objectives: "..tostring(careerStats.completedObjectives).."\n"
|
||||
|
||||
if careerStats.medals == 0 then
|
||||
summary = summary.."- Medals: None"
|
||||
else
|
||||
summary = summary.."- Medals:"
|
||||
for i=1,careerStats.medals do
|
||||
summary = summary.."\n - "..MEDALS[i][1]
|
||||
end
|
||||
end
|
||||
|
||||
local ribbonCount = getRibbonCount()
|
||||
if ribbonCount < MAX_RIBBONS or careerStats.rank < #RANKS or careerStats.medals < #MEDALS then
|
||||
summary = summary.."\n"
|
||||
|
||||
if careerStats.rank < #RANKS then
|
||||
summary = summary.."\n- Next promotion: "..DCSEx.string.toStringThousandsSeparator(RANKS[careerStats.rank + 1][3]).." xp"
|
||||
end
|
||||
|
||||
if ribbonCount < MAX_RIBBONS then
|
||||
summary = summary.."\n- Next ribbon: "..tostring((ribbonCount + 1) * OBJECTIVES_PER_RIBBON).." objectives"
|
||||
end
|
||||
|
||||
if careerStats.medals < #MEDALS then
|
||||
summary = summary.."\n- Next medal: "..DCSEx.string.toStringThousandsSeparator(MEDALS[careerStats.medals + 1][2]).." xp in a single flight"
|
||||
end
|
||||
end
|
||||
|
||||
return summary
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Loads the player career from the disk. Only works in single-player missions
|
||||
-- @return True if everything worked (or disabled because of MP), false if an error happened
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.load()
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer
|
||||
if not DCSEx.io.canReadAndWrite() then return false end
|
||||
|
||||
local jsonString = DCSEx.io.load("TheUniversalMission.sav")
|
||||
if jsonString then
|
||||
-- TODO: what if Json is malformed?
|
||||
careerStats = net.json2lua(jsonString)
|
||||
if not careerStats then
|
||||
careerStats = {}
|
||||
fixIncompleteStats()
|
||||
TUM.log("Failed to load player career data, career data reset.")
|
||||
else
|
||||
TUM.log("Player career data loaded successfully.")
|
||||
end
|
||||
else
|
||||
fixIncompleteStats()
|
||||
return false
|
||||
end
|
||||
|
||||
fixIncompleteStats()
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Called on mission start up
|
||||
-- @return True if started up properly, false if an error happened
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.onStartUp()
|
||||
fixIncompleteStats()
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer
|
||||
|
||||
if DCSEx.io.canReadAndWrite() then
|
||||
TUM.log("Lua IO module available, can read and write.")
|
||||
TUM.playerCareer.load()
|
||||
else
|
||||
local msg = "IO module is disabled, CANNOT read and write persistant data. Player progress will NOT be saved.\n"
|
||||
msg = msg.."To enable the IO module, comment or remove the \"sanitizeModule('io')\" line in \n"
|
||||
msg = msg.."[DCSWorld installation directory]\\Scripts\\MissionScripting.lua and restart the game."
|
||||
|
||||
TUM.log(msg, TUM.logLevel.WARNING)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Resets the player career stats and save them. Only works in single-player missions
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.reset()
|
||||
if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer
|
||||
|
||||
careerStats = nil
|
||||
fixIncompleteStats()
|
||||
TUM.playerCareer.save()
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Save the player career to the disk. Only works in single-player missions
|
||||
-- @return True if everything worked (or disabled), false if an error happened
|
||||
-------------------------------------
|
||||
function TUM.playerCareer.save()
|
||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer
|
||||
if not DCSEx.io.canReadAndWrite() then return true end -- IO disabled, career and scoring disabled
|
||||
|
||||
fixIncompleteStats()
|
||||
|
||||
if DCSEx.io.save("TheUniversalMission.sav", net.lua2json(careerStats)) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user