Utility Hotel

A script by XenoS.exe

No reviews yet.
Utility Hotel main image

Full Description

Utility Hotel

Make your city feel alive with the all-in-one hotel system


Card (1)
Frame 2
Youtube Showcase Frame 6


Screenshots









Contains
  • 9+ Pre-configured hotels
  • 4 Props
  • Hotel & Room creator
Details
  • NPC Hotel management
  • Player Hotel management
  • Custom GTA 5 style UI for each hotel, customizable
  • RP Friendly expiration letter to remind rent expiration
  • Raid option for law enforcers
  • Integration with Utility Keyhanger
  • Realistic and coeshive interaction animations
  • Model or polyzone interaction system
  • Male/Female animations for toilette
  • Key item system with possibility to copy key and extend rent
  • Wardrobe to save outfits and apply them
  • Possibility to add custom functions before, during and after each interaction
Pre configured Hotels

You can easily add new hotels using the included Hotel & Room Creator.
This list may become outdated, so please contact us for the most up-to-date information.

Config

Configuration may change over time due to ongoing updates

Config = {}

Config.Debug = false
Config.DebugCreation = false

Config.DefaultRentAccount = "money" -- Default account to use for rent if not specified in the hotel config
Config.CreateStashOnExpired = true  -- Create a temporary stash for the room when the rent expires
Config.CanExtendRent = true         -- Allow players to extend the rent time for their room
Config.InteractionDrawSprite = true -- Draw a sprite at the center of the zone

Config.ExpirationLetter = {
	active = true,
	notifyAt = 5 * 24 -- Notify players 5 days before the room expires
}

Config.Key = {
	EnableAnim = true,                                              -- Enable key animation when renting a room from the receptionist
	EnableHangerAnim = GetResourceState("utility_keyhanger") == "started", -- Enable key hanger animation when renting a room from the receptionist
	EnableCopy = true,                                              -- Allow players to copy keys for their room
}

Config.RoomTypeCreator = {
	command = "open_room_creator",
	enabled = true
}

Config.HotelCreator = {
	command = "open_hotel_creator",
    enabled = true
}

Config.Raid = {
	enable = true,
	permissions = {
		--[[{
		    type = "job",
		    values = {
		        -- ["mechanic"] = 1, -- [job_name] = job_grade (all players with job_name and job_grade higher than this)
		        -- ["mechanic2"] = 1
		        --
		        -- OR
		        --
		        -- "mechanic", -- job_name
		        -- "mechanic2"
		    },
		},
		{
		    type = "ace", -- https://forum.cfx.re/t/basic-aces-principals-overview-guide/90917
		    values = { "mechanic", "admin" } or "mechanic",
		},
		{
		    type = "identifier",
		    values = { "license:test123" } or "license:test123" -- https://docs.fivem.net/docs/scripting-reference/runtimes/lua/functions/GetPlayerIdentifiers/#identifier-types
		},
		{
		    type = "character",
		    values = { "char1:test123" } or "char1:test123" -- https://docs.fivem.net/docs/scripting-reference/runtimes/lua/functions/GetPlayerIdentifiers/#identifier-types
		},
		{
		    type = "custom",
		    func = function(src, room) -- Any code, return true if allowed
		        return true
		    end
		},]]
	}
}

-- Help us translate!
--  You can contribute by improving existing translations or adding translations for new languages
--  https://localazy.com/p/mxc
Config.Language = "en"

Config.Hotels = {
	["mxc-mirror_hotel"] = {
		label = "Mirror Hotel & Restaurant",
		management = MANAGEMENTS.NPC, -- MANAGEMENTS.NPC or MANAGEMENTS.PLAYER
        required = {
            resource = "cfx-mxc-mirror"
        },

		-- Hotel coordinates
		coords = vec3(-1322.8687, -1051.5690, 12.4651),

		-- Map blip for the hotel
		blip = {
			active = true,
			sprite = 78,
			color = 7,
			scale = 1.0,
		},

		-- Player Managed: Reception menu
		reception = vec3(-1344.4399, -1070.1528, 6.9578),

		-- Lost and Found coords (if Config.CreateStashOnExpired is true)
		lostAndFound = {
			model = "mxc_hotel_prop_lostandfound",
			coords = vec4(-1344.58826, -1075.91138, 6.137314, -150)
		},

		-- NPC Managed: Receptionist NPC
		npc = {
			model = "u_f_m_debbie_01",
			coords = vec4(-1343.8285, -1070.0105, 6.9545, 212.1466),
		},

		-- Room booking screen
		screens = {
			{
				model = "mxc_hotel_prop_roompad",
				coords = vec4(-1342.9021, -1070.45764, 7.00843048, -60.0),
				action = "menu", -- "menu" or "ui" (menu = ox_lib menu, ui = lore friendly UI)

				animation = {
					dict = "anim@amb@office@laptops@male@var_a@base@",
					anim = "idle_b",
					flags = 49,
					duration = -1,
				},

				ui = {
					url = "www.mirror-restaurant.com",
					backgroundColor = "#1e1425",
					accentColor = "#8c61b2",
					accentColor2 = "#8146b4",
					buttonText = "#ffffff",
					buttonTextHover = "#000000",
					buttonHoverBackgroundColor = "#ffffff"
				}
			},
			{
				model = "mxc_hotel_prop_roompad",
				coords = vec4(-1344.63525, -1071.45837, 7.00843048, -60.0),
				action = "ui", -- "menu" or "ui" (menu = ox_lib menu, ui = lore friendly UI)

				animation = {
					dict = "anim@amb@office@laptops@male@var_a@base@",
					anim = "idle_b",
					flags = 49,
					duration = -1,
				},

				ui = {
					url = "www.mirror-restaurant.com",
					backgroundColor = "#1e1425",
					accentColor = "#8c61b2",
					accentColor2 = "#8146b4",
					buttonText = "#ffffff",
					buttonTextHover = "#000000",
					buttonHoverBackgroundColor = "#ffffff"
				}
			},
		},

		-- Optional: Key hanger (if using utility_keyhanger resource)
		keyhanger = {
			active = true,
			model = "mxc_keyhanger_prop_frame_2",
			coords = vec3(-1345.23291, -1068.48572, 7.35462427),
			rotation = vec3(0.0, 0.0, 32.3582),
		},

		key = {
			item = "key_mirror", -- Item used as a key for the room
			copyCost = {
				account = "money",
				price = 100,
			},
		},

		permissions = {
			-- Who can manage the hotel in player managed mode, comment to disable (reception, etc...)
			playerManaged = {
				--[[ {
					type = "job",
					values = {
						-- ["mechanic"] = 1, -- [job_name] = job_grade
						-- ["mechanic2"] = 1
						--
						-- OR
						--
						-- "mechanic", -- job_name
						-- "mechanic2"
					},
				},
				{
					type = "ace", -- https://forum.cfx.re/t/basic-aces-principals-overview-guide/90917
					values = { "mechanic", "admin" } or "mechanic",
				},
				{
					type = "identifier",
					values = { "license:test123" } or "license:test123" -- https://docs.fivem.net/docs/scripting-reference/runtimes/lua/functions/GetPlayerIdentifiers/#identifier-types
				},
				{
					type = "character",
					values = { "char1:test123" } or "char1:test123" -- https://docs.fivem.net/docs/scripting-reference/runtimes/lua/functions/GetPlayerIdentifiers/#identifier-types
				},
				{
					type = "custom",
					func = function(src, room) -- Any code, return true if allowed
						return true
					end
				}, ]]
			}
		},

		layers = {
			-- Contain all the layers info, deducted for characters limit in the CFX Forum
		}
	},
	["mxc-dreamview_motel"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	},
	["mxc-southseas_motel"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	},
	["kiiya-lapuerta-1"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	},
	["kiiya-lapuerta-2"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	},
	["kiiya-bilingsgate_motel"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	},
	["kiiya-crownjewels_motel"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	},
	["prompt-sandy_motel"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	},
	["tstudio-opiumnights_hotel"] = {
		-- Contain all the hotel info like other hotels, deducted for characters limit in the CFX Forum
	}
}

Config.RoomTypes = {
	["mxc-mirror_default_room"] = {
		rent = {
			account = "money",
			price = 500,
			time = 24 * 7,
		},
		door = {
			model = "mxc_venetian_hroom_prop_entrancedoor",
		},
		modules = {
			{
				type = MODULES.STASH,
				coords = vec4(1.37, -4.36, 0.27, -88.07),
				model = "mxc_venetian_hroom_prop_stash",
				slots = 50,
				maxWeight = 10000,
				config = {
					audio = "drawer_open_close",
					propAnimation = {
						open = {
							dict = "clip@venetianroom_anim",
							anim = "mxc_venetian_hroom_prop_stash_open"
						},
						close = {
							dict = "clip@venetianroom_anim",
							anim = "mxc_venetian_hroom_prop_stash_close"
						}
					},
				},
			},
			{
				type = MODULES.SHOWER,
				coords = vec4(3.7, -0.45, -0.05, -177.31),
				size = vec3(1.2, 1.0, 2.5),
			},
			{
				type = MODULES.BED,
				coords = vec4(3.38, -4.41, 0.61, 104.23),
				model = "mxc_venetian_hroom_r01_bed",
				config = {
					inverseLeftAndRight = true
				},
			},
			{
				type = MODULES.TOILETTE,
				coords = vec4(2.63, -1.70, 0.0, 0.0),
				size = vec3(0.5, 1.3, 2.0),
			},
			{
				type = MODULES.WARDROBE,
				coords = vec4(2.87, -2.84, -0.08, 5.64),
				model = "mxc_venetian_hroom_prop_wardrobe",
				config = {
					propAnimation = {
						open = {
							dict = "clip@venetianroom_anim",
							anim = "mxc_venetian_hroom_prop_wardrobe_open"
						},
						close = {
							dict = "clip@venetianroom_anim",
							anim = "mxc_venetian_hroom_prop_wardrobe_close"
						}
					},
				},
			},
		}
	},
	["mxc-dreamview_default_room"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["mxc-dreamview_bathroom_room"] = { -- This room is used also to make the shared bathroom work, little hack
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["mxc-southseas_default_room"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["mxc-southseas_default_room_flip"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["kiiya-lapuerta_apartments_room"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["kiiya-bilingsgate_default_room"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["kiiya_crownjewel_default_room"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["prompt-sandymotel_default_room"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	},
	["opium_nights_room"] = {
		-- Contain all the room info like other rooms, deducted for characters limit in the CFX Forum
	}
}

Config.ModulesInteractions = {
	[MODULES.STASH] = {
		{
			name = "hotel_stash",
			icon = "fa-solid fa-box-archive",
			label = "room_open_stash",

			-- Called before the interaction starts
			beforeInteract = function()

			end,
			-- Called after the interaction ends
			afterInteract = function()

			end,
			-- Return true to allow interaction, false to block it
			canInteract = function()
				return true
			end,
		},
	},
	[MODULES.BED] = {
		{
			name = "hotel_bed",
			icon = "fa-solid fa-bed",
			label = "room_sleep",

			beforeInteract = function()

			end,
			afterInteract = function()

			end,
			canInteract = function()

				return true
			end,

			inverseLeftAndRight = false,
			r = {
				offsets = vec3(-1.45, 1.5, 0.35),
				rot = 200.0,

				walkOffsets = vec3(-1.4, 1.1, 0.0),
				walkRot = 212.5326
			},
			l = {
				offsets = vec3(-0.1, 0.0, -0.15),
				rot = 90.0,

				walkOffsets = vec3(1.4, 0.0, 0.0),
				walkRot = 118.2094
			}
		},
	},
	[MODULES.SOFABED] = {
		{
			noBusy = true,
			name = "room_sofabed_open",
			icon = "fa-solid fa-bed",
			label = "room_sofabed_open",

			beforeInteract = function()

			end,
			afterInteract = function()

			end,
			canInteract = function()
				return true
			end
		},
		{
			noBusy = true,
			name = "room_sofabed_close",
			icon = "fa-solid fa-bed",
			label = "room_sofabed_close",

			beforeInteract = function()

			end,
			afterInteract = function()

			end,
			canInteract = function()
				return true
			end
		}
	},
	[MODULES.WARDROBE] = {
		{
			name = "hotel_wardrobe",
			icon = "fa-solid fa-shirt",
			label = "room_change_clothes",

			beforeInteract = function()
				
			end,
			afterInteract = function()
				
			end,
			canInteract = function()
				
				return true
			end,
		},
	},
	[MODULES.SHOWER] = {
		{
			name = "room_shower",
			icon = "fa-solid fa-shower",
			label = "room_shower",
			
			beforeInteract = function()
				
			end,
			afterInteract = function()
				
			end,
			canInteract = function()
				
				return true
			end,

			--

			particlesOffset = vec3(0.0, 0.0, 0.0),

			cam = {
				enable = true,
				offset = vec3(0.0, 1.5, 0.7),
				rotation = vec3(-5.0, 0.0, -180.0),
				shake = 0.7
			},

			enterScene = {
				offset = vec3(0.75, 1.0, 0.08),
				rotation = vec3(0.0, 0.0, 90.0)
			},

			exitTeleport = {
				offset = vec3(1.0, 0.4, -1.0),
				heading = 90.0
			},
		},
	},
	[MODULES.TOILETTE] = {
		{
			name = "room_toilette_pee",
			icon = "fa-solid fa-toilet",
			label = "room_toilette_pee",

			beforeInteract = function()
				
			end,
			afterInteract = function()
				
			end,
			canInteract = function()
				
				return true
			end,

			---

			cleanDecals = true,
			goToCoords = true,
			animLoop = true,
			anim = {
				male = {
					{
						dict = "misscarsteal2peeing",
						anim = "peeing_loop",
						offset = vec4(0.0, 0.75, 0.0, 180.0),
						duration = 20000,
					},
				},
				female = {
					{
						goToOffset = vec3(0.0, 0.75, 0.0),
						scenario = "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER",
						offset = vec4(0.0, 0.0, -0.5, 0.0),
					},
				},
			},
			particles = {
				male = {
					asset = "scr_amb_chop",
					name = "ent_anim_dog_peeing",
					offset = vec3(0.0, 0.15, 0.0),
					rotation = vec3(0.0, 0.0, 90.0),
				},
				female = {},
			},

			cam = {
				enable = true,
				offset = vec3(-0.45, 1.0, 0.9),
				rotation = vec3(-45.0, 0.0, -150.0),
				shake = 0.7
			}
		},
		{
			name = "room_toilette_poop",
			icon = "fa-solid fa-toilet",
			label = "room_toilette_poop",

			beforeInteract = function()
				
			end,
			afterInteract = function()
				
			end,
			canInteract = function()
				
				return true
			end,

			---

			goToOffset = vec3(0.0, 0.75, 0.0),
			goToCoords = true,
			animLoop = true,
			anim = {
				{
					scenario = "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER",
					offset = vec4(0.0, 0.0, -0.5, 0.0),
				},
			},
			prop = {
				spawn = true,
				offset = vec3(0.0, 0.0, -0.3),
				despawnTime = 10000 -- (ms) 10 seconds
			}
		},
	},
}


Config.Translations = {
	-- Accounts
	["shared_account_money"] = "cash",
	["shared_account_bank"] = "money (bank)",

	-- Contain all the translations, deducted for characters limit in the CFX Forum
}
Config (functions)

Configuration may change over time due to ongoing updates

Config.Functions = {
	StartFramework = function()
		if GetResourceState("es_extended") ~= "missing" then
			ESX = exports["es_extended"]:getSharedObject()
		elseif GetResourceState("qb-core") ~= "missing" then
			QBCore = exports["qb-core"]:GetCoreObject()
		end
	end,

	HaveMoney = function(source, type, amount)
		if amount == 0 then
			return true
		end

		-- https://utility-library.github.io/documentation/server/esx_integration/xplayer/HaveMoney/
		return HaveMoney(source, type, amount)
	end,
	RemoveMoney = function(source, type, amount)
		if amount <= 0 then
			return
		end

		-- https://utility-library.github.io/documentation/server/esx_integration/xplayer/RemoveMoney/
		RemoveMoney(source, type, amount)
	end,
	AddMoney = function(source, type, amount)
		-- https://utility-library.github.io/documentation/server/esx_integration/xplayer/AddMoney/
		AddMoney(source, type, amount)
	end,

	GetPlayer = function(source)
		if ESX then
			return ESX.GetPlayerFromId(source)
		elseif QBCore then
			return QBCore.Functions.GetPlayer(source)
		end
	end,
	GetPlayerFromIdentifier = function(identifier)
		if ESX then
			return ESX.GetPlayerFromIdentifier(identifier)
		elseif QBCore then
			return QBCore.Functions.GetPlayerByCitizenId(identifier)
		end
	end,

	Input = function(title, defaultValue, maxLength)
		local titleEntry = "${GetCurrentResourceName():upper()}_WINDOW_TITLE"

        AddTextEntry(titleEntry, title);
		DisplayOnscreenKeyboard(1, titleEntry, "", defaultValue or "", "", "", "", maxLength)

		Wait(0)

		while true do
			local status = UpdateOnscreenKeyboard()

			if status == 3 or status == 2 then -- cancelled
				break
			elseif status == 1 then
				return GetOnscreenKeyboardResult()
			end

			DisableAllControlActions(0)
			Wait(0)
		end
	end,

	ShowHelpText = function(text)
		local helpShown = true

		CreateThread(function()
			while helpShown do
				BeginTextCommandDisplayHelp("STRING")
				AddTextComponentSubstringPlayerName(text)
				EndTextCommandDisplayHelp(0, false, true, -1)
				SetFloatingHelpTextStyle(0, 2, 2, 0, 3, 0)
				Wait(0)
			end
		end)
		return function()
			helpShown = false
		end
	end,

	ShowCancelHelp = function()
		local show = true

		CreateThread(function()
			while show do
				ButtonNotification(Translate("interaction_cancel"), false)
				Wait(0)
			end
		end)

		return function()
			show = false
		end
	end,

	UndressPlayer = function(onlyPants)
		local player = PlayerPedId()
		local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01`

		if isMale then
			if not onlyPants then
				SetPedComponentVariation(player, 1, -1) -- mask
				SetPedComponentVariation(player, 5, 0) -- bag
				SetPedComponentVariation(player, 8, 15) -- TSHIRT
				SetPedComponentVariation(player, 11, 15) -- TORSO
				SetPedComponentVariation(player, 3, 15) -- ARMS
				SetPedComponentVariation(player, 9, 0) -- bulletproof
				SetPedComponentVariation(player, 6, 5) -- shoes
				
				ClearPedProp(player, 0) -- Helmet
				ClearPedProp(player, 1) -- Glasses
				SetPedComponentVariation(player, 4, 16, 3) -- PANTS
			else
				SetPedComponentVariation(player, 4, 61, 0) -- PANTS
			end
		else
			if not onlyPants then
				SetPedComponentVariation(player, 1, -1, 0) -- mask
				SetPedComponentVariation(player, 5, 0, 0) -- bag
				SetPedComponentVariation(player, 8, 2, 0) -- TSHIRT
				SetPedComponentVariation(player, 11, 18, 0) -- TORSO
				SetPedComponentVariation(player, 3, 15, 0) -- ARMS
				SetPedComponentVariation(player, 9, 0, 0) -- bulletproof
				SetPedComponentVariation(player, 6, 5, 0) -- shoes
				
				ClearPedProp(player, 0) -- Helmet
				ClearPedProp(player, 1) -- Glasses
				
				SetPedComponentVariation(player, 4, 15, 0) -- PANTS
			else
				SetPedComponentVariation(player, 4, 15, 0) -- PANTS
			end
		end
	end
}

Products

Code is accessible Partially, open functions for compatibility
Subscription-based No
Lines (approximately) 8000+
Requirements utility_lib
Support Yes
Config Merger
Have you tried Config Merger? Merge your configs easily