# Midnight Thoughts 1.5 — Forge 1.20.1
**Theme: control, compatibility & integrations.**
Version 1.5 makes the mod fully data-driven, modular, and scriptable. Retune every subsystem from config, toggle any part off, and react to (or drive) the mod from KubeJS — while it stays completely self-sufficient when KubeJS isn't installed.
---
## Redesigned sleep overlay
The sleep overlay was rebuilt from the ground up:
- **New layout** — the moon-phase image now sits **above** the text, centered and more compact, instead of crowding it.
- **Vignette + star dust** — a soft vignette frames the screen and gentle drifting "star dust" animates in the background for atmosphere.
- **Crossfade between slides** — slides now fade smoothly into one another instead of hard-cutting.
- **Slide progress indicator** — a subtle progress bar shows how far along the current slide is.
- **Auto-fitting text** — text scales to fit cleanly regardless of length, so long facts and short thoughts both look right.
- **Configurable fades & opacity** — `fadeInDurationMs`, `fadeOutDurationMs`, `overlayOpacity`, `textOpacity` and `imageOpacity` are all in config and synced from the server.
New toggles: `enableStarDust`, `showSlideProgress`.
---
## Data-driven comfort
Comfort used to sum hardcoded `+1 / -1` values. Now the weights and thresholds live in `ComfortSettings`:
- **`weights`** per category — `lighting`, `carpet`, `furniture`, `decoration`, `structure` (positive) and `macabre`, `hostile`, `dark` (negative). Defaults keep the old behaviour, but every value is tunable.
- **Threshold semantics** — a rule fires when `comfortLevel <= threshold`.
- `nightmareThreshold` (default **-1**) — nightmares at `comfort <= -1`.
- `sleepBlockThreshold` (default **-2**) — sleep blocked at `comfort <= -2`.
- Which blocks belong to a category is still driven by **tags** (datapack) — unchanged.
- Comfort recalculates on `/reload` (cache is cleared).
---
## Flexible `/reload`
`/midnightthoughts reload [config | achievements | content | all]` (permission level 2):
- **`config`** — re-read the config and re-broadcast it to all clients.
- **`achievements`** — reload achievement definitions and re-broadcast.
- **`content`** — reload user content (facts / slides) and re-broadcast.
- **`all`** — everything at once.
Config changes apply **in place**, so every live system reads the fresh values instantly — no restart. Already-applied well-rested buffs aren't recalculated (they live in NBT); new applications use the new values.
---
## Admin commands
- `/midnightthoughts comfort` — debug the comfort around you: level, which categories were found (`●` / `○`), and each category's contribution.
- `/midnightthoughts wellrested grant <targets> <1-5>` — grant well-rested manually.
- `/midnightthoughts wellrested clear <targets>` — remove it.
All require permission level 2.
---
## Full modularity
Master toggles for every subsystem, so a pack can disable any part without touching code:
- `wellRested.enabled`
- `comfort.enabled`, `comfort.nightmareEnabled`, `comfort.sleepBlockEnabled`
- `mvp.enabled`
- `sleepOverlay.enableOverlay`, `enableImage`, `enableDailySummaryScreen`, `useFactsApi`
- `server.resetPhantomTimerForNonSleepers`
---
## Java Event API
A pure-Java API on the Forge event bus (`mt.api.event`), always available and independent of any third-party mod. Any mod can subscribe with plain Java.
| Event | Fields | Notes |
|-------|--------|-------|
| `WellRestedAppliedEvent` | `player`, `level`, `isMvp`, `durationTicks` | Fired on normal grant and on MVP grant |
| `WellRestedExpiredEvent` | `player` | Natural expiry, death, manual clear, or disabled-branch |
| `NightmareEvent` | `player`, `comfortLevel` | Fired when a player sleeps into a nightmare |
| `MvpDeterminedEvent` | `mvp` | Fired at day rollover when an MVP is chosen |
| `ComfortCalculatedEvent` | `player`, `level`, `setLevel(int)` | **Overridable** — listeners can rewrite the computed comfort |
`ComfortCalculatedEvent` is posted before the result is cached, so an override changes the value everyone downstream sees (nightmare / sleep-block checks included).
---
## KubeJS integration (optional)
Layered design: the Java Event API above is always on; the KubeJS bridge is a **soft/optional dependency**. Without KubeJS the mod is fully self-sufficient — no errors, no crash.
### Events (mod → script)
Event group: **`MidnightThoughtsEvents`** (server events).
```js
MidnightThoughtsEvents.wellRestedApplied(event => {
// event.player, event.level, event.mvp (boolean), event.durationTicks
let mins = Math.max(1, Math.floor(event.durationTicks / 1200))
event.player.tell('Well-rested ' + event.level + ' for ' + mins + ' min')
})
MidnightThoughtsEvents.wellRestedExpired(event => {
// event.player
event.player.tell('The rested feeling fades...')
})
MidnightThoughtsEvents.nightmare(event => {
// event.player, event.comfortLevel
event.player.runCommandSilent('effect give @s minecraft:nausea 12 0 true')
})
MidnightThoughtsEvents.mvpDetermined(event => {
// event.mvp = the MVP ServerPlayer
event.mvp.server.tell(event.mvp.username + ' is the MVP of the day!')
})
MidnightThoughtsEvents.comfortCalculated(event => {
// event.player, event.level, event.setLevel(int) — override the result
if (event.player.block.canSeeSky) {
event.setLevel(event.level + 1)
}
})
```
### Bindings (script → mod)
Global object: **`MidnightThoughts`**.
| Binding | Signature | Effect |
|---------|-----------|--------|
| `grantWellRested` | `(player, level)` | Apply well-rested at the given level |
| `grantMvp` | `(player)` | Apply the MVP-level well-rested |
| `clearWellRested` | `(player)` | Remove the buff **and** fire `WellRestedExpiredEvent` |
| `hasWellRested` | `(player) → boolean` | Is the buff active |
| `getLevel` | `(player) → int` | Current well-rested level (0 if none) |
| `getComfort` | `(player) → int` | Raw comfort at the player (no event fired, recursion-safe) |
```js
PlayerEvents.inventoryChanged(event => {
if (event.item.id == 'minecraft:nether_star') {
MidnightThoughts.grantWellRested(event.player, 5)
event.player.tell('§dThe star fills you with restful energy...')
}
})
```
`getComfort` reads raw comfort without posting `ComfortCalculatedEvent`, so calling it from a `comfortCalculated` handler cannot recurse. `grant*` / `clearWellRested` respect `wellRested.enabled`.
---
## Catalogue branding
Custom **icon**, **banner**, **background** and links (website) in the mod list when Catalogue is installed.
---
## Fixed / Improved
- `mvp.enabled` now actually gates MVP selection (was a dead flag).
- Config changes apply in place on reload — live systems pick them up instantly.
- Comfort cache is cleared on reload so new weights/thresholds take effect immediately.
- Overlay rendering fixes: no more content dimming or alpha build-up; fades and opacity behave correctly.
---
## Performance
Profiled with spark on a dedicated server (multiplayer, KubeJS loaded, active sleeping / nightmares): **~0.13% of server-tick time**, a flat **20 TPS**, MSPT 4–14 ms against a 50 ms budget.
**Requires Forge 1.20.1.**
---
Full documentation will be available soon on the Wiki.
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }