forked from LeenkxTeam/LNXSDK
Repe [T3DU] and Moises Jpelaez updates
This commit is contained in:
@ -1,53 +1,258 @@
|
||||
package iron.object;
|
||||
|
||||
import iron.Scene;
|
||||
import iron.data.Data;
|
||||
import iron.App;
|
||||
import iron.data.SceneFormat;
|
||||
import iron.system.Time;
|
||||
import haxe.ds.Map;
|
||||
|
||||
|
||||
@:allow(iron.Scene)
|
||||
class Tilesheet {
|
||||
|
||||
public var tileX = 0.0; // Tile offset on tilesheet texture 0-1
|
||||
public var tileY = 0.0;
|
||||
|
||||
public var raw: TTilesheetData;
|
||||
public var tileX: Float = 0.0;
|
||||
public var tileY: Float = 0.0;
|
||||
public var flipX: Bool = false;
|
||||
public var flipY: Bool = false;
|
||||
public var paused: Bool = false;
|
||||
public var frame: Int = 0;
|
||||
public var actions: Array<TTilesheetAction>;
|
||||
public var action: TTilesheetAction = null;
|
||||
var ready: Bool;
|
||||
|
||||
public var paused = false;
|
||||
public var frame = 0;
|
||||
var time = 0.0;
|
||||
public var ready: Bool = false;
|
||||
var time: Float = 0.0;
|
||||
var onActionComplete: Void->Void = null;
|
||||
var onReady: Void->Void = null;
|
||||
var onEvent: String->Void = null; // Callback for tilesheet events
|
||||
var prevFrame: Int = -1; // Track previous frame to detect changes
|
||||
var owner: MeshObject = null;
|
||||
var currentMesh: MeshObject = null;
|
||||
var meshCache: Map<String, MeshObject> = new Map();
|
||||
var pendingAction: String = null;
|
||||
var pendingOnComplete: Void->Void = null;
|
||||
|
||||
public function new(sceneName: String, tilesheet_ref: String, tilesheet_action_ref: String) {
|
||||
ready = false;
|
||||
Data.getSceneRaw(sceneName, function(format: TSceneFormat) {
|
||||
for (ts in format.tilesheet_datas) {
|
||||
if (ts.name == tilesheet_ref) {
|
||||
raw = ts;
|
||||
Scene.active.tilesheets.push(this);
|
||||
play(tilesheet_action_ref);
|
||||
ready = true;
|
||||
break;
|
||||
public function new(tilesheetData: TTilesheetData, ownerObject: MeshObject = null) {
|
||||
owner = ownerObject;
|
||||
actions = tilesheetData.actions;
|
||||
|
||||
pendingAction = tilesheetData.start_action;
|
||||
if ((pendingAction == null || pendingAction == "") && actions.length > 0) {
|
||||
pendingAction = actions[0].name;
|
||||
}
|
||||
|
||||
flipX = tilesheetData.flipx;
|
||||
flipY = tilesheetData.flipy;
|
||||
|
||||
// If no actions need mesh swapping, ready immediately
|
||||
var hasMeshActions: Bool = false;
|
||||
for (a in actions) {
|
||||
if (a.mesh != null && a.mesh != "") {
|
||||
hasMeshActions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasMeshActions) {
|
||||
ready = true;
|
||||
if (pendingAction != null) {
|
||||
playAction(pendingAction);
|
||||
pendingAction = null;
|
||||
}
|
||||
if (onReady != null) onReady();
|
||||
}
|
||||
}
|
||||
|
||||
public function update() {
|
||||
if (App.pauseUpdates) return;
|
||||
|
||||
if (!ready) {
|
||||
if (tryInitialize()) {
|
||||
ready = true;
|
||||
if (pendingAction != null) {
|
||||
playAction(pendingAction, pendingOnComplete);
|
||||
pendingAction = null;
|
||||
pendingOnComplete = null;
|
||||
}
|
||||
if (onReady != null) onReady();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (paused || action == null || action.start >= action.end) return;
|
||||
|
||||
time += Time.renderDelta;
|
||||
|
||||
var frameTime = 1 / action.framerate;
|
||||
var framesToAdvance = 0;
|
||||
|
||||
while (time >= frameTime) {
|
||||
time -= frameTime;
|
||||
framesToAdvance++;
|
||||
}
|
||||
|
||||
if (framesToAdvance > 0) {
|
||||
setFrame(frame + framesToAdvance);
|
||||
}
|
||||
}
|
||||
|
||||
function tryInitialize(): Bool {
|
||||
if (owner == null) return false;
|
||||
|
||||
// If no children, use the owner mesh itself
|
||||
if (owner.children == null || owner.children.length == 0) {
|
||||
if (owner.data != null && !meshCache.exists(owner.data.name)) {
|
||||
meshCache.set(owner.data.name, owner);
|
||||
// Also cache by object name for flexible lookup
|
||||
if (owner.name != owner.data.name) {
|
||||
meshCache.set(owner.name, owner);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Use child meshes for mesh swapping
|
||||
for (child in owner.children) {
|
||||
if (Std.isOfType(child, MeshObject)) {
|
||||
var meshChild = cast(child, MeshObject);
|
||||
if (meshChild.data != null && !meshCache.exists(meshChild.data.name)) {
|
||||
meshCache.set(meshChild.data.name, meshChild);
|
||||
meshChild.visible = false;
|
||||
// Also cache by object name for flexible lookup
|
||||
if (meshChild.name != meshChild.data.name) {
|
||||
meshCache.set(meshChild.name, meshChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (a in actions) {
|
||||
if (a.mesh != null && a.mesh != "" && !meshCache.exists(a.mesh)) {
|
||||
if (findMatchingMesh(a.mesh) == null) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Find mesh by base name pattern (handles linked objects with different suffixes). */
|
||||
function findMatchingMesh(actionMeshName: String): MeshObject {
|
||||
var baseName = actionMeshName;
|
||||
|
||||
// Strip "Mesh" prefix if present
|
||||
if (StringTools.startsWith(baseName, "Mesh")) {
|
||||
baseName = baseName.substr(4);
|
||||
}
|
||||
|
||||
// Strip suffix after underscore (e.g., "_character.blend")
|
||||
var idx = baseName.indexOf("_");
|
||||
if (idx > 0) baseName = baseName.substr(0, idx);
|
||||
|
||||
for (meshName in meshCache.keys()) {
|
||||
if (meshName.indexOf(baseName) != -1) {
|
||||
var mesh = meshCache.get(meshName);
|
||||
meshCache.set(actionMeshName, mesh); // Cache alias
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function play(action_ref: String, onActionComplete: Void->Void = null) {
|
||||
this.onActionComplete = onActionComplete;
|
||||
for (a in raw.actions) {
|
||||
if (actions == null) return;
|
||||
|
||||
if (!ready) {
|
||||
pendingAction = action_ref;
|
||||
pendingOnComplete = onActionComplete;
|
||||
return;
|
||||
}
|
||||
|
||||
playAction(action_ref, onActionComplete);
|
||||
}
|
||||
|
||||
public function notifyOnReady(callback: Void->Void) {
|
||||
onReady = callback;
|
||||
if (ready) onReady();
|
||||
}
|
||||
|
||||
public function notifyOnEvent(callback: String->Void) {
|
||||
onEvent = callback;
|
||||
}
|
||||
|
||||
function playAction(action_ref: String, onComplete: Void->Void = null) {
|
||||
if (action != null && action.name == action_ref) {
|
||||
paused = false;
|
||||
return;
|
||||
}
|
||||
|
||||
onActionComplete = onComplete;
|
||||
|
||||
for (a in actions) {
|
||||
if (a.name == action_ref) {
|
||||
action = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == null) return;
|
||||
|
||||
if (action.mesh != null && action.mesh != "") {
|
||||
var targetMesh = meshCache.get(action.mesh);
|
||||
if (targetMesh != null && targetMesh != currentMesh) {
|
||||
swapMesh(targetMesh);
|
||||
}
|
||||
}
|
||||
|
||||
prevFrame = -1; // Reset previous frame for new action
|
||||
setFrame(action.start);
|
||||
paused = false;
|
||||
time = 0.0;
|
||||
}
|
||||
|
||||
function swapMesh(meshObj: MeshObject) {
|
||||
if (owner == null || meshObj == null) return;
|
||||
currentMesh = meshObj;
|
||||
if (meshObj.data != null) owner.setData(meshObj.data);
|
||||
if (meshObj.materials != null) owner.materials = meshObj.materials;
|
||||
}
|
||||
|
||||
function setFrame(f: Int) {
|
||||
frame = f;
|
||||
|
||||
if (frame > action.end && action.start < action.end) {
|
||||
// Check for events on last frame before completing
|
||||
checkEvents(prevFrame, action.end);
|
||||
if (onActionComplete != null) onActionComplete();
|
||||
if (action.loop) {
|
||||
prevFrame = -1; // Reset for loop
|
||||
setFrame(action.start);
|
||||
} else {
|
||||
paused = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for events between previous frame and current frame
|
||||
checkEvents(prevFrame, frame);
|
||||
prevFrame = frame;
|
||||
|
||||
var tx = frame % action.tilesx;
|
||||
var ty = Std.int(frame / action.tilesx);
|
||||
tileX = tx / action.tilesx;
|
||||
tileY = ty / action.tilesy;
|
||||
}
|
||||
|
||||
/** Check and fire events for frames between fromFrame (exclusive) and toFrame (inclusive). */
|
||||
function checkEvents(fromFrame: Int, toFrame: Int) {
|
||||
if (onEvent == null || action == null || action.events == null) return;
|
||||
|
||||
// Convert to action-relative frame numbers
|
||||
var relativeFrom = fromFrame - action.start;
|
||||
var relativeTo = toFrame - action.start;
|
||||
|
||||
for (evt in action.events) {
|
||||
// Fire event if it falls in the range (fromFrame, toFrame]
|
||||
if (evt.frame > relativeFrom && evt.frame <= relativeTo) {
|
||||
onEvent(evt.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function pause() {
|
||||
paused = true;
|
||||
}
|
||||
@ -57,61 +262,33 @@ class Tilesheet {
|
||||
}
|
||||
|
||||
public function remove() {
|
||||
Scene.active.tilesheets.remove(this);
|
||||
ready = false;
|
||||
action = null;
|
||||
actions = null;
|
||||
owner = null;
|
||||
currentMesh = null;
|
||||
pendingAction = null;
|
||||
pendingOnComplete = null;
|
||||
onEvent = null;
|
||||
prevFrame = -1;
|
||||
meshCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frame of the current active tilesheet action. Automatically un-pauses action.
|
||||
* @param frame Frame offset with 0 as the first frame of the active action.
|
||||
**/
|
||||
public function setFrameOffset(frame: Int) {
|
||||
if (action == null) return;
|
||||
setFrame(action.start + frame);
|
||||
paused = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current frame.
|
||||
* @return Frame offset with 0 as the first frame of the active action.
|
||||
*/
|
||||
public function getFrameOffset(): Int {
|
||||
return frame - action.start;
|
||||
return action != null ? frame - action.start : 0;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (!ready || paused || action.start >= action.end) return;
|
||||
|
||||
time += Time.renderDelta;
|
||||
|
||||
var frameTime = 1 / raw.framerate;
|
||||
var framesToAdvance = 0;
|
||||
|
||||
// Check how many animation frames passed during the last render frame
|
||||
// and catch up if required. The remaining `time` that couldn't fit in
|
||||
// another animation frame will be used in the next `update()`.
|
||||
while (time >= frameTime) {
|
||||
time -= frameTime;
|
||||
framesToAdvance++;
|
||||
}
|
||||
|
||||
if (framesToAdvance != 0) {
|
||||
setFrame(frame + framesToAdvance);
|
||||
}
|
||||
public function getTilesX(): Int {
|
||||
return action != null ? action.tilesx : 1;
|
||||
}
|
||||
|
||||
function setFrame(f: Int) {
|
||||
frame = f;
|
||||
|
||||
// Action end
|
||||
if (frame > action.end && action.start < action.end) {
|
||||
if (onActionComplete != null) onActionComplete();
|
||||
if (action.loop) setFrame(action.start);
|
||||
else paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var tx = frame % raw.tilesx;
|
||||
var ty = Std.int(frame / raw.tilesx);
|
||||
tileX = tx * (1 / raw.tilesx);
|
||||
tileY = ty * (1 / raw.tilesy);
|
||||
public function getTilesY(): Int {
|
||||
return action != null ? action.tilesy : 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user