Skip to content

Commit

Permalink
deathmatch: additional improvements (#522)
Browse files Browse the repository at this point in the history
  • Loading branch information
jlillis authored Sep 17, 2024
1 parent 6354efa commit b94ffdd
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 87 deletions.
80 changes: 22 additions & 58 deletions [gamemodes]/[deathmatch]/deathmatch/client/hud.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,13 @@ _hud.scoreDisplay.update = function()
)
end

-- respawn screen
_hud.respawnScreen = {}
-- respawn counter (You will respawn in x seconds)
_hud.respawnScreen.respawnCounter = dxText:create("", 0.5, 0.5, true, "beckett", 4)
_hud.respawnScreen.respawnCounter:type("stroke", 6)
_hud.respawnScreen.respawnCounter:color(255, 225, 225)
_hud.respawnScreen.respawnCounter:visible(false)
_hud.respawnScreen.setVisible = function(_, visible)
_hud.respawnScreen.respawnCounter:visible(visible)
end
_hud.respawnScreen.startCountdown = function()
if _respawnTime > 0 then
startCountdown(_respawnTime)
else
_hud.respawnScreen.respawnCounter:text("Wasted")
end
-- wasted screen
_hud.wastedScreen = {}
_hud.wastedScreen.text = dxText:create("Wasted", 0.5, 0.5, true, "beckett", 4)
_hud.wastedScreen.text:type("border", 2)
_hud.wastedScreen.text:color(255, 0, 0)
_hud.wastedScreen.setVisible = function(_, visible)
_hud.wastedScreen.text:visible(visible)
end

-- end screen
Expand Down Expand Up @@ -93,49 +84,22 @@ _hud.endScreen.update = function(_, winner, draw, aborted)
playSound("client/audio/mission_accomplished.mp3")
end
end

-- spectate screen
_hud.spectateScreen = {}
-- spectating info label
_hud.spectateScreen.infoLabel = dxText:create("You are currently spectating.\nUse left and right arrow to switch players.", 0, 0, false, "default-bold", 2)
_hud.spectateScreen.infoLabel:color(225, 225, 225, 225)
_hud.spectateScreen.infoLabel:boundingBox(0, 0.8, 1, 1, true)
_hud.spectateScreen.infoLabel:align("bottom", "center")
_hud.spectateScreen.infoLabel:type("border", 2)
_hud.spectateScreen.setVisible = function(_, visible)
_hud.spectateScreen.infoLabel:visible(visible)
end

-- hide all HUD elements by default
_hud.loadingScreen:setVisible(false)
_hud.scoreDisplay:setVisible(false)
_hud.respawnScreen:setVisible(false)
_hud.wastedScreen:setVisible(false)
_hud.endScreen:setVisible(false)

-- TODO: clean this junk up
local function dxSetAlpha ( dx, a )
local r,g,b = dx:color()
dx:color(r,g,b,a)
end

local countdownCR
local function countdown(time)
for i=time,0,-1 do
_hud.respawnScreen.respawnCounter:text("Wasted\n"..i)
setTimer ( countdownCR, 1000, 1 )
coroutine.yield()
end
end

local function hideCountdown()
setTimer (
function()
_hud.respawnScreen:setVisible(false)
end,
600, 1
)
Animation.createAndPlay(
_hud.respawnScreen.respawnCounter,
{{ from = 225, to = 0, time = 400, fn = dxSetAlpha }}
)
removeEventHandler ( "onClientPlayerSpawn", localPlayer, hideCountdown )
end

function startCountdown(time)
Animation.createAndPlay(
_hud.respawnScreen.respawnCounter,
{{ from = 0, to = 225, time = 600, fn = dxSetAlpha }}
)
addEventHandler ( "onClientPlayerSpawn", localPlayer, hideCountdown )
_hud.respawnScreen:setVisible(true)
time = math.floor(time/1000)
countdownCR = coroutine.wrap(countdown)
countdownCR(time)
end
_hud.spectateScreen:setVisible(false)
82 changes: 68 additions & 14 deletions [gamemodes]/[deathmatch]/deathmatch/client/main.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local _wastedTimer, _respawnTimer

--
-- startGamemodeClient: initializes the gamemode client
--
Expand All @@ -9,6 +11,8 @@ local function startGamemodeClient()
exports.scoreboard:scoreboardSetSortBy("Rank")
-- fade out camera
fadeCamera(false, 0)
-- disable zone name HUD element
setPlayerHudComponentVisible("area_name", false)
-- if a game is in progress, apply the loading camera matrix
if getElementData(resourceRoot, "gameState") == GAME_IN_PROGRESS then
setCameraMatrix(unpack(calculateLoadingCameraMatrix()))
Expand All @@ -25,6 +29,8 @@ local function stopGamemodeClient()
exports.scoreboard:scoreboardRemoveColumn("Rank")
-- hide scoreboard
exports.scoreboard:setScoreboardForced(false)
-- re-enable zone name HUD element
setPlayerHudComponentVisible("area_name", true)
end
addEventHandler("onClientResourceStop", resourceRoot, stopGamemodeClient)

Expand Down Expand Up @@ -68,10 +74,15 @@ addEventHandler("onClientGamemodeMapStop", resourceRoot, stopGamemodeMap)
-- startGamemodeRound: triggered when a round begins
--
local function startGamemodeRound()
-- attach player wasted handler
addEventHandler("onClientPlayerWasted", localPlayer, _hud.respawnScreen.startCountdown)
-- attach player spawn and wasted handler
addEventHandler("onClientPlayerSpawn", localPlayer, localPlayerSpawn)
addEventHandler("onClientPlayerWasted", localPlayer, localPlayerWasted)
-- attach element data change handler
addEventHandler("onClientElementDataChange", root, elementDataChange)
-- stop spectating
if isSpectating() then
stopSpectating(true)
end
-- hide end/loading screens and scoreboard
_hud.loadingScreen:setVisible(false)
_hud.endScreen:setVisible(false)
Expand All @@ -87,30 +98,73 @@ addEventHandler("onClientGamemodeRoundStart", resourceRoot, startGamemodeRound)
-- stopGamemodeRound: triggered when a round ends
--
local function stopGamemodeRound(winner, draw, aborted)
-- remove player wasted handler and hide respawn screen if active
removeEventHandler("onClientPlayerWasted", localPlayer, _hud.respawnScreen.startCountdown)
_hud.respawnScreen.setVisible(false)
-- remove player spawn & wasted handler and hide respawn screen if active
removeEventHandler("onClientPlayerWasted", localPlayer, localPlayerWasted)
removeEventHandler("onClientPlayerSpawn", localPlayer, localPlayerSpawn)
-- remove element data change handler
removeEventHandler("onClientElementDataChange", root, elementDataChange)
-- hide score display
_hud.scoreDisplay:setVisible(false)
-- hide wasted screen and cancel the wasted and respawn timers
_hud.wastedScreen:setVisible(false)
if isTimer(_wastedTimer) then
killTimer(_wastedTimer)
end
if isElement(_respawnTimer) then
destroyElement(_respawnTimer)
end
-- spectate the winner
if winner and player ~= winner then
if winner then
setCameraTarget(winner)
startSpectating(winner)
end
-- exit spectate mode and go to black if round was aborted
if aborted then
if isSpectating() then
stopSpectating(true)
end
toggleAllControls(true, true, false)
fadeCamera(false, 0)
else
-- begin fading out the screen
fadeCamera(false, ROUND_START_DELAY/1000)
-- show end screen and scoreboard
_hud.endScreen:update(winner, draw, aborted)
_hud.endScreen:setVisible(true)
exports.scoreboard:setScoreboardForced(true)
end
-- begin fading out the screen
fadeCamera(false, CAMERA_LOAD_DELAY/1000)
-- show end screen and scoreboard
_hud.endScreen:update(winner, draw, aborted)
_hud.endScreen:setVisible(true)
exports.scoreboard:setScoreboardForced(true)
end
addEvent("onClientGamemodeRoundEnd", true)
addEventHandler("onClientGamemodeRoundEnd", resourceRoot, stopGamemodeRound)

--
-- localPlayerWasted: triggered when local player is killed
--
function localPlayerWasted()
-- show the wasted screen
_hud.wastedScreen:setVisible(true)

-- set timer to show the spectate screen
_wastedTimer = setTimer(startSpectating, WASTED_CAMERA_DURATION, 1)

-- create a respawn timer is repawn is enabled
if _respawnTime > 0 then
_respawnTimer = exports.missionTimer:createMissionTimer(WASTED_CAMERA_DURATION + _respawnTime, true, "You will respawn in %s seconds", 0.5, 50, true, "default-bold", 1)
end
end

--
-- localPlayerSpawn: triggered when local player is spawned
--
function localPlayerSpawn()
-- if we're spectating, stop spectating
if isSpectating() then
stopSpectating()
end
-- kill the respawn timer if it exists
if isElement(_respawnTimer) then
destroyElement(_respawnTimer)
end
end

--
-- elementDataChange: triggered when element data changes - used to track score changes
--
Expand Down
168 changes: 168 additions & 0 deletions [gamemodes]/[deathmatch]/deathmatch/client/spectate.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
local _spectating = false
local _currentTarget
local _validTargets = {}

--
-- startSpectating([target]): starts spectating the targted player, or a random one if target == nil
--
function startSpectating(target)
-- fade camera out, hide radar hud and score screen
fadeCamera(false, 0)
setPlayerHudComponentVisible("radar", false)
_hud.scoreDisplay:setVisible(false)

-- hide wasted screen and destroy wasted timer
_hud.wastedScreen:setVisible(false)
if isElement(_wastedTimer) then
destroyElement(_wastedTimer)
end

-- if target is nil, pick a random player
if not target then
target = _validTargets[math.random(1, #_validTargets)]
end

-- if there still isn't a target, error out
if not target then
-- TODO: handle this more gracefully
error("no valid spectate target", 2)
end

-- set camera target and disable controls
iprint(target)
setCameraTarget(target)
toggleAllControls(false, true, false)

-- show spectate screen
_hud.spectateScreen:setVisible(true)

-- bind left and right arrow keys to cycle spectate target
bindKey("left", "down", cycleSpectateTarget, true)
bindKey("right", "down", cycleSpectateTarget)

-- fade camera in next frame
setTimer(fadeCamera, 50, 1, true, 1)

_spectating = true
_currentTarget = nil
end

--
-- stopSpectating([fadeOut]): exits spectate mode. if fadeOut == true camera will not fade back in
--
function stopSpectating(fadeOut)
-- fade camera out, restore radar hud and score screen
fadeCamera(false, 0)
setPlayerHudComponentVisible("radar", true)
_hud.scoreDisplay:setVisible(true)

-- reset camera target and controls
setCameraTarget(localPlayer)
toggleAllControls(true, true, false)

-- hide spectate screen
_hud.spectateScreen:setVisible(false)

-- bind left and right arrow keys to cycle spectate target
unbindKey("left", "down", cycleSpectateTarget)
unbindKey("right", "down", cycleSpectateTarget)

-- fade camera in next frame
if not fadeOut then
setTimer(fadeCamera, 50, 1, true, 1)
end

_spectating = false
_currentTarget = nil
end

--
-- isSpectating(): returns true if local player is spectating, false otherwise
--
function isSpectating()
return _spectating
end

--
-- setSpectateTarget(target): updates spectate target
--
function setSpectateTarget(target)
if not _spectating then
error("local player is not spectating", 2)
end

if target == _currentTarget then
return
end

_currentTarget = target
setCameraTarget(target)
end

--
-- cycleSpectateTarget(): cycles to next or previous target while in spectate mode
--
function cycleSpectateTarget(previous)
if not _spectating then
error("local player is not spectating", 2)
end

local index = 1
for i, validTarget in ipairs(_validTargets) do
if validTarget == _currentTarget then
index = i
break
end
end

if previous then
index = index - 1
if index < 1 then
index = #_validTargets
end
else
index = index + 1
if index > #_validTargets then
index = 1
end
end

setSpectateTarget(_validTargets[index])
end

--
-- functions to update target list on player spawn, death, and resource start
--
local function addValidTarget(playerTeam)
if source == localPlayer then
return
end

table.insert(_validTargets, source)
end
addEventHandler("onClientPlayerSpawn", root, addValidTarget)

local function removeValidTarget()
if source == localPlayer then
return
end

for i, target in ipairs(_validTargets) do
table.remove(_validTargets, i)
end
end
addEventHandler("onClientPlayerWasted", root, removeValidTarget)

local function refreshValidTargets()
local players = getElementsByType("player", root)

-- remove local player and dead players from list
for i, player in ipairs(players) do
if player == localPlayer or isPedDead(player) then
table.remove(players, i)
end
end

_validTargets = players
end
addEventHandler("onClientResourceStart", resourceRoot, refreshValidTargets)
Loading

0 comments on commit b94ffdd

Please sign in to comment.