Fix the math around sleeping to be simpler

This commit is contained in:
Andrew Miner
2025-09-04 15:58:04 -06:00
parent fb58946028
commit 6ebc151742
2 changed files with 99 additions and 40 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ server.workbench3taxrate "0"
// Animal populations should reflect realistic proportion of predators to prey. They should also // Animal populations should reflect realistic proportion of predators to prey. They should also
// appear at about twice the natural population density to provide a good balance between realism // appear at about twice the natural population density to provide a good balance between realism
// and fun gameplay. // and fun gameplay. The totals provided are assuming a 9sqkm map (3000 world size).
bear.population "0.33" // per sqkm, 3 total bear.population "0.33" // per sqkm, 3 total
boar.population "2.77" // per sqkm, 25 total boar.population "2.77" // per sqkm, 25 total
+95 -36
View File
@@ -14,36 +14,46 @@ namespace Oxide.Plugins {
[Description("An accelerated way to sleep through the night.")] [Description("An accelerated way to sleep through the night.")]
public class GoodNightsSleep : RustPlugin { public class GoodNightsSleep : RustPlugin {
// Constants ///////////////////////////////////////////////////////////
// the earliest time you can go to sleep // the earliest time you can go to sleep
public const float BED_TIME = 22f; public const float BED_TIME = 20f;
// the amount you heal per second from sleeping // the amount you heal per second from sleeping
public const float HEAL_RATIO = 1f; 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 // the amount of wall-clock time (in seconds) between sleep steps
public const float STEP_SIZE = 1f; public const float STEP_SIZE = 1f;
// the real-world time sleeping should last (in seconds)
public const float SLEEP_TIME = 30f;
// the time you wake up in the morning // the time you wake up in the morning
public const float WAKE_TIME = 7f; public const float WAKE_TIME = 8f;
// the prefab sound to play when someone yawns
public const string YAWN_SOUND = "assets/bundled/prefabs/fx/player/yawn.prefab";
// Helper Class ////////////////////////////////////////////////////////
// a an actual instance of sleeping through the night // a an actual instance of sleeping through the night
private class SleepSession { private class SleepSession {
// the player doing the sleeping
public BasePlayer player = null;
// in-game time when we last completed a step // in-game time when we last completed a step
private float lastStepAt = 0f; private float lastStepAt = 0f;
// callback for when the session is over // callback for when the session is over
private Action onComplete = null; private Action onComplete = null;
// the player doing the sleeping
private BasePlayer player = null;
// the plugin itself // the plugin itself
private RustPlugin plugin = null; private RustPlugin plugin = null;
// the in-game time when sleep began
private float startedSleepingAt = 0;
// the timer controlling the steps // the timer controlling the steps
private Timer timer = null; private Timer timer = null;
@@ -58,9 +68,8 @@ namespace Oxide.Plugins {
this.Abort(); this.Abort();
this.timer = timer; this.timer = timer;
this.lastStepAt = this.GetTime();
this.player.StartSleeping(); this.player.StartSleeping();
this.player.ChatMessage("You slowly start to drift off to sleep."); this.startedSleepingAt = this.GetTime();
} }
public void KeepSleeping() { public void KeepSleeping() {
@@ -74,16 +83,13 @@ namespace Oxide.Plugins {
return; return;
} }
var now = this.GetTime(); var totalHours = (24f - BED_TIME) + WAKE_TIME;
var sleepDuration = (now - this.lastStepAt) * 60 * 60; // in seconds var totalSteps = SLEEP_TIME / STEP_SIZE;
this.player.Heal(sleepDuration * GoodNightsSleep.HEAL_RATIO); var incrementHours = totalHours / totalSteps;
var incrementSeconds = incrementHours * 60 * 60;
this.lastStepAt = now; this.player.Heal(incrementSeconds * GoodNightsSleep.HEAL_RATIO);
this.SetTime( this.SetTime((this.GetTime() + incrementHours) % 24f);
this.GetTime() + (
GoodNightsSleep.STEP_SIZE * GoodNightsSleep.SLEEP_SPEED / 60 / 60
)
);
} }
public void Abort() { public void Abort() {
@@ -118,18 +124,31 @@ namespace Oxide.Plugins {
private bool ShouldWake() { private bool ShouldWake() {
if (this.ShouldAbort()) return false; if (this.ShouldAbort()) return false;
if (this.GetTime() > GoodNightsSleep.WAKE_TIME) return true;
var now = this.GetTime();
var outsideSleepHours = (
(now > GoodNightsSleep.WAKE_TIME) &&
(now < GoodNightsSleep.BED_TIME)
);
if (outsideSleepHours) return true;
return false; return false;
} }
}; };
// Member Variables ////////////////////////////////////////////////////
// the specific instance of sleeping through the night // the specific instance of sleeping through the night
private SleepSession session = null; private SleepSession session = null;
// the last time each player got a yawn reminder
private int lastYawnDay = -1;
// Hooks /////////////////////////////////////////////////////////////////////////////////// // Hooks ///////////////////////////////////////////////////////////////////////////////////
void OnServerInitialized() { void OnServerInitialized() {
Puts("Good Night's Sleep is active"); Puts("Good Night's Sleep is active");
timer.Every(60f, () => this.CheckSleepiness());
} }
// Commands //////////////////////////////////////////////////////////////////////////////// // Commands ////////////////////////////////////////////////////////////////////////////////
@@ -139,14 +158,26 @@ namespace Oxide.Plugins {
if (!this.IsSleepy(player)) return; if (!this.IsSleepy(player)) return;
this.session = new SleepSession(player, () => this.OnSleepSessionEnded()); this.session = new SleepSession(player, () => this.OnSleepSessionEnded());
player.ChatMessage("You slowly start to drift off to sleep.");
timer.Once(1f, () => {
this.session.Begin(timer.Every(STEP_SIZE, () => this.session.KeepSleeping())); this.session.Begin(timer.Every(STEP_SIZE, () => this.session.KeepSleeping()));
});
} }
// Helper Methods ////////////////////////////////////////////////////////////////////////// // Helper Methods //////////////////////////////////////////////////////////////////////////
private void OnSleepSessionEnded() { private void CheckSleepiness() {
this.session.Abort(); var today = this.GetDay();
this.session = null; if (this.lastYawnDay == today) return;
var now = this.GetTime();
if ((now > GoodNightsSleep.BED_TIME) && (now < GoodNightsSleep.BED_TIME + 1f)) {
this.lastYawnDay = today;
foreach (var player in BasePlayer.activePlayerList) {
Effect.server.Run(GoodNightsSleep.YAWN_SOUND, player.transform.position);
}
}
} }
private bool IsSleepy(BasePlayer player) { private bool IsSleepy(BasePlayer player) {
@@ -172,7 +203,7 @@ namespace Oxide.Plugins {
var bag = this.FindNearbySleepingBag(player); var bag = this.FindNearbySleepingBag(player);
if (bag == null) { if (bag == null) {
player.ChatMessage("There's nowhere to sleep nearby."); player.ChatMessage("You'd be a lot more comfy in your bed.");
return false; return false;
} }
@@ -185,17 +216,23 @@ namespace Oxide.Plugins {
} }
private SleepingBag FindNearbySleepingBag(BasePlayer player) { private SleepingBag FindNearbySleepingBag(BasePlayer player) {
foreach (var bag in UnityEngine.Object.FindObjectsOfType<SleepingBag>()) { // move up a bit so we can look down
if (bag == null) continue; var position = player.transform.position + Vector3.up * 0.1f;
if (bag.deployerUserID != player.userID) continue;
RaycastHit hit;
Physics.Raycast(position, Vector3.down, out hit, 2f);
if (hit.collider == null) return null;
var bag = hit.collider.GetComponentInParent<SleepingBag>();
if (bag == null) return null;
if (bag.deployerUserID != player.userID) return null;
var distance = Vector3.Distance(player.transform.position, bag.transform.position);
if (distance < 2f) {
return bag; return bag;
} }
}
return null; private int GetDay() {
return TOD_Sky.Instance.Cycle.Day;
} }
private float GetTime() { private float GetTime() {
@@ -206,8 +243,7 @@ namespace Oxide.Plugins {
return ( return (
this.IsOnFoundation(bag) && this.IsOnFoundation(bag) &&
this.IsUnderRoof(bag) && this.IsUnderRoof(bag) &&
this.IsNearToolchest(bag) && this.IsNearToolchest(bag)
true
); );
} }
@@ -216,17 +252,23 @@ namespace Oxide.Plugins {
Vis.Entities(bag.transform.position, 25f, privlidges, LayerMask.Deployed); Vis.Entities(bag.transform.position, 25f, privlidges, LayerMask.Deployed);
var result = privlidges.Count > 0; var result = privlidges.Count > 0;
Pool.FreeList(ref privlidges); Pool.FreeList(ref privlidges);
if (!result) {
Puts("Sleeping bag isn't close to a toolchest");
}
return result; return result;
} }
private bool IsOnFoundation(SleepingBag bag) { private bool IsOnFoundation(SleepingBag bag) {
// get a bit off the floor so we can look down // get a bit off the floor so we can look down
var position = bag.transform.position + Vector3.up * 0.1f; var position = bag.transform.position + Vector3.up * 0.5f;
// check that there's some kind of player-placed block under the sleeping bag // check that there's some kind of player-placed block under the sleeping bag
RaycastHit hit; RaycastHit hit;
Physics.Raycast(position, Vector3.down, out hit, 2f); Physics.Raycast(position, Vector3.down, out hit, 2f);
if (hit.collider.GetComponentInParent<BuildingBlock>() == null) return false; if (hit.collider.GetComponentInParent<BuildingBlock>() == null) {
Puts("Sleeping bag is not on a foundation");
return false;
}
return true; return true;
} }
@@ -234,7 +276,24 @@ namespace Oxide.Plugins {
private bool IsUnderRoof(SleepingBag bag) { private bool IsUnderRoof(SleepingBag bag) {
RaycastHit hit; RaycastHit hit;
var position = bag.transform.position; var position = bag.transform.position;
return Physics.Raycast(position, Vector3.up, out hit, LayerMask.Construction); if (!Physics.Raycast(position, Vector3.up, out hit, 10f, LayerMask.Construction)) {
Puts("Sleeping Bag is not under a roof");
return false;
}
return true;
}
private void OnSleepSessionEnded() {
if (this.session == null) return;
var player = this.session.player;
this.session.Abort();
this.session = null;
timer.Once(1f, () => {
player.ChatMessage("You awake feeling rested and refreshed.");
});
} }
} }
} }