diff --git a/Program/assets/Background.png b/Program/assets/Background.png index 4ffb304..0e8c567 100644 Binary files a/Program/assets/Background.png and b/Program/assets/Background.png differ diff --git a/Program/src/Components/clickable.ts b/Program/src/Components/clickable.ts index 5855622..95ee7f0 100644 --- a/Program/src/Components/clickable.ts +++ b/Program/src/Components/clickable.ts @@ -1,10 +1,12 @@ -import { Component } from "ecsy"; -import { interaction } from "pixi.js"; +import { Component, TagComponent } from "ecsy" +import { interaction } from "pixi.js" + +export class InitializedClickable extends TagComponent {} export class Clickable extends Component { - actions: { [id: string] : (event: interaction.InteractionEvent) => void } = {} + actions: { [id: string]: (event: interaction.InteractionEvent) => void } = {} - reset(){ + reset() { this.actions = {} } -} \ No newline at end of file +} diff --git a/Program/src/Components/hoverObject.ts b/Program/src/Components/hoverObject.ts new file mode 100644 index 0000000..1e363dc --- /dev/null +++ b/Program/src/Components/hoverObject.ts @@ -0,0 +1,11 @@ +import { Component, TagComponent } from "ecsy" + +export class InitializedHoverObject extends TagComponent {} + +export class HoverObject extends Component { + label: string + + reset() { + this.label = "" + } +} diff --git a/Program/src/Components/initializedClickable.ts b/Program/src/Components/initializedClickable.ts deleted file mode 100644 index c434c57..0000000 --- a/Program/src/Components/initializedClickable.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { TagComponent } from "ecsy"; - -export class InitializedClickable extends TagComponent {} \ No newline at end of file diff --git a/Program/src/Systems/ClickableSystem.ts b/Program/src/Systems/ClickableSystem.ts index c957a7f..cd51e15 100644 --- a/Program/src/Systems/ClickableSystem.ts +++ b/Program/src/Systems/ClickableSystem.ts @@ -1,11 +1,13 @@ import { System, Entity, Not } from "ecsy" import { PixiRepresentation } from "../Components/rendering/pixiRepresentation" -import { Clickable } from "../Components/clickable" -import { InitializedClickable } from "../Components/initializedClickable" +import { Clickable, InitializedClickable } from "../Components/clickable" import globals from "../globals" import { interaction } from "pixi.js" +import { HoverObject, InitializedHoverObject } from "../Components/hoverObject" +import { TooltipSystem } from "./UI/TooltipSystem" +import { hide } from "../util" -export class ClickableSystem extends System { +export class MouseSystem extends System { priority = 80 init() { @@ -42,9 +44,46 @@ export class ClickableSystem extends System { //remove init clickable tag so it gets initialized again as soon as a pixirepresentation exists again entity.removeComponent(InitializedClickable) }) + + // Iterate through all the entities on the query + this.queries.newHoverObjects.results.forEach((entity: Entity) => { + let object = entity.getComponent(PixiRepresentation).value + let hover = entity.getComponent(HoverObject) + + let ttSystem = globals.world.getSystem(TooltipSystem) as TooltipSystem + + object.interactive = true + object.on("pointerover", () => ttSystem?.showTooltip(hover.label, entity)) + object.on("pointerout", () => ttSystem?.cancelTooltip(entity)) + object.on("pointercancel", () => ttSystem?.cancelTooltip(entity)) + object.on(hide, () => ttSystem?.cancelTooltip(entity)) + + entity.addComponent(InitializedHoverObject) + }) + + this.queries.changedHoverObjects.changed.forEach((entity: Entity) => { + let object = entity.getComponent(PixiRepresentation).value + let hover = entity.getComponent(HoverObject) + + let ttSystem = globals.world.getSystem(TooltipSystem) as TooltipSystem + object.removeListener("pointerover") + object.removeListener("pointerout") + object.removeListener("pointercancel") + object.removeListener(hide) + object.on("pointerover", () => ttSystem?.showTooltip(hover.label, entity)) + object.on("pointerout", () => ttSystem?.cancelTooltip(entity)) + object.on("pointercancel", () => ttSystem?.cancelTooltip(entity)) + object.on(hide, () => ttSystem?.cancelTooltip(entity)) + }) + + this.queries.invisibleHoverObjects.results.forEach((entity: Entity) => { + //remove init clickable tag so it gets initialized again as soon as a pixirepresentation exists again + entity.removeComponent(InitializedHoverObject) + }) } static queries = { + //clickable stuff newClickables: { components: [PixiRepresentation, Clickable, Not(InitializedClickable)], }, @@ -57,6 +96,23 @@ export class ClickableSystem extends System { invisibleClickables: { components: [InitializedClickable, Not(PixiRepresentation)], }, + + newHoverObjects: { + components: [ + PixiRepresentation, + HoverObject, + Not(InitializedHoverObject), + ], + }, + changedHoverObjects: { + components: [PixiRepresentation, HoverObject, InitializedHoverObject], + listen: { + changed: [Clickable], + }, + }, + invisibleHoverObjects: { + components: [InitializedHoverObject, Not(PixiRepresentation)], + }, } queries: any } diff --git a/Program/src/Systems/UI/InfoSystem.ts b/Program/src/Systems/UI/InfoSystem.ts index bb9da11..511ee3c 100644 --- a/Program/src/Systems/UI/InfoSystem.ts +++ b/Program/src/Systems/UI/InfoSystem.ts @@ -5,6 +5,7 @@ import globals from "../../globals" import { Adventure } from "../../Components/adventure" import { InfoMenu } from "../../UI/InfoMenu" import { PlanningMenu } from "../../UI/PlanningMenu" +import { setVisible } from "../../util" enum InfoState { None, @@ -46,8 +47,8 @@ export class InfoSystem extends System { setState(newState: InfoState) { let previousStateMenu = this.getStateMenu(this.state) let nextStateMenu = this.getStateMenu(newState) - if (previousStateMenu) previousStateMenu.visible = false - if (nextStateMenu) nextStateMenu.visible = true + if (previousStateMenu) setVisible(previousStateMenu, false) + if (nextStateMenu) setVisible(nextStateMenu, true) this.state = newState } diff --git a/Program/src/Systems/UI/TooltipSystem.ts b/Program/src/Systems/UI/TooltipSystem.ts index f003aa9..888f875 100644 --- a/Program/src/Systems/UI/TooltipSystem.ts +++ b/Program/src/Systems/UI/TooltipSystem.ts @@ -15,6 +15,9 @@ export class TooltipSystem extends System { fontFamily: "babyblocks", fontSize: 8, fill: 0x000000, + + stroke: 0xeec39a, + strokeThickness: 2, }) globals.app.stage.addChild(this.tooltip) this.tooltip.anchor.set(0, 1) @@ -27,6 +30,7 @@ export class TooltipSystem extends System { this.source = source this.tooltip.text = text this.tooltip.visible = true + this.updatePosition() } cancelTooltip(source: any) { @@ -38,11 +42,15 @@ export class TooltipSystem extends System { execute(delta: number) { if (this.active) { - let pos = globals.app.renderer.plugins.interaction.mouse.global - this.tooltip.position = pos + this.updatePosition() } } + updatePosition() { + let pos = globals.app.renderer.plugins.interaction.mouse.global + this.tooltip.position = pos + } + static queries = {} queries: any } diff --git a/Program/src/Systems/VisibleHumanSystem.ts b/Program/src/Systems/VisibleHumanSystem.ts index 01bc2d8..590cdca 100644 --- a/Program/src/Systems/VisibleHumanSystem.ts +++ b/Program/src/Systems/VisibleHumanSystem.ts @@ -5,6 +5,8 @@ import { InCabin } from "../Components/inCabin" import { Position } from "../Components/position" import { SpriteRenderer } from "../Components/rendering/spriteRenderer" import { Vector } from "../Datatypes/MathTypes/vector" +import { HoverObject } from "../Components/hoverObject" +import { Name } from "../Components/name" // MovableSystem export class VisibleHumanSystem extends System { @@ -18,6 +20,8 @@ export class VisibleHumanSystem extends System { texture: texture, offset: new Vector(-texture.width / 2, -texture.height), }) + let name = entity.getComponent(Name) + entity.addComponent(HoverObject, { label: name.fullName() }) }) //update sprite texture this.queries.visibleHumans.changed.forEach((entity: Entity) => { diff --git a/Program/src/UI/AdventurerMenu.ts b/Program/src/UI/AdventurerMenu.ts new file mode 100644 index 0000000..d7e0aab --- /dev/null +++ b/Program/src/UI/AdventurerMenu.ts @@ -0,0 +1,21 @@ +import { Container } from "pixi.js" + +import { InfoSystem } from "../Systems/UI/InfoSystem" +//import { canvasWidth, canvasHeight } from "../constants" + +export class AdventurerMenu extends Container { + system: InfoSystem + listContainer: Container + + constructor(system: InfoSystem) { + super() + + this.system = system + + this.listContainer = new Container() + + this.addChild(this.listContainer) + + this.visible = true + } +} diff --git a/Program/src/UI/Button.ts b/Program/src/UI/Button.ts index 6f22b42..25ddd7a 100644 --- a/Program/src/UI/Button.ts +++ b/Program/src/UI/Button.ts @@ -1,6 +1,7 @@ import { Sprite, Texture, interaction } from "pixi.js" import globals from "../globals" import { TooltipSystem } from "../Systems/UI/TooltipSystem" +import { hide, show } from "../util" export class Button extends Sprite { //TODO: sound utility @@ -29,6 +30,8 @@ export class Button extends Sprite { this.on("pointerover", this.startHover) this.on("pointerout", this.endHover) this.on("pointercancel", this.endHover) + this.on(hide, this.endHover) + this.on(show, this.tryCatchMouse) this.on("pointerdown", this.startClick) this.on("pointerup", this.endClick) @@ -79,6 +82,13 @@ export class Button extends Sprite { this.texture = this.defaultSprite } + tryCatchMouse() { + let mousePos = globals.app.renderer.plugins.interaction.mouse.global + if (this.getBounds().contains(mousePos.x, mousePos.y)) { + this.startHover() + } + } + onClick(action: (event: interaction.InteractionEvent) => void, context: any) { this.on("click", action, context) } diff --git a/Program/src/UI/PlanningMenu.ts b/Program/src/UI/PlanningMenu.ts index d27b71f..a0a2417 100644 --- a/Program/src/UI/PlanningMenu.ts +++ b/Program/src/UI/PlanningMenu.ts @@ -3,15 +3,25 @@ import { Container, Loader, Texture, interaction } from "pixi.js" import { Point } from "../Datatypes/MathTypes/point" import { InfoSystem } from "../Systems/UI/InfoSystem" import { Button } from "./Button" +import { AdventurerMenu } from "./AdventurerMenu" +import { canvasWidth, canvasHeight } from "../constants" export class PlanningMenu extends Container { system: InfoSystem + adventurers: AdventurerMenu + constructor(system: InfoSystem) { super() this.system = system + this.adventurers = new AdventurerMenu(system) + this.adventurers.position = new Point(0, 0) + this.adventurers.width = canvasWidth + this.adventurers.height = canvasHeight - canvasWidth - 18 + this.addChild(this.adventurers) + this.setupButtons() this.visible = false } @@ -30,7 +40,12 @@ export class PlanningMenu extends Container { action: this.system.abortPlans, context: this.system, }, - { texture: buttons["People"], tooltip: "Adventurers" }, + { + texture: buttons["People"], + tooltip: "Adventurers", + action: () => (this.adventurers.visible = true), + context: this, + }, { texture: buttons["Items"], tooltip: "Supplies" }, { texture: buttons["Dir"], tooltip: "Travel Direction" }, { texture: buttons["Go"], tooltip: "Start Adventure!" }, diff --git a/Program/src/index.ts b/Program/src/index.ts index a8f6c89..c1a6317 100644 --- a/Program/src/index.ts +++ b/Program/src/index.ts @@ -9,8 +9,8 @@ globals.app = new Application({ width: canvasWidth, height: canvasHeight, backgroundColor: 0x10101010, - resolution: 1, - antialias: false + resolution: 10, + antialias: false, }) document.body.appendChild(globals.app.view) diff --git a/Program/src/setup.ts b/Program/src/setup.ts index 7d22c2b..746a46c 100644 --- a/Program/src/setup.ts +++ b/Program/src/setup.ts @@ -1,7 +1,7 @@ import { Loader, Sprite } from "pixi.js" import { Door } from "./Components/door" -import { DebugRect } from "./Components/rendering/debugRect" -import { roomBounds } from "./constants" +//import { DebugRect } from "./Components/rendering/debugRect" +//import { roomBounds } from "./constants" import globals from "./globals" import { createRandomHuman } from "./util" import { Position } from "./Components/position" @@ -14,7 +14,7 @@ import { RandomWalkSystem } from "./Systems/RandomWalkSystem" import { PathWalkerSystem } from "./Systems/PathWalkerSystem" import { VisibleHumanSystem } from "./Systems/VisibleHumanSystem" import { SpriteSystem } from "./Systems/rendering/SpriteSystem" -import { ClickableSystem } from "./Systems/ClickableSystem" +import { MouseSystem } from "./Systems/ClickableSystem" import { InfoSystem } from "./Systems/UI/InfoSystem" import { DebugRenderSystem } from "./Systems/rendering/DebugRenderSystem" import { ZOrderSystem } from "./Systems/rendering/ZOrderSystem" @@ -41,7 +41,7 @@ export function setup() { .registerSystem(TooltipSystem) .registerSystem(SpriteSystem) //prio 70 .registerSystem(DebugRenderSystem) //prio 70 - .registerSystem(ClickableSystem) //prio 80 + .registerSystem(MouseSystem) //prio 80 .registerSystem(ZOrderSystem) //prio 80 .registerSystem(PixiCleanupSystem) //prio 99 .registerSystem(RenderSystem) //prio 100 @@ -67,9 +67,9 @@ export function setup() { }) //debug room bounds - globals.world - .createEntity() - .addComponent(DebugRect, { color: 0x0000ff, rect: roomBounds }) + //globals.world + // .createEntity() + // .addComponent(DebugRect, { color: 0x0000ff, rect: roomBounds }) //example humans //TODO delete those diff --git a/Program/src/util.ts b/Program/src/util.ts index 9df6f56..10bfff1 100644 --- a/Program/src/util.ts +++ b/Program/src/util.ts @@ -1,5 +1,5 @@ import { Entity, ComponentConstructor, Component, World } from "ecsy" -import { Loader, interaction } from "pixi.js" +import { Loader, interaction, Container } from "pixi.js" import { Human } from "./Components/human" import { Name } from "./Components/name" import { Appearance } from "./Components/appearance" @@ -11,6 +11,9 @@ import { roomBounds, FirstNames, LastNames } from "./constants" import { Clickable } from "./Components/clickable" import globals from "./globals" +export let hide = "hide" +export let show = "show" + export function addOrSetComponent( entity: Entity, Component: ComponentConstructor, @@ -37,6 +40,22 @@ export function randomArrayValue(array: any[]): any { return array[randomIndex] } +export function setVisible(container: PIXI.Container, visible: boolean) { + container.visible = visible + emitRecursiveEvent(container, visible ? show : hide) +} + +function emitRecursiveEvent( + container: Container | PIXI.DisplayObject, + event: string +) { + container.emit(event) + if (container instanceof Container) + for (const child of container.children) { + emitRecursiveEvent(child, event) + } +} + export function createRandomHuman(world: World) { let resources = Loader.shared.resources let entity = world.createEntity() @@ -44,10 +63,10 @@ export function createRandomHuman(world: World) { .addComponent(Human) .addComponent(Name, { first: randomArrayValue(FirstNames), - last: randomArrayValue(LastNames) + last: randomArrayValue(LastNames), }) .addComponent(Appearance, { - idleTexture: resources["Human"].texture + idleTexture: resources["Human"].texture, }) //Todo: generate appearance from body traits instead? .addComponent(InCabin) .addComponent(Position, { value: roomBounds.randomPoint() }) @@ -57,7 +76,7 @@ export function createRandomHuman(world: World) { actions: { click: (event: interaction.InteractionEvent) => { globals.selected.set(entity) - } - } + }, + }, }) }