blob: a8d590e4d8e5ef4c2c2099b53ab1a7f4f71ed393 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chrome://resources/js/assert.js';
import {IS_HIDPI} from './constants.js';
import type {ImageSpriteProvider} from './image_sprite_provider.js';
import type {SpritePosition} from './sprite_position.js';
import {getRandomNum} from './utils.js';
export interface BackgroundElSpriteConfig {
height: number;
width: number;
offset: number;
xPos: number;
fixed: boolean;
// Only present when fixed is true.
fixedXPos?: number;
// Only present when fixed is true.
fixedYPos1?: number;
// Only present when fixed is true.
fixedYPos2?: number;
}
export interface BackgroundElConfig {
maxBgEls: number;
maxGap: number;
minGap: number;
pos: number;
speed: number;
yPos: number;
msPerFrame?: number; // only needed when spriteConfig.fixed is true
}
/**
* Background element object config.
* Real values assigned when game type changes.
*/
let globalConfig: BackgroundElConfig = {
maxBgEls: 0,
maxGap: 0,
minGap: 0,
msPerFrame: 0,
pos: 0,
speed: 0,
yPos: 0,
};
export function getGlobalConfig(): BackgroundElConfig {
return globalConfig;
}
export function setGlobalConfig(config: BackgroundElConfig) {
globalConfig = config;
}
export class BackgroundEl {
gap: number;
xPos: number;
remove: boolean = false;
private canvas: HTMLCanvasElement;
private canvasCtx: CanvasRenderingContext2D;
private spritePos: SpritePosition;
private yPos: number = 0;
private type: string;
private animTimer: number = 0;
private spriteConfig: BackgroundElSpriteConfig;
private switchFrames: boolean = false;
private imageSpriteProvider: ImageSpriteProvider;
/**
* Background item.
* Similar to cloud, without random y position.
*/
constructor(
canvas: HTMLCanvasElement, spritePos: SpritePosition,
containerWidth: number, type: string,
imageSpriteProvider: ImageSpriteProvider) {
this.canvas = canvas;
const canvasContext = this.canvas.getContext('2d');
assert(canvasContext);
this.canvasCtx = canvasContext;
this.spritePos = spritePos;
this.imageSpriteProvider = imageSpriteProvider;
this.xPos = containerWidth;
this.type = type;
this.gap = getRandomNum(getGlobalConfig().minGap, getGlobalConfig().maxGap);
const spriteConfig =
imageSpriteProvider.getSpriteDefinition().backgroundEl[this.type];
assert(spriteConfig);
this.spriteConfig = spriteConfig;
this.init();
}
/**
* Initialise the element setting the y position.
*/
init() {
if (this.spriteConfig.fixed) {
assert(this.spriteConfig.fixedXPos);
this.xPos = this.spriteConfig.fixedXPos;
}
this.yPos = getGlobalConfig().yPos - this.spriteConfig.height +
this.spriteConfig.offset;
this.draw();
}
/**
* Draw the element.
*/
draw() {
this.canvasCtx.save();
let sourceWidth = this.spriteConfig.width;
let sourceHeight = this.spriteConfig.height;
let sourceX = this.spriteConfig.xPos;
const outputWidth = sourceWidth;
const outputHeight = sourceHeight;
const imageSprite = this.imageSpriteProvider.getRunnerImageSprite();
assert(imageSprite);
if (IS_HIDPI) {
sourceWidth *= 2;
sourceHeight *= 2;
sourceX *= 2;
}
this.canvasCtx.drawImage(
imageSprite, sourceX, this.spritePos.y, sourceWidth, sourceHeight,
this.xPos, this.yPos, outputWidth, outputHeight);
this.canvasCtx.restore();
}
/**
* Update the background element position.
*/
update(speed: number) {
if (!this.remove) {
if (this.spriteConfig.fixed) {
const globalConfig = getGlobalConfig();
assert(globalConfig.msPerFrame);
this.animTimer += speed;
if (this.animTimer > globalConfig.msPerFrame) {
this.animTimer = 0;
this.switchFrames = !this.switchFrames;
}
if (this.spriteConfig.fixedYPos1 && this.spriteConfig.fixedYPos2) {
this.yPos = this.switchFrames ? this.spriteConfig.fixedYPos1 :
this.spriteConfig.fixedYPos2;
}
} else {
// Fixed speed, regardless of actual game speed.
this.xPos -= getGlobalConfig().speed;
}
this.draw();
// Mark as removable if no longer in the canvas.
if (!this.isVisible()) {
this.remove = true;
}
}
}
/**
* Check if the element is visible on the stage.
*/
isVisible(): boolean {
return this.xPos + this.spriteConfig.width > 0;
}
}