Setup sprint and random decoration
Co-authored-by: Yowoshi246 <Yowoshi246@users.noreply.github.com>
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/node_modules
|
||||
/out
|
||||
/include
|
||||
*.tsbuildinfo
|
||||
6
.prettierrc
Normal file
6
.prettierrc
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "all",
|
||||
"useTabs": true
|
||||
}
|
||||
6
.vscode/extensions.json
vendored
Normal file
6
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"roblox-ts.vscode-roblox-ts",
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
}
|
||||
15
.vscode/settings.json
vendored
Normal file
15
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"files.eol": "\n",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"eslint.run": "onType",
|
||||
"eslint.format.enable": true,
|
||||
"eslint.useFlatConfig": false
|
||||
}
|
||||
58
default.project.json
Normal file
58
default.project.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "roblox-ts-game",
|
||||
"globIgnorePaths": [
|
||||
"**/package.json",
|
||||
"**/tsconfig.json"
|
||||
],
|
||||
"tree": {
|
||||
"$className": "DataModel",
|
||||
"ServerScriptService": {
|
||||
"$className": "ServerScriptService",
|
||||
"TS": {
|
||||
"$path": "out/server"
|
||||
}
|
||||
},
|
||||
"ReplicatedStorage": {
|
||||
"$className": "ReplicatedStorage",
|
||||
"rbxts_include": {
|
||||
"$path": "include",
|
||||
"node_modules": {
|
||||
"$className": "Folder",
|
||||
"@rbxts": {
|
||||
"$path": "node_modules/@rbxts"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TS": {
|
||||
"$path": "out/shared"
|
||||
}
|
||||
},
|
||||
"StarterPlayer": {
|
||||
"$className": "StarterPlayer",
|
||||
"StarterPlayerScripts": {
|
||||
"$className": "StarterPlayerScripts",
|
||||
"TS": {
|
||||
"$path": "out/client"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Workspace": {
|
||||
"$className": "Workspace",
|
||||
"$properties": {
|
||||
"FilteringEnabled": true
|
||||
}
|
||||
},
|
||||
"HttpService": {
|
||||
"$className": "HttpService",
|
||||
"$properties": {
|
||||
"HttpEnabled": true
|
||||
}
|
||||
},
|
||||
"SoundService": {
|
||||
"$className": "SoundService",
|
||||
"$properties": {
|
||||
"RespectFilteringEnabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
eslint.config.mjs
Normal file
51
eslint.config.mjs
Normal file
@@ -0,0 +1,51 @@
|
||||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
// import robloxTs from "eslint-plugin-roblox-ts";
|
||||
import prettier from "eslint-plugin-prettier";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores(["out"]),
|
||||
{
|
||||
extends: compat.extends(
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:roblox-ts/recommended-legacy",
|
||||
"plugin:prettier/recommended",
|
||||
),
|
||||
|
||||
plugins: {
|
||||
"@typescript-eslint": typescriptEslint,
|
||||
// "roblox-ts": robloxTs,
|
||||
prettier,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 2018,
|
||||
sourceType: "module",
|
||||
|
||||
parserOptions: {
|
||||
jsx: true,
|
||||
useJSXTextNode: true,
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
"prettier/prettier": "warn",
|
||||
},
|
||||
},
|
||||
]);
|
||||
2443
package-lock.json
generated
Normal file
2443
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
package.json
Normal file
37
package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "aconit",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "rbxtsc",
|
||||
"watch": "rbxtsc -w",
|
||||
"lint": "eslint"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.37.0",
|
||||
"@rbxts/compiler-types": "^3.0.0-types.0",
|
||||
"@rbxts/net": "^3.0.10",
|
||||
"@rbxts/services": "^1.5.5",
|
||||
"@rbxts/signal": "^1.1.1",
|
||||
"@rbxts/types": "^1.0.882",
|
||||
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
||||
"@typescript-eslint/parser": "^8.44.0",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-roblox-ts": "^1.2.1",
|
||||
"prettier": "^3.6.2",
|
||||
"rbxts-transformer-services": "^1.1.1",
|
||||
"roblox-ts": "^3.0.0",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"-": "^0.0.1"
|
||||
}
|
||||
}
|
||||
16
src/client/main.client.ts
Normal file
16
src/client/main.client.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Players, UserInputService } from "@rbxts/services";
|
||||
import Gameplay_Remotes from "shared/gameplay";
|
||||
|
||||
const sprint = Gameplay_Remotes.Client.Get("Sprint");
|
||||
|
||||
UserInputService.InputBegan.Connect((input) => {
|
||||
if (input.KeyCode === Enum.KeyCode.LeftShift) {
|
||||
sprint.SendToServer(true);
|
||||
}
|
||||
});
|
||||
|
||||
UserInputService.InputEnded.Connect((input) => {
|
||||
if (input.KeyCode === Enum.KeyCode.LeftShift) {
|
||||
sprint.SendToServer(false);
|
||||
}
|
||||
});
|
||||
29
src/server/command.server.ts
Normal file
29
src/server/command.server.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Players, TextChatService } from "@rbxts/services";
|
||||
import { makeRagdoll, stopRagdoll, takeNetworkOwner } from "shared/ragdoll";
|
||||
|
||||
const ragdollCommand = new Instance("TextChatCommand");
|
||||
ragdollCommand.Parent = TextChatService.WaitForChild("TextChatCommands");
|
||||
ragdollCommand.PrimaryAlias = "/ragdoll";
|
||||
ragdollCommand.Triggered.Connect((TextSource) => {
|
||||
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
||||
|
||||
makeRagdoll(player!.Character!);
|
||||
});
|
||||
|
||||
const unragdollCommand = new Instance("TextChatCommand");
|
||||
unragdollCommand.Parent = TextChatService.WaitForChild("TextChatCommands");
|
||||
unragdollCommand.PrimaryAlias = "/unragdoll";
|
||||
unragdollCommand.Triggered.Connect((TextSource) => {
|
||||
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
||||
|
||||
stopRagdoll(player!.Character!);
|
||||
});
|
||||
|
||||
const takeownerCommand = new Instance("TextChatCommand");
|
||||
takeownerCommand.Parent = TextChatService.WaitForChild("TextChatCommands");
|
||||
takeownerCommand.PrimaryAlias = "/takeowner";
|
||||
takeownerCommand.Triggered.Connect((TextSource) => {
|
||||
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
||||
|
||||
takeNetworkOwner(player!.Character!);
|
||||
});
|
||||
12
src/server/decoration.server.ts
Normal file
12
src/server/decoration.server.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ReplicatedStorage } from "@rbxts/services";
|
||||
import { randomDecorationPlace } from "shared/build";
|
||||
|
||||
const flowers = ReplicatedStorage.FindFirstChild("Decoration")?.FindFirstChild("FlowerTypes")?.GetChildren();
|
||||
if (flowers) {
|
||||
randomDecorationPlace("Build_Flower", flowers as Model[]);
|
||||
}
|
||||
|
||||
const rocks = ReplicatedStorage.FindFirstChild("Decoration")?.FindFirstChild("RockTypes")?.GetChildren();
|
||||
if (rocks) {
|
||||
randomDecorationPlace("Build_Gravel", rocks as Model[]);
|
||||
}
|
||||
62
src/server/gameplay.server.ts
Normal file
62
src/server/gameplay.server.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Players } from "@rbxts/services";
|
||||
import Gameplay_Remotes from "shared/gameplay";
|
||||
|
||||
const sprint = Gameplay_Remotes.Server.Get("Sprint");
|
||||
|
||||
const RunAnim = new Instance("Animation");
|
||||
RunAnim.AnimationId = "rbxassetid://88297455683117";
|
||||
const base_sprint_speed = 16;
|
||||
const sprint_speed = 1.5;
|
||||
const runanim = new Map<number, AnimationTrack>();
|
||||
const isrunning = new Map<number, boolean>();
|
||||
|
||||
Players.PlayerAdded.Connect((player) => {
|
||||
let runningConn: RBXScriptConnection;
|
||||
|
||||
player.CharacterAdded.Connect((character) => {
|
||||
const humanoid = character.FindFirstChildWhichIsA("Humanoid");
|
||||
if (humanoid) {
|
||||
const animator = humanoid.FindFirstChildOfClass("Animator");
|
||||
const anim = animator!.LoadAnimation(RunAnim);
|
||||
anim.Priority = Enum.AnimationPriority.Action;
|
||||
runanim.set(player.UserId, anim);
|
||||
|
||||
runningConn = humanoid.Running.Connect((speed) => {
|
||||
if (isrunning.get(player.UserId)) {
|
||||
if (speed > 2) {
|
||||
if (!anim.IsPlaying) anim.Play(0.2);
|
||||
} else {
|
||||
if (anim.IsPlaying) anim?.Stop(0.2);
|
||||
}
|
||||
} else if (anim?.IsPlaying) {
|
||||
anim?.Stop(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
player.CharacterRemoving.Once(() => runningConn.Disconnect());
|
||||
});
|
||||
|
||||
sprint.Connect((player, enabled) => {
|
||||
const character = player.Character;
|
||||
isrunning.set(player.UserId, enabled);
|
||||
if (character) {
|
||||
const humanoid = character.FindFirstChildOfClass("Humanoid");
|
||||
if (humanoid) {
|
||||
if (enabled) {
|
||||
humanoid.WalkSpeed = base_sprint_speed * sprint_speed;
|
||||
// if (humanoid.GetStateEnabled(Enum.HumanoidStateType.Running)) {
|
||||
// const anim = runanim.get(player.UserId);
|
||||
// anim?.Play(0.2);
|
||||
// }
|
||||
} else {
|
||||
humanoid.WalkSpeed = base_sprint_speed;
|
||||
// if (humanoid.GetStateEnabled(Enum.HumanoidStateType.Running)) {
|
||||
// const anim = runanim.get(player.UserId);
|
||||
// anim?.Stop(1);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
14
src/server/main.server.ts
Normal file
14
src/server/main.server.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Players } from "@rbxts/services";
|
||||
import { makeRagdoll, stopRagdoll, waitMotionLess } from "shared/ragdoll";
|
||||
|
||||
Players.PlayerAdded.Connect((player) => {
|
||||
player.CharacterAdded.Connect((character) => {
|
||||
const humanoid = character.FindFirstChildOfClass("Humanoid");
|
||||
if (!humanoid) return;
|
||||
humanoid.FreeFalling.Connect(() => {
|
||||
makeRagdoll(character);
|
||||
waitMotionLess(character, 0.1).Wait();
|
||||
stopRagdoll(character);
|
||||
});
|
||||
});
|
||||
});
|
||||
16
src/shared/build.ts
Normal file
16
src/shared/build.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { CollectionService } from "@rbxts/services";
|
||||
|
||||
export function randomDecorationPlace(tag: string, decorations: Model[]) {
|
||||
function getDecoration() {
|
||||
return decorations[math.random(0, decorations.size() - 1)];
|
||||
}
|
||||
|
||||
CollectionService.GetTagged(tag).forEach((part) => {
|
||||
if (part.IsA("Part")) {
|
||||
const deco = getDecoration().Clone();
|
||||
deco.Parent = part.Parent;
|
||||
deco.MoveTo(part.Position);
|
||||
part.Destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
7
src/shared/gameplay.ts
Normal file
7
src/shared/gameplay.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import Net from "@rbxts/net";
|
||||
|
||||
const Gameplay_Remotes = Net.Definitions.Create({
|
||||
Sprint: Net.Definitions.ClientToServerEvent<[enabled: boolean]>(),
|
||||
});
|
||||
|
||||
export default Gameplay_Remotes;
|
||||
0
src/shared/module.ts
Normal file
0
src/shared/module.ts
Normal file
98
src/shared/ragdoll.ts
Normal file
98
src/shared/ragdoll.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import Signal from "@rbxts/signal";
|
||||
|
||||
/**
|
||||
* Takes the Network Ownership of a player's character.
|
||||
* @param character The character of a player.
|
||||
*/
|
||||
export function takeNetworkOwner(character: Model) {
|
||||
const part = character.GetDescendants();
|
||||
|
||||
part.forEach((part) => {
|
||||
if (part.IsA("Part")) {
|
||||
part.SetNetworkOwner(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the Network Ownership of a player's character.
|
||||
* @param character The character of a player
|
||||
*/
|
||||
export function releaseNetworkOwnership(character: Model) {
|
||||
const part = character.GetDescendants();
|
||||
|
||||
part.forEach((part) => {
|
||||
if (part.IsA("Part")) {
|
||||
part.SetNetworkOwnershipAuto();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the player's character Ragdoll by creating attachments for each part of the R6 Model.
|
||||
* @param character The character of a player.
|
||||
*/
|
||||
export function makeRagdoll(character: Model) {
|
||||
takeNetworkOwner(character);
|
||||
const children = character.GetDescendants();
|
||||
const humanoid = character.FindFirstChildOfClass("Humanoid")!;
|
||||
humanoid.RequiresNeck = false;
|
||||
humanoid.ChangeState(Enum.HumanoidStateType.Physics);
|
||||
children.forEach((motor) => {
|
||||
if (motor.IsA("Motor6D")) {
|
||||
const ballsocketconstraint = new Instance("BallSocketConstraint"),
|
||||
attachment0 = new Instance("Attachment"),
|
||||
attachment1 = new Instance("Attachment");
|
||||
|
||||
attachment0.CFrame = motor.C0;
|
||||
attachment0.Parent = motor.Part0;
|
||||
attachment0.AddTag("Ragdoll");
|
||||
|
||||
attachment1.CFrame = motor.C1;
|
||||
attachment1.Parent = motor.Part1;
|
||||
attachment1.AddTag("Ragdoll");
|
||||
|
||||
ballsocketconstraint.Attachment0 = attachment0;
|
||||
ballsocketconstraint.Attachment1 = attachment1;
|
||||
ballsocketconstraint.Parent = motor.Parent;
|
||||
ballsocketconstraint.AddTag("Ragdoll");
|
||||
|
||||
motor.Enabled = false;
|
||||
}
|
||||
});
|
||||
takeNetworkOwner(character);
|
||||
}
|
||||
|
||||
export function stopRagdoll(character: Model) {
|
||||
releaseNetworkOwnership(character);
|
||||
character.GetDescendants().forEach((des) => {
|
||||
if (des.HasTag("Ragdoll")) {
|
||||
des.Destroy();
|
||||
}
|
||||
if (des.IsA("Motor6D")) {
|
||||
des.Enabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return a signal which fire when the player stop moving.
|
||||
* @param character The character of the player
|
||||
* @param threshold The maximum distance (in studs) the player must travel in 0.3 seconds for the signal to be fired.
|
||||
* @returns The signal.
|
||||
*/
|
||||
export function waitMotionLess(character: Model, threshold: number) {
|
||||
const signal = new Signal();
|
||||
let delta: number = -1;
|
||||
const root = character.FindFirstChild("HumanoidRootPart");
|
||||
if (!root?.IsA("Part")) throw "Bro are you an idiot ?";
|
||||
task.spawn(() => {
|
||||
while (0 > delta || delta > threshold) {
|
||||
const lastPos = root.Position;
|
||||
task.wait(0.3);
|
||||
delta = root.Position.sub(lastPos).Magnitude;
|
||||
}
|
||||
signal.Fire();
|
||||
});
|
||||
return signal;
|
||||
}
|
||||
5
src/shared/replicated.ts
Normal file
5
src/shared/replicated.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import Net from "@rbxts/net";
|
||||
const Remotes = Net.Definitions.Create({
|
||||
SendAttack: Net.Definitions.ClientToServerEvent<[attackId: number]>(),
|
||||
});
|
||||
export default Remotes;
|
||||
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// required
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"downlevelIteration": true,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "Node",
|
||||
"noLib": true,
|
||||
"resolveJsonModule": true,
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleDetection": "force",
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"typeRoots": ["node_modules/@rbxts"],
|
||||
|
||||
// configurable
|
||||
"rootDir": "src",
|
||||
"outDir": "out",
|
||||
"baseUrl": "src",
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "out/tsconfig.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user