Skip to content

Commit b94ffdd

Browse files
authored
deathmatch: additional improvements (#522)
1 parent 6354efa commit b94ffdd

File tree

8 files changed

+326
-87
lines changed

8 files changed

+326
-87
lines changed

[gamemodes]/[deathmatch]/deathmatch/client/hud.lua

Lines changed: 22 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,13 @@ _hud.scoreDisplay.update = function()
4848
)
4949
end
5050

51-
-- respawn screen
52-
_hud.respawnScreen = {}
53-
-- respawn counter (You will respawn in x seconds)
54-
_hud.respawnScreen.respawnCounter = dxText:create("", 0.5, 0.5, true, "beckett", 4)
55-
_hud.respawnScreen.respawnCounter:type("stroke", 6)
56-
_hud.respawnScreen.respawnCounter:color(255, 225, 225)
57-
_hud.respawnScreen.respawnCounter:visible(false)
58-
_hud.respawnScreen.setVisible = function(_, visible)
59-
_hud.respawnScreen.respawnCounter:visible(visible)
60-
end
61-
_hud.respawnScreen.startCountdown = function()
62-
if _respawnTime > 0 then
63-
startCountdown(_respawnTime)
64-
else
65-
_hud.respawnScreen.respawnCounter:text("Wasted")
66-
end
51+
-- wasted screen
52+
_hud.wastedScreen = {}
53+
_hud.wastedScreen.text = dxText:create("Wasted", 0.5, 0.5, true, "beckett", 4)
54+
_hud.wastedScreen.text:type("border", 2)
55+
_hud.wastedScreen.text:color(255, 0, 0)
56+
_hud.wastedScreen.setVisible = function(_, visible)
57+
_hud.wastedScreen.text:visible(visible)
6758
end
6859

6960
-- end screen
@@ -93,49 +84,22 @@ _hud.endScreen.update = function(_, winner, draw, aborted)
9384
playSound("client/audio/mission_accomplished.mp3")
9485
end
9586
end
87+
88+
-- spectate screen
89+
_hud.spectateScreen = {}
90+
-- spectating info label
91+
_hud.spectateScreen.infoLabel = dxText:create("You are currently spectating.\nUse left and right arrow to switch players.", 0, 0, false, "default-bold", 2)
92+
_hud.spectateScreen.infoLabel:color(225, 225, 225, 225)
93+
_hud.spectateScreen.infoLabel:boundingBox(0, 0.8, 1, 1, true)
94+
_hud.spectateScreen.infoLabel:align("bottom", "center")
95+
_hud.spectateScreen.infoLabel:type("border", 2)
96+
_hud.spectateScreen.setVisible = function(_, visible)
97+
_hud.spectateScreen.infoLabel:visible(visible)
98+
end
99+
96100
-- hide all HUD elements by default
97101
_hud.loadingScreen:setVisible(false)
98102
_hud.scoreDisplay:setVisible(false)
99-
_hud.respawnScreen:setVisible(false)
103+
_hud.wastedScreen:setVisible(false)
100104
_hud.endScreen:setVisible(false)
101-
102-
-- TODO: clean this junk up
103-
local function dxSetAlpha ( dx, a )
104-
local r,g,b = dx:color()
105-
dx:color(r,g,b,a)
106-
end
107-
108-
local countdownCR
109-
local function countdown(time)
110-
for i=time,0,-1 do
111-
_hud.respawnScreen.respawnCounter:text("Wasted\n"..i)
112-
setTimer ( countdownCR, 1000, 1 )
113-
coroutine.yield()
114-
end
115-
end
116-
117-
local function hideCountdown()
118-
setTimer (
119-
function()
120-
_hud.respawnScreen:setVisible(false)
121-
end,
122-
600, 1
123-
)
124-
Animation.createAndPlay(
125-
_hud.respawnScreen.respawnCounter,
126-
{{ from = 225, to = 0, time = 400, fn = dxSetAlpha }}
127-
)
128-
removeEventHandler ( "onClientPlayerSpawn", localPlayer, hideCountdown )
129-
end
130-
131-
function startCountdown(time)
132-
Animation.createAndPlay(
133-
_hud.respawnScreen.respawnCounter,
134-
{{ from = 0, to = 225, time = 600, fn = dxSetAlpha }}
135-
)
136-
addEventHandler ( "onClientPlayerSpawn", localPlayer, hideCountdown )
137-
_hud.respawnScreen:setVisible(true)
138-
time = math.floor(time/1000)
139-
countdownCR = coroutine.wrap(countdown)
140-
countdownCR(time)
141-
end
105+
_hud.spectateScreen:setVisible(false)

[gamemodes]/[deathmatch]/deathmatch/client/main.lua

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
local _wastedTimer, _respawnTimer
2+
13
--
24
-- startGamemodeClient: initializes the gamemode client
35
--
@@ -9,6 +11,8 @@ local function startGamemodeClient()
911
exports.scoreboard:scoreboardSetSortBy("Rank")
1012
-- fade out camera
1113
fadeCamera(false, 0)
14+
-- disable zone name HUD element
15+
setPlayerHudComponentVisible("area_name", false)
1216
-- if a game is in progress, apply the loading camera matrix
1317
if getElementData(resourceRoot, "gameState") == GAME_IN_PROGRESS then
1418
setCameraMatrix(unpack(calculateLoadingCameraMatrix()))
@@ -25,6 +29,8 @@ local function stopGamemodeClient()
2529
exports.scoreboard:scoreboardRemoveColumn("Rank")
2630
-- hide scoreboard
2731
exports.scoreboard:setScoreboardForced(false)
32+
-- re-enable zone name HUD element
33+
setPlayerHudComponentVisible("area_name", true)
2834
end
2935
addEventHandler("onClientResourceStop", resourceRoot, stopGamemodeClient)
3036

@@ -68,10 +74,15 @@ addEventHandler("onClientGamemodeMapStop", resourceRoot, stopGamemodeMap)
6874
-- startGamemodeRound: triggered when a round begins
6975
--
7076
local function startGamemodeRound()
71-
-- attach player wasted handler
72-
addEventHandler("onClientPlayerWasted", localPlayer, _hud.respawnScreen.startCountdown)
77+
-- attach player spawn and wasted handler
78+
addEventHandler("onClientPlayerSpawn", localPlayer, localPlayerSpawn)
79+
addEventHandler("onClientPlayerWasted", localPlayer, localPlayerWasted)
7380
-- attach element data change handler
7481
addEventHandler("onClientElementDataChange", root, elementDataChange)
82+
-- stop spectating
83+
if isSpectating() then
84+
stopSpectating(true)
85+
end
7586
-- hide end/loading screens and scoreboard
7687
_hud.loadingScreen:setVisible(false)
7788
_hud.endScreen:setVisible(false)
@@ -87,30 +98,73 @@ addEventHandler("onClientGamemodeRoundStart", resourceRoot, startGamemodeRound)
8798
-- stopGamemodeRound: triggered when a round ends
8899
--
89100
local function stopGamemodeRound(winner, draw, aborted)
90-
-- remove player wasted handler and hide respawn screen if active
91-
removeEventHandler("onClientPlayerWasted", localPlayer, _hud.respawnScreen.startCountdown)
92-
_hud.respawnScreen.setVisible(false)
101+
-- remove player spawn & wasted handler and hide respawn screen if active
102+
removeEventHandler("onClientPlayerWasted", localPlayer, localPlayerWasted)
103+
removeEventHandler("onClientPlayerSpawn", localPlayer, localPlayerSpawn)
93104
-- remove element data change handler
94105
removeEventHandler("onClientElementDataChange", root, elementDataChange)
95106
-- hide score display
96107
_hud.scoreDisplay:setVisible(false)
108+
-- hide wasted screen and cancel the wasted and respawn timers
109+
_hud.wastedScreen:setVisible(false)
110+
if isTimer(_wastedTimer) then
111+
killTimer(_wastedTimer)
112+
end
113+
if isElement(_respawnTimer) then
114+
destroyElement(_respawnTimer)
115+
end
97116
-- spectate the winner
98117
if winner and player ~= winner then
99-
if winner then
100-
setCameraTarget(winner)
118+
startSpectating(winner)
119+
end
120+
-- exit spectate mode and go to black if round was aborted
121+
if aborted then
122+
if isSpectating() then
123+
stopSpectating(true)
101124
end
102-
toggleAllControls(true, true, false)
125+
fadeCamera(false, 0)
126+
else
127+
-- begin fading out the screen
128+
fadeCamera(false, ROUND_START_DELAY/1000)
129+
-- show end screen and scoreboard
130+
_hud.endScreen:update(winner, draw, aborted)
131+
_hud.endScreen:setVisible(true)
132+
exports.scoreboard:setScoreboardForced(true)
103133
end
104-
-- begin fading out the screen
105-
fadeCamera(false, CAMERA_LOAD_DELAY/1000)
106-
-- show end screen and scoreboard
107-
_hud.endScreen:update(winner, draw, aborted)
108-
_hud.endScreen:setVisible(true)
109-
exports.scoreboard:setScoreboardForced(true)
110134
end
111135
addEvent("onClientGamemodeRoundEnd", true)
112136
addEventHandler("onClientGamemodeRoundEnd", resourceRoot, stopGamemodeRound)
113137

138+
--
139+
-- localPlayerWasted: triggered when local player is killed
140+
--
141+
function localPlayerWasted()
142+
-- show the wasted screen
143+
_hud.wastedScreen:setVisible(true)
144+
145+
-- set timer to show the spectate screen
146+
_wastedTimer = setTimer(startSpectating, WASTED_CAMERA_DURATION, 1)
147+
148+
-- create a respawn timer is repawn is enabled
149+
if _respawnTime > 0 then
150+
_respawnTimer = exports.missionTimer:createMissionTimer(WASTED_CAMERA_DURATION + _respawnTime, true, "You will respawn in %s seconds", 0.5, 50, true, "default-bold", 1)
151+
end
152+
end
153+
154+
--
155+
-- localPlayerSpawn: triggered when local player is spawned
156+
--
157+
function localPlayerSpawn()
158+
-- if we're spectating, stop spectating
159+
if isSpectating() then
160+
stopSpectating()
161+
end
162+
-- kill the respawn timer if it exists
163+
if isElement(_respawnTimer) then
164+
destroyElement(_respawnTimer)
165+
end
166+
end
167+
114168
--
115169
-- elementDataChange: triggered when element data changes - used to track score changes
116170
--
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
local _spectating = false
2+
local _currentTarget
3+
local _validTargets = {}
4+
5+
--
6+
-- startSpectating([target]): starts spectating the targted player, or a random one if target == nil
7+
--
8+
function startSpectating(target)
9+
-- fade camera out, hide radar hud and score screen
10+
fadeCamera(false, 0)
11+
setPlayerHudComponentVisible("radar", false)
12+
_hud.scoreDisplay:setVisible(false)
13+
14+
-- hide wasted screen and destroy wasted timer
15+
_hud.wastedScreen:setVisible(false)
16+
if isElement(_wastedTimer) then
17+
destroyElement(_wastedTimer)
18+
end
19+
20+
-- if target is nil, pick a random player
21+
if not target then
22+
target = _validTargets[math.random(1, #_validTargets)]
23+
end
24+
25+
-- if there still isn't a target, error out
26+
if not target then
27+
-- TODO: handle this more gracefully
28+
error("no valid spectate target", 2)
29+
end
30+
31+
-- set camera target and disable controls
32+
iprint(target)
33+
setCameraTarget(target)
34+
toggleAllControls(false, true, false)
35+
36+
-- show spectate screen
37+
_hud.spectateScreen:setVisible(true)
38+
39+
-- bind left and right arrow keys to cycle spectate target
40+
bindKey("left", "down", cycleSpectateTarget, true)
41+
bindKey("right", "down", cycleSpectateTarget)
42+
43+
-- fade camera in next frame
44+
setTimer(fadeCamera, 50, 1, true, 1)
45+
46+
_spectating = true
47+
_currentTarget = nil
48+
end
49+
50+
--
51+
-- stopSpectating([fadeOut]): exits spectate mode. if fadeOut == true camera will not fade back in
52+
--
53+
function stopSpectating(fadeOut)
54+
-- fade camera out, restore radar hud and score screen
55+
fadeCamera(false, 0)
56+
setPlayerHudComponentVisible("radar", true)
57+
_hud.scoreDisplay:setVisible(true)
58+
59+
-- reset camera target and controls
60+
setCameraTarget(localPlayer)
61+
toggleAllControls(true, true, false)
62+
63+
-- hide spectate screen
64+
_hud.spectateScreen:setVisible(false)
65+
66+
-- bind left and right arrow keys to cycle spectate target
67+
unbindKey("left", "down", cycleSpectateTarget)
68+
unbindKey("right", "down", cycleSpectateTarget)
69+
70+
-- fade camera in next frame
71+
if not fadeOut then
72+
setTimer(fadeCamera, 50, 1, true, 1)
73+
end
74+
75+
_spectating = false
76+
_currentTarget = nil
77+
end
78+
79+
--
80+
-- isSpectating(): returns true if local player is spectating, false otherwise
81+
--
82+
function isSpectating()
83+
return _spectating
84+
end
85+
86+
--
87+
-- setSpectateTarget(target): updates spectate target
88+
--
89+
function setSpectateTarget(target)
90+
if not _spectating then
91+
error("local player is not spectating", 2)
92+
end
93+
94+
if target == _currentTarget then
95+
return
96+
end
97+
98+
_currentTarget = target
99+
setCameraTarget(target)
100+
end
101+
102+
--
103+
-- cycleSpectateTarget(): cycles to next or previous target while in spectate mode
104+
--
105+
function cycleSpectateTarget(previous)
106+
if not _spectating then
107+
error("local player is not spectating", 2)
108+
end
109+
110+
local index = 1
111+
for i, validTarget in ipairs(_validTargets) do
112+
if validTarget == _currentTarget then
113+
index = i
114+
break
115+
end
116+
end
117+
118+
if previous then
119+
index = index - 1
120+
if index < 1 then
121+
index = #_validTargets
122+
end
123+
else
124+
index = index + 1
125+
if index > #_validTargets then
126+
index = 1
127+
end
128+
end
129+
130+
setSpectateTarget(_validTargets[index])
131+
end
132+
133+
--
134+
-- functions to update target list on player spawn, death, and resource start
135+
--
136+
local function addValidTarget(playerTeam)
137+
if source == localPlayer then
138+
return
139+
end
140+
141+
table.insert(_validTargets, source)
142+
end
143+
addEventHandler("onClientPlayerSpawn", root, addValidTarget)
144+
145+
local function removeValidTarget()
146+
if source == localPlayer then
147+
return
148+
end
149+
150+
for i, target in ipairs(_validTargets) do
151+
table.remove(_validTargets, i)
152+
end
153+
end
154+
addEventHandler("onClientPlayerWasted", root, removeValidTarget)
155+
156+
local function refreshValidTargets()
157+
local players = getElementsByType("player", root)
158+
159+
-- remove local player and dead players from list
160+
for i, player in ipairs(players) do
161+
if player == localPlayer or isPedDead(player) then
162+
table.remove(players, i)
163+
end
164+
end
165+
166+
_validTargets = players
167+
end
168+
addEventHandler("onClientResourceStart", resourceRoot, refreshValidTargets)

0 commit comments

Comments
 (0)