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() { instanceFromSchema() {
const Component = this; const Component = this;
return class VulnerableInstance extends super.instanceFromSchema() { return class VulnerableInstance extends super.instanceFromSchema() {
damages = {};
id = 0; id = 0;
Types = DamageTypes; Types = DamageTypes;
damage(specification) { 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}); Component.markChange(this.entity, 'damage', {[this.id++]: specification});
} }
}; };

View File

@ -34,7 +34,7 @@ function Damage({
<div <div
className={styles.damage} className={styles.damage}
style={{ 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`, '--positionX': `${position.x * scale - camera.x}px`,
'--positionY': `${position.y * scale - camera.y}px`, '--positionY': `${position.y * scale - camera.y}px`,
'--randomnessX': randomness.x, '--randomnessX': randomness.x,
@ -44,7 +44,7 @@ function Damage({
zIndex, zIndex,
}} }}
> >
<p>{amount}</p> <p>{Math.abs(amount)}</p>
</div> </div>
); );
} }

View File

@ -92,20 +92,42 @@
} }
@keyframes fade { @keyframes fade {
0% { --opacity: 0.75; } 0% {
25% { --opacity: 1; } --opacity: 0.75;
91.6% { --opacity: 1; } }
100% { --opacity: 0; } 25% {
--opacity: 1;
}
91.6% {
--opacity: 1;
}
100% {
--opacity: 0;
}
} }
@keyframes grow { @keyframes grow {
0% { --scale: 0.35; } 0% {
33% { --scale: 1; } --scale: 0.35;
45% { --scale: 1; } }
40% { --scale: 1.5; } 33% {
45% { --scale: 1; } --scale: 1;
91.6% { --scale: 1; } }
95% { --scale: 0; } 45% {
--scale: 1;
}
40% {
--scale: 1.5;
}
45% {
--scale: 1;
}
91.6% {
--scale: 1;
}
95% {
--scale: 0;
}
} }
@keyframes pain { @keyframes pain {

View File

@ -4,6 +4,7 @@ import {usePacket} from '@/react/context/client.js';
import {useEcs, useEcsTick} from '@/react/context/ecs.js'; import {useEcs, useEcsTick} from '@/react/context/ecs.js';
import {parseLetters} from '@/util/dialogue.js'; import {parseLetters} from '@/util/dialogue.js';
import Damages from './damages.jsx';
import Entity from './entity.jsx'; import Entity from './entity.jsx';
export default function Entities({ export default function Entities({
@ -14,6 +15,7 @@ export default function Entities({
}) { }) {
const [ecs] = useEcs(); const [ecs] = useEcs();
const [entities, setEntities] = useState({}); const [entities, setEntities] = useState({});
const [damages, setDamages] = useState({});
usePacket('EcsChange', async () => { usePacket('EcsChange', async () => {
setEntities({}); setEntities({});
}, [setEntities]); }, [setEntities]);
@ -90,17 +92,18 @@ export default function Entities({
} }
const {damage} = update.Vulnerable || {}; const {damage} = update.Vulnerable || {};
if (damage) { if (damage) {
const {damages} = updating[id].Vulnerable;
for (const key in damage) { for (const key in damage) {
damages[key] = damage[key]; const composite = [id, key].join('-');
damages[key].onClose = () => { damage[key].onClose = () => {
setEntities((entities) => ({ setDamages((damages) => {
...entities, const {[composite]: _, ...rest} = damages; // eslint-disable-line no-unused-vars
[id]: ecs.rebuild(id), return rest;
})); })
delete damages[key];
}; };
setDamages((damages) => ({
...damages,
[composite]: damage[key],
}));
} }
} }
} }
@ -128,6 +131,11 @@ export default function Entities({
return ( return (
<> <>
{renderables} {renderables}
<Damages
camera={camera}
damages={damages}
scale={scale}
/>
</> </>
); );
} }

View File

@ -1,7 +1,6 @@
import {memo} from 'react'; import {memo} from 'react';
import Dialogues from './dialogues.jsx'; import Dialogues from './dialogues.jsx';
import Damages from './damages.jsx';
function Entity({camera, entity, scale}) { function Entity({camera, entity, scale}) {
return ( return (
@ -13,13 +12,6 @@ function Entity({camera, entity, scale}) {
scale={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: {}, VisibleAabb: {},
}); });
const kitty = { const kitty = {
Alive: {health: 100},
Behaving: { Behaving: {
routines: { routines: {
initial: '/assets/kitty/initial.js', initial: '/assets/kitty/initial.js',

View File

@ -1,8 +1,8 @@
const playerEntity = ecs.lookupPlayerEntity(entity.Owned.owner); const playerEntity = ecs.lookupPlayerEntity(entity.Owned.owner);
if (playerEntity !== other && other.Vulnerable) { if (playerEntity !== other && other.Vulnerable) {
const magnitude = Math.floor(Math.random() * 3) const magnitude = Math.floor(Math.random() * 2)
other.Vulnerable.damage({ other.Vulnerable.damage({
amount: Math.floor( amount: -Math.floor(
Math.pow(10, magnitude) Math.pow(10, magnitude)
+ Math.random() * (Math.pow(10, magnitude + 1) - 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);
}