diff --git a/public/sprites/hud_instructions/hud_instructions.png b/public/sprites/hud_instructions/hud_instructions.png new file mode 100644 index 0000000..4a2abd4 Binary files /dev/null and b/public/sprites/hud_instructions/hud_instructions.png differ diff --git a/public/sprites/referee/REFEREE.png b/public/sprites/referee/REFEREE.png index 2b195ee..09b4172 100644 Binary files a/public/sprites/referee/REFEREE.png and b/public/sprites/referee/REFEREE.png differ diff --git a/public/sprites/score_numbers/score_numbers.json b/public/sprites/score_numbers/score_numbers.json new file mode 100644 index 0000000..2e0b010 --- /dev/null +++ b/public/sprites/score_numbers/score_numbers.json @@ -0,0 +1,108 @@ +{ "frames": { + "score_numbers 0.aseprite": { + "frame": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 1.aseprite": { + "frame": { "x": 48, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 2.aseprite": { + "frame": { "x": 96, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 3.aseprite": { + "frame": { "x": 144, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 4.aseprite": { + "frame": { "x": 192, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 5.aseprite": { + "frame": { "x": 240, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 6.aseprite": { + "frame": { "x": 288, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 7.aseprite": { + "frame": { "x": 336, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 8.aseprite": { + "frame": { "x": 384, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + }, + "score_numbers 9.aseprite": { + "frame": { "x": 432, "y": 0, "w": 48, "h": 48 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 48, "h": 48 }, + "sourceSize": { "w": 48, "h": 48 }, + "duration": 100 + } + }, + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.2.34.1-arm64", + "image": "score_numbers.png", + "format": "RGBA8888", + "size": { "w": 480, "h": 48 }, + "scale": "1", + "frameTags": [ + { "name": "0", "from": 0, "to": 0, "direction": "forward" }, + { "name": "1", "from": 1, "to": 1, "direction": "forward" }, + { "name": "2", "from": 2, "to": 2, "direction": "forward" }, + { "name": "3", "from": 3, "to": 3, "direction": "forward" }, + { "name": "4", "from": 4, "to": 4, "direction": "forward" }, + { "name": "5", "from": 5, "to": 5, "direction": "forward" }, + { "name": "6", "from": 6, "to": 6, "direction": "forward" }, + { "name": "7", "from": 7, "to": 7, "direction": "forward" }, + { "name": "8", "from": 8, "to": 8, "direction": "forward" }, + { "name": "9", "from": 9, "to": 9, "direction": "forward" } + ], + "layers": [ + { "name": "Layer 1", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/public/sprites/score_numbers/score_numbers.png b/public/sprites/score_numbers/score_numbers.png new file mode 100644 index 0000000..5b4bc2a Binary files /dev/null and b/public/sprites/score_numbers/score_numbers.png differ diff --git a/public/sprites/team_badges/blue_team_badge.png b/public/sprites/team_badges/blue_team_badge.png new file mode 100644 index 0000000..f2c92c8 Binary files /dev/null and b/public/sprites/team_badges/blue_team_badge.png differ diff --git a/public/sprites/team_badges/red_team_badge.png b/public/sprites/team_badges/red_team_badge.png new file mode 100644 index 0000000..e484433 Binary files /dev/null and b/public/sprites/team_badges/red_team_badge.png differ diff --git a/src/actors/base-player.ts b/src/actors/base-player.ts index 44b3f81..97519f0 100644 --- a/src/actors/base-player.ts +++ b/src/actors/base-player.ts @@ -24,15 +24,15 @@ export class BasePlayer extends ex.Actor { animations: Record isPain = false + painFrames = 5 isSprinting = false debug = false constructor({ sprite, debug, team, ...args }: BasePlayerArgs) { super({ width: 16, - height: 12, + height: 16, collisionType: ex.CollisionType.Active, - collider: ex.Shape.Box(16, 12, ex.vec(0.5, 1), ex.vec(0, 12)), ...args, }) this.team = team @@ -52,7 +52,7 @@ export class BasePlayer extends ex.Actor { this.animations.Pain.events.on('loop', (a) => { isPainCount++ - if (isPainCount > 3) { + if (isPainCount > this.painFrames) { this.isPain = false isPainCount = 0 } @@ -91,17 +91,21 @@ export class BasePlayer extends ex.Actor { this.currentGraphic().flipHorizontal = flip } + isAnimation(name: string) { + return this.currentGraphic() === this.animations[name] + } + moveTo(pos: ex.Vector, speed: number) { const angle = pos.sub(this.pos).toAngle() this.vel = ex.vec(speed * Math.cos(angle), speed * Math.sin(angle)) } - hit(direction: ex.Vector) { + scare(direction: ex.Vector) { this.isPain = true this.isSprinting = false this.setAnimation('Pain') - assets.snd_clack.play() + assets.snd_dashB.play() this.vel = direction.scale(500) } diff --git a/src/actors/icecream-truck.ts b/src/actors/icecream-truck.ts new file mode 100644 index 0000000..b93b00f --- /dev/null +++ b/src/actors/icecream-truck.ts @@ -0,0 +1,108 @@ +import { Engine } from 'excalibur' +import { assets } from 'src/assets' +import MatchScene from 'src/classes/MatchScene' + +export class IcecreamTruck extends ex.Actor { + declare scene: MatchScene + + hasIcecream = true + isGivingIcecream = false + + animations = { + PopUp: assets.ase_icecreamTruck.getAnimation('PopUp')!, + PopDown: assets.ase_icecreamTruck.getAnimation('PopDown')!, + } + + sprites = { + hide: assets.ase_icecreamTruck.getSpriteSheet()?.getSprite(0, 0)!, + } + + initialPosition: ex.Vector + constructor() { + super({ + z: 1, + collisionType: ex.CollisionType.Fixed, + width: 80, + height: 48, + anchor: ex.vec(0.5, 1), + }) + + this.body.mass = 999999 + this.graphics.use(this.sprites.hide) + } + + onInitialize(_engine: Engine): void { + this.initialPosition = ex.vec( + this.scene.field.right - this.width / 2 - 80, + this.scene.field.top + this.height + 4 + ) + + this.pos = this.initialPosition + + this.animations.PopUp.events.on('frame', (ev: any) => { + const isLastFrame = this.animations.PopUp.frames[3].graphic === ev.graphic + + if (isLastFrame) { + this.animations.PopUp.pause() + } + }) + + this.animations.PopDown.events.on('loop', () => { + this.graphics.use(this.sprites.hide) + }) + } + + update(engine: Engine, delta: number): void { + const distanceToRef = this.scene.referee.pos.distance(this.pos) + + const currentAnim = this.graphics.current[0] + + if (!this.isGivingIcecream) { + if ( + this.hasIcecream && + distanceToRef < 50 && + currentAnim.graphic !== this.animations.PopUp + ) { + this.animations.PopUp.goToFrame(0) + this.graphics.use(this.animations.PopUp) + this.animations.PopUp.play() + } else if ( + distanceToRef >= 50 && + currentAnim.graphic === this.animations.PopUp + ) { + this.graphics.use(this.animations.PopDown) + } + } + } + + giveIcecream() { + const distractionTime = 20000 + const restockTime = 20000 + + if (!this.isGivingIcecream && this.hasIcecream) { + assets.snd_bell.play() + this.isGivingIcecream = true + this.hasIcecream = false + this.graphics.use(this.animations.PopUp) + this.animations.PopUp.play() + this.scene.away.goalie.distract(this, distractionTime) + + this.actions + .delay(distractionTime) + .toPromise() + .then(() => { + this.isGivingIcecream = false + this.graphics.use(this.animations.PopDown) + this.actions + .delay(restockTime) + .toPromise() + .then(() => { + this.hasIcecream = true + assets.snd_watchOut.play() + }) + }) + return true + } + return false + } +} diff --git a/src/actors/net.ts b/src/actors/net.ts index ff15edd..11cdd38 100644 --- a/src/actors/net.ts +++ b/src/actors/net.ts @@ -82,8 +82,8 @@ export class Net extends ex.Actor { name: 'goalLine', collisionType: ex.CollisionType.Passive, collider: new ex.EdgeCollider({ - begin: topFront.add(ex.vec(this.team === 'home' ? -12 : 12, 0)), - end: bottomFront.add(ex.vec(this.team === 'home' ? -12 : 12, 0)), + begin: topFront.add(ex.vec(this.team === 'home' ? -12 : 12, 4)), + end: bottomFront.add(ex.vec(this.team === 'home' ? -12 : 12, -2)), }), }) diff --git a/src/actors/referee.ts b/src/actors/referee.ts index fe471d1..354c5a4 100644 --- a/src/actors/referee.ts +++ b/src/actors/referee.ts @@ -4,6 +4,7 @@ import { assets } from 'src/assets' import { DirectionQueue } from 'src/classes/DirectionQueue' import { Ball } from './ball' import { TeamPlayer } from './team-player' +import { IcecreamTruck } from './icecream-truck' const controls = { left: ex.Input.Keys.A, @@ -16,11 +17,7 @@ const controls = { export class Referee extends BasePlayer { directionQueue: DirectionQueue - moveSpeed = 125 - - isPunching = false - isWhistling = false - isKicking = false + moveSpeed = 130 constructor() { super({ @@ -39,29 +36,27 @@ export class Referee extends BasePlayer { this.on('collisionstart', this.onCollisionStart.bind(this)) this.animations.Punch.events.on('loop', () => { - this.isPunching = false - this.isWhistling = false // you can sometimes get stuck if you punch and whistle, quick fix + this.setAnimation('Idle') }) this.animations.Whistle.events.on('loop', () => { - this.isWhistling = false - this.isPunching = false + this.setAnimation('Idle') }) - this.animations.RedCard.events.on('loop', () => { - this.isWhistling = false + this.animations.GiveMoney.events.on('loop', () => { + this.setAnimation('Idle') }) this.animations.Kick.events.on('loop', () => { - this.isKicking = false + this.setAnimation('Idle') }) } onInitialize(_engine: Engine): void { super.onInitialize(_engine) this.pos = ex.vec( - Math.round(this.scene.field.width / 2) + 38, - Math.round(this.scene.field.height / 2) - 24 + Math.round(this.scene.field.width / 2), + Math.round(this.scene.field.height / 2) - 64 ) this.directionQueue = new DirectionQueue(controls) @@ -75,14 +70,35 @@ export class Referee extends BasePlayer { this.directionQueue.update(engine) if (engine.input.keyboard.wasPressed(controls.context)) { - this.punch() + const icecreamTruck = this.scene.entities.find((entity) => { + if (entity instanceof IcecreamTruck) { + return true + } + }) as IcecreamTruck | undefined + + const isInfrontOfIcecreamTruck = + icecreamTruck && + this.pos.distance(icecreamTruck.pos) < 45 && + this.pos.y > icecreamTruck.pos.y + + if (isInfrontOfIcecreamTruck) { + if (icecreamTruck.giveIcecream()) { + this.setAnimation('GiveMoney') + } + } else { + this.punch() + } } if (engine.input.keyboard.wasPressed(controls.whistle)) { this.blowWhistle() } - if (!this.isPunching && !this.isWhistling) { + if ( + !this.isAnimation('Punch') && + !this.isAnimation('Whistle') && + !this.isAnimation('GiveMoney') + ) { const inputs = this.directionQueue.heldDirections const isLeftHeld = inputs.includes('LEFT') @@ -95,7 +111,7 @@ export class Referee extends BasePlayer { isDownHeld ? this.moveSpeed : isUpHeld ? -this.moveSpeed : 0 ) - if (!this.isKicking) { + if (!this.isAnimation('Kick')) { if (this.vel.x !== 0 || this.vel.y !== 0) { this.setAnimation('Run') @@ -116,21 +132,19 @@ export class Referee extends BasePlayer { } kickBall() { - if (!this.isWhistling) { + if (!this.isAnimation('Whistle') && !this.isAnimation('Punch')) { const successful = this.scene.ball.kick( this.scene.ball.pos.sub(this.pos).normalize().scale(500) ) if (successful) { - this.isKicking = true this.setAnimation('Kick') } } } punch() { - if (!this.isWhistling) { - this.isPunching = true + if (!this.isAnimation('Whistle') && !this.isAnimation('Punch')) { this.setAnimation('Punch') this.vel = ex.vec(0, 0) @@ -138,20 +152,19 @@ export class Referee extends BasePlayer { (entity) => entity !== this && entity instanceof BasePlayer && - entity.pos.distance(this.pos) < 20 + entity.pos.distance(this.pos) < 30 ) as BasePlayer[] if (player) { - player.hit(player.pos.sub(this.pos).normalize()) + player.scare(player.pos.sub(this.pos).normalize()) } else { - assets.snd_dashA.play() + assets.snd_dashB.play() } } } blowWhistle(reset = true) { - if (!this.isWhistling) { - this.isWhistling = true + if (!this.isAnimation('Whistle')) { this.setAnimation('Whistle') this.vel = ex.vec(0, 0) assets.snd_whistle.play() diff --git a/src/actors/team-goalie.ts b/src/actors/team-goalie.ts index 0734c06..4b74791 100644 --- a/src/actors/team-goalie.ts +++ b/src/actors/team-goalie.ts @@ -1,7 +1,7 @@ import { assets } from 'src/assets' import { BasePlayer, BasePlayerArgs, Team } from './base-player' -import { Engine, randomInRange } from 'excalibur' +import { Actor, Engine, randomInRange } from 'excalibur' import { Net } from './net' import { Ball } from './ball' @@ -21,7 +21,10 @@ export class TeamGoalie extends BasePlayer { net: Net isKicking = false + isDistracted = false + distractedBy: Actor | undefined + painFrames = 2 power = 300 friction = 0.1 slideDuration = 400 @@ -39,6 +42,7 @@ export class TeamGoalie extends BasePlayer { this.animations.BlockUp = this.sprite.getAnimation('BlockUp')! this.animations.BlockDown = this.sprite.getAnimation('BlockDown')! + this.animations.HeartEyes = this.sprite.getAnimation('HeartEyes')! } onInitialize(_engine: Engine): void { @@ -88,6 +92,24 @@ export class TeamGoalie extends BasePlayer { if (this.isKicking) { return } + + if (this.isDistracted && this.distractedBy) { + const distance = this.pos.distance(this.distractedBy.pos) + + if (distance > 10) { + this.moveTo(this.distractedBy.pos, 100) + } else { + this.vel = ex.vec(0, 0) + } + + if (this.vel.x !== 0 || this.vel.y !== 0) { + this.setAnimation('Run') + } else { + this.setAnimation('HeartEyes') + } + return + } + const { top: netTop, bottom: netBottom } = this.getGoalBounds() const goalieTop = this.pos.y @@ -158,6 +180,7 @@ export class TeamGoalie extends BasePlayer { bottom: this.net.pos.y, } } + kickBall() { const success = this.scene.ball.kick( ex.vec( @@ -202,4 +225,32 @@ export class TeamGoalie extends BasePlayer { this.vel.y = direction === 'up' ? -this.slideSpeed : this.slideSpeed } } + + distract(actor: Actor, duration: number) { + this.isDistracted = true + this.isKicking = false + this.isPain = false + this._slideTime = 0 + this.distractedBy = actor + this.body.collisionType = ex.CollisionType.Passive + + const undistract = () => { + this.isDistracted = false + this.distractedBy = undefined + this.body.collisionType = ex.CollisionType.Active + } + actor.once('kill', (ev: ex.KillEvent) => { + if (actor === ev.other) { + undistract() + } + }) + this.actions + .delay(duration) + .toPromise() + .then(() => { + if (actor === this.distractedBy) { + undistract() + } + }) + } } diff --git a/src/actors/team-player.ts b/src/actors/team-player.ts index b50e299..49ebf05 100644 --- a/src/actors/team-player.ts +++ b/src/actors/team-player.ts @@ -288,8 +288,16 @@ export class TeamPlayer extends BasePlayer { const zone = this.getPositionZone() let pos = this.team === 'home' - ? ex.vec(zone.left + 36 + zone.width / 3, zone.center.y) - : ex.vec(zone.right + 36 - zone.width / 3, zone.center.y) + ? ex.vec(zone.left + zone.width / 3, zone.center.y - 36) + : ex.vec(zone.right - zone.width / 3, zone.center.y - 36) + + if (this.teamPosition === 'defender') { + pos.x += (zone.width / 4) * (this.team === 'home' ? 1 : -1) + } + + if (this.teamPosition === 'forward') { + pos.x -= (zone.width / 4) * (this.team === 'home' ? 1 : -1) + } const teammateOfSamePosition = this.getTeammateOfSamePosition() @@ -414,8 +422,8 @@ export class TeamPlayer extends BasePlayer { } } - hit(direction: Vector): void { - super.hit(direction) + scare(direction: Vector): void { + super.scare(direction) this.isKicking = false } trip() { diff --git a/src/assets.ts b/src/assets.ts index eb0c6fa..d88707a 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -8,6 +8,15 @@ export const assets = { img_netLeftFront: new ex.ImageSource('sprites/net/net_left_front.png'), img_netRightBack: new ex.ImageSource('sprites/net/net_right_back.png'), img_netRightFront: new ex.ImageSource('sprites/net/net_right_back.png'), + img_hudInstructions: new ex.ImageSource( + 'sprites/hud_instructions/hud_instructions.png' + ), + img_blueTeamBadge: new ex.ImageSource( + 'sprites/team_badges/blue_team_badge.png' + ), + img_redTeamBadge: new ex.ImageSource( + 'sprites/team_badges/red_team_badge.png' + ), ase_ballVertical: new AsepriteResource('sprites/ball/ball_vertical.json'), ase_ballHorizontal: new AsepriteResource('sprites/ball/ball_horizontal.json'), @@ -26,6 +35,12 @@ export const assets = { ase_goalieBlue: new AsepriteResource('sprites/players/GOALIE_BLUE.json'), ase_goalieRed: new AsepriteResource('sprites/players/GOALIE_RED.json'), ase_referee: new AsepriteResource('sprites/referee/REFEREE.json'), + ase_scoreNumbers: new AsepriteResource( + 'sprites/score_numbers/score_numbers.json' + ), + ase_icecreamTruck: new AsepriteResource( + 'sprites/ice_cream_truck/ice_cream_truck.json' + ), snd_clack: new ex.Sound('sfx/Clack.mp3'), snd_cleanImpact: new ex.Sound('sfx/CleanImpact.mp3'), @@ -35,6 +50,8 @@ export const assets = { snd_crowdA: new ex.Sound('sfx/CrowdA.mp3'), snd_crowdBHigh: new ex.Sound('sfx/CrowdBHigh.mp3'), snd_crowdBLow: new ex.Sound('sfx/CrowdBLow.mp3'), + snd_bell: new ex.Sound('sfx/Bell1.mp3'), + snd_watchOut: new ex.Sound('sfx/WatchOut.mp3'), } class DevLoader extends ex.Loader { diff --git a/src/classes/MatchScene.ts b/src/classes/MatchScene.ts index 429bd50..a725715 100644 --- a/src/classes/MatchScene.ts +++ b/src/classes/MatchScene.ts @@ -1,12 +1,15 @@ import { assets } from 'src/assets' import { Ball } from 'src/actors/ball' import { TeamPlayer } from 'src/actors/team-player' -import { Actor, Engine } from 'excalibur' +import { Actor, Engine, ScreenElement } from 'excalibur' import { Sprite } from 'src/actors/sprite' import { TeamGoalie } from 'src/actors/team-goalie' import { Net } from 'src/actors/net' import { Referee } from 'src/actors/referee' import { Team } from 'src/actors/base-player' +import { HudInstructions } from '../hud/hud-instructions' +import { Scoreboard } from '../hud/scoreboard' +import { IcecreamTruck } from 'src/actors/icecream-truck' export default class MatchScene extends ex.Scene { ball: Ball @@ -33,6 +36,7 @@ export default class MatchScene extends ex.Scene { engine.add( new Sprite({ + name: 'field_image', x: 0, y: 0, anchor: ex.Vector.Zero, @@ -49,13 +53,7 @@ export default class MatchScene extends ex.Scene { bottom: fieldSprite.height, }) - // the real bounds of the field - this.field = new ex.BoundingBox({ - left: 38, - right: 825, - top: 24, - bottom: 323, - }) + this.field = worldBounds this.zones = { left: new ex.BoundingBox(0, 0, this.field.width / 3, this.field.height), @@ -73,8 +71,8 @@ export default class MatchScene extends ex.Scene { ), } this.ball = new Ball({ - x: Math.round(this.field.width / 2) + 38, - y: Math.round(this.field.height / 2) + 8, + x: Math.round(this.field.width / 2), + y: Math.round(this.field.height / 2) - 36, }) engine.add( @@ -181,12 +179,17 @@ export default class MatchScene extends ex.Scene { this.engine.add(player) }) + // add HUD + engine.add(new HudInstructions()) + engine.add(new Scoreboard()) + // setup camera this.camera.strategy.lockToActor(this.referee) this.camera.strategy.limitCameraBounds( new ex.BoundingBox(0, 0, worldBounds.right, worldBounds.bottom) ) + this.add(new IcecreamTruck()) this.on('goal', this.onGoal.bind(this)) setTimeout(() => { this.start() @@ -217,8 +220,8 @@ export default class MatchScene extends ex.Scene { this.ball.actions .moveTo( ex.vec( - Math.round(this.field.width / 2) + 38 + posession, - Math.round(this.field.height / 2) + 8 + Math.round(this.field.width / 2) + posession, + Math.round(this.field.height / 2) - 36 ), 500 ) @@ -240,8 +243,11 @@ export default class MatchScene extends ex.Scene { // set zindex in order sorted.forEach((actor, i) => { + if (actor.name === 'field_image' || actor instanceof ScreenElement) { + return + } if (actor instanceof Actor) { - actor.z = i + actor.z = i - 100 } }) } diff --git a/src/hud/hud-instructions.ts b/src/hud/hud-instructions.ts new file mode 100644 index 0000000..9d8de2c --- /dev/null +++ b/src/hud/hud-instructions.ts @@ -0,0 +1,12 @@ +import { ScreenElement } from 'excalibur' +import { assets } from '../assets' + +export class HudInstructions extends ScreenElement { + constructor() { + super({ + x: 4, + y: 180, + }) + this.graphics.use(assets.img_hudInstructions.toSprite()) + } +} diff --git a/src/hud/scoreboard.ts b/src/hud/scoreboard.ts new file mode 100644 index 0000000..975cada --- /dev/null +++ b/src/hud/scoreboard.ts @@ -0,0 +1,66 @@ +import { Engine, ScreenElement } from 'excalibur' +import { assets } from '../assets' +import MatchScene from 'src/classes/MatchScene' + +class ScoreboardNumber extends ScreenElement { + score = 0 + team: string + declare scene: MatchScene + + constructor({ x = 0, y = 0, team = 'home' }) { + super({ + x, + y, + }) + this.team = team + this.graphics.use(assets.ase_scoreNumbers.getAnimation(String(this.score))!) + } + + onInitialize() { + this.scene.on('goal', (event: any) => { + if (event.team === this.team) { + if (this.score < 9) { + this.score += 1 + } + this.graphics.use( + assets.ase_scoreNumbers.getAnimation(String(this.score))! + ) + } + }) + } +} + +class ScoreboardTeamBadge extends ScreenElement { + constructor({ x = 0, y = 0, team = 'home' }) { + super({ + x, + y, + }) + this.graphics.use( + team === 'home' + ? assets.img_blueTeamBadge.toSprite() + : assets.img_redTeamBadge.toSprite() + ) + } +} + +export class Scoreboard extends ScreenElement { + constructor() { + super({ + x: 0, + y: 0, + }) + } + + onInitialize(engine: Engine): void { + const homeScoreNumber = new ScoreboardNumber({ x: 0, y: 0, team: 'home' }) + engine.add(homeScoreNumber) + const homeBadge = new ScoreboardTeamBadge({ x: 36, y: 6, team: 'home' }) + engine.add(homeBadge) + + const awayScoreNumber = new ScoreboardNumber({ x: 270, y: 0, team: 'away' }) + engine.add(awayScoreNumber) + const awayBadge = new ScoreboardTeamBadge({ x: 268, y: 6, team: 'away' }) + engine.add(awayBadge) + } +}