About Me
Hello. I am writing this as a scripter. The point of this post is to document some things I have done in the past.
I have been scripting in Roblox for about 5 years. Pretty much all of the projects I have created came from “I wonder..” or “Would this work?”
Not everything I do works well, most of it just works once, then stops when showing to others. :C But I find you can learn a lot more about building a rocket when it crashes and burns than if nothing goes wrong.
It was been good fun, and look forward to doing more, either myself or with others.
Showcase
Here are some of the projects I have worked on. They are all solo things. The few group projects I was a part of were never documented. (which might have something to do with the lack of organization in those projects…)
I’ll post an image, some information, and a code sample from the project.
(If snooping around, feel free to take any ideas you want from the code )
Before They Come
A tower defense game in which the player would collect resources on a randomly generated tile-world to build defenses to protect their orb. The project was titled “Before They Come” and was only ever a prototype, but it was fun to make.
For a code example, this is the main control script for the project.
Can you tell it was a personal project?
local Players = game:GetService("Players")
local repStorage = game:GetService("ReplicatedStorage")
local serStorage = game:GetService("ServerStorage")
local eventFolder = repStorage:WaitForChild("Events")
local itemPickup = eventFolder:WaitForChild("ItemPickup")
local toolUse = eventFolder:WaitForChild("ToolUse")
local buildRequest = eventFolder:WaitForChild("BuildRequest")
local soundChange = eventFolder.SoundChange
local items = serStorage:WaitForChild("Items")
local objectStats = require(repStorage:WaitForChild("Modules"):WaitForChild("ObjectStats"))
local harvestType = require(repStorage.Modules:WaitForChild("HarvestTypes"))
local buildingStats = require(repStorage.Modules.BuildingStats)
local gameControl = require(repStorage.Modules.GameControl)
local guardStats = require(repStorage.Modules.GuardStats)
local Buildings = repStorage:WaitForChild("Buildings")
--Configs for stuff
local pickupDis = 10
local toolHitDis = 10
--_______ITEM PICKUP CONTROL_______↓
local function FindTarget(target)
local parts = workspace:GetChildren()
for _, v in next, parts do
if v == target then
return true
end--end if v==target
end--end for loop
end--end function
itemPickup.OnServerEvent:Connect(function(player,target)
--first find out if the player is close enough to get the item
local disToPart = (player.Character.HumanoidRootPart.Position - target.Position).magnitude--get the distance from player to item
--print(target.Name)
if disToPart <= pickupDis then
local find = FindTarget(target)
if find == true then--we want to make sure the item is still in the game
--print("Close enough")
--Now we get all the stuff from the player that we need
local playerGui = player.PlayerGui
local inventoryGui = playerGui.InventoryGui
local itemFrameHolder = inventoryGui.MainFrame.ItemFrame
local itemFrame = itemFrameHolder:FindFirstChild(target.Name)--this is badly named, but it is the items panel
local inventory = player:FindFirstChild("Inventory")--the inventory where the item will be stored
local item = inventory:FindFirstChild(target.Name)
item.Value = item.Value + 1--give the player the item in their inventroy
itemFrame.Amount.Text = tostring(item.Value)
--the following lines are to remind me of my failuers
--local amount = itemFrame:FindFirstChild("Amount")
--amount.Text = tostring(item.Value)
target:Destroy()--remove the item so that they cannot get more
--print("Item has been collected")--this is for testing
end
end
end)
--_______END OF PICKUP CONTROL________↑
--_____TOOL USE (AXE AND PICKAXE)________↓
toolUse.OnServerEvent:Connect(function(player,character,tool)
local humRootPos = character.HumanoidRootPart.Position
for _, part in next, game.Workspace:GetChildren() do
if part:FindFirstChild("Health", true) then--loop though everything! Might be a bad idea
local health = part:FindFirstChild("Health",true)
if health:IsA("IntValue") then--make sure the health is not from a character
local distance = (health.Parent.Position - character.HumanoidRootPart.Position).magnitude
if distance <= toolHitDis then
local object = health.Parent--getting the parent of health, which is a part
local objectModel = object.Parent--getting the object parent
--print(object)
--Now we can do stuff to damage it or harvest it
local hType = harvestType[object.Name].htype
if hType == "wood" then--Find out what damage will be done, and the tool will do the damage
local damage = objectStats[tool.Name].woodDamage
health.Value = health.Value - damage
--print(health.Value)
elseif hType == "stone" then
local damage = objectStats[tool.Name].stoneDamage
health.Value = health.Value - damage
--print(health.Value)
end
--If health is 0 or lower
if health.Value <= 0 then
--time to harvest
local drop = harvestType[object.Name].yield--what item to get
local amount = harvestType[object.Name].amount--how much to get
local neededItem = items:FindFirstChild(tostring(drop))
--print(neededItem)
repeat
local dropClone = neededItem:Clone()
dropClone.Position = object.Position
dropClone.Parent = workspace
amount = amount - 1
until amount <= 0
if objectModel:FindFirstChild("Leaf") then--leaf spawning because it won't work properly for some reason
local leaf = objectModel.Leaf
local leafAmount = harvestType[tostring(leaf)].amount
local neededItem = items:FindFirstChild(tostring(leaf))
repeat
local dropClone = neededItem:Clone()
dropClone.Position = object.Position
dropClone.Parent = workspace
leafAmount = leafAmount - 1
until leafAmount <= 0
end
objectModel:Destroy()
end
end
end--end if health:IsA
end--end if part:FindFirstChild
end--end for loop
end)
--________END OF TOOL USE (AXE AND PICKAXE)_____↑
--_________PLAYER BUILDING CONTROL_________↓
local function HammerCheck(player)--checking if the player has the hammer tool
if player.Character:FindFirstChild("Hammer") then--
return true
else
return false
end--end if player.Character.Hammer then
end--end funton HammerCheck
buildRequest.OnServerEvent:Connect(function(player,requestedBuilding,cframe,pos)--this function works somehow
local hasHammer = HammerCheck(player)--tell server to check for tool "Hammer"
if hasHammer == true then
local playerInventory = player.Inventory--the players requested inventory
local playerGui = player.PlayerGui
local inventoryGui = playerGui.InventoryGui
local itemFrame = inventoryGui.MainFrame.ItemFrame
local mouse = player:GetMouse()
local neededBuilding = Buildings:FindFirstChild(tostring(requestedBuilding))
local buildStats = buildingStats[tostring(neededBuilding)]
local buildRecipe = buildingStats[tostring(neededBuilding)].Recipe
local canBuild = true
--Find we want to find out if the player has enough material
for item, number in pairs(buildRecipe) do
if playerInventory:FindFirstChild(item).Value - number >= 0 then
--print("all good")
else
canBuild = false
end--end if playerInventory
end--end for loop
if canBuild == true then--at this point, we do the building stuff
--take the item from playerInv
--time to do the stuff to set up the building
local neededBuildingClone = neededBuilding:Clone()
neededBuildingClone:MoveTo(pos)
neededBuildingClone:SetPrimaryPartCFrame(cframe)
neededBuildingClone.Parent = workspace--put the building into play
for item,number in pairs(buildRecipe) do
local itemInInv = playerInventory:FindFirstChild(item)
itemInInv.Value = itemInInv.Value - number
--Update player GUI
local itemHolder = itemFrame:FindFirstChild(tostring(item))--find the item frame we need
itemHolder.Amount.Text = tostring(itemInInv.Value)--update the number
end--end for itenm,number #2
end--end if canBuild == true
end--end is hasHammer == true
end)--end function
--____________END OF PLAYER BUILDING CONTROL_______↑
--________WORLD TIME________________↓
local timeStamps = serStorage.TimeStamps
local globalTime = 0--250
local function TimeCheck()--this function forwards global time and also changes the time values when needed
globalTime = globalTime + 1 --add 1 second to the global time
--print(globalTime)
for i,v in pairs(gameControl) do--for #, time value in pairs do
--print(i)
for _,value in pairs(v) do
local sTime = v.sTime--the start time of the time value
local eTime = v.eTime--the end time of the time value
local active = v.active
if globalTime > sTime then
if globalTime < eTime then--checking if the time slot is correct for the current time
--print(i)
--print(tostring(i).. " is ".. tostring((timeStamps:FindFirstChild(tostring(i)).Value)))
timeStamps:FindFirstChild(tostring(i)).Value = true--setting the value to true
else--else if globalTime < eTime then--checking if the time slot is correct for the current time
timeStamps:FindFirstChild(tostring(i)).Value = false--setting the value to false
end--end if globalTime < etime
else--else if globaltime > sTime
timeStamps:FindFirstChild(tostring(i)).Value = false--setting the value to false
end--end if global time > sTime
if timeStamps:FindFirstChild(tostring(i)).Value == true then
local soundId = gameControl[tostring(i)].soundId
soundChange:FireAllClients(soundId)
end--end if timeStamps:FindFirstChild(tostring(i)).Value == true then
end--end for _, value in pairs(v) do
end--end for i,v in pairs(gameControl) do
end--end function TimeCheck()
--_____END OF TIME CONTROL___________↑
--_______GUARD SPAWNING__________↓
local maxSpawn = 30 --how many guards can be in the game at once
local currentSpawn = script.CurrentSpawn.Value--how many guards are in the game ATM
local spawners = game.Workspace.BaseWorld.Spawners:GetChildren()
local guardFolder = serStorage:WaitForChild("Guards")
local spawnTime = 0
local controlScript = repStorage:WaitForChild("Scripts").GuardScript
local function CurrentSpawn()--Counts the number of guards in the workspace
currentSpawn = 0
for _, model in next, game.Workspace:GetChildren() do
--print(model)
if model:IsA("Model") then--we only are going to target models
if guardStats[model.Name] then--we only want to target guards, not the player
--print(model)
currentSpawn = currentSpawn + 1
--print("Count")
end--end if humRoot and model.Humanoid.Health > 0 then
end--end if guardStats[model.Name] then
end--end if model:IsA("Model") then
wait(.1)
return currentSpawn
end--end function CurrentSpawn
local function GuardSpawn()--Spawns guards in.
spawnTime = spawnTime + 1
local defendTime = serStorage.TimeStamps.Defend.Value
while defendTime == true do
local guardAmount = CurrentSpawn()
if guardAmount < maxSpawn then--if there are not max guards, pleae go on
-- print(currentSpawn)
spawners = game.Workspace.BaseWorld.Spawners:GetChildren()
local randomNum = math.random(1,#spawners)--make a random number
local randomSpawn = spawners[randomNum]--pick the spawner the guards will spawn from
local randomSpawn = spawners[randomNum]
local throwGuards = guardFolder:GetChildren()--lol the guards are throwing
local guardNmber = nil
local guardToSpawn = nil--which guard has been selected to spawn
repeat--Getting guars to spawn into the game, but we also want to make sure we spawn the right guards at the right time
guardNmber = math.random(1,# throwGuards)
guardToSpawn = throwGuards[guardNmber]--getting the type of guard we want to spawn
wait(.1)
until guardStats[tostring(guardToSpawn)].mTime < spawnTime --repeat untill we can spawn in guards that furfill the time stamp
local spawnAmount = math.random(guardStats[tostring(guardToSpawn)].minSpawn, guardStats[tostring(guardToSpawn)].maxSpawn)
repeat
spawnAmount = spawnAmount - 1
local guardClone = guardFolder:FindFirstChild(tostring(guardToSpawn)):Clone()
local scriptClone = controlScript:Clone()--putting the guard control sctip into the guard
scriptClone.Parent = guardClone
scriptClone.Disabled = false--Give it LIFE
guardClone.Parent = workspace
guardClone:MoveTo(randomSpawn.Position)
wait(.1)
until spawnAmount == 0
end--end if guardStats < maxSpawn then
end--end while globalTime["Defend"].active == true do
end--end function GuardSpawn
timeStamps.Defend.Changed:Connect(function()
if timeStamps.Defend.Value == true then--find out if it is time to call guard spawn
GuardSpawn()
end
end)--end timeStamps.Defend.Changed:Connect(function()
--________END OF GUARD SPAWNING___________↑
--____TOOL GIVE AND REMOVE_________↓
local tools = repStorage:WaitForChild("Tools")
--local Axe = tools.Axe
--local Pickaxe = tools.Pickaxe
--local Hammer = tools.Hammer
for _, timeStamp in next, timeStamps:GetChildren() do
--if timeStamp.Name ~= "Defend" then--we don't want to do anything for defend, this is taken care of somewhere else
timeStamp.Changed:Connect(function()
if timeStamp.Value == true then
local neededTools = gameControl[tostring(timeStamp)].Tools
for _, player in next, Players:GetChildren() do--do this for every player Multiplayer confirmed?
local char = player.Character
local pBackpack = player.Backpack
print("Hello")
--Clear the player backpack real quick
for _, pTool in next, pBackpack:GetChildren() do--clearing the player backpack
print(pTool)
if pTool:IsA("Tool") then
pTool:Destroy()
end--end if pTool:IsA("Tool") then
end--end for _, pTool in next, pBackpack:GetChildren() do
for _, part in next, char:GetChildren() do--clear tools from the character
if part:IsA("Tool") then
part:Destroy()--destroy the tool
end--end if part:IsA("Tool") then
end--end for _, part in next, char:GetChildren()
for tool, _ in pairs(neededTools) do--loop through every tool needed
local stringTool = tostring(tool)
local toolToClone = tools:FindFirstChild(stringTool)
if toolToClone ~= nil then--make sure this time stamp has a tool
local toolClone = tools:FindFirstChild(stringTool):Clone()
toolClone.Parent = pBackpack--put the tools each players backpack
end--end if toolToClone ~= nil
--toolToGet.Parent = pBackpack
end--end for _, tool in pairs(neededTools) do
end--end for_,player in next, Players:GetChildren() do
end--end if timeStamp.Value == true then
end)--end tomeStamp.Changed:Connect(function()
--end--end if v.Name ~= "Defend" then
end--end for _, v in next, timeStamps:GetChildren() do
--_____END OF TOOL GIVE AND REMOVE_____↑
--_____LOSING FUNCTION____↓
local objectV = game.Workspace:WaitForChild("ObjectOfValue",10)--wait up to 10 seconds for ObjectV
local gameLose = false
local function LoseGame()
end--end function LoseGame
objectV.Touched:Connect( function(part)-- if objectV was touched
local partParent = part.Parent
if gameLose == false then
if guardStats[tostring(partParent)] then
print("You Lose")
gameLose = true
--Do the stuff that happeneds whne you lose
for _, player in next, Players:GetChildren() do
if player then
player:Kick("You have lost the game, good try though ")--kick the player
end--end if player then
end--end for _, player in next, Players:GetChildren()
end--end if guardStats[tostring(partParent)] then
end-- end if gameLose == false then
end)--end objectV.Touched:Connect( function(part)
--____END LOSING FUNCTION___↑
while true do
print(globalTime)
TimeCheck()
wait(1)
if globalTime >= 360 then--end game
for _, player in next, Players:GetChildren() do
if player then
player:Kick("You have won. Sorry there is nothing after,.")
end
end
end--end game
end
Yea, this one is not a good example for code readability. This project was only ever to see if I could do it.
NPC Controlling Test
This was a test for creating units that a player could command. Although the actual gameplay was never completed beyond making the NPCS follow commands, I still was pleased with what I learned from it.
I don’t have any good code to show for this one.
It is pretty much some remote events for a player to tell their units “Move up” or to purchase more units.
Survival Game Project
This project was designed to be a game where you hit stuff to build stuff. It did allow you to hit stuff and was also fun to make. The building side never went though, but it the lessons I learned from this one were very valuable.
I would also like to ask for forgiveness for the GUI. It was thrown together in 10 minuets…
The code in this one is also not interesting. I’ll still post something…
Here is an example of something you should never do:
Server-side GUI control
equipTool.OnServerEvent:Connect(function(player,toolSlot,slotNum)
local plrData = player.PlayerData
local plrTrack = plrData.Tracker
local equippedSlot = plrTrack.EquippedToolSlot
local plrGui = player.PlayerGui
local hotbarFrame = plrGui.HUDGui.ToolsBar
local slotKeys = {}--for num keys
local findSlot --For clicking on the slot
if toolSlot then
findSlot = hotbarFrame:FindFirstChild(toolSlot.Name)
if toolSlot and findSlot and findSlot.Name ~= "Slot" then
--Check if tool slot is already selected
local numSave
for num, slot in pairs(hotbarFrame:GetChildren()) do
if slot:IsA("ImageButton") then
table.insert(slotKeys,num,tostring(slot))
end
end
--Track which number slot is being used
for num, slot in pairs(slotKeys) do
if tostring(slot) == toolSlot.Name then
--set equippted slot
equippedSlot.Value = num - 1 --the first number is not a slot
numSave = equippedSlot.Value
functions.EquipTool(nil,player)
else
functions.EquipTool(toolSlot,player)
break
end
end
--Update Equipped slot
for num, slot in pairs(slotKeys) do
if tostring(slot) == toolSlot.Name then
--set equippted slot
equippedSlot.Value = num - 1 --the first number is not a slot
break
end
end
end
elseif slotNum ~= nil then--for pressing a number key
--decode which slot it is
--getting all the slots
for _, slot in pairs(hotbarFrame:GetChildren()) do
if slot:IsA("ImageButton") then
table.insert(slotKeys,slot)
end
end
--get which tool slot is called
local toolSlot = slotKeys[slotNum]
--check if tool slot is already selected
if slotNum == equippedSlot.Value then --or toolSlot.Name == "Slot"
--Unequip tool by sending nil
functions.EquipTool(nil,player)
equippedSlot.Value = 0 --reset slot value (otherwise you cannot drawl tool again)
else
if toolSlot.Name ~= "Slot" then--check if slot is not empty
equippedSlot.Value = slotNum
functions.EquipTool(toolSlot,player)
end
end
end--end if toolSlot then/elseif slotNum ~= nil then
--check if toolSlot is a child of player hotbar
end)
(Yes, this was one of the first projects I made)
Tank Game
This comes from a long term project to make component style vehicles. The first thing I made with it was a tank. Why? Because it’s cool.
Working on this ranged from very fun to very unfun. I find OOP code to be your worst best friend.
This is also a good time to show a code example. Here is the VehicleController class written for the vehicle.
Note: Some of the sections of this code are incomplete. It appears as if some changes did not get saved. This was painful for me to find out.
Always spam ctrl+s
--!nonstrict
--set to strict to see crimes
--[[
Ground Vehicle Controller
Makes a platform with wheels
Pointer: Fancy name of ObjectValue because it is a pointer but for Instances lolz
]]
----_DEPENDENCIES_-----
--Service
local Players = game:GetService("Players")
----_END_DEPENDENCIES_----
----_CLASS_----
local wheelVehicleController = {}
wheelVehicleController.interface = {}
wheelVehicleController.schema = {}
wheelVehicleController.metatable = {__index = wheelVehicleController.schema}
wheelVehicleController.staticFunctions = {}
----_END_CLASS_----
----_VARS_----
--Config
local config = {
FOLDER_NAMES = {
WHEEL_FOLDER_NAME = "Wheels",
SCRIPTS_FOLDER_NAME = "ControlScripts",
SOUNDS_FOLDER_NAME = "Sounds",
MAIN_ASSEMBLY_FOLDER_NAME = "MainAssembly"
},
--Expected names of important MainAssembly pointers
MAIN_ASSEMBLY_POINTER = {
CONTROL_SEAT = "ControlSeat",
CHASSIS = "Chassis",
MASS = "Mass"
},
--Expected names of important scripts
SCRIPT_NAMES = {
LOCAL_CONTROL_SCRIPT = "control_local"
}
}
---_END_VARS_----
----_STATIC_FUNCTIONS_----
--Attempts to find the wheel folder
function wheelVehicleController.staticFunctions.get_folder_from_vehicle(vehicle : Model, folder_name : string) : Folder?
--Find instance with matching name
local requiredFolder = vehicle:FindFirstChild(folder_name)
if not requiredFolder then return nil end--Could not find instance
--Make sure instance is a folder (It could be anything, but it gotta be a folder because um... yes)
if not requiredFolder:IsA("Folder") then return nil end
return requiredFolder
end
--Function to search through folder of object values and return the value of the object with the corrosponding name
function wheelVehicleController.staticFunctions.get_instance_from_pointer_folder(pointer_folder : Folder, instance_name : string) : Instance?
--Attempt to find pointer (object value) in folder by name
local result = pointer_folder:FindFirstChild(instance_name)
if not result then
error( `Could not find {instance_name} in folder {pointer_folder.Name} (Required Instance missing!`)
end
--Make sure the result is an ObjectValue
if not result:IsA("ObjectValue") then
error(`{instance_name} in folder {pointer_folder} is not an ObjectValue. (Required Instance could not be found or is missing!`)
end
--If value is nil, there is potentially a problem
if not result.Value then
warn(`ObjectValue {instance_name} found in folder {pointer_folder}, but the value is nil. This may cause an error. (Make sure the value is set!`)
end
--All checks passed. Return object
return result.Value
end
--Returns array of wheels from vehicile. (Will error if it cannot find the wheels)
function wheelVehicleController.staticFunctions.get_wheels(vehicle) : {Part} --TODO: wheelArray refrence is a possible memory leak!!!! (Check this)
--Attempt to find wheel folder from vehicle
local wheelFolder = wheelVehicleController.staticFunctions.get_folder_from_vehicle(vehicle, config.FOLDER_NAMES.WHEEL_FOLDER_NAME)
assert(wheelFolder, `Could not find Wheel folder. (Expected name is {config.FOLDER_NAMES.WHEEL_FOLDER_NAME})`)
--Make array of wheels
local wheelArray = {}
for _, wheel in ipairs(wheelFolder:GetChildren()) do
if not wheel:IsA("Part") then continue end --Filter out non-part instances
table.insert(wheelArray,wheel)
end
return wheelArray
end
--Returns array of main assembly Instances
function wheelVehicleController.staticFunctions.get_main_assembly(vehicle : Model) : {ControlSeat : VehicleSeat, Chassis : Part, Mass : Part}
--Get MainAssembly folder
local assemblyFolder = wheelVehicleController.staticFunctions.get_folder_from_vehicle(vehicle, config.FOLDER_NAMES.MAIN_ASSEMBLY_FOLDER_NAME)
if not assemblyFolder then
error(`Could not find MainAssembly folder. (Expected name is {config.FOLDER_NAMES.MAIN_ASSEMBLY_FOLDER_NAME})`)
end
--Get the required instances from the folder
local controlSeat = wheelVehicleController.staticFunctions.get_instance_from_pointer_folder(assemblyFolder, config.MAIN_ASSEMBLY_POINTER.CONTROL_SEAT)
local chassis = wheelVehicleController.staticFunctions.get_instance_from_pointer_folder(assemblyFolder, config.MAIN_ASSEMBLY_POINTER.CHASSIS)
local mass = wheelVehicleController.staticFunctions.get_instance_from_pointer_folder(assemblyFolder, config.MAIN_ASSEMBLY_POINTER.MASS)
--_Check status of required instances_ (WARNING: LARGE IF BLOCK CHAIN!!!)--
if not controlSeat then
error("Could not find control VehicleSeat! (Make sure the ObjectValue for the control seat in MainAssembly is not nil")
elseif not controlSeat:IsA("VehicleSeat") then
error("ControlSeat ObjectValue does not point to a VehicleSeat!")
end
if not chassis then
error("Could not find vehicle Chassis! (Make sure the ObjectValue for the Chassis in MainAssembly is not nil")
elseif not chassis:IsA("Part") then
error("ControlSeat ObjectValue does not point to a Part!")
end
if not mass then
error("Could not find vehicle Mass! (Make sure the ObjectValue for the Mass in MainAssembly is not nil")
elseif not mass:IsA("Part") then
error("ControlSeat ObjectValue does not point to a Part!")
end
--_↑_--
return {ControlSeat = controlSeat, Chassis = chassis, Mass = mass}
end
--Returns the LocalControl script for the vehicle
function wheelVehicleController.staticFunctions.get_control_scripts(vehicle : Model) : {LocalControl : LocalScript}
--Get the ControlScripts folder
local scriptsFolder = wheelVehicleController.staticFunctions.get_folder_from_vehicle(vehicle, config.FOLDER_NAMES.SCRIPTS_FOLDER_NAME)
if not scriptsFolder then
error(`Could not find MainAssembly folder. (Expected name is {config.FOLDER_NAMES.MAIN_ASSEMBLY_FOLDER_NAME})`)
end
--Get required instance from the folder
local localControl = scriptsFolder:FindFirstChild(config.SCRIPT_NAMES.LOCAL_CONTROL_SCRIPT)
--_Check status of required instance_↓_
if not localControl then
error("Could not find local vehicle control script in scripts folder!")
elseif not localControl:IsA("LocalScript") then
error("LocalControl script is not a LocalScript!")
end
--_↑_--
--All checks passed
return {LocalControl = localControl}
end
--To-Be-Written (TBW)
--function wheelVehicleController.staticFunctions.get_sounds(vehicle) : {}
--end
--Handle when the seat occupant changes
function wheelVehicleController.staticFunctions.on_seated(self : class)
--[[
1: Check for seat occupant.
Seat occupant: take route A
No seat occupant: take route B
Route A:
Get the humanoid occupant of the seat
Assume the parent of the Humanoid is a character
Attempt to get the player who owns the character. (If cannot get player, remove character from seat)
Call function which returns a clone of the local control script (the local control script has a refrence to the vehicle)
]]
local seatOccupant : Humanoid? = self._controlSeat.Occupant
if self._controlSeat.Occupant then
--Path A: Set up player to control vehicle
local humOccupant : Humanoid = self._controlSeat.Occupant
--Get the occupant's player
local plrOccupant = Players:GetPlayerFromCharacter(humOccupant.Parent)
if not plrOccupant then
warn("Failed to get player from the occupant. Removing character from seat")
humOccupant.Jump = true
humOccupant.Sit = false
--_Clear_local_control_scripts
wheelVehicleController.staticFunctions.clear_local_control_scripts(self)
return
end
--_Give_player_ownership_of_vehicle_↓_--
self._controlSeat:SetNetworkOwner(plrOccupant)
self._currentPlayer = plrOccupant
--_↑_--
--_Give_player_local_control_script_↓_
local localControlClone = wheelVehicleController.staticFunctions.get_local_control_script_clone(self)
table.insert(self._activeLocalControlScripts, localControlClone)
localControlClone.Parent = humOccupant.Parent
localControlClone.Enabled = true--Local control should start disabled
--_↑_
else
--Path B: Remove previous player from vehicle control
--Clear local control scripts that may still exist
wheelVehicleController.staticFunctions.clear_local_control_scripts(self)
end
end
--Function to get a local control script clone, which is set up to allow for the player to control the vehicle
function wheelVehicleController.staticFunctions.get_local_control_script_clone(self : class) : LocalScript?
--Make sure the local control script was not deleted
if not self._controlScripts.LocalControl then
warn(`LocalControlScript for {self._vehicle} has gone missing!`)
return nil
end
--Make a clone of the local control script
local localControlClone = self._controlScripts.LocalControl:Clone()
--Put in a pointer back to the vehicle for the local control script
local objVal = Instance.new("ObjectValue")
objVal.Name = "VehicleRefrence"
objVal.Value = self._vehicle
objVal.Parent = localControlClone
return localControlClone
end
--Function to delete all local control scripts the object has created
function wheelVehicleController.staticFunctions.clear_local_control_scripts(self : class)
for i,v : Instance in ipairs(self._activeLocalControlScripts) do
v:Destroy()
end
table.clear(self._activeLocalControlScripts)
end
----_END_STATIC_FUNCTIONS_----
----_INTERFACE_----
function wheelVehicleController.interface.new(vehicle : Model)
--_Getting/Checking for folders_↓_--
local controlScriptsFolder = wheelVehicleController.staticFunctions.get_folder_from_vehicle(vehicle, config.FOLDER_NAMES.SCRIPTS_FOLDER_NAME)
local soundsFolder = wheelVehicleController.staticFunctions.get_folder_from_vehicle(vehicle, config.FOLDER_NAMES.SOUNDS_FOLDER_NAME)
local vehicleWheels = wheelVehicleController.staticFunctions.get_wheels(vehicle)--This will always either return or error
assert(soundsFolder, `Could not find Sounds folder. (Expected name is {config.FOLDER_NAMES.SOUNDS_FOLDER_NAME})`)
assert(controlScriptsFolder, `Could not find Control Scripts folder. (Expected name is {config.FOLDER_NAMES.SCRIPTS_FOLDER_NAME})`)
--_↑_--+
local self = setmetatable({}, wheelVehicleController.metatable)
self._config = {}
self._activeLocalControlScripts = {} :: {LocalScript}--Put local control scripts created by the controller to clean then up when needed
self._controlScripts = wheelVehicleController.staticFunctions.get_control_scripts(vehicle)
self._mainAssembly = wheelVehicleController.staticFunctions.get_main_assembly(vehicle)
self._currentPlayer = nil :: Player | nil
self._controlSeat = self._mainAssembly.ControlSeat
self._vehicle = vehicle
self._wheels = vehicleWheels
--_Internal_Connections_↓--
self._internalConnections = {
--VehicleSeat.Changed handler. (Used for checking if a character is sitting in the seat)
self._controlSeat.Changed:Connect(function(changed : string)
if changed ~= "Occupant" then return end
--Handle character sitting in seat/getting up
wheelVehicleController.staticFunctions.on_seated(self)
end)
}
--_↑_
return self
end
----_END_INTERFACE_----
----_METHODS_----
----_END_METHODS_----
----_CLASS_TYPE_----
type class = typeof(wheelVehicleController.interface.new(table.unpack(...)))
----_END_CLASS_TYPE_----
return wheelVehicleController.interface
Tool Crafting
There is no image for this one, as it was only ever a proof of concept for coding. I mainly included this to show a more up-to-date of my programming style nowaways
--Used to determine if the tool_parts passed has all of the required parts for the certain Tool Blueprint
local function hasRequiredParts(blueprint_data : toolTypeData.ToolBlueprint, tool_parts : {toolTypeData.ToolPart}) : boolean
--[[
1.Make a list of things needed by the Blueprint
2.Make a list of things provided by the "tool_parts"
3.Compare the lists
If they are equal, required parts present, return true
If they are not equal, required parts missing, return false
]]
--_Vars_↓_--
local blueprintCount = countPartList(blueprint_data._requiredParts)--Count of parts required to create the tool
local partCount = countPartList(tool_parts) --Count of parts provided to make the tool
--Flag
local hasParts = true--If the required parts are present. Assumed to be true, we want to disprove that
--_↑_--
--_Compare_Counts_↓_--
for partType,amount in pairs(blueprintCount) do
if partCount[partType] then
partCount[partType] -= amount
--Check if "tool_parts" has less then required amount of a certain part
if partCount[partType] ~= 0 then
hasParts = false
end
else
--If we cannot find this required type of part in "tool_parts", then not all required parts present!
hasParts = false
end
end
--_↑_--
return hasParts
end
Other
Nowadays, I spend more time with pencil and paper than I do in Roblox Studio. This helps make better developed ideas and code.
The amount of paper everywhere has got to be a fire hazard
Contact
You can contact me here on the Developer Forum for any question. I’ll try to respond as quick as I can.
Well, that’s about all that is interesting.
Thanks for reading!