From afcec7e125331384202aac619abab9395b8b081e Mon Sep 17 00:00:00 2001 From: Paul Shannon Date: Mon, 13 Nov 2017 22:44:06 -0700 Subject: [PATCH] Added actions to add a static ball to the page --- src/commands/randomizeEncounter.ts | 18 +++++++- src/commands/removeDerpyball.ts | 8 ++++ src/commands/throwDerpyball.ts | 10 +++++ src/containers/ControlsContainer.ts | 26 +++++++++++ src/containers/OutsideContainer.ts | 17 ++++--- src/context/OutsideContext.ts | 64 ++++++++++++++------------ src/framework/Executor.ts | 2 +- src/initialize.ts | 14 ++++-- src/main.ts | 29 +++++++++++- src/widgets/Controls.ts | 6 --- src/widgets/Outside.ts | 70 ++++++++++++++++++++++++----- 11 files changed, 206 insertions(+), 58 deletions(-) create mode 100644 src/commands/removeDerpyball.ts create mode 100644 src/commands/throwDerpyball.ts create mode 100644 src/containers/ControlsContainer.ts diff --git a/src/commands/randomizeEncounter.ts b/src/commands/randomizeEncounter.ts index 276713c..ef113f7 100644 --- a/src/commands/randomizeEncounter.ts +++ b/src/commands/randomizeEncounter.ts @@ -3,6 +3,20 @@ import OutsideContext from '../context/OutsideContext'; export type RandomizeEncounterAction = Action; -export default function randomizeEncounter({ state: outsideContext }: RandomizeEncounterAction) { - outsideContext.randomizeEncounter(); +export default function randomizeEncounter({ state: outside }: RandomizeEncounterAction) { + const monsters = outside.getMonsterDefinitions(); + const num = Math.floor(Math.random() * monsters.length); + const monster = monsters[num]; + const { + heights, + name + } = monster; + const distance = Math.random() * 8 + 2; + const height = Math.random() * (heights.max - heights.min) + heights.min; + + outside.setMonster({ + name, + height, + distance + }); }; diff --git a/src/commands/removeDerpyball.ts b/src/commands/removeDerpyball.ts new file mode 100644 index 0000000..c856fc6 --- /dev/null +++ b/src/commands/removeDerpyball.ts @@ -0,0 +1,8 @@ +import { Action } from '../framework/Executor'; +import OutsideContext from '../context/OutsideContext'; + +export type RemoveDerpyballAction = Action; + +export default function removeDerpyball({ state: outside }: RemoveDerpyballAction) { + outside.removeBall(); +} diff --git a/src/commands/throwDerpyball.ts b/src/commands/throwDerpyball.ts new file mode 100644 index 0000000..873b87e --- /dev/null +++ b/src/commands/throwDerpyball.ts @@ -0,0 +1,10 @@ +import { Action } from '../framework/Executor'; +import OutsideContext, { Throw } from '../context/OutsideContext'; + +export type ThrowDerpyballAction = Action; + +export default function throwDerpyball({ payload, state: outside }: ThrowDerpyballAction) { + if (payload) { + outside.throwBall(payload) + } +} diff --git a/src/containers/ControlsContainer.ts b/src/containers/ControlsContainer.ts new file mode 100644 index 0000000..1c2a8a4 --- /dev/null +++ b/src/containers/ControlsContainer.ts @@ -0,0 +1,26 @@ +import Container from '../framework/Container'; +import { ActionType, State } from '../initialize'; +import Controls, { ControlsProperties } from '../widgets/Controls'; +import { Throw } from '../context/OutsideContext'; + +const ControlsContainer = Container(Controls, [ State.App, State.Executor ], { + getProperties([ app, executor]): ControlsProperties { + return { + onActionButtonPressed() { + console.log('pressed', app.state); + const derpyball: Throw = { + direction: [ 1, 0, 0], + initialTime: performance.now(), + position: [ 0, 3, -4], + speed: 1 + }; + executor.execute({ type: ActionType.ThrowDerpyball, payload: derpyball }); + }, + onActionButtonReleased() { + console.log('released', app.state); + } + } + } +}); + +export default ControlsContainer; diff --git a/src/containers/OutsideContainer.ts b/src/containers/OutsideContainer.ts index 6ecdc5b..abf61d8 100644 --- a/src/containers/OutsideContainer.ts +++ b/src/containers/OutsideContainer.ts @@ -2,14 +2,16 @@ import Outside, { OutsideProperties } from '../widgets/Outside'; import Container from '../framework/Container'; import { throws } from '../util/properties'; import OutsideContext from '../context/OutsideContext'; -import { State } from '../initialize'; +import { ActionType, State } from '../initialize'; import AssetContext from '../context/AssetContext'; +import Executor from '../framework/Executor'; -const OutsideContainer = Container(Outside, [ State.Outside, State.Asset ], { - getProperties(payload: [ OutsideContext, AssetContext ]): OutsideProperties { +const OutsideContainer = Container(Outside, [ State.Outside, State.Asset, State.Executor ], { + getProperties(payload: [ OutsideContext, AssetContext, Executor ]): OutsideProperties { const [ outside = throws(), - appContext = throws() + appContext = throws(), + executor = throws() ] = payload; let monster: OutsideProperties['monster']; const monsterInfo = outside.monster; @@ -27,7 +29,12 @@ const OutsideContainer = Container(Outside, [ State.Outside, State.Asset ], { } return { environment: outside.environment, - monster + derpyball: outside.derpyball, + monster, + + removeDerpyball() { + executor.execute(ActionType.RemoveDerpyball); + } } } }); diff --git a/src/context/OutsideContext.ts b/src/context/OutsideContext.ts index 49b9e1c..b68f900 100644 --- a/src/context/OutsideContext.ts +++ b/src/context/OutsideContext.ts @@ -1,4 +1,5 @@ import { InjectorBase } from '../framework/InjectorBase'; +import { assign } from '@dojo/core/lang'; interface Monster { distance: number; @@ -12,6 +13,16 @@ export const enum Environment { Forest = 'forest' } +declare type Dimension3 = [ number, number, number ]; + +export interface Throw { + direction: Dimension3; + initialTime: number; + position: Dimension3; + speed: number; + thrownTime?: number; +} + /** * Defines a monster's traits */ @@ -28,12 +39,14 @@ export interface MonsterDefinition { } export default class OutsideContext extends InjectorBase { - private _environment: Environment = Environment.Forest; + environment: Environment = Environment.Forest; + + private _derpyball?: Throw; private _monster?: Monster; private _monsterDefinitions: Map = new Map(); - get environment(): Environment { - return this._environment; + get derpyball(): Throw | undefined { + return this._derpyball; } get monster(): Monster | undefined { @@ -44,39 +57,34 @@ export default class OutsideContext extends InjectorBase { this._monsterDefinitions.set(definitions.name, definitions); } - randomizeEncounter() { - const { - heights, - name, - } = this.randomMonster(); - const distance = Math.random() * 8 + 2; - const height = Math.random() * (heights.max - heights.min) + heights.min; + ballThrown() { + if (this._derpyball && !this._derpyball.thrownTime) { + this._derpyball.thrownTime = performance.now(); + } + } - this.setMonster({ - name, - height, - distance - }); + getMonsterDefinitions(): MonsterDefinition[] { + return Array.from(this._monsterDefinitions.values()); } - setEnvironment(name: Environment) { - if (this._environment !== name) { - this._environment = name; - this.emitInvalidate(); + removeBall() { + if (this._derpyball) { + this._derpyball = undefined; } } - setMonster(monster: Monster | undefined) { - if (this._monster !== monster) { - this._monster = monster; - this.emitInvalidate(); + throwBall(value: Throw) { + if (!this._derpyball && value) { + this._derpyball = { + direction: Array.from(value.direction), + initialTime: value.initialTime || performance.now(), + position: Array.from(value.position), + speed: value.speed + } } } - private randomMonster() { - const max = this._monsterDefinitions.size; - const num = Math.floor(Math.random() * max); - const definitions = Array.from(this._monsterDefinitions.values()); - return definitions[num]; + setMonster(monster: Monster | undefined) { + this._monster = assign({}, monster); } } diff --git a/src/framework/Executor.ts b/src/framework/Executor.ts index 898ad80..85cb3d2 100644 --- a/src/framework/Executor.ts +++ b/src/framework/Executor.ts @@ -9,7 +9,7 @@ export interface Command { export interface Action { type: string; - payload?: T; + payload: T; state: U; } diff --git a/src/initialize.ts b/src/initialize.ts index 16a455a..2e83d3d 100644 --- a/src/initialize.ts +++ b/src/initialize.ts @@ -8,6 +8,8 @@ import randomizeEncounter from './commands/randomizeEncounter'; import registerMonsters from './commands/registerMonsters'; import AssetContext from './context/AssetContext'; import loadedMonsters from './commands/loadedMonsters'; +import removeDerpyball from './commands/removeDerpyball'; +import throwDerpyball from './commands/throwDerpyball'; // Require globals require('aframe'); @@ -23,20 +25,24 @@ export const enum State { export const enum ActionType { Initialize = 'initialize', - LoadMonsters = 'loadMonsters', LoadedMonsters = 'loadedMonsters', + LoadMonsters = 'loadMonsters', RandomizeEncounter = 'randomizeEncounter', - RegisterMonsters = 'registerMonsters' + RegisterMonsters = 'registerMonsters', + RemoveDerpyball = 'removeDerpyball', + ThrowDerpyball = 'throwDerpyball' } export default function initialize() { const registry = new Registry(); const executor = new Executor(registry, [ { type: ActionType.Initialize, handler: initializeApp, state: [ State.App, State.Executor ] }, - { type: ActionType.LoadMonsters, handler: loadMonsters, state: State.Executor }, { type: ActionType.LoadedMonsters, handler: loadedMonsters, state: [ State.App, State.Executor ] }, + { type: ActionType.LoadMonsters, handler: loadMonsters, state: State.Executor }, { type: ActionType.RandomizeEncounter, handler: randomizeEncounter, state: State.Outside }, - { type: ActionType.RegisterMonsters, handler: registerMonsters, state: [ State.Asset, State.Outside ] } + { type: ActionType.RegisterMonsters, handler: registerMonsters, state: [ State.Asset, State.Outside ] }, + { type: ActionType.RemoveDerpyball, handler: removeDerpyball, state: State.Outside }, + { type: ActionType.ThrowDerpyball, handler: throwDerpyball, state: State.Outside } ]); const appContext = new AppContext(); const assetContext = new AssetContext(); diff --git a/src/main.ts b/src/main.ts index 3600463..cc1d8c5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,7 @@ import { ProjectorMixin } from '@dojo/widget-core/mixins/Projector'; -import initialize from './initialize'; +import initialize, { ActionType } from './initialize'; import AppContainer from './containers/AppContainer'; +import Executor from './framework/Executor'; const root = document.querySelector('go-derpy') || undefined; @@ -14,5 +15,31 @@ const projector = new Projector(); projector.setProperties({ registry }); projector.append(root); +// TODO move this into a listener +document.addEventListener('keydown', (event: KeyboardEvent) => { + if (event.keyCode === 32) { + const executor = registry.getInjector('executor'); + if (!executor) { + console.warn('missing executor'); + return; + } + const ball = { + direction: [ 1, 0, 0], + initialTime: performance.now(), + position: [ 0, 3, -4], + speed: 1 + }; + executor.execute(ActionType.ThrowDerpyball, ball); + } + else if (event.key === '-') { + const executor = registry.getInjector('executor'); + if (!executor) { + console.warn('missing executor'); + return; + } + executor.execute(ActionType.RemoveDerpyball); + } +}); + ( root.ownerDocument).registry = registry; ( root.ownerDocument).projector = projector; diff --git a/src/widgets/Controls.ts b/src/widgets/Controls.ts index be18de7..428d240 100644 --- a/src/widgets/Controls.ts +++ b/src/widgets/Controls.ts @@ -7,12 +7,6 @@ export interface ControlsProperties { onActionButtonReleased?: EventHandler } -function logEvent(name: string) { - return (event: Event) => { - console.log(name, event); - } -} - export default class Controls extends WidgetBase { protected render(): DNode[] { return [ diff --git a/src/widgets/Outside.ts b/src/widgets/Outside.ts index 0c98c1f..b1df291 100644 --- a/src/widgets/Outside.ts +++ b/src/widgets/Outside.ts @@ -1,8 +1,9 @@ import { v, w } from '@dojo/widget-core/d'; -import { WidgetProperties } from '@dojo/widget-core/interfaces'; +import { DNode, WidgetProperties } from '@dojo/widget-core/interfaces'; import { WidgetBase } from '@dojo/widget-core/WidgetBase'; import ObjModel from './ObjModel'; import { objHeight } from '../components/heightComponent'; +import { Throw } from '../context/OutsideContext'; export interface Monster { name: string; @@ -13,15 +14,47 @@ export interface Monster { } export interface OutsideProperties extends WidgetProperties { + derpyball?: Throw; environment: string; monster?: Monster; + + removeDerpyball(): void; } export default class Outside extends WidgetBase { protected render() { + return [ + ... this.renderEnvironment(), + this.renderDerpyball(), + this.renderMonster() + ]; + } + + private onComponentChanged(event: any) { + if (event.detail.name === 'position') { + const position = event.detail.target.getAttribute('position'); + console.log(position); + if (position.y < -1) { + this.properties.removeDerpyball(); + } + } + } + + private renderDerpyball() { const { - environment, - monster + derpyball + } = this.properties; + + return derpyball ? v('a-sphere', { + 'static-body': 'shape: sphere', + oncomponentchanged: this.onComponentChanged, + position: derpyball.position.join(' ') + }) : null; + } + + private renderEnvironment(): DNode[] { + const { + environment } = this.properties; return [ @@ -29,13 +62,28 @@ export default class Outside extends WidgetBase { environment: `preset: ${ environment }`, 'static-body': '' }), - monster ? w(ObjModel, { - mtl: monster.mtl, - src: monster.obj, - position: `0 0 -${ monster.distance }`, - 'static-body': 'shape: box', - [objHeight]: `${ monster.height }` - }) : null - ]; + v('a-box', { + 'class': '.ground', + depth: '30', + height: '0.1', + 'static-body': 'shape: auto', + visible: 'false', + width: '30' + }), + ] + } + + private renderMonster() { + const { + monster + } = this.properties; + + return monster ? w(ObjModel, { + mtl: monster.mtl, + src: monster.obj, + position: `0 0 -${ monster.distance }`, + 'static-body': 'shape: box', + [objHeight]: `${ monster.height }` + }) : null } }