├── fxmanifest.lua ├── locales ├── es.lua ├── en.lua └── ro.lua ├── locale.lua ├── .github └── workflows │ └── discord.yml ├── README.md ├── server └── server.lua ├── config.lua └── client └── client.lua /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | 2 | fx_version "adamant" 3 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' 4 | 5 | version '3.0.0' 6 | games {"rdr3"} 7 | 8 | client_scripts { 9 | 'client/*.lua' 10 | } 11 | 12 | shared_scripts { 13 | 'config.lua', 14 | 'locale.lua', 15 | 'locales/*.lua', 16 | } 17 | 18 | server_scripts { 19 | '@oxmysql/lib/MySQL.lua', 20 | 'server/*.lua' 21 | } 22 | 23 | dependency 'vorp_core' 24 | -------------------------------------------------------------------------------- /locales/es.lua: -------------------------------------------------------------------------------- 1 | Locales['es'] = { 2 | NoMoney = 'No tienes suficiente dinero', 3 | Shoptext = 'Usa (E) para mostrar Mascotas en Venta', 4 | GiveAway = 'Reglar Mascota', 5 | ReleasePet = 'Liberaste a tu Mascota', 6 | PetAway = 'Guardas a tu Mascota', 7 | PetHealed = 'La Mascota fue curada por un medico', 8 | SpawnLimiter = 'Animal engendrado recientement, por favor espere', 9 | ReplacePet = 'Reempazo a su antigua Mascota', 10 | NewPet = 'Compraste una nueva Mascota', 11 | NoPet = 'No tienes Mascota' 12 | } -------------------------------------------------------------------------------- /locale.lua: -------------------------------------------------------------------------------- 1 | Locales = {} 2 | 3 | function _(str, ...) -- Translate string 4 | 5 | if Locales[Config.Locale] ~= nil then 6 | 7 | if Locales[Config.Locale][str] ~= nil then 8 | return string.format(Locales[Config.Locale][str], ...) 9 | else 10 | return 'Translation [' .. Config.Locale .. '][' .. str .. '] does not exist' 11 | end 12 | 13 | else 14 | return 'Locale [' .. Config.Locale .. '] does not exist' 15 | end 16 | 17 | end 18 | 19 | function _U(str, ...) -- Translate string first char uppercase 20 | return tostring(_(str, ...):gsub("^%l", string.upper)) 21 | end -------------------------------------------------------------------------------- /.github/workflows/discord.yml: -------------------------------------------------------------------------------- 1 | name: "Release Notification" 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | github-releases-to-discord: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v3 13 | - name: Set env 14 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 15 | - name: Github Releases To Discord 16 | uses: SethCohen/github-releases-to-discord@v1.16.2 17 | with: 18 | webhook_url: ${{ secrets.WEBHOOK_URL }} 19 | color: "15844367" 20 | username: "BCC Changelog" 21 | avatar_url: "https://bcc-scripts.com/logo.png" 22 | content: "# BCC-pets ${{ env.RELEASE_VERSION }} \n\n https://github.com/BryceCanyonCounty/bcc-pets" 23 | footer_timestamp: false 24 | max_description: "4096" 25 | reduce_headings: true 26 | -------------------------------------------------------------------------------- /locales/en.lua: -------------------------------------------------------------------------------- 1 | Locales['en'] = { 2 | NoMoney = 'You do not have enough money', 3 | PetAway = 'You sent your pet home...', 4 | PutAwayPet = 'Send your pet home', 5 | CallPet = 'Call your pet', 6 | PetHealed = 'Pet was healed by a doctor!', 7 | TransferOwnership = 'Transfer ownership', 8 | TransferedOwnership = 'You transfered ownership of your pet!', 9 | TransferedOwnershipRecieve = "You are a new Pet owner!", 10 | NewPet = 'You bought a new pet!', 11 | NoPet = 'You do not have a pet...', 12 | YouAlreadyHaveAPet = 'You have the max amount of pets already...', 13 | AlreadyHasPet = 'This person has the max amount of pets...', 14 | CalledPet = "You have called your ", 15 | PetNotOut = "This pet is not out...", 16 | IncorrectID = "Requires Person's ID #...", 17 | IssueTransferring = "There was an issue transferring the pet...", 18 | IssueBuying = "There was an issue buying the pet...", 19 | SoldPet = "You sold your pet for $" 20 | } -------------------------------------------------------------------------------- /locales/ro.lua: -------------------------------------------------------------------------------- 1 | Locales['ro'] = { 2 | NoMoney = 'Nu ai destui bani', 3 | PetAway = 'Ai trimis animalul acasa...', 4 | PutAwayPet = 'Trimite animalul acasa', 5 | CallPet = 'Cheama-ti animalul', 6 | PetHealed = 'Animalul a fost vindecat de un doctor!', 7 | TransferOwnership = 'Transfera proprietatea', 8 | TransferedOwnership = 'Ai transferat proprietatea animalului tau!', 9 | TransferedOwnershipRecieve = "Esti noul proprietar al animalului!", 10 | NewPet = 'Ai cumparat un animal nou!', 11 | NoPet = 'Nu ai un animal de companie...', 12 | YouAlreadyHaveAPet = 'Ai deja numarul maxim de animale de companie...', 13 | AlreadyHasPet = 'Aceasta persoana are deja numarul maxim de animale de companie...', 14 | CalledPet = "Ai chemat ", 15 | PetNotOut = "Acest animal nu este afara...", 16 | IncorrectID = "Necesita ID-ul persoanei...", 17 | IssueTransferring = "A aparut o problema la transferul animalului...", 18 | IssueBuying = "A aparut o problema la cumpararea animalului...", 19 | SoldPet = "Ai vândut animalul tau pentru $" 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BCC - Pets 2 | 3 | Join the [VORP Community Discord](https://discord.gg/23MPbQ6) 4 | 5 | ## Features 6 | 1. Configurable pet shops 7 | 2. Purchase pets (w/ Optional Joblock) 8 | 3. Pets will follow you around 9 | 4. /pet for full Pet Ineractions menu 10 | 5. Locale config. 11 | - Currently supports [en], [ro] 12 | 13 | ## Installation 14 | 1. Download this repo/codebase 15 | 2. Extract and place `bcc-pets` into your `resources` folder 16 | 3. Add `ensure bcc-pets` to your `server.cfg` file 17 | 4. Restart your server (unless you have nightly restarts) 18 | 19 | ## How-to-configure 20 | All configurations available in `config.lua` 21 | 22 | ## Disclaimers and Credits 23 | - This is a heavy modification of [bwrp_animalshelter](https://github.com/nerakhon/bwrp_animalshelter) (Made by nerakhon), _[bcc-pets will continue to diverge from the original codebase, hence this new codebase]_ 24 | - Based on Malik's and Blue's animal shelters and vorp animal shelter 25 | - Originally based on HORSE SHOP FOR REDEM 0.1v by #mrlupo# / #Proky# / #Plouffe# 26 | 27 | ## Dependency 28 | - Vorp Core 29 | -------------------------------------------------------------------------------- /server/server.lua: -------------------------------------------------------------------------------- 1 | -- VORP 2 | local VorpCore = {} 3 | TriggerEvent("getCore",function(core) 4 | VorpCore = core 5 | end) 6 | VORP = exports.vorp_core:vorpAPI() 7 | 8 | CreateThread(function() 9 | -- Initiate Table 10 | local result = MySQL.query.await([[ 11 | CREATE TABLE IF NOT EXISTS `pets` ( 12 | `petid` int(11) NOT NULL AUTO_INCREMENT, 13 | `identifier` varchar(40) NOT NULL, 14 | `charidentifier` int(11) NOT NULL DEFAULT 0, 15 | `dog` varchar(255) NOT NULL, 16 | `skin` int(11) NOT NULL DEFAULT 0, 17 | `xp` int(11) NOT NULL DEFAULT 0, 18 | `transfered` int(11) NOT NULL DEFAULT 0, 19 | `called` int(11) NOT NULL DEFAULT 0, 20 | KEY `petid` (`petid`) 21 | ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; 22 | ]]) 23 | if not result then 24 | print("ERROR: Failed to create AFK WL Table") 25 | end 26 | end) 27 | 28 | RegisterServerEvent('bcc-pets:sellpet', function(petid, SellPrice) 29 | local _source = source 30 | local User = VorpCore.getUser(_source) 31 | local Character = User.getUsedCharacter 32 | local u_charid = Character.charIdentifier 33 | Character.addCurrency(0, SellPrice) 34 | MySQL.query("DELETE FROM pets WHERE charidentifier = @charidentifier AND petid = @petid", {['charidentifier'] = u_charid, ["petid"] = petid}) 35 | TriggerClientEvent('bcc-pets:removedog', _source, petid) 36 | VorpCore.NotifyRightTip(_source, _U("SoldPet")..SellPrice, 5000) 37 | end) 38 | 39 | RegisterServerEvent('bcc-pets:buydog', function (args, ShopID) 40 | local _source = source 41 | local User = VorpCore.getUser(_source) 42 | local Character = User.getUsedCharacter 43 | local _price = args['Price'] 44 | local _model = args['Model'] 45 | local skin = math.floor(math.random(0, 2)) 46 | local u_identifier = Character.identifier 47 | local u_charid = Character.charIdentifier 48 | local u_money = Character.money 49 | if u_money <= _price then 50 | VorpCore.NotifyRightTip(_source, _U("NoMoney"), 5000) 51 | return 52 | end 53 | local result = MySQL.query.await( "SELECT * FROM pets WHERE identifier = @identifier AND charidentifier = @charidentifier", {['identifier'] = u_identifier, ['charidentifier'] = u_charid}) 54 | if #result <= Config.MaxPets then 55 | local Parameters = { ['identifier'] = u_identifier, ['charidentifier'] = u_charid, ['dog'] = _model, ['skin'] = skin, ['called'] = 1} 56 | local BuyPet = MySQL.insert.await("INSERT INTO pets ( `identifier`,`charidentifier`,`dog`,`skin`,`called` ) VALUES ( @identifier,@charidentifier, @dog, @skin, @called )", Parameters) 57 | if BuyPet then 58 | Character.removeCurrency(0, _price) 59 | TriggerClientEvent('bcc-pets:spawndog', _source, _model, skin, true, BuyPet, ShopID) 60 | VorpCore.NotifyRightTip(_source, _U("NewPet"), 5000) 61 | else 62 | VorpCore.NotifyRightTip(_source, _U("IssueBuying"), 5000) 63 | end 64 | else 65 | VorpCore.NotifyRightTip(_source, _U("YouAlreadyHaveAPet"), 5000) 66 | end 67 | end) 68 | 69 | RegisterServerEvent('bcc-pets:transferownership', function(newownerid, petid) 70 | -- Trading Player 71 | local _source = source 72 | local User = VorpCore.getUser(_source) 73 | local Character = User.getUsedCharacter 74 | local u_identifier = Character.identifier 75 | local u_charid = Character.charIdentifier 76 | -- Check if player owns Pet already 77 | local Parameters1 = { 78 | ['identifier'] = u_identifier, 79 | ['charidentifier'] = u_charid 80 | } 81 | local result = MySQL.query.await("SELECT * FROM pets WHERE identifier = @identifier AND charidentifier = @charidentifier", Parameters1) 82 | if result then 83 | if #result <= Config.MaxPets then 84 | TriggerClientEvent('bcc-pets:removedog', _source, petid) 85 | -- Recieving Player 86 | local UserNew = VorpCore.getUser(newownerid) 87 | local CharacterNew = UserNew.getUsedCharacter 88 | local New_identifier = CharacterNew.identifier 89 | local New_charid = CharacterNew.charIdentifier 90 | local TransferPet = MySQL.update.await("UPDATE pets SET identifier = ?, charidentifier = ?, transfered = ? WHERE petid = ?", {New_identifier, New_charid, 1, petid}) 91 | if TransferPet then 92 | VorpCore.NotifyRightTip(_source, _U("TransferedOwnership"), 5000) 93 | VorpCore.NotifyRightTip(newownerid, _U("TransferedOwnershipRecieve"), 5000) 94 | else 95 | VorpCore.NotifyRightTip(_source, _U("IssueTransferring"), 5000) 96 | VorpCore.NotifyRightTip(newownerid, _U("IssueTransferring"), 5000) 97 | end 98 | else 99 | VorpCore.NotifyRightTip(_source, _U("AlreadyHasPet"), 5000) 100 | VorpCore.NotifyRightTip(newownerid, _U("YouAlreadyHaveAPet"), 5000) 101 | end 102 | else 103 | VorpCore.NotifyRightTip(_source, _U("IssueTransferring"), 5000) 104 | VorpCore.NotifyRightTip(newownerid, _U("IssueTransferring"), 5000) 105 | end 106 | end) 107 | 108 | RegisterServerEvent('bcc-pets:callpet', function(petid) 109 | local _source = source 110 | local User = VorpCore.getUser(_source) 111 | local Character = User.getUsedCharacter 112 | local u_identifier = Character.identifier 113 | local u_charid = Character.charIdentifier 114 | local Parameters1 = { 115 | ['identifier'] = u_identifier, 116 | ['charidentifier'] = u_charid, 117 | ['petid'] = petid 118 | } 119 | local result = MySQL.query.await("SELECT * FROM pets WHERE identifier = @identifier AND charidentifier = @charidentifier AND petid = @petid", Parameters1) 120 | if result then 121 | MySQL.update('UPDATE pets SET called = ? WHERE petid = ?', {1, petid}, function(UpdateOutStatus) 122 | if UpdateOutStatus then 123 | local dog = result[1].dog 124 | local skin = result[1].skin 125 | TriggerClientEvent("bcc-pets:spawndog", _source, dog, skin, false, petid) 126 | VorpCore.NotifyRightTip(_source,_U("CalledPet")..Config.Pets[dog][1].Text, 5000) 127 | end 128 | end) 129 | else 130 | VorpCore.NotifyRightTip(_source,_U("NoPet"), 5000) 131 | end 132 | end) 133 | 134 | RegisterServerEvent('bcc-pets:server:putawaypet', function(petid) 135 | local _source = source 136 | local UpdateOutStatus = MySQL.update.await('UPDATE pets SET called = ? WHERE petid = ?', {0, petid}) 137 | if UpdateOutStatus then 138 | VorpCore.NotifyRightTip(_source,_U("PetAway"), 5000) 139 | end 140 | end) 141 | 142 | VorpCore.addRpcCallback("GetPlayersPets", function(source, cb) 143 | local _source = source 144 | local User = VorpCore.getUser(_source) 145 | local Character = User.getUsedCharacter 146 | local u_identifier = Character.identifier 147 | local u_charid = Character.charIdentifier 148 | local Parameters1 = { 149 | ['identifier'] = u_identifier, 150 | ['charidentifier'] = u_charid, 151 | } 152 | local result = MySQL.query.await('SELECT * FROM pets WHERE identifier = @identifier AND charidentifier = @charidentifier', Parameters1) 153 | if result then 154 | cb(result) 155 | else 156 | cb(false) 157 | end 158 | end) 159 | 160 | if Config.PetMenu.active then 161 | RegisterCommand(Config.PetMenu.command, function(source) 162 | TriggerClientEvent('bcc-pets:openpetmenu', source) 163 | end) 164 | end -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | -- Based on Malik's and Blue's animal shelters and vorp animal shelter -- 2 | 3 | 4 | Config = {} 5 | 6 | Config.Locale = "en" -- en 7 | 8 | Config.AllowMultipleTrades = false -- Allow a pet to be traded more than once 9 | Config.MaxPets = 5 -- Max number of pets a player can own 10 | Config.CallPetHotKey = { 11 | active = false, 12 | key = 0xDFF812F9 13 | } 14 | Config.PetMenu = { 15 | active = true, 16 | command = 'pet', -- Command to open Pet Menu 17 | animation = true, -- Opening book scenario 18 | } 19 | 20 | Config.PetAttributes = { 21 | FollowDistance = 5, 22 | Invincible = true, 23 | } 24 | 25 | Config.SellPercentage = 30 -- % of the pet price the player recieves for selling back to Shops 26 | Config.ShopsPromptKey = 0x760A9C6F -- Hash of the Key used to access the Shop locations 27 | Config.ShopsPromptText = "Shop for a pet" 28 | Config.ShopsPromptDistance = 2.0 -- Should not be larger than 10 29 | Config.Shops = { 30 | { 31 | Name = 'Pet Shelter', -- Name on blip and prompt 32 | Coords = vector3(-273.894287109375, 685.2567138671875, 113.41388702392578), 33 | Spawndog = vector4( -284.09, 685.34, 113.59, 234.45 ), 34 | JobLock = { 35 | purchasing = false, -- true or false - Limit purchasing pets to require a job 36 | selling = false, -- true or false - Limit selling pets to the NPC to require a job 37 | jobs = {'doctor', 'vetenarian', 'shepard'} 38 | }, 39 | Blip = { 40 | active = true, -- true or false 41 | sprite = "blip_animal", -- Use Hashname 42 | scale = 0.5, 43 | coords = vector3(-273.51, 689.26, 113.41), 44 | }, 45 | Ped = { 46 | active = true, -- true or false 47 | modelhash = "am_valentinedoctors_females_01", 48 | coords = vector4(-273.81170654296875, 684.918701171875, 112.41393280029295, 23.742), 49 | frozen = true, 50 | invincible = true, 51 | } 52 | } 53 | } 54 | 55 | Config.Pets = { 56 | ['a_c_frogbull_01'] = { 57 | { 58 | Text = "Bull Frog", 59 | SubText = "", 60 | Desc = "Ribbet Ribbet", 61 | Param = { 62 | Price = 30, 63 | Model = "a_c_frogbull_01", 64 | Level = 1 65 | } 66 | }, 67 | }, 68 | ['a_c_pig_01'] = { 69 | { 70 | Text = "Farm Pig", 71 | SubText = "", 72 | Desc = "Oink oink", 73 | Param = { 74 | Price = 75, 75 | Model = "a_c_pig_01", 76 | Level = 1 77 | } 78 | } 79 | }, 80 | ['A_C_DogHusky_01'] = { 81 | { 82 | Text = "Husky", 83 | SubText = "", 84 | Desc = "Best pet you'll ever have", 85 | Param = { 86 | Price = 200, 87 | Model = "A_C_DogHusky_01", 88 | Level = 1 89 | } 90 | }, 91 | }, 92 | ['A_C_DogCatahoulaCur_01'] = { 93 | { 94 | Text = "Mutt", 95 | SubText = "", 96 | Desc = "Best pet you'll ever have", 97 | Param = { 98 | Price = 50, 99 | Model = "A_C_DogCatahoulaCur_01", 100 | Level = 1 101 | } 102 | }, 103 | }, 104 | ['A_C_DogLab_01'] = { 105 | { 106 | Text = "Labrador Retriever", 107 | SubText = "", 108 | Desc = "Best pet you'll ever have", 109 | Param = { 110 | Price = 100, 111 | Model = "A_C_DogLab_01", 112 | Level = 1 113 | } 114 | }, 115 | }, 116 | ['A_C_DogRufus_01'] = { 117 | { 118 | Text = "Rufus", 119 | SubText = "", 120 | Desc = "Best pet you'll ever have", 121 | Param = { 122 | Price = 100, 123 | Model = "A_C_DogRufus_01", 124 | Level = 1 125 | } 126 | }, 127 | }, 128 | ['A_C_DogBluetickCoonhound_01'] = { 129 | { 130 | Text = "Blue Hound", 131 | SubText = "", 132 | Desc = "Best pet you'll ever have", 133 | Param = { 134 | Price = 150, 135 | Model = "A_C_DogBluetickCoonhound_01", 136 | Level = 1 137 | } 138 | }, 139 | }, 140 | ['A_C_DogHound_01'] = { 141 | { 142 | Text = "Hound Dog", 143 | SubText = "", 144 | Desc = "Best pet you'll ever have", 145 | Param = { 146 | Price = 150, 147 | Model = "A_C_DogHound_01", 148 | Level = 1 149 | } 150 | }, 151 | }, 152 | ['A_C_DogCollie_01'] = { 153 | { 154 | Text = "Border Collie", 155 | SubText = "", 156 | Desc = "Best pet you'll ever have", 157 | Param = { 158 | Price = 200, 159 | Model = "A_C_DogCollie_01", 160 | Level = 1 161 | } 162 | }, 163 | }, 164 | ['A_C_DogPoodle_01'] = { 165 | { 166 | Text = "Poodle", 167 | SubText = "", 168 | Desc = "Best pet you'll ever have", 169 | Param = { 170 | Price = 200, 171 | Model = "A_C_DogPoodle_01", 172 | Level = 1 173 | } 174 | }, 175 | }, 176 | ['A_C_DogAmericanFoxhound_01'] = { 177 | { 178 | Text = "Foxhound", 179 | SubText = "", 180 | Desc = "Best pet you'll ever have", 181 | Param = { 182 | Price = 100, 183 | Model = "A_C_DogAmericanFoxhound_01", 184 | Level = 1 185 | } 186 | }, 187 | }, 188 | ['A_C_DogAustralianSheperd_01'] = { 189 | { 190 | Text = "Australian Shephard", 191 | SubText = "", 192 | Desc = "Best pet you'll ever have", 193 | Param = { 194 | Price = 100, 195 | Model = "A_C_DogAustralianSheperd_01", 196 | Level = 1 197 | } 198 | }, 199 | }, 200 | ['A_C_Cat_01'] = { 201 | { 202 | Text = "Cat", 203 | SubText = "", 204 | Desc = "Best pet you'll ever have", 205 | Param = { 206 | Price = 100, 207 | Model = "A_C_Cat_01", 208 | Level = 1 209 | } 210 | }, 211 | }, 212 | ['A_C_Alligator_01'] = { 213 | { 214 | Text = "Alligator", 215 | SubText = "", 216 | Desc = "Watch your toes!", 217 | Param = { 218 | Price = 2750, 219 | Model = "A_C_Alligator_01", 220 | Level = 1 221 | } 222 | }, 223 | }, 224 | ['a_c_alligator_03'] = { 225 | { 226 | Text = "Baby Alligator", 227 | SubText = "", 228 | Desc = "Watch your toes!", 229 | Param = { 230 | Price = 2650, 231 | Model = "a_c_alligator_03", 232 | Level = 1 233 | } 234 | }, 235 | }, 236 | ['a_c_sheep_01'] = { 237 | { 238 | Text = "Sheep", 239 | SubText = "", 240 | Desc = "Baaahhhhh I am here!", 241 | Param = { 242 | Price = 150, 243 | Model = "a_c_sheep_01", 244 | Level = 1 245 | } 246 | }, 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /client/client.lua: -------------------------------------------------------------------------------- 1 | local recentlySpawned = 0 2 | local currentPetPed = nil 3 | local ActivePets = {} 4 | local sleep = 500 5 | local SellPrice = 0 6 | 7 | -- MenuAPI 8 | TriggerEvent("menuapi:getData",function(call) 9 | MenuData = call 10 | end) 11 | AddEventHandler('menuapi:closemenu', function() end) 12 | 13 | -- VORP 14 | local VORPcore = {} 15 | TriggerEvent("getCore", function(core) 16 | VORPcore = core 17 | end) 18 | 19 | -- VORP Util 20 | local VORPutils = {} 21 | TriggerEvent("getUtils", function(utils) 22 | VORPutils = utils 23 | end) 24 | 25 | -- Functions 26 | function SetPetAttributes(entity) 27 | -- | SET_ATTRIBUTE_POINTS | -- 28 | Citizen.InvokeNative( 0x09A59688C26D88DF, entity, 0, 1100 ) 29 | Citizen.InvokeNative( 0x09A59688C26D88DF, entity, 1, 1100 ) 30 | Citizen.InvokeNative( 0x09A59688C26D88DF, entity, 2, 1100 ) 31 | -- | ADD_ATTRIBUTE_POINTS | -- 32 | Citizen.InvokeNative( 0x75415EE0CB583760, entity, 0, 1100 ) 33 | Citizen.InvokeNative( 0x75415EE0CB583760, entity, 1, 1100 ) 34 | Citizen.InvokeNative( 0x75415EE0CB583760, entity, 2, 1100 ) 35 | -- | SET_ATTRIBUTE_BASE_RANK | -- 36 | Citizen.InvokeNative( 0x5DA12E025D47D4E5, entity, 0, 10 ) 37 | Citizen.InvokeNative( 0x5DA12E025D47D4E5, entity, 1, 10 ) 38 | Citizen.InvokeNative( 0x5DA12E025D47D4E5, entity, 2, 10 ) 39 | -- | SET_ATTRIBUTE_BONUS_RANK | -- 40 | Citizen.InvokeNative( 0x920F9488BD115EFB, entity, 0, 10 ) 41 | Citizen.InvokeNative( 0x920F9488BD115EFB, entity, 1, 10 ) 42 | Citizen.InvokeNative( 0x920F9488BD115EFB, entity, 2, 10 ) 43 | -- | SET_ATTRIBUTE_OVERPOWER_AMOUNT | -- 44 | Citizen.InvokeNative( 0xF6A7C08DF2E28B28, entity, 0, 5000.0, false ) 45 | Citizen.InvokeNative( 0xF6A7C08DF2E28B28, entity, 1, 5000.0, false ) 46 | Citizen.InvokeNative( 0xF6A7C08DF2E28B28, entity, 2, 5000.0, false ) 47 | end 48 | 49 | function SetPetBehavior(petPed) 50 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), GetHashKey('PLAYER')) 51 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 143493179) 52 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -2040077242) 53 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1222652248) 54 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1077299173) 55 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -887307738) 56 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1998572072) 57 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -661858713) 58 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1232372459) 59 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1836932466) 60 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1878159675) 61 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1078461828) 62 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1535431934) 63 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1862763509) 64 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1663301869) 65 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1448293989) 66 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1201903818) 67 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -886193798) 68 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1996978098) 69 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 555364152) 70 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -2020052692) 71 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 707888648) 72 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 378397108) 73 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -350651841) 74 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1538724068) 75 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1030835986) 76 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1919885972) 77 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1976316465) 78 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 841021282) 79 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 889541022) 80 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1329647920) 81 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -319516747) 82 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -767591988) 83 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -989642646) 84 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), 1986610512) 85 | SetRelationshipBetweenGroups(1, GetPedRelationshipGroupHash(petPed), -1683752762) 86 | end 87 | 88 | function FollowOwner(Pet, PlayerPedId, isInShop) 89 | TaskFollowToOffsetOfEntity(Pet, PlayerPedId, 0.0, -1.5, 0.0, 1.0, -1, Config.PetAttributes.FollowDistance * 100000000, 1, 1, 0, 0, 1) 90 | if isInShop then 91 | Citizen.InvokeNative(0x489FFCCCE7392B55, Pet, PlayerPedId) 92 | end 93 | end 94 | 95 | function SpawnAnimal(model, player, x, y, z, h, skin, PlayerPedId, isdead, isshop, petid) 96 | currentPetPed = CreatePed(model, x, y, z, h, 1, 1 ) 97 | SET_PED_OUTFIT_PRESET( currentPetPed, skin ) 98 | SET_BLIP_TYPE( currentPetPed ) 99 | 100 | if Config.PetAttributes.Invincible then 101 | SetEntityInvincible(currentPetPed, true) 102 | end 103 | 104 | SetPetAttributes(currentPetPed) 105 | SetPetBehavior(currentPetPed) 106 | SetPedAsGroupMember(currentPetPed, GetPedGroupIndex(PlayerPedId)) 107 | 108 | while (GetScriptTaskStatus(currentPetPed, 0x4924437d) ~= 8) do 109 | Wait(1000) 110 | end 111 | 112 | FollowOwner(currentPetPed, player, isshop) 113 | 114 | if isdead and Config.PetAttributes.Invincible == false then 115 | TriggerEvent("vorp:TipRight", _U("petHealed")) 116 | end 117 | ActivePets[petid] = currentPetPed 118 | end 119 | 120 | function BuyPetMenu(shopName, ShopID) 121 | MenuData.CloseAll() 122 | local elements = {} 123 | for _,v in pairs(Config.Pets) do 124 | elements[#elements+1] = {label = v[1].Text.." - "..v[1].Param.Price, value = v[1].Param, desc = v[1].Desc} 125 | end 126 | Wait(1000) 127 | elements[#elements+1] = {label = "Close Pet Shop", value = "close", desc = ""} 128 | MenuData.Open('default', GetCurrentResourceName(), 'pet_purchase',{ 129 | title = shopName, 130 | subtext = "Purchase pets", 131 | align = "top-right", 132 | elements = elements, 133 | }, 134 | function(data, menu) 135 | if data.current.value then 136 | if data.current.value ~= "close" then 137 | TriggerServerEvent('bcc-pets:buydog', data.current.value, ShopID) 138 | menu.close() 139 | else 140 | ClearPedTasks(PlayerPedId()) 141 | menu.close() 142 | end 143 | end 144 | end, 145 | function(data, menu) 146 | ClearPedTasks(PlayerPedId()) 147 | menu.close() 148 | end) 149 | end 150 | 151 | function SellPetMenu() 152 | MenuData.CloseAll() 153 | local elements = {} 154 | local MenuNumber = 1 155 | VORPcore.RpcCall("GetPlayersPets", function(result) 156 | if result then 157 | for _,v in pairs(result) do 158 | if Config.AllowMultipleTrades then 159 | if v.called == 0 then 160 | SellPrice = math.ceil(Config.Pets[v.dog][1].Param.Price / Config.SellPercentage) 161 | elements[MenuNumber] = {label = Config.Pets[v.dog][1].Text.." - $"..SellPrice, value = "sell", value2 = v.petid, value3 = SellPrice, desc = "", info = v.dog} 162 | MenuNumber = MenuNumber + 1 163 | end 164 | else 165 | if v.called == 0 and v.transfered == 0 then 166 | SellPrice = math.ceil(Config.Pets[v.dog][1].Param.Price / Config.SellPercentage) 167 | elements[MenuNumber] = {label = Config.Pets[v.dog][1].Text.." - $"..SellPrice, value = "sell", value2 = v.petid, value3 = SellPrice, desc = "", info = v.dog} 168 | MenuNumber = MenuNumber + 1 169 | end 170 | end 171 | end 172 | end 173 | end) 174 | Wait(1000) 175 | elements[#elements+1] = {label = "Close Pet menu", value = "close", desc = ""} 176 | MenuData.Open('default', GetCurrentResourceName(), 'pet_select',{ 177 | title = "Pet menu", 178 | subtext = "Sell your pets", 179 | align = "top-right", 180 | elements = elements, 181 | }, 182 | function(data, menu) 183 | if data.current.value then 184 | if data.current.value == "sell" then 185 | TriggerServerEvent("bcc-pets:sellpet", data.current.value2, data.current.value3) 186 | ClearPedTasks(PlayerPedId()) 187 | menu.close() 188 | else 189 | ClearPedTasks(PlayerPedId()) 190 | menu.close() 191 | end 192 | end 193 | end, 194 | function(data, menu) 195 | ClearPedTasks(PlayerPedId()) 196 | menu.close() 197 | end) 198 | end 199 | 200 | function CallPetMenu() 201 | MenuData.CloseAll() 202 | local elements = {} 203 | local timer = 1000 204 | if Config.PetMenu.animation then 205 | if not IsPedOnMount(PlayerPedId()) then 206 | TaskStartScenarioInPlace(PlayerPedId(), GetHashKey("WORLD_HUMAN_WRITE_NOTEBOOK"), -1, true, false, false, false) 207 | end 208 | end 209 | local MenuNumber = 1 210 | VORPcore.RpcCall("GetPlayersPets", function(result) 211 | if result then 212 | for _,v in pairs(result) do 213 | if v.called == 1 then 214 | elements[MenuNumber] = {label = Config.Pets[v.dog][1].Text.." - Out", value = "call", value2 = v, desc = "", info = v.dog} 215 | MenuNumber = MenuNumber + 1 216 | else 217 | elements[MenuNumber] = {label = Config.Pets[v.dog][1].Text.." - Home", value = "call", value2 = v, desc = "", info = v.dog} 218 | MenuNumber = MenuNumber + 1 219 | end 220 | end 221 | end 222 | end) 223 | Wait(timer + 500) 224 | elements[#elements+1] = {label = "Close Pet menu", value = "close", desc = ""} 225 | MenuData.Open('default', GetCurrentResourceName(), 'pet_select',{ 226 | title = "Pet menu", 227 | subtext = "Manage your pets", 228 | align = "top-right", 229 | elements = elements, 230 | }, 231 | function(data, menu) 232 | if data.current.value then 233 | if data.current.value == "call" then 234 | menu.close() 235 | PetManagement(data.current.value2) 236 | else 237 | ClearPedTasks(PlayerPedId()) 238 | menu.close() 239 | end 240 | end 241 | end, 242 | function(data, menu) 243 | ClearPedTasks(PlayerPedId()) 244 | menu.close() 245 | end) 246 | end 247 | 248 | function VetPetMenu(ShopName, ShopID) 249 | MenuData.CloseAll() 250 | local elements = {} 251 | if Config.Shops[ShopID].JobLock.purchasing then 252 | VORPcore.RpcCall("GetPlayerJob", function(result) 253 | if result then 254 | for _,v in pairs(Config.Shops[ShopID].JobLock.jobs) do 255 | if tostring(result) == v then 256 | elements[#elements+1] = {label = "Buy Pet", value = "buy", desc = ""} 257 | break 258 | end 259 | end 260 | end 261 | end) 262 | else 263 | elements[#elements+1] = {label = "Buy Pet", value = "buy", desc = ""} 264 | end 265 | Wait(100) 266 | if Config.Shops[ShopID].JobLock.selling then 267 | VORPcore.RpcCall("GetPlayerJob", function(result) 268 | if result then 269 | for _,v in pairs(Config.Shops[ShopID].JobLock.jobs) do 270 | if tostring(result) == v then 271 | elements[#elements+1] = {label = "Sell Pet", value = "sell", desc = ""} 272 | break 273 | end 274 | end 275 | end 276 | end) 277 | else 278 | elements[#elements+1] = {label = "Sell Pet", value = "sell", desc = ""} 279 | end 280 | Wait(100) 281 | elements[#elements+1] = {label = "Close Pet Shop", value = "close", desc = ""} 282 | MenuData.Open('default', GetCurrentResourceName(), 'pet_purchase',{ 283 | title = ShopName, 284 | subtext = "Vet menu", 285 | align = "top-right", 286 | elements = elements, 287 | }, 288 | function(data, menu) 289 | if data.current.value then 290 | if data.current.value == "buy" then 291 | BuyPetMenu(ShopName, ShopID) 292 | elseif data.current.value == "sell" then 293 | SellPetMenu() 294 | else 295 | ClearPedTasks(PlayerPedId()) 296 | menu.close() 297 | end 298 | end 299 | end, 300 | function(data, menu) 301 | ClearPedTasks(PlayerPedId()) 302 | menu.close() 303 | end) 304 | end 305 | 306 | function PetManagement(PetDataTable) 307 | MenuData.CloseAll() 308 | local elements = {} 309 | if PetDataTable.called == 1 then 310 | elements[1] = {label = "Send pet home", value = "home", desc = ""} 311 | else 312 | elements[1] = {label = "Call pet", value = "call", desc = ""} 313 | end 314 | if PetDataTable.transfered ~= 1 then 315 | elements[#elements+1] = {label = "Transfer Ownership", value = "transfer", desc = ""} 316 | end 317 | elements[#elements+1] = {label = "Close Pet menu", value = "close", desc = ""} 318 | MenuData.Open('default', GetCurrentResourceName(), 'pet_select',{ 319 | title = "Pet menu", 320 | subtext = "Manage your pets", 321 | align = "top-right", 322 | elements = elements, 323 | }, 324 | function(data, menu) 325 | if data.current.value then 326 | if data.current.value == "call" then 327 | ClearPedTasks(PlayerPedId()) 328 | menu.close() 329 | TriggerServerEvent('bcc-pets:callpet', PetDataTable.petid) 330 | elseif data.current.value == "home" then 331 | if ActivePets[PetDataTable.petid] then 332 | ClearPedTasks(PlayerPedId()) 333 | menu.close() 334 | DeleteEntity(ActivePets[PetDataTable.petid]) 335 | TriggerServerEvent('bcc-pets:server:putawaypet', PetDataTable.petid) 336 | else 337 | VORPcore.NotifyRightTip(_U("PetNotOut"),4000) 338 | end 339 | elseif data.current.value == "transfer" then 340 | menu.close() 341 | TriggerEvent('bcc-pets:transferpetinput', PetDataTable.petid) 342 | else 343 | ClearPedTasks(PlayerPedId()) 344 | menu.close() 345 | end 346 | end 347 | end, 348 | function(data, menu) 349 | ClearPedTasks(PlayerPedId()) 350 | menu.close() 351 | end) 352 | end 353 | 354 | function SET_BLIP_TYPE (animal) 355 | return Citizen.InvokeNative(0x23f74c2fda6e7c61, -1749618580, animal) 356 | end 357 | 358 | function SET_ANIMAL_TUNING_BOOL_PARAM (animal, p1, p2) 359 | return Citizen.InvokeNative( 0x9FF1E042FA597187, animal, p1, p2 ) 360 | end 361 | 362 | function SET_PED_DEFAULT_OUTFIT (dog) 363 | return Citizen.InvokeNative( 0x283978A15512B2FE, dog, true ) 364 | end 365 | 366 | function SET_PED_OUTFIT_PRESET (dog, preset) 367 | return Citizen.InvokeNative( 0x77FF8D35EEC6BBC4, dog, preset, 0 ) 368 | end 369 | 370 | -- Threads 371 | CreateThread(function() 372 | for _, v in pairs(Config.Shops) do 373 | if v.Blip.active then 374 | local blip = VORPutils.Blips:SetBlip(v.Name, v.Blip.sprite, v.Blip.scale, v.Blip.coords.x, v.Blip.coords.y, v.Blip.coords.z) 375 | end 376 | if v.Ped.active then 377 | local ped = VORPutils.Peds:Create(v.Ped.modelhash, v.Ped.coords.x, v.Ped.coords.y, v.Ped.coords.z, v.Ped.coords.w, 'world', false) 378 | if v.Ped.frozen then 379 | ped:Freeze() 380 | end 381 | if v.Ped.invincible then 382 | ped:Invincible() 383 | end 384 | end 385 | end 386 | local PetStores = VORPutils.Prompts:SetupPromptGroup() 387 | local PetStoresPrompt = PetStores:RegisterPrompt(Config.ShopsPromptText, Config.ShopsPromptKey, 1, 1, false, 'hold', {timedeventhash = "SHORT_TIMED_EVENT"}) 388 | while true do 389 | sleep = 1000 390 | local coords = GetEntityCoords(PlayerPedId()) 391 | for k, v in pairs(Config.Shops) do 392 | local dist = #(coords - v.Coords) 393 | if dist <= 10 then 394 | sleep = 5 395 | end 396 | if dist < Config.ShopsPromptDistance then 397 | PetStores:ShowGroup(v.Name) 398 | ShopName = v.Name 399 | ShopID = k 400 | end 401 | end 402 | if PetStoresPrompt:HasCompleted() then 403 | VetPetMenu(ShopName, ShopID) 404 | end 405 | Wait(sleep) 406 | end 407 | end) 408 | 409 | -- Events 410 | RegisterNetEvent('bcc-pets:transferpetinput', function(petid) 411 | local button = "Confirm" 412 | local placeholder = "Insert Person's ID #" 413 | TriggerEvent("vorpinputs:getInput", button, placeholder, function(result) 414 | if result ~= "" or result then -- making sure its not empty or nil 415 | TriggerServerEvent('bcc-pets:transferownership', result, petid) 416 | ClearPedTasks(PlayerPedId()) 417 | else 418 | ClearPedTasks(PlayerPedId()) 419 | VORPcore.NotifyRightTip(_U("IncorrectID"),4000) 420 | end 421 | end) 422 | end) 423 | 424 | RegisterNetEvent('bcc-pets:removedog', function (petid) 425 | if ActivePets[petid] then 426 | DeleteEntity(ActivePets[petid]) 427 | end 428 | end) 429 | 430 | RegisterNetEvent('bcc-pets:spawndog', function (dog, skin, isInShop, petid, ShopID) 431 | local player = PlayerPedId() 432 | local model = GetHashKey(dog) 433 | local x, y, z, w, heading, b 434 | 435 | -- Set initial pet location 436 | if isInShop then 437 | x, y, z, heading = -373.302, 786.904, 116.169, 273.18 438 | else 439 | x, y, z = table.unpack( GetOffsetFromEntityInWorldCoords( player, 0.0, -5.0, 0.3 ) ) 440 | _, b = GetGroundZAndNormalFor_3dCoord( x, y, z + 10 ) 441 | end 442 | 443 | RequestModel( model ) 444 | 445 | while not HasModelLoaded( model ) do 446 | Wait(500) 447 | end 448 | 449 | if isInShop then 450 | x, y, z, w = table.unpack(Config.Shops[ShopID].Spawndog) 451 | SpawnAnimal(model, player, x, y, z, w, skin, PlayerPedId(), false, true, petid) 452 | else 453 | if EntityIsDead then 454 | SpawnAnimal(model, player, x, y, b, heading, skin, PlayerPedId(), true, false, petid) 455 | else 456 | SpawnAnimal(model, player, x, y, b, heading, skin, PlayerPedId(), false, false, petid) 457 | end 458 | end 459 | end) 460 | 461 | RegisterNetEvent('bcc-pets:openpetmenu', function() 462 | CallPetMenu() 463 | end) 464 | 465 | AddEventHandler('onResourceStop', function(resourceName) 466 | if (GetCurrentResourceName() ~= resourceName) then 467 | return 468 | end 469 | for k,_ in pairs(ActivePets) do 470 | DeleteEntity(ActivePets[k]) 471 | end 472 | end) 473 | --------------------------------------------------------------------------------