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
- ✔ React UI
- ❌ Multiple rooms
- ✔ Send relevant entity URIs to client; cache and use instead of full transfer
- ✔ Forget remembered entities after a while
- ✔ Optimize informed and Packer::pack: don't use .map on immutables
- ✔ Optimize Damaging::tick
- ✔ Optimize Harmful::tick
- ✔ Manual state sync
- ✔ 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) {
// Maintain damage element.
const promise = this.stage.findUiElement('.damage');
// Maintain harm element.
const promise = this.stage.findUiElement('.harm');
promise.then((inner) => {
const innerStyles = () => {
const {realOffset} = this.stage.camera;

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ export function blueFireJSON() {
const json = fireJSON();
json.traits.animated.params.animations.idle.uri = '/blue-fire.animation.json';
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';
return json;
}

View File

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

View File

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

View File

@ -125,6 +125,18 @@ export function kittyJSON() {
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.
export function potionJSON() {
const causeHealing = buildInvoke(['wielder', 'takeDamageFrom'], [
const causeHealing = buildInvoke(['wielder', 'takeHarmFrom'], [
buildTraversal(['item']),
]);
const decrement = buildInvoke(
@ -15,9 +15,9 @@ export function potionJSON() {
);
return {
traits: {
damaging: {
harmful: {
params: {
damageSpecs: [
harmSpecs: [
{
affinity: AFFINITY_NONE,
lock: 0,

View File

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