Compare commits
No commits in common. "f9f3abf4efa9cee4585770500ab3cfdc48a7a9cf" and "3875f897c29f510205f896aa9ba528ae7ee23bf3" have entirely different histories.
f9f3abf4ef
...
3875f897c2
|
@ -4,7 +4,6 @@ import {floodwalk2D, ortho, removeCollinear} from '@/util/math.js';
|
|||
import vector2d from './helpers/vector-2d';
|
||||
|
||||
class LayerProxy {
|
||||
$$sourceJson;
|
||||
constructor(instance, Component, index) {
|
||||
this.instance = instance;
|
||||
this.Component = Component;
|
||||
|
@ -80,15 +79,9 @@ class LayerProxy {
|
|||
get layer() {
|
||||
return this.instance.layers[this.index];
|
||||
}
|
||||
async load() {
|
||||
this.$$sourceJson = await this.Component.ecs.readJson(this.layer.source);
|
||||
}
|
||||
get source() {
|
||||
return this.layer.source;
|
||||
}
|
||||
get sourceJson() {
|
||||
return this.$$sourceJson;
|
||||
}
|
||||
stamp(at, data) {
|
||||
const changes = {};
|
||||
for (const row in data) {
|
||||
|
@ -119,7 +112,7 @@ class LayerProxy {
|
|||
}
|
||||
|
||||
export default class TileLayers extends Component {
|
||||
async insertMany(entities) {
|
||||
insertMany(entities) {
|
||||
for (const [id, {layerChange}] of entities) {
|
||||
if (layerChange) {
|
||||
const component = this.get(id);
|
||||
|
@ -131,24 +124,14 @@ export default class TileLayers extends Component {
|
|||
}
|
||||
layers[layerIndex] = {...layers[layerIndex]};
|
||||
component.$$layersProxies[layerIndex] = new LayerProxy(component, this, layerIndex);
|
||||
await component.$$layersProxies[layerIndex].load();
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.insertMany(entities);
|
||||
}
|
||||
instanceFromSchema() {
|
||||
return class TileLayersInstance extends super.instanceFromSchema() {
|
||||
$$layersProxies = {};
|
||||
layer(index) {
|
||||
return this.$$layersProxies[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
async load(instance) {
|
||||
load(instance) {
|
||||
for (const index in instance.layers) {
|
||||
instance.$$layersProxies[index] = new LayerProxy(instance, this, index);
|
||||
await instance.$$layersProxies[index].load();
|
||||
}
|
||||
}
|
||||
mergeDiff(original, update) {
|
||||
|
@ -166,6 +149,14 @@ export default class TileLayers extends Component {
|
|||
}
|
||||
return {layerChange};
|
||||
}
|
||||
instanceFromSchema() {
|
||||
return class TileLayersInstance extends super.instanceFromSchema() {
|
||||
$$layersProxies = {};
|
||||
layer(index) {
|
||||
return this.$$layersProxies[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
static properties = {
|
||||
layers: {
|
||||
type: 'array',
|
||||
|
|
|
@ -18,6 +18,6 @@ export default class Time extends Component {
|
|||
};
|
||||
}
|
||||
static properties = {
|
||||
irlSeconds: {defaultValue: 10 * realSecondsPerGameHour, type: 'uint16'},
|
||||
irlSeconds: {defaultValue: 18 * realSecondsPerGameHour, type: 'uint16'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -94,13 +94,6 @@ export default class Engine {
|
|||
}
|
||||
this.incomingActions.get(connection).push(payload);
|
||||
});
|
||||
this.server.addPacketListener('AdminAction', (connection, payload) => {
|
||||
// check...
|
||||
if (!this.incomingActions.has(connection)) {
|
||||
this.incomingActions.set(connection, []);
|
||||
}
|
||||
this.incomingActions.get(connection).push(payload);
|
||||
});
|
||||
}
|
||||
|
||||
acceptActions() {
|
||||
|
@ -115,14 +108,6 @@ export default class Engine {
|
|||
const {Controlled, Ecs, Interacts, Inventory, Wielder} = entity;
|
||||
for (const payload of payloads) {
|
||||
switch (payload.type) {
|
||||
case 'paint': {
|
||||
const ecs = this.ecses[Ecs.path];
|
||||
const {TileLayers} = ecs.get(1);
|
||||
const {brush, layer: paintLayer, stamp} = payload.value;
|
||||
const layer = TileLayers.layer(paintLayer);
|
||||
layer.stamp(stamp.at, stamp.data)
|
||||
break;
|
||||
}
|
||||
case 'changeSlot': {
|
||||
if (!Controlled.locked) {
|
||||
Wielder.activeSlot = payload.value - 1;
|
||||
|
|
|
@ -80,26 +80,26 @@ if (import.meta.hot) {
|
|||
const resolver = createResolver();
|
||||
resolvers.push(resolver);
|
||||
await beforeResolver;
|
||||
// const oldBuffer = await engine.server.readData('players/0');
|
||||
// const oldPlayer = JSON.parse((new TextDecoder()).decode(oldBuffer));
|
||||
// const buffer = await createPlayer(0);
|
||||
// const player = JSON.parse((new TextDecoder()).decode(buffer));
|
||||
// // Less jarring
|
||||
// player.Ecs = oldPlayer.Ecs;
|
||||
// player.Direction = oldPlayer.Direction;
|
||||
// player.Position = oldPlayer.Position;
|
||||
// await engine.server.writeData('players/0', (new TextEncoder()).encode(JSON.stringify(player)));
|
||||
const oldBuffer = await engine.server.readData('players/0');
|
||||
const oldPlayer = JSON.parse((new TextDecoder()).decode(oldBuffer));
|
||||
const buffer = await createPlayer(0);
|
||||
const player = JSON.parse((new TextDecoder()).decode(buffer));
|
||||
// Less jarring
|
||||
player.Ecs = oldPlayer.Ecs;
|
||||
player.Direction = oldPlayer.Direction;
|
||||
player.Position = oldPlayer.Position;
|
||||
await engine.server.writeData('players/0', (new TextEncoder()).encode(JSON.stringify(player)));
|
||||
resolver.resolve();
|
||||
});
|
||||
import.meta.hot.accept('../../create-homestead.js', async ({default: createHomestead}) => {
|
||||
const resolver = createResolver();
|
||||
resolvers.push(resolver);
|
||||
await beforeResolver;
|
||||
// delete engine.ecses['homesteads/0'];
|
||||
// await engine.server.removeData('homesteads/0');
|
||||
// const homestead = await createHomestead(engine.Ecs);
|
||||
// homestead.get(2).Ecs.path = 'houses/0';
|
||||
// await engine.saveEcs('homesteads/0', homestead);
|
||||
delete engine.ecses['homesteads/0'];
|
||||
await engine.server.removeData('homesteads/0');
|
||||
const homestead = await createHomestead(engine.Ecs);
|
||||
homestead.get(2).Ecs.path = 'houses/0';
|
||||
await engine.saveEcs('homesteads/0', homestead);
|
||||
resolver.resolve();
|
||||
});
|
||||
import.meta.hot.on('vite:afterUpdate', async () => {
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import Packet from '@/net/packet.js';
|
||||
|
||||
export default class AdminAction extends Packet {}
|
|
@ -1,134 +0,0 @@
|
|||
import {useRef, useState} from 'react';
|
||||
|
||||
import {useEcs} from '@/context/ecs.js';
|
||||
|
||||
import styles from './devtools.module.css';
|
||||
|
||||
export default function Devtools({
|
||||
brush,
|
||||
layer,
|
||||
setBrush,
|
||||
setLayer,
|
||||
setStamp,
|
||||
}) {
|
||||
const offsetRef = useRef();
|
||||
const [selection, setSelection] = useState({x: 0, y: 0, w: 2, h: 2});
|
||||
const [moveStart, setMoveStart] = useState();
|
||||
const [ecs] = useEcs();
|
||||
if (!ecs) {
|
||||
return false;
|
||||
}
|
||||
const master = ecs.get(1);
|
||||
if (!master) {
|
||||
return false;
|
||||
}
|
||||
const {TileLayers} = master;
|
||||
const {sourceJson, tileSize} = TileLayers.layer(0);
|
||||
const {w, h} = sourceJson.meta.size;
|
||||
return (
|
||||
<div className={styles.devtools}>
|
||||
<form>
|
||||
<div className={styles.topBar}>
|
||||
<div className={styles.layer}>
|
||||
<label>
|
||||
Layer:
|
||||
<select
|
||||
onChange={(event) => {
|
||||
setLayer(event.target.value)
|
||||
}}
|
||||
value={layer}
|
||||
>
|
||||
{
|
||||
Object.keys(TileLayers.layers)
|
||||
.map((layer, i) => (
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
>
|
||||
{i}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.brush}>
|
||||
<label>
|
||||
Brush:
|
||||
<select
|
||||
onChange={(event) => {
|
||||
setLayer(event.target.value)
|
||||
}}
|
||||
value={brush}
|
||||
>
|
||||
<option>Paint</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||
<div
|
||||
onMouseDown={(event) => {
|
||||
if (!offsetRef.current) {
|
||||
return;
|
||||
}
|
||||
const {left, top} = offsetRef.current.getBoundingClientRect();
|
||||
const x = Math.floor((event.clientX - left) / tileSize.x);
|
||||
const y = Math.floor((event.clientY - top) / tileSize.y);
|
||||
setMoveStart({x, y});
|
||||
setSelection({x, y, w: 1, h: 1});
|
||||
}}
|
||||
onMouseMove={(event) => {
|
||||
if (!offsetRef.current) {
|
||||
return;
|
||||
}
|
||||
if (!moveStart) {
|
||||
return;
|
||||
}
|
||||
const {x: sx, y: sy} = moveStart;
|
||||
const {left, top} = offsetRef.current.getBoundingClientRect();
|
||||
const x = Math.floor(
|
||||
Math.max(0, Math.min(w - 1, (event.clientX - left)) / tileSize.x),
|
||||
);
|
||||
const y = Math.floor(
|
||||
Math.max(0, Math.min(h - 1, (event.clientY - top)) / tileSize.y),
|
||||
);
|
||||
const mx = Math.min(sx, x);
|
||||
const my = Math.min(sy, y);
|
||||
setSelection({x: mx, y: my, w: Math.abs(sx - x) + 1, h: Math.abs(sy - y) + 1});
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
setMoveStart();
|
||||
const stamp = [];
|
||||
const {x, y, w: sw, h: sh} = selection;
|
||||
const tw = w / tileSize.x;
|
||||
for (let iy = 0; iy < sh; ++iy) {
|
||||
const row = [];
|
||||
for (let ix = 0; ix < sw; ++ix) {
|
||||
row.push((y + iy) * tw + x + ix);
|
||||
}
|
||||
stamp.push(row);
|
||||
}
|
||||
setStamp(stamp);
|
||||
}}
|
||||
className={styles.selectionWrapper}
|
||||
ref={offsetRef}
|
||||
>
|
||||
<div
|
||||
className={styles.selection}
|
||||
style={{
|
||||
top: selection.y * tileSize.y,
|
||||
left: selection.x * tileSize.x,
|
||||
height: selection.h * tileSize.x,
|
||||
width: selection.w * tileSize.y,
|
||||
}}
|
||||
/>
|
||||
<img
|
||||
alt="tileset"
|
||||
src={TileLayers.layer(0).source.replace('.json', '.png')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
.topBar {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.devtools {
|
||||
background-color: #444444;
|
||||
color: white;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.devtools p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.selectionWrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.selectionWrapper img {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.selection {
|
||||
background-color: #ffffff44;
|
||||
position: absolute;
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100% / var(--scale));
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform: scale(var(--scale));
|
||||
|
|
|
@ -44,6 +44,10 @@ function createLayerMask(layer) {
|
|||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||
for (let i = 0; i < hulls.length; ++i) {
|
||||
const {x, y} = hulls[i][0];
|
||||
if (7 !== layer.tile({x: Math.floor(x / tileSize.x), y: Math.floor(y / tileSize.y)})) {
|
||||
continue;
|
||||
}
|
||||
const hull = [...hulls[i]];
|
||||
hull.push(hull[0]);
|
||||
ctx.beginPath();
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 75%;
|
||||
height: 100%;
|
||||
height: calc(100% - var(--space) * 2);
|
||||
padding: var(--space);
|
||||
position: relative;
|
||||
width: 100%;
|
||||
width: calc(100% - var(--space) * 2);
|
||||
}
|
||||
|
||||
.qty {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {useEffect, useRef, useState} from 'react';
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
import addKeyListener from '@/add-key-listener.js';
|
||||
import ClientEcs from '@/client-ecs';
|
||||
|
@ -8,13 +8,14 @@ import {useDebug} from '@/context/debug.js';
|
|||
import {useEcs, useEcsTick} from '@/context/ecs.js';
|
||||
import {useMainEntity} from '@/context/main-entity.js';
|
||||
|
||||
import Devtools from './devtools.jsx';
|
||||
import Disconnected from './disconnected.jsx';
|
||||
import Dom from './dom.jsx';
|
||||
import HotBar from './hotbar.jsx';
|
||||
import Pixi from './pixi.jsx';
|
||||
import styles from './ui.module.css';
|
||||
|
||||
const ratio = RESOLUTION.x / RESOLUTION.y;
|
||||
|
||||
function emptySlots() {
|
||||
return Array(10).fill(undefined);
|
||||
}
|
||||
|
@ -22,22 +23,16 @@ function emptySlots() {
|
|||
export default function Ui({disconnected}) {
|
||||
// Key input.
|
||||
const client = useClient();
|
||||
const gameRef = useRef();
|
||||
const [mainEntity, setMainEntity] = useMainEntity();
|
||||
const [debug, setDebug] = useDebug();
|
||||
const [ecs, setEcs] = useEcs();
|
||||
const [showDisconnected, setShowDisconnected] = useState(false);
|
||||
const [bufferSlot, setBufferSlot] = useState();
|
||||
const [devtoolsIsOpen, setDevtoolsIsOpen] = useState(false);
|
||||
const ratio = (RESOLUTION.x * (devtoolsIsOpen ? 2 : 1)) / RESOLUTION.y;
|
||||
const [hotbarSlots, setHotbarSlots] = useState(emptySlots());
|
||||
const [activeSlot, setActiveSlot] = useState(0);
|
||||
const [scale, setScale] = useState(2);
|
||||
const [Components, setComponents] = useState();
|
||||
const [Systems, setSystems] = useState();
|
||||
const [layer, setLayer] = useState(0);
|
||||
const [brush, setBrush] = useState(0);
|
||||
const [stamp, setStamp] = useState([]);
|
||||
useEffect(() => {
|
||||
async function setEcsStuff() {
|
||||
const {default: Components} = await import('@/ecs-components/index.js');
|
||||
|
@ -92,15 +87,6 @@ export default function Ui({disconnected}) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 'F4': {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if ('keyDown' === type) {
|
||||
setDevtoolsIsOpen(!devtoolsIsOpen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'w': {
|
||||
actionPayload = {type: 'moveUp', value: KEY_MAP[type]};
|
||||
break;
|
||||
|
@ -193,7 +179,7 @@ export default function Ui({disconnected}) {
|
|||
});
|
||||
}
|
||||
});
|
||||
}, [client, debug, devtoolsIsOpen, setDebug, setScale]);
|
||||
}, [client, debug, setDebug, setScale]);
|
||||
usePacket('EcsChange', async () => {
|
||||
setMainEntity(undefined);
|
||||
setEcs(new ClientEcs({Components, Systems}));
|
||||
|
@ -245,75 +231,16 @@ export default function Ui({disconnected}) {
|
|||
};
|
||||
}, [])
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
className={styles.ui}
|
||||
>
|
||||
<style>
|
||||
{`
|
||||
@media (max-aspect-ratio: ${ratio}) { .${styles.game} { width: 100%; } }
|
||||
@media (min-aspect-ratio: ${ratio}) { .${styles.game} { height: 100%; } }
|
||||
.${styles.game} {
|
||||
cursor: ${
|
||||
bufferSlot
|
||||
? `url('${bufferSlot.icon}'), auto !important`
|
||||
: 'auto'
|
||||
};
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */ }
|
||||
<div
|
||||
className={[styles.game, devtoolsIsOpen && styles.devtoolsIsOpen].filter(Boolean).join(' ')}
|
||||
onMouseDown={(event) => {
|
||||
switch (event.button) {
|
||||
case 0:
|
||||
if (devtoolsIsOpen) {
|
||||
if (!gameRef.current || !mainEntity) {
|
||||
return;
|
||||
}
|
||||
const {top, left, width} = gameRef.current.getBoundingClientRect();
|
||||
const master = ecs.get(1);
|
||||
if (!master) {
|
||||
return;
|
||||
}
|
||||
const {Camera} = ecs.get(mainEntity);
|
||||
const {TileLayers} = master;
|
||||
const {area, tileSize} = TileLayers.layer(0);
|
||||
const size = width / RESOLUTION.x;
|
||||
const cr = {
|
||||
x: (event.clientX - left) / size,
|
||||
y: (event.clientY - top) / size,
|
||||
};
|
||||
const cm = {
|
||||
x: ((Camera.x * scale) - (RESOLUTION.x / 2)),
|
||||
y: ((Camera.y * scale) - (RESOLUTION.y / 2)),
|
||||
}
|
||||
const at = {
|
||||
x: Math.floor((cr.x + cm.x) / (tileSize.x * scale)),
|
||||
y: Math.floor((cr.y + cm.y) / (tileSize.y * scale)),
|
||||
};
|
||||
if (at.x < 0 || at.y < 0 || at.x >= area.x || at.y >= area.y) {
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
brush,
|
||||
layer,
|
||||
stamp: {
|
||||
at,
|
||||
data: stamp,
|
||||
},
|
||||
}
|
||||
client.send({
|
||||
type: 'AdminAction',
|
||||
payload: {type: 'paint', value: payload},
|
||||
});
|
||||
}
|
||||
else {
|
||||
client.send({
|
||||
type: 'Action',
|
||||
payload: {type: 'use', value: 1},
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
client.send({
|
||||
|
@ -355,11 +282,23 @@ export default function Ui({disconnected}) {
|
|||
});
|
||||
}
|
||||
}}
|
||||
ref={gameRef}
|
||||
>
|
||||
<style>
|
||||
{`
|
||||
@media (max-aspect-ratio: ${ratio}) { .${styles.ui} { width: 100%; } }
|
||||
@media (min-aspect-ratio: ${ratio}) { .${styles.ui} { height: 100%; } }
|
||||
.${styles.ui} {
|
||||
cursor: ${
|
||||
bufferSlot
|
||||
? `url('${bufferSlot.icon}'), auto !important`
|
||||
: 'auto'
|
||||
};
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<Pixi scale={scale} />
|
||||
{mainEntity && (
|
||||
<Dom devtoolsIsOpen={devtoolsIsOpen}>
|
||||
<Dom>
|
||||
<HotBar
|
||||
active={activeSlot}
|
||||
onActivate={(i) => {
|
||||
|
@ -376,17 +315,6 @@ export default function Ui({disconnected}) {
|
|||
</Dom>
|
||||
)}
|
||||
</div>
|
||||
<div className={[styles.devtools, devtoolsIsOpen && styles.devtoolsIsOpen].filter(Boolean).join(' ')}>
|
||||
<Devtools
|
||||
brush={brush}
|
||||
layer={layer}
|
||||
stamp={stamp}
|
||||
setBrush={setBrush}
|
||||
setLayer={setLayer}
|
||||
setStamp={setStamp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
.devtools {
|
||||
display: none;
|
||||
height: 100%;
|
||||
&.devtoolsIsOpen {
|
||||
display: block;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.game {
|
||||
.ui {
|
||||
align-self: center;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
&.devtoolsIsOpen {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.ui {
|
||||
display: flex;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ html, body {
|
|||
margin: 0;
|
||||
width: 100%;
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user