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}!`;
-}