fun: das a lodda damage
This commit is contained in:
parent
091e19c7de
commit
08dfe8ac29
|
@ -1,4 +1,24 @@
|
||||||
import Component from '@/ecs/component.js';
|
import Component from '@/ecs/component.js';
|
||||||
|
|
||||||
export default class Vulnerable extends Component {
|
export default class Vulnerable extends Component {
|
||||||
|
mergeDiff(original, update) {
|
||||||
|
const merged = {};
|
||||||
|
if (update.damage) {
|
||||||
|
merged.damage = {
|
||||||
|
...original.damage,
|
||||||
|
...update.damage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
instanceFromSchema() {
|
||||||
|
const Component = this;
|
||||||
|
return class VulnerableInstance extends super.instanceFromSchema() {
|
||||||
|
damages = {};
|
||||||
|
id = 0;
|
||||||
|
damage(specification) {
|
||||||
|
Component.markChange(this.entity, 'damage', {[this.id++]: specification});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
72
app/react/components/dom/damage.jsx
Normal file
72
app/react/components/dom/damage.jsx
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
|
import {useRadians} from '@/react/context/radians.js';
|
||||||
|
|
||||||
|
import styles from './damage.module.css';
|
||||||
|
|
||||||
|
export default function Damage({
|
||||||
|
camera,
|
||||||
|
damage,
|
||||||
|
scale,
|
||||||
|
}) {
|
||||||
|
const [hue, setHue] = useState(0);
|
||||||
|
const [opacity, setOpacity] = useState(0.5);
|
||||||
|
const [growth, setGrowth] = useState(0.25);
|
||||||
|
const [offset, setOffset] = useState({x: 0, y: 0});
|
||||||
|
const [randomness] = useState({
|
||||||
|
hue: Math.random() * 10,
|
||||||
|
x: 1 * (Math.random() - 0.5),
|
||||||
|
y: 150 * (Math.random()),
|
||||||
|
rotation: Math.random() * (Math.PI / 8) - (Math.PI / 16),
|
||||||
|
});
|
||||||
|
const radians = useRadians();
|
||||||
|
useEffect(() => {
|
||||||
|
setHue(15 + (Math.cos(radians * 4) * (5 + randomness.hue)));
|
||||||
|
}, [radians, randomness.hue]);
|
||||||
|
useEffect(() => {
|
||||||
|
let handle;
|
||||||
|
let accumulated = 0;
|
||||||
|
let last = Date.now();
|
||||||
|
function float() {
|
||||||
|
const elapsed = (Date.now() - last) / 1000;
|
||||||
|
accumulated += elapsed;
|
||||||
|
last = Date.now();
|
||||||
|
if (accumulated < 0.25) {
|
||||||
|
setOpacity(0.5 + accumulated * 2)
|
||||||
|
}
|
||||||
|
if (accumulated < 0.5) {
|
||||||
|
setGrowth(0.25 + (accumulated * 1.5))
|
||||||
|
setOffset({
|
||||||
|
x: 80 * (accumulated * randomness.x),
|
||||||
|
y: -((80 + randomness.y) * (accumulated)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (accumulated >= 1.5) {
|
||||||
|
damage.onClose();
|
||||||
|
}
|
||||||
|
handle = requestAnimationFrame(float);
|
||||||
|
}
|
||||||
|
handle = requestAnimationFrame(float);
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(handle);
|
||||||
|
};
|
||||||
|
}, [damage, randomness.x, randomness.y]);
|
||||||
|
|
||||||
|
const {amount, position} = damage;
|
||||||
|
const left = position.x * scale - camera.x + offset.x;
|
||||||
|
const top = position.y * scale - camera.y + offset.y;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.damage}
|
||||||
|
style={{
|
||||||
|
color: `hsl(${hue} 100% 50%)`,
|
||||||
|
left: `${left}px`,
|
||||||
|
opacity,
|
||||||
|
top: `${top}px`,
|
||||||
|
transform: `scale(${growth}) rotate(${randomness.rotation}rad)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>{amount}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
25
app/react/components/dom/damage.module.css
Normal file
25
app/react/components/dom/damage.module.css
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
.damage {
|
||||||
|
color: red;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
position: fixed;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: -66%;
|
||||||
|
text-shadow:
|
||||||
|
0px -1px 0px black,
|
||||||
|
1px 0px 0px black,
|
||||||
|
0px 1px 0px black,
|
||||||
|
-1px 0px 0px black,
|
||||||
|
|
||||||
|
0px -2px 0px black,
|
||||||
|
2px 0px 0px black,
|
||||||
|
0px 2px 0px black,
|
||||||
|
-2px 0px 0px black
|
||||||
|
|
||||||
|
;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
user-select: none;
|
||||||
|
max-width: 66%;
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
22
app/react/components/dom/damages.jsx
Normal file
22
app/react/components/dom/damages.jsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import styles from './damages.module.css';
|
||||||
|
|
||||||
|
import Damage from './damage.jsx';
|
||||||
|
|
||||||
|
export default function Damages({camera, damages, scale}) {
|
||||||
|
const elements = [];
|
||||||
|
for (const key in damages) {
|
||||||
|
elements.push(
|
||||||
|
<Damage
|
||||||
|
camera={camera}
|
||||||
|
damage={damages[key]}
|
||||||
|
key={key}
|
||||||
|
scale={scale}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (0 === elements.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return <div className={styles.damages}>{elements}</div>;
|
||||||
|
}
|
||||||
|
|
4
app/react/components/dom/damages.module.css
Normal file
4
app/react/components/dom/damages.module.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.damages {
|
||||||
|
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
|
@ -88,6 +88,21 @@ 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];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setEntities((entities) => {
|
setEntities((entities) => {
|
||||||
for (const id in deleting) {
|
for (const id in deleting) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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 (
|
||||||
|
@ -12,6 +13,13 @@ function Entity({camera, entity, scale}) {
|
||||||
scale={scale}
|
scale={scale}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{entity.Vulnerable && (
|
||||||
|
<Damages
|
||||||
|
camera={camera}
|
||||||
|
damages={entity.Vulnerable.damages}
|
||||||
|
scale={scale}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const playerEntity = ecs.lookupPlayerEntity(entity.Owned.owner);
|
const playerEntity = ecs.lookupPlayerEntity(entity.Owned.owner);
|
||||||
if (playerEntity !== other && other.Vulnerable) {
|
if (playerEntity !== other && other.Vulnerable) {
|
||||||
ecs.destroy(other.id);
|
other.Vulnerable.damage({amount: Math.round(60 + Math.random() * 10), position: other.Position.toJSON()})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user