mirror of
https://github.com/unixtensor/Roblox-Elevator-Game.git
synced 2025-12-16 10:41:54 +00:00
Server refactored and *working*, client needs refactored to work
This commit is contained in:
77
src/server/main/PlayerAdded/Character/Actions.lua
Normal file
77
src/server/main/PlayerAdded/Character/Actions.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
type EventFunction = (KeyPressed: Enum.KeyCode) -> ()
|
||||
type ActionsTCP = RBXScriptConnection
|
||||
type LocalPlayer = Player
|
||||
type TCP = RemoteEvent
|
||||
type CharacterShared = Folder
|
||||
|
||||
type EventsDict = {
|
||||
[Enum.KeyCode]: EventFunction
|
||||
}
|
||||
|
||||
type ClassConstructor = typeof(setmetatable({} :: Constructor_Return_Props, {} :: Impl_Constructor))
|
||||
type Impl_Constructor = {
|
||||
__index: Impl_Constructor,
|
||||
constructor: Constructor_Fun,
|
||||
--Class functions
|
||||
Add: (self: ClassConstructor, Key: Enum.KeyCode, f: EventFunction) -> (),
|
||||
Remove: (self: ClassConstructor, Key: Enum.KeyCode) -> ()
|
||||
} & Impl_Static_Props
|
||||
|
||||
type Constructor_Fun = (CharacterShared: CharacterShared, LocalPlayer: LocalPlayer) -> ClassConstructor
|
||||
type Impl_Static_Props = {}
|
||||
type Constructor_Return_Props = {
|
||||
Events: EventsDict,
|
||||
CurrentActionsTCP_Event: ActionsTCP
|
||||
}
|
||||
|
||||
export type ActionsConstructor = ClassConstructor
|
||||
|
||||
local Actions = {} :: Impl_Constructor
|
||||
Actions.__index = Actions
|
||||
|
||||
function Actions.constructor(CharacterShared, LocalPlayer)
|
||||
local Events: EventsDict = {}
|
||||
|
||||
local Remote = Instance.new("RemoteEvent") :: TCP
|
||||
Remote.Name = "Actions"
|
||||
Remote.Parent = CharacterShared
|
||||
|
||||
local ActionsTCP_Event = Remote.OnServerEvent:Connect(function(Messenger: Player, Key: Enum.KeyCode)
|
||||
if Messenger.UserId == LocalPlayer.UserId then
|
||||
if typeof(Key) == "EnumItem" then
|
||||
local switch = Events[Key]
|
||||
if switch then
|
||||
switch(Key)
|
||||
end
|
||||
else
|
||||
warn(`[Server Actions]: Got an unknown type, Key="{typeof(Key)}" Value="{Key}" from: "{Messenger.Name}"`)
|
||||
end
|
||||
else
|
||||
Messenger:Kick(`"{Messenger.Name}", {Messenger.UserId} r="{Remote.Name}", 1="{tostring(Key)}"`)
|
||||
end
|
||||
end)
|
||||
|
||||
return setmetatable({
|
||||
Events = Events,
|
||||
CurrentActionsTCP_Event = ActionsTCP_Event
|
||||
}, Actions)
|
||||
end
|
||||
|
||||
function Actions:Add(Key, f)
|
||||
self.Events[Key] = f
|
||||
end
|
||||
|
||||
function Actions:Remove(Key)
|
||||
self.Events[Key] = nil
|
||||
|
||||
if not next(self.Events) then
|
||||
self.CurrentActionsTCP_Event:Disconnect()
|
||||
print("[Server Actions]: ")
|
||||
end
|
||||
end
|
||||
|
||||
return Actions
|
||||
123
src/server/main/PlayerAdded/Character/Flashlight.lua
Normal file
123
src/server/main/PlayerAdded/Character/Flashlight.lua
Normal file
@@ -0,0 +1,123 @@
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
type UDP = UnreliableRemoteEvent
|
||||
type FlashlightUDP = RBXScriptConnection
|
||||
type LocalPlayer = Player
|
||||
type HumanoidRootPart = BasePart
|
||||
type EulerXYZ_struct = {number}
|
||||
type rbxassetid = string
|
||||
type CharacterShared = Folder
|
||||
|
||||
type ClassConstructor = typeof(setmetatable({} :: Constructor_Return_Props, {} :: Impl_Constructor))
|
||||
type Impl_Constructor = {
|
||||
__index: Impl_Constructor,
|
||||
constructor: Constructor_Fun,
|
||||
--Class functions
|
||||
On: (self: ClassConstructor) -> (),
|
||||
Off: (self: ClassConstructor) -> (),
|
||||
Toggle: (self: ClassConstructor) -> ()
|
||||
} & Impl_Static_Props
|
||||
|
||||
type Constructor_Fun = (CharacterShared: CharacterShared, LocalPlayer: LocalPlayer, HumanoidRootPart: HumanoidRootPart) -> ClassConstructor
|
||||
type Impl_Static_Props = {
|
||||
Enabled: boolean,
|
||||
HeadHeight: number,
|
||||
ToggleSound: rbxassetid
|
||||
}
|
||||
type Constructor_Return_Props = {
|
||||
FlashlightPart: Part,
|
||||
SpotLight: SpotLight,
|
||||
ToggleSound: Sound
|
||||
}
|
||||
|
||||
export type FlashlightConstructor = ClassConstructor
|
||||
|
||||
local Flashlight = {} :: Impl_Constructor
|
||||
Flashlight.__index = Flashlight
|
||||
|
||||
Flashlight.Enabled = false
|
||||
Flashlight.HeadHeight = 1
|
||||
Flashlight.ToggleSound = "rbxassetid://16454678462"
|
||||
|
||||
local function FlashlightPart(HumanoidRootPart: HumanoidRootPart): (Part, SpotLight, Sound)
|
||||
local FlashlightPart = Instance.new("Part") :: Part
|
||||
FlashlightPart.Size = Vector3.new(.1,.1,.1)
|
||||
FlashlightPart.CFrame = HumanoidRootPart.CFrame+Vector3.yAxis
|
||||
FlashlightPart.CanCollide = false
|
||||
FlashlightPart.CastShadow = false
|
||||
FlashlightPart.Anchored = true
|
||||
FlashlightPart.Transparency = 1
|
||||
|
||||
local SpotLight = Instance.new("SpotLight") :: SpotLight
|
||||
SpotLight.Color = Color3.new(1,1,1)
|
||||
SpotLight.Angle = 80
|
||||
SpotLight.Range = 30
|
||||
SpotLight.Brightness = 2
|
||||
SpotLight.Shadows = true
|
||||
SpotLight.Enabled = false
|
||||
SpotLight.Parent = FlashlightPart
|
||||
|
||||
local ToggleSound = Instance.new("Sound") :: Sound
|
||||
ToggleSound.Name = "Flashlight"
|
||||
ToggleSound.SoundId = Flashlight.ToggleSound
|
||||
ToggleSound.Volume = .5
|
||||
ToggleSound.Parent = FlashlightPart
|
||||
|
||||
FlashlightPart.Parent = HumanoidRootPart
|
||||
|
||||
return FlashlightPart, SpotLight, ToggleSound
|
||||
end
|
||||
|
||||
function Flashlight.constructor(CharacterShared, LocalPlayer, HumanoidRootPart)
|
||||
local FlashlightPart, SpotLight, ToggleSound = FlashlightPart(HumanoidRootPart)
|
||||
|
||||
local Flashlight_UDP = Instance.new("UnreliableRemoteEvent") :: UDP
|
||||
Flashlight_UDP.Name = "Flashlight"
|
||||
Flashlight_UDP.Parent = CharacterShared
|
||||
|
||||
Flashlight_UDP.OnServerEvent:Connect(function(Messenger: Player, CameraEuler: EulerXYZ_struct)
|
||||
if Messenger.UserId == LocalPlayer.UserId then
|
||||
if CameraEuler[4] and CameraEuler[4] == 3 then
|
||||
local RootPartCFrame = HumanoidRootPart.CFrame
|
||||
FlashlightPart.CFrame = CFrame.new(
|
||||
RootPartCFrame.X,
|
||||
RootPartCFrame.Y+Flashlight.HeadHeight,
|
||||
RootPartCFrame.Z)*CFrame.Angles(CameraEuler[1], CameraEuler[2], CameraEuler[3])
|
||||
end
|
||||
else
|
||||
Messenger:Kick(`"{Messenger.Name}", {Messenger.UserId} r="{Flashlight_UDP.Name}", 1="{tostring(CameraEuler)}"`)
|
||||
end
|
||||
end)
|
||||
|
||||
return setmetatable({
|
||||
FlashlightPart = FlashlightPart,
|
||||
SpotLight = SpotLight,
|
||||
ToggleSound = ToggleSound
|
||||
}, Flashlight)
|
||||
end
|
||||
|
||||
function Flashlight:On()
|
||||
Flashlight.Enabled = true
|
||||
|
||||
self.ToggleSound:Play()
|
||||
self.SpotLight.Enabled = Flashlight.Enabled
|
||||
end
|
||||
|
||||
function Flashlight:Off()
|
||||
Flashlight.Enabled = false
|
||||
|
||||
self.ToggleSound:Play()
|
||||
self.SpotLight.Enabled = Flashlight.Enabled
|
||||
end
|
||||
|
||||
function Flashlight:Toggle()
|
||||
if Flashlight.Enabled then
|
||||
self:Off()
|
||||
else
|
||||
self:On()
|
||||
end
|
||||
end
|
||||
|
||||
return Flashlight
|
||||
58
src/server/main/PlayerAdded/Character/Shadows.lua
Normal file
58
src/server/main/PlayerAdded/Character/Shadows.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
--This really should be only client
|
||||
|
||||
type Character = Model
|
||||
|
||||
type ClassConstructor = typeof(setmetatable({} :: Constructor_Return_Props, {} :: Impl_Constructor))
|
||||
type Impl_Constructor = {
|
||||
__index: Impl_Constructor,
|
||||
constructor: Constructor_Fun,
|
||||
--Class functions
|
||||
PartToggle: (self: ClassConstructor, Instance: Instance, CastingShadow: boolean) -> (),
|
||||
Remove: (self: ClassConstructor, Key: Enum.KeyCode) -> (),
|
||||
on: (self: ClassConstructor) -> (),
|
||||
off: (self: ClassConstructor) -> ()
|
||||
}
|
||||
|
||||
type Constructor_Fun = (Character: Character) -> ClassConstructor
|
||||
type Constructor_Return_Props = {
|
||||
Character: Character
|
||||
}
|
||||
|
||||
export type ShadowsConstructor = ClassConstructor
|
||||
|
||||
local Shadows = {} :: Impl_Constructor
|
||||
Shadows.__index = Shadows
|
||||
|
||||
function Shadows.constructor(Character)
|
||||
return setmetatable({
|
||||
Character = Character
|
||||
}, Shadows)
|
||||
end
|
||||
|
||||
function Shadows:PartToggle(Instance, CastingShadow)
|
||||
if Instance:IsA("BasePart") then
|
||||
Instance.CastShadow = CastingShadow
|
||||
end
|
||||
end
|
||||
|
||||
local function CharacterShadows(self: ClassConstructor, enabled: boolean)
|
||||
local CharacterDescendants = self.Character:GetDescendants()
|
||||
|
||||
for n: number = 1, #CharacterDescendants do
|
||||
self:PartToggle(CharacterDescendants[n] :: BasePart, enabled)
|
||||
end
|
||||
end
|
||||
|
||||
function Shadows:on()
|
||||
CharacterShadows(self, true)
|
||||
end
|
||||
|
||||
function Shadows:off()
|
||||
CharacterShadows(self, false)
|
||||
end
|
||||
|
||||
return Shadows
|
||||
105
src/server/main/PlayerAdded/Character/SpineKinematics.lua
Normal file
105
src/server/main/PlayerAdded/Character/SpineKinematics.lua
Normal file
@@ -0,0 +1,105 @@
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
type Head = BasePart
|
||||
type UpperTorso = BasePart
|
||||
type Neck = Motor6D
|
||||
type Waist = Motor6D
|
||||
type NeckC0 = CFrame
|
||||
type WaistC0 = CFrame
|
||||
type UDP = UnreliableRemoteEvent
|
||||
|
||||
type struct_SpineMovement = {
|
||||
Neck: CFrame,
|
||||
Waist: CFrame
|
||||
}
|
||||
|
||||
type ClassConstructor = typeof(setmetatable({} :: Constructor_Return_Props, {} :: Impl_Constructor))
|
||||
type Impl_Constructor = {
|
||||
__index: Impl_Constructor,
|
||||
constructor: Constructor_Fun,
|
||||
--Class functions
|
||||
Move: (self: ClassConstructor, CameraCFrame: CFrame, IsFirstPerson: boolean) -> ()
|
||||
} & Impl_Static_Props
|
||||
|
||||
type Constructor_Fun = (Head: Head, UpperTorso: UpperTorso) -> ClassConstructor
|
||||
type Impl_Static_Props = {
|
||||
Enabled: boolean,
|
||||
Multiplier: number
|
||||
}
|
||||
type Constructor_Return_Props = {
|
||||
Remote: UDP,
|
||||
Head: Head,
|
||||
UpperTorso: UpperTorso,
|
||||
Neck: Neck,
|
||||
Waist: Waist,
|
||||
NeckC0: NeckC0,
|
||||
WaistC0: WaistC0
|
||||
}
|
||||
|
||||
export type SpineKinematicsConstructor = ClassConstructor
|
||||
|
||||
local Spine = {} :: Impl_Constructor
|
||||
Spine.__index = Spine
|
||||
|
||||
Spine.Enabled = true
|
||||
Spine.Multiplier = .5
|
||||
|
||||
local Remote = Instance.new("UnreliableRemoteEvent") :: UDP
|
||||
Remote.Name = "SpineStream"
|
||||
Remote.Parent = _G.include(script, "CharacterShared")
|
||||
|
||||
function Spine.constructor(Head, UpperTorso)
|
||||
local self = {}
|
||||
self.Head = Head
|
||||
self.UpperTorso = UpperTorso
|
||||
self.Neck = Head:WaitForChild("Neck") :: Motor6D
|
||||
self.Waist = UpperTorso:WaitForChild("Waist") :: Motor6D
|
||||
self.Remote = Remote
|
||||
self.NeckC0 = (self.Neck :: Motor6D).C0
|
||||
self.WaistC0 = (self.Neck :: Motor6D).C0
|
||||
|
||||
return setmetatable(self, Spine)
|
||||
end
|
||||
|
||||
local function SpineMovement(self: ClassConstructor, CameraCFrame: CFrame, IsFirstPerson: boolean): struct_SpineMovement
|
||||
local HeadCFrame: CFrame = self.Head.CFrame
|
||||
local TorsoPosition: Vector3 = self.UpperTorso.Position
|
||||
local TorsoLookVector: Vector3 = self.UpperTorso.CFrame.LookVector
|
||||
local HeadPosition: Vector3 = HeadCFrame.Position
|
||||
local CameraPosition: Vector3 = CameraCFrame.Position
|
||||
|
||||
local HeadDelta: Vector3 = HeadPosition-CameraPosition
|
||||
local TorsoDeltaNormal: Vector3 = (TorsoPosition-CameraPosition).Unit
|
||||
local HeadDeltaNormal: Vector3 = HeadDelta.Unit
|
||||
local HeadDeltaMagnitude: number = HeadDelta.Magnitude
|
||||
|
||||
local arc = Spine.Multiplier*math.asin(HeadDelta.Y/HeadDeltaMagnitude)
|
||||
local Neck = CFrame.Angles(arc, 0, 0)
|
||||
local Waist = CFrame.Angles(arc, 0, 0)
|
||||
|
||||
if not IsFirstPerson then
|
||||
--Make this less cringe at some point, combine the dot product into the equation
|
||||
local LookingAtSelf = CameraCFrame.LookVector:Dot(HeadCFrame.LookVector)<0 --Make our head face the camera if we are looking at ourself
|
||||
local HeadCrossDelta = Spine.Multiplier*HeadDeltaNormal:Cross(TorsoLookVector).Y
|
||||
local TorsoCrossDelta = Spine.Multiplier*TorsoDeltaNormal:Cross(TorsoLookVector).Y
|
||||
|
||||
Neck*=CFrame.Angles(0, HeadCrossDelta, 0)
|
||||
Waist*=CFrame.Angles(0, LookingAtSelf and TorsoCrossDelta or -TorsoCrossDelta, 0)
|
||||
end
|
||||
|
||||
return {
|
||||
Neck = Neck,
|
||||
Waist = Waist
|
||||
}
|
||||
end
|
||||
|
||||
function Spine:Move(CameraCFrame, IsFirstPerson)
|
||||
local SpineIK = SpineMovement(self, CameraCFrame, IsFirstPerson)
|
||||
|
||||
self.Neck.C0 = self.Neck.C0:Lerp(self.NeckC0*SpineIK.Neck, .9)
|
||||
self.Waist.C0 = self.Waist.C0:Lerp(self.WaistC0*SpineIK.Waist, .9)
|
||||
end
|
||||
|
||||
return Spine
|
||||
102
src/server/main/PlayerAdded/Character/init.lua
Normal file
102
src/server/main/PlayerAdded/Character/init.lua
Normal file
@@ -0,0 +1,102 @@
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
local Shadows = require(script:WaitForChild("Shadows"))
|
||||
local SpineModule = require(script:WaitForChild("SpineKinematics"))
|
||||
local FlashlightModule = require(script:WaitForChild("Flashlight"))
|
||||
local ActionsModule = require(script:WaitForChild("Actions"))
|
||||
|
||||
type UDP = UnreliableRemoteEvent
|
||||
type CharacterSharedFolder = Folder
|
||||
type Character = Model
|
||||
type HumanoidRootPart = BasePart
|
||||
|
||||
type ClassConstructor = typeof(setmetatable({} :: Constructor_Return_Props, {} :: Impl_Constructor))
|
||||
type Impl_Constructor = {
|
||||
__index: Impl_Constructor,
|
||||
constructor: Constructor_Fun,
|
||||
--Class functions
|
||||
Enable: (self: ClassConstructor) -> (),
|
||||
Disable: (self: ClassConstructor) -> ()
|
||||
} & Impl_Static_Props
|
||||
|
||||
type Constructor_Fun = (Character: Character) -> ClassConstructor
|
||||
type Impl_Static_Props = {
|
||||
FlashlightDebounce: boolean,
|
||||
FlashlightCooldown: number,
|
||||
KeyBinds: {
|
||||
Flashlight: Enum.KeyCode
|
||||
}
|
||||
}
|
||||
|
||||
type Constructor_Return_Props = {
|
||||
CharacterShared: CharacterSharedFolder,
|
||||
CharacterShadows: Shadows.ShadowsConstructor,
|
||||
Spine: SpineModule.SpineKinematicsConstructor,
|
||||
Flashlight: FlashlightModule.FlashlightConstructor,
|
||||
Actions: ActionsModule.ActionsConstructor
|
||||
}
|
||||
|
||||
local Character = {} :: Impl_Constructor
|
||||
Character.__index = Character
|
||||
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
Character.FlashlightDebounce = false
|
||||
Character.FlashlightCooldown = .10
|
||||
|
||||
Character.KeyBinds = {
|
||||
Flashlight = Enum.KeyCode.F
|
||||
}
|
||||
|
||||
function Character.constructor(CharacterModel)
|
||||
local Player = Players:GetPlayerFromCharacter(CharacterModel)
|
||||
|
||||
local self = {}
|
||||
self.CharacterShared = Instance.new("Folder")
|
||||
self.CharacterShared.Name = "shared"
|
||||
self.CharacterShared.Parent = CharacterModel
|
||||
|
||||
local Head = CharacterModel:WaitForChild("Head") :: BasePart
|
||||
local UpperTorso = CharacterModel:WaitForChild("UpperTorso") :: BasePart
|
||||
local HumanoidRootPart = CharacterModel:WaitForChild("HumanoidRootPart") :: HumanoidRootPart
|
||||
|
||||
self.CharacterShadows = Shadows.constructor(CharacterModel)
|
||||
self.Spine = SpineModule.constructor(Head, UpperTorso)
|
||||
self.Flashlight = FlashlightModule.constructor(self.CharacterShared, Player, HumanoidRootPart)
|
||||
self.Actions = ActionsModule.constructor(self.CharacterShared, Player)
|
||||
|
||||
self.CharacterShadows:off() --I plan having 2+ player support and characters block a ton of light
|
||||
CharacterModel.DescendantAdded:Connect(function(Instance)
|
||||
task.wait() --Wait for the instance to properly replicate...
|
||||
self.CharacterShadows:PartToggle(Instance, false)
|
||||
end)
|
||||
|
||||
self.Actions:Add(Character.KeyBinds.Flashlight, function(_KeyPressed)
|
||||
if not Character.FlashlightDebounce then
|
||||
self.Flashlight:Toggle()
|
||||
|
||||
Character.FlashlightDebounce = true
|
||||
task.wait(Character.FlashlightCooldown)
|
||||
Character.FlashlightDebounce = false
|
||||
end
|
||||
end)
|
||||
|
||||
self.Spine.Remote.OnServerEvent:Connect(function(Messenger: Player, CameraPosition: CFrame, IsFirstPerson: boolean)
|
||||
if Messenger.UserId == Player.UserId then
|
||||
if SpineModule.Enabled then
|
||||
self.Spine:Move(CameraPosition, IsFirstPerson)
|
||||
else
|
||||
--reset
|
||||
print("TODO reached -", script.Name..".lua")
|
||||
end
|
||||
else
|
||||
Messenger:Kick(`"{Messenger.Name}", {Messenger.UserId} r="{self.Spine.Remote.Name}", 1="{tostring(CameraPosition)}", 2="{tostring(IsFirstPerson)}"`)
|
||||
end
|
||||
end)
|
||||
|
||||
return setmetatable(self, Character)
|
||||
end
|
||||
|
||||
return Character
|
||||
15
src/server/main/PlayerAdded/Users.lua
Normal file
15
src/server/main/PlayerAdded/Users.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
type Users = {
|
||||
Admin: {number},
|
||||
Banned: {number}
|
||||
}
|
||||
|
||||
local Users = {
|
||||
Admin = {},
|
||||
Banned = {}
|
||||
}
|
||||
|
||||
return Users
|
||||
40
src/server/main/PlayerAdded/init.lua
Normal file
40
src/server/main/PlayerAdded/init.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
--!optimize 2
|
||||
--!native
|
||||
--!strict
|
||||
|
||||
type Character = Model
|
||||
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local Users = require(script:WaitForChild("Users"))
|
||||
local CharacterModule = require(script:WaitForChild("Character"))
|
||||
|
||||
local function LoadCharacter(Player: Player)
|
||||
CharacterModule.constructor(Player.Character or Player.CharacterAdded:Wait())
|
||||
Player.CharacterAdded:Connect(CharacterModule.constructor)
|
||||
end
|
||||
|
||||
local function AdminUser(Player: Player)
|
||||
print(`[Admin] "{Player.Name}" | {tostring(Player.UserId)} has logged`)
|
||||
|
||||
Player.Chatted:Connect(function(Message: string, _: Player?)
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
return Players.PlayerAdded:Connect(function(Player: Player)
|
||||
for n: number = 1, #Users.Banned do
|
||||
if Player.UserId ~= Users.Banned[n] then
|
||||
for n2: number = 1, #Users.Admin do
|
||||
if Player.UserId == Users.Admin[n2] then
|
||||
AdminUser(Player)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
LoadCharacter(Player)
|
||||
else
|
||||
Player:Kick()
|
||||
end
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user