🔥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";
|
import { Flamework } from "@flamework/core";
|
||||||
|
|
||||||
// Flamework.addPaths("src/server/components");
|
Flamework.addPaths("src/server/components");
|
||||||
Flamework.addPaths("src/server/services");
|
Flamework.addPaths("src/server/services");
|
||||||
// Flamework.addPaths("src/shared/components");
|
// Flamework.addPaths("src/shared/components");
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import { OnStart, Service } from "@flamework/core";
|
import { OnStart, Service } from "@flamework/core";
|
||||||
import { Players, TextChatService } from "@rbxts/services";
|
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
|
* Setup Chat Command
|
||||||
*/
|
*/
|
||||||
@Service()
|
@Service()
|
||||||
class CommandsService implements OnStart {
|
class CommandsService implements OnStart {
|
||||||
|
constructor(private readonly ragdollService: RagdollService) {}
|
||||||
onStart(): void {
|
onStart(): void {
|
||||||
const ragdollCommand = new TextChatCommand();
|
const ragdollCommand = new TextChatCommand();
|
||||||
ragdollCommand.Parent = TextChatService.WaitForChild("TextChatCommands");
|
ragdollCommand.Parent = TextChatService.WaitForChild("TextChatCommands");
|
||||||
ragdollCommand.PrimaryAlias = "/ragdoll";
|
ragdollCommand.PrimaryAlias = "/ragdoll";
|
||||||
ragdollCommand.Triggered.Connect((TextSource) => {
|
ragdollCommand.Triggered.Connect((TextSource) => {
|
||||||
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
||||||
const handler = RagdollHandler.getHandlerByCharacter(player!.Character!, true);
|
this.ragdollService.ragdoll(player?.Character!);
|
||||||
handler.ragdoll();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const unragdollCommand = new TextChatCommand();
|
const unragdollCommand = new TextChatCommand();
|
||||||
@@ -22,8 +23,7 @@ class CommandsService implements OnStart {
|
|||||||
unragdollCommand.PrimaryAlias = "/unragdoll";
|
unragdollCommand.PrimaryAlias = "/unragdoll";
|
||||||
unragdollCommand.Triggered.Connect((TextSource) => {
|
unragdollCommand.Triggered.Connect((TextSource) => {
|
||||||
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
const player = Players.GetPlayerByUserId(TextSource.UserId);
|
||||||
const handler = RagdollHandler.getHandlerByCharacter(player!.Character!, true);
|
this.ragdollService.ragdoll(player?.Character!);
|
||||||
handler.stopRagdoll();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const takeownerCommand = new TextChatCommand();
|
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 { Modding, OnStart, Service } from "@flamework/core";
|
||||||
import { RagdollHandler } from "shared/ownership";
|
import { RunService } from "@rbxts/services";
|
||||||
import { OnPlayerJoined } from "../modding/playerjoinservice";
|
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 related to Ragdoll.
|
||||||
*/
|
*/
|
||||||
@Service()
|
@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 {
|
onPlayerJoined(player: Player): void {
|
||||||
player.CharacterAdded.Connect((character) => {
|
player.CharacterAdded.Connect((character) => {
|
||||||
const ragdollhandler = new RagdollHandler(character);
|
this.prepareRagdoll(character);
|
||||||
ragdollhandler.getRagdollFallingEvent().Connect(() => {
|
this.getRagdollFallingEvent(character).Connect(() => {
|
||||||
ragdollhandler.ragdoll();
|
this.ragdoll(character);
|
||||||
ragdollhandler.waitMotionLess().Wait();
|
this.sprintService.setRunningInterdiction(player, true);
|
||||||
ragdollhandler.stopRagdoll();
|
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 { OnStart, Service } from "@flamework/core";
|
||||||
import { Players } from "@rbxts/services";
|
import { Players } from "@rbxts/services";
|
||||||
import { GameplayServerEvents } from "server/networking";
|
import { GameplayServerEvents } from "server/networking";
|
||||||
import { OnPlayerJoined } from "../modding/playerjoinservice";
|
import { OnPlayerJoined } from "shared/modding/player_events";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service related to Sprinting.
|
* Service related to Sprinting.
|
||||||
*/
|
*/
|
||||||
@Service()
|
@Service()
|
||||||
class SprintService implements OnStart, OnPlayerJoined {
|
export class SprintService implements OnStart, OnPlayerJoined {
|
||||||
// Sprint config.
|
// Sprint config.
|
||||||
private default_stamina = 100;
|
private default_stamina = 100;
|
||||||
private base_sprint_speed = 16;
|
private base_sprint_speed = 16;
|
||||||
private sprint_speed = 1.5;
|
private sprint_speed = 1.5;
|
||||||
private animation_id = "rbxassetid://88297455683117";
|
private animation_id = "rbxassetid://88297455683117";
|
||||||
|
|
||||||
// Cache
|
// Data
|
||||||
private is_running = new Set<number>();
|
private is_running = new Set<number>();
|
||||||
private is_sprinting = new Set<number>();
|
private is_sprinting = new Set<number>();
|
||||||
|
private cant_run = new Set<number>();
|
||||||
private stamina = new Map<number, NumberValue>();
|
private stamina = new Map<number, NumberValue>();
|
||||||
private timeout = new Map<number, number>();
|
private timeout = new Map<number, number>();
|
||||||
private runanim = new Animation();
|
private runanim = new Animation();
|
||||||
@@ -60,7 +61,7 @@ class SprintService implements OnStart, OnPlayerJoined {
|
|||||||
|
|
||||||
// Change the sprint state with networking
|
// Change the sprint state with networking
|
||||||
GameplayServerEvents.sprint.connect((player, enabled) => {
|
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;
|
const character = player.Character;
|
||||||
enabled ? this.is_running.add(player.UserId) : this.is_running.delete(player.UserId);
|
enabled ? this.is_running.add(player.UserId) : this.is_running.delete(player.UserId);
|
||||||
if (character) {
|
if (character) {
|
||||||
@@ -75,6 +76,7 @@ class SprintService implements OnStart, OnPlayerJoined {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onPlayerJoined(player: Player): void {
|
onPlayerJoined(player: Player): void {
|
||||||
let running_connection: RBXScriptConnection;
|
let running_connection: RBXScriptConnection;
|
||||||
|
|
||||||
@@ -119,4 +121,30 @@ class SprintService implements OnStart, OnPlayerJoined {
|
|||||||
// Clean up the connection when the character died.
|
// Clean up the connection when the character died.
|
||||||
player.CharacterRemoving.Once(() => running_connection.Disconnect());
|
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 { Modding, OnStart, Service } from "@flamework/core";
|
||||||
import { Players } from "@rbxts/services";
|
import { Players } from "@rbxts/services";
|
||||||
|
import { OnPlayerJoined } from "shared/modding/player_events";
|
||||||
export interface OnPlayerJoined {
|
|
||||||
onPlayerJoined(player: Player): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
class PlayerJoinService implements OnStart {
|
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