Add current state of development

This commit is contained in:
Andrew Miner
2025-09-02 21:23:59 -06:00
parent cd2fd3bea8
commit fb58946028
5 changed files with 609 additions and 0 deletions
+240
View File
@@ -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);
}
}
}