import { Math_radians } from "../../core/builtins"; import { globalConfig } from "../../core/config"; import { DrawParameters } from "../../core/draw_parameters"; import { Rectangle } from "../../core/rectangle"; import { AtlasSprite } from "../../core/sprites"; import { enumDirection, Vector } from "../../core/vector"; import { types } from "../../savegame/serialization"; import { Component } from "../component"; export class StaticMapEntityComponent extends Component { static getId() { return "StaticMapEntity"; } static getSchema() { return { origin: types.tileVector, tileSize: types.tileVector, rotation: types.float, originalRotation: types.float, spriteKey: types.nullable(types.string), blueprintSpriteKey: types.string, silhouetteColor: types.nullable(types.string), }; } duplicateWithoutContents() { return new StaticMapEntityComponent({ origin: this.origin.copy(), tileSize: this.tileSize.copy(), rotation: this.rotation, originalRotation: this.originalRotation, spriteKey: this.spriteKey, silhouetteColor: this.silhouetteColor, blueprintSpriteKey: this.blueprintSpriteKey, }); } /** * * @param {object} param0 * @param {Vector=} param0.origin Origin (Top Left corner) of the entity * @param {Vector=} param0.tileSize Size of the entity in tiles * @param {number=} param0.rotation Rotation in degrees. Must be multiple of 90 * @param {number=} param0.originalRotation Original Rotation in degrees. Must be multiple of 90 * @param {string=} param0.spriteKey Optional sprite * @param {string} param0.blueprintSpriteKey Blueprint sprite, required * @param {string=} param0.silhouetteColor Optional silhouette color override */ constructor({ origin = new Vector(), tileSize = new Vector(1, 1), rotation = 0, originalRotation = 0, spriteKey = null, silhouetteColor = null, blueprintSpriteKey = null, }) { super(); assert( rotation % 90 === 0, "Rotation of static map entity must be multiple of 90 (was " + rotation + ")" ); this.origin = origin; this.tileSize = tileSize; this.spriteKey = spriteKey; this.rotation = rotation; this.originalRotation = originalRotation; this.silhouetteColor = silhouetteColor; this.blueprintSpriteKey = blueprintSpriteKey; } /** * Returns the effective rectangle of this entity in tile space * @returns {Rectangle} */ getTileSpaceBounds() { switch (this.rotation) { case 0: return new Rectangle(this.origin.x, this.origin.y, this.tileSize.x, this.tileSize.y); case 90: return new Rectangle( this.origin.x - this.tileSize.y + 1, this.origin.y, this.tileSize.y, this.tileSize.x ); case 180: return new Rectangle( this.origin.x - this.tileSize.x + 1, this.origin.y - this.tileSize.y + 1, this.tileSize.x, this.tileSize.y ); case 270: return new Rectangle( this.origin.x, this.origin.y - this.tileSize.x + 1, this.tileSize.y, this.tileSize.x ); default: assert(false, "Invalid rotation"); } } /** * Transforms the given vector/rotation from local space to world space * @param {Vector} vector * @returns {Vector} */ applyRotationToVector(vector) { return vector.rotateFastMultipleOf90(this.rotation); } /** * Transforms the given vector/rotation from world space to local space * @param {Vector} vector * @returns {Vector} */ unapplyRotationToVector(vector) { return vector.rotateFastMultipleOf90(360 - this.rotation); } /** * Transforms the given direction from local space * @param {enumDirection} direction * @returns {enumDirection} */ localDirectionToWorld(direction) { return Vector.transformDirectionFromMultipleOf90(direction, this.rotation); } /** * Transforms the given direction from world to local space * @param {enumDirection} direction * @returns {enumDirection} */ worldDirectionToLocal(direction) { return Vector.transformDirectionFromMultipleOf90(direction, 360 - this.rotation); } /** * Transforms from local tile space to global tile space * @param {Vector} localTile * @returns {Vector} */ localTileToWorld(localTile) { const result = this.applyRotationToVector(localTile); result.addInplace(this.origin); return result; } /** * Transforms from world space to local space * @param {Vector} worldTile */ worldToLocalTile(worldTile) { const localUnrotated = worldTile.sub(this.origin); return this.unapplyRotationToVector(localUnrotated); } /** * Returns whether the entity should be drawn for the given parameters * @param {DrawParameters} parameters */ shouldBeDrawn(parameters) { let x = 0; let y = 0; let w = 0; let h = 0; switch (this.rotation) { case 0: { x = this.origin.x; y = this.origin.y; w = this.tileSize.x; h = this.tileSize.y; break; } case 90: { x = this.origin.x - this.tileSize.y + 1; y = this.origin.y; w = this.tileSize.y; h = this.tileSize.x; break; } case 180: { x = this.origin.x - this.tileSize.x + 1; y = this.origin.y - this.tileSize.y + 1; w = this.tileSize.x; h = this.tileSize.y; break; } case 270: { x = this.origin.x; y = this.origin.y - this.tileSize.x + 1; w = this.tileSize.y; h = this.tileSize.x; break; } default: assert(false, "Invalid rotation"); } return parameters.visibleRect.containsRect4Params( x * globalConfig.tileSize, y * globalConfig.tileSize, w * globalConfig.tileSize, h * globalConfig.tileSize ); } /** * Draws a sprite over the whole space of the entity * @param {DrawParameters} parameters * @param {AtlasSprite} sprite * @param {number=} extrudePixels How many pixels to extrude the sprite * @param {boolean=} clipping Whether to clip * @param {Vector=} overridePosition Whether to drwa the entity at a different location */ drawSpriteOnFullEntityBounds( parameters, sprite, extrudePixels = 0, clipping = true, overridePosition = null ) { if (!this.shouldBeDrawn(parameters) && !overridePosition) { return; } let worldX = this.origin.x * globalConfig.tileSize; let worldY = this.origin.y * globalConfig.tileSize; if (overridePosition) { worldX = overridePosition.x * globalConfig.tileSize; worldY = overridePosition.y * globalConfig.tileSize; } if (this.rotation === 0) { // Early out, is faster sprite.drawCached( parameters, worldX - extrudePixels * this.tileSize.x, worldY - extrudePixels * this.tileSize.y, globalConfig.tileSize * this.tileSize.x + 2 * extrudePixels * this.tileSize.x, globalConfig.tileSize * this.tileSize.y + 2 * extrudePixels * this.tileSize.y, false ); } else { const rotationCenterX = worldX + globalConfig.halfTileSize; const rotationCenterY = worldY + globalConfig.halfTileSize; parameters.context.translate(rotationCenterX, rotationCenterY); parameters.context.rotate(Math_radians(this.rotation)); sprite.drawCached( parameters, -globalConfig.halfTileSize - extrudePixels * this.tileSize.x, -globalConfig.halfTileSize - extrudePixels * this.tileSize.y, globalConfig.tileSize * this.tileSize.x + 2 * extrudePixels * this.tileSize.x, globalConfig.tileSize * this.tileSize.y + 2 * extrudePixels * this.tileSize.y, false ); parameters.context.rotate(-Math_radians(this.rotation)); parameters.context.translate(-rotationCenterX, -rotationCenterY); } } }