feat: interactive throbber

This commit is contained in:
cha0s 2021-02-16 15:50:38 -06:00
parent 0a1bfa0732
commit 98c3fa22ac
2 changed files with 84 additions and 12 deletions

View File

@ -1,18 +1,48 @@
/* eslint-disable no-unused-vars */
import {buildInvoke} from '@avocado/behavior';
import {Rectangle, Vector} from '@avocado/math';
import {Ticker} from '@avocado/timing';
import {LfoResult, Ticker} from '@avocado/timing';
import {Trait} from '@avocado/traits';
export default () => class Initiator extends Trait {
#ticker = new Ticker(0.25);
#adjustmentThrobber;
#glowThrobber;
#ticker = new Ticker(0.125);
#target;
constructor() {
super();
this.#ticker.on('tick', this.determineTarget, this);
if ('client' === process.env.SIDE) {
this.#adjustmentThrobber = {brightness: 1.5};
this.#adjustmentThrobber.lfo = new LfoResult(
this.#adjustmentThrobber,
{
brightness: {
frequency: 0.75,
magnitude: 0.5,
median: 1.5,
modulators: ['Triangle', 'Sine'],
},
},
);
this.#glowThrobber = {outerStrength: 0.125};
this.#glowThrobber.lfo = new LfoResult(
this.#glowThrobber,
{
outerStrength: {
frequency: 0.75,
magnitude: 0.25,
median: 0.125,
modulators: ['Triangle', 'Sine'],
},
},
);
}
}
static dependencies() {
@ -23,23 +53,28 @@ export default () => class Initiator extends Trait {
}
determineTarget() {
const incident = Vector.add(
this.entity.position,
Vector.directionalProjection(this.entity.direction, [0, 8]),
);
const interactives = this.entity.list.queryEntities(
Rectangle.centerOn(
Vector.add(
this.entity.position,
Vector.directionalProjection(this.entity.direction, [0, 8]),
),
[16, 16],
),
Rectangle.centerOn(incident, [16, 16]),
buildInvoke(['query', 'is'], ['Interactive']),
);
const oldTarget = this.#target;
if (interactives.length > 0) {
// TODO sort distance
[this.#target] = interactives;
[this.#target] = interactives
.map((interactive) => [interactive.distanceFrom({position: incident}), interactive])
.sort(([l], [r]) => (l < r ? -1 : 1))
.map(([, interactive]) => interactive);
}
else {
this.#target = undefined;
}
if (oldTarget !== this.#target) {
this.targetChanged(oldTarget, this.#target);
}
}
hooks() {
@ -58,8 +93,31 @@ export default () => class Initiator extends Trait {
};
}
// eslint-disable-next-line class-methods-use-this
targetChanged(oldTarget) {
oldTarget?.container?.removeFilter('interactive', 'adjustment');
oldTarget?.container?.removeFilter('interactive', 'glow');
}
renderTick(elapsed) {
this.tickAdjustment(elapsed);
this.tickGlow(elapsed);
}
tick(elapsed) {
this.#ticker.tick(elapsed);
}
tickAdjustment(elapsed) {
const {brightness, lfo} = this.#adjustmentThrobber;
lfo.tick(elapsed);
this.#target?.container?.addFilter('interactive', 'adjustment', {brightness});
}
tickGlow(elapsed) {
const {outerStrength, lfo} = this.#glowThrobber;
lfo.tick(elapsed);
this.#target?.container?.addFilter('interactive', 'glow', {color: 0, outerStrength});
}
};

View File

@ -4,9 +4,14 @@ import {
compile,
Context,
} from '@avocado/behavior';
import {Trait} from '@avocado/traits';
import {StateProperty, Trait} from '@avocado/traits';
import {compose} from '@latus/core';
export default (latus) => class Interactive extends Trait {
const decorate = compose(
StateProperty('isInteractive'),
);
export default (latus) => class Interactive extends decorate(Trait) {
#actions;
@ -16,6 +21,12 @@ export default (latus) => class Interactive extends Trait {
};
}
static defaultState() {
return {
isInteractive: true,
};
}
async load(json) {
await super.load(json);
this.#actions = new Actions(compile(this.params.actions, latus));
@ -25,6 +36,9 @@ export default (latus) => class Interactive extends Trait {
return {
interact: (initiator) => {
if (!super.isInteractive) {
return;
}
const context = new Context(
{
entity: this.entity,