From 25d7b3ca0313b7fa501951e4a40f743bf1389da3 Mon Sep 17 00:00:00 2001 From: Bishop Date: Thu, 30 Jul 2026 20:40:16 -0500 Subject: [PATCH 1/2] Animation tools --- Server/src/main/core/api/ContentAPI.kt | 20 ++++++ .../command/sets/AnimationCommandSet.kt | 66 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index 7423518a7..d1c3a40ef 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -586,6 +586,26 @@ fun resetAnimator(player: Player) { player.animator.animate(Animation(-1, Animator.Priority.VERY_HIGH)) } +/** + * Sets a player's render animation to a given animation. + * @param player the player whose render animation to set + * @param animation the ID of the render animation to set + */ +fun renderAnimation(player: Player, animation: Int) { + player.appearance.setAnimations(Animation(animation)) + player.appearance.sync() +} + +/** + * Resets a player's render animation to the default. + * @param player the player whose render animation to reset + */ +fun resetRenderAnimation(player: Player) { + player.appearance.setDefaultAnimations() + player.appearance.setAnimations() + player.appearance.sync() +} + /** * Get the number of ticks an animation lasts * @param animation the Animation object to check the duration of diff --git a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt index d06258333..ba5dc8201 100644 --- a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt @@ -2,6 +2,7 @@ package core.game.system.command.sets import core.api.animate import core.api.delayScript +import core.api.log import core.api.queueScript import core.api.stopExecuting import core.game.interaction.QueueStrength @@ -12,6 +13,7 @@ import core.plugin.Initializable import core.game.system.command.CommandPlugin.Companion.toInteger import core.game.system.command.Privilege import core.game.world.GameWorld +import core.tools.Log import java.util.* @Initializable @@ -113,5 +115,69 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { player.appearance.setAnimations() player.appearance.sync() } + + + /** + * Dumps the animations contained within a given render animation, by ID. + */ + define("ranimdump", Privilege.ADMIN, "::ranimdump Animation ID", "Dumps the render animation sub-animations for the given animation ID.") { player, args -> + if (args.size < 2) { + reject(player, "Syntax error: ::ranimdump ") + } + val argAnimId = args[1].toInt() + val def = core.cache.def.impl.RenderAnimationDefinition.forId(argAnimId) + notify(player, "Sub-animations of render anim ID $argAnimId: stand=${def.standAnimationId}, walk=${def.walkAnimationId}, run=${def.runAnimationId},") + notify(player, "Sub-animations of render anim ID $argAnimId: turn180=${def.turn180Animation}, turnCW=${def.turnCWAnimation}, turnCCW=${def.turnCCWAnimation}") + } + + + /** + * Searches all animations for render animations containing a given animation as a sub-animation, by ID. + */ + define("ranimsearch", Privilege.ADMIN, "::ranimsearch Animation ID", "Searches all animation definitions for render animations that incorporate the given animation ID.") { player, args -> + if (args.size < 2) { + reject(player, "Syntax error: ::ranimsearch ") + } + val argAnimId = args[1].toInt() + val everyRAnim = core.cache.Cache.getIndexes()[2].getFilesSize(32) + var hits = 0 + for (animId in 0 until everyRAnim) { + val def = core.cache.def.impl.RenderAnimationDefinition.forId(animId) ?: continue + if (def.standAnimationId == argAnimId || def.walkAnimationId == argAnimId || def.runAnimationId == argAnimId || + def.turn180Animation == argAnimId || def.turnCWAnimation == argAnimId || def.turnCCWAnimation == argAnimId) { + hits++ + val output1 = "Sub-animations of render anim ID $animId: stand=${def.standAnimationId}, walk=${def.walkAnimationId}, run=${def.runAnimationId}" + val output2 = "Sub-animations of render anim ID $animId: turn180=${def.turn180Animation}, turnCW=${def.turnCWAnimation}, turnCCW=${def.turnCCWAnimation}" + if (hits <= 20) { + notify(player, output1) + notify(player, output2) + } + log(this::class.java, Log.DEBUG, output1) + log(this::class.java, Log.DEBUG, output2) + } + } + val total = "$hits render animations found referencing animation ID $argAnimId." + notify(player, total) + log(this::class.java, Log.DEBUG, total) + if (hits > 20) { + notify(player, "Displayed results truncated to 20, see console for full list.") + } + } + + + /** + * Prints the render animation ID of a given NPC, and its sub-animations, by NPC ID. + */ + define("ranimnpc", Privilege.ADMIN, "::ranimnpc NPC ID", "Prints the render animation ID (and sub-animation IDs) of an NPC.") { player, args -> + if (args.size < 2) { + reject(player, "Syntax error: ::ranimnpc ") + } + val targetNPC = args[1].toInt() + val rAnimId = core.cache.def.impl.NPCDefinition.forId(targetNPC).renderAnimationId + val def = core.cache.def.impl.RenderAnimationDefinition.forId(rAnimId) + notify(player, "Render animation for NPC ID $targetNPC: animation ID $rAnimId") + notify(player, "Sub-animations of render anim ID $rAnimId: stand=${def.standAnimationId}, walk=${def.walkAnimationId}, run=${def.runAnimationId},") + notify(player, "Sub-animations of render anim ID $rAnimId: turn180=${def.turn180Animation}, turnCW=${def.turnCWAnimation}, turnCCW=${def.turnCCWAnimation}") + } } } From 15b15e400c23c479dccff5779c20821b733ba186 Mon Sep 17 00:00:00 2001 From: Bishop Date: Sat, 1 Aug 2026 11:21:32 -0500 Subject: [PATCH 2/2] Adopted ::objanim from Oftheshire --- .../command/sets/AnimationCommandSet.kt | 73 ++++++++++++++----- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt index ba5dc8201..207dc0ce3 100644 --- a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt @@ -1,6 +1,8 @@ package core.game.system.command.sets +import core.api.addScenery import core.api.animate +import core.api.animateScenery import core.api.delayScript import core.api.log import core.api.queueScript @@ -28,16 +30,17 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { */ define("anim", Privilege.ADMIN, "::anim Animation ID", "Plays the animation with the given ID."){ player, args -> if (args.size < 2) { - reject(player, "Syntax error: ::anim ") + reject(player, "Syntax error: ::anim Animation ID") + return@define } val animation = Animation(args[1].toInt()) player.animate(animation) } - define("anims", Privilege.ADMIN, "::anims <(opt) Duration Per Animation>", "Plays animations from the From ID to the To ID, with a delay of 3 between animations, unless specified otherwise"){ player, args -> - + define("anims", Privilege.ADMIN, "::anims From Animation ID To Animation ID (opt) Duration Per Animation", "Plays animations from the From ID to the To ID, with a delay of 3 between animations, unless specified otherwise"){ player, args -> if (args.size < 3) { - reject(player, "Syntax error: ::anims <(opt) Duration Per Animation>") + reject(player, "Syntax error: ::anims From Animation ID To Animation ID (opt) Duration Per Animation") + return@define } val animationFrom = args[1].toInt() val animationTo = args[2].toInt() @@ -61,7 +64,8 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { */ define("loopanim", Privilege.ADMIN, "::loopanim Animation ID Times", "Plays the animation with the given ID the given number of times"){ player, args -> if (args.size < 2) { - reject(player, "Syntax error: ::loopanim ") + reject(player, "Syntax error: ::loopanim Animation ID Loop Amount") + return@define } val start = toInteger(args[1]) var end = toInteger((args[2])) @@ -83,7 +87,8 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { */ define("ranim", Privilege.ADMIN, "::ranim Render Anim ID", "Sets the player's render (walk/idle) animation."){ player, args -> if (args.size < 2) { - reject(player, "Syntax error: ::ranim ") + reject(player, "Syntax error: ::ranim Render Animation ID") + return@define } if (args.size > 2) { GameWorld.Pulser.submit(object : Pulse(3, player) { @@ -105,7 +110,6 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { } } - /** * Reset the player's render animation to default */ @@ -116,13 +120,13 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { player.appearance.sync() } - /** * Dumps the animations contained within a given render animation, by ID. */ define("ranimdump", Privilege.ADMIN, "::ranimdump Animation ID", "Dumps the render animation sub-animations for the given animation ID.") { player, args -> - if (args.size < 2) { - reject(player, "Syntax error: ::ranimdump ") + if (args.size < 2 || args[1].toIntOrNull() == null) { + reject(player, "Syntax error: ::ranimdump Animation ID") + return@define } val argAnimId = args[1].toInt() val def = core.cache.def.impl.RenderAnimationDefinition.forId(argAnimId) @@ -130,13 +134,13 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { notify(player, "Sub-animations of render anim ID $argAnimId: turn180=${def.turn180Animation}, turnCW=${def.turnCWAnimation}, turnCCW=${def.turnCCWAnimation}") } - /** * Searches all animations for render animations containing a given animation as a sub-animation, by ID. */ define("ranimsearch", Privilege.ADMIN, "::ranimsearch Animation ID", "Searches all animation definitions for render animations that incorporate the given animation ID.") { player, args -> - if (args.size < 2) { - reject(player, "Syntax error: ::ranimsearch ") + if (args.size < 2 || args[1].toIntOrNull() == null) { + reject(player, "Syntax error: ::ranimsearch Animation ID") + return@define } val argAnimId = args[1].toInt() val everyRAnim = core.cache.Cache.getIndexes()[2].getFilesSize(32) @@ -156,7 +160,7 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { log(this::class.java, Log.DEBUG, output2) } } - val total = "$hits render animations found referencing animation ID $argAnimId." + val total = "$hits render animation(s) found referencing animation ID $argAnimId." notify(player, total) log(this::class.java, Log.DEBUG, total) if (hits > 20) { @@ -164,13 +168,13 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { } } - /** * Prints the render animation ID of a given NPC, and its sub-animations, by NPC ID. */ define("ranimnpc", Privilege.ADMIN, "::ranimnpc NPC ID", "Prints the render animation ID (and sub-animation IDs) of an NPC.") { player, args -> - if (args.size < 2) { - reject(player, "Syntax error: ::ranimnpc ") + if (args.size < 2 || args[1].toIntOrNull() == null) { + reject(player, "Syntax error: ::ranimnpc NPC ID") + return@define } val targetNPC = args[1].toInt() val rAnimId = core.cache.def.impl.NPCDefinition.forId(targetNPC).renderAnimationId @@ -179,5 +183,40 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { notify(player, "Sub-animations of render anim ID $rAnimId: stand=${def.standAnimationId}, walk=${def.walkAnimationId}, run=${def.runAnimationId},") notify(player, "Sub-animations of render anim ID $rAnimId: turn180=${def.turn180Animation}, turnCW=${def.turnCWAnimation}, turnCCW=${def.turnCCWAnimation}") } + + /** + * Spawns an object with a given ID at the player's location, and animates it. + */ + define("objanim", usage = "::objanim objectId animFrom (opt) animTo (opt) delay", description = "Spawns an object and animates it with the provided anim ID. If a second ID is provided, it iterates through the anims.") { player, args -> + if (args.size < 3 || args[1].toIntOrNull() == null || args[2].toIntOrNull() == null) { + reject(player, "Syntax error: ::objanim objectId animFrom (opt) animTo (opt) delay") + return@define + } + + val objectId = args[1].toInt() + val animFrom = args[2].toInt() + var animTo = args.getOrNull(3)?.toIntOrNull() + val delay = args.getOrNull(4)?.toIntOrNull() ?: 3 + + // spawn the object + val obj = core.game.node.scenery.Scenery(objectId, player.location) + addScenery(obj) + + // set the range if it's just a single anim + if (animTo == null) animTo = animFrom + + // loop through the anims if it's a range + notify(player, "Spawned object $objectId, playing anims $animFrom to $animTo.") + queueScript(player, 1, QueueStrength.STRONG) { stage: Int -> + val currentAnimId = animFrom + stage + animateScenery(player, obj, currentAnimId) + notify(player, "Playing object animation $currentAnimId") + if (currentAnimId >= animTo) { + return@queueScript stopExecuting(player) + } + return@queueScript delayScript(player, delay) + } + } } + }