fun: life and death
This commit is contained in:
parent
fb747b38e6
commit
5c619b26c0
53
app/ecs/components/alive.js
Normal file
53
app/ecs/components/alive.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import Component from '@/ecs/component.js';
|
||||
|
||||
export default class Alive extends Component {
|
||||
instanceFromSchema() {
|
||||
const {ecs} = this;
|
||||
return class AliveInstance extends super.instanceFromSchema() {
|
||||
$$dead = false;
|
||||
acceptDamage(amount) {
|
||||
const health = Math.min(this.maxHealth, Math.max(0, this.health + amount));
|
||||
this.health = health;
|
||||
if (0 === health) {
|
||||
this.die();
|
||||
}
|
||||
}
|
||||
die() {
|
||||
if (this.$$dead) {
|
||||
return;
|
||||
}
|
||||
this.$$dead = true;
|
||||
const {Ticking} = ecs.get(this.entity);
|
||||
if (Ticking) {
|
||||
const ticker = this.$$death.ticker();
|
||||
ecs.addDestructionDependency(this.entity.id, ticker);
|
||||
Ticking.add(ticker);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
async load(instance) {
|
||||
// heavy handed...
|
||||
if ('undefined' !== typeof window) {
|
||||
return;
|
||||
}
|
||||
instance.$$death = await this.ecs.readScript(
|
||||
instance.deathScript,
|
||||
{
|
||||
ecs: this.ecs,
|
||||
entity: this.ecs.get(instance.entity),
|
||||
},
|
||||
);
|
||||
if (0 === instance.maxHealth) {
|
||||
instance.maxHealth = instance.health;
|
||||
}
|
||||
}
|
||||
static properties = {
|
||||
deathScript: {
|
||||
defaultValue: '/assets/misc/death-default.js',
|
||||
type: 'string',
|
||||
},
|
||||
health: {type: 'uint32'},
|
||||
maxHealth: {type: 'uint32'},
|
||||
};
|
||||
}
|
|
@ -20,10 +20,17 @@ export default class Vulnerable extends Component {
|
|||
instanceFromSchema() {
|
||||
const Component = this;
|
||||
return class VulnerableInstance extends super.instanceFromSchema() {
|
||||
damages = {};
|
||||
id = 0;
|
||||
Types = DamageTypes;
|
||||
damage(specification) {
|
||||
const {Alive} = Component.ecs.get(this.entity);
|
||||
if (Alive) {
|
||||
switch (specification.type) {
|
||||
case DamageTypes.PAIN: {
|
||||
Alive.acceptDamage(specification.amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
Component.markChange(this.entity, 'damage', {[this.id++]: specification});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ function Damage({
|
|||
<div
|
||||
className={styles.damage}
|
||||
style={{
|
||||
'--magnitude': Math.max(1, Math.floor(Math.log10(amount))),
|
||||
'--magnitude': Math.max(1, Math.floor(Math.log10(Math.abs(amount)))),
|
||||
'--positionX': `${position.x * scale - camera.x}px`,
|
||||
'--positionY': `${position.y * scale - camera.y}px`,
|
||||
'--randomnessX': randomness.x,
|
||||
|
@ -44,7 +44,7 @@ function Damage({
|
|||
zIndex,
|
||||
}}
|
||||
>
|
||||
<p>{amount}</p>
|
||||
<p>{Math.abs(amount)}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -92,20 +92,42 @@
|
|||
}
|
||||
|
||||
@keyframes fade {
|
||||
0% { --opacity: 0.75; }
|
||||
25% { --opacity: 1; }
|
||||
91.6% { --opacity: 1; }
|
||||
100% { --opacity: 0; }
|
||||
0% {
|
||||
--opacity: 0.75;
|
||||
}
|
||||
25% {
|
||||
--opacity: 1;
|
||||
}
|
||||
91.6% {
|
||||
--opacity: 1;
|
||||
}
|
||||
100% {
|
||||
--opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes grow {
|
||||
0% { --scale: 0.35; }
|
||||
33% { --scale: 1; }
|
||||
45% { --scale: 1; }
|
||||
40% { --scale: 1.5; }
|
||||
45% { --scale: 1; }
|
||||
91.6% { --scale: 1; }
|
||||
95% { --scale: 0; }
|
||||
0% {
|
||||
--scale: 0.35;
|
||||
}
|
||||
33% {
|
||||
--scale: 1;
|
||||
}
|
||||
45% {
|
||||
--scale: 1;
|
||||
}
|
||||
40% {
|
||||
--scale: 1.5;
|
||||
}
|
||||
45% {
|
||||
--scale: 1;
|
||||
}
|
||||
91.6% {
|
||||
--scale: 1;
|
||||
}
|
||||
95% {
|
||||
--scale: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pain {
|
||||
|
|
|
@ -4,6 +4,7 @@ import {usePacket} from '@/react/context/client.js';
|
|||
import {useEcs, useEcsTick} from '@/react/context/ecs.js';
|
||||
import {parseLetters} from '@/util/dialogue.js';
|
||||
|
||||
import Damages from './damages.jsx';
|
||||
import Entity from './entity.jsx';
|
||||
|
||||
export default function Entities({
|
||||
|
@ -14,6 +15,7 @@ export default function Entities({
|
|||
}) {
|
||||
const [ecs] = useEcs();
|
||||
const [entities, setEntities] = useState({});
|
||||
const [damages, setDamages] = useState({});
|
||||
usePacket('EcsChange', async () => {
|
||||
setEntities({});
|
||||
}, [setEntities]);
|
||||
|
@ -90,17 +92,18 @@ export default function Entities({
|
|||
}
|
||||
const {damage} = update.Vulnerable || {};
|
||||
if (damage) {
|
||||
const {damages} = updating[id].Vulnerable;
|
||||
for (const key in damage) {
|
||||
damages[key] = damage[key];
|
||||
damages[key].onClose = () => {
|
||||
setEntities((entities) => ({
|
||||
...entities,
|
||||
[id]: ecs.rebuild(id),
|
||||
}));
|
||||
delete damages[key];
|
||||
const composite = [id, key].join('-');
|
||||
damage[key].onClose = () => {
|
||||
setDamages((damages) => {
|
||||
const {[composite]: _, ...rest} = damages; // eslint-disable-line no-unused-vars
|
||||
return rest;
|
||||
})
|
||||
};
|
||||
|
||||
setDamages((damages) => ({
|
||||
...damages,
|
||||
[composite]: damage[key],
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +131,11 @@ export default function Entities({
|
|||
return (
|
||||
<>
|
||||
{renderables}
|
||||
<Damages
|
||||
camera={camera}
|
||||
damages={damages}
|
||||
scale={scale}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {memo} from 'react';
|
||||
|
||||
import Dialogues from './dialogues.jsx';
|
||||
import Damages from './damages.jsx';
|
||||
|
||||
function Entity({camera, entity, scale}) {
|
||||
return (
|
||||
|
@ -13,13 +12,6 @@ function Entity({camera, entity, scale}) {
|
|||
scale={scale}
|
||||
/>
|
||||
)}
|
||||
{entity.Vulnerable && (
|
||||
<Damages
|
||||
camera={camera}
|
||||
damages={entity.Vulnerable.damages}
|
||||
scale={scale}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -138,6 +138,7 @@ export default async function createHomestead(id) {
|
|||
VisibleAabb: {},
|
||||
});
|
||||
const kitty = {
|
||||
Alive: {health: 100},
|
||||
Behaving: {
|
||||
routines: {
|
||||
initial: '/assets/kitty/initial.js',
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const playerEntity = ecs.lookupPlayerEntity(entity.Owned.owner);
|
||||
if (playerEntity !== other && other.Vulnerable) {
|
||||
const magnitude = Math.floor(Math.random() * 3)
|
||||
const magnitude = Math.floor(Math.random() * 2)
|
||||
other.Vulnerable.damage({
|
||||
amount: Math.floor(
|
||||
amount: -Math.floor(
|
||||
Math.pow(10, magnitude)
|
||||
+ Math.random() * (Math.pow(10, magnitude + 1) - Math.pow(10, magnitude)),
|
||||
),
|
||||
|
|
19
public/assets/misc/death-default.js
Normal file
19
public/assets/misc/death-default.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
const {Sprite, Ticking} = entity;
|
||||
if (Sprite) {
|
||||
const {promise} = transition(
|
||||
entity.Sprite,
|
||||
{
|
||||
scaleX: {
|
||||
duration: 0.25,
|
||||
magnitude: -entity.Sprite.scaleX,
|
||||
},
|
||||
scaleY: {
|
||||
duration: 0.25,
|
||||
magnitude: entity.Sprite.scaleY * 2,
|
||||
},
|
||||
},
|
||||
)
|
||||
Ticking.add(promise);
|
||||
await promise;
|
||||
ecs.destroy(entity.id);
|
||||
}
|
Loading…
Reference in New Issue
Block a user