Files
GDevelop/Extensions/SkeletonObject/Gskeletonruntimeobject.ts
T
Florian Rival 4b2cd90320 Add documentation of extensions in the generated GDJS documentation
Only show in developer changelog
2021-01-14 21:32:13 +00:00

487 lines
12 KiB
TypeScript

namespace gdjs {
/*
GDevelop - Skeleton Object Extension
Copyright (c) 2017-2018 Franco Maciel (francomaciel10@gmail.com)
This project is released under the MIT License.
*/
/**
* The SkeletonRuntimeObject imports and displays skeletal animations files.
* @ignore
*/
export class SkeletonRuntimeObject extends gdjs.RuntimeObject {
rootArmature: any;
animationPlaying: boolean = true;
animationSmooth: boolean = true;
timeScale: float = 1.0;
scaleX: number = 1.0;
scaleY: number = 1.0;
hitboxSlot: any = null;
manager: any;
x: any;
y: any;
angle: float;
constructor(runtimeScene, objectData) {
super(runtimeScene, objectData);
this.rootArmature = new gdjs.sk.Armature(this);
this.rootArmature.getRenderer().putInScene(this, runtimeScene);
this.rootArmature.setAsRoot();
this.manager = gdjs.SkeletonObjectsManager.getManager(runtimeScene);
this.getSkeletonData(runtimeScene, objectData);
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
}
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
if (initialInstanceData.customSize) {
this.setWidth(initialInstanceData.width);
this.setHeight(initialInstanceData.height);
}
}
getSkeletonData(runtimeScene, objectData) {
const skeletonData = this.manager.getSkeleton(
runtimeScene,
this.name,
objectData
);
if (skeletonData.armatures.length > 0) {
this.rootArmature.loadData(
skeletonData.armatures[skeletonData.rootArmature],
skeletonData,
objectData.debugPolygons
);
this.rootArmature.resetState();
}
}
// RuntimeObject overwrites
setX(x): void {
this.x = x;
this.rootArmature.setX(x);
}
setY(y): void {
this.y = y;
this.rootArmature.setY(y);
}
setAngle(angle): void {
this.angle = angle;
this.rootArmature.setRot(angle);
}
getRendererObject() {
return this.rootArmature.getRendererObject();
}
getHitBoxes() {
if (this.hitboxSlot) {
return this.hitboxSlot.getPolygons();
}
return [this.rootArmature.getAABB()];
}
stepBehaviorsPreEvents(runtimeScene) {
const delta = this.getElapsedTime(runtimeScene) / 1000.0;
if (this.animationPlaying) {
this.rootArmature.updateAnimation(delta * this.timeScale);
}
super.stepBehaviorsPreEvents(runtimeScene);
}
update(runtimeScene): void {
this.rootArmature.update();
}
getDrawableX(): float {
return (
this.getX() -
this.rootArmature.shared.aabb[0][0] * Math.abs(this.scaleX)
);
}
getDrawableY(): float {
return (
this.getY() -
this.rootArmature.shared.aabb[0][1] * Math.abs(this.scaleY)
);
}
// Object instructions
getScaleX(): float {
return this.scaleX;
}
setScaleX(scaleX): void {
this.scaleX = scaleX;
this.rootArmature.setSx(scaleX);
}
getScaleY(): float {
return this.scaleY;
}
setScaleY(scaleY): void {
this.scaleY = scaleY;
this.rootArmature.setSy(scaleY);
}
getWidth(): float {
return this.rootArmature.getDefaultWidth() * Math.abs(this.scaleX);
}
setWidth(width): void {
if (width < 0) {
width = 0;
}
this.setScaleX(width / this.rootArmature.getDefaultWidth());
}
getHeight(): float {
return this.rootArmature.getDefaultHeight() * Math.abs(this.scaleY);
}
setHeight(height): void {
if (height < 0) {
height = 0;
}
this.setScaleY(height / this.rootArmature.getDefaultHeight());
}
setDefaultHitbox(slotPath): void {
if (slotPath === '') {
this.hitboxSlot = null;
return;
}
const slot = this.getSlot(slotPath);
if (slot) {
this.hitboxSlot = slot;
}
}
// Animation instructions
isAnimationPaused(): boolean {
return !this.animationPlaying;
}
setAnimationPaused(paused): void {
this.animationPlaying = !paused;
}
isAnimationFinished(): boolean {
return this.rootArmature.isAnimationFinished();
}
getAnimationTime(): float {
return this.rootArmature.getAnimationTime();
}
setAnimationTime(time): void {
this.rootArmature.setAnimationTime(time);
}
getAnimationTimeLength() {
return this.rootArmature.getAnimationTimeLength();
}
getAnimationFrame() {
return this.rootArmature.getAnimationFrame();
}
setAnimationFrame(frame): void {
this.rootArmature.setAnimationFrame(frame);
}
getAnimationFrameLength() {
return this.rootArmature.getAnimationFrameLength();
}
getAnimationIndex() {
return this.rootArmature.getAnimationIndex();
}
setAnimationIndex(newAnimation, blendTime = 0, loops = -1): void {
this.rootArmature.setAnimationIndex(newAnimation, blendTime, loops);
}
getAnimationName(): string {
return this.rootArmature.getAnimationName();
}
setAnimationName(newAnimation, blendTime = 0, loops = -1): void {
this.rootArmature.setAnimationName(newAnimation, blendTime, loops);
}
isAnimationSmooth(): boolean {
return this.animationSmooth;
}
setAnimationSmooth(smooth): void {
this.animationSmooth = smooth;
}
getAnimationTimeScale(): float {
return this.timeScale;
}
setAnimationTimeScale(timeScale): void {
if (timeScale < 0) {
timeScale = 0;
}
// Support negative timeScale (backward animation) ?
this.timeScale = timeScale;
}
resetAnimation(): void {
this.rootArmature.resetAnimation();
}
// Bone instructions
getBoneX(bonePath): float {
const bone = this.getBone(bonePath);
return bone ? bone.getGlobalX() : 0;
}
setBoneX(bonePath, x): void {
const bone = this.getBone(bonePath);
if (bone) {
bone.setGlobalX(x);
bone.update();
}
}
getBoneY(bonePath): float {
const bone = this.getBone(bonePath);
return bone ? bone.getGlobalY() : 0;
}
setBoneY(bonePath, y): void {
const bone = this.getBone(bonePath);
if (bone) {
bone.setGlobalY(y);
bone.update();
}
}
getBoneAngle(bonePath): float {
const bone = this.getBone(bonePath);
return bone ? bone.getGlobalRot() : 0;
}
setBoneAngle(bonePath, angle): void {
const bone = this.getBone(bonePath);
if (bone) {
bone.setGlobalRot(angle);
bone.update();
}
}
getBoneScaleX(bonePath): float {
const bone = this.getBone(bonePath);
return bone ? bone.getSx() : 0;
}
setBoneScaleX(bonePath, scaleX): void {
const bone = this.getBone(bonePath);
if (bone) {
bone.setSx(scaleX);
bone.update();
}
}
getBoneScaleY(bonePath): float {
const bone = this.getBone(bonePath);
return bone ? bone.getSy() : 0;
}
setBoneScaleY(bonePath, scaleY): void {
const bone = this.getBone(bonePath);
if (bone) {
bone.setSy(scaleY);
bone.update();
}
}
resetBone(bonePath): void {
const bone = this.getBone(bonePath);
if (bone) {
bone.resetState();
}
}
// Slot instructions
setSlotColor(slotPath, color): void {
const slot = this.getSlot(slotPath);
if (slot) {
const rgb = color.split(';');
if (rgb.length < 3) {
return;
}
slot.setColor(...rgb);
}
}
isSlotVisible(slotPath): boolean {
const slot = this.getSlot(slotPath);
return slot ? slot.getVisible() : false;
}
setSlotVisible(slotPath, visible): void {
const slot = this.getSlot(slotPath);
if (slot) {
slot.setVisible(visible);
}
}
getSlotZOrder(slotPath): float {
const slot = this.getSlot(slotPath);
return slot ? slot.getZ() : 0;
}
setSlotZOrder(slotPath, z): void {
const slot = this.getSlot(slotPath);
if (slot) {
slot.setZ(z);
}
}
isPointInsideSlot(slotPath, x, y): boolean {
const hitBoxes = this.getPolygons(slotPath);
if (!hitBoxes) {
return false;
}
for (let i = 0; i < this.hitBoxes.length; ++i) {
if (gdjs.Polygon.isPointInside(hitBoxes[i], x, y)) {
return true;
}
}
return false;
}
// Extension instructions
raycastSlot(slotPath, x, y, angle, dist, closest) {
let result = gdjs.Polygon.raycastTestStatics.result;
result.collision = false;
const endX = x + dist * Math.cos((angle * Math.PI) / 180.0);
const endY = y + dist * Math.sin((angle * Math.PI) / 180.0);
let testSqDist = closest ? dist * dist : 0;
const hitBoxes = this.getPolygons(slotPath);
if (!hitBoxes) {
return result;
}
for (let i = 0; i < hitBoxes.length; i++) {
const res = gdjs.Polygon.raycastTest(hitBoxes[i], x, y, endX, endY);
if (res.collision) {
if (closest && res.closeSqDist < testSqDist) {
testSqDist = res.closeSqDist;
result = res;
} else {
if (
!closest &&
res.farSqDist > testSqDist &&
res.farSqDist <= dist * dist
) {
testSqDist = res.farSqDist;
result = res;
}
}
}
}
return result;
}
// Warning!, assuming gdjs.evtTools.object.twoListsTest calls the predicate
// respecting the given objects lists paramenters order
static slotObjectCollisionTest(skl, obj, slotPath) {
const hitBoxes1 = skl.getPolygons(slotPath);
if (!hitBoxes1) {
return false;
}
const hitBoxes2 = obj.getHitBoxes();
for (let k = 0, lenBoxes1 = hitBoxes1.length; k < lenBoxes1; ++k) {
for (let l = 0, lenBoxes2 = hitBoxes2.length; l < lenBoxes2; ++l) {
if (
gdjs.Polygon.collisionTest(hitBoxes1[k], hitBoxes2[l], false)
.collision
) {
return true;
}
}
}
return false;
}
static slotSlotCollisionTest(skl1, skl2, slotPaths) {
const hitBoxes1 = skl1.getPolygons(slotPaths[0]);
const hitBoxes2 = skl2.getPolygons(slotPaths[1]);
if (!hitBoxes1 || !hitBoxes2) {
return false;
}
for (let k = 0, lenBoxes1 = hitBoxes1.length; k < lenBoxes1; ++k) {
for (let l = 0, lenBoxes2 = hitBoxes2.length; l < lenBoxes2; ++l) {
if (
gdjs.Polygon.collisionTest(hitBoxes1[k], hitBoxes2[l], false)
.collision
) {
return true;
}
}
}
return false;
}
// Helpers
getSlot(slotPath) {
const slotArray = slotPath.split('/');
let currentSlot = null;
if (slotArray[0] in this.rootArmature.slotsMap) {
currentSlot = this.rootArmature.slotsMap[slotArray[0]];
for (let i = 1; i < slotArray.length; i++) {
if (
currentSlot.type === gdjs.sk.SLOT_ARMATURE &&
slotArray[i] in currentSlot.childArmature.slotsMap
) {
currentSlot = currentSlot.childArmature.slotsMap[slotArray[i]];
} else {
return null;
}
}
}
return currentSlot;
}
getBone(bonePath) {
const slotArray = bonePath.split('/');
const boneName = slotArray.pop();
if (slotArray.length === 0 && boneName in this.rootArmature.bonesMap) {
return this.rootArmature.bonesMap[boneName];
}
const slot = this.getSlot(slotArray.join('/'));
if (
slot &&
slot.type === gdjs.sk.SLOT_ARMATURE &&
boneName in slot.childArmature.bonesMap
) {
return slot.childArmature.bonesMap[boneName];
}
return null;
}
getPolygons(slotPath) {
if (slotPath === '') {
return this.rootArmature.getHitBoxes();
}
const slot = this.getSlot(slotPath);
if (slot) {
return slot.getPolygons();
}
return null;
}
}
gdjs.registerObject('SkeletonObject::Skeleton', gdjs.SkeletonRuntimeObject);
}