Old Lady Cleaning

This commit is contained in:
JericoFX committed 2025-05-11 12:58:01 -03:00
1 parent 67c9fbf783
commit 78444c7aae
1 file changed
+226 -189
+226 -189
View File
@@ -1,112 +1,158 @@
-- Cutscene Manager for FiveM
-- Handles cutscene loading, playback, and character management
---@class Cutscene
Cutscenes = Cutscenes or {}
Cutscene = Cutscene or {}
Cutscene.done = true
local names = {
{male = 'MP_1'},
{male = 'MP_2'},
{male = 'MP_3'},
{male = 'MP_4'},
{male = 'MP_Male_Character', female = 'MP_Female_Character'},
{male = 'MP_Male_Character_1', female = 'MP_Female_Character_1'},
{male = 'MP_Male_Character_2', female = 'MP_Female_Character_2'},
{male = 'MP_Male_Character_3', female = 'MP_Female_Character_3'},
{male = 'MP_Male_Character_4', female = 'MP_Female_Character_4'},
{male = 'MP_Plane_Passenger_1'},
{male = 'MP_Plane_Passenger_2'},
{male = 'MP_Plane_Passenger_3'},
{male = 'MP_Plane_Passenger_4'},
{male = 'MP_Plane_Passenger_5'},
{male = 'MP_Plane_Passenger_6'},
{male = 'MP_Plane_Passenger_7'},
{male = 'MP_Plane_Passenger_8'},
{male = 'MP_Plane_Passenger_9'},
-- Constants
local LOAD_TIMEOUT <const> = 10000
local FADE_DURATION <const> = 1000
local CUTSCENE_WAIT <const> = 1000
-- Character model definitions
local characterTags <const> = {
{ male = 'MP_1' },
{ male = 'MP_2' },
{ male = 'MP_3' },
{ male = 'MP_4' },
{ male = 'MP_Male_Character', female = 'MP_Female_Character' },
{ male = 'MP_Male_Character_1', female = 'MP_Female_Character_1' },
{ male = 'MP_Male_Character_2', female = 'MP_Female_Character_2' },
{ male = 'MP_Male_Character_3', female = 'MP_Female_Character_3' },
{ male = 'MP_Male_Character_4', female = 'MP_Female_Character_4' },
{ male = 'MP_Plane_Passenger_1' },
{ male = 'MP_Plane_Passenger_2' },
{ male = 'MP_Plane_Passenger_3' },
{ male = 'MP_Plane_Passenger_4' },
{ male = 'MP_Plane_Passenger_5' },
{ male = 'MP_Plane_Passenger_6' },
{ male = 'MP_Plane_Passenger_7' },
{ male = 'MP_Plane_Passenger_8' },
{ male = 'MP_Plane_Passenger_9' },
}
function Cutscene.GetTags(cutscene)
if not Cutscene.Load(cutscene) then return end
StartCutscene(0)
Wait(1000)
local tags = {}
for k, tag in pairs(names) do
if DoesCutsceneEntityExist(tag.male, 0) or DoesCutsceneEntityExist(tag.female, 0) then
table.insert(tags, tag)
end
-- Component definitions for character customization
local componentsToSave <const> = {
-- Components
{ name = "head", id = 0, type = "drawable" },
{ name = "beard", id = 1, type = "drawable" },
{ name = "hair", id = 2, type = "drawable" },
{ name = "arms", id = 3, type = "drawable" },
{ name = "pants", id = 4, type = "drawable" },
{ name = "parachute", id = 5, type = "drawable" },
{ name = "feet", id = 6, type = "drawable" },
{ name = "accessories", id = 7, type = "drawable" },
{ name = "undershirt", id = 8, type = "drawable" },
{ name = "vest", id = 9, type = "drawable" },
{ name = "decals", id = 10, type = "drawable" },
{ name = "jacket", id = 11, type = "drawable" },
-- Props
{ name = "hat", id = 0, type = "prop" },
{ name = "glasses", id = 1, type = "prop" },
{ name = "ears", id = 2, type = "prop" },
{ name = "watch", id = 3, type = "prop" },
{ name = "bracelet", id = 4, type = "prop" },
{ name = "misc", id = 5, type = "prop" },
{ name = "left_wrist", id = 6, type = "prop" },
{ name = "right_wrist", id = 7, type = "prop" },
{ name = "prop8", id = 8, type = "prop" },
{ name = "prop9", id = 9, type = "prop" },
}
-- Utility Functions
function table.shallow_copy(t)
local t2 = {}
for k, v in pairs(t) do
t2[k] = v
end
StopCutsceneImmediately()
Wait(2000)
return tags
return t2
end
function Cutscene.Load(cutscene)
assert(cutscene, "Cutscene.Load called without a cutscene name.")
if IsPedMale(PlayerPedId()) then
RequestCutsceneWithPlaybackList(cutscene, 31, 8)
else
RequestCutsceneWithPlaybackList(cutscene, 103, 8)
end
local timeout = GetGameTimer() + 10000
while not (HasCutsceneLoaded()) and GetGameTimer() < timeout do
local function WaitForModelLoad(modelHash)
local timeout = GetGameTimer() + LOAD_TIMEOUT
while not HasModelLoaded(modelHash) and GetGameTimer() < timeout do
Wait(0)
end
return HasModelLoaded(modelHash)
end
local function CreatePedFromModel(modelName, coords)
local model = GetHashKey(modelName)
RequestModel(model)
if not WaitForModelLoad(model) then
print("Failed to load model: " .. modelName)
return nil
end
local ped = CreatePed(0, model, coords.x, coords.y, coords.z, 0.0, true, false)
if not DoesEntityExist(ped) then
print("Failed to create ped from model: " .. modelName)
return nil
end
return ped
end
-- Cutscene Core Functions
local function LoadCutscene(cutscene)
assert(cutscene, "Cutscene.Load called without a cutscene name.")
local playbackList = IsPedMale(PlayerPedId()) and 31 or 103
RequestCutsceneWithPlaybackList(cutscene, playbackList, 8)
local timeout = GetGameTimer() + LOAD_TIMEOUT
while not HasCutsceneLoaded() and GetGameTimer() < timeout do
Wait(0)
end
if not HasCutsceneLoaded() then
print("Cutscene failed to load: ", cutscene)
return false
end
return true
end
function Cutscene.SavePedOutfit(ped)
local outfitData = {}
local componentsToSave = {
-- Components
{ name = "head", id = 0, type = "drawable" },
{ name = "beard", id = 1, type = "drawable" },
{ name = "hair", id = 2, type = "drawable" },
{ name = "arms", id = 3, type = "drawable" },
{ name = "pants", id = 4, type = "drawable" },
{ name = "parachute", id = 5, type = "drawable" },
{ name = "feet", id = 6, type = "drawable" },
{ name = "accessories", id = 7, type = "drawable" },
{ name = "undershirt", id = 8, type = "drawable" },
{ name = "vest", id = 9, type = "drawable" },
{ name = "decals", id = 10, type = "drawable" },
{ name = "jacket", id = 11, type = "drawable" },
local function GetCutsceneTags(cutscene)
if not LoadCutscene(cutscene) then return end
-- Props
{ name = "hat", id = 0, type = "prop" },
{ name = "glasses", id = 1, type = "prop" },
{ name = "ears", id = 2, type = "prop" },
{ name = "watch", id = 3, type = "prop" },
{ name = "bracelet", id = 4, type = "prop" },
{ name = "misc", id = 5, type = "prop" },
{ name = "left_wrist", id = 6, type = "prop" },
{ name = "right_wrist", id = 7, type = "prop" },
{ name = "prop8", id = 8, type = "prop" },
{ name = "prop9", id = 9, type = "prop" },
}
StartCutscene(0)
Wait(CUTSCENE_WAIT)
local tags = {}
for _, tag in pairs(characterTags) do
if DoesCutsceneEntityExist(tag.male, 0) or DoesCutsceneEntityExist(tag.female, 0) then
table.insert(tags, tag)
end
end
StopCutsceneImmediately()
Wait(CUTSCENE_WAIT * 2)
return tags
end
-- Character Outfit Management
local function SavePedOutfit(ped)
local outfitData = {}
for _, component in ipairs(componentsToSave) do
if component.type == "drawable" then
local drawable = GetPedDrawableVariation(ped, component.id)
local texture = GetPedTextureVariation(ped, component.id)
local palette = GetPedPaletteVariation(ped, component.id)
outfitData[component.name] = {
id = component.id,
type = component.type,
drawable = drawable,
texture = texture,
palette = palette,
drawable = GetPedDrawableVariation(ped, component.id),
texture = GetPedTextureVariation(ped, component.id),
palette = GetPedPaletteVariation(ped, component.id),
}
elseif component.type == "prop" then
local propIndex = GetPedPropIndex(ped, component.id)
local propTexture = GetPedPropTextureIndex(ped, component.id)
outfitData[component.name] = {
id = component.id,
type = component.type,
propIndex = propIndex,
propTexture = propTexture,
propIndex = GetPedPropIndex(ped, component.id),
propTexture = GetPedPropTextureIndex(ped, component.id),
}
end
end
@@ -114,7 +160,7 @@ function Cutscene.SavePedOutfit(ped)
return outfitData
end
function Cutscene.ApplyPedOutfit(ped, outfitData)
local function ApplyPedOutfit(ped, outfitData)
if not outfitData or type(outfitData) ~= "table" then
print("ApplyPedOutfit: Invalid outfitData provided.")
return
@@ -122,29 +168,40 @@ function Cutscene.ApplyPedOutfit(ped, outfitData)
for componentName, data in pairs(outfitData) do
if data.type == "drawable" then
-- Ensure all values are present before applying
local drawable = data.drawable or 0
local texture = data.texture or 0
local palette = data.palette or 0
SetPedComponentVariation(ped, data.id, drawable, texture, palette)
SetPedComponentVariation(ped, data.id, data.drawable or 0, data.texture or 0, data.palette or 0)
elseif data.type == "prop" then
-- Clear prop if it's marked as not present (-1 index)
if data.propIndex == -1 or data.propIndex == nil then
ClearPedProp(ped, data.id)
else
local propTexture = data.propTexture or 0
SetPedPropIndex(ped, data.id, data.propIndex, propTexture, true)
SetPedPropIndex(ped, data.id, data.propIndex, data.propTexture or 0, true)
end
end
end
end
-- Public Interface
function Cutscene.GetTags(cutscene)
return GetCutsceneTags(cutscene)
end
function Cutscene.Load(cutscene)
return LoadCutscene(cutscene)
end
function Cutscene.SavePedOutfit(ped)
return SavePedOutfit(ped)
end
function Cutscene.ApplyPedOutfit(ped, outfitData)
return ApplyPedOutfit(ped, outfitData)
end
function Cutscene.Create(cutscene, coords, srcs)
local lastCoords = coords or GetEntityCoords(PlayerPedId())
DoScreenFadeOut(0)
local tagsFromCutscene = Cutscene.GetTags(cutscene)
if not Cutscene.Load(cutscene) then
local tagsFromCutscene = GetCutsceneTags(cutscene)
if not LoadCutscene(cutscene) then
print("Cutscene.Create: Failed to load cutscene", cutscene)
DoScreenFadeIn(0)
return false
@@ -152,100 +209,85 @@ function Cutscene.Create(cutscene, coords, srcs)
srcs = srcs or {}
local clothes = {}
local localped = PlayerPedId()
local playersToProcess = {}
table.insert(playersToProcess, { ped = localped, identifier = "localplayer", coords = lastCoords})
for i, src_raw in ipairs(srcs) do
if type(src_raw) == 'number' then
if src_raw and not DoesEntityExist(src_raw) then
local playerPed = GetPlayerPed(GetPlayerFromServerId(src_raw)) -- Assuming GetPlayerPed takes server ID
-- Process players and create necessary peds
local playersToProcess = { { ped = localped, identifier = "localplayer", coords = lastCoords } }
for _, src_raw in ipairs(srcs) do
if type(src_raw) == 'number' then
if src_raw and not DoesEntityExist(src_raw) then
local playerPed = GetPlayerPed(GetPlayerFromServerId(src_raw))
if DoesEntityExist(playerPed) then
local ped = ClonePed(playerPed, false, false, true)
table.insert(playersToProcess, { ped = ped, identifier = "player" })
else
print("Cutscene.Create: Ped for src " .. src_raw .. " does not exist.")
end
else
table.insert(playersToProcess, { ped = src_raw, identifier = "user"}) --developer passed a ped
else
table.insert(playersToProcess, { ped = src_raw, identifier = "user" })
end
elseif type(src_raw) == 'string' then
-- impliment creating a ped by model name
local model = GetHashKey(src_raw)
RequestModel(model)
local timeout = GetGameTimer() + 10000
while not HasModelLoaded(model) and GetGameTimer() < timeout do
Wait(0)
end
if not HasModelLoaded(model) then
print("Cutscene.Create: Failed to load model " .. src_raw)
return false
end
local pos = GetEntityCoords(localped)
local ped = CreatePed(0, model, pos.x, pos.y, pos.z, 0.0, true, false)
if ped and DoesEntityExist(ped) then
local ped = CreatePedFromModel(src_raw, GetEntityCoords(localped))
if ped then
table.insert(playersToProcess, { ped = ped, identifier = 'script' })
else
print("Cutscene.Create: Failed to create ped for src " .. src_raw)
end
elseif type(src_raw) == 'boolean' and src_raw then
print("Cutscene.Create: src_raw is a boolean, creating random ped currently not implimented.")
-- local pos = GetEntityCoords(localped)
-- local ped = CreateRandomPed(pos.x, pos.y, pos.z)
-- if ped and DoesEntityExist(ped)then
-- table.insert(playersToProcess, { ped = ped, identifier = 'script' })
-- else
-- print("Cutscene.Create: Failed to create random ped for src " )
-- end
end
end
local availableTags = {} -- Create a mutable copy of tags
for _, tagName in ipairs(tagsFromCutscene or {}) do
table.insert(availableTags, tagName)
end
-- Process available tags and assign to players
local availableTags = table.shallow_copy(tagsFromCutscene or {})
local usedTags = {}
local cleanupTags = {}
for _, playerData in ipairs(playersToProcess) do
local currentPed = playerData.ped
local isPedMale = IsPedMale(currentPed)
local tagTable = availableTags[1]
if not currentPed or not DoesEntityExist(currentPed) then goto continue end
local tagTable = table.remove(availableTags, 1)
if not tagTable then
print("Cutscene.Create: No available tags for player", playerData.identifier)
break
end
local tag = not isPedMale and tagTable.female or tagTable.male
local unusedTag = nil
if isPedMale and tagTable.female then
unusedTag = isPedMale and tagTable.female or tagTable.male
end
local isPedMale = IsPedMale(currentPed)
local tag = isPedMale and tagTable.male or tagTable.female
local unusedTag = isPedMale and tagTable.female or tagTable.male
SetCutsceneEntityStreamingFlags(tag, 0, 1)
RegisterEntityForCutscene(currentPed, tag, 0, GetEntityModel(currentPed), 64)
SetCutscenePedComponentVariationFromPed(tag, currentPed, 0)
clothes[tag] = { clothing = Cutscene.SavePedOutfit(currentPed), ped = currentPed }
table.insert(usedTags, tag) -- Track used tags
table.insert(cleanupTags, unusedTag) -- Track tags to be cleaned up later
table.remove(availableTags, 1) -- Remove used tag
SetCutscenePedComponentVariationFromPed(tag, currentPed, 0)
clothes[tag] = {
clothing = SavePedOutfit(currentPed),
ped = currentPed
}
table.insert(usedTags, tag)
if unusedTag then table.insert(cleanupTags, unusedTag) end
::continue::
end
-- Clean up unused tags
for _, tag in ipairs(cleanupTags) do
local ped = RegisterEntityForCutscene(0, tag, 3, 0, 64)
NetworkSetEntityInvisibleToNetwork(ped, true)
if ped then
SetEntityVisible(ped, false, false)
end
end
-- Handle remaining unused tags
for _, tag in ipairs(availableTags) do
local ped1 = RegisterEntityForCutscene(0, tag.male, 3, 0, 64)
NetworkSetEntityInvisibleToNetwork(ped1, true)
local ped2 = RegisterEntityForCutscene(0, tag.female, 3, 0, 64)
NetworkSetEntityInvisibleToNetwork(ped2, true)
for _, tagType in pairs({ tag.male, tag.female }) do
if tagType then
local ped = RegisterEntityForCutscene(0, tagType, 3, 0, 64)
if ped then
SetEntityVisible(ped, false, false)
end
end
end
end
local data = {
return {
cutscene = cutscene,
coords = coords,
tags = usedTags,
@@ -253,44 +295,47 @@ function Cutscene.Create(cutscene, coords, srcs)
peds = playersToProcess,
clothes = clothes,
}
Cutscene.done = false
return data
end
function Cutscene.Start(cutsceneData)
DoScreenFadeIn(1000)
Cutscene.done = false
if not cutsceneData then
print("Cutscene.Start: Cutscene data is nil.")
return
end
DoScreenFadeIn(FADE_DURATION)
Cutscene.done = false
local clothes = cutsceneData.clothes
local coords = cutsceneData.coords
if coords then
-- Start cutscene
if coords then
if type(coords) == 'boolean' then
coords = GetEntityCoords(PlayerPedId())
end
StartCutsceneAtCoords(coords.x, coords.y, coords.z, 0 )
StartCutsceneAtCoords(coords.x, coords.y, coords.z, 0)
else
StartCutscene(0)
end
Wait(100)
-- Apply clothing to cutscene characters
for k, datam in pairs(clothes) do
local ped = datam.ped
if DoesEntityExist(ped) then
SetCutscenePedComponentVariationFromPed(k, ped, 0)
Cutscene.ApplyPedOutfit(ped, datam.clothing)
else
print("Cutscene.Create: Ped " .. k .. " does not exist.")
ApplyPedOutfit(ped, datam.clothing)
end
end
local lastCoords = nil
-- Scene loading thread
CreateThread(function()
while not Cutscene.done do
local lastCoords
while not Cutscene.done do
local coords = GetWorldCoordFromScreenCoord(0.5, 0.5)
if not lastCoords or (lastCoords and #(lastCoords - coords) > 100) then
if not lastCoords or #(lastCoords - coords) > 100 then
NewLoadSceneStartSphere(coords.x, coords.y, coords.z, 2000, 0)
lastCoords = coords
end
@@ -298,6 +343,7 @@ function Cutscene.Start(cutsceneData)
end
end)
-- Control blocking thread
CreateThread(function()
while not Cutscene.done do
DisableAllControlActions(0)
@@ -306,43 +352,34 @@ function Cutscene.Start(cutsceneData)
end
end)
while not HasCutsceneFinished() and not Cutscene.done do
-- Main cutscene loop
while not HasCutsceneFinished() and not Cutscene.done do
Wait(0)
if IsDisabledControlJustPressed(0, 200) then
DoScreenFadeOut(1000)
Wait(1000)
DoScreenFadeOut(FADE_DURATION)
Wait(FADE_DURATION)
StopCutsceneImmediately()
Wait(500)
Wait(500)
Cutscene.done = true
break
end
end
DoScreenFadeIn(1000)
-- clean up any peds marked as 'script'
-- Cleanup
DoScreenFadeIn(FADE_DURATION)
for _, playerData in ipairs(cutsceneData.peds) do
local ped = playerData.ped
if ped and DoesEntityExist(ped) and playerData.identifier == 'script' then
if not ped or not DoesEntityExist(ped) then goto continue end
if playerData.identifier == 'script' then
DeleteEntity(ped)
elseif playerData.identifier == 'localplayer' then
SetEntityCoords(ped, playerData.coords.x, playerData.coords.y, playerData.coords.z, false, false, false,
false)
end
if ped and DoesEntityExist(ped) and playerData.identifier == 'localplayer' then
SetEntityCoords(ped, playerData.coords.x, playerData.coords.y, playerData.coords.z)
end
::continue::
end
Cutscene.done = true
end
-- RegisterCommand('cutscene', function(source, args, rawCommand)
-- if not Cutscene.done then return end
-- local cutscene = args[1]
-- if not cutscene then return print("Usage: /cutscene <cutscene_name>") end
-- local cutsceneData = Cutscene.Create(cutscene, nil, {1, 1, 1 })
-- Cutscene.Start(cutsceneData)
-- end, false)
return Cutscene
return Cutscene