Implement the RCO animation system and focus handling
Add the 11 RCO animation record types (MoveTo/Recolour/Resize/Fade/ FireEvent/...), an AnimFactory, and populate the resource animtable. Anim play() applies the final state immediately so the menu lays out correctly. Also add setInterval overloads (5+ params), setFocus(VsmxBaseObject), and animation offsets (animX/Y/Z) to position objects, fixing the dispatch warnings that were silently stalling the menu.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco;
|
||||
|
||||
import dev.quoije.umd.rco.anim.Anim;
|
||||
import dev.quoije.umd.rco.anim.BaseAnim;
|
||||
import dev.quoije.umd.rco.anim.DelayAnim;
|
||||
import dev.quoije.umd.rco.anim.FadeAnim;
|
||||
import dev.quoije.umd.rco.anim.FireEventAnim;
|
||||
import dev.quoije.umd.rco.anim.LockAnim;
|
||||
import dev.quoije.umd.rco.anim.MoveToAnim;
|
||||
import dev.quoije.umd.rco.anim.RecolourAnim;
|
||||
import dev.quoije.umd.rco.anim.ResizeAnim;
|
||||
import dev.quoije.umd.rco.anim.RotateAnim;
|
||||
import dev.quoije.umd.rco.anim.SlideOutAnim;
|
||||
import dev.quoije.umd.rco.anim.UnlockAnim;
|
||||
|
||||
/** Creates RCO animation records by type id. */
|
||||
public final class AnimFactory {
|
||||
|
||||
private AnimFactory() {
|
||||
}
|
||||
|
||||
public static BaseAnim newAnim(int type) {
|
||||
return switch (type) {
|
||||
case 1 -> new Anim();
|
||||
case 2 -> new MoveToAnim();
|
||||
case 3 -> new RecolourAnim();
|
||||
case 4 -> new RotateAnim();
|
||||
case 5 -> new ResizeAnim();
|
||||
case 6 -> new FadeAnim();
|
||||
case 7 -> new DelayAnim();
|
||||
case 8 -> new FireEventAnim();
|
||||
case 9 -> new LockAnim();
|
||||
case 10 -> new UnlockAnim();
|
||||
case 11 -> new SlideOutAnim();
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@ public final class RcoFile {
|
||||
private final Map<Integer, String> events = new LinkedHashMap<>();
|
||||
private final Map<Integer, ImageObject> images = new LinkedHashMap<>();
|
||||
private final Map<Integer, BaseObject> objects = new LinkedHashMap<>();
|
||||
private final Map<Integer, BaseObject> anims = new LinkedHashMap<>();
|
||||
|
||||
private final byte[] buffer;
|
||||
private int offset;
|
||||
@@ -109,6 +110,9 @@ public final class RcoFile {
|
||||
for (BaseObject object : objects.values()) {
|
||||
object.init(initContext);
|
||||
}
|
||||
for (BaseObject anim : anims.values()) {
|
||||
anim.init(initContext);
|
||||
}
|
||||
ok = true;
|
||||
} catch (RuntimeException e) {
|
||||
ok = false;
|
||||
@@ -148,6 +152,10 @@ public final class RcoFile {
|
||||
return objects;
|
||||
}
|
||||
|
||||
public Map<Integer, BaseObject> anims() {
|
||||
return anims;
|
||||
}
|
||||
|
||||
public static String tableName(int id) {
|
||||
return switch (id) {
|
||||
case TABLE_MAIN -> "MAIN";
|
||||
@@ -305,6 +313,14 @@ public final class RcoFile {
|
||||
if (dataLength > 0) {
|
||||
entry.data = readBytes(dataLength);
|
||||
}
|
||||
BaseObject anim = AnimFactory.newAnim(entry.type);
|
||||
if (anim != null) {
|
||||
if (entry.data != null) {
|
||||
anim.read(new RcoContext(entry.data, 0, events, images, objects));
|
||||
}
|
||||
anims.put(entryOffset, anim);
|
||||
entry.obj = anim;
|
||||
}
|
||||
}
|
||||
case TABLE_TEXT -> {
|
||||
if (entry.type == 1) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
/** A generic animation container (type 1): no direct effect, plays its children. */
|
||||
public class Anim extends BaseAnim {
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.object.BaseObject;
|
||||
import dev.quoije.umd.rco.object.BasePositionObject;
|
||||
import dev.quoije.umd.rco.type.ObjectType;
|
||||
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
|
||||
import dev.quoije.umd.rco.vsmx.interpreter.VsmxNativeObject;
|
||||
import dev.quoije.umd.rco.vsmx.objects.Resource;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Base of RCO animation records. {@link #play} applies the animation (and its
|
||||
* children) to the target. This port applies the final state immediately
|
||||
* rather than tweening over time; the menu still lays out correctly.
|
||||
*/
|
||||
public class BaseAnim extends BaseObject {
|
||||
|
||||
/** Plays this animation and all its child animations on the target. */
|
||||
public void play(VsmxBaseObject thisObject, VsmxBaseObject object) {
|
||||
doPlay(object);
|
||||
|
||||
if (thisObject.hasPropertyValue(Resource.childrenName)) {
|
||||
VsmxBaseObject children = thisObject.getPropertyValue(Resource.childrenName);
|
||||
for (String name : children.getPropertyNames()) {
|
||||
VsmxBaseObject child = children.getPropertyValue(name);
|
||||
if (child instanceof VsmxNativeObject nativeObject && nativeObject.getObject() instanceof BaseAnim anim) {
|
||||
anim.play(child, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies this animation's effect to the given target. */
|
||||
protected void doPlay(VsmxBaseObject object) {
|
||||
}
|
||||
|
||||
/** Resolves an object reference and applies the action to the target. */
|
||||
protected void applyTo(ObjectType ref, Consumer<BasePositionObject> action) {
|
||||
BasePositionObject target = ref == null ? null : ref.getPositionObject();
|
||||
if (target != null) {
|
||||
action.accept(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.FloatType;
|
||||
|
||||
/** A timed delay between animations (type 7). */
|
||||
public class DelayAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public FloatType time;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.FloatType;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
import dev.quoije.umd.rco.type.ObjectType;
|
||||
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
|
||||
|
||||
/** Fades the referenced object to the given transparency (type 6). */
|
||||
public class FadeAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public ObjectType ref;
|
||||
@ObjectField(order = 2) public FloatType duration;
|
||||
@ObjectField(order = 3) public IntType accelMode;
|
||||
@ObjectField(order = 4) public FloatType transparency;
|
||||
|
||||
@Override
|
||||
protected void doPlay(VsmxBaseObject object) {
|
||||
applyTo(ref, target -> {
|
||||
target.alphaScale.setFloatValue(transparency.getFloatValue());
|
||||
target.onDisplayUpdated();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.EventType;
|
||||
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
|
||||
|
||||
/** Fires the referenced event script (type 8). */
|
||||
public class FireEventAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public EventType event;
|
||||
|
||||
@Override
|
||||
protected void doPlay(VsmxBaseObject object) {
|
||||
if (event != null && event.getEvent() != null) {
|
||||
object.getInterpreter().interpretScript(object, event.getEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
|
||||
/** Locks input (type 9). */
|
||||
public class LockAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public IntType unknownInt0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.FloatType;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
import dev.quoije.umd.rco.type.ObjectType;
|
||||
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
|
||||
|
||||
/** Moves the referenced object by the given animation offset (type 2). */
|
||||
public class MoveToAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public ObjectType ref;
|
||||
@ObjectField(order = 2) public FloatType duration;
|
||||
@ObjectField(order = 3) public IntType accelMode;
|
||||
@ObjectField(order = 4) public FloatType x;
|
||||
@ObjectField(order = 5) public FloatType y;
|
||||
@ObjectField(order = 6) public FloatType z;
|
||||
|
||||
@Override
|
||||
protected void doPlay(VsmxBaseObject object) {
|
||||
applyTo(ref, target -> {
|
||||
target.animX = x.getFloatValue();
|
||||
target.animY = y.getFloatValue();
|
||||
target.animZ = z.getFloatValue();
|
||||
target.onDisplayUpdated();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.FloatType;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
import dev.quoije.umd.rco.type.ObjectType;
|
||||
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
|
||||
|
||||
/** Recolours the referenced object (type 3). */
|
||||
public class RecolourAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public ObjectType ref;
|
||||
@ObjectField(order = 2) public FloatType duration;
|
||||
@ObjectField(order = 3) public IntType accelMode;
|
||||
@ObjectField(order = 4) public FloatType red;
|
||||
@ObjectField(order = 5) public FloatType green;
|
||||
@ObjectField(order = 6) public FloatType blue;
|
||||
@ObjectField(order = 7) public FloatType alpha;
|
||||
|
||||
@Override
|
||||
protected void doPlay(VsmxBaseObject object) {
|
||||
applyTo(ref, target -> {
|
||||
target.redScale.setFloatValue(red.getFloatValue());
|
||||
target.greenScale.setFloatValue(green.getFloatValue());
|
||||
target.blueScale.setFloatValue(blue.getFloatValue());
|
||||
target.alphaScale.setFloatValue(alpha.getFloatValue());
|
||||
target.onDisplayUpdated();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.FloatType;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
import dev.quoije.umd.rco.type.ObjectType;
|
||||
import dev.quoije.umd.rco.vsmx.interpreter.VsmxBaseObject;
|
||||
|
||||
/** Resizes the referenced object (type 5). */
|
||||
public class ResizeAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public ObjectType ref;
|
||||
@ObjectField(order = 2) public FloatType duration;
|
||||
@ObjectField(order = 3) public IntType accelMode;
|
||||
@ObjectField(order = 4) public FloatType width;
|
||||
@ObjectField(order = 5) public FloatType height;
|
||||
@ObjectField(order = 6) public FloatType depth;
|
||||
|
||||
@Override
|
||||
protected void doPlay(VsmxBaseObject object) {
|
||||
applyTo(ref, target -> {
|
||||
target.scaleWidth.setFloatValue(width.getFloatValue());
|
||||
target.scaleHeight.setFloatValue(height.getFloatValue());
|
||||
target.scaleDepth.setFloatValue(depth.getFloatValue());
|
||||
target.onDisplayUpdated();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.FloatType;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
import dev.quoije.umd.rco.type.ObjectType;
|
||||
|
||||
/** Rotates the referenced object (type 4). */
|
||||
public class RotateAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public ObjectType ref;
|
||||
@ObjectField(order = 2) public FloatType duration;
|
||||
@ObjectField(order = 3) public IntType accelMode;
|
||||
@ObjectField(order = 4) public FloatType x;
|
||||
@ObjectField(order = 5) public FloatType y;
|
||||
@ObjectField(order = 6) public FloatType z;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.FloatType;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
import dev.quoije.umd.rco.type.ObjectType;
|
||||
|
||||
/** Slides the referenced object out (type 11). */
|
||||
public class SlideOutAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public ObjectType ref;
|
||||
@ObjectField(order = 2) public FloatType duration;
|
||||
@ObjectField(order = 3) public IntType accelMode;
|
||||
@ObjectField(order = 4) public FloatType xspeed;
|
||||
@ObjectField(order = 5) public FloatType yspeed;
|
||||
@ObjectField(order = 6) public FloatType xcompress;
|
||||
@ObjectField(order = 7) public FloatType ycompress;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package dev.quoije.umd.rco.anim;
|
||||
|
||||
import dev.quoije.umd.rco.ObjectField;
|
||||
import dev.quoije.umd.rco.type.IntType;
|
||||
|
||||
/** Unlocks input (type 10). */
|
||||
public class UnlockAnim extends BaseAnim {
|
||||
|
||||
@ObjectField(order = 1) public IntType unknownInt0;
|
||||
}
|
||||
@@ -32,13 +32,18 @@ public class BasePositionObject extends BaseObject {
|
||||
@ObjectField(order = 114) public IntType iconOffset;
|
||||
@ObjectField(order = 115) public EventType onInit;
|
||||
|
||||
/** Animation offsets (added to the authored position). */
|
||||
public float animX;
|
||||
public float animY;
|
||||
public float animZ;
|
||||
|
||||
@Override
|
||||
public int getX() {
|
||||
int parentX = 0;
|
||||
if (getParent() instanceof BasePositionObject positionParent) {
|
||||
parentX = positionParent.getX();
|
||||
}
|
||||
return parentX + posX.getIntValue();
|
||||
return parentX + posX.getIntValue() + Math.round(animX);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,7 +52,7 @@ public class BasePositionObject extends BaseObject {
|
||||
if (getParent() instanceof BasePositionObject positionParent) {
|
||||
parentY = positionParent.getY();
|
||||
}
|
||||
return parentY + posY.getIntValue();
|
||||
return parentY + posY.getIntValue() + Math.round(animY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,6 +99,10 @@ public class BasePositionObject extends BaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void setFocus(VsmxBaseObject object) {
|
||||
setFocus();
|
||||
}
|
||||
|
||||
public void setPos(VsmxBaseObject object, VsmxBaseObject x, VsmxBaseObject y) {
|
||||
posX.setFloatValue(x.getFloatValue());
|
||||
posY.setFloatValue(y.getFloatValue());
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.quoije.umd.rco.type;
|
||||
|
||||
import dev.quoije.umd.rco.RcoContext;
|
||||
import dev.quoije.umd.rco.object.BaseObject;
|
||||
import dev.quoije.umd.rco.object.BasePositionObject;
|
||||
import dev.quoije.umd.rco.object.ImageObject;
|
||||
|
||||
/**
|
||||
@@ -60,6 +61,13 @@ public class BaseReferenceType extends BaseType {
|
||||
return object;
|
||||
}
|
||||
|
||||
public BasePositionObject getPositionObject() {
|
||||
if (object instanceof BasePositionObject positionObject) {
|
||||
return positionObject;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ImageObject getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -121,6 +121,18 @@ public class Timer extends BaseNativeObject {
|
||||
return setInterval(object, function, interval, new VsmxBaseObject[]{param1, param2, param3});
|
||||
}
|
||||
|
||||
public VsmxBaseObject setInterval(VsmxBaseObject object, VsmxBaseObject function, VsmxBaseObject interval, VsmxBaseObject param1, VsmxBaseObject param2, VsmxBaseObject param3, VsmxBaseObject param4) {
|
||||
return setInterval(object, function, interval, new VsmxBaseObject[]{param1, param2, param3, param4});
|
||||
}
|
||||
|
||||
public VsmxBaseObject setInterval(VsmxBaseObject object, VsmxBaseObject function, VsmxBaseObject interval, VsmxBaseObject param1, VsmxBaseObject param2, VsmxBaseObject param3, VsmxBaseObject param4, VsmxBaseObject param5) {
|
||||
return setInterval(object, function, interval, new VsmxBaseObject[]{param1, param2, param3, param4, param5});
|
||||
}
|
||||
|
||||
public VsmxBaseObject setInterval(VsmxBaseObject object, VsmxBaseObject function, VsmxBaseObject interval, VsmxBaseObject param1, VsmxBaseObject param2, VsmxBaseObject param3, VsmxBaseObject param4, VsmxBaseObject param5, VsmxBaseObject param6) {
|
||||
return setInterval(object, function, interval, new VsmxBaseObject[]{param1, param2, param3, param4, param5, param6});
|
||||
}
|
||||
|
||||
public void clearInterval(VsmxBaseObject object, VsmxBaseObject id) {
|
||||
int idValue = id.getPropertyValue(0).getIntValue();
|
||||
synchronized (timers) {
|
||||
|
||||
Reference in New Issue
Block a user