Compare commits
10 Commits
f971295825
...
3eb94f2ef8
Author | SHA1 | Date | |
---|---|---|---|
|
3eb94f2ef8 | ||
|
cbe5f473a6 | ||
|
49f46b4b00 | ||
|
a0943c8bbb | ||
|
80c1f78c6a | ||
|
52f19b1d89 | ||
|
3d7b0fc14e | ||
|
c45d24b909 | ||
|
f197658199 | ||
|
fb1a286a86 |
|
@ -3,6 +3,32 @@ import Component from '@/ecs/component.js';
|
||||||
export default class Controlled extends Component {
|
export default class Controlled extends Component {
|
||||||
instanceFromSchema() {
|
instanceFromSchema() {
|
||||||
return class ControlledInstance extends super.instanceFromSchema() {
|
return class ControlledInstance extends super.instanceFromSchema() {
|
||||||
|
directionMove(direction) {
|
||||||
|
const x = Math.cos(direction);
|
||||||
|
if (x > 0) {
|
||||||
|
this.moveLeft = 0;
|
||||||
|
this.moveRight = x;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.moveLeft = -x;
|
||||||
|
this.moveRight = 0;
|
||||||
|
}
|
||||||
|
const y = Math.sin(direction);
|
||||||
|
if (y > 0) {
|
||||||
|
this.moveUp = 0;
|
||||||
|
this.moveDown = y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.moveUp = -y;
|
||||||
|
this.moveDown = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
this.moveRight = 0;
|
||||||
|
this.moveDown = 0;
|
||||||
|
this.moveLeft = 0;
|
||||||
|
this.moveUp = 0;
|
||||||
|
}
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
import Component from '@/ecs/component.js';
|
import Component from '@/ecs/component.js';
|
||||||
|
import {HALF_PI, TAU} from '@/util/math.js';
|
||||||
|
|
||||||
export default class Direction extends Component {
|
export default class Direction extends Component {
|
||||||
|
instanceFromSchema() {
|
||||||
|
return class DirectionInstance extends super.instanceFromSchema() {
|
||||||
|
static quantize(d, n) {
|
||||||
|
return Math.floor(((d + (TAU / (n * 2))) % TAU) / (TAU / n));
|
||||||
|
}
|
||||||
|
quantize(n) {
|
||||||
|
return this.constructor.quantize(this.direction, n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
static properties = {
|
static properties = {
|
||||||
direction: {type: 'uint8'},
|
direction: {defaultValue: HALF_PI, type: 'float32'},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
import Component from '@/ecs/component.js';
|
import Component from '@/ecs/component.js';
|
||||||
|
|
||||||
export default class Emitter extends Component {
|
export default class Emitter extends Component {
|
||||||
mergeDiff(original, update) {
|
|
||||||
const merged = {};
|
|
||||||
if (update.emit) {
|
|
||||||
merged.emit = {
|
|
||||||
...original.emit,
|
|
||||||
...update.emit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
instanceFromSchema() {
|
instanceFromSchema() {
|
||||||
const Component = this;
|
const Component = this;
|
||||||
return class EmitterInstance extends super.instanceFromSchema() {
|
return class EmitterInstance extends super.instanceFromSchema() {
|
||||||
|
@ -21,4 +11,14 @@ export default class Emitter extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
mergeDiff(original, update) {
|
||||||
|
const merged = {};
|
||||||
|
if (update.emit) {
|
||||||
|
merged.emit = {
|
||||||
|
...original.emit,
|
||||||
|
...update.emit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -8,18 +8,19 @@ export default class Interacts extends Component {
|
||||||
const {Direction, Position} = ecs.get(this.entity);
|
const {Direction, Position} = ecs.get(this.entity);
|
||||||
let x0 = Position.x - 8;
|
let x0 = Position.x - 8;
|
||||||
let y0 = Position.y - 8;
|
let y0 = Position.y - 8;
|
||||||
if (0 === Direction.direction) {
|
const direction = Direction.quantize(4);
|
||||||
y0 -= 12
|
if (0 === direction) {
|
||||||
}
|
|
||||||
if (1 === Direction.direction) {
|
|
||||||
x0 += 12
|
x0 += 12
|
||||||
}
|
}
|
||||||
if (2 === Direction.direction) {
|
if (1 === direction) {
|
||||||
y0 += 12
|
y0 += 12
|
||||||
}
|
}
|
||||||
if (3 === Direction.direction) {
|
if (2 === direction) {
|
||||||
x0 -= 12
|
x0 -= 12
|
||||||
}
|
}
|
||||||
|
if (3 === direction) {
|
||||||
|
y0 -= 12
|
||||||
|
}
|
||||||
return {x0, x1: x0 + 15, y0, y1: y0 + 15};
|
return {x0, x1: x0 + 15, y0, y1: y0 + 15};
|
||||||
}
|
}
|
||||||
toJSON() {
|
toJSON() {
|
||||||
|
|
|
@ -33,21 +33,21 @@ class ItemProxy {
|
||||||
let startY = position.y;
|
let startY = position.y;
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 0:
|
case 0:
|
||||||
startX += projection.distance[1];
|
|
||||||
startY -= projection.distance[0];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
startX += projection.distance[0];
|
startX += projection.distance[0];
|
||||||
startY += projection.distance[1];
|
startY += projection.distance[1];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
startX -= projection.distance[1];
|
startX -= projection.distance[1];
|
||||||
startY += projection.distance[0];
|
startY += projection.distance[0];
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 2:
|
||||||
startX -= projection.distance[0];
|
startX -= projection.distance[0];
|
||||||
startY -= projection.distance[1];
|
startY -= projection.distance[1];
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
startX += projection.distance[1];
|
||||||
|
startY -= projection.distance[0];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
const projected = [];
|
const projected = [];
|
||||||
for (const row in projection.grid) {
|
for (const row in projection.grid) {
|
||||||
|
@ -58,17 +58,17 @@ class ItemProxy {
|
||||||
let axe;
|
let axe;
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 0:
|
case 0:
|
||||||
axe = [column, row];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
axe = [-row, column];
|
axe = [-row, column];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
axe = [-column, -row];
|
axe = [-column, -row];
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 2:
|
||||||
axe = [row, -column];
|
axe = [row, -column];
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
axe = [column, row];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
const x = startX + parseInt(axe[0]);
|
const x = startX + parseInt(axe[0]);
|
||||||
const y = startY + parseInt(axe[1]);
|
const y = startY + parseInt(axe[1]);
|
||||||
|
@ -91,6 +91,9 @@ class ItemProxy {
|
||||||
get icon() {
|
get icon() {
|
||||||
return this.json.icon;
|
return this.json.icon;
|
||||||
}
|
}
|
||||||
|
get label() {
|
||||||
|
return this.json.label;
|
||||||
|
}
|
||||||
get projection() {
|
get projection() {
|
||||||
return this.json.projection;
|
return this.json.projection;
|
||||||
}
|
}
|
||||||
|
@ -191,6 +194,12 @@ export default class Inventory extends Component {
|
||||||
const tmp = [$$items[l], slots[l]];
|
const tmp = [$$items[l], slots[l]];
|
||||||
[$$items[l], slots[l]] = [$$items[r], slots[r]];
|
[$$items[l], slots[l]] = [$$items[r], slots[r]];
|
||||||
[$$items[r], slots[r]] = tmp;
|
[$$items[r], slots[r]] = tmp;
|
||||||
|
if (undefined === slots[l]) {
|
||||||
|
delete slots[l];
|
||||||
|
}
|
||||||
|
if (undefined === slots[r]) {
|
||||||
|
delete slots[r];
|
||||||
|
}
|
||||||
Component.markChange(this.entity, 'swapped', [[l, r]]);
|
Component.markChange(this.entity, 'swapped', [[l, r]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {System} from '@/ecs/index.js';
|
import {System} from '@/ecs/index.js';
|
||||||
|
import {HALF_PI} from '@/util/math.js';
|
||||||
|
|
||||||
export default class ControlDirection extends System {
|
export default class ControlDirection extends System {
|
||||||
|
|
||||||
|
@ -9,16 +10,16 @@ export default class ControlDirection extends System {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (moveUp > 0) {
|
if (moveUp > 0) {
|
||||||
Direction.direction = 0;
|
Direction.direction = HALF_PI * 3;
|
||||||
}
|
}
|
||||||
if (moveDown > 0) {
|
if (moveDown > 0) {
|
||||||
Direction.direction = 2;
|
Direction.direction = HALF_PI * 1;
|
||||||
}
|
}
|
||||||
if (moveLeft > 0) {
|
if (moveLeft > 0) {
|
||||||
Direction.direction = 3;
|
Direction.direction = HALF_PI * 2;
|
||||||
}
|
}
|
||||||
if (moveRight > 0) {
|
if (moveRight > 0) {
|
||||||
Direction.direction = 1;
|
Direction.direction = HALF_PI * 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,12 @@ export default class SpriteDirection extends System {
|
||||||
}
|
}
|
||||||
if (Direction) {
|
if (Direction) {
|
||||||
const name = {
|
const name = {
|
||||||
0: 'up',
|
0: 'right',
|
||||||
1: 'right',
|
1: 'down',
|
||||||
2: 'down',
|
2: 'left',
|
||||||
3: 'left',
|
3: 'up',
|
||||||
};
|
};
|
||||||
parts.push(name[Direction.direction]);
|
parts.push(name[Direction.quantize(4)]);
|
||||||
}
|
}
|
||||||
if (parts.length > 0) {
|
if (parts.length > 0) {
|
||||||
Sprite.animation = parts.join(':');
|
Sprite.animation = parts.join(':');
|
||||||
|
|
|
@ -20,11 +20,11 @@ export default function Tiles({eventsChannel}) {
|
||||||
const [ecs] = useEcs();
|
const [ecs] = useEcs();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ecs) {
|
if (!ecs) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
const master = ecs.get(1);
|
const master = ecs.get(1);
|
||||||
if (!master) {
|
if (!master) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
const {TileLayers} = master;
|
const {TileLayers} = master;
|
||||||
const {area, tileSize} = TileLayers.layer(0);
|
const {area, tileSize} = TileLayers.layer(0);
|
||||||
|
|
54
app/react/components/dom/bag.jsx
Normal file
54
app/react/components/dom/bag.jsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import styles from './bag.module.css';
|
||||||
|
import Slot from './slot.jsx';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inventory bag. 10-40 slots of inventory.
|
||||||
|
*/
|
||||||
|
export default function Bag({
|
||||||
|
isInventoryOpen,
|
||||||
|
slots,
|
||||||
|
}) {
|
||||||
|
const Slots = slots.map((slot, i) => (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
[styles.slotWrapper]
|
||||||
|
.filter(Boolean).join(' ')
|
||||||
|
}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
<Slot
|
||||||
|
icon={slot?.icon}
|
||||||
|
// onMouseDown={(event) => {
|
||||||
|
// onActivate(i)
|
||||||
|
// event.stopPropagation();
|
||||||
|
// }}
|
||||||
|
// onMouseUp={(event) => {
|
||||||
|
// event.stopPropagation();
|
||||||
|
// }}
|
||||||
|
// onDragOver={(event) => {
|
||||||
|
// event.preventDefault();
|
||||||
|
// }}
|
||||||
|
// onDragStart={(event) => {
|
||||||
|
// if (!slot) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// }
|
||||||
|
// event.dataTransfer.setData('silphius/item', i);
|
||||||
|
// onActivate(i);
|
||||||
|
// }}
|
||||||
|
// onDrop={(event) => {
|
||||||
|
// event.preventDefault();
|
||||||
|
// onActivate(i);
|
||||||
|
// }}
|
||||||
|
qty={slot?.qty}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.bag}
|
||||||
|
style={isInventoryOpen ? {transition: 'none'} : {left: '-440px'}}
|
||||||
|
>
|
||||||
|
{Slots}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
31
app/react/components/dom/bag.module.css
Normal file
31
app/react/components/dom/bag.module.css
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
.bag {
|
||||||
|
align-self: left;
|
||||||
|
--border: calc(var(--unit) * 3px);
|
||||||
|
background-color: rgba(02, 02, 28, 0.6);
|
||||||
|
border: var(--border) solid #444444;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
left: calc(var(--unit) * 20px);
|
||||||
|
line-height: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--unit) * 90px);
|
||||||
|
transition: left 150ms;
|
||||||
|
max-width: 430.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slotWrapper {
|
||||||
|
border: var(--border) solid #999999;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 0;
|
||||||
|
padding: 0;
|
||||||
|
&:not(:nth-child(10n)) {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
&:not(:nth-last-of-type(-n+10)) {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,3 @@
|
||||||
user-select: text;
|
user-select: text;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Cookbook";
|
|
||||||
src: url("/assets/fonts/Cookbook.woff") format("woff");
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
background-color: #00000044;
|
background-color: #00000044;
|
||||||
border: 1px solid #333333;
|
border: 1px solid #333333;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: "Cookbook";
|
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -97,7 +97,7 @@ export default function Dialogue({
|
||||||
position.y * scale - camera.y,
|
position.y * scale - camera.y,
|
||||||
RESOLUTION.y - bounds.y * scale - 16,
|
RESOLUTION.y - bounds.y * scale - 16,
|
||||||
),
|
),
|
||||||
bounds.y * scale + 88,
|
bounds.y * scale + 16,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -14,6 +14,9 @@ export default function Dialogues({camera, dialogues, scale}) {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (0 === elements.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return <div className={styles.dialogues}>{elements}</div>;
|
return <div className={styles.dialogues}>{elements}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,3 @@
|
||||||
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Cookbook";
|
|
||||||
src: url("/assets/fonts/Cookbook.woff") format("woff");
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,12 @@ import Slot from './slot.jsx';
|
||||||
/**
|
/**
|
||||||
* The hotbar. 10 slots of inventory with an active selection.
|
* The hotbar. 10 slots of inventory with an active selection.
|
||||||
*/
|
*/
|
||||||
export default function Hotbar({active, onActivate, slots}) {
|
export default function Hotbar({
|
||||||
|
active,
|
||||||
|
hotbarIsHidden,
|
||||||
|
onActivate,
|
||||||
|
slots,
|
||||||
|
}) {
|
||||||
const Slots = slots.map((slot, i) => (
|
const Slots = slots.map((slot, i) => (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
|
@ -43,7 +48,9 @@ export default function Hotbar({active, onActivate, slots}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.hotbar}
|
className={styles.hotbar}
|
||||||
|
style={hotbarIsHidden ? {top: '-50px'} : {transition: 'none'}}
|
||||||
>
|
>
|
||||||
|
<p className={styles.label}>{slots[active] && slots[active].label}</p>
|
||||||
{Slots}
|
{Slots}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,13 +1,32 @@
|
||||||
.hotbar {
|
.hotbar {
|
||||||
align-self: center;
|
align-self: left;
|
||||||
--border: calc(var(--unit) * 3px);
|
--border: calc(var(--unit) * 3px);
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(02, 02, 57, 0.6);
|
||||||
border: var(--border) solid #444444;
|
border: var(--border) solid #444444;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
left: calc(var(--unit) * 20px);
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(var(--unit) * 25px);
|
top: calc(var(--unit) * 20px);
|
||||||
|
transition: top 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
background-color: transparent;
|
||||||
|
color: white;
|
||||||
|
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||||
|
left: 50%;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
text-shadow:
|
||||||
|
0px -1px 0px black,
|
||||||
|
1px 0px 0px black,
|
||||||
|
0px 1px 0px black,
|
||||||
|
-1px 0px 0px black
|
||||||
|
;
|
||||||
|
top: -17.5px;
|
||||||
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.slotWrapper {
|
.slotWrapper {
|
||||||
|
|
|
@ -108,7 +108,7 @@ export default function Ecs({applyFilters, camera, monopolizers, scale}) {
|
||||||
if (entity) {
|
if (entity) {
|
||||||
const {Direction, Position, Wielder} = entity;
|
const {Direction, Position, Wielder} = entity;
|
||||||
setPosition(Position.toJSON());
|
setPosition(Position.toJSON());
|
||||||
setProjected(Wielder.activeItem()?.project(Position.tile, Direction.direction));
|
setProjected(Wielder.activeItem()?.project(Position.tile, Direction.quantize(4)));
|
||||||
}
|
}
|
||||||
}, [ecs, mainEntity, scale]);
|
}, [ecs, mainEntity, scale]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import {Sprite} from '@pixi/sprite';
|
||||||
|
|
||||||
import {useRadians} from '@/react/context/radians.js';
|
import {useRadians} from '@/react/context/radians.js';
|
||||||
|
|
||||||
|
import {deferredLighting} from './lights.js';
|
||||||
|
|
||||||
const tileSize = {x: 16, y: 16};
|
const tileSize = {x: 16, y: 16};
|
||||||
const radius = 9;
|
const radius = 9;
|
||||||
|
|
||||||
|
@ -68,6 +70,7 @@ const TargetingGridInternal = PixiComponent('TargetingGrid', {
|
||||||
container.mask = area;
|
container.mask = area;
|
||||||
const top = new Container();
|
const top = new Container();
|
||||||
top.addChild(container, area);
|
top.addChild(container, area);
|
||||||
|
top.parentGroup = deferredLighting.diffuseGroup;
|
||||||
return top;
|
return top;
|
||||||
},
|
},
|
||||||
applyProps: ({children: [container]}, oldProps, {x, y, radians}) => {
|
applyProps: ({children: [container]}, oldProps, {x, y, radians}) => {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import addKeyListener from './add-key-listener.js';
|
||||||
import ClientEcs from './client-ecs.js';
|
import ClientEcs from './client-ecs.js';
|
||||||
import Disconnected from './dom/disconnected.jsx';
|
import Disconnected from './dom/disconnected.jsx';
|
||||||
import Chat from './dom/chat/chat.jsx';
|
import Chat from './dom/chat/chat.jsx';
|
||||||
|
import Bag from './dom/bag.jsx';
|
||||||
import Dom from './dom/dom.jsx';
|
import Dom from './dom/dom.jsx';
|
||||||
import Entities from './dom/entities.jsx';
|
import Entities from './dom/entities.jsx';
|
||||||
import HotBar from './dom/hotbar.jsx';
|
import HotBar from './dom/hotbar.jsx';
|
||||||
|
@ -18,6 +19,11 @@ import Pixi from './pixi/pixi.jsx';
|
||||||
import Devtools from './devtools.jsx';
|
import Devtools from './devtools.jsx';
|
||||||
import styles from './ui.module.css';
|
import styles from './ui.module.css';
|
||||||
|
|
||||||
|
const KEY_MAP = {
|
||||||
|
keyDown: 1,
|
||||||
|
keyUp: 0,
|
||||||
|
};
|
||||||
|
|
||||||
function emptySlots() {
|
function emptySlots() {
|
||||||
return Array(10).fill(undefined);
|
return Array(10).fill(undefined);
|
||||||
}
|
}
|
||||||
|
@ -50,6 +56,9 @@ function Ui({disconnected}) {
|
||||||
const [chatHistoryCaret, setChatHistoryCaret] = useState(-1);
|
const [chatHistoryCaret, setChatHistoryCaret] = useState(-1);
|
||||||
const [chatMessages, setChatMessages] = useState({});
|
const [chatMessages, setChatMessages] = useState({});
|
||||||
const [pendingMessage, setPendingMessage] = useState('');
|
const [pendingMessage, setPendingMessage] = useState('');
|
||||||
|
const [hotbarIsHidden, setHotbarIsHidden] = useState(true);
|
||||||
|
const [hotbarHideHandle, setHotbarHideHandle] = useState();
|
||||||
|
const [isInventoryOpen, setIsInventoryOpen] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function setEcsStuff() {
|
async function setEcsStuff() {
|
||||||
const {default: Components} = await import('@/ecs/components/index.js');
|
const {default: Components} = await import('@/ecs/components/index.js');
|
||||||
|
@ -76,6 +85,44 @@ function Ui({disconnected}) {
|
||||||
clearTimeout(handle);
|
clearTimeout(handle);
|
||||||
};
|
};
|
||||||
}, [disconnected]);
|
}, [disconnected]);
|
||||||
|
useEffect(() => {
|
||||||
|
return addKeyListener(document.body, ({type, payload}) => {
|
||||||
|
if (chatInputRef.current) {
|
||||||
|
chatInputRef.current.focus();
|
||||||
|
}
|
||||||
|
if (chatIsOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let actionPayload;
|
||||||
|
switch (payload) {
|
||||||
|
case 'w': {
|
||||||
|
actionPayload = {type: 'moveUp', value: KEY_MAP[type]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'a': {
|
||||||
|
actionPayload = {type: 'moveLeft', value: KEY_MAP[type]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's': {
|
||||||
|
actionPayload = {type: 'moveDown', value: KEY_MAP[type]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd': {
|
||||||
|
actionPayload = {type: 'moveRight', value: KEY_MAP[type]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (actionPayload) {
|
||||||
|
client.send({
|
||||||
|
type: 'Action',
|
||||||
|
payload: actionPayload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [
|
||||||
|
chatIsOpen,
|
||||||
|
client,
|
||||||
|
]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return addKeyListener(document.body, ({event, type, payload}) => {
|
return addKeyListener(document.body, ({event, type, payload}) => {
|
||||||
if ('Escape' === payload && 'keyDown' === type && chatIsOpen) {
|
if ('Escape' === payload && 'keyDown' === type && chatIsOpen) {
|
||||||
|
@ -88,10 +135,6 @@ function Ui({disconnected}) {
|
||||||
if (chatIsOpen) {
|
if (chatIsOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const KEY_MAP = {
|
|
||||||
keyDown: 1,
|
|
||||||
keyUp: 0,
|
|
||||||
};
|
|
||||||
let actionPayload;
|
let actionPayload;
|
||||||
switch (payload) {
|
switch (payload) {
|
||||||
case '-':
|
case '-':
|
||||||
|
@ -123,26 +166,25 @@ function Ui({disconnected}) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'w': {
|
|
||||||
actionPayload = {type: 'moveUp', value: KEY_MAP[type]};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'a': {
|
|
||||||
actionPayload = {type: 'moveLeft', value: KEY_MAP[type]};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 's': {
|
|
||||||
actionPayload = {type: 'moveDown', value: KEY_MAP[type]};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'd': {
|
|
||||||
actionPayload = {type: 'moveRight', value: KEY_MAP[type]};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ' ': {
|
case ' ': {
|
||||||
actionPayload = {type: 'use', value: KEY_MAP[type]};
|
actionPayload = {type: 'use', value: KEY_MAP[type]};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'Tab': {
|
||||||
|
if ('keyDown' === type) {
|
||||||
|
if (isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsInventoryOpen(!isInventoryOpen);
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
case 'Enter': {
|
case 'Enter': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
setChatIsOpen(true);
|
setChatIsOpen(true);
|
||||||
|
@ -161,60 +203,150 @@ function Ui({disconnected}) {
|
||||||
}
|
}
|
||||||
case '1': {
|
case '1': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 1};
|
actionPayload = {type: 'changeSlot', value: 1};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '2': {
|
case '2': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 2};
|
actionPayload = {type: 'changeSlot', value: 2};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '3': {
|
case '3': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 3};
|
actionPayload = {type: 'changeSlot', value: 3};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '4': {
|
case '4': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 4};
|
actionPayload = {type: 'changeSlot', value: 4};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '5': {
|
case '5': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 5};
|
actionPayload = {type: 'changeSlot', value: 5};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '6': {
|
case '6': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 6};
|
actionPayload = {type: 'changeSlot', value: 6};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '7': {
|
case '7': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 7};
|
actionPayload = {type: 'changeSlot', value: 7};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '8': {
|
case '8': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 8};
|
actionPayload = {type: 'changeSlot', value: 8};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '9': {
|
case '9': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 9};
|
actionPayload = {type: 'changeSlot', value: 9};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '0': {
|
case '0': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
actionPayload = {type: 'changeSlot', value: 10};
|
actionPayload = {type: 'changeSlot', value: 10};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -227,7 +359,17 @@ function Ui({disconnected}) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [chatIsOpen, client, debug, devtoolsIsOpen, monopolizers, setDebug, setScale]);
|
}, [
|
||||||
|
chatIsOpen,
|
||||||
|
client,
|
||||||
|
debug,
|
||||||
|
devtoolsIsOpen,
|
||||||
|
hotbarHideHandle,
|
||||||
|
isInventoryOpen,
|
||||||
|
monopolizers,
|
||||||
|
setDebug,
|
||||||
|
setScale,
|
||||||
|
]);
|
||||||
usePacket('EcsChange', async () => {
|
usePacket('EcsChange', async () => {
|
||||||
setEcs(new ClientEcs({Components, Systems}));
|
setEcs(new ClientEcs({Components, Systems}));
|
||||||
setMainEntity(undefined);
|
setMainEntity(undefined);
|
||||||
|
@ -387,6 +529,15 @@ function Ui({disconnected}) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!isInventoryOpen) {
|
||||||
|
setHotbarIsHidden(false);
|
||||||
|
if (hotbarHideHandle) {
|
||||||
|
clearTimeout(hotbarHideHandle);
|
||||||
|
}
|
||||||
|
setHotbarHideHandle(setTimeout(() => {
|
||||||
|
setHotbarIsHidden(true);
|
||||||
|
}, 4000));
|
||||||
|
}
|
||||||
if (event.deltaY > 0) {
|
if (event.deltaY > 0) {
|
||||||
client.send({
|
client.send({
|
||||||
type: 'Action',
|
type: 'Action',
|
||||||
|
@ -411,6 +562,7 @@ function Ui({disconnected}) {
|
||||||
<Dom>
|
<Dom>
|
||||||
<HotBar
|
<HotBar
|
||||||
active={activeSlot}
|
active={activeSlot}
|
||||||
|
hotbarIsHidden={hotbarIsHidden}
|
||||||
onActivate={(i) => {
|
onActivate={(i) => {
|
||||||
client.send({
|
client.send({
|
||||||
type: 'Action',
|
type: 'Action',
|
||||||
|
@ -419,6 +571,10 @@ function Ui({disconnected}) {
|
||||||
}}
|
}}
|
||||||
slots={hotbarSlots}
|
slots={hotbarSlots}
|
||||||
/>
|
/>
|
||||||
|
<Bag
|
||||||
|
isInventoryOpen={isInventoryOpen}
|
||||||
|
slots={Array(30).fill(undefined)}
|
||||||
|
/>
|
||||||
<Entities
|
<Entities
|
||||||
camera={camera}
|
camera={camera}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
|
|
|
@ -26,3 +26,8 @@ body {
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border: 3px solid #333;
|
border: 3px solid #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Cookbook";
|
||||||
|
src: url("/assets/fonts/Cookbook.woff") format("woff");
|
||||||
|
}
|
||||||
|
|
|
@ -116,7 +116,8 @@ export default async function createHomestead(id) {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
Controlled: {},
|
Controlled: {},
|
||||||
Direction: {direction: 2},
|
Direction: {},
|
||||||
|
Emitter: {},
|
||||||
Forces: {},
|
Forces: {},
|
||||||
Interactive: {
|
Interactive: {
|
||||||
interacting: 1,
|
interacting: 1,
|
||||||
|
@ -167,16 +168,18 @@ export default async function createHomestead(id) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
collisionStartScript: `
|
collisionStartScript: `
|
||||||
ecs.switchEcs(
|
if (other.Player) {
|
||||||
other,
|
ecs.switchEcs(
|
||||||
'town',
|
other,
|
||||||
{
|
'town',
|
||||||
Position: {
|
{
|
||||||
x: 940,
|
Position: {
|
||||||
y: 480,
|
x: 940,
|
||||||
|
y: 480,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
);
|
}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default async function createPlayer(id) {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
Controlled: {},
|
Controlled: {},
|
||||||
Direction: {direction: 2},
|
Direction: {},
|
||||||
Ecs: {path: ['homesteads', `${id}`].join('/')},
|
Ecs: {path: ['homesteads', `${id}`].join('/')},
|
||||||
Emitter: {},
|
Emitter: {},
|
||||||
Forces: {},
|
Forces: {},
|
||||||
|
|
|
@ -38,16 +38,18 @@ export default async function createTown() {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
collisionStartScript: `
|
collisionStartScript: `
|
||||||
ecs.switchEcs(
|
if (other.Player) {
|
||||||
other,
|
ecs.switchEcs(
|
||||||
['homesteads', '0'].join('/'),
|
other,
|
||||||
{
|
['homesteads', other.Player.id].join('/'),
|
||||||
Position: {
|
{
|
||||||
x: 20,
|
Position: {
|
||||||
y: 438,
|
x: 20,
|
||||||
|
y: 438,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
);
|
}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
Position: {x: 952, y: 480},
|
Position: {x: 952, y: 480},
|
||||||
|
|
|
@ -63,6 +63,7 @@ export default class Engine {
|
||||||
if (entity !== connectedPlayer.entity) {
|
if (entity !== connectedPlayer.entity) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const {id} = entity.Player;
|
||||||
// remove entity link to connection to start queueing actions and pause updates
|
// remove entity link to connection to start queueing actions and pause updates
|
||||||
delete connectedPlayer.entity;
|
delete connectedPlayer.entity;
|
||||||
// forget previous state
|
// forget previous state
|
||||||
|
@ -93,6 +94,7 @@ export default class Engine {
|
||||||
}
|
}
|
||||||
// recreate the entity in the new ECS and again associate it with the connection
|
// recreate the entity in the new ECS and again associate it with the connection
|
||||||
connectedPlayer.entity = engine.ecses[path].get(await engine.ecses[path].create(dumped));
|
connectedPlayer.entity = engine.ecses[path].get(await engine.ecses[path].create(dumped));
|
||||||
|
connectedPlayer.entity.Player.id = id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,11 +228,12 @@ export default class Engine {
|
||||||
await this.loadEcs(entityJson.Ecs.path);
|
await this.loadEcs(entityJson.Ecs.path);
|
||||||
}
|
}
|
||||||
const ecs = this.ecses[entityJson.Ecs.path];
|
const ecs = this.ecses[entityJson.Ecs.path];
|
||||||
const entity = await ecs.create(entityJson);
|
const entity = ecs.get(await ecs.create(entityJson));
|
||||||
|
entity.Player.id = id
|
||||||
this.connectedPlayers.set(
|
this.connectedPlayers.set(
|
||||||
connection,
|
connection,
|
||||||
{
|
{
|
||||||
entity: ecs.get(entity),
|
entity,
|
||||||
id,
|
id,
|
||||||
memory: {
|
memory: {
|
||||||
chunks: new Map(),
|
chunks: new Map(),
|
||||||
|
|
|
@ -45,7 +45,12 @@ export const {
|
||||||
SQRT2,
|
SQRT2,
|
||||||
} = Math;
|
} = Math;
|
||||||
|
|
||||||
|
export const SQRT_2_2 = Math.sqrt(2) / 2;
|
||||||
|
export const EIGHTH_PI = Math.PI / 8;
|
||||||
|
export const QUARTER_PI = Math.PI / 4;
|
||||||
|
export const HALF_PI = Math.PI / 2;
|
||||||
export const TAU = Math.PI * 2;
|
export const TAU = Math.PI * 2;
|
||||||
|
export const PI_180 = Math.PI / 180;
|
||||||
|
|
||||||
export function bresenham({x: x1, y: y1}, {x: x2, y: y2}) {
|
export function bresenham({x: x1, y: y1}, {x: x2, y: y2}) {
|
||||||
const points = [];
|
const points = [];
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"icon": "/assets/brush/brush.png",
|
"icon": "/assets/brush/brush.png",
|
||||||
|
"label": "Brush",
|
||||||
"start": "/assets/brush/start.js"
|
"start": "/assets/brush/start.js"
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
const {Collider, Controlled, Interacts, Inventory, Sound, Sprite} = wielder
|
const {Collider, Controlled, Interacts, Inventory, Sound, Sprite} = wielder
|
||||||
const entities = Collider.closest(Interacts.aabb());
|
const entities = Collider.closest(Interacts.aabb());
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
const {Tags} = entity;
|
const {Emitter, Position, Tags} = entity;
|
||||||
if (Tags && Tags.has('kittan')) {
|
if (Tags && Tags.has('kittan')) {
|
||||||
Controlled.locked = 1
|
Controlled.locked = 1
|
||||||
const [, direction] = Sprite.animation.split(':')
|
const [, direction] = Sprite.animation.split(':')
|
||||||
|
@ -17,6 +17,94 @@ for (const entity of entities) {
|
||||||
source: '/assets/furball/furball.json',
|
source: '/assets/furball/furball.json',
|
||||||
});
|
});
|
||||||
Controlled.locked = 0;
|
Controlled.locked = 0;
|
||||||
|
|
||||||
|
const heartParticles = {
|
||||||
|
behaviors: [
|
||||||
|
{
|
||||||
|
type: 'moveAcceleration',
|
||||||
|
config: {
|
||||||
|
accel: {
|
||||||
|
x: 0,
|
||||||
|
y: -100,
|
||||||
|
},
|
||||||
|
minStart: 0,
|
||||||
|
maxStart: 0,
|
||||||
|
rotate: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'moveSpeed',
|
||||||
|
config: {
|
||||||
|
speed: {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
time: 0,
|
||||||
|
value: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: 1,
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'scale',
|
||||||
|
config: {
|
||||||
|
scale: {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
value: 0.5,
|
||||||
|
time: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0.125,
|
||||||
|
time: 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'textureSingle',
|
||||||
|
config: {
|
||||||
|
texture: '/assets/heart/heart.png',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lifetime: {
|
||||||
|
min: 0.5,
|
||||||
|
max: 0.5,
|
||||||
|
},
|
||||||
|
frequency: 0.1,
|
||||||
|
emitterLifetime: 0.25,
|
||||||
|
pos: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
rotation: 180,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Emitter.emit({
|
||||||
|
...heartParticles,
|
||||||
|
behaviors: [
|
||||||
|
...heartParticles.behaviors,
|
||||||
|
{
|
||||||
|
type: 'spawnShape',
|
||||||
|
config: {
|
||||||
|
type: 'rect',
|
||||||
|
data: {
|
||||||
|
x: Position.x - 8,
|
||||||
|
y: Position.y,
|
||||||
|
w: 16,
|
||||||
|
h: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"icon": "/assets/furball/furball.png"
|
"icon": "/assets/furball/furball.png",
|
||||||
|
"label": "Fur Ball"
|
||||||
}
|
}
|
||||||
|
|
BIN
public/assets/heart/heart.png
Normal file
BIN
public/assets/heart/heart.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 570 B |
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"icon": "/assets/hoe/icon.png",
|
"icon": "/assets/hoe/icon.png",
|
||||||
|
"label": "Hoe",
|
||||||
"projectionCheck": "/assets/hoe/projection-check.js",
|
"projectionCheck": "/assets/hoe/projection-check.js",
|
||||||
"projection": {
|
"projection": {
|
||||||
"distance": [3, -1],
|
"distance": [3, -1],
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const {Direction, Position, Wielder} = wielder
|
const {Direction, Position, Wielder} = wielder
|
||||||
const projected = Wielder.activeItem()?.project(Position.tile, Direction.direction)
|
const projected = Wielder.activeItem()?.project(Position.tile, Direction.quantize(4))
|
||||||
if (projected?.length > 0) {
|
if (projected?.length > 0) {
|
||||||
|
|
||||||
const {Controlled, Emitter, Sound, Sprite} = wielder
|
const {Controlled, Emitter, Sound, Sprite} = wielder
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
entity.Direction.direction = Math.floor(Math.random() * 4);
|
entity.Direction.direction = Math.random() * Math.TAU;
|
||||||
|
|
||||||
|
entity.Controlled.directionMove(entity.Direction.direction);
|
||||||
|
|
||||||
const map = {0: 'moveUp', 1: 'moveRight', 2: 'moveDown', 3: 'moveLeft'};
|
|
||||||
entity.Controlled[map[entity.Direction.direction]] = 1;
|
|
||||||
await wait(0.25 + Math.random() * 2.25);
|
await wait(0.25 + Math.random() * 2.25);
|
||||||
entity.Controlled[map[entity.Direction.direction]] = 0;
|
|
||||||
|
entity.Controlled.stop();
|
||||||
|
|
||||||
entity.Sprite.isAnimating = 0;
|
entity.Sprite.isAnimating = 0;
|
||||||
|
|
||||||
await wait(1 + Math.random() * 3);
|
await wait(1 + Math.random() * 3);
|
||||||
|
|
||||||
entity.Direction.direction = Math.floor(Math.random() * 4);
|
entity.Direction.direction = Math.random() * Math.TAU;
|
||||||
|
|
||||||
await wait(0.5 + Math.random() * 2.5);
|
await wait(0.5 + Math.random() * 2.5);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"icon": "/assets/potion/icon.png",
|
"icon": "/assets/potion/icon.png",
|
||||||
|
"label": "Potion",
|
||||||
"start": "/assets/potion/start.js"
|
"start": "/assets/potion/start.js"
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
const {Direction, Position, Wielder} = wielder
|
const {Direction, Position, Wielder} = wielder
|
||||||
const projected = Wielder.activeItem()?.project(Position.tile, Direction.direction)
|
const projected = Wielder.activeItem()?.project(Position.tile, Direction.quantize(4))
|
||||||
if (projected?.length > 0) {
|
if (projected?.length > 0) {
|
||||||
const {Controlled, Emitter, Sound, Sprite} = wielder
|
const {Controlled, Emitter, Sound, Sprite} = wielder
|
||||||
const {TileLayers} = ecs.get(1)
|
const {TileLayers} = ecs.get(1)
|
||||||
|
@ -120,9 +120,10 @@ if (projected?.length > 0) {
|
||||||
Sound.play('/assets/sow.wav');
|
Sound.play('/assets/sow.wav');
|
||||||
Sprite.animation = ['moving', direction].join(':');
|
Sprite.animation = ['moving', direction].join(':');
|
||||||
|
|
||||||
|
const directionMap = {0: 'right', 1: 'down', 2: 'left', 3: 'up'};
|
||||||
for (let i = 0; i < 6; ++i) {
|
for (let i = 0; i < 6; ++i) {
|
||||||
Direction.direction = Math.floor(Math.random() * 4);
|
Direction.direction = Math.HALF_PI * Math.floor(Math.random() * 4);
|
||||||
Sprite.animation = ['moving', 0 === Direction.direction ? 'up' : (1 === Direction.direction ? 'right' : (2 === Direction.direction ? 'down' : 'left'))].join(':');
|
Sprite.animation = ['moving', directionMap[Direction.quantize(4)]].join(':');
|
||||||
await wait(0.125);
|
await wait(0.125);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"icon": "/assets/tomato-seeds/icon.png",
|
"icon": "/assets/tomato-seeds/icon.png",
|
||||||
|
"label": "Tomato Seeds",
|
||||||
"projection": {
|
"projection": {
|
||||||
"distance": [1, -1],
|
"distance": [1, -1],
|
||||||
"grid": [
|
"grid": [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const {Direction, Position, Wielder} = wielder
|
const {Direction, Position, Wielder} = wielder
|
||||||
const projected = Wielder.activeItem()?.project(Position.tile, Direction.direction)
|
const projected = Wielder.activeItem()?.project(Position.tile, Direction.quantize(4))
|
||||||
if (projected?.length > 0) {
|
if (projected?.length > 0) {
|
||||||
|
|
||||||
const {Controlled, Emitter, Sound, Sprite} = wielder
|
const {Controlled, Emitter, Sound, Sprite} = wielder
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"icon": "/assets/watering-can/icon.png",
|
"icon": "/assets/watering-can/icon.png",
|
||||||
|
"label": "Watering Can",
|
||||||
"projectionCheck": "/assets/watering-can/projection-check.js",
|
"projectionCheck": "/assets/watering-can/projection-check.js",
|
||||||
"projection": {
|
"projection": {
|
||||||
"distance": [3, -1],
|
"distance": [3, -1],
|
||||||
|
|
49
server.js
49
server.js
|
@ -3,6 +3,9 @@ import compression from 'compression';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import morgan from 'morgan';
|
import morgan from 'morgan';
|
||||||
|
|
||||||
|
// patch pixi server context
|
||||||
|
import('./app/server/pixi-context.js');
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const isInsecure = process.env.SILPHIUS_INSECURE_HTTP;
|
const isInsecure = process.env.SILPHIUS_INSECURE_HTTP;
|
||||||
|
|
||||||
|
@ -32,6 +35,20 @@ else {
|
||||||
server = createServer(serverOptions, app);
|
server = createServer(serverOptions, app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// immediately start listening and queueing up connections
|
||||||
|
let resolve, promise = new Promise((res) => {
|
||||||
|
resolve = res;
|
||||||
|
});
|
||||||
|
app.use(async (req, res, next) => {
|
||||||
|
await promise;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
const port = process.env.PORT || 3000;
|
||||||
|
server.listen(port, () =>
|
||||||
|
console.log(`Express server listening at http${isInsecure ? '' : 's'}://localhost:${port}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
// possibly load dev server and build the request handler up front
|
||||||
const viteDevServer = isProduction
|
const viteDevServer = isProduction
|
||||||
? undefined
|
? undefined
|
||||||
: await import('vite').then((vite) =>
|
: await import('vite').then((vite) =>
|
||||||
|
@ -39,26 +56,18 @@ const viteDevServer = isProduction
|
||||||
server: {middlewareMode: {server}},
|
server: {middlewareMode: {server}},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
const build = () => (
|
||||||
let websocketBuilt = false;
|
viteDevServer
|
||||||
|
? viteDevServer.ssrLoadModule('virtual:remix/server-build')
|
||||||
|
: import('./build/server/index.js')
|
||||||
|
);
|
||||||
|
const ssr = await build();
|
||||||
|
await ssr.entry.module.websocket(server, viteDevServer);
|
||||||
const remixHandler = createRequestHandler({
|
const remixHandler = createRequestHandler({
|
||||||
build: async () => {
|
build: () => ssr,
|
||||||
// patch pixi server context
|
|
||||||
import('./app/server/pixi-context.js');
|
|
||||||
const ssr = await (
|
|
||||||
viteDevServer
|
|
||||||
? viteDevServer.ssrLoadModule('virtual:remix/server-build')
|
|
||||||
: import('./build/server/index.js')
|
|
||||||
);
|
|
||||||
if (!websocketBuilt) {
|
|
||||||
await ssr.entry.module.websocket(server, viteDevServer);
|
|
||||||
websocketBuilt = true;
|
|
||||||
}
|
|
||||||
return ssr;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// configure middleware
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
|
|
||||||
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
|
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
|
||||||
|
@ -85,7 +94,5 @@ app.use(morgan('tiny'));
|
||||||
// handle SSR requests
|
// handle SSR requests
|
||||||
app.all('*', remixHandler);
|
app.all('*', remixHandler);
|
||||||
|
|
||||||
const port = process.env.PORT || 3000;
|
// finally let requests resolve
|
||||||
server.listen(port, () =>
|
resolve();
|
||||||
console.log(`Express server listening at http${isInsecure ? '' : 's'}://localhost:${port}`)
|
|
||||||
);
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user