feat: interactions

This commit is contained in:
cha0s 2024-07-01 18:12:53 -05:00
parent bb87f553fc
commit 43a6b12488
14 changed files with 187 additions and 8 deletions

View 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'},
};
}

View 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'},
};
}

View 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;
}
}
}
}
}

View File

@ -58,7 +58,7 @@ export default class Engine {
entity,
payload,
] of this.incomingActions) {
const {Controlled, Inventory, Wielder} = entity;
const {Controlled, Ecs, Interacts, Inventory, Wielder} = entity;
switch (payload.type) {
case 'changeSlot': {
if (!Controlled.locked) {
@ -85,6 +85,18 @@ export default class Engine {
}
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 = [];
@ -153,6 +165,7 @@ export default class Engine {
'RunAnimations',
'RunTickingPromises',
'Water',
'Interactions',
];
defaultSystems.forEach((defaultSystem) => {
const System = ecs.system(defaultSystem);
@ -171,6 +184,7 @@ export default class Engine {
Ecs: {path: join('homesteads', `${id}`)},
Emitter: {},
Forces: {},
Interacts: {},
Inventory: {
slots: {
// 1: {

View File

@ -7,6 +7,7 @@ const WIRE_MAP = {
'moveLeft': 3,
'use': 4,
'changeSlot': 5,
'interact': 6,
};
Object.entries(WIRE_MAP)
.forEach(([k, v]) => {

View File

@ -1,12 +1,43 @@
import {AdjustmentFilter} from '@pixi/filter-adjustment';
import {GlowFilter} from '@pixi/filter-glow';
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';
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 = [];
for (const id in entities) {
const isHighlightedInteraction = id == willInteractWith;
renderables.push(
<Entity
filters={isHighlightedInteraction ? filters : []}
entity={entities[id]}
key={id}
/>

View File

@ -29,7 +29,7 @@ function Crosshair({x, y}) {
);
}
function Entities({entity}) {
function Entity({entity, ...rest}) {
const [debug] = useDebug();
if (!entity) {
return false;
@ -41,6 +41,7 @@ function Entities({entity}) {
{entity.Sprite && (
<Sprite
entity={entity}
{...rest}
/>
)}
{entity.Emitter && (
@ -55,4 +56,4 @@ function Entities({entity}) {
);
}
export default memo(Entities);
export default memo(Entity);

View File

@ -2,7 +2,7 @@ import {Sprite as PixiSprite} from '@pixi/react';
import {useAsset} from '@/context/assets.js';
export default function Sprite({entity}) {
export default function Sprite({entity, ...rest}) {
const asset = useAsset(entity.Sprite.source);
if (!asset) {
return false;
@ -21,6 +21,7 @@ export default function Sprite({entity}) {
texture={texture}
x={Math.round(entity.Position.x)}
y={Math.round(entity.Position.y)}
{...rest}
/>
);
}

View File

@ -92,6 +92,10 @@ export default function Ui({disconnected}) {
actionPayload = {type: 'use', value: KEY_MAP[type]};
break;
}
case 'e': {
actionPayload = {type: 'interact', value: KEY_MAP[type]};
break;
}
case '1': {
if ('keyDown' === type) {
actionPayload = {type: 'changeSlot', value: 1};

18
package-lock.json generated
View File

@ -7,6 +7,8 @@
"name": "silphius-next",
"dependencies": {
"@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/react": "^7.1.2",
"@pixi/spritesheet": "^7.4.2",
@ -3302,6 +3304,14 @@
"@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": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@pixi/filter-alpha/-/filter-alpha-7.4.2.tgz",
@ -3342,6 +3352,14 @@
"@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": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@pixi/filter-noise/-/filter-noise-7.4.2.tgz",

View File

@ -14,6 +14,8 @@
},
"dependencies": {
"@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/react": "^7.1.2",
"@pixi/spritesheet": "^7.4.2",

View File

@ -1,4 +1,6 @@
const {Sprite} = ecs.get(plant.entity);
const {Interactive, Sprite} = ecs.get(plant.entity);
plant.growth = 0
if (plant.stage < 3) {
plant.stage += 1
@ -6,8 +8,8 @@ if (plant.stage < 3) {
if (4 === plant.stage) {
plant.stage = 3
}
if (3 !== plant.stage) {
plant.growth = 0
if (3 === plant.stage) {
Interactive.interacting = true;
}
Sprite.animation = ['stage', plant.stage].join('/')

View File

@ -0,0 +1,4 @@
const {Interactive, Plant, Sprite} = subject;
Interactive.interacting = false;
Plant.stage = 4;
Sprite.animation = ['stage', Plant.stage].join('/')

View File

@ -9,10 +9,13 @@ if (projected?.length > 0) {
const [, direction] = Sprite.animation.split(':')
const plant = {
Interactive: {
interactScript: '/assets/tomato-plant/interact.js',
},
Plant: {
growScript: '/assets/tomato-plant/grow.js',
mayGrowScript: '/assets/tomato-plant/may-grow.js',
stages: Array(5).fill(60),
stages: Array(5).fill(5),
},
Sprite: {
anchor: {x: 0.5, y: 0.75},