Complete redo of the game's structure due to orthogonality and ETC
concerns
This commit is contained in:
parent
909bfe30d8
commit
1a5208b1f9
17 changed files with 603 additions and 2068 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
||||||
/node_modules
|
/node_modules
|
||||||
/out
|
/out
|
||||||
/include
|
/include
|
||||||
/models
|
/.old-src
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
|
@ -31,18 +31,6 @@
|
||||||
"$className": "Workspace",
|
"$className": "Workspace",
|
||||||
"$properties": {
|
"$properties": {
|
||||||
"FilteringEnabled": true
|
"FilteringEnabled": true
|
||||||
},
|
|
||||||
"Enemies": {
|
|
||||||
"$className": "Folder"
|
|
||||||
},
|
|
||||||
"Effects": {
|
|
||||||
"$className": "Folder"
|
|
||||||
},
|
|
||||||
"Projectiles": {
|
|
||||||
"$className": "Folder"
|
|
||||||
},
|
|
||||||
"Map": {
|
|
||||||
"$className": "Folder"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
|
@ -56,115 +44,16 @@
|
||||||
"TS": {
|
"TS": {
|
||||||
"$path": "out/shared"
|
"$path": "out/shared"
|
||||||
},
|
},
|
||||||
"Remotes": {
|
|
||||||
"$className": "Folder",
|
|
||||||
"Input": {
|
"Input": {
|
||||||
"$className": "RemoteEvent"
|
"$className": "RemoteEvent",
|
||||||
|
"$properties": {
|
||||||
|
"Name": "Input"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Output": {
|
"Output": {
|
||||||
"$className": "RemoteEvent"
|
"$className": "RemoteEvent",
|
||||||
},
|
|
||||||
"Dialog": {
|
|
||||||
"$className": "RemoteEvent"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Meshpile": {
|
|
||||||
"$className": "Folder",
|
|
||||||
"SlashM": {
|
|
||||||
"$className": "MeshPart",
|
|
||||||
"$properties": {
|
"$properties": {
|
||||||
"MeshId": "rbxassetid://5718206850"
|
"Name": "Output"
|
||||||
}
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
184
old.default.project.json
Normal file
184
old.default.project.json
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/client/ClientMessenger.ts
Normal file
9
src/client/ClientMessenger.ts
Normal file
|
@ -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);
|
||||||
|
}
|
203
src/client/GuiHandler.ts
Normal file
203
src/client/GuiHandler.ts
Normal file
|
@ -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;
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -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"));
|
|
13
src/server/ServerMessenger.ts
Normal file
13
src/server/ServerMessenger.ts
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
function addPlayer(player: Player) {
|
||||||
|
playerStorage[player.UserId] = initPlayer(player);
|
||||||
// Universal variables
|
messageClient(player, "init", "idk");
|
||||||
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 removePlayer(player: Player) {
|
||||||
function floorToBase(number: number, base: number) { // Is in multiple scripts, could be consolidated
|
playerStorage[player.UserId] = deinitPlayer(playerStorage[player.UserId], player);
|
||||||
return floor(number * base) / base;
|
|
||||||
}
|
}
|
||||||
|
// 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 AFrame(x: number,y: number,z: number,rx: number,ry: number,rz: number) { // Is in multiple scripts, could be consolidated
|
function handleInput(player: Player, messageType: unknown, messageContent: unknown) {
|
||||||
return new cF(x,y,z).mul(xyz(rad(rx),rad(ry),rad(rz)));
|
if (messageType === "EnterGame") {
|
||||||
}
|
try {
|
||||||
|
entityStorage[player.UserId] = loadInPlayer(player);
|
||||||
const uv = new v3(0,1,0);
|
messageClient(player, "enterGame");
|
||||||
function lookAt(start: Vector3,ende: Vector3) { // Is in multiple scripts, could be consolidated (This is the master version)
|
} catch (thrownError) {
|
||||||
let fv = (ende.sub(start)).Unit;
|
const errorMessage = 'Error when creating entity of "' + player.DisplayName + '": ' + thrownError;
|
||||||
let rv = fv.Cross(uv);
|
warn(errorMessage);
|
||||||
let uv2 = rv.Cross(fv);
|
messageClient(player, "promptError", errorMessage);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
} else if (messageType === "placeholder") {
|
||||||
|
const entity = entityStorage[player.UserId];
|
||||||
function sideColorTable(set,sideNumber)
|
if (entity && entity.puppet) {
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
// Action phase
|
||||||
let wingFolder = storage.WaitForChild("WingFolder") as Folder // Temporary
|
Players.PlayerAdded.Connect(addPlayer);
|
||||||
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)
|
Players.PlayerRemoving.Connect(removePlayer);
|
||||||
wings[1].ClearAllChildren()
|
bindToInput(handleInput);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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)
|
|
||||||
|
|
78
src/services.d.ts
vendored
78
src/services.d.ts
vendored
|
@ -1,52 +1,36 @@
|
||||||
interface emitterTable {
|
type puppetEntry = ["Character", Player] | ["Placeholder", "Placeholder"];
|
||||||
parent: Instance;
|
type serverMessageType = "init" | "promptError" | "enterGame";
|
||||||
color: ColorSequence;
|
type clientMessageType = "Placeholder";
|
||||||
lightE?: number;
|
|
||||||
lightI?: number;
|
interface saveDataEntry {
|
||||||
size: NumberSequence;
|
placeholder: string;
|
||||||
texture: string;
|
|
||||||
transparency: NumberSequence;
|
|
||||||
drag?: number;
|
|
||||||
locked: boolean;
|
|
||||||
lifetime: NumberRange;
|
|
||||||
rate: number;
|
|
||||||
rotation?: NumberRange;
|
|
||||||
rotSpeed?: NumberRange;
|
|
||||||
speed: NumberRange;
|
|
||||||
spread?: Vector2;
|
|
||||||
}
|
}
|
||||||
interface Workspace extends Instance {
|
interface playerStorageEntry {
|
||||||
Effects: Folder;
|
inMainMenu: boolean;
|
||||||
Enemies: Folder;
|
// + Other data that is unique to players but does not persist between sessions
|
||||||
|
saveData: saveDataEntry;
|
||||||
|
entity?: entity;
|
||||||
}
|
}
|
||||||
interface ReplicatedStorage extends Instance {
|
interface puppet {
|
||||||
Remotes: Folder & {
|
model: Model;
|
||||||
Input: RemoteEvent;
|
rootPart: Part;
|
||||||
Output: RemoteEvent;
|
//placeholder: (x: string) => string; // + "Puppet string" functions will (not?) go here
|
||||||
};
|
|
||||||
Meshpile: Folder;
|
|
||||||
}
|
}
|
||||||
interface wingModelType extends Model {
|
interface entity {
|
||||||
M: MeshPart;
|
baseStats: [number, number, number, number]; // MaxHealth, Attack, Speed, Defense (things used for calculation, only modified by buffs and debuffs)
|
||||||
S: MeshPart;
|
baseAmounts: [number, number]; // Health, Barrier (things of indescribable importance)
|
||||||
|
puppet: puppet;
|
||||||
}
|
}
|
||||||
interface Player extends Instance {
|
interface event {
|
||||||
PlayerGui: PlayerGui;
|
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
|
||||||
interface Character extends Model {
|
timeout?: number; // A timeout for the event; passes a lose condition if there are other completion requirements that have not been satisfied
|
||||||
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 hookInEntry {
|
||||||
|
name: string;
|
||||||
|
guiObject: GuiObject;
|
||||||
|
}*/
|
||||||
|
/*interface runningEvent {
|
||||||
|
endTime?: number;
|
||||||
|
}*/
|
||||||
|
|
11
src/shared/EntityManager.ts
Normal file
11
src/shared/EntityManager.ts
Normal file
|
@ -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;
|
||||||
|
}
|
13
src/shared/EventManager.ts
Normal file
13
src/shared/EventManager.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/shared/PlayerManager.ts
Normal file
28
src/shared/PlayerManager.ts
Normal file
|
@ -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
|
||||||
|
}
|
21
src/shared/PuppetLibraries/Character.ts
Normal file
21
src/shared/PuppetLibraries/Character.ts
Normal file
|
@ -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];
|
||||||
|
}
|
18
src/shared/Puppetmaster.ts
Normal file
18
src/shared/Puppetmaster.ts
Normal file
|
@ -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] + '"!';
|
||||||
|
}
|
||||||
|
}
|
4
src/shared/Remotes.ts
Normal file
4
src/shared/Remotes.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// "Remotes"
|
||||||
|
const ReplicatedStorage = game.GetService("ReplicatedStorage");
|
||||||
|
export const Input = ReplicatedStorage.WaitForChild("Input", 1);
|
||||||
|
export const Output = ReplicatedStorage.WaitForChild("Output", 1);
|
|
@ -1,3 +0,0 @@
|
||||||
export function makeHello(name: string) {
|
|
||||||
return `Hello from ${name}!`;
|
|
||||||
}
|
|
Loading…
Reference in a new issue