Add current state of development
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
using Facepunch;
|
||||
using Oxide.Core.Plugins;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
class LayerMask {
|
||||
public const int Deployed = 1 << 8;
|
||||
public const int Construction = 1 << 21;
|
||||
}
|
||||
|
||||
namespace Oxide.Plugins {
|
||||
|
||||
[Info("GoodNightsSleep", "TungstonMiner", "0.1.0")]
|
||||
[Description("An accelerated way to sleep through the night.")]
|
||||
public class GoodNightsSleep : RustPlugin {
|
||||
|
||||
// the earliest time you can go to sleep
|
||||
public const float BED_TIME = 22f;
|
||||
|
||||
// the amount you heal per second from sleeping
|
||||
public const float HEAL_RATIO = 1f;
|
||||
|
||||
// the ratio at which time passes (e.g., 10 means 10x usual speed)
|
||||
public const float SLEEP_SPEED = 20;
|
||||
|
||||
// the amount of wall-clock time (in seconds) between sleep steps
|
||||
public const float STEP_SIZE = 1f;
|
||||
|
||||
// the time you wake up in the morning
|
||||
public const float WAKE_TIME = 7f;
|
||||
|
||||
// a an actual instance of sleeping through the night
|
||||
private class SleepSession {
|
||||
|
||||
// in-game time when we last completed a step
|
||||
private float lastStepAt = 0f;
|
||||
|
||||
// callback for when the session is over
|
||||
private Action onComplete = null;
|
||||
|
||||
// the player doing the sleeping
|
||||
private BasePlayer player = null;
|
||||
|
||||
// the plugin itself
|
||||
private RustPlugin plugin = null;
|
||||
|
||||
// the timer controlling the steps
|
||||
private Timer timer = null;
|
||||
|
||||
public SleepSession(BasePlayer player, Action onComplete) {
|
||||
this.onComplete = onComplete;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
// Lifecycle Methods ///////////////////////////////////////////////
|
||||
|
||||
public void Begin(Timer timer) {
|
||||
this.Abort();
|
||||
|
||||
this.timer = timer;
|
||||
this.lastStepAt = this.GetTime();
|
||||
this.player.StartSleeping();
|
||||
this.player.ChatMessage("You slowly start to drift off to sleep.");
|
||||
}
|
||||
|
||||
public void KeepSleeping() {
|
||||
if (this.ShouldAbort()) {
|
||||
this.Abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ShouldWake()) {
|
||||
this.End();
|
||||
return;
|
||||
}
|
||||
|
||||
var now = this.GetTime();
|
||||
var sleepDuration = (now - this.lastStepAt) * 60 * 60; // in seconds
|
||||
this.player.Heal(sleepDuration * GoodNightsSleep.HEAL_RATIO);
|
||||
|
||||
this.lastStepAt = now;
|
||||
this.SetTime(
|
||||
this.GetTime() + (
|
||||
GoodNightsSleep.STEP_SIZE * GoodNightsSleep.SLEEP_SPEED / 60 / 60
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void Abort() {
|
||||
if (this.timer == null) return;
|
||||
|
||||
this.timer.Destroy();
|
||||
this.timer = null;
|
||||
this.onComplete?.Invoke();
|
||||
}
|
||||
|
||||
public void End() {
|
||||
this.player.EndSleeping();
|
||||
this.Abort();
|
||||
}
|
||||
|
||||
// Helper Methods //////////////////////////////////////////////////
|
||||
|
||||
private float GetTime() {
|
||||
return TOD_Sky.Instance.Cycle.Hour;
|
||||
}
|
||||
|
||||
private void SetTime(float time) {
|
||||
ConsoleSystem.Run(ConsoleSystem.Option.Server, $"env.time {time}");
|
||||
}
|
||||
|
||||
private bool ShouldAbort() {
|
||||
if (!this.player.IsConnected) return true;
|
||||
if (this.player.IsDead()) return true;
|
||||
if (!this.player.IsSleeping()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ShouldWake() {
|
||||
if (this.ShouldAbort()) return false;
|
||||
if (this.GetTime() > GoodNightsSleep.WAKE_TIME) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// the specific instance of sleeping through the night
|
||||
private SleepSession session = null;
|
||||
|
||||
// Hooks ///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnServerInitialized() {
|
||||
Puts("Good Night's Sleep is active");
|
||||
}
|
||||
|
||||
// Commands ////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[ChatCommand("sleep")]
|
||||
private void SleepCommand(BasePlayer player, string command, string[] args) {
|
||||
if (!this.IsSleepy(player)) return;
|
||||
|
||||
this.session = new SleepSession(player, () => this.OnSleepSessionEnded());
|
||||
this.session.Begin(timer.Every(STEP_SIZE, () => this.session.KeepSleeping()));
|
||||
}
|
||||
|
||||
// Helper Methods //////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void OnSleepSessionEnded() {
|
||||
this.session.Abort();
|
||||
this.session = null;
|
||||
}
|
||||
|
||||
private bool IsSleepy(BasePlayer player) {
|
||||
var now = this.GetTime();
|
||||
|
||||
if (!player) return false;
|
||||
if (!player.IsConnected) return false;
|
||||
|
||||
if (player.IsDead()) {
|
||||
player.ChatMessage("You're too dead to sleep.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((now > GoodNightsSleep.WAKE_TIME) && (now < GoodNightsSleep.BED_TIME)) {
|
||||
player.ChatMessage("You aren't feeling sleepy yet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.IsSleeping() || (this.session != null)) {
|
||||
player.ChatMessage("You are already asleep.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var bag = this.FindNearbySleepingBag(player);
|
||||
if (bag == null) {
|
||||
player.ChatMessage("There's nowhere to sleep nearby.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.IsInBase(bag)) {
|
||||
player.ChatMessage("You're too uneasy to sleep here.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private SleepingBag FindNearbySleepingBag(BasePlayer player) {
|
||||
foreach (var bag in UnityEngine.Object.FindObjectsOfType<SleepingBag>()) {
|
||||
if (bag == null) continue;
|
||||
if (bag.deployerUserID != player.userID) continue;
|
||||
|
||||
var distance = Vector3.Distance(player.transform.position, bag.transform.position);
|
||||
if (distance < 2f) {
|
||||
return bag;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private float GetTime() {
|
||||
return TOD_Sky.Instance.Cycle.Hour;
|
||||
}
|
||||
|
||||
private bool IsInBase(SleepingBag bag) {
|
||||
return (
|
||||
this.IsOnFoundation(bag) &&
|
||||
this.IsUnderRoof(bag) &&
|
||||
this.IsNearToolchest(bag) &&
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private bool IsNearToolchest(SleepingBag bag) {
|
||||
var privlidges = Pool.GetList<BuildingPrivlidge>();
|
||||
Vis.Entities(bag.transform.position, 25f, privlidges, LayerMask.Deployed);
|
||||
var result = privlidges.Count > 0;
|
||||
Pool.FreeList(ref privlidges);
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsOnFoundation(SleepingBag bag) {
|
||||
// get a bit off the floor so we can look down
|
||||
var position = bag.transform.position + Vector3.up * 0.1f;
|
||||
|
||||
// check that there's some kind of player-placed block under the sleeping bag
|
||||
RaycastHit hit;
|
||||
Physics.Raycast(position, Vector3.down, out hit, 2f);
|
||||
if (hit.collider.GetComponentInParent<BuildingBlock>() == null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsUnderRoof(SleepingBag bag) {
|
||||
RaycastHit hit;
|
||||
var position = bag.transform.position;
|
||||
return Physics.Raycast(position, Vector3.up, out hit, LayerMask.Construction);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user