From 1a5208b1f950bade2fb7dd4c873adac4888d2a3f Mon Sep 17 00:00:00 2001 From: loplkc Date: Sun, 26 Dec 2021 01:25:08 -0500 Subject: [PATCH] Complete redo of the game's structure due to orthogonality and ETC concerns --- .gitignore | 2 +- default.project.json | 127 +-- old.default.project.json | 184 ++++ src/client/ClientMessenger.ts | 9 + src/client/GuiHandler.ts | 203 ++++ src/client/init.client.ts | 1151 +---------------------- src/client/main.client.ts | 5 - src/server/ServerMessenger.ts | 13 + src/server/main.server.ts | 801 +--------------- src/services.d.ts | 78 +- src/shared/EntityManager.ts | 11 + src/shared/EventManager.ts | 13 + src/shared/PlayerManager.ts | 28 + src/shared/PuppetLibraries/Character.ts | 21 + src/shared/Puppetmaster.ts | 18 + src/shared/Remotes.ts | 4 + src/shared/module.ts | 3 - 17 files changed, 603 insertions(+), 2068 deletions(-) create mode 100644 old.default.project.json create mode 100644 src/client/ClientMessenger.ts create mode 100644 src/client/GuiHandler.ts delete mode 100644 src/client/main.client.ts create mode 100644 src/server/ServerMessenger.ts create mode 100644 src/shared/EntityManager.ts create mode 100644 src/shared/EventManager.ts create mode 100644 src/shared/PlayerManager.ts create mode 100644 src/shared/PuppetLibraries/Character.ts create mode 100644 src/shared/Puppetmaster.ts create mode 100644 src/shared/Remotes.ts delete mode 100644 src/shared/module.ts diff --git a/.gitignore b/.gitignore index fe46883..33ab997 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /node_modules /out /include -/models +/.old-src *.tsbuildinfo diff --git a/default.project.json b/default.project.json index 84ec99c..a15875e 100644 --- a/default.project.json +++ b/default.project.json @@ -31,18 +31,6 @@ "$className": "Workspace", "$properties": { "FilteringEnabled": true - }, - "Enemies": { - "$className": "Folder" - }, - "Effects": { - "$className": "Folder" - }, - "Projectiles": { - "$className": "Folder" - }, - "Map": { - "$className": "Folder" } }, "ReplicatedStorage": { @@ -56,115 +44,16 @@ "TS": { "$path": "out/shared" }, - "Remotes": { - "$className": "Folder", - "Input": { - "$className": "RemoteEvent" - }, - "Output": { - "$className": "RemoteEvent" - }, - "Dialog": { - "$className": "RemoteEvent" + "Input": { + "$className": "RemoteEvent", + "$properties": { + "Name": "Input" } }, - "Meshpile": { - "$className": "Folder", - "SlashM": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://5718206850" - } - }, - "SlashS": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://5718207735" - } - }, - "BallEffect": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://5718227062" - } - }, - "SphereFHD": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://888904946" - } - }, - "SphereQHD": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://1005510660" - } - }, - "SphereUHD": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://1221611042" - } - }, - "CylinderUHD": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://563974169" - } - }, - "CylinderFHD": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://442130733" - } - }, - "CylinderQHD": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://465435723" - } - }, - "10smash": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://489415286" - } - }, - "GroundWave": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://863344136" - } - }, - "InvertedSphere": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://4729380505" - } - }, - "FlatSlash1": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://6355336640" - } - }, - "CylinderShockwave1": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://6386165479" - } - }, - "SphereShockwave1": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://6355255043" - } - }, - "diamond": { - "$className": "MeshPart", - "$properties": { - "MeshId": "rbxassetid://2570899763" - } + "Output": { + "$className": "RemoteEvent", + "$properties": { + "Name": "Output" } } }, diff --git a/old.default.project.json b/old.default.project.json new file mode 100644 index 0000000..84ec99c --- /dev/null +++ b/old.default.project.json @@ -0,0 +1,184 @@ +{ + "name": "roblox-ts-game", + "globIgnorePaths": [ + "**/package.json", + "**/tsconfig.json" + ], + "tree": { + "$className": "DataModel", + "ServerScriptService": { + "$className": "ServerScriptService", + "TS": { + "$path": "out/server" + } + }, + "StarterPlayer": { + "$className": "StarterPlayer", + "StarterPlayerScripts": { + "$className": "StarterPlayerScripts", + "Client": { + "$path": "out/client" + } + } + }, + "Players": { + "$className": "Players", + "$properties": { + "CharacterAutoLoads": false + } + }, + "Workspace": { + "$className": "Workspace", + "$properties": { + "FilteringEnabled": true + }, + "Enemies": { + "$className": "Folder" + }, + "Effects": { + "$className": "Folder" + }, + "Projectiles": { + "$className": "Folder" + }, + "Map": { + "$className": "Folder" + } + }, + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "rbxts_include": { + "$path": "include", + "node_modules": { + "$path": "node_modules/@rbxts" + } + }, + "TS": { + "$path": "out/shared" + }, + "Remotes": { + "$className": "Folder", + "Input": { + "$className": "RemoteEvent" + }, + "Output": { + "$className": "RemoteEvent" + }, + "Dialog": { + "$className": "RemoteEvent" + } + }, + "Meshpile": { + "$className": "Folder", + "SlashM": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://5718206850" + } + }, + "SlashS": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://5718207735" + } + }, + "BallEffect": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://5718227062" + } + }, + "SphereFHD": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://888904946" + } + }, + "SphereQHD": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://1005510660" + } + }, + "SphereUHD": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://1221611042" + } + }, + "CylinderUHD": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://563974169" + } + }, + "CylinderFHD": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://442130733" + } + }, + "CylinderQHD": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://465435723" + } + }, + "10smash": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://489415286" + } + }, + "GroundWave": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://863344136" + } + }, + "InvertedSphere": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://4729380505" + } + }, + "FlatSlash1": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://6355336640" + } + }, + "CylinderShockwave1": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://6386165479" + } + }, + "SphereShockwave1": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://6355255043" + } + }, + "diamond": { + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxassetid://2570899763" + } + } + } + }, + "HttpService": { + "$className": "HttpService", + "$properties": { + "HttpEnabled": true + } + }, + "SoundService": { + "$className": "SoundService", + "$properties": { + "RespectFilteringEnabled": true + } + } + } +} diff --git a/src/client/ClientMessenger.ts b/src/client/ClientMessenger.ts new file mode 100644 index 0000000..b29aed5 --- /dev/null +++ b/src/client/ClientMessenger.ts @@ -0,0 +1,9 @@ +import { Input, Output } from "shared/Remotes"; +export function bindToOutput(functionToBind: Callback) { + assert(Output?.IsA("RemoteEvent"), 'Remote event "Input" is of incorrect class or nil'); + Output.OnClientEvent.Connect(functionToBind); +} +export function messageServer(messageType: clientMessageType, messageContent?: string) { + assert(Input?.IsA("RemoteEvent"), 'Remote event "Output" is of incorrect class or nil'); + Input.FireServer(messageType, messageContent); +} diff --git a/src/client/GuiHandler.ts b/src/client/GuiHandler.ts new file mode 100644 index 0000000..2a3ec61 --- /dev/null +++ b/src/client/GuiHandler.ts @@ -0,0 +1,203 @@ +// "GuiHandler": Handle Gui. +const tweenService = game.GetService("TweenService"); + +type supportedGuiElements = + | "Frame" + | "ScrollingFrame" + | "ViewportFrame" + | "TextLabel" + | "ImageLabel" + | "TextButton" + | "ImageButton" + | "TextBox"; + +interface guiStyleElement { + BackgroundTransparency?: number; + BackgroundColor?: Color3; + TextScaled?: boolean; + TextColor?: Color3; + Font?: Enum.Font; +} + +type onClickEntry = [[string, string, string?], string?]; + +interface guiElementEntry extends guiStyleElement { + Type: supportedGuiElements; + Style: guiStyle; + Position: UDim2; + Size: UDim2; + Text?: string; + onClick?: onClickEntry; + children?: guiElementEntry[]; +} + +type guiStyle = { + default: { + BackgroundTransparency: number; + BackgroundColor: Color3; + }; +} & { + [guiElementName in supportedGuiElements]?: guiStyleElement; +}; + +interface guiStyleTable { + [guiStyle: string]: guiStyle; +} + +interface guiEntry { + position?: UDim2; + size?: UDim2; + color?: Color3; + transparency: number; + pages: guiElementEntry[][]; + closeFunction: Callback; +} + +interface guiTable { + [gui: string]: guiEntry; +} + +const stylesTable: guiStyleTable = { + placeholder: { + default: { + BackgroundTransparency: 1, + BackgroundColor: Color3.fromRGB(0, 0, 0), + }, + TextLabel: { + TextColor: Color3.fromRGB(255, 0, 0), + Font: Enum.Font.AmaticSC, + TextScaled: true, + }, + TextButton: { + BackgroundTransparency: 0.125, + BackgroundColor: Color3.fromRGB(255, 0, 255), + TextScaled: true, + }, + }, +}; + +const guiTable: guiTable = { + MainMenu: { + transparency: 0, + pages: [ + [ + { + Type: "TextLabel", + Style: stylesTable["placeholder"], + Position: new UDim2(0, 0, 0.175, 0), + Size: new UDim2(1, 0, 0.15, 0), + Text: "Placeholder", + }, + { + Type: "TextButton", + Style: stylesTable["placeholder"], + Position: new UDim2(0.375, 0, 0.5, 0), + Size: new UDim2(0.25, 0, 0.125, 0), + Text: "Play", + onClick: [["MessageServer", "EnterGame"]], // The main menu should disappear in respone + }, + { + Type: "TextButton", + Style: stylesTable["placeholder"], + Position: new UDim2(0.375, 0, 0.665, 0), + Size: new UDim2(0.25, 0, 0.125, 0), + Text: "Settings", + }, + { + Type: "TextButton", + Style: stylesTable["placeholder"], + Position: new UDim2(0.375, 0, 0.83, 0), + Size: new UDim2(0.25, 0, 0.125, 0), + Text: "Changelog", + }, + ], + ], + closeFunction: function (baseFrame) { + tweenService.Create(baseFrame, new TweenInfo(0.5), { Position: new UDim2(1, 0, 0, 0) }).Play(); + }, + }, +}; + +export function handleGuiInput( + messageServerFunction: Callback, + dataModification: [string, string, string?], // Should be a clientMessageType but I cannot be bothered + guiReaction?: string, +) { + if (dataModification[0] === "MessageServer") { + messageServerFunction(dataModification[1], dataModification[2]); + } + if (guiReaction === "erase") { + // Gui should have an unload function + } +} + +function configureTextElement( + elementEntry: guiElementEntry, + elementStyle: guiStyleElement | undefined, + guiElement: TextLabel | TextButton | TextBox, +) { + guiElement.Text = elementEntry.Text || ""; // eslint-disable-line + guiElement.Font = elementEntry.Font || elementStyle?.Font || Enum.Font.Arial; // eslint-disable-line + guiElement.TextColor3 = elementEntry.TextColor || elementStyle?.TextColor || new Color3(0, 0, 0); + guiElement.TextScaled = elementEntry.TextScaled || elementStyle?.TextScaled || false; +} + +function drawGuiElement(elementEntry: guiElementEntry, objectsToReturn: [TextButton | ImageButton, onClickEntry][]) { + const elementType = elementEntry["Type"]; + const elementStyle = elementEntry["Style"][elementType]; + const elementStyleDefault = elementEntry["Style"].default; + const guiElement = new Instance(elementType); + guiElement.BackgroundTransparency = + elementEntry.BackgroundTransparency || // eslint-disable-line + elementStyle?.BackgroundTransparency || + elementStyleDefault.BackgroundTransparency; + guiElement.BackgroundColor3 = + elementEntry.BackgroundColor || elementStyle?.BackgroundColor || elementStyleDefault.BackgroundColor; + guiElement.Position = elementEntry.Position; + guiElement.Size = elementEntry.Size; + if (classIs(guiElement, "TextLabel") || classIs(guiElement, "TextBox") || classIs(guiElement, "TextButton")) { + configureTextElement(elementEntry, elementStyle, guiElement); + } + if (elementEntry.children) { + for (const subGuiElementEntry of elementEntry.children) { + const subGuiElement = drawGuiElement(subGuiElementEntry, objectsToReturn); + subGuiElement.Parent = guiElement; + } + } + if (elementEntry.onClick) { + assert( + classIs(guiElement, "TextButton") || classIs(guiElement, "ImageButton"), + 'Property "OnClick" should not exist on non-button instance!', + ); + objectsToReturn.push([guiElement, elementEntry.onClick]); + } + return guiElement; +} + +export function drawGui(PLAYERGUI: PlayerGui, guiName: "MainMenu") { + const objectsToReturn: [TextButton | ImageButton, onClickEntry][] = []; + const guiEntry = guiTable[guiName]; + assert(guiEntry, 'Error in Gui Handler: Gui "' + guiName + " not found!"); + // Actual Gui creation + const screenGui = new Instance("ScreenGui"); + screenGui.Name = guiName; + screenGui.ResetOnSpawn = false; // ? + screenGui.Parent = PLAYERGUI; + const baseFrame = new Instance("Frame"); + baseFrame.BackgroundTransparency = guiEntry.transparency; + baseFrame.BackgroundColor3 = guiEntry.color || Color3.fromRGB(0, 0, 0); + baseFrame.Position = guiEntry.position || new UDim2(0, 0, 0, 0); + baseFrame.Size = guiEntry.size || new UDim2(1, 0, 1, 0); + for (const guiElementEntry of guiEntry.pages[0]) { + const guiElement = drawGuiElement(guiElementEntry, objectsToReturn); + guiElement.Parent = baseFrame; + } + screenGui.Parent = PLAYERGUI; + return objectsToReturn; +} + +export function closeGui(PLAYERGUI: PlayerGui, guiName: "MainMenu") { + const screenGui = PLAYERGUI.WaitForChild(guiName, 1); + assert(screenGui, 'ScreenGui"' + guiName + '" does not exist!'); + screenGui; +} diff --git a/src/client/init.client.ts b/src/client/init.client.ts index 955f346..3c78a76 100644 --- a/src/client/init.client.ts +++ b/src/client/init.client.ts @@ -1,1129 +1,34 @@ -// Setup and Variables +// "init": The local script. This script doesn't have to account for any other players. +const Players = game.GetService("Players"); +const UserInputService = game.GetService("UserInputService"); +import { bindToOutput, messageServer } from "./ClientMessenger"; +import { handleGuiInput, drawGui, closeGui } from "./GuiHandler"; +const LOCALPLAYER = Players.LocalPlayer; +const PLAYERGUI = LOCALPLAYER.WaitForChild("PlayerGui", 1) as PlayerGui; +assert(PLAYERGUI && classIs(PLAYERGUI, "PlayerGui"), 'PlayerGui of "' + LOCALPLAYER.Name + '"does not exist! (HOW???)'); +let inMainMenu = true; -// Universal variables -import { makeHello } from "shared/module"; -const plrs = game.GetService("Players"); -const ws = game.GetService("Workspace"); -const tween = game.GetService("TweenService"); -const runService = game.GetService("RunService"); -const storage = game.GetService("ReplicatedStorage"); -// Aliases -const uD2 = UDim2; -const v3 = Vector3; -const cF = CFrame; -const inst = Instance; -const tN = tonumber; -const tS = tostring; -const wait = task.wait; -const clock = os.clock; -const floor = math.floor; -const ceil = math.ceil; -const abs = math.abs; -const cos = math.cos; -const sin = math.sin; -const rad = math.rad; -const rand = math.random; -const clamp = math.clamp; -const sub = string.sub; -// Constructors -const rgb = Color3.fromRGB; -const hsv = Color3.fromHSV; -const xyz = CFrame.fromEulerAnglesXYZ; -const wrap = coroutine.wrap; - -const zF = new cF(0, 0, 0); -//End of universal variables - -const uIS = game.GetService("UserInputService"); - -const rid = "rbxassetid://"; - -//const sequence = NumberSequence.new; -const upper = string.upper; -// const len = string.len; -const Plr = plrs.LocalPlayer; -const Cam = game.Workspace.CurrentCamera; -const efFolder = ws.Effects; - -const remotes = storage.Remotes; -const input = remotes.Input; -const output = remotes.Output; -//const ModeEvent = script.ModeEvent; -const camera = ws.CurrentCamera; -const camCustom = Enum.CameraType.Custom; -const camScriptable = Enum.CameraType.Scriptable; - -//const DialogEvent = Remotes.Dialog; -//const isDialog = false; -//const DialogList = require(script.Parent.Dialog) -//const enemyCEs = require(script.EnemyClientEvents) -/* -const eLM = storage.EnemyLocalModules.GetChildren(); -var enemyModules = {}; -warn("Amount of enemy modules: " + tSt(eLM.length)); -for i, v in pairs(eLM:GetChildren()) do - enemyModules[v.Name] = require(v) - warn("Required " .. v.Name) -end -*/ - -//const v3Lerp = Vector3.new().Lerp; - -function aRa(v: number) { - //NOT A JOJO REFERENCE SHUT UP - return (rand(-100, 100) / 100) * v; +function openMainMenu(playerGui: PlayerGui) { + messageServer("Placeholder", "OpeningMainMenu"); // > Server should check if there are entities to clean up + const mainMenuButtons = drawGui(playerGui, "MainMenu"); + for (const mainMenuButton of mainMenuButtons) { + mainMenuButton[0].Activated.Connect(function () { + handleGuiInput(messageServer, mainMenuButton[1][0], mainMenuButton[1][1]); + }); + } + inMainMenu = true; } -function getDist(one: Vector3, two: Vector3) { - return one.add(two).Magnitude; -} -function floorToBase(number: number, base: number) { - return floor(number * base) / base; -} -//music -const Music = new inst("Sound"); -Music.Name = "Music"; -Music.Looped = true; -Music.SoundId = rid + "564466195"; -Music.Parent = ws; -Music.Play(); -const SendPlaybackLoudness = false; -//Music.PlaybackSpeed = 1 -const coolUpdateCycle = 0; - -function tFind(tab: string[], element: string) { - return tab.findIndex((thisElement) => thisElement === element) !== -1; -} - -function omniWait(waitTime: number) { - let sum = 0; - while (sum < waitTime) { - const [t, step] = runService.Stepped.Wait(); - sum += step; +function handleInput(input: InputObject, otherInteraction: boolean) { + if (input.UserInputType === Enum.UserInputType.MouseButton1) { } } - -const plrGui = Plr.WaitForChild("PlayerGui"); -//////////////////////// -// Decorative Set GUI // -//////////////////////// -/* -var Gui = nil; -var Title = nil; -var TitleShadow = nil; -var Vis = nil; -var VisTable = {}; -var MusicLabel = nil; -function makeGui(whichGui) - Gui = script[whichGui + "Gui"].Clone() - Gui.Parent = plrGui - Title = Gui.Title - TitleShadow = Gui:FindFirstChild("TitleShadow") - Vis = Gui.Visualizer - local absSize = Vis.AbsoluteSize - Vis.Position = Vis.Position + uD2(0,absSize.Y-absSize.X,0,0) - Vis.Size = Vis.Size + uD2(0,absSize.Y-absSize.X,0,0) - VisTable = {} - MusicLabel = Gui.Top.Music - for i = 1, #Vis:GetChildren() do - local bar = Vis["Bar" .. tSt(i)] - tInsert(VisTable,{bar,bar.Position,bar.Size}) - end -end -makeGui("Star") - - -function changeGuiMode(name,color1,color2,font,artist,track) - Title.Text = upper(name) - Title.Font = font - Title.TextColor3 = color1 - Title.TextStrokeColor3 = color2 - MusicLabel.Text = artist .. " - " .. track - if TitleShadow then - TitleShadow.Text = upper(name) - TitleShadow.Font = font - end - for i, v in pairs(VisTable) do - v[1].BackgroundColor3 = color1 - end - //adapt to aspect ratio - local absSize = Vis.AbsoluteSize - Vis.Position = Vis.Position + uD2(0,absSize.Y-absSize.X,0,0) - Vis.Size = Vis.Size + uD2(0,absSize.Y-absSize.X,0,0) -end -*/ -//Miscellaneous -/* -function CamShake(ins,reps) { - var Hum = Plr.Character.Humanoid - for (let i = 1; i < reps; i++) { - local mult = 1-i/reps - local goal = v3(aRa(ins)*mult,aRa(ins)*mult,aRa(ins)*mult) - for j = 1,2 do - local mult = 1-i/reps - Hum.CameraOffset = v3Lerp(Hum.CameraOffset,goal,0.5) - omniWait(0.016) - end - } - local goal = v3(0,0,0) - for j = 1,2 do - Hum.CameraOffset = v3Lerp(Hum.CameraOffset,goal,0.5) - omniWait(0.016) - end - Hum.CameraOffset = goal -} - -var Notifier = plrGui.WaitForChild("Help") - -local tim = nil -local NotifyTinfo1 = TweenInfo.new(0.25,Enum.EasingStyle.Quad,Enum.EasingDirection.Out) -local NotifyTinfo2 = TweenInfo.new(0.5,Enum.EasingStyle.Linear) -local goal1 = {TextTransparency = 0} -local goal2 = {TextTransparency = 1} -function Notify(message) - tim = os.time() - local tim2 = tim - Notifier.Label.Text = message - if not Notifier.Enabled then - Notifier.Enabled = true - tween:Create(Notifier.Label,NotifyTinfo1,goal1):Play() - end - wait(2) - if tim == tim2 then - tween:Create(Notifier.Label,NotifyTinfo2,goal2):Play() - wait(1) - Notifier.Enabled = false - end -end -*/ -const modeKeys = ["Q", "E", "R", "F"]; -const abilityKeys = ["Z", "X", "C", "V"]; -const otherModeKeys = ["B", "N", "M", "1", "2", "3", "4"]; - -//////////////////// -// Main interface // -//////////////////// -/* -local mainInterface = plrGui:WaitForChild("MainInterface") -local interfaceCooldowns = mainInterface.Cooldowns -local interfaceDiamond = mainInterface.Diamond -local interfaceMenu = mainInterface.Menu -interfaceDiamond.Visible = false -interfaceMenu.Visible = false -*/ -//////////////////// -// Cooldown stuff // -//////////////////// -/* -local coolDown = {} -local movesExist = {Z = nil,X = nil,C = nil,V = nil} - -function getCoolTime(tab) - if not tab then - return 0 - end - local t = tab[2]-(clock()-tab[1]) - return t > 0 and floorToBase(t,10) or 0 -end - -local currentCoolCycle = 0 - -function updateCooldownGui(Set,Side,Mode,Color) - //Stop any other cooldown loops currently occurring - local thisCycle = clock() - currentCoolCycle = thisCycle - for i, inp in pairs(abilityKeys) do - local part = interfaceCooldowns[inp] - if movesExist[inp] then - part.Visible = true - if Color then - part.Star1.BackgroundColor3 = Color - part.Star1.Star12.BackgroundColor3 = Color - end - - //Show the counter - part.Gradient.Transparency = sequence(1,0.7) - part.WhiteBarBottom.Visible = true - part.WhiteBarTop.Visible = true - part.FramePart.Visible = true - part.WhiteBarDiagonal.Visible = true - part.Time.Visible = true - - //Get potential cooldowns - local cool = nil - local coolTime = 0 - - local cools = {coolDown.Main,coolDown[inp],coolDown[Set .. Side .. Mode .. inp]} - - //Determine the greatest cooldown - for i, v in pairs(cools) do - local t = getCoolTime(v) - if t > coolTime then - cool = v - coolTime = t - end - end - - coroutine.wrap(function() // Cool down - while currentCoolCycle == thisCycle and coolTime > 0 do //Update the cooltime and GUI while it exists - coolTime = getCoolTime(cool) - part.Time.Text = coolTime - wait(0.1) - end - - if coolTime <= 0 then //Remove the counter (only) once done cooling - part.Gradient.Transparency = sequence(1) - part.WhiteBarBottom.Visible = false - part.WhiteBarTop.Visible = false - part.FramePart.Visible = false - part.WhiteBarDiagonal.Visible = false - part.Time.Visible = false - end - end)() - else - part.Visible = false - end - end -end - - -function handleCooldown(cool,inp,Set,Side,Mode) - local now = floorToBase(clock(),10) - coolDown.Main = {now,cool} - coolDown[inp] = {now,cool*3} - coolDown[Set .. Side .. Mode .. inp] = {now,cool*9} - updateCooldownGui(Set,Side,Mode) -end -*/ - -////////////////////////////////// -// Mode switching diamond stuff // -////////////////////////////////// -let storedModeKey = ""; -/* -function resizeDiamond() - local diamondAbs = interfaceDiamond.AbsoluteSize - local sizeDiff = diamondAbs.Y-diamondAbs.X - interfaceDiamond.Size = uD2(0.281,sizeDiff,0.5,0) - interfaceDiamond.Position = uD2(0.359,sizeDiff/(-2),0.25,0) - local strokeSize = ceil(diamondAbs.Y*0.00926) - for i = 1, 4 do - interfaceDiamond["Box" .. tSt(i)].Inside.UIStroke.Thickness = strokeSize - end -end -resizeDiamond() - -function updateDiamond(data) - for j = 1, 4 do - local thisBox = interfaceDiamond["Box" .. tSt(j)].Inside - for i = 1, 4 do - local thisStar = thisBox["Star" .. tSt(i)] - //print("Trying to obtain " .. tSt(modeKeys[j]) .. tSt(modeKeys[i])) - local thisColor = data[modeKeys[j]][modeKeys[i]] - if thisColor then - thisStar.BackgroundColor3 = thisColor - thisStar.Piece1.BackgroundColor3 = thisColor - thisStar.Visible = true - else - thisStar.Visible = false - end - end - end -end - -local normalPositions = {uD2(0,0,0,0),uD2(0.5,0,0,0),uD2(0.5,0,0.5,0),uD2(0,0,0.5,0)} -local boxResizes = {{1,1},{0,1},{0,0},{1,0}} -local boxMajorOffsets = {{0,-1},{1,0},{0,1},{-1,0}} -local diamondOffsets = {0.12,0.2} - -function resizeBoxes(mainBox,bigSize,smallSize) - for i = 1, 4 do - local object = interfaceDiamond["Box" .. tSt(i)] - local size = (i == mainBox and bigSize) or smallSize //Use the big box size if the box is the main one - local offset = 0.5-size - local boxFactors = boxResizes[i] - object.Size = uD2(size,0,size,0) - object.Position = normalPositions[i]+uD2(boxFactors[1]*offset,0,boxFactors[2]*offset,0) - end - local mainOffsets = boxMajorOffsets[mainBox] - interfaceDiamond.Position = uD2(0.359-diamondOffsets[1]*mainOffsets[1],0,0.25-diamondOffsets[2]*mainOffsets[2],0) //Offset the main diamond -end -*/ -////////////// -// The Menu // -////////////// - -const menuIsOpen = false; -const currentPage = 2; // 1 is forms, 2 is equips, 3 is summons, 4 is settings/info -const menuInTransit = false; -/* -local buttonMouseIsOn = "" -local pages = interfaceMenu.Pages -local mainStar = mainInterface.FatStar -local lineR, lineB = interfaceMenu.RightLine, interfaceMenu.BottomLine -local starR, starB = interfaceMenu.StarTR, interfaceMenu.StarBL -local buttonParent = interfaceMenu.NavButtons -local buttons = {buttonParent.Forms,buttonParent.Equips,buttonParent.Summons,buttonParent.Other} -local buttonTags = {"Forms","Equipment","Enemies","Information and Settings"} -local pageFolder = interfaceMenu.Pages -local pages = {nil,pageFolder.Equipment,nil,nil} -local selectName, selectDetails = interfaceMenu.SelectName, interfaceMenu.SelectDetails -local pageName, nameDecoration = interfaceMenu.PageName, interfaceMenu.NameDecoration -local hoverLabel = mainInterface.HoverLabel -hoverLabel.Visible = false -*/ -//////////////////////- -// Page 2: Equipment // -//////////////////////- -/* -local equipModule = require(storage.EquipModule) -local equipDisplay = pages[2].Display -local displaySortType, displaySortDirection = pages[2].SortType, pages[2].SortDirection -local tabWeapons, tabArmor = pages[2].TabWeapons, pages[2].TabArmor -local equipFolder = storage.Equipment -local sEquipName = pages[2].WeaponName // sEquip is shorthand for "selected equip" -local sEquipDescription = pages[2].WeaponDescription -local sEquipType = pages[2].WeaponType -local sEquipTraits = {pages[2].WeaponTrait1,pages[2].WeaponTrait2,pages[2].WeaponTrait3,pages[2].WeaponTrait4} -local sEquipFrame = pages[2].ItemSelected.ViewportFrame -local sEquipCam = new("Camera") -sEquipCam.CameraType = Enum.CameraType.Scriptable -sEquipCam.Parent = sEquipFrame -sEquipCam.CFrame = CFrame.new(0,10,-10) -sEquipFrame.CurrentCamera = sEquipCam - -local spinning = false -local spinAngle = 0 -local spinMagnitude = 0 -local spinY = 0 - -local currentEquipCategory = 1 -local sEquip = 0 - -local equipRarityColors = {rgb(255,255,255),rgb(255,246,124),rgb(255,36,50),rgb(216,58,255),rgb(101,59,255),rgb(64,182,255)} -local equipRarityNames = {"Regular", "Advanced", "Elite", "Extraordinary", "Transcendent", "Exalted"} -local equipTraitNames = {"%attack", "defense", "%health", "%resistance", "speed"} -local equipTraitColors = {"ff0e0e", "7db4f3", "5aee7d", "3155f5", "eeea5b"} - -local currentSortOrder = 1 -local sortReverse = false - -local function flipArray(array) - local n = #array - for i = 1, floor(n/2) do - local a, b = array[i], array[n + 1 - i] - array[i] = b - array[n + 1 - i] = a - end -end - -local function sortRarity(one,two,fallbackSort,fallbackFallback) - local a, b = one[2], two[2] - if a < b then - return false - elseif a > b or not fallbackSort then - return true - else - return fallbackSort(one,two,fallbackFallback) - end -end - - -local function sortClass(one,two,fallbackSort,fallbackFallback) - local a, b = tN(sub(tSt(one[7]),1,1)), tN(sub(tSt(two[7]),1,1)) - if a > b then - return false - elseif a < b or not fallbackSort then - return true - else - return fallbackSort(one,two,fallbackFallback) - end -end - -local function sortAlpabetically(one,two,fallbackSort,fallbackFallback) - local a, b = one[1], two[1] - local aN, bN = len(a), len(b) - local n = (aN <= bN and aN) or bN - for i = 1, n do - local A, B = upper(sub(a,i,i)), upper(sub(b,i,i)) - if A < B then - return true - elseif A > B then - return false - end - end //At this point, the names are entirely identical until the shortest one ends - if aN == bN and fallbackSort then - return fallbackSort(one,two,fallbackFallback) - elseif n == aN then // If a is the shorter one (n), we return it. If the strings are identical, we return a (true) to keep them in their previous order (stable sorting). - return true - else - return false - end -end - -local sortOrders = { - {"Rarity", {sortRarity, sortClass, sortAlpabetically}}, - {"Class", {sortClass, sortRarity, sortAlpabetically}}, - {"Name", {sortAlpabetically, sortRarity, sortClass}} -} - - //////////////////////////////////////////////- -//| leftSorter -> leftEnd | rightSorter -> last |// - //////////////////////////////////////////////- -local function merge(array,first,last,sorter1,sorter2,sorter3) - local leftEnd = floor((first + last)/2) //1 - local leftSorter = first //1 - local rightSorter = leftEnd + 1 // 2 - local sorted = {unpack(array)} - for i = first, last do - if rightSorter > last or (leftSorter <= leftEnd and sorter1(array[leftSorter],array[rightSorter],sorter2,sorter3)) then //Switch last to < to reverse order - sorted[i] = array[leftSorter] - leftSorter += 1 - else - sorted[i] = array[rightSorter] - rightSorter += 1 - end - end - for i = first, last do - array[i] = sorted[i] - //print(sorted[i][1]) - end -end - -function mergeSort(array,first,last,sorter1,sorter2,sorter3) - first = first or 1 - last = last or #array - - if first < last then - local middle = floor((first + last)/2) - mergeSort(array,first,middle,sorter1,sorter2,sorter3) // Sort left (recursively) - mergeSort(array,middle + 1,last,sorter1,sorter2,sorter3) // Sort right (recursively) - - merge(array,first,last,sorter1,sorter2,sorter3) - end -end - -local format = string.format - -function refreshEquips(ownedWeapons) - local equips = {unpack(equipModule[currentEquipCategory])} //Later this will be only the equips you have unlocked - local count = #equips - local rows = ceil(count/4) - // Sort the equips - mergeSort(equips,nil,nil,unpack(sortOrders[currentSortOrder][2])) - if sortReverse then - flipArray(equips) - end - // Adjust the canvas size to make all of the frames perfectly square - local pixelWidth = equipDisplay.Frame.AbsoluteSize.X - local displayY = (pixelWidth/5)*(rows*6 + 1) - local displayAbsY = equipDisplay.AbsoluteSize.Y - equipDisplay.CanvasSize = uD2(0,0,0,displayY) - //Adjust if there are < 4 rows - local practicalCanvas = 1 - local offset - if rows < 4 and displayY <= displayAbsY then - offset = pixelWidth/5/displayAbsY//offset = 0.0467289 - else - offset = 1/(rows*6 + 1) - end - // Adjust the frame sizes to make them take up the right amounts of the canvas - //local offset = practicalCanvas/(rows*6 + 1) - local height = offset*5 - local diff = offset*6 - - for i, v in pairs(equipDisplay:GetChildren()) do - if v.Name ~= "Frame" then - v:Destroy() - end - end - - local frameName = "WeaponFrame" - if currentEquipCategory == 2 then - frameName = "ArmorFrame" - end - - for i, equip in paris(equips) do - local row = ceil(i/4) - 1 - local column = i - row*4 - 1 - - local f = new("Frame") - f.Name = frameName - f.Size = uD2(0.2,0,height,0) - f.Position = uD2(0.04 + column*0.24,0,offset + row*diff,0) - f.BorderSizePixel = 0 - - local vF = new("ViewportFrame") - vF.Name = equip[8] - vF.Size = uD2(1,0,1,0) - vF.BackgroundTransparency = 1 - vF.Position = uD2(0,0,0,0) - local w = equipFolder[equip[1]]:Clone() - w.Parent = vF - local c = new("Camera") - c.CFrame = equip[4] - c.Parent = vF - vF.CurrentCamera = c - vF.Parent = f - - local g = new("UIGradient") - g.Rotation = -45 - g.Transparency = NumberSequence.new(0,1) - g.Parent = f - if equip[2] == 6 then - f.BackgroundColor3 = equipRarityColors[equip[2]] - else - f.BackgroundColor3 = equipRarityColors[equip[2]] - end - - f.Parent = equipDisplay - end -end - -local uv = v3(0,1,0) -function lookAt(start,ende) // Is in multiple scripts, could be consolidated - local fv = (ende-start).Unit - local rv = fv:Cross(uv) - local uv2 = rv:Cross(fv) - return CFrame.fromMatrix(start,rv,uv2) -end - -function handleEquipClick(equipId,equipCategory,keepSpin) - local equip = equipModule[equipCategory][equipId] - local rarityColor = equipRarityColors[equip[2]] - local hexColor = format("%02X%02X%02X",rarityColor.R*255,rarityColor.G*255,rarityColor.B*255) - // Change the information - sEquipName.Text = equip[1] - sEquipType.Text = '' .. equipRarityNames[equip[2]] .. ' ' .. equip[3] - sEquipDescription.Text = equip[5] - local traits = equip[6] - for i = 1, 4 do // Show the traits - local traitId = traits[i*2 - 1] - if traitId then - local amount = traits[i*2] - local traitName = equipTraitNames[traitId] - // Add the sign based on positivity or negativity - amount = (amount >= 0 and "+ " .. tSt(amount)) or "- " .. tSt(amount) - // Add the % if the trait uses % as the measure - if sub(traitName,1,1) == "%" then - amount = amount .. "%" - traitName = sub(traitName,2,len(traitName)) - end - sEquipTraits[i].Text = '' .. amount .. ' ' .. traitName - else - sEquipTraits[i].Text = "" - end - end - // Replace the existing model - local model = sEquipFrame:FindFirstChildOfClass("Model") - if model then - model:Destroy() - end - model = equipFolder:FindFirstChild(equip[1]) - if model then - model:Clone().Parent = sEquipFrame - end - // Adjust spin values - if not keepSpin then - spinAngle = 0 - end - spinY = equip[4].Position.Y - spinMagnitude = v3(equip[4].Position.X,0,equip[4].Position.Z).Magnitude -end - -local mTInfo1 = TweenInfo.new(0.25,Enum.EasingStyle.Quad,Enum.EasingDirection.Out) -local mTInfo2 = TweenInfo.new(0.25,Enum.EasingStyle.Quad,Enum.EasingDirection.In) -function openMenu() - refreshEquips() - handleEquipClick(1,1,true) - menuInTransit = true - - interfaceMenu.BackgroundTransparency = 1 //Set the stage - lineB.Position = uD2(1,0,0.998,0) //Push the stars and lines to the corner - lineB.Size = uD2(0,0,0.005,0) - lineR.Position = uD2(0.998,0,1,0) - lineR.Size = uD2(0.003,0,0,0) - starB.Position = uD2(0.994,0,0.989,0) - starR.Position = uD2(0.994,0,0.989,0) - for i, v in pairs(buttons) do - v.BackgroundTransparency = 1 - v.NavButton.ImageTransparency = 1 - end - selectName.TextTransparency = 1 - selectName.TextStrokeTransparency = 1 - selectDetails.TextTransparency = 1 - selectDetails.TextStrokeTransparency = 1 - - pageName.TextTransparency = 1 - pageName.TextStrokeTransparency = 1 - nameDecoration.ImageTransparency = 1 - nameDecoration.Frame.BackgroundTransparency = 1 - nameDecoration.Frame.Image.ImageTransparency = 1 - - local children = pages[currentPage]:GetChildren() - for i, v in pairs(children) do - v.Visible = false - end - - tween:Create(mainStar,mTInfo1,{Position = uD2(0.854,0,0.803,0), Size = uD2(0.042,0,0.075,0)}):Play() - wait(0.25) - - interfaceMenu.Visible = true //The curtain rises - - tween:Create(lineB,mTInfo1,{Position = uD2(0,0,0.998,0), Size = uD2(1,0,0.005,0)}):Play() - tween:Create(lineR,mTInfo1,{Position = uD2(0.998,0,0,0), Size = uD2(0.003,0,1,0)}):Play() - tween:Create(starB,mTInfo1,{Position = uD2(-0.006,0,0.989,0)}):Play() - tween:Create(starR,mTInfo1,{Position = uD2(0.994,0,-0.01,0)}):Play() - wait(0.25) - tween:Create(interfaceMenu,mTInfo1,{BackgroundTransparency = 0.5}):Play() - for i, v in pairs(buttons) do - tween:Create(v,mTInfo1,{BackgroundTransparency = 0.5}):Play() - tween:Create(v.NavButton,mTInfo1,{ImageTransparency = 0}):Play() - end - tween:Create(pageName,mTInfo1,{TextTransparency = 0, TextStrokeTransparency = 0.85}):Play() - tween:Create(nameDecoration,mTInfo1,{ImageTransparency = 0.5}):Play() - tween:Create(nameDecoration.Frame,mTInfo1,{BackgroundTransparency = 0.5}):Play() - tween:Create(nameDecoration.Frame.Image,mTInfo1,{ImageTransparency = 0.5}):Play() - for i, v in pairs(children) do //Show the page elements - v.Visible = true - wait() - end - menuIsOpen = true - menuInTransit = false - while menuIsOpen do // MAJOR SPOT FOR CLEANUP - if currentPage == 2 then - local x, y = sin(rad(spinAngle)), cos(rad(spinAngle)) - sEquipCam.CFrame = lookAt(v3(x*spinMagnitude,spinY,y*spinMagnitude),v3(0,0,0)) - spinAngle += 2 - wait() - end - local mPos = uIS:GetMouseLocation() - Vector2.new(0,26) - //print(mPos.Y) - local gObjects = plrGui:GetGuiObjectsAtPosition(mPos.X,mPos.Y) - local thisButton = "" - local sawWeaponFrame = false - for i, v in pairs(gObjects) do - //print(v.Name) - //print(v.Name == "NavButton") - //hoverLabel.Visible = false - if v.Name == "NavButton" then - thisButton = v.Parent - print(thisButton.Name) - elseif v.Name == "WeaponFrame" then - hoverLabel.Text = equipModule[currentEquipCategory][tN(v:FindFirstChildOfClass("ViewportFrame").Name)][1] - hoverLabel.Position = uD2(0,mPos.X+5,0,mPos.Y) - hoverLabel.Visible = true - sawWeaponFrame = true - end - end - if not sawWeaponFrame then - hoverLabel.Visible = false - end - if thisButton ~= buttonMouseIsOn then - // Shrink formerly emphasized button - if buttonMouseIsOn ~= "" then - local size = buttonMouseIsOn.Size - local position = buttonMouseIsOn.Position - local sX, sY = size.X.Scale, size.Y.Scale - //local pX, pY = position.X.Scale+sX*0.1, position.Y.Scale+sY*0.1 - tween:Create(buttonMouseIsOn,mTInfo1,{Position = uD2(-0.25 + tFind(buttons,buttonMouseIsOn)*0.25,0,0,0), Size = uD2(0.21,0,1,0)}):Play() - else - selectName.Text = buttonTags[tFind(buttons,thisButton)] - tween:Create(selectName,mTInfo1,{TextTransparency = 0, TextStrokeTransparency = 0.85}):Play() - end - buttonMouseIsOn = thisButton - // Enlarge newly emphasized button - if buttonMouseIsOn ~= "" then - local size = buttonMouseIsOn.Size - local position = buttonMouseIsOn.Position - local sX, sY = size.X.Scale, size.Y.Scale - //local pX, pY = position.X.Scale-sX*0.125, position.Y.Scale-sY*0.125 - tween:Create(buttonMouseIsOn,mTInfo1,{Position = uD2(-0.271 + tFind(buttons,buttonMouseIsOn)*0.25,0,0,0), Size = uD2(0.252,0,1.2,0)}):Play() - else - tween:Create(selectName,mTInfo1,{TextTransparency = 1, TextStrokeTransparency = 1}):Play() - end - end - wait() - end -end - -function closeMenu() - menuInTransit = true - tween:Create(interfaceMenu,mTInfo2,{BackgroundTransparency = 1}):Play() - for i, v in pairs(buttons) do - tween:Create(v,mTInfo2,{BackgroundTransparency = 1}):Play() - tween:Create(v.NavButton,mTInfo2,{ImageTransparency = 1}):Play() - end - tween:Create(pageName,mTInfo2,{TextTransparency = 1, TextStrokeTransparency = 1}):Play() - tween:Create(nameDecoration,mTInfo2,{ImageTransparency = 1}):Play() - tween:Create(nameDecoration.Frame,mTInfo2,{BackgroundTransparency = 1}):Play() - tween:Create(nameDecoration.Frame.Image,mTInfo2,{ImageTransparency = 1}):Play() - local children = pages[currentPage]:GetChildren() //Hide the page elements - local when = clock() + 0.25 - for i, v in pairs(children) do - v.Visible = false - wait() - end - wait(when-clock()) - tween:Create(lineB,mTInfo2,{Position = uD2(1,0,0.998,0), Size = uD2(0,0,0.005,0)}):Play() - tween:Create(lineR,mTInfo2,{Position = uD2(0.998,0,1,0), Size = uD2(0.003,0,0,0)}):Play() - tween:Create(starB,mTInfo2,{Position = uD2(0.994,0,0.989,0)}):Play() - tween:Create(starR,mTInfo2,{Position = uD2(0.994,0,0.989,0)}):Play() - wait(0.25) - tween:Create(mainStar,mTInfo2,{Position = uD2(0,106,0,7), Size = uD2(0,26,0,26)}):Play() - wait(0.25) - interfaceMenu.Visible = false //The curtain falls - menuIsOpen = false - menuInTransit = false -end - -function changeSortType() - currentSortOrder = (currentSortOrder == 3 and 1) or currentSortOrder + 1 - displaySortType.Text = "Sort by: " .. sortOrders[currentSortOrder][1] - refreshEquips() -end - -function changeSortDirection() - if sortReverse then - sortReverse = false - displaySortDirection.Rotation = 0 - else - sortReverse = true - displaySortDirection.Rotation = 180 - end - refreshEquips() -end - -function changeEquipCategory(armor) - if armor and currentEquipCategory ~= 2 then - currentEquipCategory = 2 - tabArmor.BackgroundTransparency = 0.7 - tabArmor.TextColor3 = rgb(255,255,255) - tabWeapons.BackgroundTransparency = 0.6 - tabWeapons.TextColor3 = rgb(171,171,171) - refreshEquips() - elseif not armor and currentEquipCategory ~= 1 then - currentEquipCategory = 1 - tabWeapons.BackgroundTransparency = 0.7 - tabWeapons.TextColor3 = rgb(255,255,255) - tabArmor.BackgroundTransparency = 0.6 - tabArmor.TextColor3 = rgb(171,171,171) - refreshEquips() - end -end - -function handleMenuClick() - local mPos = uIS:GetMouseLocation() - Vector2.new(0,26) - local gObjects = plrGui:GetGuiObjectsAtPosition(mPos.X,mPos.Y) - for i, v in pairs(gObjects) do - if v.Name == "WeaponFrame" then - handleEquipClick(tN(v:FindFirstChildOfClass("ViewportFrame").Name),1) - elseif v.Name == "ArmorFrame" then - handleEquipClick(tN(v:FindFirstChildOfClass("ViewportFrame").Name),2) - elseif v == displaySortType then - changeSortType() - elseif v == displaySortDirection then - changeSortDirection() - elseif v == tabWeapons then - changeEquipCategory() - elseif v == tabArmor then - changeEquipCategory(true) - end - end -end -*/ - -//Camera stuff -//camera.CameraType = camScriptable -//uIS.MouseBehavior = uISLockedCenter - -//Dialog stuff that's here from the SG:RPG, planning to rewrite it ~~if~~ WHEN we add NPCs to the map - -/* -diaCache = {} -function createDialog(f,tab) - if tab.Fire then - DialogEvent:FireServer(tab.Fire) - end - if tab.c ~= 0 then - f.SF.CanvasSize = UDim2.new(0,0,0,80+(tab.c-1)*90) - for i = 1, tab.c do - local c = f.SF["0"]:Clone() - c.TB.Text = tab[i].Choice - c.Visible = true - c.Name = i - c.Position = UDim2.new(0,0,0,90*(i-1)) - c.Parent = f.SF - //Make the box show the cool gradient when the mouse enters it. - diaCache[#diaCache+1] = c.TB.MouseEnter:Connect(function() - for i = 1, 8 do - c.BackgroundTransparency = c.BackgroundTransparency/2 - runService.Heartbeat:Wait() - end - c.BackgroundTransparency = 0 - end) - //Make the box fade out when the mouse leaves. - diaCache[#diaCache+1] = c.TB.MouseLeave:Connect(function() - for i = 1, 8 do - c.BackgroundTransparency = (c.BackgroundTransparency+1)/2 - runService.Heartbeat:Wait() - end - c.BackgroundTransparency = 1 - end) - //Handle button click transitions. - diaCache[#diaCache+1] = c.TB.Activated:Connect(function() - - f.Words.Text = tab[i].Resp - if string.len(tab[i].Resp) > 100 then - f.Words.TextScaled = true - else - f.Words.TextScaled = false - end - - for i, v in pairs(f.SF:GetChildren()) do - if v.Name ~= "0" then - v:Destroy() - end - end - for i, v in pairs(diaCache) do - v:Disconnect() - end - diaCache = {} - createDialog(f,tab[i]) - end) - end - else - wait(1+string.len(f.Words.Text)*0.03) - for i = 1, 20 do - pcall(function() - f.Position = f.Position:Lerp(UDim2.new(0,100,1,0),0.3) - runService.Heartbeat:Wait() - end) - end - pcall(function() f.Parent:Destroy() end) - isDialog = false - end -end - -function Dialog(strin,model) - isDialog = true - local g = script.Dialog:Clone() - g.Parent = Plr.PlayerGui - local f = g.Frame - - f.NPC.Text = strin.NPC - f.Words.Text = strin.FT - if string.len(strin.FT) > 100 then - f.Words.TextScaled = true - end - - createDialog(f,strin) - - local c = model:Clone() - c.Parent = f.VF - local cam = Instance.new("Camera",f.VF) - f.VF.CurrentCamera = cam - cam.CFrame = c.ClickBox.CFrame*strin.C - - g.Frame.Position = UDim2.new(0,100,1,0) - g.Enabled = true - for i = 1, 20 do - f.Position =f.Position:Lerp(UDim2.new(0,100,0.6,0),0.3) - runService.Heartbeat:Wait() - end - g.Frame.Position = UDim2.new(0,100,0.6,0) -end -*/ -/* -local shouldStartPBLLoop = true -local connectionTable = {} - -local ngh = script.sgAnimate -function giveScript(plr,mode) - if not script:FindFirstChild(plr.Name) then - local c = ngh:Clone() - c.Name = plr.Name - c.Parent = script - c.Disabled = false - if mode then - local uId = plr.UserId - local thisOne = rand(1,99999) - while connectionTable[thisOne] do - thisOne = rand(1,99999) - end - connectionTable[thisOne] = ModeEvent.Event:Connect(function(typ,aUId) - if typ == "Request" and aUId == uId then - connectionTable[thisOne]:Disconnect() - ModeEvent:Fire("Reply",uId,mode) - end - end) - end - end -end -*/ -//const rayCast = ws.Raycast; -const hitParams = new RaycastParams(); -hitParams.FilterDescendantsInstances = [efFolder]; -hitParams.FilterType = Enum.RaycastFilterType.Blacklist; -function mouse() { - const chr = Plr.Character; - if (chr) { - hitParams.FilterDescendantsInstances = [efFolder, chr]; - } else { - hitParams.FilterDescendantsInstances = [efFolder]; - } - const mouseLocation = uIS.GetMouseLocation(); - if (Cam) { - const unitRay = Cam.ViewportPointToRay(mouseLocation.X, mouseLocation.Y); - const cast = ws.Raycast(unitRay.Origin, unitRay.Direction.mul(1000), hitParams); - if (cast) { - return [cast.Position, cast.Normal, cast.Instance]; - } else { - return [unitRay.Origin.add(unitRay.Direction.mul(1000)), new v3(0, 0, 0), undefined]; - } - } else { - print("Camera not found!"); +function handleOutput(messageType: unknown, messageContent: unknown) { + if (messageType === "init") { + openMainMenu(PLAYERGUI); + } else if (messageType === "enterGame") { + closeGui(PLAYERGUI, "MainMenu"); } } -/* -Output.OnClientEvent.Connect((event: string,arg1,arg2,arg3,arg4,arg5,arg6) => { - if (event == "GiveScript") { - //giveScript(arg1,arg2) - } else if (event == "ModeChange" && arg1 == Plr.UserId) { - Music.SoundId = rid + arg2.music[1] - //changeGuiMode(arg2.name,arg2.main,arg2.sec,arg2.font,arg2.music[2],arg2.music[3]) - if arg2.sendpbl and shouldStartPBLLoop then - shouldStartPBLLoop = false - SendPlaybackLoudness = true - while SendPlaybackLoudness do - wait(0.1) - Input:FireServer("PBL",Music.PlaybackLoudness) - end - shouldStartPBLLoop = true //This makes it so that multiple loops are not created if a player switches from one PBL mode to another - else - SendPlaybackLoudness = false - end - //} else if event == "Shake" then - //CamShake(arg1,arg2) - //} else if (event == "Notify") { - //Notify(arg1) - //} else if (event == "Cooldown") { - //handleCooldown(arg1,arg2,arg3,arg4,arg5,arg6) - //} else if (event == "MovesExist") { - //movesExist = arg1 - //updateCooldownGui(arg2,arg3,arg4,arg5) - //} else if (event == "SideColors") { - //updateDiamond(arg1) - //} else if event == "EnemyInit" then - //warn("Got enemy init message: " .. arg1) - //enemyModules[arg1]["Init"](arg2) - //} else if event == "EnemyClientEvent" then - //print("Got enemy client event of " .. arg1 .. " " .. arg2) - //enemyModules[arg1][arg2](arg3,arg4,arg5) - //} else if event == "MouseRequest" or (event == "ClientEvent" and arg2 == "BeamUpdate" and arg1 == Plr.UserId) then - //print("Client got mouserequest, updating") - //Input:FireServer("Input",nil,mouse()) - } -}); -*/ - -//Inputs -function handleModeInput(key: string) { - storedModeKey += key; - if (storedModeKey.size() >= 2) { - //print("Firing server with " .. storedModeKey) - input.FireServer("Input", storedModeKey, mouse()); - storedModeKey = ""; - //interfaceDiamond.Visible = false; - } else { - //interfaceDiamond.Visible = true - /* - for (let i = 1; i <= 5; i++) { - local thisBox = interfaceDiamond["Box" .. tSt(i)].Inside - if (i == modeKeys.findIndex(,)) { - thisBox.Key.TextTransparency = 0 - } else { - thisBox.Key.TextTransparency = 0.75 - } - } - resizeBoxes(tFind(modeKeys,key),0.6,0.4) - */ - } -} - -function handleInput(k: InputObject, object: boolean) { - const keyCode = k.KeyCode; - if (k.UserInputType === Enum.UserInputType.MouseButton1 && !menuInTransit) { - if (menuIsOpen) { - //handleMenuClick() - } else if (!object) { - input.FireServer("Input", "click", mouse()); - } - } else if (!object && keyCode && !menuInTransit) { - const key = sub(tS(keyCode), 14); - //print("Got key " .. keyCode) - if (key === "G") { - if (menuIsOpen) { - //closeMenu() - } else { - //openMenu() - } - } else if (!menuIsOpen && tFind(modeKeys, key)) { - //print("Got mode key") - handleModeInput(key); - } else if (!menuIsOpen && (tFind(abilityKeys, key) || tFind(otherModeKeys, key))) { - input.FireServer("Input", key, mouse()); - } - } -} - -uIS.InputBegan.Connect(handleInput); - -wait(); -/* -for i, v in pairs (plrs:GetChildren()) do - giveScript(v.Name) -end - -plrs.PlayerAdded:Connect(function(plr) - giveScript(plr.Name) -end) -*/ - -//hey -input.FireServer("Message", Plr.Name + "'s local script has loaded."); -print(makeHello("The Reborn Client")); - -//More legacy stuff from the SG:RPG -/* -Plr.CharacterAdded:Connect(function() //Handle the health bar (it has to get the humanoid every time the character respawns - isDialog = false - wait(0.5) - hum = Plr.Character.Humanoid - G.Box.HP.L.Text = tostring(hum.MaxHealth) .. "/" .. tostring(hum.MaxHealth) - pcall(function() conch:Disconnect() end) - conch = hum.HealthChanged:Connect(function() - print(tostring(math.floor(hum.Health))) - G.Box.HP.L.Text = tostring(math.floor(hum.Health)) .. "/" .. tostring(hum.MaxHealth) - repeat - G.Box.HP.B.Size = G.Box.HP.B.Size:Lerp(UDim2.new(hum.Health/hum.MaxHealth,0,0,13),0.1) - runService.Heartbeat:Wait() - until G.Box.HP.B.Size == UDim2.new(hum.Health/hum.MaxHealth,0,0,13) or not hum - end) -end) - -repeat //-Get the gosh darn humanoid already - pcall(function() hum = Plr.Character.Humanoid end) - wait() -until hum - -if not conch then //The script's not fast enough to detect the initial spawn so I'm just leaving this here - conch = hum.HealthChanged:Connect(function() - print(tostring(math.floor(hum.Health))) - G.Box.HP.L.Text = tostring(math.floor(hum.Health)) .. "/" .. tostring(hum.MaxHealth) - repeat - G.Box.HP.B.Size = G.Box.HP.B.Size:Lerp(UDim2.new(hum.Health/hum.MaxHealth,0,0,13),0.1) - runService.Heartbeat:Wait() - until G.Box.HP.B.Size == UDim2.new(hum.Health/hum.MaxHealth,0,0,13) or not hum - end) -end -*/ +// Action phase +UserInputService.InputBegan.Connect(handleInput); +bindToOutput(handleOutput); diff --git a/src/client/main.client.ts b/src/client/main.client.ts deleted file mode 100644 index 3110bed..0000000 --- a/src/client/main.client.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { makeHello } from "shared/module"; -const tween = game.GetService("TweenService"); -const v3 = Vector3; -print("hello again"); -print(makeHello("main.client.ts")); diff --git a/src/server/ServerMessenger.ts b/src/server/ServerMessenger.ts new file mode 100644 index 0000000..99d8bec --- /dev/null +++ b/src/server/ServerMessenger.ts @@ -0,0 +1,13 @@ +import { Input, Output } from "shared/Remotes"; +export function bindToInput(functionToBind: Callback) { + assert(Input?.IsA("RemoteEvent"), 'Remote event "Input" is of incorrect class or nil'); + Input.OnServerEvent.Connect(functionToBind); +} +export function messageClient(client: Player, messageType: serverMessageType, messageContent?: string) { + assert(Output?.IsA("RemoteEvent"), 'Remote event "Output" is of incorrect class or nil'); + Output.FireClient(client, messageType, messageContent); +} +export function messageAllClients(messageType: serverMessageType, messageContent?: string) { + assert(Output?.IsA("RemoteEvent"), 'Remote event "Output" is of incorrect class or nil'); + Output.FireAllClients(messageType, messageContent); +} diff --git a/src/server/main.server.ts b/src/server/main.server.ts index 3fa4418..2b43a92 100644 --- a/src/server/main.server.ts +++ b/src/server/main.server.ts @@ -1,776 +1,37 @@ -//This is the core of reality. +// "main": This is the core of reality. It serves as the highest-level abstraction. +// + Prevent this from coupling with the entity manager, if possible +const Players = game.GetService("Players"); +import { makeEntity } from "shared/EntityManager"; +import { initPlayer, deinitPlayer, loadInPlayer, teleportPlayer } from "shared/PlayerManager"; +import { bindToInput, messageClient, messageAllClients } from "./ServerMessenger"; +const playerStorage: (playerStorageEntry | undefined)[] = []; +const entityStorage: entity[] = []; -// BASIC SETUP - -// Universal variables -import { makeHello } from "shared/module"; -const plrs = game.GetService("Players"); -const ws = game.GetService("Workspace"); -const tween = game.GetService("TweenService"); -const runService = game.GetService("RunService"); -const storage = game.GetService("ReplicatedStorage"); -// Aliases -const uD2 = UDim2; -const v3 = Vector3; -const cF = CFrame; -const inst = Instance; -const tN = tonumber; -const tS = tostring; -const wait = task.wait; -const clock = os.clock; -const floor = math.floor; -const ceil = math.ceil; -const abs = math.abs; -const cos = math.cos; -const sin = math.sin; -const rad = math.rad; -const rand = math.random; -const clamp = math.clamp; -const sub = string.sub; -// Constructors -const rgb = Color3.fromRGB; -const hsv = Color3.fromHSV; -const xyz = CFrame.fromEulerAnglesXYZ; -const wrap = coroutine.wrap; - -const zF = new cF(0, 0, 0); -//End of universal variables - -//Other variables -const neon = Enum.Material.Neon - -const remotes = storage.Remotes; -const input = remotes.Input; -const output = remotes.Output; - -function tB(data: any) { // to boolean - if (data) {return true;} else {return false;} +function addPlayer(player: Player) { + playerStorage[player.UserId] = initPlayer(player); + messageClient(player, "init", "idk"); } - -function floorToBase(number: number, base: number) { // Is in multiple scripts, could be consolidated - return floor(number * base) / base; +function removePlayer(player: Player) { + playerStorage[player.UserId] = deinitPlayer(playerStorage[player.UserId], player); } - -function AFrame(x: number,y: number,z: number,rx: number,ry: number,rz: number) { // Is in multiple scripts, could be consolidated - return new cF(x,y,z).mul(xyz(rad(rx),rad(ry),rad(rz))); -} - -const uv = new v3(0,1,0); -function lookAt(start: Vector3,ende: Vector3) { // Is in multiple scripts, could be consolidated (This is the master version) - let fv = (ende.sub(start)).Unit; - let rv = fv.Cross(uv); - let uv2 = rv.Cross(fv); - return cF.fromMatrix(start,rv,uv2); -} - -function weld(name: string, p0: BasePart, p1: BasePart | undefined, cZero: CFrame, cOne: CFrame) { - let we = new inst("Motor6D"); // plot twist, it was a motor6D all along, now go home - we.Name = name; - we.Part0 = p0; - we.Part1 = p1; - we.C0 = cZero; - we.C1 = cOne; - we.Parent = p0; - return(we); -} - -function newPart(parent: Instance,color: Color3,material: Enum.Material,transparency: number,anchored: boolean,canCollide: boolean,size: Vector3,shape?: Enum.PartType,hasmass?: boolean,hasshadow?: boolean) { - let part = new inst("Part"); - part.Color = color; - part.Material = material; - part.Transparency = transparency; - part.Anchored = anchored; - part.CanCollide = canCollide; - part.Size = size; - part.Shape = shape || Enum.PartType.Block; - part.Massless = hasmass || false; - part.CastShadow = hasshadow || false; - part.Parent = parent; - return(part); -} - -function emitter(tab: emitterTable) { // Is in multiple scripts, could be consolidated (This is the master version) - let e = new inst("ParticleEmitter"); - e.Color = tab.color; - e.LightEmission = tab.lightE || 1; - e.LightInfluence = tab.lightI || 0; - e.Size = tab.size; - e.Texture = tab.texture; - e.Transparency = tab.transparency; - e.Drag = tab.drag || 0; - e.LockedToPart = tab.locked || false; - e.Lifetime = tab.lifetime; - e.Rate = tab.rate; - e.Rotation = tab.rotation || new NumberRange(-180, 180); - e.RotSpeed = tab.rotSpeed || new NumberRange(-100, 100); - e.Speed = tab.speed; - e.SpreadAngle = tab.spread || new Vector2(180,180); - e.Parent = tab.parent; - return e; -} - -function getDist(one: Vector3, two: Vector3) { // Is in multiple scripts, could be consolidated - return one.add(two).Magnitude; -} - -//local wingFolder = game:GetService("ServerStorage").Wings - -//const modules = game.GetService("ServerScriptService").Server.Modules -//const modes = require(Modules.Modes) -//const abilties = require(Modules.Abilities) -/* - _________________ - \ / - //| Inputs |// - /_________________\ - - */// -//const eK = Enum.KeyCode -const modeKeys = ["Q","E","R","F"];//{ek.Q,ek.E,ek.R,ek.T,ek.Y,ek.U,ek.F,ek.G,ek.H,ek.J,ek.K,ek.L} -const subModeKeys = ["B","N","M"];//{ek.B,ek.N,ek.M} -const sideKeys = ["1","2","3","4"];//{ek.One,ek.Two,ek.Three,ek.Four} -const abilityKeys = ["Z","X","C","V"]; -//coolDown.Z, coolDown.X, coolDown.C, coolDown.V = {}, {}, {}, {} -//local abilityKeysString = {"Z","X","C","V"} - -//local pvp = {} //Stores all of the PvP states for every player. -//var mouseData = {} //Mouse data (mouseData[player id] = {pos, norm, targ}) -/* -function localModeTable(modeTable) { - local wc = modeTable.wingc - return { - name = modeTable.name, font = modeTable.tag[4], - main = modeTable.main, sec = modeTable.sec, - wanim = modeTable.wanim, manim = modeTable.manim, - height = modeTable.height, - music = modeTable.music, - lefts = wc.left, rights = wc.right, - sendpbl = modeTable.sendpbl or nil, coloreffect = modeTable.coloreffect or nil, coloraffected = modeTable.coloraffected or nil, parttoaffect = modeTable.parttoaffect or nil - } -} - -function sideColorTable(set,sideNumber) - local colorTable = {Q = {},E = {},R = {},F = {}} - for i, v in pairs(Modes[set][sideNumber]) do - if len(i) == 2 then - print("Set " .. tSt(sub(i,1,1)) .. tSt(sub(i,2))) - colorTable[sub(i,1,1)][sub(i,2)] = v.main - end - end - return colorTable -end -*/ -interface movesTable { - Z: boolean; - X: boolean; - C: boolean; - V: boolean; -} - -interface pEntry { - set: number; - mode: string; - - canDoAction: boolean; - hasMoves: movesTable; - clickAttack: [number, number]; - coolDown: {[key: string]: number}; - pvp: boolean; - mousePos: Vector3; - - parts: [undefined?, Model, Humanoid, Part, Part, Part, Part, Part, Part, Part]; - wings: [undefined?, Folder, Part[][], Part]; - bGui: [undefined?, BillboardGui, TextLabel, TextLabel, GuiObject[], GuiObject[]] -} - -var p: {[key: number]: pEntry} = []; // The god table. Legend says it holds the secrets of the universe. - -function initPlr(plr: Player) { - let uId = plr.UserId; - // PLAYER VARIABLES - let chrInfo = setUp(uId) - let entry: pEntry = { // This should load information from datastores in the future - set = 1, - mode = "Q", - - canDoAction = true, - hasMoves = {Z = false, X = false, C = false, V = false}, - clickAttack = [1,0], - coolDown = {}, - pvp = false, - mousePos = new v3 (0,0,0), - - parts = chrInfo[1], - wings = chrInfo[2], - }; - - //guh.StoredModeInfo = {} - //PlaybackLoudness information - //guh.PBL = Instance.new("NumberValue",plr) - //guh.PBL.Name = "PBL" - //Stored tweens (like for mode changes) - //guh.Tweens = {} - //mouseData[uId] = {v3(0,0,0),v3(0,0,0),1} - //pvp[uId] = 0 //Zero is none, one is friends, two is all. - //Finish setup - p[uId] = entry; - //setUp(plr); // TEMPORARY - //plr.CharacterAdded.Connect(setUp) - - /* WILL BE REIMPLEMENTED WITH THE SGANIMATE REWORK - output.FireAllClients("GiveScript",plr) //Tell all clients to animate this player - - for i, otherPlayer in pairs(plrs:GetChildren()) do //Tell this client to animate all of the other players - if otherPlayer ~= plr then - print("Giving script of " .. otherPlayer.Name .. " to " .. plr.Name) - Output:FireClient(plr,"GiveScript",otherPlayer,p[otherPlayer.UserId].StoredModeInfo) - end - end - */ - print(plr.Name + " has initiallized successfully."); -} - -function deinitPlr(plr: Player) { - let uId = plr.UserId; - // backup to datastore or something goes here - delete p[uId]; - print(plr.Name + "has deinitiallized successfully."); -} - -plrs.PlayerAdded.Connect(initPlr); -plrs.PlayerRemoving.Connect(deinitPlr); - -// Initialization Complete. // - -/* May not be readded since PvP may be available on the arena if no PvE event is ongoing -function playerZone(plr) //Locate where the player is on the map - local chr = plr.Character - if chr then - local root = chr:FindFirstChild("HumanoidRootPart") - if root and root.Position.Y < -2056 then - return "Outside" - elseif root and root.Position.Y <= -1024 then - return "Plateau" - end - end - return "Spawn" -end -*/ -//////////////////////////////////- - -// Character Variables // - -//////////////////////////////////- -// Functions used when setting up the character -// Get joints should be a different function (on the client) -function getParts(uId: number) { // You need to make sure and check how the instances compile (1 instead of 0?) - let plr = plrs.GetPlayerByUserId(uId) - if (plr) { - // Get the character - let chr = plr.Character; - while (!chr) { - chr = plr.Character; - wait(); - } - // Humanoid - let hum = chr.WaitForChild("Humanoid") as Humanoid; - // Body Parts - let root = chr.WaitForChild("Root") as Part; - let torso = chr.WaitForChild("Torso") as Part; - let head = chr.WaitForChild("Head") as Part; - let lArm = chr.WaitForChild("Left Arm") as Part; - let rArm = chr.WaitForChild("Right Arm") as Part; - let lLeg = chr.WaitForChild("Left Leg") as Part; - let rLeg = chr.WaitForChild("Right Leg") as Part; - let newParts: [undefined?, Model, Humanoid, Part, Part, Part, Part, Part, Part, Part] = [undefined, chr, hum, root, torso, head, lArm, rArm, lLeg, rLeg]; - return newParts; - } -} - -function makeWings(parts: [undefined?, Model, Humanoid, Part, Part, Part, Part, Part, Part, Part]) { // Make the core, true wings, etc. - // Decor model - Parent of all of the visible wings + the core - let adds = new inst("Folder"); - adds.Name = "Decor"; - // Wing folder - I seriously don't know how I (or birdmaid, god bless their soul) lived without table wings before - let iWingsFolder = new inst("Folder"); - iWingsFolder.Name = "Wings"; - let iWings: Part[][] = []; // The "true" wings (tiny invisible parts), which are animated by the client - // Central part + its weld - let cWPart = newPart(iWingsFolder, rgb(0,0,0), neon, 1, false, false, new v3(0.1,0.1,0.1)); // The center of the wings (and the core). - cWPart.Name = "CWPart"; - weld("CoreWeld", cWPart, undefined, zF, zF); - let mainWeld = weld("MainWeld", parts[4] as Part, cWPart, new cF(0,1.5,1.05), new cF(0,0,0)); - // Make the wing parts, put them in the table, and weld them - for (let j = 1; j <= 2; j += 1) { - iWings[j] = [] - for (let i = 1; i <= 6; i += 1) { - iWings[j][i] = newPart(iWingsFolder, rgb(0,0,0), neon, 1, false, false, new v3(0.1,0.1,0.1)); - weld((tS(j) + "_" + tS(i)),cWPart,iWings[j][i],zF,zF); - weld("WingWeld", iWings[j][i], undefined, zF, zF); - } - } - // Finalize - let chr = parts[1] - adds.Parent = chr; - iWingsFolder.Parent = chr; - // Assign to table and return - let wings: [undefined?, Folder, Part[][], Part] = [undefined, adds, iWings, cWPart]; - return wings; -} - -let wingFolder = storage.WaitForChild("WingFolder") as Folder // Temporary -function makeDecor(wings: [undefined?, Folder, Part[][], Part], wing: string,core: string) { // Make the actual visible wings (This will take arguments pulled from a table about the set's intended wings later on, and wing alternates will be a thing) - wings[1].ClearAllChildren() - // Make the visible wings - let wingModel = wingFolder.FindFirstChild(wing) - assert(wingModel,"Server: Wing " + wing + " not found!") - for (let j = 1; j <= 2; j++) { - let theseIWings = wings[2][j]; - for (let i = 1; i <= 6; i++) { - let thisWing = wingModel.Clone() as wingModelType; - thisWing.Name = tS(j) + "_" + tS(i); - thisWing.M.Size = thisWing.M.Size.mul(0.5 + ceil(i/3)/2); // Wings 4 to 6 get enlarged (I have decided that this objectively just looks cooler) - thisWing.Parent = wings[1]; - // Weld the visible wings to the invisible wings - let wingWeld = theseIWings[i].FindFirstChild("WingWeld") as Motor6D | undefined; - assert(wingWeld, "Server: WingWeld of wing " + thisWing.Name + " not found!"); - wingWeld.Part1 = thisWing.M; +// Handling input is a complicated process that requires passing a large variety of data to a large variety of places, so it's staying here for now +function handleInput(player: Player, messageType: unknown, messageContent: unknown) { + if (messageType === "EnterGame") { + try { + entityStorage[player.UserId] = loadInPlayer(player); + messageClient(player, "enterGame"); + } catch (thrownError) { + const errorMessage = 'Error when creating entity of "' + player.DisplayName + '": ' + thrownError; + warn(errorMessage); + messageClient(player, "promptError", errorMessage); + } + } else if (messageType === "placeholder") { + const entity = entityStorage[player.UserId]; + if (entity && entity.puppet) { } } - // Make the visible core - let coreModel = wingFolder.FindFirstChild(core) - assert(coreModel,"Server: Core " + core + " not found!") - let thisCore = coreModel.Clone() as wingModelType - thisCore.Name = "Core" - thisCore.Parent = wings[1] - // Weld the visible core the cWPart - let coreWeld = wings[3].FindFirstChild("CoreWeld") as Motor6D | undefined; - assert(coreWeld, "Server: CoreWeld of core not found!"); - coreWeld.Part1 = thisCore.M; } - -function makeBGui() { - let gui = new inst("BillboardGui");//game.ServerStorage.StarG:Clone() - //let BGui: [undefined?, BillboardGui, TextLabel, TextLabel, GuiObject[], GuiObject[]] = []; - //gui.Parent = p[uId].Parts[1] - //gui.Adornee = p[uId].Parts[4] - //gui; - let title = new inst("TextLabel");//gui.Title - let extra = new inst("TextLabel");//gui.Extra - let primaries: GuiObject[] = [] //Parts of the gui to be recolored to the primary color - let secondaries: GuiObject[] = [] //Parts of the gui to be recolored to the secondary color - for (let guiPiece of gui.GetDescendants()) { //Sort the rest of the GUI into these tables - if (guiPiece.IsA("GuiObject")) { - const startLetter = sub(guiPiece.Name,1,1) - if (startLetter == "p") { - primaries.push(guiPiece) - } else if (startLetter == "s") { - secondaries.push(guiPiece) - } - } - } - let bGui: [undefined, BillboardGui, TextLabel, TextLabel, GuiObject[], GuiObject[]] = [undefined, gui,title,extra,primaries,secondaries]; -} - -function setUp(uId: number) { - /* - Chr = plr.Character - repeat - pcall(getParts) - wait() - until Torso and RootJoint - Hum = Chr.Humanoid - Hum.MaxHealth = 200 - Hum.Health = 200 - - if (Chr) { - Plr = plrFromChr(plrs,Chr) - } else { - do { - Chr = Plr.Character - wait() - } - while (!Chr); - } - let uId = Plr.UserId - getParts(Plr,Chr) - MakeDecor(uId) - GrabWings(uId,"StarWing","Ring",unpack(p[uId].Decor)) - GiveGui(uId) - wait() - StandardTransform(Plr,1,"QQ") - wait(1) - StandardTransform(Plr,1,"QQ",nil,true) - Output:FireClient(Plr,"SideColors",sideColorTable("Star",1)) - */ - let parts = getParts(uId); - assert(parts,"Server: Player by UserId of " + tS(uId) + " not found, giving up...")//if (!parts) { warn("Server: Player by UserId of " + tS(uId) + " not found, returning..."); return; } // One last chance to turn back - let wings = makeWings(parts); // From here, we are nihilists (if the thread errors, it errors) - makeDecor(wings, "SpectrumWing", "SpectrumCore") - return [undefined, parts, wings] as [undefined?, [undefined?, Model, Humanoid, Part, Part, Part, Part, Part, Part, Part], [undefined?, Folder, Part[][], Part]]; - //waitForLeaveSpawn() -} - -//Mode switching functions -//local TT = {} -wsti = TweenInfo.new(0.75) - -function tweenRecolor(Decor,TT,coloraffected,count,colors,materials) //Recolor, show and hide wings using the power of tweens - local Adds = Decor[1] - local CWings = Decor[3] - local VWings = Decor[8] - local Core = Decor[10][1] - for j = 1, 2 do - for i = 1, 6 do - local GM = {} //the goal tables used in the tweens - local GMless = {} - local GS = {} - local num = nil - if i <= count[j] then //wings that should be shown - num = 0 - if not coloraffected or i > coloraffected[j][2] or i < coloraffected[j][1] then //Ignore recoloring if the client is supposed to take over - GM.Color = colors[j][i] - GMless.Color = colors[j][i] - GS.Color = colors["d"] - end - //VWings[j][i].M.Material = colors["m"][1] //almost forgot that you can't tween materials - pcall(function() - VWings[j][i].S.Material = colors["m"][2] - end) - VWings[j][i].M.Color = VWings[j][i].M.Color - VWings[j][i].Trail.Color = ColorSequence.new(colors[j][1]) - VWings[j][i].M.Light.Enabled = true - else //wings to hide - num = 1 - spawn(function() - wait(wsti.Time) - VWings[j][i].M.Light.Enabled = false - end) - end - GM.Transparency = num - GS.Transparency = num - GMless.Brightness = 0.25-num/4 - VWings[j][i].Trail.Transparency = NumberSequence.new(num,1) - insert(TT,tweenServiceCreate(twS,VWings[j][i].M,wsti,GM)) - insert(TT,tweenServiceCreate(twS,VWings[j][i].M.Light,wsti,GMless)) - VWings[j][i].M.Material = materials[j][i] - pcall(function() - insert(TT,tweenServiceCreate(twS,VWings[j][i].S,wsti,GS)) - end) - end - end - - local GCE = {} //core extra - - if not coloraffected then //Ignore if the client is supposed to take over - local GC = {Color = colors.core[1]} //core - GCE.Color = colors[1][4] - - Core.Extra.Material = materials[1][4] - - insert(TT,tweenServiceCreate(twS,Core.Main,wsti,GC)) - - insert(TT,tweenServiceCreate(twS,Core.Main.Light,wsti,GC)) - end - - if count[1] < 6 or count[2] < 6 then - GCE["Transparency"] = 1 - else - GCE["Transparency"] = 0 - end - insert(TT,tweenServiceCreate(twS,Core.Extra,wsti,GCE)) -end - -function recolorBasedOnClass(item,color,color2) - local class = item.ClassName - if class == "Frame" then - item.BackgroundColor3 = color - elseif class == "ImageLabel" then - item.ImageColor3 = color - elseif class == "TextLabel" then - item.TextColor3 = color - item.TextStrokeColor3 = color2 - end -end - -function returnTableBasedOnClass(item,color,color2) - local class = item.ClassName - if class == "Frame" then - return {BackgroundColor3 = color} - elseif class == "ImageLabel" then - return {ImageColor3 = color} - elseif class == "TextLabel" then - return {TextColor3 = color, TextStrokeColor3 = color2} - end -end - -function nameChange(BGui,TT,Head,coloreffect,one, two, text, font,extra) //This one doesn't really need an explanation - local gui = BGui[1] - local nem = BGui[2] - local extr = BGui[3] - nem.Text = text - nem.Font = font - if not coloreffect then - local GT = {} - GT.TextColor3 = one - GT.TextStrokeColor3 = two - insert(TT,tweenServiceCreate(twS,nem,wsti,GT)) - for i, v in pairs(BGui[4]) do //Recolor the extra components (primary) - insert(TT,tweenServiceCreate(twS,v,wsti,returnTableBasedOnClass(v,one,two)))//recolorBasedOnClass(v,one,two) - end - for i, v in pairs(BGui[5]) do //Recolor the extra components (secondary) - insert(TT,tweenServiceCreate(twS,v,wsti,returnTableBasedOnClass(v,two,one)))//recolorBasedOnClass(v,two,one) - end - end - if extra then - extr.Text = extra - extr.Color = one//extr.g.Color = ColorSequence.new({ColorSequenceKeypoint.new(0,two),ColorSequenceKeypoint.new(0.5,one),ColorSequenceKeypoint.new(1,two)}) - extr.Visible = true - else - extr.Visible = false - end -end - -function checkForMove(set,side,mode,inp) - local sane = nil - pcall(function() - sane = Abilties[inp][set][side][mode] - end) - return sane -end - - -function StandardTransform(Plr,side,mod,sub,override) //I wouldn't call this spaghetti but it probably is damn hard to read - //Reads a mode and calls nameChange and tweenRecolor as well as doing some other basic stuff - local guh = p[Plr.UserId] - local set = guh.Set - if side ~= guh.Side or mod ~= guh.Mode or sub or override then - - if sub then //the idea of appending the sub key to the current mode is originally gun's idea - mod = mod .. sub - end - - local modeTable = Modes[set][side][mod] - - if modeTable then //make sure the mode actually exists - guh.CDA = false - guh.modeTable = modeTable - - for i, v in pairs(guh.Tweens) do - v:Pause() - v:Destroy() - end - //print(test) - guh.Tweens = {} - if side ~= guh.Side then - Output:FireClient(Plr,"SideColors",sideColorTable(set,side)) - end - guh.Side, guh.Mode = side, mod - - //Recolor the billboard GUI - local text = modeTable.name - if modeTable.tag[1] then //make the name uppercase - text = string.upper(text) - end - nameChange(guh.BGui,guh.Tweens,guh.Parts[1],modeTable.coloreffect or nil, modeTable.tag[2], modeTable.tag[3], text, modeTable.tag[4]) - - //Deal with stats like walkspeed and hover height - local Hum = guh.Parts[2] - Hum.WalkSpeed = modeTable.ws - Hum.HipHeight = modeTable.height or 0 - //storedWalkSpeed = modeTable[7] - - //Recolor the wings - local wc = modeTable.wingc - //{{table for left wings}, {table for right wings}, d = detailing color, m = {material (default neon), detailing material (default glass)}, ring = ring color (default main wing color)} - local colores = {{},{},d = wc.d,m = {wc.m or neon, wc.dm or Enum.Material.Glass}, ring = wc.ring or wc.L1} //yeah this table is unreadable - local materials = {{wc["ML1"] or neon},{wc["MR1"] or neon}} - colores[1][1] = wc.L1 //This is the only wing color that is explicitly required, every other one will default to this - colores[2][1] = wc.R1 or wc.L1 //See? - for j = 1, 2 do //Give every other wing either the designated color or the color of the previous wing - local l = (j == 1 and "L") or "R" - for i = 2, 6 do - colores[j][i] = wc[l .. tSt(i)] or colores[j][i-1] - materials[j][i] = wc["M" .. l .. tSt(i)] or materials[j][i-1] - end - end - colores.core = {wc.core or colores[1][1]} - - tweenRecolor(guh.Decor,guh.Tweens,modeTable.coloraffected or nil,{wc.left,wc.right},colores,materials) - - local localTable = localModeTable(modeTable) - guh.StoredModeInfo = localTable - Output:FireAllClients("ModeChange",Plr.UserId,localTable) - //wait(1) - for i, v in pairs(guh.Tweens) do - v:Play() - end - guh.CDA = true - - //Give all of the other data to the client - - local mE = guh.MovesExist - local mEC = {} - for i, inp in pairs(abilityKeys) do - mE[inp] = checkForMove(set,side,mod,inp) - //if mE[inp] then - //print("Got move for " .. inp) - //print(mE[inp]) - //end - mEC[inp] = toboolean(mE[inp]) - end - Output:FireClient(Plr,"MovesExist",mEC,set,side,mod,modeTable.main) - end - end -end - -// Attacks // - -wlist = Enum.RaycastFilterType.Whitelist -blist = Enum.RaycastFilterType.Blacklist -function CastRay(start,ende,list,listype) - local params = RaycastParams.new() - params.FilterDescendantsInstances = list - params.FilterType = listype - return workspace:Raycast(start,ende-start,params) -end - -function checkCoolDowns(cd,inp,Set,Side,Mode) - local now = floorToBase(clock(),10) - for i, v in pairs(cd) do - if now > v[1] + v[2] then - cd[i] = nil - end - end -end -/* -function reg3HitBox(Chr,siz,pos) - local realsize = v3(math.ceil(siz.X*0.25)*4,math.ceil(siz.Y*0.25)*4,math.ceil(siz.Z*0.25)*4) - - local reg3 = Region3.new(pos-realsize*0.5,pos+realsize*0.5) - local results = workspace:FindPartsInRegion3WithIgnoreList(reg3,{Chr,game.Workspace.Map},64) - local tosend = {} - for i, v in pairs(results) do - if v.Parent:FindFirstChild("Humanoid") then - if not plrs:GetPlayerFromCharacter(v.Parent) or (plrs:GetPlayerFromCharacter(v.Parent).Pvp.Value == true and Pvp.Value == true) then - insert(tosend,v) - end - end - end - return tosend -end - -function hitSphere(rad,pos,damage,effect,pass) - local res = reg3HitBox(v3(rad*2,rad*2,rad*2),pos) - local done = {} - for i, v in pairs(res) do - if not tFind(done,v.Parent) then - if (v.Position-pos).Magnitude < rad then - insert(done,v.Parent) - v.Parent:FindFirstChild("Humanoid"):TakeDamage(damage) - end - end - end - return (done[1] and done) or nil //DONE AND DONE -end - -function clickAttackNumberFunction() - clickAttackNumber = (clickAttackNumber == 3 and 1) or clickAttackNumber+1 - local ostime = os.time()+ rand - print(ostime) - clickAttackNumberFunctionStoredNumber = ostime - wait(0.5) - if ostime == clickAttackNumberFunctionStoredNumber then - clickAttackNumber = 1 - end -end -*/ - -Input.OnServerEvent:Connect(function(plr,typ,inp,pos,norm,targ) - //print(inp) - local uId = plr.UserId - local guh = p[uId] - - mouseData[uId] = {pos or mouseData[uId][1],norm or mouseData[uId][2],targ or mouseData[uId][3]} - pos, norm, targ = unpack(mouseData[uId]) - if type(inp) ~= "string" then - Update:Fire("MouseResponse",uId,mouseData[uId]) - return - end - - if typ == "Message" then - print(inp) - elseif typ == "PBL" then - guh.PBL.Value = inp - elseif typ == "Input" and guh.CDA then //regular attacks - - if inp == "click" then - if playerZone(plr) == "Spawn" then - Output:FireClient(plr,"Notify","Abilities cannot be used at spawn.") - else - //Check if click attack should be reverted to 1 (too long ago/out of bounds) - local cd = guh.ClickAttack - local now = floorToBase(clock(),10) - if now - cd[2] > 0.5 or cd[1] == 4 then - cd[1] = 1 - end - - //Click Attack - Output:FireAllClients("Attack",uId,"Click",guh.Set,cd[1]) - guh.CDA = false - Abilties["Click"][guh.Set][cd[1*/(plr,pvp,pos, norm, targ,unpack(guh.Parts)) - - //Finish - cd[1] += 1 - cd[2] = floorToBase(clock(),10) - guh.CDA = true - end - elseif tFind(abilityKeys,inp) then - if playerZone(plr) == "Spawn" then - Output:FireClient(plr,"Notify","Abilities cannot be used at spawn.") - else - local coolDown = guh.CoolDown - local Set = guh.Set - local Side = guh.Side - local Mode = guh.Mode - //print(tostring(inp)) - //inp = sub(tSt(inp),14) - checkCoolDowns(coolDown,inp,Set,Side,Mode) - if not coolDown.Main and not coolDown[inp] and not coolDown[Set .. Side .. Mode .. inp] then - - local move = guh.MovesExist[inp] - //print(inp) - //for i,v in pairs(guh.MovesExist) do - // print(i) - //end - print(move) - if not move then - return - end - - Output:FireAllClients("Attack",uId,inp,Set,Side,Mode) - guh.CDA = false - local cool = move(plr,pvp,guh.Gyro,pos,norm,targ,unpack(guh.Parts)) - //local cool = sane(plr,pvp,pos, norm, targ,unpack(guh.Parts))//Chr,Root,Pos,Norm,Target) - - //cooldown - local now = floorToBase(clock(),10) - Output:FireClient(plr,"Cooldown",cool,inp,Set,Side,Mode) - coolDown.Main = {now,cool} - coolDown[inp] = {now,cool*3} - coolDown[Set .. Side .. Mode .. inp] = {now,cool*9} - guh.CDA = true - end - end - elseif tFind(modeKeys,sub(inp,1,1)) and tFind(modeKeys,sub(inp,2)) then - //inp = sub(tSt(inp),14) - StandardTransform(plr,guh.Side,inp) - elseif tFind(subModeKeys,inp) then - //inp = sub(tSt(inp),14) - StandardTransform(plr,guh.Side,guh.Mode,inp) - elseif tFind(sideKeys,inp) then - StandardTransform(plr,tFind(sideKeys,inp),"QQ") - end - end - -end) +// Action phase +Players.PlayerAdded.Connect(addPlayer); +Players.PlayerRemoving.Connect(removePlayer); +bindToInput(handleInput); diff --git a/src/services.d.ts b/src/services.d.ts index 5d13919..6db54b3 100644 --- a/src/services.d.ts +++ b/src/services.d.ts @@ -1,52 +1,36 @@ -interface emitterTable { - parent: Instance; - color: ColorSequence; - lightE?: number; - lightI?: number; - size: NumberSequence; - texture: string; - transparency: NumberSequence; - drag?: number; - locked: boolean; - lifetime: NumberRange; - rate: number; - rotation?: NumberRange; - rotSpeed?: NumberRange; - speed: NumberRange; - spread?: Vector2; +type puppetEntry = ["Character", Player] | ["Placeholder", "Placeholder"]; +type serverMessageType = "init" | "promptError" | "enterGame"; +type clientMessageType = "Placeholder"; + +interface saveDataEntry { + placeholder: string; } -interface Workspace extends Instance { - Effects: Folder; - Enemies: Folder; +interface playerStorageEntry { + inMainMenu: boolean; + // + Other data that is unique to players but does not persist between sessions + saveData: saveDataEntry; + entity?: entity; } -interface ReplicatedStorage extends Instance { - Remotes: Folder & { - Input: RemoteEvent; - Output: RemoteEvent; - }; - Meshpile: Folder; +interface puppet { + model: Model; + rootPart: Part; + //placeholder: (x: string) => string; // + "Puppet string" functions will (not?) go here } -interface wingModelType extends Model { - M: MeshPart; - S: MeshPart; +interface entity { + baseStats: [number, number, number, number]; // MaxHealth, Attack, Speed, Defense (things used for calculation, only modified by buffs and debuffs) + baseAmounts: [number, number]; // Health, Barrier (things of indescribable importance) + puppet: puppet; } -interface Player extends Instance { - PlayerGui: PlayerGui; -} -interface Character extends Model { - HumanoidRootPart: Part & { - RootJoint: Motor6D; - }; - Torso: Part & { - Neck: Motor6D; - ["Left Shoulder"]: Motor6D; - ["Right Shoulder"]: Motor6D; - ["Left Hip"]: Motor6D; - ["Right Hip"]: Motor6D; - }; - Head: Part; - ["Left Arm"]: Part; - ["Right Arm"]: Part; - ["Left Leg"]: Part; - ["Right Leg"]: Part; +interface event { + winEvents?: event[]; // A list of events that need to return true (in sequence) to complete this event + winEntities?: entity[]; // A list of entities that need to die to complete the event + timeout?: number; // A timeout for the event; passes a lose condition if there are other completion requirements that have not been satisfied } + +/*interface hookInEntry { + name: string; + guiObject: GuiObject; +}*/ +/*interface runningEvent { + endTime?: number; +}*/ diff --git a/src/shared/EntityManager.ts b/src/shared/EntityManager.ts new file mode 100644 index 0000000..9b0ea1f --- /dev/null +++ b/src/shared/EntityManager.ts @@ -0,0 +1,11 @@ +// "EntityManager": Create entities objects and deck them out with functions to use. +// + Functions are here, as to avoid storing unecessary data in the server store. +import { makePuppet } from "./Puppetmaster"; +export function makeEntity(puppetEntry: puppetEntry) { + const newEntity: entity = { + baseStats: [0, 0, 0, 0], + baseAmounts: [0, 0], + puppet: makePuppet(puppetEntry), + }; + return newEntity; +} diff --git a/src/shared/EventManager.ts b/src/shared/EventManager.ts new file mode 100644 index 0000000..b00d6ae --- /dev/null +++ b/src/shared/EventManager.ts @@ -0,0 +1,13 @@ +// "The": Handle events. +// WORST CONDITION RigHT NOW +import { makeEntity } from "./EntityManager"; +export function runEvent(event: event) { + let complete = false; + const startTime = os.clock(); + const endTime = 2; + while (!complete) { + if (event.timeout === 2) { + complete = true; + } + } +} diff --git a/src/shared/PlayerManager.ts b/src/shared/PlayerManager.ts new file mode 100644 index 0000000..1714a75 --- /dev/null +++ b/src/shared/PlayerManager.ts @@ -0,0 +1,28 @@ +// "PlayerManager": Handle the data of players. This involves receiving them when they arrive, cleaning up after they exit, teleporting them, etc. +import { makeEntity } from "./EntityManager"; + +export function initPlayer(player: Player) { + const newEntry: playerStorageEntry = { + inMainMenu: true, + saveData: { + placeholder: "placeholder", + }, + }; + // + Load player's datastore into server store + return newEntry; // Return the entry to be put into the server store +} +export function deinitPlayer(entry: playerStorageEntry | undefined, player: Player) { + assert(entry, "Trying to remove entry of player " + player.DisplayName + ", but entry does not exist!"); + // ? Tell the entity to unload, if it still exists (the entity will tell the other clients to remove the player) + // + Unload player's server store to datastores + return undefined; // A nil entry to replace the entry to be wiped and maybe a success value in a wrapper +} +export function loadInPlayer(player: Player) { + const entity = makeEntity(["Character", player]); + // + Give the entity the stats it's supposed to have, load from save data maybe? + return entity; +} +export function teleportPlayer() { + // + Do checking related to where the player is allowed to go + // + Teleport player to other server, sending a message to have them load in automatically +} diff --git a/src/shared/PuppetLibraries/Character.ts b/src/shared/PuppetLibraries/Character.ts new file mode 100644 index 0000000..27ba7c5 --- /dev/null +++ b/src/shared/PuppetLibraries/Character.ts @@ -0,0 +1,21 @@ +// "Character": Tell the puppetmaster how to make and handle characters. +const RunService = game.GetService("RunService"); + +export function makeModel(player: Player) { + player.LoadCharacter(); + // Get model + let model: Model | undefined; + const now = os.clock(); + do { + model = player.Character; + RunService.Heartbeat.Wait(); + } while (!model && os.clock() - now < 5); + assert(model, 'Timed out: Model of player "' + player.DisplayName + '" failed to load!'); + // Get root part + const root = model.WaitForChild("HumanoidRootPart", 1); + assert( + root && classIs(root, "Part"), + 'Timed out: Root Part of player "' + player.DisplayName + '" failed to load!', + ); + return [model, root] as [Model, Part]; +} diff --git a/src/shared/Puppetmaster.ts b/src/shared/Puppetmaster.ts new file mode 100644 index 0000000..08f13bd --- /dev/null +++ b/src/shared/Puppetmaster.ts @@ -0,0 +1,18 @@ +// "Puppetmaster": Create consistent, reliable interfaces for the physical characters. +import * as m1 from "./PuppetLibraries/Character"; +const puppetLibraries = { + ["Character"]: m1, + ["Placeholder"]: m1, +}; + +export function makePuppet(puppetEntry: puppetEntry) { + if (puppetEntry[0] === "Character") { + const model: [Model, Part] = puppetLibraries[puppetEntry[0]].makeModel(puppetEntry[1]); + return { + model: model[0], + rootPart: model[1], + }; + } else { + throw 'Invalid puppet type "' + puppetEntry[0] + '"!'; + } +} diff --git a/src/shared/Remotes.ts b/src/shared/Remotes.ts new file mode 100644 index 0000000..6f69c8d --- /dev/null +++ b/src/shared/Remotes.ts @@ -0,0 +1,4 @@ +// "Remotes" +const ReplicatedStorage = game.GetService("ReplicatedStorage"); +export const Input = ReplicatedStorage.WaitForChild("Input", 1); +export const Output = ReplicatedStorage.WaitForChild("Output", 1); diff --git a/src/shared/module.ts b/src/shared/module.ts deleted file mode 100644 index 68a26bf..0000000 --- a/src/shared/module.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function makeHello(name: string) { - return `Hello from ${name}!`; -}