Skip to content

Commit

Permalink
Add player horse looting (#110)
Browse files Browse the repository at this point in the history
* Update comps.lua

Updated list of comps

* Update fxmanifest.lua

- version bump

* Update version

- version bump

* Update package.json

- version bump

* Update server/main.lua

- add check for registered inventory

* Update client/main.lua

- add function for horse holster
- housekeeping

* Update client/main.lua

* Update bcc_stables.sql

- housekeeping

* Update config/main.lua

- add key for horse looting

* Update fxmanifest.lua

- version bump

* Update version

- version bump

* languages

* Update README.md

* Update package.json

- version bump

* Update server/main.lua

- add function to save horse id for player horse looting

* Update client/main.lua

- add functions to allow player horse looting
- housekeeping

* Update client/main.lua

- add inventory sharing check to horse spawn

---------

Co-authored-by: BurntJannings <82676805+BurntJannings@users.noreply.github.com>
  • Loading branch information
JusCampin and BurntJannings authored Jun 10, 2024
1 parent 0fecb7d commit 1752cbb
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 42 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Groom, feed, and clean your horse to keep them at peak performance.
- Horse Trading between players.
- Config option: Only trainers can buy horses from a stable (set per stable).
- Revive your downed horse using the horse reviver item.
- Allow player horse looting.

## Horse Training
### XP System
Expand Down
26 changes: 13 additions & 13 deletions bcc_stables.sql
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
CREATE TABLE IF NOT EXISTS `player_horses` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`id` INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`identifier` VARCHAR(50) NOT NULL,
`charid` INT(11) NOT NULL,
`selected` int(11) NOT NULL DEFAULT 0,
`selected` INT(11) NOT NULL DEFAULT 0,
`name` VARCHAR(100) NOT NULL,
`model` VARCHAR(100) NOT NULL,
`gender` ENUM('male', 'female') DEFAULT 'male',
`components` varchar(5000) NOT NULL DEFAULT '{}',
`xp` int(11) NOT NULL DEFAULT 0,
`health` int(11) NOT NULL DEFAULT 50,
`stamina` int(11) NOT NULL DEFAULT 50,
`captured` int(11) NOT NULL DEFAULT 0,
`born` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP()
`components` VARCHAR(5000) NOT NULL DEFAULT '{}',
`xp` INT(11) NOT NULL DEFAULT 0,
`health` INT(11) NOT NULL DEFAULT 50,
`stamina` INT(11) NOT NULL DEFAULT 50,
`captured` INT(11) NOT NULL DEFAULT 0,
`born` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP()
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`gender` ENUM('male', 'female') DEFAULT 'male');
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`xp` int(11) NOT NULL DEFAULT 0);
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`captured` int(11) NOT NULL DEFAULT 0);
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`born` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP());
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`health` int(11) NOT NULL DEFAULT 50);
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`stamina` int(11) NOT NULL DEFAULT 50);
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`xp` INT(11) NOT NULL DEFAULT 0);
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`captured` INT(11) NOT NULL DEFAULT 0);
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`born` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP());
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`health` INT(11) NOT NULL DEFAULT 50);
ALTER TABLE `player_horses` ADD COLUMN IF NOT EXISTS (`stamina` INT(11) NOT NULL DEFAULT 50);

INSERT INTO `items`(`item`, `label`, `limit`, `can_remove`, `type`, `usable`) VALUES ('oil_lantern', 'Oil Lantern', 1, 1, 'item_standard', 1)
ON DUPLICATE KEY UPDATE `item`='oil_lantern', `label`='Oil Lantern', `limit`=1, `can_remove`=1, `type`='item_standard', `usable`=1;
Expand Down
95 changes: 71 additions & 24 deletions client/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ local TameGroup = GetRandomIntInRange(0, 0xffffff)
local TradeHorse
local TradeGroup = GetRandomIntInRange(0, 0xffffff)

-- Loot Prompts
local LootHorse
local LootGroup = GetRandomIntInRange(0, 0xffffff)

-- Target Prompts
local HorseDrink, HorseRest, HorseSleep, HorseWallow = nil, nil, nil, nil

Expand Down Expand Up @@ -56,14 +60,14 @@ CreateThread(function()
PromptSetEnabled(OpenShops, false)
PromptSetEnabled(OpenCall, Config.closedCall)
PromptSetEnabled(OpenReturn, Config.closedReturn)
if Citizen.InvokeNative(0xC92AC953F0A982AE, OpenCall) then -- UiPromptHasStandardModeCompleted
if Citizen.InvokeNative(0xC92AC953F0A982AE, OpenCall) then -- PromptHasStandardModeCompleted
if siteCfg.shop.jobsEnabled then
CheckPlayerJob(false, site)
if not HasJob then goto END end
end
GetSelectedHorse()
end
if Citizen.InvokeNative(0xC92AC953F0A982AE, OpenReturn) then -- UiPromptHasStandardModeCompleted
if Citizen.InvokeNative(0xC92AC953F0A982AE, OpenReturn) then -- PromptHasStandardModeCompleted
if siteCfg.shop.jobsEnabled then
CheckPlayerJob(false, site)
if not HasJob then goto END end
Expand All @@ -88,21 +92,21 @@ CreateThread(function()
PromptSetEnabled(OpenCall, true)
PromptSetEnabled(OpenReturn, true)

if Citizen.InvokeNative(0xC92AC953F0A982AE, OpenShops) then -- UiPromptHasStandardModeCompleted
if Citizen.InvokeNative(0xC92AC953F0A982AE, OpenShops) then -- PromptHasStandardModeCompleted
CheckPlayerJob(false, site)
if siteCfg.shop.jobsEnabled then
if not HasJob then goto END end
end
OpenStable(site)

elseif Citizen.InvokeNative(0xC92AC953F0A982AE, OpenCall) then -- UiPromptHasStandardModeCompleted
elseif Citizen.InvokeNative(0xC92AC953F0A982AE, OpenCall) then -- PromptHasStandardModeCompleted
if siteCfg.shop.jobsEnabled then
CheckPlayerJob(false, site)
if not HasJob then goto END end
end
GetSelectedHorse()

elseif Citizen.InvokeNative(0xC92AC953F0A982AE, OpenReturn) then -- UiPromptHasStandardModeCompleted
elseif Citizen.InvokeNative(0xC92AC953F0A982AE, OpenReturn) then -- PromptHasStandardModeCompleted
if siteCfg.shop.jobsEnabled then
CheckPlayerJob(false, site)
if not HasJob then goto END end
Expand Down Expand Up @@ -419,7 +423,10 @@ function SpawnHorse(data)
end
Spawning = true

DeleteMyHorse()
if MyHorse then
DeleteEntity(MyHorse)
MyHorse = nil
end

local horseModel = data.model
local xp = data.xp
Expand Down Expand Up @@ -527,6 +534,11 @@ function SpawnHorse(data)

TriggerServerEvent('bcc-stables:RegisterInventory', MyHorseId, horseModel)

if Config.shareInventory then
local myHorseNetId = NetworkGetNetworkIdFromEntity(MyHorse)
TriggerServerEvent('bcc-stables:SetLootHorseData', myHorseNetId, MyHorseId)
end

if Config.horseTag then
TriggerEvent('bcc-stables:HorseTag')
end
Expand All @@ -544,6 +556,34 @@ function SpawnHorse(data)
SendHorse()
end

-- Loot Players Horse Inventory
CreateThread(function()
if Config.shareInventory then
while true do
local pedId, horseId, isLeading, ownerOfMount = nil, nil, nil, nil
local playerPed = PlayerPedId()
local sleep = 1000

pedId = Citizen.InvokeNative(0x0501D52D24EA8934, 1, Citizen.ResultAsInteger()) -- Get HorsePedId in Range

if (IsEntityDead(playerPed)) or (pedId == 0) or (pedId == MyHorse) then goto END end

ownerOfMount = Citizen.InvokeNative(0xAD03B03737CE6810, pedId) -- GetPlayerOwnerOfMount
isLeading = Citizen.InvokeNative(0xEFC4303DDC6E60D3, playerPed) -- IsPedLeadingHorse
if (ownerOfMount == 255) or isLeading then goto END end

sleep = 0
PromptSetActiveGroupThisFrame(LootGroup, CreateVarString(10, 'LITERAL_STRING', _U('lootInventory')), 1, 0, 0, 0)
if Citizen.InvokeNative(0xC92AC953F0A982AE, LootHorse) then -- PromptHasStandardModeCompleted
horseId = Entity(pedId).state.myHorseId
OpenInventory(pedId, horseId, true)
end
::END::
Wait(sleep)
end
end
end)

-- Set Horse Name and Health Bar Above Horse
AddEventHandler('bcc-stables:HorseTag', function()
local gamerTagId = Citizen.InvokeNative(0xE961BF23EAB76B12, MyHorse, HorseName) -- CreateMpGamerTagOnEntity
Expand Down Expand Up @@ -584,7 +624,7 @@ AddEventHandler('bcc-stables:HorsePrompts', function()
sleep = 0

if Citizen.InvokeNative(0x91AEF906BCA88877, 0, `INPUT_OPEN_SATCHEL_HORSE_MENU`) then -- IsDisabledControlJustPressed
OpenInventory()
OpenInventory(MyHorse, MyHorseId, false)
end

if InWrithe then
Expand Down Expand Up @@ -798,7 +838,8 @@ function WhistleHorse()
if Citizen.InvokeNative(0x77F1BEB8863288D5, MyHorse, 0x4924437D, 0) ~= 0 then -- GetScriptTaskStatus
local dist = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(MyHorse))
if dist >= 100 then
DeleteMyHorse()
DeleteEntity(MyHorse)
MyHorse = nil
GetSelectedHorse()
else
Sending = true
Expand Down Expand Up @@ -1004,7 +1045,8 @@ AddEventHandler('bcc-stables:HorseDamaged', function()
if IsEntityDead(MyHorse) then
SaveHorseStats(true)
Wait(5000)
DeleteMyHorse()
DeleteEntity(MyHorse)
MyHorse = nil
return
end

Expand Down Expand Up @@ -1047,10 +1089,10 @@ AddEventHandler('bcc-stables:ReviveHorse', function()
end
end)

function OpenInventory()
local hasBags = Citizen.InvokeNative(0xFB4891BD7578CDC1, MyHorse, -2142954459) -- IsMetaPedUsingComponent
function OpenInventory(horsePedId, horseId, isLooting)
local hasBags = Citizen.InvokeNative(0xFB4891BD7578CDC1, horsePedId, -2142954459) -- IsMetaPedUsingComponent

if Config.useSaddlebags and not hasBags then
if not isLooting and Config.useSaddlebags and not hasBags then
VORPcore.NotifyRightTip(_U('noSaddlebags'), 4000)
return
end
Expand All @@ -1059,14 +1101,15 @@ function OpenInventory()
Citizen.InvokeNative(0xCD181A959CFDD7F4, PlayerPedId(), MyHorse, joaat('Interaction_LootSaddleBags'), 0, 1) -- TaskAnimalInteraction
end

TriggerServerEvent('bcc-stables:OpenInventory', MyHorseId)
TriggerServerEvent('bcc-stables:OpenInventory', horseId)
end

function FleeHorse()
Citizen.InvokeNative(0x22B0D0E37CCB840D, MyHorse, PlayerPedId(), 150.0, 10000, 6, 3.0) -- TaskSmartFleePed
SaveHorseStats(false)
Wait(10000)
DeleteMyHorse()
DeleteEntity(MyHorse)
MyHorse = nil
end

AddEventHandler('bcc-stables:HorseBonding', function()
Expand Down Expand Up @@ -1387,7 +1430,6 @@ AddEventHandler('bcc-stables:TradeHorse', function()
if closestPlayer and closestDistance <= 2.0 then
sleep = 0
PromptSetActiveGroupThisFrame(TradeGroup, CreateVarString(10, 'LITERAL_STRING', HorseName), 1, 0, 0, 0)
PromptSetEnabled(TradeHorse, true)
if Citizen.InvokeNative(0xE0F65F0640EF0617, TradeHorse) then -- PromptHasHoldModeCompleted
local serverId = GetPlayerServerId(closestPlayer)
TriggerServerEvent('bcc-stables:SaveHorseTrade', serverId, MyHorseId)
Expand Down Expand Up @@ -1608,7 +1650,8 @@ function ReturnHorse()
end

SaveHorseStats(false)
DeleteMyHorse()
DeleteEntity(MyHorse)
MyHorse = nil
VORPcore.NotifyRightTip(_U('horseReturned'), 4000)
end

Expand Down Expand Up @@ -1684,13 +1727,6 @@ function Rotation(dir)
SetEntityHeading(entity, (GetEntityHeading(entity) + dir) % 360)
end

function DeleteMyHorse()
if MyHorse then
DeleteEntity(MyHorse)
MyHorse = nil
end
end

RegisterCommand(Config.commands.horseRespawn, function(source, args, rawCommand)
Spawning = false
WhistleSpawn()
Expand Down Expand Up @@ -1771,9 +1807,19 @@ function StartPrompts()
PromptSetControlAction(TradeHorse, Config.keys.trade)
PromptSetText(TradeHorse, CreateVarString(10, 'LITERAL_STRING', _U('tradePrompt')))
PromptSetVisible(TradeHorse, true)
PromptSetEnabled(TradeHorse, true)
PromptSetHoldMode(TradeHorse, 2000)
PromptSetGroup(TradeHorse, TradeGroup, 0)
PromptRegisterEnd(TradeHorse)

LootHorse = PromptRegisterBegin()
PromptSetControlAction(LootHorse, Config.keys.loot)
PromptSetText(LootHorse, CreateVarString(10, 'LITERAL_STRING', _U('lootHorsePrompt')))
PromptSetVisible(LootHorse, true)
PromptSetEnabled(LootHorse, true)
PromptSetStandardMode(LootHorse)
PromptSetGroup(LootHorse, LootGroup, 0)
PromptRegisterEnd(LootHorse)
end

function HorseTargetPrompts(menuGroup)
Expand Down Expand Up @@ -2067,7 +2113,8 @@ AddEventHandler('onResourceStop', function(resourceName)

if MyHorse then
SaveHorseStats(false)
DeleteMyHorse()
DeleteEntity(MyHorse)
MyHorse = nil
end
for _, siteCfg in pairs(Stables) do
if siteCfg.Blip then
Expand Down
3 changes: 2 additions & 1 deletion config/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Config.keys = {
rest = 0x620A6C5E, --[V] Horse Rest
sleep = 0x43CDA5B0, --[Z] Horse Sleep
wallow = 0x9959A6F0, --[C] Horse Wallow
loot = 0x27D1C284, --[R] Loot Horse
}
-----------------------------------------------------

Expand Down Expand Up @@ -73,7 +74,7 @@ Config.writheHealth = 35 -- Default: 35 / Set to 0 to Disable

-- Horse Inventory
Config.useSaddlebags = true -- Default: true / Require Saddlebags for Inventory
Config.shareInventory = false -- Default: false / Share with All Players
Config.shareInventory = false -- Default: false / Share with All Players / Allows Looting Player Horses
Config.allowWeapons = true -- Default: true / Allow Weapons
-----------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion fxmanifest.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ files {
"ui/img/*.*"
}

version '1.4.7'
version '1.4.8'

2 changes: 2 additions & 0 deletions languages/en_lang.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Locales["en_lang"] = {
sleepPrompt = "Sleep",
wallowPrompt = "Wallow",
invPrompt = "Horse Cargo",
lootHorsePrompt = 'Open',
lootInventory = 'Horse Cargo',
closed = " ~o~: ~e~CLOSED",
to = ":00~q~ to ~o~",
hundred = ":00",
Expand Down
2 changes: 2 additions & 0 deletions languages/fr_lang.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Locales["fr_lang"] = {
sleepPrompt = "Sleep",
wallowPrompt = "Wallow",
invPrompt = "Horse Cargo",
lootHorsePrompt = 'Open',
lootInventory = 'Horse Cargo',
closed = " ~o~: ~e~FERMÉ",
to = ":00~q~ à ~o~",
hundred = ":00",
Expand Down
2 changes: 2 additions & 0 deletions languages/it_lang.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Locales["it_lang"] = {
sleepPrompt = "Sleep",
wallowPrompt = "Wallow",
invPrompt = "Horse Cargo",
lootHorsePrompt = 'Open',
lootInventory = 'Horse Cargo',
closed = " ~o~: ~e~CHIUSO",
to = ":00~q~ a ~o~",
hundred = ":00",
Expand Down
2 changes: 2 additions & 0 deletions languages/ro_lang.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Locales["ro_lang"] = {
sleepPrompt = "Sleep",
wallowPrompt = "Wallow",
invPrompt = "Horse Cargo",
lootHorsePrompt = 'Open',
lootInventory = 'Horse Cargo',
closed = " ~o~: ~e~INCHIS",
to = ":00~q~ pana la ~o~",
hundred = ":00",
Expand Down
8 changes: 8 additions & 0 deletions server/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ RegisterServerEvent('bcc-stables:SetTamedData', function(netId)
Entity(tamed).state:set('netId', netId, true)
end)

RegisterServerEvent('bcc-stables:SetLootHorseData', function(netId, myHorseId)
if not netId or not myHorseId then
return print('Invalid params sent to SetMyHorseData')
end
local myHorse = NetworkGetEntityFromNetworkId(netId)
Entity(myHorse).state:set('myHorseId', myHorseId, true)
end)

RegisterServerEvent('bcc-stables:SellTamedHorse', function(horseModel)
local src = source
local Character = VORPcore.getUser(src).getUsedCharacter
Expand Down
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "bcc-stables",

"version": "1.4.7",
"version": "1.4.8",

"private": true,
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions version
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<1.4.7>
See GitHub for details!
<1.4.8>
Added: Player horse looting!

0 comments on commit 1752cbb

Please sign in to comment.