refactor: damage -> harm

This commit is contained in:
cha0s 2019-12-12 19:27:19 -06:00
parent 21fb634e69
commit 6e952983c7
11 changed files with 137 additions and 119 deletions

View File

@ -1,10 +1,14 @@
# TODO # TODO
- ✔ React UI - ✔ React UI
- ❌ Multiple rooms
- ✔ Send relevant entity URIs to client; cache and use instead of full transfer - ✔ Send relevant entity URIs to client; cache and use instead of full transfer
- ✔ Forget remembered entities after a while - ✔ Forget remembered entities after a while
- ✔ Optimize informed and Packer::pack: don't use .map on immutables - ✔ Optimize informed and Packer::pack: don't use .map on immutables
- ✔ Optimize Damaging::tick - ✔ Optimize Harmful::tick
- ✔ Manual state sync - ✔ Manual state sync
- ✔ Informed tracks seen entities (etc?), responsible for upgrading updates - ✔ Informed tracks seen entities (etc?), responsible for upgrading updates
- ❌ Multiple rooms
- ✔ Refactor input to send serial actions
- ✔ "use" action
- ❌ chat

View File

@ -448,8 +448,8 @@ export class App extends decorate(class {}) {
} }
renderIntoDom(node) { renderIntoDom(node) {
// Maintain damage element. // Maintain harm element.
const promise = this.stage.findUiElement('.damage'); const promise = this.stage.findUiElement('.harm');
promise.then((inner) => { promise.then((inner) => {
const innerStyles = () => { const innerStyles = () => {
const {realOffset} = this.stage.camera; const {realOffset} = this.stage.camera;

View File

@ -1,6 +1,6 @@
import {Packet} from '@avocado/net'; import {Packet} from '@avocado/net';
export class DamagePacket extends Packet { export class HarmPacket extends Packet {
static get schema() { static get schema() {
return { return {
@ -8,11 +8,11 @@ export class DamagePacket extends Packet {
data: [ data: [
{ {
amount: 'varuint', amount: 'varuint',
damageSpec: { harmSpec: {
affinity: 'uint8', affinity: 'uint8',
}, },
from: 'uint32', from: 'uint32',
isDamage: 'bool', isHarm: 'bool',
}, },
] ]
} }

View File

@ -7,66 +7,65 @@ import {fromRad, normalizeAngleRange, Vector} from '@avocado/math';
import {AFFINITY_PHYSICAL} from './constants'; import {AFFINITY_PHYSICAL} from './constants';
const decorate = compose( const decorate = compose(
StateProperty('isDamaging'), StateProperty('isHarmful'),
); );
export class Damaging extends decorate(Trait) { export class Harmful extends decorate(Trait) {
static defaultParams() { static defaultParams() {
return { return {
damagingSound: '', harmfulSound: '',
damageSpecs: [], harmSpecs: [],
}; };
} }
static defaultState() { static defaultState() {
return { return {
isDamaging: true, isHarmful: true,
}; };
} }
static type() { static type() {
return 'damaging'; return 'harmful';
} }
constructor(entity, params, state) { constructor(entity, params, state) {
super(entity, params, state); super(entity, params, state);
this._doesNotDamage = []; this._doesNotHarm = [];
const damageSpecsJSON = this.params.damageSpecs; const harmSpecsJSON = this.params.harmSpecs;
this._damageSpecs = damageSpecsJSON.map((damageSpec) => { this._harmSpecs = harmSpecsJSON.map((harmSpec) => {
return { return {
affinity: AFFINITY_PHYSICAL,
lock: 0.1, lock: 0.1,
power: 0, power: 0,
variance: 0.2, variance: 0.2,
...damageSpec, ...harmSpec,
}; };
}); });
this._damagingSound = this.params.damagingSound; this._harmfulSound = this.params.harmfulSound;
} }
doesNotDamageEntity(entity) { doesNotHarmEntity(entity) {
if (!this.entity.isDamaging) { if (!this.entity.isHarmful) {
return true; return true;
} }
return -1 !== this._doesNotDamage.indexOf(entity); return -1 !== this._doesNotHarm.indexOf(entity);
} }
get damageSpecs() { get harmSpecs() {
return this._damageSpecs; return this._harmSpecs;
} }
get damagingSound() { get harmfulSound() {
return this._damagingSound; return this._harmfulSound;
} }
tryDamagingEntity(entity) { tryHarmfulEntity(entity) {
if (this.doesNotDamageEntity(entity)) { if (this.doesNotHarmEntity(entity)) {
return; return;
} }
if (entity.is('vulnerable')) { if (entity.is('vulnerable')) {
if (!entity.isInvulnerable) { if (!entity.isInvulnerable) {
entity.takeDamageFrom(this.entity); entity.takeHarmFrom(this.entity);
} }
} }
} }
@ -76,7 +75,7 @@ export class Damaging extends decorate(Trait) {
particles: () => { particles: () => {
return { return {
damaging: { harmful: {
traits: { traits: {
emitted: { emitted: {
params: { params: {
@ -115,7 +114,7 @@ export class Damaging extends decorate(Trait) {
const listeners = {}; const listeners = {};
if (AVOCADO_SERVER) { if (AVOCADO_SERVER) {
listeners.collisionStart = (other) => { listeners.collisionStart = (other) => {
this.tryDamagingEntity(other); this.tryHarmfulEntity(other);
}; };
} }
return listeners; return listeners;
@ -124,14 +123,14 @@ export class Damaging extends decorate(Trait) {
methods() { methods() {
return { return {
emitDamagingParticles: (other, json = {}) => { emitHarmfulParticles: (other, json = {}) => {
const diff = Vector.sub(this.entity.position, other.position); const diff = Vector.sub(this.entity.position, other.position);
const velocityAngle = Vector.toAngle(Vector.normalize(diff)); const velocityAngle = Vector.toAngle(Vector.normalize(diff));
const [fromAngle, toAngle] = normalizeAngleRange( const [fromAngle, toAngle] = normalizeAngleRange(
velocityAngle - Math.PI / 8, velocityAngle - Math.PI / 8,
velocityAngle + Math.PI / 8, velocityAngle + Math.PI / 8,
); );
this.entity.emitParticle('damaging', merge( this.entity.emitParticle('harmful', merge(
{}, {},
{ {
traits: { traits: {
@ -153,16 +152,16 @@ export class Damaging extends decorate(Trait) {
)); ));
}, },
setDoesDamage: (entity) => { setDoesHarm: (entity) => {
const index = this._doesNotDamage.indexOf(entity); const index = this._doesNotHarm.indexOf(entity);
if (-1 !== index) { if (-1 !== index) {
this._doesNotDamage.splice(index, 1); this._doesNotHarm.splice(index, 1);
} }
}, },
setDoesNotDamage: (entity) => { setDoesNotHarm: (entity) => {
if (-1 === this._doesNotDamage.indexOf(entity)) { if (-1 === this._doesNotHarm.indexOf(entity)) {
this._doesNotDamage.push(entity); this._doesNotHarm.push(entity);
} }
}, },
@ -174,7 +173,7 @@ export class Damaging extends decorate(Trait) {
if (this.entity.is('collider')) { if (this.entity.is('collider')) {
const isCollidingWith = this.entity.isCollidingWith; const isCollidingWith = this.entity.isCollidingWith;
for (let i = 0; i < isCollidingWith.length; i++) { for (let i = 0; i < isCollidingWith.length; i++) {
this.tryDamagingEntity(isCollidingWith[i]); this.tryHarmfulEntity(isCollidingWith[i]);
} }
} }
} }

View File

@ -5,27 +5,27 @@ import {
buildTraversal, buildTraversal,
Context, Context,
} from '@avocado/behavior'; } from '@avocado/behavior';
import {iterateForEach} from '@avocado/core'; import {flatten, invokeHookFlat, iterateForEach} from '@avocado/core';
import {hasGraphics, TextNodeRenderer} from '@avocado/graphics'; import {hasGraphics, TextNodeRenderer} from '@avocado/graphics';
import {randomNumber, Vector} from '@avocado/math'; import {randomNumber, Vector} from '@avocado/math';
import {DamagePacket} from './damage.packet'; import {HarmPacket} from './harm.packet';
export class Vulnerable extends Trait { export class Vulnerable extends Trait {
static defaultParams() { static defaultParams() {
const emitDamage = buildInvoke(['entity', 'emitDamageParticles'], [ const emitHarm = buildInvoke(['entity', 'emitHarmParticles'], [
buildTraversal(['damage']), buildTraversal(['harm']),
]); ]);
const playDamagingSound = buildInvoke(['damage', 'from', 'playSound'], [ const playHarmfulSound = buildInvoke(['harm', 'from', 'playSound'], [
buildTraversal(['damage', 'from', 'damagingSound']), buildTraversal(['harm', 'from', 'harmfulSound']),
]); ]);
return { return {
tookDamageActions: { tookHarmActions: {
type: 'actions', type: 'actions',
traversals: [ traversals: [
emitDamage, emitHarm,
playDamagingSound, playHarmfulSound,
], ],
}, },
modifiers: undefined, modifiers: undefined,
@ -38,55 +38,55 @@ export class Vulnerable extends Trait {
constructor(entity, params, state) { constructor(entity, params, state) {
super(entity, params, state); super(entity, params, state);
this.damageTickingPromises = []; this.harmTickingPromises = [];
this.damages = []; this.harms = [];
this._isInvulnerable = false; this._isInvulnerable = false;
this.locks = new Map(); this.locks = new Map();
this._tookDamageActions = behaviorItemFromJSON( this._tookHarmActions = behaviorItemFromJSON(
this.params.tookDamageActions this.params.tookHarmActions
); );
} }
destroy() { destroy() {
this.locks.clear(); this.locks.clear();
this.damageTickingPromises = []; this.harmTickingPromises = [];
} }
acceptDamage(damage) { acceptHarm(harm) {
const context = new Context({ const context = new Context({
damage, harm,
entity: this.entity, entity: this.entity,
}); });
const tickingPromise = this._tookDamageActions.tickingPromise(context); const tickingPromise = this._tookHarmActions.tickingPromise(context);
tickingPromise.then(() => { tickingPromise.then(() => {
context.destroy(); context.destroy();
}); });
this.damageTickingPromises.push(tickingPromise); this.harmTickingPromises.push(tickingPromise);
if (damage.from) { if (harm.from) {
damage.from.emitDamagingParticles(this.entity); harm.from.emitHarmfulParticles(this.entity);
} }
} }
acceptPacket(packet) { acceptPacket(packet) {
if (packet instanceof DamagePacket) { if (packet instanceof HarmPacket) {
for (let i = 0; i < packet.data.length; ++i) { for (let i = 0; i < packet.data.length; ++i) {
const damage = packet.data[i]; const harm = packet.data[i];
if (this.entity.is('listed') && this.entity.list) { if (this.entity.is('listed') && this.entity.list) {
damage.from = this.entity.list.findEntity(damage.from); harm.from = this.entity.list.findEntity(harm.from);
} }
else { else {
damage.from = undefined; harm.from = undefined;
} }
this.acceptDamage(damage); this.acceptHarm(harm);
} }
} }
} }
cleanPackets() { cleanPackets() {
this.damages = []; this.harms = [];
} }
damageTextSize(amount) { harmTextSize(amount) {
const biggest = 16; const biggest = 16;
const smallest = biggest / 2; const smallest = biggest / 2;
const step = biggest / 6; const step = biggest / 6;
@ -113,8 +113,8 @@ export class Vulnerable extends Trait {
} }
packets(informed) { packets(informed) {
if (this.damages.length > 0) { if (this.harms.length > 0) {
return new DamagePacket(this.damages); return new HarmPacket(this.harms);
} }
} }
@ -123,7 +123,7 @@ export class Vulnerable extends Trait {
particles: () => { particles: () => {
return { return {
damage: { harm: {
traits: { traits: {
darkened: { darkened: {
params: { params: {
@ -242,9 +242,9 @@ export class Vulnerable extends Trait {
this._isInvulnerable = isDying; this._isInvulnerable = isDying;
}, },
tookDamage: (damage) => { tookHarm: (harm) => {
if (AVOCADO_SERVER) { if (AVOCADO_SERVER) {
this.damages.push(damage); this.harms.push(harm);
this.setDirty(); this.setDirty();
} }
}, },
@ -255,10 +255,10 @@ export class Vulnerable extends Trait {
methods() { methods() {
return { return {
emitDamageParticles: (damage) => { emitHarmParticles: (harm) => {
const {amount, isDamage} = damage; const {amount, isHarm} = harm;
const fill = isDamage ? '#FF0000' : '#00FF77'; const fill = isHarm ? '#FF0000' : '#00FF77';
this.entity.emitParticle('damage', { this.entity.emitParticle('harm', {
traits: { traits: {
textual: { textual: {
state: { state: {
@ -266,7 +266,7 @@ export class Vulnerable extends Trait {
textStyle: { textStyle: {
fill, fill,
fontFamily: 'joystix', fontFamily: 'joystix',
fontSize: this.damageTextSize(amount), fontSize: this.harmTextSize(amount),
strokeThickness: 2, strokeThickness: 2,
}, },
}, },
@ -279,23 +279,23 @@ export class Vulnerable extends Trait {
}); });
}, },
takeDamageFrom: (entity) => { takeHarmFrom: (entity) => {
const damageSpecs = entity.damageSpecs; const harmSpecs = entity.harmSpecs;
for (let i = 0; i < damageSpecs.length; ++i) { for (let i = 0; i < harmSpecs.length; ++i) {
const damageSpec = damageSpecs[i]; const harmSpec = harmSpecs[i];
if (this.locks.has(damageSpec)) { if (this.locks.has(harmSpec)) {
continue; continue;
} }
this.locks.set(damageSpec, damageSpec.lock); this.locks.set(harmSpec, harmSpec.lock);
let power = damageSpec.power; let power = harmSpec.power;
// Check if vulnerable to this affinity. // Check if vulnerable to this affinity.
if (this.params.modifiers) { if (this.params.modifiers) {
if (damageSpec.affinity in this.params.modifiers) { if (harmSpec.affinity in this.params.modifiers) {
power *= this.params.modifiers[damageSpec.affinity]; power *= this.params.modifiers[harmSpec.affinity];
} }
} }
// Give any knockback. // Give any knockback.
if (damageSpec.knockback) { if (harmSpec.knockback) {
if ( if (
this.entity.is('mobile') this.entity.is('mobile')
&& this.entity.is('positioned') && this.entity.is('positioned')
@ -306,35 +306,35 @@ export class Vulnerable extends Trait {
entity.position, entity.position,
); );
const unit = Vector.normalize(difference) const unit = Vector.normalize(difference)
const knockback = Vector.scale(unit, damageSpec.knockback); const knockback = Vector.scale(unit, harmSpec.knockback);
this.entity.applyMovement(knockback); this.entity.applyMovement(knockback);
} }
} }
const variance = Math.random() * damageSpec.variance * 2 - damageSpec.variance; const variance = Math.random() * harmSpec.variance * 2 - harmSpec.variance;
const difference = power * variance; const difference = power * variance;
// Account for variance past 0, so track if it's damage or not. // Account for variance past 0, so track if it's harm or not.
let amount = Math.round(power + difference); let amount = Math.round(power + difference);
let isDamage; let isHarm;
if (power < 0) { if (power < 0) {
isDamage = false; isHarm = false;
if (amount > 0) { if (amount > 0) {
amount = 0; amount = 0;
} }
} }
else { else {
isDamage = true; isHarm = true;
if (amount < 0) { if (amount < 0) {
amount = 0; amount = 0;
} }
} }
amount = Math.abs(amount); amount = Math.abs(amount);
const damage = { const harm = {
isDamage, isHarm,
amount, amount,
damageSpec, harmSpec,
from: entity.numericUid, from: entity.numericUid,
}; };
this.entity.emit('tookDamage', damage); this.entity.emit('tookHarm', harm);
} }
}, },
@ -342,8 +342,8 @@ export class Vulnerable extends Trait {
} }
tick(elapsed) { tick(elapsed) {
for (let i = 0; i < this.damageTickingPromises.length; ++i) { for (let i = 0; i < this.harmTickingPromises.length; ++i) {
this.damageTickingPromises[i].tick(elapsed); this.harmTickingPromises[i].tick(elapsed);
} }
if (AVOCADO_SERVER) { if (AVOCADO_SERVER) {
iterateForEach(this.locks.keys(), (key) => { iterateForEach(this.locks.keys(), (key) => {

View File

@ -7,7 +7,7 @@ export function blueFireJSON() {
const json = fireJSON(); const json = fireJSON();
json.traits.animated.params.animations.idle.uri = '/blue-fire.animation.json'; json.traits.animated.params.animations.idle.uri = '/blue-fire.animation.json';
json.traits.existent.state.name = 'Blue fire'; json.traits.existent.state.name = 'Blue fire';
json.traits.damaging.params.damageSpecs[0].power = -15; json.traits.harmful.params.harmSpecs[0].power = -15;
json.traits.audible.params.sounds.fire.uri = '/blue-fire.sound.json'; json.traits.audible.params.sounds.fire.uri = '/blue-fire.sound.json';
return json; return json;
} }

View File

@ -32,10 +32,10 @@ export function fireJSON() {
isSensor: true, isSensor: true,
}, },
}, },
damaging: { harmful: {
params: { params: {
damagingSound: 'fire', harmfulSound: 'fire',
damageSpecs: [ harmSpecs: [
{ {
affinity: AFFINITY_FIRE, affinity: AFFINITY_FIRE,
lock: 0.15, lock: 0.15,

View File

@ -67,14 +67,17 @@ export function kittyFireJSON() {
// for (let i = 0; i < 30; ++i) { // for (let i = 0; i < 30; ++i) {
// addEntityWithRandomPosition('/flower-barrel.entity.json'); // addEntityWithRandomPosition('/flower-barrel.entity.json');
// } // }
for (let i = 0; i < 5; ++i) { // for (let i = 0; i < 15; ++i) {
addEntityWithRandomPosition('/mama-kitty-spawner.entity.json'); // addEntityWithRandomPosition('/mama-kitty-spawner.entity.json');
// }
for (let i = 0; i < 50; ++i) {
addEntityWithRandomPosition('/kitty.entity.json');
} }
// for (let i = 0; i < 50; ++i) { // for (let i = 0; i < 150; ++i) {
// addEntityWithRandomPosition('/fire.entity.json'); // addEntityWithRandomPosition('/fire.entity.json');
// } // }
for (let i = 0; i < 5; ++i) { // for (let i = 0; i < 5; ++i) {
addEntityWithRandomPosition('/blue-fire.entity.json'); // addEntityWithRandomPosition('/blue-fire.entity.json');
} // }
return roomJSON; return roomJSON;
} }

View File

@ -125,6 +125,18 @@ export function kittyJSON() {
uri: '/yarn-ball.entity.json', uri: '/yarn-ball.entity.json',
}, },
}, },
{
perc: 80,
json: {
uri: '/yarn-ball.entity.json',
},
},
{
perc: 90,
json: {
uri: '/yarn-ball.entity.json',
},
},
], ],
}, },
}, },

View File

@ -4,7 +4,7 @@ import {AFFINITY_NONE} from '../../common/combat/constants';
// Healing potion. // Healing potion.
export function potionJSON() { export function potionJSON() {
const causeHealing = buildInvoke(['wielder', 'takeDamageFrom'], [ const causeHealing = buildInvoke(['wielder', 'takeHarmFrom'], [
buildTraversal(['item']), buildTraversal(['item']),
]); ]);
const decrement = buildInvoke( const decrement = buildInvoke(
@ -15,9 +15,9 @@ export function potionJSON() {
); );
return { return {
traits: { traits: {
damaging: { harmful: {
params: { params: {
damageSpecs: [ harmSpecs: [
{ {
affinity: AFFINITY_NONE, affinity: AFFINITY_NONE,
lock: 0, lock: 0,

View File

@ -4,7 +4,7 @@ import {AFFINITY_NONE} from '../../common/combat/constants';
// Projectile rock. // Projectile rock.
export function rockProjectileJSON() { export function rockProjectileJSON() {
// Set as not damaging to user. // Set as not harmful to user.
const setDoesNotCollideWith = buildInvoke( const setDoesNotCollideWith = buildInvoke(
['entity', 'setDoesNotCollideWith'], ['entity', 'setDoesNotCollideWith'],
[ [
@ -44,8 +44,8 @@ export function rockProjectileJSON() {
['entity', 'isColliding'], ['entity', 'isColliding'],
false, false,
); );
const setIsNotDamaging = buildTraversal( const setIsNotHarmful = buildTraversal(
['entity', 'isDamaging'], ['entity', 'isHarmful'],
false, false,
); );
const fadeOutAndSlowDown = buildInvoke( const fadeOutAndSlowDown = buildInvoke(
@ -114,7 +114,7 @@ export function rockProjectileJSON() {
type: 'actions', type: 'actions',
traversals: [ traversals: [
setIsNotColliding, setIsNotColliding,
setIsNotDamaging, setIsNotHarmful,
afterImpact, afterImpact,
destroy, destroy,
], ],
@ -149,9 +149,9 @@ export function rockProjectileJSON() {
isSensor: true, isSensor: true,
}, },
}, },
damaging: { harmful: {
params: { params: {
damageSpecs: [ harmSpecs: [
{ {
affinity: AFFINITY_NONE, affinity: AFFINITY_NONE,
knockback: 500, knockback: 500,
@ -170,7 +170,7 @@ export function rockProjectileJSON() {
emitter: { emitter: {
params: { params: {
particles: { particles: {
damaging: { harmful: {
rate: 0.0125, rate: 0.0125,
count: 5, count: 5,
traits: { traits: {