From 1a8cefd32502872e2eca61ac2312d3d0cbe44e4d Mon Sep 17 00:00:00 2001 From: cha0s Date: Thu, 4 Jul 2024 15:17:33 -0500 Subject: [PATCH] feat: day/night --- app/constants.js | 2 ++ app/create-ecs.js | 1 + app/create-homestead.js | 2 ++ app/create-house.js | 1 + app/ecs-components/time.js | 23 +++++++++++++++ app/ecs-systems/pass-time.js | 15 ++++++++++ app/react-components/ecs.jsx | 57 +++++++++++++++++++++++++++++++++++- package-lock.json | 1 + package.json | 1 + 9 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 app/ecs-components/time.js create mode 100644 app/ecs-systems/pass-time.js diff --git a/app/constants.js b/app/constants.js index aeed46e..f9fa32f 100644 --- a/app/constants.js +++ b/app/constants.js @@ -2,6 +2,8 @@ export const CLIENT_LATENCY = 0; export const CLIENT_PREDICTION = true; +export const IRL_MINUTES_PER_GAME_DAY = 20; + export const RESOLUTION = { x: 800, y: 450, diff --git a/app/create-ecs.js b/app/create-ecs.js index 417653d..712b259 100644 --- a/app/create-ecs.js +++ b/app/create-ecs.js @@ -4,6 +4,7 @@ import Systems from '@/ecs-systems/index.js'; export default function createEcs(Ecs) { const ecs = new Ecs({Components, Systems}); const defaultSystems = [ + 'PassTime', 'Attract', 'ResetForces', 'ApplyControlMovement', diff --git a/app/create-homestead.js b/app/create-homestead.js index 5a61079..d1275c4 100644 --- a/app/create-homestead.js +++ b/app/create-homestead.js @@ -16,6 +16,7 @@ export default async function createHomestead(Ecs) { } ], }, + Time: {}, Water: {water: {}}, }); await ecs.create({ @@ -49,6 +50,7 @@ export default async function createHomestead(Ecs) { anchorY: 0.8, source: '/assets/shit-shack/shit-shack.json', }, + Ticking: {}, VisibleAabb: {}, }); return ecs; diff --git a/app/create-house.js b/app/create-house.js index 042cde6..b926113 100644 --- a/app/create-house.js +++ b/app/create-house.js @@ -36,6 +36,7 @@ export default async function createHouse(Ecs) { x: 72, y: 320, }, + Ticking: {}, }); return ecs; } diff --git a/app/ecs-components/time.js b/app/ecs-components/time.js new file mode 100644 index 0000000..d2001a1 --- /dev/null +++ b/app/ecs-components/time.js @@ -0,0 +1,23 @@ +import {IRL_MINUTES_PER_GAME_DAY} from '@/constants'; +import Component from '@/ecs/component.js'; + +const realSecondsPerGameDay = 60 * IRL_MINUTES_PER_GAME_DAY; +const realSecondsPerGameHour = realSecondsPerGameDay / 24; +const realSecondsPerGameMinute = realSecondsPerGameHour / 60; + +export default class Time extends Component { + instanceFromSchema() { + return class TimeInstance extends super.instanceFromSchema() { + static gameDayLengthInRealSeconds = 24 * realSecondsPerGameHour; + get hour() { + return this.$$irlSeconds / realSecondsPerGameHour; + } + get minute() { + return (this.$$irlSeconds % realSecondsPerGameHour) / realSecondsPerGameMinute; + } + }; + } + static properties = { + irlSeconds: {defaultValue: 18 * realSecondsPerGameHour, type: 'uint16'}, + }; +} diff --git a/app/ecs-systems/pass-time.js b/app/ecs-systems/pass-time.js new file mode 100644 index 0000000..416e5ad --- /dev/null +++ b/app/ecs-systems/pass-time.js @@ -0,0 +1,15 @@ +import {System} from '@/ecs/index.js'; + +export default class PassTime extends System { + + tick(elapsed) { + const {Time} = this.ecs.get(1); + if (Time) { + Time.irlSeconds += elapsed; + while (Time.irlSeconds >= Time.constructor.gameDayLengthInRealSeconds) { + Time.irlSeconds -= Time.constructor.gameDayLengthInRealSeconds; + } + } + } + +} diff --git a/app/react-components/ecs.jsx b/app/react-components/ecs.jsx index 5408531..c350cb0 100644 --- a/app/react-components/ecs.jsx +++ b/app/react-components/ecs.jsx @@ -1,5 +1,5 @@ import {Container} from '@pixi/react'; -import {useState} from 'react'; +import {useEffect, useState} from 'react'; import {RESOLUTION} from '@/constants.js'; import {usePacket} from '@/context/client.js'; @@ -12,10 +12,55 @@ import TargetingGrid from './targeting-grid.jsx'; import TileLayer from './tile-layer.jsx'; import Water from './water.jsx'; +const NIGHTNESS = 0.1; + +function calculateDarkness(hour) { + let darkness = 0; + if (hour >= 21 || hour < 4) { + darkness = 0.8; + } + if (hour >= 4 && hour < 7) { + darkness = 0.8 * ((7 - hour) / 3); + } + if (hour >= 18 && hour < 21) { + darkness = 0.8 * ((3 - (21 - hour)) / 3); + } + return Math.floor(darkness * 1000) / 1000; +} + export default function Ecs({scale}) { const [ecs] = useEcs(); const [entities, setEntities] = useState({}); const [mainEntity] = useMainEntity(); + const [hour, setHour] = useState(10); + const [night, setNight] = useState(); + useEffect(() => { + async function buildNightFilter() { + const {ColorMatrixFilter} = await import('@pixi/filter-color-matrix'); + class NightFilter extends ColorMatrixFilter { + setIntensity(intensity) { + const double = NIGHTNESS * 2; + const half = NIGHTNESS / 2; + const redDown = 1 - (intensity * (1 + double)); + const blueUp = 1 - (intensity * (1 - half)); + const scale = intensity * NIGHTNESS; + this.uniforms.m = [ + redDown, -scale, 0, 0, 0, + -scale, (1 - intensity), scale, 0, 0, + 0, scale, blueUp, 0, 0, + 0, 0, 0, 1, 0, + ]; + } + } + setNight(new NightFilter()); + } + buildNightFilter(); + }, []); + useEffect(() => { + if (night) { + night.setIntensity(calculateDarkness(hour)); + } + }, [hour, night]); usePacket('EcsChange', async () => { setEntities({}); }, [setEntities]); @@ -30,6 +75,11 @@ export default function Ecs({scale}) { delete updatedEntities[id]; } else { + if ('1' === id) { + if (update.Time) { + setHour(Math.round(ecs.get(1).Time.hour * 60) / 60); + } + } updatedEntities[id] = ecs.get(id); if (update.Emitter?.emit) { updatedEntities[id].Emitter.emitting = { @@ -52,12 +102,17 @@ export default function Ecs({scale}) { const projected = Wielder.activeItem()?.project(Position.tile, Direction.direction) const {Camera} = entity; const {TileLayers: {layers: [layer]}, Water: WaterEcs} = ecs.get(1); + const filters = []; + if (night) { + filters.push(night); + } const [cx, cy] = [ Math.round((Camera.x * scale) - RESOLUTION.x / 2), Math.round((Camera.y * scale) - RESOLUTION.y / 2), ]; return (