You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tobspr_shapez.io/src/js/game/components/static_map_entity.js

300 lines
9.1 KiB

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 { getBuildingDataFromCode } from "../building_codes";
import { Component } from "../component";
export class StaticMapEntityComponent extends Component {
static getId() {
return "StaticMapEntity";
}
static getSchema() {
return {
origin: types.tileVector,
rotation: types.float,
originalRotation: types.float,
// See building_codes.js
code: types.uint,
};
}
/**
* Returns the effective tile size
* @returns {Vector}
*/
getTileSize() {
return getBuildingDataFromCode(this.code).tileSize;
}
/**
* Returns the sprite
* @returns {AtlasSprite}
*/
getSprite() {
return getBuildingDataFromCode(this.code).sprite;
}
/**
* Returns the blueprint sprite
* @returns {AtlasSprite}
*/
getBlueprintSprite() {
return getBuildingDataFromCode(this.code).blueprintSprite;
}
/**
* Returns the silhouette color
* @returns {string}
*/
getSilhouetteColor() {
return getBuildingDataFromCode(this.code).silhouetteColor;
}
/**
* Returns the meta building
* @returns {import("../meta_building").MetaBuilding}
*/
getMetaBuilding() {
return getBuildingDataFromCode(this.code).metaInstance;
}
/**
* Returns the buildings variant
* @returns {string}
*/
getVariant() {
return getBuildingDataFromCode(this.code).variant;
}
/**
* Returns the buildings rotation variant
* @returns {number}
*/
getRotationVariant() {
return getBuildingDataFromCode(this.code).rotationVariant;
}
/**
* Copy the current state to another component
* @param {Component} otherComponent
*/
copyAdditionalStateTo(otherComponent) {
return new StaticMapEntityComponent({
origin: this.origin.copy(),
rotation: this.rotation,
originalRotation: this.originalRotation,
code: this.code,
});
}
/**
*
* @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 {number=} param0.code Building code
*/
constructor({
origin = new Vector(),
tileSize = new Vector(1, 1),
rotation = 0,
originalRotation = 0,
code = 0,
}) {
super();
assert(
rotation % 90 === 0,
"Rotation of static map entity must be multiple of 90 (was " + rotation + ")"
);
this.origin = origin;
this.rotation = rotation;
this.code = code;
this.originalRotation = originalRotation;
}
/**
* Returns the effective rectangle of this entity in tile space
* @returns {Rectangle}
*/
getTileSpaceBounds() {
const size = this.getTileSize();
switch (this.rotation) {
case 0:
return new Rectangle(this.origin.x, this.origin.y, size.x, size.y);
case 90:
return new Rectangle(this.origin.x - size.y + 1, this.origin.y, size.y, size.x);
case 180:
return new Rectangle(this.origin.x - size.x + 1, this.origin.y - size.y + 1, size.x, size.y);
case 270:
return new Rectangle(this.origin.x, this.origin.y - size.x + 1, size.y, size.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 = localTile.rotateFastMultipleOf90(this.rotation);
result.x += this.origin.x;
result.y += this.origin.y;
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;
const size = this.getTileSize();
switch (this.rotation) {
case 0: {
x = this.origin.x;
y = this.origin.y;
w = size.x;
h = size.y;
break;
}
case 90: {
x = this.origin.x - size.y + 1;
y = this.origin.y;
w = size.y;
h = size.x;
break;
}
case 180: {
x = this.origin.x - size.x + 1;
y = this.origin.y - size.y + 1;
w = size.x;
h = size.y;
break;
}
case 270: {
x = this.origin.x;
y = this.origin.y - size.x + 1;
w = size.y;
h = size.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 {Vector=} overridePosition Whether to drwa the entity at a different location
*/
drawSpriteOnBoundsClipped(parameters, sprite, extrudePixels = 0, overridePosition = null) {
if (!this.shouldBeDrawn(parameters) && !overridePosition) {
return;
}
const size = this.getTileSize();
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 * size.x,
worldY - extrudePixels * size.y,
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y
);
} 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 * size.x,
-globalConfig.halfTileSize - extrudePixels * size.y,
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
false // no clipping possible here
);
parameters.context.rotate(-Math.radians(this.rotation));
parameters.context.translate(-rotationCenterX, -rotationCenterY);
}
}
}