What do you want to achieve?
I’d like to have the battery item (and potentially other items) spawn in randomized locations in the furniture pieces while also ensuring that if it’s a locked room, a key will also spawn.
What is the issue?
I’m quite new to scripting so I’m not entirely sure how to do what I’m trying to accomplish. I’ll insert the code I’ve written thus far but I would like some help with trying to figure out how I can add more items to the table within my script. (See below for the scripts)
What solutions have you tried so far?
I’ve posted in discord servers & tried to find videos to accomplish what I’m trying to accomplish; however, there is no specific resource I can find.
local item = {}
function item.Interact(player, prompt, template, itemName)
if player.Character then
if itemName == "Key" then
local tool = workspace.Items.Key:Clone()
tool.Parent = player.Character
tool.Handle.KeyJingle:Play()
end
prompt.Enabled = false
template:Destroy()
end
end
function item.New(location, itemName)
local itemObject = workspace.Items:FindFirstChild(itemName)
if itemObject then
local itemHandle = itemObject.Handle:Clone()
itemHandle.Position = location.WorldPosition
local weld = Instance.new("WeldConstraint")
weld.Part0 = itemHandle
weld.Part1 = location.Parent
weld.Parent = itemHandle
itemHandle.Parent = location
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = ""
prompt.MaxActivationDistance = 5
prompt.Parent = location
prompt.Style = "Custom" --added
prompt.Triggered:Connect(function(player)
item.Interact(player, prompt, itemHandle, itemName)
end)
end
end
return item
Furniture Script
local TweenService = game:GetService("TweenService")
local furniture = {}
local closet = require(script.Closet)
function furniture.OpenDrawer(drawer)
drawer:SetAttribute("Moving", true)
local isOpen = drawer:GetAttribute("Open")
local direction = isOpen and 1 or -1
local cframe = drawer.CFrame * CFrame.new(0,0,2 * direction)
local drawerTween = TweenService:Create(drawer, TweenInfo.new(0.5),{CFrame =cframe})
drawer.Move:Play()
drawerTween:Play()
drawerTween.Completed:Wait()
drawer:SetAttribute("Moving", false)
drawer:SetAttribute("Open", not isOpen)
end
function furniture.New (template, roomModel)
local furnitureModel = workspace.Furniture:FindFirstChild(template.Name)
if furnitureModel then
furnitureModel = furnitureModel:Clone()
furnitureModel:PivotTo(template.CFrame)
local itemLocations = {}
for _,descendant in furnitureModel:GetDescendants() do
if descendant:IsA("Attachment") and descendant.Name=="Location" then
table.insert(itemLocations, descendant)
end
end
if furnitureModel:FindFirstChild("Drawers") then
for i, drawer in ipairs(furnitureModel.Drawers:GetChildren()) do
drawer:SetAttribute("Open", false)
drawer:SetAttribute("Moving", false)
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = ""
prompt.MaxActivationDistance = 5
prompt.Parent = drawer.Toggle
prompt.Style = "Custom" --added
prompt.Triggered:Connect(function()
if drawer:GetAttribute("Moving") == false then
furniture.OpenDrawer(drawer)
end
end)
end
end
furnitureModel.Parent = template.Parent
template:Destroy()
return itemLocations
end
end
function furniture.FurnishRoom(roomModel)
local roomItemLocations = {}
if roomModel:FindFirstChild("Furniture") then
local templates = roomModel.Furniture:GetChildren()
for i, part in ipairs(templates) do
if part.Name == "Locker" then
--create new closet
closet.New(part)
else
local locations = furniture.New(part, roomModel)
if locations then
for index, value in ipairs(locations) do
table.insert(roomItemLocations, value)
end
end
end
end
end
if #roomItemLocations > 0 then
return roomItemLocations
end
end
return furniture
Room Script
local TweenService = game:GetService("TweenService")
local door = require(script.Door)
local furniture = require(script.Furniture)
local item = require(script.Item)
local room = {}
room.info = require(script.RoomInfo)
room.lastTurnDirection = nil
room.random = Random.new()
function room.FlickerLight(lightPart)
local info = TweenInfo.new(0.2, Enum.EasingStyle.Elastic, Enum.EasingDirection.InOut, 1, false)
local flickOn = TweenService:Create(lightPart.PointLight, info, {Brightness = .5})
local flickOff = TweenService:Create(lightPart.PointLight, info, {Brightness = 0})
flickOff:Play()
flickOff.Completed:Wait()
lightPart.Material = Enum.Material.Glass
flickOn:Play()
flickOn.Completed:Wait()
lightPart.Material = Enum.Material.Neon
flickOff:Play()
flickOff.Completed:Wait()
lightPart.Material = Enum.Material.Glass
end
function room.Blackout (roomModel)
local lights = roomModel.Lights:GetChildren()
for i, light in ipairs(lights) do
for i, obj in ipairs(light:GetChildren()) do
if obj.Name == "Shade" then
task.spawn(function()
room.FlickerLight(obj)
end)
end
end
end
end
function room.GetRandom(PrevRoom)
local totalWeight = 0
for i, info in pairs(room.info) do
totalWeight += info.Weight
end
local randomWeight = room.random:NextNumber(0, totalWeight)
local currentWeight = 0
local randomRoom = nil
for i, info in pairs(room.info) do
currentWeight += info.Weight
if randomWeight <= currentWeight then
randomRoom = workspace.Rooms[i]
break
end
end
--[1]Nxt room must be dif than prev room
--[2]if corner then next turn other way
--[3]if prev room = stairs, then next cannot
local direction = room.info[randomRoom.Name]["Direction"]
local hasStairs = room.info[randomRoom.Name]["Stairs"]
local prevHadStairs = room.info[PrevRoom.Name]["Stairs"]
if (PrevRoom.Name == randomRoom.Name)
or (direction and direction == room.lastTurnDirection)
or (hasStairs and prevHadStairs)
then
return room.GetRandom(PrevRoom)
else
if direction then
room.lastTurnDirection = direction
end
return randomRoom
end
end
function room.Generate(PrevRoom, number)
local randomRoom = room.GetRandom(PrevRoom)
local newRoom = randomRoom:Clone()
newRoom.PrimaryPart = newRoom.Entrance
newRoom:PivotTo (PrevRoom.Exit.CFrame)
newRoom.Entrance.Transparency = 1
newRoom.Exit.Transparency = 1
local requiresKey = false
local locations = furniture.FurnishRoom(newRoom)
if locations then
if room.random:NextInteger(1, 3) == 3 then
local random = room.random:NextInteger(1, #locations)
local randomLocation = locations[random]
requiresKey = true
item.New(randomLocation, "Key")
end
end
local newDoor = door.New(newRoom, number, requiresKey)
newRoom.Parent = workspace.GeneratedRooms
return newRoom
end
return room
Let me know if you’d like me to provide more information that would be useful to help solve my issue!
This previous forum post of mine (related to a similar question) may also provide some more context to this issue!
I REALLY appreciate your assistance and help You are all geniuses & have helped me learn so much already about scripting
Thank you for your feedback; however, it’s a bit more complicated (in my opinion).
The ‘Key’ item is a tool, while the ‘Battery’ item is just a model that if the player clicks on, the flashlight battery will recharge.
Therefore, I don’t believe I can just do it as I did before — also, I’m not entirely sure where to add the name of the item or which script to insert that information based on the answer you’ve provided.
I’m guessing the part that you’ll need to change is this one:
local locations = furniture.FurnishRoom(newRoom)
if locations then
if room.random:NextInteger(1, 3) == 3 then
local random = room.random:NextInteger(1, #locations)
local randomLocation = locations[random]
requiresKey = true
item.New(randomLocation, "Key")
end
end
One issue I see here is that a room can be made locked, even if there’s no locations for a key to spawn. So before any items are generated, you’ll need to check to see if there’s at least one spawn location. One way is to just generate the key first inside a loop. If the loop never runs because there’s no spawn locations, then the key will never spawn and the room will never be locked (which is what you’d want):
local itemsThatCanSpawn = {"Battery"}
local locations = furniture.FurnishRoom(newRoom)
if locations then
local shouldBeLockedRoomed = room.random:NextNumber() < (1/4)
-- the odds of a room being locked is 1/4
for index,itemLocation in locations do
--guarantees that there will always be a key
--if the room is supposed to be locked
if index==1 and shouldBeLockedRoomed then
requiresKey = true
item.New(itemLocation, "Key")
--moves on to trying to spawn the next item
continue
end
--spawns the other items like batteries
local shouldSpawnAnItem = room.random:NextNumber() < (1/3)
if shouldSpawnAnItem then
local itemToSpawn = itemsThatCanSpawn[room.random:NextInteger(1, #itemsThatCanSpawn)]
item.New(itemLocation, itemToSpawn)
end
end
end
I haven’t tested it, but something like above should work.
EDIT:
The only small problem with this method is that the key will more than most likely always spawn at the same location. One way you can prevent this is by shuffling/mixing the locations array before the loop:
for i = #locations, 2, -1 do
local j = room.random:NextInteger(1, i)
locations[i], locations[j] = locations[j], locations[i]
end
It makes more sense to be inside the room script. However, you might want to find an automated way to create the itemsThatCanSpawn array. I’d personally use attributes on the items and have a function inside the item module that looks at those attributes and returns the names of non-special items (meaning the key item would be excluded). The attribute could be something like IsSpecial and you just put that on the key item with a value of true.
function room.Generate(PrevRoom, number)
local randomRoom = room.GetRandom(PrevRoom)
local newRoom = randomRoom:Clone()
newRoom.PrimaryPart = newRoom.Entrance
newRoom:PivotTo (PrevRoom.Exit.CFrame)
newRoom.Entrance.Transparency = 1
newRoom.Exit.Transparency = 1
local requiresKey = false
local locations = furniture.FurnishRoom(newRoom)
if locations then
if room.random:NextInteger(1, 3) == 3 then
local random = room.random:NextInteger(1, #locations)
local randomLocation = locations[random]
requiresKey = true
item.New(randomLocation, "Key")
end
end
local newDoor = door.New(newRoom, number, requiresKey)
newRoom.Parent = workspace.GeneratedRooms
return newRoom
end
return room
It should look like this:
function room.Generate(PrevRoom, number)
local randomRoom = room.GetRandom(PrevRoom)
local newRoom = randomRoom:Clone()
newRoom.PrimaryPart = newRoom.Entrance
newRoom:PivotTo (PrevRoom.Exit.CFrame)
newRoom.Entrance.Transparency = 1
newRoom.Exit.Transparency = 1
local requiresKey = false
local locations = furniture.FurnishRoom(newRoom)
if locations then
local shouldBeLockedRoomed = room.random:NextNumber() < (1/4)
-- the odds of a room being locked is 1/4
for i = #locations, 2, -1 do
local j = room.random:NextInteger(1, i)
locations[i], locations[j] = locations[j], locations[i]
end
for index,itemLocation in locations do
--guarantees that there will always be a key
--if the room is supposed to be locked
if index==1 and shouldBeLockedRoomed then
requiresKey = true
item.New(itemLocation, "Key")
--moves on to trying to spawn the next item
continue
end
--spawns the other items like batteries
local shouldSpawnAnItem = room.random:NextNumber() < (1/3)
if shouldSpawnAnItem then
local itemToSpawn = itemsThatCanSpawn[room.random:NextInteger(1, #itemsThatCanSpawn)]
item.New(itemLocation, itemToSpawn)
end
end
end
local newDoor = door.New(newRoom, number, requiresKey)
newRoom.Parent = workspace.GeneratedRooms
return newRoom
end
return room
Also, in regards to the itemsThatCanSpawn array … how would that look & would that go into the Item script?
This is my current Item script:
local item = {}
function item.Interact(player, prompt, template, itemName)
if player.Character then
if itemName == "Key" then
local tool = workspace.Items.Key:Clone()
tool.Parent = player.Character
tool.Handle.KeyJingle:Play()
end
prompt.Enabled = false
template:Destroy()
end
end
function item.New(location, itemName)
local itemObject = workspace.Items:FindFirstChild(itemName)
if itemObject then
local itemHandle = itemObject.Handle:Clone()
itemHandle.Position = location.WorldPosition
local weld = Instance.new("WeldConstraint")
weld.Part0 = itemHandle
weld.Part1 = location.Parent
weld.Parent = itemHandle
itemHandle.Parent = location
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = ""
prompt.MaxActivationDistance = 5
prompt.Parent = location
prompt.Style = "Custom" --added
prompt.Triggered:Connect(function(player)
item.Interact(player, prompt, itemHandle, itemName)
end)
end
end
return item
function item.GetItemNames(includeSpecialItems: boolean): {string}
local itemNames = {}
local itemFolder = workspace.Items
for _,item in itemFolder:GetChildren() do
if item:GetAttribute("IsSpecial") and not includeSpecialItems then
continue
end
table.insert(itemNames, item.Name)
end
return itemNames
end
and then itemsThatCanSpawn would become:
local itemsThatCanSpawn = item.GetItemNames(false)
-- Create a table of items to spawn
local itemsToSpawn = {
"Battery",
"Key"
}
-- Create a function to spawn the items in random locations within the furniture pieces
function SpawnItems(furniture)
for _, item in pairs(itemsToSpawn) do
local randomPosition = Vector3.new(math.random(-10, 10), math.random(-10, 10), math.random(-10, 10)) -- Generate a random position within the furniture piece
-- Spawn the item at the random position
local spawnedItem = Instance.new("Part") -- Create a part instance to represent the item being spawned
spawnedItem.Name = item -- Set the name of the part instance to match the name of the item being spawned
-- Set properties of the part instance to match that of an actual item in Roblox Studio
spawnedItem.Size = Vector3.new(1, 1, 1) -- Set size of part instance
-- Parent and position part instance relative to furniture piece
spawnedItem.Parent = furniture -- Parent part instance to furniture piece
spawnedItem.Position = randomPosition + furniture.Position -- Position part instance relative to furniture piece's position
end
end
local item = {}
function item.GetItems(includeSpecialItems: boolean): {string}
local itemNames = {}
local itemFolder = workspace.Items
for _,item in itemFolder:GetChildren() do
if item:GetAttribute("IsSpecial") and not includeSpecialItems then
continue
end
table.insert(itemNames, item.Name)
end
return itemNames
end
local itemsThatCanSpawn = item.GetItemNames(false)
--NOT SURE IF THIS IS WHERE TO ADD THIS?
function item.Interact(player, prompt, template, itemName)
if player.Character then
if itemName == "Key" then
local tool = workspace.Items.Key:Clone()
tool.Parent = player.Character
tool.Handle.KeyJingle:Play()
end
prompt.Enabled = false
template:Destroy()
end
end
function item.New(location, itemName)
local itemObject = workspace.Items:FindFirstChild(itemName)
if itemObject then
local itemHandle = itemObject.Handle:Clone()
itemHandle.Position = location.WorldPosition
local weld = Instance.new("WeldConstraint")
weld.Part0 = itemHandle
weld.Part1 = location.Parent
weld.Parent = itemHandle
itemHandle.Parent = location
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = ""
prompt.MaxActivationDistance = 5
prompt.Parent = location
prompt.Style = "Custom" --added
prompt.Triggered:Connect(function(player)
item.Interact(player, prompt, itemHandle, itemName)
end)
end
end
return item
However, I am getting an error on Line 34 stating
Handle is not a valid member of UnionOperation “Workspace.Items.Battery”
This makes sense as the Battery doesn’t have a handle; however, that’s because it isn’t a tool.
Also, I’m getting an error on Line 16
ServerScriptService.Server.Room.Item:16: attempt to call a nil value
ServerScriptService.Server.Room.Item:16: attempt to call a nil value
That wouldn’t work because you’re trying to clone an object that might not exist. Something more like this would might work:
function item.New(location, itemName)
local itemObject = workspace.Items:FindFirstChild(itemName)
if itemObject==nil then
return
end
local objectToDuplicate = (itemObject:IsA("Tool") and itemObject:FindFirstChild("Handle")) or itemObject
local newObject = objectToDuplicate:Clone()
newObject:PivotTo(location.WorldCFrame)
local partToWeldTo = if newObject:IsA("Model") then newObject.PrimaryPart else newObject
if partToWeldTo:IsA("BasePart") then
local weld = Instance.new("WeldConstraint")
weld.Part0 = partToWeldTo
weld.Part1 = location.Parent
weld.Parent = partToWeldTo
end
newObject.Parent = location
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = ""
prompt.MaxActivationDistance = 5
prompt.Parent = location
prompt.Style = Enum.ProximityPromptStyle.Custom
prompt.Triggered:Connect(function(player)
item.Interact(player, prompt, newObject, itemName)
end)
end)
That code above assumes all objects are one of the following: BasePart, Model, or Tool.