gameplay: example money, money fountain, ped money drop and player ID systems

This commit is contained in:
blattersturm
2020-11-01 12:50:24 +01:00
parent 537eae7a00
commit 5073447ef4
14 changed files with 753 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
-- add text entries for all the help types we have
AddTextEntry('FOUNTAIN_HELP', 'This fountain currently contains $~1~.~n~Press ~INPUT_PICKUP~ to obtain $~1~.~n~Press ~INPUT_DETONATE~ to place $~1~.')
AddTextEntry('FOUNTAIN_HELP_DRAINED', 'This fountain currently contains $~1~.~n~Press ~INPUT_DETONATE~ to place $~1~.')
AddTextEntry('FOUNTAIN_HELP_BROKE', 'This fountain currently contains $~1~.~n~Press ~INPUT_PICKUP~ to obtain $~1~.')
AddTextEntry('FOUNTAIN_HELP_BROKE_N_DRAINED', 'This fountain currently contains $~1~.')
AddTextEntry('FOUNTAIN_HELP_INUSE', 'This fountain currently contains $~1~.~n~You can use it again in ~a~.')
-- upvalue aliases so that we will be fast if far away
local Wait = Wait
local GetEntityCoords = GetEntityCoords
local PlayerPedId = PlayerPedId
-- timer, don't tick as frequently if we're far from any money fountain
local relevanceTimer = 500
CreateThread(function()
local pressing = false
while true do
Wait(relevanceTimer)
local coords = GetEntityCoords(PlayerPedId())
for _, data in pairs(moneyFountains) do
-- if we're near this fountain
local dist = #(coords - data.coords)
-- near enough to draw
if dist < 40 then
-- ensure per-frame tick
relevanceTimer = 0
DrawMarker(29, data.coords.x, data.coords.y, data.coords.z, 0, 0, 0, 0.0, 0, 0, 1.0, 1.0, 1.0, 0, 150, 0, 120, false, true, 2, false, nil, nil, false)
else
-- put the relevance timer back to the way it was
relevanceTimer = 500
end
-- near enough to use
if dist < 1 then
-- are we able to use it? if not, display appropriate help
local player = LocalPlayer
local nextUse = player.state['fountain_nextUse']
-- GetNetworkTime is synced for everyone
if nextUse and nextUse >= GetNetworkTime() then
BeginTextCommandDisplayHelp('FOUNTAIN_HELP_INUSE')
AddTextComponentInteger(GlobalState['fountain_' .. data.id])
AddTextComponentSubstringTime(math.tointeger(nextUse - GetNetworkTime()), 2 | 4) -- seconds (2), minutes (4)
EndTextCommandDisplayHelp(0, false, false, 1000)
else
-- handle inputs for pickup/place
if not pressing then
if IsControlPressed(0, 38 --[[ INPUT_PICKUP ]]) then
TriggerServerEvent('money_fountain:tryPickup', data.id)
pressing = true
elseif IsControlPressed(0, 47 --[[ INPUT_DETONATE ]]) then
TriggerServerEvent('money_fountain:tryPlace', data.id)
pressing = true
end
else
if not IsControlPressed(0, 38 --[[ INPUT_PICKUP ]]) and
not IsControlPressed(0, 47 --[[ INPUT_DETONATE ]]) then
pressing = false
end
end
-- decide the appropriate help message
local youCanSpend = (player.state['money_cash'] or 0) >= data.amount
local fountainCanSpend = GlobalState['fountain_' .. data.id] >= data.amount
local helpName
if youCanSpend and fountainCanSpend then
helpName = 'FOUNTAIN_HELP'
elseif youCanSpend and not fountainCanSpend then
helpName = 'FOUNTAIN_HELP_DRAINED'
elseif not youCanSpend and fountainCanSpend then
helpName = 'FOUNTAIN_HELP_BROKE'
else
helpName = 'FOUNTAIN_HELP_BROKE_N_DRAINED'
end
-- and print it
BeginTextCommandDisplayHelp(helpName)
AddTextComponentInteger(GlobalState['fountain_' .. data.id])
if fountainCanSpend then
AddTextComponentInteger(data.amount)
end
if youCanSpend then
AddTextComponentInteger(data.amount)
end
EndTextCommandDisplayHelp(0, false, false, 1000)
end
end
end
end
end)

View File

@@ -0,0 +1,18 @@
version '1.0.0'
description 'An example money system client containing a money fountain.'
author 'Cfx.re <pr@fivem.net>'
fx_version 'bodacious'
game 'gta5'
client_script 'client.lua'
server_script 'server.lua'
shared_script 'mapdata.lua'
dependencies {
'mapmanager',
'money'
}
lua54 'yes'

View File

@@ -0,0 +1,28 @@
-- define the money fountain list (SHARED SCRIPT)
moneyFountains = {}
-- index to know what to remove
local fountainIdx = 1
AddEventHandler('getMapDirectives', function(add)
-- add a 'money_fountain' map directive
add('money_fountain', function(state, name)
return function(data)
local coords = data[1]
local amount = data.amount or 100
local idx = fountainIdx
fountainIdx += 1
moneyFountains[idx] = {
id = name,
coords = coords,
amount = amount
}
state.add('idx', idx)
end
end, function(state)
moneyFountains[state.idx] = nil
end)
end)

View File

@@ -0,0 +1,107 @@
-- track down what we've added to global state
local sentState = {}
-- money system
local ms = exports['money']
-- get the fountain content from storage
local function getMoneyForId(fountainId)
return GetResourceKvpInt(('money:%s'):format(fountainId)) / 100.0
end
-- set the fountain content in storage + state
local function setMoneyForId(fountainId, money)
GlobalState['fountain_' .. fountainId] = math.tointeger(money)
return SetResourceKvpInt(('money:%s'):format(fountainId), math.tointeger(money * 100.0))
end
-- get the nearest fountain to the player + ID
local function getMoneyFountain(id, source)
local coords = GetEntityCoords(GetPlayerPed(source))
for _, v in pairs(moneyFountains) do
if v.id == id then
if #(v.coords - coords) < 2.5 then
return v
end
end
end
return nil
end
-- generic function for events
local function handleFountainStuff(source, id, pickup)
-- if near the fountain we specify
local fountain = getMoneyFountain(id, source)
if fountain then
-- and we can actually use the fountain already
local player = Player(source)
local nextUse = player.state['fountain_nextUse']
if not nextUse then
nextUse = 0
end
-- GetGameTimer ~ GetNetworkTime on client
if nextUse <= GetGameTimer() then
-- not rate limited
local success = false
local money = getMoneyForId(fountain.id)
-- decide the op
if pickup then
-- if the fountain is rich enough to get the per-use amount
if money >= fountain.amount then
-- give the player money
if ms:addMoney(source, 'cash', fountain.amount) then
money -= fountain.amount
success = true
end
end
else
-- if the player is rich enough
if ms:removeMoney(source, 'cash', fountain.amount) then
-- add to the fountain
money += fountain.amount
success = true
end
end
-- save it and set the player's cooldown
if success then
setMoneyForId(fountain.id, money)
player.state['fountain_nextUse'] = GetGameTimer() + GetConvarInt('moneyFountain_cooldown', 5000)
end
end
end
end
-- event for picking up fountain->player
RegisterNetEvent('money_fountain:tryPickup')
AddEventHandler('money_fountain:tryPickup', function(id)
handleFountainStuff(source, id, true)
end)
-- event for donating player->fountain
RegisterNetEvent('money_fountain:tryPlace')
AddEventHandler('money_fountain:tryPlace', function(id)
handleFountainStuff(source, id, false)
end)
-- listener: if a new fountain is added, set its current money in state
CreateThread(function()
while true do
Wait(500)
for _, fountain in pairs(moneyFountains) do
if not sentState[fountain.id] then
GlobalState['fountain_' .. fountain.id] = math.tointeger(getMoneyForId(fountain.id))
sentState[fountain.id] = true
end
end
end
end)