refactor: water
This commit is contained in:
parent
4faf685029
commit
b2ab8858dc
22
packages/farm/src/packets/room-water.js
Normal file
22
packages/farm/src/packets/room-water.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import {Packet} from '@latus/socket';
|
||||||
|
|
||||||
|
export default () => class RoomWaterPacket extends Packet {
|
||||||
|
|
||||||
|
static get data() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
hash: 'uint32',
|
||||||
|
amount: 'int8',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static pack(data) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
data[i].amount = Math.floor(data[i].amount);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -1,11 +1,36 @@
|
||||||
import {Rectangle, Vector} from '@avocado/math';
|
import {
|
||||||
|
Rectangle,
|
||||||
|
spontaneous,
|
||||||
|
Vector,
|
||||||
|
} from '@avocado/math';
|
||||||
|
|
||||||
|
import Renderable from './room/renderable';
|
||||||
|
|
||||||
const DIRT = 6;
|
const DIRT = 6;
|
||||||
const TILLED = 7;
|
const TILLED = 7;
|
||||||
|
|
||||||
export default (Room) => class FarmableRoom extends Room {
|
export default (Room) => class FarmableRoom extends Room {
|
||||||
|
|
||||||
#water = {};
|
static Renderable = Renderable(Room);
|
||||||
|
|
||||||
|
#evaporation = 0;
|
||||||
|
|
||||||
|
water = {};
|
||||||
|
|
||||||
|
#waterChanged = false;
|
||||||
|
|
||||||
|
#waterChanges = [];
|
||||||
|
|
||||||
|
acceptPacket(packet) {
|
||||||
|
super.acceptPacket(packet);
|
||||||
|
if ('RoomWater' === packet.constructor.type) {
|
||||||
|
for (let i = 0; i < packet.data.length; i++) {
|
||||||
|
const {amount, hash} = packet.data[i];
|
||||||
|
this.water[hash] = amount;
|
||||||
|
}
|
||||||
|
this.emit('waterChanged');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adjustWaterAt(target, increase) {
|
adjustWaterAt(target, increase) {
|
||||||
const [tiles] = this.tiles;
|
const [tiles] = this.tiles;
|
||||||
|
@ -16,18 +41,28 @@ export default (Room) => class FarmableRoom extends Room {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const hash = Vector.packToUint32(target);
|
const hash = Vector.packToUint32(target);
|
||||||
if (!(hash in this.#water)) {
|
if (!(hash in this.water)) {
|
||||||
this.#water[hash] = 0;
|
this.water[hash] = 0;
|
||||||
}
|
}
|
||||||
this.#water[hash] += increase;
|
const oldWater = this.water[hash];
|
||||||
if (this.#water[hash] < 0) {
|
this.water[hash] += increase;
|
||||||
|
if (this.water[hash] < 0) {
|
||||||
// Drought
|
// Drought
|
||||||
this.#water[hash] = Math.max(this.#water[hash], -128);
|
this.water[hash] = Math.max(this.water[hash], -128);
|
||||||
}
|
}
|
||||||
else if (this.#water[hash] > 0) {
|
else if (this.water[hash] > 0) {
|
||||||
// Flood
|
// Flood
|
||||||
this.#water[hash] = Math.min(this.#water[hash], 127);
|
this.water[hash] = Math.min(this.water[hash], 127);
|
||||||
}
|
}
|
||||||
|
if (this.water[hash] !== oldWater) {
|
||||||
|
this.#waterChanged = true;
|
||||||
|
this.#waterChanges.push({hash, amount: this.water[hash]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanPackets() {
|
||||||
|
super.cleanPackets();
|
||||||
|
this.#waterChanges = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
isPlantAt(target) {
|
isPlantAt(target) {
|
||||||
|
@ -41,10 +76,23 @@ export default (Room) => class FarmableRoom extends Room {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async load(json) {
|
||||||
|
await super.load(json);
|
||||||
|
const {
|
||||||
|
evaporation,
|
||||||
|
water,
|
||||||
|
} = json;
|
||||||
|
if (evaporation) {
|
||||||
|
this.#evaporation = evaporation / 144;
|
||||||
|
}
|
||||||
|
if (water) {
|
||||||
|
this.water = water;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mayPrepareSoilAt(target) {
|
mayPrepareSoilAt(target) {
|
||||||
const [tiles] = this.tiles;
|
const [tiles] = this.tiles;
|
||||||
return DIRT === tiles.tileAt(target);
|
return DIRT === tiles.tileAt(target);
|
||||||
// return -1 !== GRASS.indexOf(tiles.tileAt(target));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
maySowAt(target) {
|
maySowAt(target) {
|
||||||
|
@ -56,11 +104,65 @@ export default (Room) => class FarmableRoom extends Room {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packetsFor(informed) {
|
||||||
|
const packets = super.packetsFor(informed);
|
||||||
|
if (this.#waterChanges.length > 0) {
|
||||||
|
packets.push(['RoomWater', this.#waterChanges]);
|
||||||
|
}
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
prepareSoilAt(target) {
|
prepareSoilAt(target) {
|
||||||
if ('client' !== process.env.SIDE) {
|
if ('client' !== process.env.SIDE) {
|
||||||
const [tiles] = this.tiles;
|
const [tiles] = this.tiles;
|
||||||
tiles.setTileAt(target, TILLED);
|
tiles.setTileAt(target, TILLED);
|
||||||
|
this.adjustWaterAt(target, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tick(elapsed) {
|
||||||
|
super.tick(elapsed);
|
||||||
|
this.tickWater(elapsed);
|
||||||
|
if (this.#waterChanged) {
|
||||||
|
this.#waterChanged = false;
|
||||||
|
this.emit('waterChanged');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tickWater(elapsed) {
|
||||||
|
if ('client' === process.env.SIDE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const water = Object.entries(this.water);
|
||||||
|
for (let i = 0; i < water.length; i++) {
|
||||||
|
const [hash] = water[i];
|
||||||
|
if (spontaneous(elapsed, 10)) {
|
||||||
|
this.adjustWaterAt(Vector.unpackFromUint32(hash), -this.#evaporation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
...super.toJSON(),
|
||||||
|
water: this.water,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toNetwork(informed) {
|
||||||
|
return {
|
||||||
|
...super.toNetwork(informed),
|
||||||
|
water: this.water,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
waterAt(target) {
|
||||||
|
const [tiles] = this.tiles;
|
||||||
|
if (TILLED !== tiles.tileAt(target)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const hash = Vector.packToUint32(target);
|
||||||
|
return hash in this.water ? this.water[hash] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
50
packages/farm/src/resources/decorators/room/renderable.js
Normal file
50
packages/farm/src/resources/decorators/room/renderable.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import {Color, Primitives} from '@avocado/graphics';
|
||||||
|
import {Vector} from '@avocado/math';
|
||||||
|
|
||||||
|
export default (Room) => class extends Room.Renderable {
|
||||||
|
|
||||||
|
#primitives = {};
|
||||||
|
|
||||||
|
constructor(room, renderer) {
|
||||||
|
super(room, renderer);
|
||||||
|
this.room = room;
|
||||||
|
this.room.on('waterChanged', this.onWaterChange, this);
|
||||||
|
this.onWaterChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
onWaterChange() {
|
||||||
|
const water = Object.entries(this.room.water);
|
||||||
|
const [tiles] = this.room.tiles;
|
||||||
|
const [renderable] = this.tilesRenderables;
|
||||||
|
for (let i = 0; i < water.length; i++) {
|
||||||
|
const [hash, amount] = water[i];
|
||||||
|
const [tw, th] = tiles.tileSize;
|
||||||
|
const [x, y] = Vector.mul(Vector.unpackFromUint32(hash), [tw, th]);
|
||||||
|
const primitives = this.primitivesFor(hash);
|
||||||
|
renderable.addChild(primitives);
|
||||||
|
primitives.clear();
|
||||||
|
if (amount > 0) {
|
||||||
|
primitives.drawRectangle(
|
||||||
|
[x, y, tw, th],
|
||||||
|
Primitives.lineStyle(new Color(0, 0, 0, amount / 256), 0),
|
||||||
|
Primitives.fillStyle(new Color(0, 0, 0, amount / 256)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (amount < 0) {
|
||||||
|
primitives.drawRectangle(
|
||||||
|
[x, y, tw, th],
|
||||||
|
Primitives.lineStyle(new Color(180, 180, 32, Math.abs(amount) / 512), 0),
|
||||||
|
Primitives.fillStyle(new Color(180, 180, 32, Math.abs(amount) / 512)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
primitivesFor(hash) {
|
||||||
|
if (!this.#primitives[hash]) {
|
||||||
|
this.#primitives[hash] = new Primitives();
|
||||||
|
}
|
||||||
|
return this.#primitives[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
import {spontaneous} from '@avocado/math';
|
import {spontaneous, Vector} from '@avocado/math';
|
||||||
import {StateProperty, Trait} from '@avocado/traits';
|
import {StateProperty, Trait} from '@avocado/traits';
|
||||||
import {compose} from '@latus/core';
|
import {compose} from '@latus/core';
|
||||||
|
|
||||||
|
@ -10,10 +10,7 @@ const decorate = compose(
|
||||||
|
|
||||||
export default (latus) => class Plant extends decorate(Trait) {
|
export default (latus) => class Plant extends decorate(Trait) {
|
||||||
|
|
||||||
constructor() {
|
#growthElapsed = 0;
|
||||||
super();
|
|
||||||
this.growthElapsed = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
acceptPacket(packet) {
|
acceptPacket(packet) {
|
||||||
if ('TraitUpdatePlant' === packet.constructor.type) {
|
if ('TraitUpdatePlant' === packet.constructor.type) {
|
||||||
|
@ -26,6 +23,8 @@ export default (latus) => class Plant extends decorate(Trait) {
|
||||||
growthCheck: '',
|
growthCheck: '',
|
||||||
growthScript: '',
|
growthScript: '',
|
||||||
stageSpecs: {},
|
stageSpecs: {},
|
||||||
|
waterIdeal: 64,
|
||||||
|
waterTolerance: 32,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ export default (latus) => class Plant extends decorate(Trait) {
|
||||||
this.entity.currentImage = image;
|
this.entity.currentImage = image;
|
||||||
this.entity.isInteractive = !!ripe;
|
this.entity.isInteractive = !!ripe;
|
||||||
if (reset) {
|
if (reset) {
|
||||||
this.growthElapsed = 0;
|
this.#growthElapsed = 0;
|
||||||
}
|
}
|
||||||
if (this.growthScript) {
|
if (this.growthScript) {
|
||||||
this.entity.addTickingPromise(this.growthScript.tickingPromise());
|
this.entity.addTickingPromise(this.growthScript.tickingPromise());
|
||||||
|
@ -112,12 +111,20 @@ export default (latus) => class Plant extends decorate(Trait) {
|
||||||
if ('client' === process.env.SIDE) {
|
if ('client' === process.env.SIDE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (spontaneous(elapsed, 15)) {
|
const {room} = this.entity;
|
||||||
this.growthElapsed += 0.25;
|
const {tileSize} = room.tiles[0];
|
||||||
|
const tile = Vector.floor(Vector.div(this.entity.position, tileSize));
|
||||||
|
const water = room.waterAt(tile);
|
||||||
|
const drift = Math.abs(this.params.waterIdeal - water);
|
||||||
|
if (this.params.waterTolerance <= drift) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (spontaneous(elapsed, 9)) {
|
||||||
|
this.#growthElapsed += 0.15;
|
||||||
const {growthStage} = this.entity;
|
const {growthStage} = this.entity;
|
||||||
const stageSpec = this.params.stageSpecs[growthStage];
|
const stageSpec = this.params.stageSpecs[growthStage];
|
||||||
if ('growAt' in stageSpec) {
|
if ('growAt' in stageSpec) {
|
||||||
if (this.growthElapsed >= stageSpec.growAt) {
|
if (this.#growthElapsed >= stageSpec.growAt) {
|
||||||
const grow = () => {
|
const grow = () => {
|
||||||
const {growTo} = stageSpec;
|
const {growTo} = stageSpec;
|
||||||
this.entity.growthStage = 'undefined' !== typeof growTo ? growTo : growthStage + 1;
|
this.entity.growthStage = 'undefined' !== typeof growTo ? growTo : growthStage + 1;
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
import {Container, Renderer, Stage} from '@avocado/graphics';
|
import {Container, Renderer, Stage} from '@avocado/graphics';
|
||||||
import {Vector} from '@avocado/math';
|
import {Vector} from '@avocado/math';
|
||||||
import {createLoop, destroyLoop} from '@avocado/timing';
|
import {createLoop, destroyLoop} from '@avocado/timing';
|
||||||
import {RoomView} from '@avocado/topdown';
|
|
||||||
import {useRoom, useSelfEntity} from '@humus/app/client';
|
import {useRoom, useSelfEntity} from '@humus/app/client';
|
||||||
|
|
||||||
const renderer = new Renderer();
|
const renderer = new Renderer();
|
||||||
|
@ -18,7 +17,7 @@ const renderer = new Renderer();
|
||||||
const RoomStage = () => {
|
const RoomStage = () => {
|
||||||
const selfEntity = useSelfEntity();
|
const selfEntity = useSelfEntity();
|
||||||
const [container] = useState(new Container());
|
const [container] = useState(new Container());
|
||||||
const [roomView, setRoomView] = useState();
|
const [roomRenderable, setRoomRenderable] = useState();
|
||||||
const [viewport] = useState([320, 180]);
|
const [viewport] = useState([320, 180]);
|
||||||
const room = useRoom();
|
const room = useRoom();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -28,22 +27,22 @@ const RoomStage = () => {
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const roomView = new RoomView(room, renderer);
|
const roomRenderable = new (room.constructor.Renderable)(room, renderer);
|
||||||
setRoomView(roomView);
|
setRoomRenderable(roomRenderable);
|
||||||
container.addChild(roomView);
|
container.addChild(roomRenderable);
|
||||||
return () => {
|
return () => {
|
||||||
container.removeChild(roomView);
|
container.removeChild(roomRenderable);
|
||||||
};
|
};
|
||||||
}, [container, room]);
|
}, [container, room]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!roomView || !selfEntity || !selfEntity.camera) {
|
if (!roomRenderable || !selfEntity || !selfEntity.camera) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const {camera} = selfEntity;
|
const {camera} = selfEntity;
|
||||||
const halfViewport = Vector.scale(viewport, 0.5);
|
const halfViewport = Vector.scale(viewport, 0.5);
|
||||||
camera.realPosition = camera.position;
|
camera.realPosition = camera.position;
|
||||||
const onCameraRealOffsetChanged = () => {
|
const onCameraRealOffsetChanged = () => {
|
||||||
roomView.renderChunksForExtent(camera.rectangle);
|
roomRenderable.renderChunksForExtent(camera.rectangle);
|
||||||
container.pivot = Vector.sub(halfViewport, Vector.scale(camera.realOffset, -1));
|
container.pivot = Vector.sub(halfViewport, Vector.scale(camera.realOffset, -1));
|
||||||
};
|
};
|
||||||
const onCameraRotationChanged = () => {
|
const onCameraRotationChanged = () => {
|
||||||
|
@ -63,7 +62,7 @@ const RoomStage = () => {
|
||||||
camera.off('rotationChanged', onCameraRotationChanged);
|
camera.off('rotationChanged', onCameraRotationChanged);
|
||||||
camera.off('scaleChanged', onCameraScaleChanged);
|
camera.off('scaleChanged', onCameraScaleChanged);
|
||||||
};
|
};
|
||||||
}, [container, roomView, selfEntity, viewport]);
|
}, [container, roomRenderable, selfEntity, viewport]);
|
||||||
const ticker = useCallback(
|
const ticker = useCallback(
|
||||||
(elapsed) => {
|
(elapsed) => {
|
||||||
container.renderTick(elapsed);
|
container.renderTick(elapsed);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user