mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-21 17:45:25 -04:00
deb802cfec
* For now, this is limited to instances inside custom objects. This will be made available in the future for all instances in scenes if this works well.
987 lines
26 KiB
TypeScript
987 lines
26 KiB
TypeScript
/*
|
|
* GDevelop JS Platform
|
|
* 2013 Florian Rival (Florian.Rival@gmail.com)
|
|
*/
|
|
namespace gdjs {
|
|
/**
|
|
* Represents a color in RGB Format
|
|
* @category Objects > Shape Painter
|
|
*/
|
|
export type RGBColor = {
|
|
/** The Red component of the color, from 0 to 255. */
|
|
r: integer;
|
|
/** The Green component of the color, from 0 to 255. */
|
|
g: integer;
|
|
/** The Blue component of the color, from 0 to 255. */
|
|
b: integer;
|
|
};
|
|
|
|
/**
|
|
* @category Objects > Shape Painter
|
|
*/
|
|
export type Antialiasing = 'none' | 'low' | 'medium' | 'high';
|
|
|
|
/**
|
|
* Initial properties for a for {@link gdjs.ShapePainterRuntimeObject}.
|
|
* @category Objects > Shape Painter
|
|
*/
|
|
export type ShapePainterObjectDataType = {
|
|
/** The color of the inner part of the painted shape */
|
|
fillColor: RGBColor | string;
|
|
/** The color of the outline of the painted shape */
|
|
outlineColor: RGBColor | string;
|
|
/** The opacity of the inner part of the painted shape, from 0 to 255 */
|
|
fillOpacity: float;
|
|
/** The opacity of the outline of the painted shape, from 0 to 255 */
|
|
outlineOpacity: float;
|
|
/** The size of the outline of the painted shape, in pixels. */
|
|
outlineSize: float;
|
|
/** Use absolute coordinates? */
|
|
absoluteCoordinates: boolean;
|
|
/** Clear the previous render before the next draw? */
|
|
clearBetweenFrames: boolean;
|
|
/** The type of anti-aliasing to apply at rendering. */
|
|
antialiasing: Antialiasing;
|
|
};
|
|
|
|
/** @category Objects > Shape Painter */
|
|
export type ShapePainterObjectData = ObjectData & ShapePainterObjectDataType;
|
|
|
|
type ShapePainterNetworkSyncDataType = {
|
|
cbf: boolean; // clearBetweenFrames
|
|
aa: Antialiasing; // antialiasing
|
|
ac: boolean; // absoluteCoordinates
|
|
fc: integer; // fillColor
|
|
oc: integer; // outlineColor
|
|
os: float; // outlineSize
|
|
fo: float; // fillOpacity
|
|
oo: float; // outlineOpacity
|
|
scaleX: number;
|
|
scaleY: number;
|
|
ifx: boolean; // isFlippedX
|
|
ify: boolean; // isFlippedY
|
|
};
|
|
|
|
/** @category Objects > Shape Painter */
|
|
export type ShapePainterNetworkSyncData = ObjectNetworkSyncData &
|
|
ShapePainterNetworkSyncDataType;
|
|
|
|
/**
|
|
* The ShapePainterRuntimeObject allows to draw graphics shapes on screen.
|
|
* @category Objects > Shape Painter
|
|
*/
|
|
export class ShapePainterRuntimeObject
|
|
extends gdjs.RuntimeObject
|
|
implements gdjs.Resizable, gdjs.Scalable, gdjs.Flippable
|
|
{
|
|
_scaleX: number = 1;
|
|
_scaleY: number = 1;
|
|
_blendMode: number = 0;
|
|
_flippedX: boolean = false;
|
|
_flippedY: boolean = false;
|
|
_customCenter: FloatPoint | null = null;
|
|
_customCollisionMask: Polygon[] | null = null;
|
|
|
|
_fillColor: integer;
|
|
_outlineColor: integer;
|
|
_fillOpacity: float;
|
|
_outlineOpacity: float;
|
|
_outlineSize: float;
|
|
_useAbsoluteCoordinates: boolean;
|
|
_clearBetweenFrames: boolean;
|
|
_antialiasing: Antialiasing;
|
|
_renderer: gdjs.ShapePainterRuntimeObjectRenderer;
|
|
|
|
private static readonly _pointForTransformation: FloatPoint = [0, 0];
|
|
|
|
/**
|
|
* @param instanceContainer The container the object belongs to.
|
|
* @param shapePainterObjectData The initial properties of the object
|
|
*/
|
|
constructor(
|
|
instanceContainer: gdjs.RuntimeInstanceContainer,
|
|
shapePainterObjectData: ShapePainterObjectData,
|
|
instanceData?: InstanceData
|
|
) {
|
|
super(instanceContainer, shapePainterObjectData, instanceData);
|
|
this._fillColor =
|
|
typeof shapePainterObjectData.fillColor === 'string'
|
|
? gdjs.rgbOrHexStringToNumber(shapePainterObjectData.fillColor)
|
|
: parseInt(
|
|
gdjs.rgbToHex(
|
|
shapePainterObjectData.fillColor.r,
|
|
shapePainterObjectData.fillColor.g,
|
|
shapePainterObjectData.fillColor.b
|
|
),
|
|
16
|
|
);
|
|
this._outlineColor =
|
|
typeof shapePainterObjectData.outlineColor === 'string'
|
|
? gdjs.rgbOrHexStringToNumber(shapePainterObjectData.outlineColor)
|
|
: parseInt(
|
|
gdjs.rgbToHex(
|
|
shapePainterObjectData.outlineColor.r,
|
|
shapePainterObjectData.outlineColor.g,
|
|
shapePainterObjectData.outlineColor.b
|
|
),
|
|
16
|
|
);
|
|
this._fillOpacity = shapePainterObjectData.fillOpacity;
|
|
this._outlineOpacity = shapePainterObjectData.outlineOpacity;
|
|
this._outlineSize = shapePainterObjectData.outlineSize;
|
|
this._useAbsoluteCoordinates = shapePainterObjectData.absoluteCoordinates;
|
|
this._clearBetweenFrames = shapePainterObjectData.clearBetweenFrames;
|
|
this._antialiasing = shapePainterObjectData.antialiasing;
|
|
this._renderer = new gdjs.ShapePainterRuntimeObjectRenderer(
|
|
this,
|
|
instanceContainer
|
|
);
|
|
|
|
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
|
this.onCreated();
|
|
}
|
|
|
|
getRendererObject() {
|
|
return this._renderer.getRendererObject();
|
|
}
|
|
|
|
updateFromObjectData(
|
|
oldObjectData: ShapePainterObjectData,
|
|
newObjectData: ShapePainterObjectData
|
|
): boolean {
|
|
if (typeof newObjectData.fillColor === 'string') {
|
|
if (oldObjectData.fillColor !== newObjectData.fillColor) {
|
|
this.setFillColor(newObjectData.fillColor);
|
|
}
|
|
}
|
|
if (
|
|
typeof oldObjectData.fillColor !== 'string' &&
|
|
typeof newObjectData.fillColor !== 'string' &&
|
|
(oldObjectData.fillColor.r !== newObjectData.fillColor.r ||
|
|
oldObjectData.fillColor.g !== newObjectData.fillColor.g ||
|
|
oldObjectData.fillColor.b !== newObjectData.fillColor.b)
|
|
) {
|
|
this.setFillColor(
|
|
'' +
|
|
newObjectData.fillColor.r +
|
|
';' +
|
|
newObjectData.fillColor.g +
|
|
';' +
|
|
newObjectData.fillColor.b
|
|
);
|
|
}
|
|
if (typeof newObjectData.outlineColor === 'string') {
|
|
if (oldObjectData.outlineColor !== newObjectData.outlineColor) {
|
|
this.setOutlineColor(newObjectData.outlineColor);
|
|
}
|
|
}
|
|
if (
|
|
typeof oldObjectData.outlineColor !== 'string' &&
|
|
typeof newObjectData.outlineColor !== 'string' &&
|
|
(oldObjectData.outlineColor.r !== newObjectData.outlineColor.r ||
|
|
oldObjectData.outlineColor.g !== newObjectData.outlineColor.g ||
|
|
oldObjectData.outlineColor.b !== newObjectData.outlineColor.b)
|
|
) {
|
|
this.setOutlineColor(
|
|
'' +
|
|
newObjectData.outlineColor.r +
|
|
';' +
|
|
newObjectData.outlineColor.g +
|
|
';' +
|
|
newObjectData.outlineColor.b
|
|
);
|
|
}
|
|
if (oldObjectData.fillOpacity !== newObjectData.fillOpacity) {
|
|
this.setFillOpacity(newObjectData.fillOpacity);
|
|
}
|
|
if (oldObjectData.outlineOpacity !== newObjectData.outlineOpacity) {
|
|
this.setOutlineOpacity(newObjectData.outlineOpacity);
|
|
}
|
|
if (oldObjectData.outlineSize !== newObjectData.outlineSize) {
|
|
this.setOutlineSize(newObjectData.outlineSize);
|
|
}
|
|
if (
|
|
oldObjectData.absoluteCoordinates !== newObjectData.absoluteCoordinates
|
|
) {
|
|
this._useAbsoluteCoordinates = newObjectData.absoluteCoordinates;
|
|
this._renderer.updatePositionX();
|
|
this._renderer.updatePositionY();
|
|
this._renderer.updateAngle();
|
|
this._renderer.updateScaleX();
|
|
this._renderer.updateScaleY();
|
|
}
|
|
if (
|
|
oldObjectData.clearBetweenFrames !== newObjectData.clearBetweenFrames
|
|
) {
|
|
this._clearBetweenFrames = newObjectData.clearBetweenFrames;
|
|
}
|
|
if (oldObjectData.antialiasing !== newObjectData.antialiasing) {
|
|
this.setAntialiasing(newObjectData.antialiasing);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
getNetworkSyncData(
|
|
syncOptions: GetNetworkSyncDataOptions
|
|
): ShapePainterNetworkSyncData {
|
|
return {
|
|
...super.getNetworkSyncData(syncOptions),
|
|
cbf: this._clearBetweenFrames,
|
|
aa: this._antialiasing,
|
|
ac: this._useAbsoluteCoordinates,
|
|
fc: this._fillColor,
|
|
oc: this._outlineColor,
|
|
os: this._outlineSize,
|
|
fo: this._fillOpacity,
|
|
oo: this._outlineOpacity,
|
|
scaleX: this.getScaleX(),
|
|
scaleY: this.getScaleY(),
|
|
ifx: this._flippedX,
|
|
ify: this._flippedY,
|
|
};
|
|
}
|
|
|
|
updateFromNetworkSyncData(
|
|
networkSyncData: ShapePainterNetworkSyncData,
|
|
options: UpdateFromNetworkSyncDataOptions
|
|
): void {
|
|
super.updateFromNetworkSyncData(networkSyncData, options);
|
|
|
|
if (networkSyncData.cbf !== undefined) {
|
|
this._clearBetweenFrames = networkSyncData.cbf;
|
|
}
|
|
if (networkSyncData.aa !== undefined) {
|
|
this.setAntialiasing(networkSyncData.aa);
|
|
}
|
|
if (networkSyncData.ac !== undefined) {
|
|
this.setCoordinatesRelative(!networkSyncData.ac);
|
|
}
|
|
if (networkSyncData.fc !== undefined) {
|
|
this._fillColor = networkSyncData.fc;
|
|
}
|
|
if (networkSyncData.oc !== undefined) {
|
|
this._outlineColor = networkSyncData.oc;
|
|
}
|
|
if (networkSyncData.os !== undefined) {
|
|
this.setOutlineSize(networkSyncData.os);
|
|
}
|
|
if (networkSyncData.fo !== undefined) {
|
|
this.setFillOpacity(networkSyncData.fo);
|
|
}
|
|
if (networkSyncData.oo !== undefined) {
|
|
this.setOutlineOpacity(networkSyncData.oo);
|
|
}
|
|
if (networkSyncData.scaleX !== undefined) {
|
|
this.setScaleX(networkSyncData.scaleX);
|
|
}
|
|
if (networkSyncData.scaleY !== undefined) {
|
|
this.setScaleY(networkSyncData.scaleY);
|
|
}
|
|
if (networkSyncData.ifx !== undefined) {
|
|
this.flipX(networkSyncData.ifx);
|
|
}
|
|
if (networkSyncData.ify !== undefined) {
|
|
this.flipY(networkSyncData.ify);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the extra parameters that could be set for an instance.
|
|
* @param initialInstanceData The extra parameters
|
|
*/
|
|
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
|
|
this.flipX(!!initialInstanceData.flippedX);
|
|
this.flipY(!!initialInstanceData.flippedY);
|
|
}
|
|
|
|
stepBehaviorsPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
|
//We redefine stepBehaviorsPreEvents just to clear the graphics before running events.
|
|
if (this._clearBetweenFrames) {
|
|
this.clear();
|
|
}
|
|
super.stepBehaviorsPreEvents(instanceContainer);
|
|
}
|
|
|
|
onDestroyed(): void {
|
|
super.onDestroyed();
|
|
this._renderer.destroy();
|
|
}
|
|
|
|
/**
|
|
* Clear the graphics.
|
|
*/
|
|
clear() {
|
|
this._renderer.clear();
|
|
}
|
|
|
|
getVisibilityAABB() {
|
|
return this._useAbsoluteCoordinates ? null : this.getAABB();
|
|
}
|
|
|
|
drawRectangle(x1: float, y1: float, x2: float, y2: float) {
|
|
this._renderer.drawRectangle(x1, y1, x2, y2);
|
|
}
|
|
|
|
drawCircle(x: float, y: float, radius: float) {
|
|
this._renderer.drawCircle(x, y, radius);
|
|
}
|
|
|
|
drawLine(x1: float, y1: float, x2: float, y2: float, thickness: float) {
|
|
this._renderer.drawLine(x1, y1, x2, y2, thickness);
|
|
}
|
|
|
|
drawLineV2(x1: float, y1: float, x2: float, y2: float, thickness: float) {
|
|
this._renderer.drawLineV2(x1, y1, x2, y2, thickness);
|
|
}
|
|
|
|
drawEllipse(centerX: float, centerY: float, width: float, height: float) {
|
|
this._renderer.drawEllipse(centerX, centerY, width, height);
|
|
}
|
|
|
|
drawFilletRectangle(
|
|
startX1: float,
|
|
startY1: float,
|
|
endX2: float,
|
|
endY2: float,
|
|
fillet: float
|
|
) {
|
|
this._renderer.drawFilletRectangle(
|
|
startX1,
|
|
startY1,
|
|
endX2,
|
|
endY2,
|
|
fillet
|
|
);
|
|
}
|
|
|
|
drawRoundedRectangle(
|
|
startX1: float,
|
|
startY1: float,
|
|
endX2: float,
|
|
endY2: float,
|
|
radius: float
|
|
) {
|
|
this._renderer.drawRoundedRectangle(
|
|
startX1,
|
|
startY1,
|
|
endX2,
|
|
endY2,
|
|
radius
|
|
);
|
|
}
|
|
|
|
drawChamferRectangle(
|
|
startX1: float,
|
|
startY1: float,
|
|
endX2: float,
|
|
endY2: float,
|
|
chamfer: float
|
|
) {
|
|
this._renderer.drawChamferRectangle(
|
|
startX1,
|
|
startY1,
|
|
endX2,
|
|
endY2,
|
|
chamfer
|
|
);
|
|
}
|
|
|
|
drawTorus(
|
|
centerX: float,
|
|
centerY: float,
|
|
innerRadius: float,
|
|
outerRadius: float,
|
|
startArc: float,
|
|
endArc: float
|
|
) {
|
|
this._renderer.drawTorus(
|
|
centerX,
|
|
centerY,
|
|
innerRadius,
|
|
outerRadius,
|
|
startArc,
|
|
endArc
|
|
);
|
|
}
|
|
|
|
drawRegularPolygon(
|
|
centerX: float,
|
|
centerY: float,
|
|
sides: float,
|
|
radius: float,
|
|
rotation: float
|
|
) {
|
|
this._renderer.drawRegularPolygon(
|
|
centerX,
|
|
centerY,
|
|
sides,
|
|
radius,
|
|
rotation
|
|
);
|
|
}
|
|
|
|
drawStar(
|
|
centerX: float,
|
|
centerY: float,
|
|
points: float,
|
|
radius: float,
|
|
innerRadius: float,
|
|
rotation: float
|
|
) {
|
|
this._renderer.drawStar(
|
|
centerX,
|
|
centerY,
|
|
points,
|
|
radius,
|
|
innerRadius,
|
|
rotation
|
|
);
|
|
}
|
|
|
|
drawArc(
|
|
centerX: float,
|
|
centerY: float,
|
|
radius: float,
|
|
startAngle: float,
|
|
endAngle: float,
|
|
anticlockwise: boolean,
|
|
closePath: boolean
|
|
) {
|
|
this._renderer.drawArc(
|
|
centerX,
|
|
centerY,
|
|
radius,
|
|
startAngle,
|
|
endAngle,
|
|
anticlockwise,
|
|
closePath
|
|
);
|
|
}
|
|
|
|
drawBezierCurve(
|
|
x1: float,
|
|
y1: float,
|
|
cpX: float,
|
|
cpY: float,
|
|
cpX2: float,
|
|
cpY2: float,
|
|
x2: float,
|
|
y2: float
|
|
) {
|
|
this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);
|
|
}
|
|
|
|
drawQuadraticCurve(
|
|
x1: float,
|
|
y1: float,
|
|
cpX: float,
|
|
cpY: float,
|
|
x2: float,
|
|
y2: float
|
|
) {
|
|
this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);
|
|
}
|
|
|
|
beginFillPath(x1: float, y1: float) {
|
|
this._renderer.beginFillPath();
|
|
this._renderer.drawPathMoveTo(x1, y1);
|
|
}
|
|
|
|
endFillPath() {
|
|
this._renderer.endFillPath();
|
|
}
|
|
|
|
drawPathMoveTo(x1: float, y1: float) {
|
|
this._renderer.drawPathMoveTo(x1, y1);
|
|
}
|
|
|
|
drawPathLineTo(x1: float, y1: float) {
|
|
this._renderer.drawPathLineTo(x1, y1);
|
|
}
|
|
|
|
drawPathBezierCurveTo(
|
|
cpX: float,
|
|
cpY: float,
|
|
cpX2: float,
|
|
cpY2: float,
|
|
toX: float,
|
|
toY: float
|
|
) {
|
|
this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);
|
|
}
|
|
|
|
drawPathArc(
|
|
cx: float,
|
|
cy: float,
|
|
radius: float,
|
|
startAngle: float,
|
|
endAngle: float,
|
|
anticlockwise: boolean
|
|
) {
|
|
this._renderer.drawPathArc(
|
|
cx,
|
|
cy,
|
|
radius,
|
|
startAngle,
|
|
endAngle,
|
|
anticlockwise
|
|
);
|
|
}
|
|
|
|
drawPathQuadraticCurveTo(cpX: float, cpY: float, toX: float, toY: float) {
|
|
this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);
|
|
}
|
|
|
|
closePath() {
|
|
this._renderer.closePath();
|
|
}
|
|
|
|
setClearBetweenFrames(value: boolean): void {
|
|
this._clearBetweenFrames = value;
|
|
}
|
|
|
|
isClearedBetweenFrames(): boolean {
|
|
return this._clearBetweenFrames;
|
|
}
|
|
|
|
setAntialiasing(value: Antialiasing): void {
|
|
this._antialiasing = value;
|
|
this._renderer.updateAntialiasing();
|
|
}
|
|
|
|
getAntialiasing(): Antialiasing {
|
|
return this._antialiasing;
|
|
}
|
|
|
|
checkAntialiasing(valueToCompare: Antialiasing): boolean {
|
|
return this._antialiasing === valueToCompare;
|
|
}
|
|
|
|
setCoordinatesRelative(value: boolean): void {
|
|
this._useAbsoluteCoordinates = !value;
|
|
}
|
|
|
|
areCoordinatesRelative(): boolean {
|
|
return !this._useAbsoluteCoordinates;
|
|
}
|
|
|
|
setFillColor(color: string): void {
|
|
this._fillColor = gdjs.rgbOrHexStringToNumber(color);
|
|
}
|
|
|
|
getFillColorR(): integer {
|
|
return gdjs.hexNumberToRGB(this._fillColor).r;
|
|
}
|
|
getFillColorG(): integer {
|
|
return gdjs.hexNumberToRGB(this._fillColor).g;
|
|
}
|
|
getFillColorB(): integer {
|
|
return gdjs.hexNumberToRGB(this._fillColor).b;
|
|
}
|
|
|
|
setOutlineColor(rgbColor: string): void {
|
|
this._outlineColor = gdjs.rgbOrHexStringToNumber(rgbColor);
|
|
this._renderer.updateOutline();
|
|
}
|
|
|
|
getOutlineColorR(): integer {
|
|
return gdjs.hexNumberToRGB(this._outlineColor).r;
|
|
}
|
|
getOutlineColorG(): integer {
|
|
return gdjs.hexNumberToRGB(this._outlineColor).g;
|
|
}
|
|
getOutlineColorB(): integer {
|
|
return gdjs.hexNumberToRGB(this._outlineColor).b;
|
|
}
|
|
|
|
setOutlineSize(size: float): void {
|
|
this._outlineSize = size;
|
|
this._renderer.updateOutline();
|
|
}
|
|
|
|
getOutlineSize() {
|
|
return this._outlineSize;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param opacity from 0 to 255
|
|
*/
|
|
setFillOpacity(opacity: float): void {
|
|
this._fillOpacity = opacity;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns an opacity value from 0 to 255.
|
|
*/
|
|
getFillOpacity() {
|
|
return this._fillOpacity;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param opacity from 0 to 255
|
|
*/
|
|
setOutlineOpacity(opacity: float): void {
|
|
this._outlineOpacity = opacity;
|
|
this._renderer.updateOutline();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns an opacity value from 0 to 255.
|
|
*/
|
|
getOutlineOpacity() {
|
|
return this._outlineOpacity;
|
|
}
|
|
|
|
setX(x: float): void {
|
|
if (x === this.x) {
|
|
return;
|
|
}
|
|
super.setX(x);
|
|
this._renderer.updatePositionX();
|
|
}
|
|
|
|
setY(y: float): void {
|
|
if (y === this.y) {
|
|
return;
|
|
}
|
|
super.setY(y);
|
|
this._renderer.updatePositionY();
|
|
}
|
|
|
|
setAngle(angle: float): void {
|
|
if (angle === this.angle) {
|
|
return;
|
|
}
|
|
super.setAngle(angle);
|
|
this._renderer.updateAngle();
|
|
this.invalidateHitboxes();
|
|
}
|
|
|
|
/**
|
|
* The center of rotation is defined relatively
|
|
* to the drawing origin (the object position).
|
|
* This avoids the center to move on the drawing
|
|
* when new shapes push the bounds.
|
|
*
|
|
* When no custom center is defined, it will move
|
|
* to stay at the center of the drawable bounds.
|
|
*
|
|
* @param x coordinate of the custom center
|
|
* @param y coordinate of the custom center
|
|
*/
|
|
setRotationCenter(x: float, y: float): void {
|
|
if (!this._customCenter) {
|
|
this._customCenter = [0, 0];
|
|
}
|
|
this._customCenter[0] = x;
|
|
this._customCenter[1] = y;
|
|
this._renderer.updateRotationCenter();
|
|
}
|
|
|
|
/**
|
|
* @returns The center X relatively to the drawing origin
|
|
* (whereas `getCenterX()` is relative to the top left drawable bound and scaled).
|
|
*/
|
|
getRotationCenterX(): float {
|
|
return this._customCenter
|
|
? this._customCenter[0]
|
|
: this._renderer.getUnscaledWidth() / 2 -
|
|
this._renderer.getFrameRelativeOriginX();
|
|
}
|
|
|
|
/**
|
|
* @returns The center Y relatively to the drawing origin
|
|
* (whereas `getCenterY()` is relative to the top left drawable bound and scaled).
|
|
*/
|
|
getRotationCenterY(): float {
|
|
return this._customCenter
|
|
? this._customCenter[1]
|
|
: this._renderer.getUnscaledHeight() / 2 -
|
|
this._renderer.getFrameRelativeOriginY();
|
|
}
|
|
|
|
getCenterX(): float {
|
|
if (!this._customCenter) {
|
|
return super.getCenterX();
|
|
}
|
|
return (
|
|
this._customCenter[0] * Math.abs(this._scaleX) +
|
|
this.getX() -
|
|
this.getDrawableX()
|
|
);
|
|
}
|
|
|
|
getCenterY(): float {
|
|
if (!this._customCenter) {
|
|
return super.getCenterY();
|
|
}
|
|
return (
|
|
this._customCenter[1] * Math.abs(this._scaleY) +
|
|
this.getY() -
|
|
this.getDrawableY()
|
|
);
|
|
}
|
|
|
|
setWidth(newWidth: float): void {
|
|
const unscaledWidth = this._renderer.getUnscaledWidth();
|
|
if (unscaledWidth !== 0) {
|
|
this.setScaleX(newWidth / unscaledWidth);
|
|
}
|
|
}
|
|
|
|
setHeight(newHeight: float): void {
|
|
const unscaledHeight = this._renderer.getUnscaledHeight();
|
|
if (unscaledHeight !== 0) {
|
|
this.setScaleY(newHeight / unscaledHeight);
|
|
}
|
|
}
|
|
|
|
setSize(newWidth: float, newHeight: float): void {
|
|
this.setWidth(newWidth);
|
|
this.setHeight(newHeight);
|
|
}
|
|
|
|
/**
|
|
* Change the scale on X and Y axis of the object.
|
|
*
|
|
* @param newScale The new scale (must be greater than 0).
|
|
*/
|
|
setScale(newScale: float): void {
|
|
this.setScaleX(newScale);
|
|
this.setScaleY(newScale);
|
|
}
|
|
|
|
/**
|
|
* Change the scale on X axis of the object (changing its width).
|
|
*
|
|
* @param newScale The new scale (must be greater than 0).
|
|
*/
|
|
setScaleX(newScale: float): void {
|
|
if (newScale < 0) {
|
|
newScale = 0;
|
|
}
|
|
if (newScale === Math.abs(this._scaleX)) {
|
|
return;
|
|
}
|
|
this._scaleX = newScale * (this._flippedX ? -1 : 1);
|
|
this._renderer.updateScaleX();
|
|
this.invalidateHitboxes();
|
|
}
|
|
|
|
/**
|
|
* Change the scale on Y axis of the object (changing its width).
|
|
*
|
|
* @param newScale The new scale (must be greater than 0).
|
|
*/
|
|
setScaleY(newScale: float): void {
|
|
if (newScale < 0) {
|
|
newScale = 0;
|
|
}
|
|
if (newScale === Math.abs(this._scaleY)) {
|
|
return;
|
|
}
|
|
this._scaleY = newScale * (this._flippedY ? -1 : 1);
|
|
this._renderer.updateScaleY();
|
|
this.invalidateHitboxes();
|
|
}
|
|
|
|
flipX(enable: boolean): void {
|
|
if (enable !== this._flippedX) {
|
|
this._scaleX *= -1;
|
|
this._flippedX = enable;
|
|
this._renderer.updateScaleX();
|
|
this.invalidateHitboxes();
|
|
}
|
|
}
|
|
|
|
flipY(enable: boolean): void {
|
|
if (enable !== this._flippedY) {
|
|
this._scaleY *= -1;
|
|
this._flippedY = enable;
|
|
this._renderer.updateScaleY();
|
|
this.invalidateHitboxes();
|
|
}
|
|
}
|
|
|
|
isFlippedX(): boolean {
|
|
return this._flippedX;
|
|
}
|
|
|
|
isFlippedY(): boolean {
|
|
return this._flippedY;
|
|
}
|
|
|
|
/**
|
|
* Get the scale of the object (or the geometric mean of the X and Y scale in case they are different).
|
|
*
|
|
* @return the scale of the object (or the geometric mean of the X and Y scale in case they are different).
|
|
*/
|
|
getScale(): number {
|
|
const scaleX = Math.abs(this._scaleX);
|
|
const scaleY = Math.abs(this._scaleY);
|
|
return scaleX === scaleY ? scaleX : Math.sqrt(scaleX * scaleY);
|
|
}
|
|
|
|
/**
|
|
* Get the scale of the object on Y axis.
|
|
*
|
|
* @return the scale of the object on Y axis
|
|
*/
|
|
getScaleY(): float {
|
|
return Math.abs(this._scaleY);
|
|
}
|
|
|
|
/**
|
|
* Get the scale of the object on X axis.
|
|
*
|
|
* @return the scale of the object on X axis
|
|
*/
|
|
getScaleX(): float {
|
|
return Math.abs(this._scaleX);
|
|
}
|
|
|
|
invalidateBounds() {
|
|
this.invalidateHitboxes();
|
|
}
|
|
|
|
getDrawableX(): float {
|
|
return this._renderer.getDrawableX();
|
|
}
|
|
|
|
getDrawableY(): float {
|
|
return this._renderer.getDrawableY();
|
|
}
|
|
|
|
getWidth(): float {
|
|
return this._renderer.getWidth();
|
|
}
|
|
|
|
getHeight(): float {
|
|
return this._renderer.getHeight();
|
|
}
|
|
|
|
updatePreRender(instanceContainer: gdjs.RuntimeInstanceContainer): void {
|
|
this._renderer.updatePreRender();
|
|
}
|
|
|
|
transformToDrawing(x: float, y: float) {
|
|
const point = ShapePainterRuntimeObject._pointForTransformation;
|
|
point[0] = x;
|
|
point[1] = y;
|
|
return this._renderer.transformToDrawing(point);
|
|
}
|
|
|
|
transformToScene(x: float, y: float) {
|
|
const point = ShapePainterRuntimeObject._pointForTransformation;
|
|
point[0] = x;
|
|
point[1] = y;
|
|
return this._renderer.transformToScene(point);
|
|
}
|
|
|
|
transformToDrawingX(x: float, y: float) {
|
|
return this.transformToDrawing(x, y)[0];
|
|
}
|
|
|
|
transformToDrawingY(x: float, y: float) {
|
|
return this.transformToDrawing(x, y)[1];
|
|
}
|
|
|
|
transformToSceneX(x: float, y: float) {
|
|
return this.transformToScene(x, y)[0];
|
|
}
|
|
|
|
transformToSceneY(x: float, y: float) {
|
|
return this.transformToScene(x, y)[1];
|
|
}
|
|
|
|
setRectangularCollisionMask(
|
|
left: float,
|
|
top: float,
|
|
right: float,
|
|
bottom: float
|
|
) {
|
|
if (!this._customCollisionMask) {
|
|
const rectangle = new gdjs.Polygon();
|
|
rectangle.vertices.push([0, 0]);
|
|
rectangle.vertices.push([0, 0]);
|
|
rectangle.vertices.push([0, 0]);
|
|
rectangle.vertices.push([0, 0]);
|
|
this._customCollisionMask = [rectangle];
|
|
}
|
|
const rectangle = this._customCollisionMask[0].vertices;
|
|
|
|
rectangle[0][0] = left;
|
|
rectangle[0][1] = top;
|
|
|
|
rectangle[1][0] = right;
|
|
rectangle[1][1] = top;
|
|
|
|
rectangle[2][0] = right;
|
|
rectangle[2][1] = bottom;
|
|
|
|
rectangle[3][0] = left;
|
|
rectangle[3][1] = bottom;
|
|
|
|
this.invalidateHitboxes();
|
|
}
|
|
|
|
updateHitBoxes(): void {
|
|
this.hitBoxes = this._defaultHitBoxes;
|
|
const width = this.getWidth();
|
|
const height = this.getHeight();
|
|
const centerX = this.getCenterX();
|
|
const centerY = this.getCenterY();
|
|
const vertices = this.hitBoxes[0].vertices;
|
|
if (this._customCollisionMask) {
|
|
const customCollisionMaskVertices =
|
|
this._customCollisionMask[0].vertices;
|
|
for (let i = 0; i < 4; i++) {
|
|
const point = this.transformToScene(
|
|
customCollisionMaskVertices[i][0],
|
|
customCollisionMaskVertices[i][1]
|
|
);
|
|
vertices[i][0] = point[0];
|
|
vertices[i][1] = point[1];
|
|
}
|
|
} else {
|
|
if (centerX === width / 2 && centerY === height / 2) {
|
|
vertices[0][0] = -centerX;
|
|
vertices[0][1] = -centerY;
|
|
vertices[1][0] = +centerX;
|
|
vertices[1][1] = -centerY;
|
|
vertices[2][0] = +centerX;
|
|
vertices[2][1] = +centerY;
|
|
vertices[3][0] = -centerX;
|
|
vertices[3][1] = +centerY;
|
|
} else {
|
|
vertices[0][0] = 0 - centerX;
|
|
vertices[0][1] = 0 - centerY;
|
|
vertices[1][0] = width - centerX;
|
|
vertices[1][1] = 0 - centerY;
|
|
vertices[2][0] = width - centerX;
|
|
vertices[2][1] = height - centerY;
|
|
vertices[3][0] = 0 - centerX;
|
|
vertices[3][1] = height - centerY;
|
|
}
|
|
if (!this._useAbsoluteCoordinates) {
|
|
this.hitBoxes[0].rotate(gdjs.toRad(this.getAngle()));
|
|
}
|
|
this.hitBoxes[0].move(
|
|
this.getDrawableX() + centerX,
|
|
this.getDrawableY() + centerY
|
|
);
|
|
}
|
|
}
|
|
}
|
|
gdjs.registerObject(
|
|
'PrimitiveDrawing::Drawer',
|
|
gdjs.ShapePainterRuntimeObject
|
|
);
|
|
ShapePainterRuntimeObject.supportsReinitialization = false;
|
|
}
|