🔥Finish Flamework Migration.
This commit is contained in:
45
src/server/components/lobby_door.ts
Normal file
45
src/server/components/lobby_door.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { BaseComponent, Component } from "@flamework/components";
|
||||
import { OnStart } from "@flamework/core";
|
||||
import { CollectionService, Players } from "@rbxts/services";
|
||||
|
||||
interface DoorInstance extends Model {
|
||||
Right_Door: Model & {
|
||||
Velocity: LinearVelocity;
|
||||
};
|
||||
Left_Door: Model & {
|
||||
Velocity: LinearVelocity;
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
tag: "Lobby_Door",
|
||||
})
|
||||
export class Doors extends BaseComponent<{}, DoorInstance> implements OnStart {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
onStart(): void {
|
||||
const doororigin = this.instance.WorldPivot.Position;
|
||||
while (task.wait(0.5)) {
|
||||
let actived = false;
|
||||
Players.GetPlayers().forEach((player) => {
|
||||
const character = player.Character;
|
||||
if (character) {
|
||||
const hrp = character.FindFirstChild("HumanoidRootPart") as Part;
|
||||
if (hrp) {
|
||||
const vectorToDoor = doororigin.sub(hrp.Position);
|
||||
const distanceToDoor = vectorToDoor.Magnitude;
|
||||
if (distanceToDoor <= 25) actived = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (actived) {
|
||||
this.instance.Right_Door.Velocity.VectorVelocity = new Vector3(-5, 0, 0);
|
||||
this.instance.Left_Door.Velocity.VectorVelocity = new Vector3(5, 0, 0);
|
||||
} else {
|
||||
this.instance.Right_Door.Velocity.VectorVelocity = new Vector3(5, 0, 0);
|
||||
this.instance.Left_Door.Velocity.VectorVelocity = new Vector3(-5, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/server/components/random_decoration.ts
Normal file
31
src/server/components/random_decoration.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { BaseComponent, Component } from "@flamework/components";
|
||||
import { OnStart } from "@flamework/core";
|
||||
import { ReplicatedStorage } from "@rbxts/services";
|
||||
|
||||
interface Attribute {
|
||||
Type: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
tag: "Random_Decoration",
|
||||
})
|
||||
class RandomDecoration extends BaseComponent<Attribute, Part> implements OnStart {
|
||||
onStart(): void {
|
||||
const decoration = this.getRandomDecoration(this.attributes.Type);
|
||||
decoration.Parent = this.instance.Parent;
|
||||
decoration.MoveTo(this.instance.Position);
|
||||
this.instance.Destroy();
|
||||
this.destroy();
|
||||
}
|
||||
private getRandomDecoration(deco_type: string) {
|
||||
function getDecoration(decorations: Model[]) {
|
||||
return decorations[math.random(0, decorations.size() - 1)];
|
||||
}
|
||||
const decorations = ReplicatedStorage.FindFirstChild("Decoration")
|
||||
?.FindFirstChild(`${deco_type}Types`)
|
||||
?.GetChildren()
|
||||
.filter((decoration) => decoration.IsA("Model"));
|
||||
if (!decorations) throw "Unknow Decoration Type";
|
||||
return getDecoration(decorations).Clone();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Flamework } from "@flamework/core";
|
||||
|
||||
// Flamework.addPaths("src/server/components");
|
||||
Flamework.addPaths("src/server/components");
|
||||
Flamework.addPaths("src/server/services");
|
||||
// Flamework.addPaths("src/shared/components");
|
||||
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { OnStart, Service } from "@flamework/core";
|
||||
import { Players, TextChatService } from "@rbxts/services";
|
||||
import { RagdollHandler, takeNetworkOwner } from "shared/ownership";
|
||||
import { takeNetworkOwner } from "shared/utils/ownership";
|
||||
import { RagdollService } from "./gameplay/ragdoll";
|
||||
|
||||
/**
|
||||
* Setup Chat Command
|
||||
*/
|
||||
@Service()
|
||||
class CommandsService implements OnStart {
|
||||
constructor(private readonly ragdollService: RagdollService) {}
|
||||
onStart(): void {
|
||||
const ragdollCommand = new TextChatCommand();
|
||||
ragdollCommand.Parent = TextChatService.WaitForChild("TextChatCommands");
|
||||
ragdollCommand.PrimaryAlias = "/ragdoll";
|
||||
ragdollCommand.Triggered.Connect((TextSource) => {
|
||||
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
||||
const handler = RagdollHandler.getHandlerByCharacter(player!.Character!, true);
|
||||
handler.ragdoll();
|
||||
this.ragdollService.ragdoll(player?.Character!);
|
||||
});
|
||||
|
||||
const unragdollCommand = new TextChatCommand();
|
||||
@@ -22,8 +23,7 @@ class CommandsService implements OnStart {
|
||||
unragdollCommand.PrimaryAlias = "/unragdoll";
|
||||
unragdollCommand.Triggered.Connect((TextSource) => {
|
||||
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
||||
const handler = RagdollHandler.getHandlerByCharacter(player!.Character!, true);
|
||||
handler.stopRagdoll();
|
||||
this.ragdollService.ragdoll(player?.Character!);
|
||||
});
|
||||
|
||||
const takeownerCommand = new TextChatCommand();
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { OnStart, Service } from "@flamework/core";
|
||||
import { ReplicatedStorage } from "@rbxts/services";
|
||||
import { randomDecorationPlace } from "shared/build";
|
||||
|
||||
/** Place Decorator on map. */
|
||||
@Service()
|
||||
class DecoratorService implements OnStart {
|
||||
onStart(): void {
|
||||
//#region Castle
|
||||
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[]);
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,181 @@
|
||||
import { OnStart, Service } from "@flamework/core";
|
||||
import { RagdollHandler } from "shared/ownership";
|
||||
import { OnPlayerJoined } from "../modding/playerjoinservice";
|
||||
import { Modding, OnStart, Service } from "@flamework/core";
|
||||
import { RunService } from "@rbxts/services";
|
||||
import Signal from "@rbxts/signal";
|
||||
import { OnPlayerJoined } from "shared/modding/player_events";
|
||||
import { releaseNetworkOwnership, takeNetworkOwner } from "shared/utils/ownership";
|
||||
import { SprintService } from "./sprint";
|
||||
|
||||
export interface OnRagdoll {
|
||||
onPlayerRagdoll: (character: Model) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service related to Ragdoll.
|
||||
*/
|
||||
@Service()
|
||||
class RagdollService implements OnPlayerJoined {
|
||||
export class RagdollService implements OnPlayerJoined, OnStart {
|
||||
private listeners = new Set<OnRagdoll>();
|
||||
|
||||
constructor(private readonly sprintService: SprintService) {}
|
||||
|
||||
//#region Flamework events.
|
||||
onStart(): void {
|
||||
Modding.onListenerAdded<OnRagdoll>((object) => this.listeners.add(object));
|
||||
Modding.onListenerRemoved<OnRagdoll>((object) => this.listeners.delete(object));
|
||||
}
|
||||
|
||||
onPlayerJoined(player: Player): void {
|
||||
player.CharacterAdded.Connect((character) => {
|
||||
const ragdollhandler = new RagdollHandler(character);
|
||||
ragdollhandler.getRagdollFallingEvent().Connect(() => {
|
||||
ragdollhandler.ragdoll();
|
||||
ragdollhandler.waitMotionLess().Wait();
|
||||
ragdollhandler.stopRagdoll();
|
||||
this.prepareRagdoll(character);
|
||||
this.getRagdollFallingEvent(character).Connect(() => {
|
||||
this.ragdoll(character);
|
||||
this.sprintService.setRunningInterdiction(player, true);
|
||||
this.waitMotionLess(character).Once(() => {
|
||||
this.stopRagdoll(character);
|
||||
this.sprintService.setRunningInterdiction(player, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Private functions.
|
||||
private getRagdollPhysic(character: Model) {
|
||||
const motors = new Set<Motor6D>();
|
||||
const constraints = new Set<BallSocketConstraint>();
|
||||
character.GetDescendants().forEach((descendant) => {
|
||||
if (descendant.IsA("Motor6D")) {
|
||||
motors.add(descendant);
|
||||
}
|
||||
if (descendant.IsA("BallSocketConstraint") && descendant.HasTag("Ragdoll")) {
|
||||
constraints.add(descendant);
|
||||
}
|
||||
});
|
||||
return $tuple(motors, constraints);
|
||||
}
|
||||
|
||||
private prepareRagdoll(character: Model) {
|
||||
const children = character.GetDescendants();
|
||||
children.forEach((motor) => {
|
||||
if (motor.IsA("Motor6D")) {
|
||||
const ballsocketconstraint = new BallSocketConstraint(),
|
||||
attachment0 = new Attachment(),
|
||||
attachment1 = new Attachment();
|
||||
|
||||
attachment0.CFrame = motor.C0;
|
||||
attachment0.Parent = motor.Part0;
|
||||
attachment0.AddTag("Ragdoll");
|
||||
attachment0.Name = motor.Name;
|
||||
|
||||
attachment1.CFrame = motor.C1;
|
||||
attachment1.Parent = motor.Part1;
|
||||
attachment1.AddTag("Ragdoll");
|
||||
attachment1.Name = motor.Name;
|
||||
|
||||
ballsocketconstraint.Attachment0 = attachment0;
|
||||
ballsocketconstraint.Attachment1 = attachment1;
|
||||
ballsocketconstraint.Parent = motor.Parent;
|
||||
ballsocketconstraint.AddTag("Ragdoll");
|
||||
ballsocketconstraint.Enabled = false;
|
||||
|
||||
if (motor.Name === "Left Hip") {
|
||||
attachment0.CFrame = attachment0.CFrame.add(new Vector3(0.5, 0, 0));
|
||||
attachment1.CFrame = attachment1.CFrame.add(new Vector3(0.5, 0, 0));
|
||||
} else if (motor.Name === "Right Hip") {
|
||||
attachment0.CFrame = attachment0.CFrame.sub(new Vector3(0.5, 0, 0));
|
||||
attachment1.CFrame = attachment1.CFrame.sub(new Vector3(0.5, 0, 0));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private fireOnRagdoll(character: Model) {
|
||||
for (const listener of this.listeners) {
|
||||
task.spawn(() => listener.onPlayerRagdoll(character));
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Main functions.
|
||||
/**
|
||||
* Ragdoll a character.
|
||||
* @param character The player character to ragdoll.
|
||||
*/
|
||||
ragdoll(character: Model) {
|
||||
this.fireOnRagdoll(character);
|
||||
takeNetworkOwner(character);
|
||||
const humanoid = character.WaitForChild("Humanoid", 10) as Humanoid;
|
||||
const [motors, constraint] = this.getRagdollPhysic(character);
|
||||
humanoid.ChangeState(Enum.HumanoidStateType.Physics);
|
||||
motors.forEach((motor) => (motor.Enabled = false));
|
||||
constraint.forEach((constraint) => (constraint.Enabled = true));
|
||||
takeNetworkOwner(character);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a Ragdoll.
|
||||
* @param character The player character to unragdoll.
|
||||
*/
|
||||
stopRagdoll(character: Model) {
|
||||
const humanoid = character.WaitForChild("Humanoid", 10) as Humanoid;
|
||||
const [motors, constraint] = this.getRagdollPhysic(character);
|
||||
humanoid.ChangeState(Enum.HumanoidStateType.Running);
|
||||
motors.forEach((motor) => (motor.Enabled = true));
|
||||
constraint.forEach((constraint) => (constraint.Enabled = false));
|
||||
releaseNetworkOwnership(character);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Events.
|
||||
/**
|
||||
* Create an Event which is fired when the player fall at an defined speed.
|
||||
* @param character The player character to check.
|
||||
* @param maximumSpeed The limit speed before the event fired.
|
||||
* @returns A signal which is fired when the player fall at the defined speed.
|
||||
*/
|
||||
getRagdollFallingEvent(character: Model, maximumSpeed: number = 38) {
|
||||
const humanoid = character.WaitForChild("Humanoid", 10) as Humanoid;
|
||||
const root = character.FindFirstChild("HumanoidRootPart") as Part;
|
||||
const event = new Signal();
|
||||
let fired = false;
|
||||
let beatEvent: RBXScriptConnection;
|
||||
humanoid.FreeFalling.Connect((active) => {
|
||||
if (active) {
|
||||
beatEvent = RunService.Heartbeat.Connect(() => {
|
||||
if (root.AssemblyLinearVelocity.Y < -maximumSpeed && !fired) {
|
||||
event.Fire();
|
||||
fired = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
beatEvent.Disconnect();
|
||||
fired = false;
|
||||
}
|
||||
});
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return a signal which fire when the player stop moving.
|
||||
* @param character The player character to check.
|
||||
* @param threshold The maximum distance (in studs) the player must travel in 0.3 seconds for the signal to be fired.
|
||||
* @returns The signal.
|
||||
*/
|
||||
waitMotionLess(character: Model, threshold: number = 0.1) {
|
||||
const root = character.FindFirstChild("HumanoidRootPart") as Part;
|
||||
|
||||
const signal = new Signal();
|
||||
let delta: number = -1;
|
||||
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;
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { OnStart, Service } from "@flamework/core";
|
||||
import { Players } from "@rbxts/services";
|
||||
import { GameplayServerEvents } from "server/networking";
|
||||
import { OnPlayerJoined } from "../modding/playerjoinservice";
|
||||
import { OnPlayerJoined } from "shared/modding/player_events";
|
||||
|
||||
/**
|
||||
* Service related to Sprinting.
|
||||
*/
|
||||
@Service()
|
||||
class SprintService implements OnStart, OnPlayerJoined {
|
||||
export class SprintService implements OnStart, OnPlayerJoined {
|
||||
// Sprint config.
|
||||
private default_stamina = 100;
|
||||
private base_sprint_speed = 16;
|
||||
private sprint_speed = 1.5;
|
||||
private animation_id = "rbxassetid://88297455683117";
|
||||
|
||||
// Cache
|
||||
// Data
|
||||
private is_running = new Set<number>();
|
||||
private is_sprinting = new Set<number>();
|
||||
private cant_run = new Set<number>();
|
||||
private stamina = new Map<number, NumberValue>();
|
||||
private timeout = new Map<number, number>();
|
||||
private runanim = new Animation();
|
||||
@@ -60,7 +61,7 @@ class SprintService implements OnStart, OnPlayerJoined {
|
||||
|
||||
// Change the sprint state with networking
|
||||
GameplayServerEvents.sprint.connect((player, enabled) => {
|
||||
if (this.timeout.has(player.UserId)) return;
|
||||
if (this.timeout.has(player.UserId) && !this.cant_run.has(player.UserId)) return;
|
||||
const character = player.Character;
|
||||
enabled ? this.is_running.add(player.UserId) : this.is_running.delete(player.UserId);
|
||||
if (character) {
|
||||
@@ -75,6 +76,7 @@ class SprintService implements OnStart, OnPlayerJoined {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPlayerJoined(player: Player): void {
|
||||
let running_connection: RBXScriptConnection;
|
||||
|
||||
@@ -119,4 +121,30 @@ class SprintService implements OnStart, OnPlayerJoined {
|
||||
// Clean up the connection when the character died.
|
||||
player.CharacterRemoving.Once(() => running_connection.Disconnect());
|
||||
}
|
||||
|
||||
setRunning(player: Player, enabled: boolean): void {
|
||||
const character = player.Character;
|
||||
if (!character) throw character;
|
||||
if (enabled) {
|
||||
this.is_running.add(player.UserId);
|
||||
} else {
|
||||
this.is_running.delete(player.UserId);
|
||||
this.is_sprinting.delete(player.UserId);
|
||||
}
|
||||
if (character) {
|
||||
const humanoid = character.FindFirstChildOfClass("Humanoid");
|
||||
if (humanoid) {
|
||||
if (enabled) {
|
||||
humanoid.WalkSpeed = this.base_sprint_speed * this.sprint_speed;
|
||||
} else {
|
||||
humanoid.WalkSpeed = this.base_sprint_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setRunningInterdiction(player: Player, enabled: boolean) {
|
||||
this.setRunning(player, !enabled);
|
||||
enabled ? this.cant_run.add(player.UserId) : this.cant_run.delete(player.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { Modding, OnStart, Service } from "@flamework/core";
|
||||
import { Players } from "@rbxts/services";
|
||||
|
||||
export interface OnPlayerJoined {
|
||||
onPlayerJoined(player: Player): void;
|
||||
}
|
||||
import { OnPlayerJoined } from "shared/modding/player_events";
|
||||
|
||||
@Service()
|
||||
class PlayerJoinService implements OnStart {
|
||||
@@ -1,16 +0,0 @@
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
3
src/shared/modding/player_events.ts
Normal file
3
src/shared/modding/player_events.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface OnPlayerJoined {
|
||||
onPlayerJoined(player: Player): void;
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
import { RunService } from "@rbxts/services";
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A class which help to ragdoll the player.
|
||||
*/
|
||||
export class RagdollHandler {
|
||||
private character: Model;
|
||||
private humanoid: Humanoid;
|
||||
private ragdollattachment: Attachment[] = [];
|
||||
private ragdollconstraint: BallSocketConstraint[] = [];
|
||||
private motors: Motor6D[] = [];
|
||||
private root: Part;
|
||||
private static ragdollHandlers: Map<Model, RagdollHandler> = new Map();
|
||||
|
||||
private getRagdollElements() {
|
||||
return [...this.ragdollattachment, ...this.ragdollconstraint];
|
||||
}
|
||||
|
||||
constructor(character: Model) {
|
||||
this.character = character;
|
||||
const humanoid = character.WaitForChild("Humanoid");
|
||||
const root = character.FindFirstChild("HumanoidRootPart");
|
||||
if (!humanoid?.IsA("Humanoid") || !root?.IsA("Part"))
|
||||
throw '"character" isn\'t a player Character. Failed to handle ragdoll.';
|
||||
this.humanoid = humanoid;
|
||||
this.root = root;
|
||||
this.prepareRagdoll();
|
||||
RagdollHandler.ragdollHandlers.set(character, this);
|
||||
}
|
||||
|
||||
private prepareRagdoll() {
|
||||
const children = this.character.GetDescendants();
|
||||
children.forEach((motor) => {
|
||||
if (motor.IsA("Motor6D")) {
|
||||
this.motors.push(motor);
|
||||
const ballsocketconstraint = new BallSocketConstraint(),
|
||||
attachment0 = new Attachment(),
|
||||
attachment1 = new Attachment();
|
||||
|
||||
attachment0.CFrame = motor.C0;
|
||||
attachment0.Parent = motor.Part0;
|
||||
attachment0.AddTag("Ragdoll");
|
||||
attachment0.Name = motor.Name;
|
||||
this.ragdollattachment.push(attachment0);
|
||||
|
||||
attachment1.CFrame = motor.C1;
|
||||
attachment1.Parent = motor.Part1;
|
||||
attachment1.AddTag("Ragdoll");
|
||||
attachment1.Name = motor.Name;
|
||||
this.ragdollattachment.push(attachment1);
|
||||
|
||||
ballsocketconstraint.Attachment0 = attachment0;
|
||||
ballsocketconstraint.Attachment1 = attachment1;
|
||||
ballsocketconstraint.Parent = motor.Parent;
|
||||
ballsocketconstraint.AddTag("Ragdoll");
|
||||
ballsocketconstraint.Enabled = false;
|
||||
this.ragdollconstraint.push(ballsocketconstraint);
|
||||
|
||||
if (motor.Name === "Left Hip") {
|
||||
attachment0.CFrame = attachment0.CFrame.add(new Vector3(0.5, 0, 0));
|
||||
attachment1.CFrame = attachment1.CFrame.add(new Vector3(0.5, 0, 0));
|
||||
} else if (motor.Name === "Right Hip") {
|
||||
attachment0.CFrame = attachment0.CFrame.sub(new Vector3(0.5, 0, 0));
|
||||
attachment1.CFrame = attachment1.CFrame.sub(new Vector3(0.5, 0, 0));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ragdoll the charactere linked to this class.
|
||||
*/
|
||||
ragdoll() {
|
||||
takeNetworkOwner(this.character);
|
||||
this.humanoid.ChangeState(Enum.HumanoidStateType.Physics);
|
||||
this.motors.forEach((motor) => (motor.Enabled = false));
|
||||
this.ragdollconstraint.forEach((constraint) => (constraint.Enabled = true));
|
||||
takeNetworkOwner(this.character);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the Ragdoll.
|
||||
*/
|
||||
stopRagdoll() {
|
||||
this.humanoid.ChangeState(Enum.HumanoidStateType.Running);
|
||||
this.motors.forEach((motor) => (motor.Enabled = true));
|
||||
this.ragdollconstraint.forEach((constraint) => (constraint.Enabled = false));
|
||||
releaseNetworkOwnership(this.character);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return a signal which fire when the player stop moving.
|
||||
* @param threshold The maximum distance (in studs) the player must travel in 0.3 seconds for the signal to be fired.
|
||||
* @returns The signal.
|
||||
*/
|
||||
waitMotionLess(threshold: number = 0.1) {
|
||||
const signal = new Signal();
|
||||
let delta: number = -1;
|
||||
task.spawn(() => {
|
||||
while (0 > delta || delta > threshold) {
|
||||
const lastPos = this.root.Position;
|
||||
task.wait(0.3);
|
||||
delta = this.root.Position.sub(lastPos).Magnitude;
|
||||
}
|
||||
signal.Fire();
|
||||
});
|
||||
return signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Event which is fired when the player fall at an defined speed.
|
||||
* @param maximumSpeed The limit speed before the event fired.
|
||||
* @returns A signal which is fired when the player fall at the defined speed.
|
||||
*/
|
||||
getRagdollFallingEvent(maximumSpeed: number = 38) {
|
||||
const event = new Signal();
|
||||
let fired = false;
|
||||
let beatEvent: RBXScriptConnection;
|
||||
this.humanoid.FreeFalling.Connect((active) => {
|
||||
if (active) {
|
||||
beatEvent = RunService.Heartbeat.Connect(() => {
|
||||
if (this.root.AssemblyLinearVelocity.Y < -maximumSpeed && !fired) {
|
||||
event.Fire();
|
||||
fired = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
beatEvent.Disconnect();
|
||||
fired = false;
|
||||
}
|
||||
});
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy all parts related to ragdoll.
|
||||
*/
|
||||
destroy() {
|
||||
this.getRagdollElements().forEach((element) => element.Destroy());
|
||||
}
|
||||
|
||||
static getHandlerByCharacter(character: Model, createifmissing?: false): RagdollHandler | undefined;
|
||||
static getHandlerByCharacter(character: Model, createifmissing: true): RagdollHandler;
|
||||
static getHandlerByCharacter(character: Model, createifmissing: boolean = false) {
|
||||
return createifmissing
|
||||
? (this.ragdollHandlers.get(character) ?? new this(character))
|
||||
: this.ragdollHandlers.get(character);
|
||||
}
|
||||
}
|
||||
27
src/shared/utils/ownership.ts
Normal file
27
src/shared/utils/ownership.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user