feat: interactions
This commit is contained in:
parent
bb87f553fc
commit
43a6b12488
37
app/ecs-components/interactive.js
Normal file
37
app/ecs-components/interactive.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import Component from '@/ecs/component.js';
|
||||||
|
|
||||||
|
export default class Interactive extends Component {
|
||||||
|
instanceFromSchema() {
|
||||||
|
const {ecs} = this;
|
||||||
|
return class ControlledInstance extends super.instanceFromSchema() {
|
||||||
|
interact(initiator) {
|
||||||
|
this.interactScriptInstance.context.initiator = initiator;
|
||||||
|
const {Ticking} = ecs.get(this.entity);
|
||||||
|
Ticking.addTickingPromise(this.interactScriptInstance.tickingPromise());
|
||||||
|
}
|
||||||
|
get interacting() {
|
||||||
|
return !!this.$$interacting;
|
||||||
|
}
|
||||||
|
set interacting(interacting) {
|
||||||
|
this.$$interacting = interacting ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async load(instance) {
|
||||||
|
// heavy handed...
|
||||||
|
if ('undefined' !== typeof window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
instance.interactScriptInstance = await this.ecs.readScript(
|
||||||
|
instance.interactScript,
|
||||||
|
{
|
||||||
|
ecs: this.ecs,
|
||||||
|
subject: this.ecs.get(instance.entity),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
static properties = {
|
||||||
|
interacting: {type: 'uint8'},
|
||||||
|
interactScript: {type: 'string'},
|
||||||
|
};
|
||||||
|
}
|
14
app/ecs-components/interacts.js
Normal file
14
app/ecs-components/interacts.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import Component from '@/ecs/component.js';
|
||||||
|
|
||||||
|
export default class Interacts extends Component {
|
||||||
|
instanceFromSchema() {
|
||||||
|
return class ControlledInstance extends super.instanceFromSchema() {
|
||||||
|
toJSON() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static properties = {
|
||||||
|
willInteractWith: {type: 'uint32'},
|
||||||
|
};
|
||||||
|
}
|
47
app/ecs-systems/interactions.js
Normal file
47
app/ecs-systems/interactions.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import {System} from '@/ecs/index.js';
|
||||||
|
|
||||||
|
export default class PlantGrowth extends System {
|
||||||
|
|
||||||
|
static queries() {
|
||||||
|
return {
|
||||||
|
default: ['Interacts'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tick() {
|
||||||
|
for (const {Direction, Interacts, Position} of this.select('default')) {
|
||||||
|
Interacts.willInteractWith = 0
|
||||||
|
const box = {
|
||||||
|
x: Position.x - 8,
|
||||||
|
y: Position.y - 8,
|
||||||
|
w: 16,
|
||||||
|
h: 16,
|
||||||
|
}
|
||||||
|
if (0 === Direction.direction) {
|
||||||
|
box.y -= 16
|
||||||
|
}
|
||||||
|
if (1 === Direction.direction) {
|
||||||
|
box.x += 16
|
||||||
|
}
|
||||||
|
if (2 === Direction.direction) {
|
||||||
|
box.y += 16
|
||||||
|
}
|
||||||
|
if (3 === Direction.direction) {
|
||||||
|
box.x -= 16
|
||||||
|
}
|
||||||
|
// todo sort
|
||||||
|
const entities = Array.from(this.ecs.system('UpdateSpatialHash').within(
|
||||||
|
box.x,
|
||||||
|
box.y,
|
||||||
|
box.w,
|
||||||
|
box.h,
|
||||||
|
));
|
||||||
|
for (let j = 0; j < entities.length; ++j) {
|
||||||
|
if (entities[j].Interactive && entities[j].Interactive.interacting) {
|
||||||
|
Interacts.willInteractWith = entities[0].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -58,7 +58,7 @@ export default class Engine {
|
||||||
entity,
|
entity,
|
||||||
payload,
|
payload,
|
||||||
] of this.incomingActions) {
|
] of this.incomingActions) {
|
||||||
const {Controlled, Inventory, Wielder} = entity;
|
const {Controlled, Ecs, Interacts, Inventory, Wielder} = entity;
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
case 'changeSlot': {
|
case 'changeSlot': {
|
||||||
if (!Controlled.locked) {
|
if (!Controlled.locked) {
|
||||||
|
@ -85,6 +85,18 @@ export default class Engine {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'interact': {
|
||||||
|
if (!Controlled.locked) {
|
||||||
|
if (payload.value) {
|
||||||
|
if (Interacts.willInteractWith) {
|
||||||
|
const ecs = this.ecses[Ecs.path];
|
||||||
|
const subject = ecs.get(Interacts.willInteractWith);
|
||||||
|
subject.Interactive.interact(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.incomingActions = [];
|
this.incomingActions = [];
|
||||||
|
@ -153,6 +165,7 @@ export default class Engine {
|
||||||
'RunAnimations',
|
'RunAnimations',
|
||||||
'RunTickingPromises',
|
'RunTickingPromises',
|
||||||
'Water',
|
'Water',
|
||||||
|
'Interactions',
|
||||||
];
|
];
|
||||||
defaultSystems.forEach((defaultSystem) => {
|
defaultSystems.forEach((defaultSystem) => {
|
||||||
const System = ecs.system(defaultSystem);
|
const System = ecs.system(defaultSystem);
|
||||||
|
@ -171,6 +184,7 @@ export default class Engine {
|
||||||
Ecs: {path: join('homesteads', `${id}`)},
|
Ecs: {path: join('homesteads', `${id}`)},
|
||||||
Emitter: {},
|
Emitter: {},
|
||||||
Forces: {},
|
Forces: {},
|
||||||
|
Interacts: {},
|
||||||
Inventory: {
|
Inventory: {
|
||||||
slots: {
|
slots: {
|
||||||
// 1: {
|
// 1: {
|
||||||
|
|
|
@ -7,6 +7,7 @@ const WIRE_MAP = {
|
||||||
'moveLeft': 3,
|
'moveLeft': 3,
|
||||||
'use': 4,
|
'use': 4,
|
||||||
'changeSlot': 5,
|
'changeSlot': 5,
|
||||||
|
'interact': 6,
|
||||||
};
|
};
|
||||||
Object.entries(WIRE_MAP)
|
Object.entries(WIRE_MAP)
|
||||||
.forEach(([k, v]) => {
|
.forEach(([k, v]) => {
|
||||||
|
|
|
@ -1,12 +1,43 @@
|
||||||
|
import {AdjustmentFilter} from '@pixi/filter-adjustment';
|
||||||
|
import {GlowFilter} from '@pixi/filter-glow';
|
||||||
import {Container} from '@pixi/react';
|
import {Container} from '@pixi/react';
|
||||||
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
|
import {useEcs} from '@/context/ecs.js';
|
||||||
|
import {useMainEntity} from '@/context/main-entity.js';
|
||||||
|
|
||||||
import Entity from './entity.jsx';
|
import Entity from './entity.jsx';
|
||||||
|
|
||||||
export default function Entities({entities}) {
|
export default function Entities({entities}) {
|
||||||
|
const [ecs] = useEcs();
|
||||||
|
const [mainEntity] = useMainEntity();
|
||||||
|
const [radians, setRadians] = useState(0);
|
||||||
|
const [willInteractWith, setWillInteractWith] = useState(0);
|
||||||
|
const [filters] = useState([new AdjustmentFilter(), new GlowFilter({color: 0x0})]);
|
||||||
|
const pulse = (Math.cos(radians) + 1) * 0.5;
|
||||||
|
filters[0].brightness = (pulse * 0.75) + 1;
|
||||||
|
filters[1].outerStrength = pulse * 0.5;
|
||||||
|
useEffect(() => {
|
||||||
|
setRadians(0);
|
||||||
|
const handle = setInterval(() => {
|
||||||
|
setRadians((radians) => (radians + 0.1) % (Math.PI * 2))
|
||||||
|
}, 50);
|
||||||
|
return () => {
|
||||||
|
clearInterval(handle);
|
||||||
|
};
|
||||||
|
}, [willInteractWith]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mainEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setWillInteractWith(ecs.get(mainEntity).Interacts.willInteractWith);
|
||||||
|
}, [entities, ecs, mainEntity]);
|
||||||
const renderables = [];
|
const renderables = [];
|
||||||
for (const id in entities) {
|
for (const id in entities) {
|
||||||
|
const isHighlightedInteraction = id == willInteractWith;
|
||||||
renderables.push(
|
renderables.push(
|
||||||
<Entity
|
<Entity
|
||||||
|
filters={isHighlightedInteraction ? filters : []}
|
||||||
entity={entities[id]}
|
entity={entities[id]}
|
||||||
key={id}
|
key={id}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -29,7 +29,7 @@ function Crosshair({x, y}) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Entities({entity}) {
|
function Entity({entity, ...rest}) {
|
||||||
const [debug] = useDebug();
|
const [debug] = useDebug();
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -41,6 +41,7 @@ function Entities({entity}) {
|
||||||
{entity.Sprite && (
|
{entity.Sprite && (
|
||||||
<Sprite
|
<Sprite
|
||||||
entity={entity}
|
entity={entity}
|
||||||
|
{...rest}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{entity.Emitter && (
|
{entity.Emitter && (
|
||||||
|
@ -55,4 +56,4 @@ function Entities({entity}) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(Entities);
|
export default memo(Entity);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {Sprite as PixiSprite} from '@pixi/react';
|
||||||
|
|
||||||
import {useAsset} from '@/context/assets.js';
|
import {useAsset} from '@/context/assets.js';
|
||||||
|
|
||||||
export default function Sprite({entity}) {
|
export default function Sprite({entity, ...rest}) {
|
||||||
const asset = useAsset(entity.Sprite.source);
|
const asset = useAsset(entity.Sprite.source);
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -21,6 +21,7 @@ export default function Sprite({entity}) {
|
||||||
texture={texture}
|
texture={texture}
|
||||||
x={Math.round(entity.Position.x)}
|
x={Math.round(entity.Position.x)}
|
||||||
y={Math.round(entity.Position.y)}
|
y={Math.round(entity.Position.y)}
|
||||||
|
{...rest}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -92,6 +92,10 @@ export default function Ui({disconnected}) {
|
||||||
actionPayload = {type: 'use', value: KEY_MAP[type]};
|
actionPayload = {type: 'use', value: KEY_MAP[type]};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'e': {
|
||||||
|
actionPayload = {type: 'interact', value: KEY_MAP[type]};
|
||||||
|
break;
|
||||||
|
}
|
||||||
case '1': {
|
case '1': {
|
||||||
if ('keyDown' === type) {
|
if ('keyDown' === type) {
|
||||||
actionPayload = {type: 'changeSlot', value: 1};
|
actionPayload = {type: 'changeSlot', value: 1};
|
||||||
|
|
18
package-lock.json
generated
18
package-lock.json
generated
|
@ -7,6 +7,8 @@
|
||||||
"name": "silphius-next",
|
"name": "silphius-next",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@msgpack/msgpack": "^3.0.0-beta2",
|
"@msgpack/msgpack": "^3.0.0-beta2",
|
||||||
|
"@pixi/filter-adjustment": "^5.1.1",
|
||||||
|
"@pixi/filter-glow": "^5.2.1",
|
||||||
"@pixi/particle-emitter": "^5.0.8",
|
"@pixi/particle-emitter": "^5.0.8",
|
||||||
"@pixi/react": "^7.1.2",
|
"@pixi/react": "^7.1.2",
|
||||||
"@pixi/spritesheet": "^7.4.2",
|
"@pixi/spritesheet": "^7.4.2",
|
||||||
|
@ -3302,6 +3304,14 @@
|
||||||
"@pixi/core": "7.4.2"
|
"@pixi/core": "7.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pixi/filter-adjustment": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pixi/filter-adjustment/-/filter-adjustment-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-AUHe03rmqXwV1ylAHq62t19AolPWOOYomCcL+Qycb1tf+LbM8FWpGXC6wmU1PkUrhgNc958uM9TrA9nRpplViA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@pixi/core": "^7.0.0-X"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pixi/filter-alpha": {
|
"node_modules/@pixi/filter-alpha": {
|
||||||
"version": "7.4.2",
|
"version": "7.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@pixi/filter-alpha/-/filter-alpha-7.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@pixi/filter-alpha/-/filter-alpha-7.4.2.tgz",
|
||||||
|
@ -3342,6 +3352,14 @@
|
||||||
"@pixi/core": "7.4.2"
|
"@pixi/core": "7.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pixi/filter-glow": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pixi/filter-glow/-/filter-glow-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-94I4XePDF9yqqA6KQuhPSphEHPJ2lXfqJLn0Bes8VVdwft0Ianj1wALqjoSUeBWqiJbhjBEXGDNkRZhPHvY3Xg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@pixi/core": "^7.0.0-X"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pixi/filter-noise": {
|
"node_modules/@pixi/filter-noise": {
|
||||||
"version": "7.4.2",
|
"version": "7.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@pixi/filter-noise/-/filter-noise-7.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@pixi/filter-noise/-/filter-noise-7.4.2.tgz",
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@msgpack/msgpack": "^3.0.0-beta2",
|
"@msgpack/msgpack": "^3.0.0-beta2",
|
||||||
|
"@pixi/filter-adjustment": "^5.1.1",
|
||||||
|
"@pixi/filter-glow": "^5.2.1",
|
||||||
"@pixi/particle-emitter": "^5.0.8",
|
"@pixi/particle-emitter": "^5.0.8",
|
||||||
"@pixi/react": "^7.1.2",
|
"@pixi/react": "^7.1.2",
|
||||||
"@pixi/spritesheet": "^7.4.2",
|
"@pixi/spritesheet": "^7.4.2",
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
const {Sprite} = ecs.get(plant.entity);
|
const {Interactive, Sprite} = ecs.get(plant.entity);
|
||||||
|
|
||||||
|
plant.growth = 0
|
||||||
|
|
||||||
if (plant.stage < 3) {
|
if (plant.stage < 3) {
|
||||||
plant.stage += 1
|
plant.stage += 1
|
||||||
|
@ -6,8 +8,8 @@ if (plant.stage < 3) {
|
||||||
if (4 === plant.stage) {
|
if (4 === plant.stage) {
|
||||||
plant.stage = 3
|
plant.stage = 3
|
||||||
}
|
}
|
||||||
if (3 !== plant.stage) {
|
if (3 === plant.stage) {
|
||||||
plant.growth = 0
|
Interactive.interacting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sprite.animation = ['stage', plant.stage].join('/')
|
Sprite.animation = ['stage', plant.stage].join('/')
|
||||||
|
|
4
public/assets/tomato-plant/interact.js
Normal file
4
public/assets/tomato-plant/interact.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
const {Interactive, Plant, Sprite} = subject;
|
||||||
|
Interactive.interacting = false;
|
||||||
|
Plant.stage = 4;
|
||||||
|
Sprite.animation = ['stage', Plant.stage].join('/')
|
|
@ -9,10 +9,13 @@ if (projected?.length > 0) {
|
||||||
const [, direction] = Sprite.animation.split(':')
|
const [, direction] = Sprite.animation.split(':')
|
||||||
|
|
||||||
const plant = {
|
const plant = {
|
||||||
|
Interactive: {
|
||||||
|
interactScript: '/assets/tomato-plant/interact.js',
|
||||||
|
},
|
||||||
Plant: {
|
Plant: {
|
||||||
growScript: '/assets/tomato-plant/grow.js',
|
growScript: '/assets/tomato-plant/grow.js',
|
||||||
mayGrowScript: '/assets/tomato-plant/may-grow.js',
|
mayGrowScript: '/assets/tomato-plant/may-grow.js',
|
||||||
stages: Array(5).fill(60),
|
stages: Array(5).fill(5),
|
||||||
},
|
},
|
||||||
Sprite: {
|
Sprite: {
|
||||||
anchor: {x: 0.5, y: 0.75},
|
anchor: {x: 0.5, y: 0.75},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user