fun: life and death

This commit is contained in:
cha0s 2024-07-27 12:31:52 -05:00
parent fb747b38e6
commit 5c619b26c0
9 changed files with 135 additions and 33 deletions

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

View File

@ -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});
}
};

View File

@ -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>
);
}

View File

@ -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 {

View File

@ -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}
/>
</>
);
}

View File

@ -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}
/>
)}
</>
)
}

View File

@ -138,6 +138,7 @@ export default async function createHomestead(id) {
VisibleAabb: {},
});
const kitty = {
Alive: {health: 100},
Behaving: {
routines: {
initial: '/assets/kitty/initial.js',

View File

@ -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)),
),

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