Conversation
WalkthroughIntroduces a new Animation controller orchestrating dual-context animation playback (viewmodel and character), extends camera controls with sensitivity modifiers, adds a Shobnode debug UI system, refactors item animations to use centralized animation handling, removes the legacy viewmodel controller, and updates animation configurations across weapon items to use priority-based selection instead of weights. Changes
Sequence Diagram(s)sequenceDiagram
participant Item as Item (base_item)
participant AnimCtrl as Animation Controller
participant CanimVW as Canim (Viewmodel)
participant CanimChr as Canim (Character)
participant ItemCfg as Item Config
rect rgb(200, 220, 255)
Note over Item,ItemCfg: Initialization (onInit)
Item->>AnimCtrl: onInit()
AnimCtrl->>ItemCfg: Iterate configs
ItemCfg-->>AnimCtrl: Animation/Pose metadata
AnimCtrl->>AnimCtrl: Load animations async<br/>(globalChecksum)
AnimCtrl->>AnimCtrl: Store tracks & typeLookup
end
rect rgb(200, 220, 255)
Note over Item,CanimChr: Runtime - Play Animation
Item->>AnimCtrl: playAnimation("reload")
AnimCtrl->>AnimCtrl: Resolve type via typeLookup
AnimCtrl->>CanimVW: play_animation or play_pose
AnimCtrl->>CanimChr: play_animation or play_pose
CanimVW-->>AnimCtrl: Track
CanimChr-->>AnimCtrl: Track
AnimCtrl-->>Item: CanimTracks
end
rect rgb(200, 220, 255)
Note over Item,CanimChr: Per-Frame Update (onRender)
Item->>AnimCtrl: onRender(dt)
AnimCtrl->>CanimVW: Update state
AnimCtrl->>CanimChr: Update state
AnimCtrl->>AnimCtrl: Render Shobnode debug display
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/client/controllers/movement_sounds.ts (1)
53-124: StopcurrentlyPlayingfrom growing without bound.
Every new sound appends a label tocurrentlyPlaying, but nothing ever removes the entry, so the array grows forever and the Shobnode panel fills with stale items. Prune the entry when the sound finishes (or otherwise) before callingdisplay_node.Apply this diff:
- this.currentlyPlaying.push(`${materialName}/${name}${soundIndex} ${volume}`); + const entry = `${materialName}/${name}${soundIndex} ${volume}`; + this.currentlyPlaying.push(entry); const sound = this.createSound(soundId, volume); sound.Play(); task.delay(sound.TimeLength, () => { if (name === "Turn") this.turnSoundCooldown = false; + const idx = this.currentlyPlaying.indexOf(entry); + if (idx !== -1) this.currentlyPlaying.splice(idx, 1); if (sound && sound.Parent) sound.Destroy(); });src/client/items/base_item.ts (1)
253-261: Reset the camera modifier on destroy.
Leaving the camera modifier at the last viewmodel transform keeps the camera offset after the item is unequipped because nothing else clears it. Reset it before teardown.Apply this diff:
- this.animation.stopAnimation(`${this.itemName}/idle`); - this.unbindActions(); + this.animation.stopAnimation(`${this.itemName}/idle`); + this.cameraAnimationModifier.setOffset(new CFrame()); + this.unbindActions();
🧹 Nitpick comments (3)
src/shared/getObjectSize.ts (1)
3-5: Consider simplifying since key and value are unused.Since the actual key and value aren't used in the loop body (only the iteration count matters), you could simplify this slightly.
Apply this diff for a cleaner implementation:
- for (const [value, key] of pairs(object)) { + for (const [_key, _value] of pairs(object)) { size += 1; }Or even simpler, if your linter allows it:
- for (const [value, key] of pairs(object)) { + for (const _ of pairs(object)) { size += 1; }src/client/shobnode/ui/ShobnodeOutput.tsx (1)
10-12: Clarify position binding usage.The
bindingis created but never updated (always remainsnew UDim2()), and all labels usebinding[0]for their position. Since theuilistlayouton Line 40 handles positioning automatically, this static binding appears unnecessary. Consider either removing the position prop from labels or clarifying if dynamic positioning is planned for future iterations.If the binding is truly unused, apply this diff:
-let binding = Roact.createBinding(new UDim2()); const label = (text: string, textcolor?: Color3) => { - return <ShobnodeNode size={8} position={binding[0]} data={[text]} color={textcolor || new Color3(1, 1, 1)} />; + return <ShobnodeNode size={8} data={[text]} color={textcolor || new Color3(1, 1, 1)} />; };src/client/shobnode/ui/ShobnodeNode.tsx (1)
8-11: Wire upanchor_rightor drop the prop.
anchor_rightis part of the public props but the component never reads it, so callers get no effect when they set it. Either implement the right-anchoring behavior or remove the prop to keep the API honest.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
src/client/controllers/animation.ts(1 hunks)src/client/controllers/camera.ts(3 hunks)src/client/controllers/freelook.ts(2 hunks)src/client/controllers/items.ts(3 hunks)src/client/controllers/movement.ts(6 hunks)src/client/controllers/movement_sounds.ts(6 hunks)src/client/controllers/viewmodel.ts(0 hunks)src/client/functions/items/create_viewmodel.ts(3 hunks)src/client/items/base_item.ts(10 hunks)src/client/shobnode/index.tsx(1 hunks)src/client/shobnode/ui/ShobnodeNode.tsx(1 hunks)src/client/shobnode/ui/ShobnodeOutput.tsx(1 hunks)src/client/shobnode/ui/ShobnodeTable.tsx(1 hunks)src/client/types/items.ts(1 hunks)src/shared/configurations/items/SR-16/animations.ts(1 hunks)src/shared/configurations/items/SR-16/properties.ts(1 hunks)src/shared/configurations/items/ak_47/animations.ts(1 hunks)src/shared/configurations/items/default/animations.ts(6 hunks)src/shared/configurations/items/tokarev_tt_33/animations.ts(1 hunks)src/shared/getObjectSize.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/client/controllers/viewmodel.ts
🧰 Additional context used
🧬 Code graph analysis (9)
src/client/shobnode/ui/ShobnodeOutput.tsx (1)
src/client/shobnode/ui/ShobnodeNode.tsx (1)
props(4-12)
src/shared/configurations/items/ak_47/animations.ts (2)
src/shared/configurations/items/ak_47/index.ts (1)
animations(12-12)src/shared/configurations/items/tokarev_tt_33/index.ts (1)
animations(12-12)
src/client/controllers/animation.ts (4)
src/client/types/items.ts (1)
Viewmodel(7-18)src/shared/configurations/items/index.ts (2)
configs(24-29)ItemConfig(7-7)src/client/functions/items/create_viewmodel.ts (1)
itemName(48-113)src/client/shobnode/index.tsx (1)
log(123-138)
src/client/shobnode/ui/ShobnodeTable.tsx (1)
src/client/shobnode/ui/ShobnodeNode.tsx (1)
props(4-12)
src/client/controllers/movement.ts (2)
src/client/controllers/core.ts (1)
OnCharacterAdded(5-7)src/client/controllers/camera.ts (1)
SensitivityModifier(75-123)
src/client/controllers/freelook.ts (1)
src/client/controllers/camera.ts (1)
Controller(167-300)
src/client/controllers/movement_sounds.ts (1)
src/client/controllers/movement.ts (3)
OnJump(15-17)OnLand(19-21)OnRunningChanged(27-29)
src/client/shobnode/index.tsx (1)
src/stories/menu.story.tsx (1)
target(9-35)
src/client/items/base_item.ts (4)
src/client/controllers/movement.ts (3)
OnJump(15-17)OnRunningChanged(27-29)OnLand(19-21)src/client/controllers/camera.ts (1)
Modifier(14-73)src/client/functions/items/create_viewmodel.ts (1)
itemName(48-113)src/client/controllers/items.ts (1)
equip(53-70)
🔇 Additional comments (8)
src/shared/configurations/items/SR-16/properties.ts (1)
6-6: LGTM! Configuration property addition.The addition of
aimOffset: 2is straightforward and aligns with the weapon property enhancements in this PR.src/client/controllers/freelook.ts (1)
11-11: LGTM! Freelook sensitivity adjustments.The Y-axis clamp tightening (0.4 vs 1.0) and mouse sensitivity increase (270 vs 300 divisor) appear to be intentional camera control tuning changes that align with the PR's camera control enhancements.
Also applies to: 34-34
src/client/types/items.ts (1)
10-13: LGTM! Type definitions extended for animation system.The addition of
CameraBone: Motor6Dwithin Torso and the "Left Arm" / "Right Arm" parts properly extend the Viewmodel interface to support the new centralized Animation controller integration.src/client/controllers/items.ts (1)
49-49: LGTM! Initialization timing coordination.The
startedflag correctly coordinates automatic slot selection betweenonStartandonCharacterAdded, preventing race conditions and ensuring the initial item equip occurs exactly once after both the controller starts and the character is added.Also applies to: 103-109, 135-139
src/shared/configurations/items/SR-16/animations.ts (1)
7-7: LGTM! Migration to priority-based animation selection.The changes consistently replace weight-based selection with priority fields (priority 2 for animations, priority 1 for idle pose) and convert the idle entry to type "Pose". This aligns with the centralized animation controller refactor described in the PR objectives.
Also applies to: 12-12, 17-17, 22-22, 25-27, 33-33
src/client/functions/items/create_viewmodel.ts (1)
9-10: LGTM! Animation controller integration.The viewmodel creation flow now properly integrates with the centralized Animation controller using dependency injection. The
assignViewmodelcall is correctly placed after the viewmodel is fully configured but before it's returned.Also applies to: 52-52, 110-110
src/shared/configurations/items/tokarev_tt_33/animations.ts (1)
7-7: LGTM! Consistent priority-based migration.The tokarev animations follow the same priority-based pattern as SR-16, ensuring consistency across weapon configurations. The migration from weights to priority fields and the idle-to-Pose conversion are applied uniformly.
Also applies to: 12-12, 17-17, 22-22, 25-27, 33-33
src/client/shobnode/ui/ShobnodeOutput.tsx (1)
18-29: LGTM! Color mapping implementation.The message type to color mapping logic correctly handles Info (blue), Error (red), and Warning (orange) message types with appropriate color values.
| playAnimation = (animationKey: string): CanimTracks | undefined => { | ||
| const animationType = this.typeLookup[animationKey]; | ||
| if (!animationType) { | ||
| throw `Couldn't find type for given animationKey (animationKey: ${animationKey})`; | ||
| } | ||
|
|
||
| print(this.viewmodelAnimator.identified_bones); | ||
| print(this.characterAnimator.identified_bones); | ||
|
|
||
| const viewmodelTrack = | ||
| (animationType === "Animation" ? this.viewmodelAnimator.play_animation(animationKey) : this.viewmodelAnimator.play_pose(animationKey)) || undefined; | ||
| const characterTrack = | ||
| (animationType === "Animation" ? this.characterAnimator.play_animation(animationKey) : this.characterAnimator.play_pose(animationKey)) || undefined; | ||
|
|
||
| return viewmodelTrack; | ||
| }; | ||
|
|
||
| stopAnimation = (animationKey: string): void => { | ||
| this.viewmodelAnimator.stop_animation(animationKey); | ||
| this.characterAnimator.stop_animation(animationKey); | ||
| }; | ||
|
|
There was a problem hiding this comment.
Store running tracks so getAnimationTrack works.
We never populate this.tracks, so getAnimationTrack always returns undefined. Persist the tracks when playing and clear them when stopping.
Apply this diff:
const viewmodelTrack =
(animationType === "Animation" ? this.viewmodelAnimator.play_animation(animationKey) : this.viewmodelAnimator.play_pose(animationKey)) || undefined;
const characterTrack =
(animationType === "Animation" ? this.characterAnimator.play_animation(animationKey) : this.characterAnimator.play_pose(animationKey)) || undefined;
+ this.tracks.viewmodel[animationKey] = viewmodelTrack;
+ this.tracks.character[animationKey] = characterTrack;
+
return viewmodelTrack;
};
stopAnimation = (animationKey: string): void => {
this.viewmodelAnimator.stop_animation(animationKey);
this.characterAnimator.stop_animation(animationKey);
+ this.tracks.viewmodel[animationKey] = undefined;
+ this.tracks.character[animationKey] = undefined;
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| playAnimation = (animationKey: string): CanimTracks | undefined => { | |
| const animationType = this.typeLookup[animationKey]; | |
| if (!animationType) { | |
| throw `Couldn't find type for given animationKey (animationKey: ${animationKey})`; | |
| } | |
| print(this.viewmodelAnimator.identified_bones); | |
| print(this.characterAnimator.identified_bones); | |
| const viewmodelTrack = | |
| (animationType === "Animation" ? this.viewmodelAnimator.play_animation(animationKey) : this.viewmodelAnimator.play_pose(animationKey)) || undefined; | |
| const characterTrack = | |
| (animationType === "Animation" ? this.characterAnimator.play_animation(animationKey) : this.characterAnimator.play_pose(animationKey)) || undefined; | |
| return viewmodelTrack; | |
| }; | |
| stopAnimation = (animationKey: string): void => { | |
| this.viewmodelAnimator.stop_animation(animationKey); | |
| this.characterAnimator.stop_animation(animationKey); | |
| }; | |
| playAnimation = (animationKey: string): CanimTracks | undefined => { | |
| const animationType = this.typeLookup[animationKey]; | |
| if (!animationType) { | |
| throw `Couldn't find type for given animationKey (animationKey: ${animationKey})`; | |
| } | |
| print(this.viewmodelAnimator.identified_bones); | |
| print(this.characterAnimator.identified_bones); | |
| const viewmodelTrack = | |
| (animationType === "Animation" ? this.viewmodelAnimator.play_animation(animationKey) : this.viewmodelAnimator.play_pose(animationKey)) || undefined; | |
| const characterTrack = | |
| (animationType === "Animation" ? this.characterAnimator.play_animation(animationKey) : this.characterAnimator.play_pose(animationKey)) || undefined; | |
| this.tracks.viewmodel[animationKey] = viewmodelTrack; | |
| this.tracks.character[animationKey] = characterTrack; | |
| return viewmodelTrack; | |
| }; | |
| stopAnimation = (animationKey: string): void => { | |
| this.viewmodelAnimator.stop_animation(animationKey); | |
| this.characterAnimator.stop_animation(animationKey); | |
| this.tracks.viewmodel[animationKey] = undefined; | |
| this.tracks.character[animationKey] = undefined; | |
| }; |
🤖 Prompt for AI Agents
In src/client/controllers/animation.ts around lines 30 to 51, playAnimation
currently returns only the viewmodel track and never persists running tracks, so
getAnimationTrack always returns undefined; update playAnimation to save the
produced viewmodelTrack and characterTrack into this.tracks (keyed by
animationKey) after creating them, and update stopAnimation to remove/clear the
entry for animationKey (and still call stop_animation on both animators) so
tracks are persisted while running and cleared when stopped.
| let animation: CanimTrack | CanimPose; | ||
|
|
||
| if (animationType === "Animation") { | ||
| animation = this.viewmodelAnimator.load_animation(key, priority, id); | ||
| this.characterAnimator.load_animation(key, priority, id); | ||
| } else { | ||
| animation = this.viewmodelAnimator.load_pose(key, priority, id); | ||
| this.characterAnimator.load_pose(key, priority, id); | ||
| } | ||
|
|
||
| animation.finished_loading.Wait(); | ||
| globalChecksum.current += 1; | ||
|
|
||
| animation.looped = looped; | ||
| // eslint-disable-next-line camelcase | ||
| animation.fade_time = fadeTime; | ||
|
|
||
| if (weights !== undefined) { | ||
| for (const [bone, weight] of pairs(weights)) { | ||
| animation.bone_weights[bone] = weight; | ||
| } | ||
| } | ||
|
|
||
| loadedTracks[animationName] = { track: animation, trackType: animationType, rebased }; |
There was a problem hiding this comment.
Configure character tracks the same as viewmodel tracks.
We drop the track returned by characterAnimator.load_*, so the character-side animations never get their looped, fade_time, or bone_weights configured and may still be loading when we mark the checksum complete. Character idles/runs will therefore stop immediately or blend incorrectly.
Apply this diff:
- let animation: CanimTrack | CanimPose;
-
- if (animationType === "Animation") {
- animation = this.viewmodelAnimator.load_animation(key, priority, id);
- this.characterAnimator.load_animation(key, priority, id);
- } else {
- animation = this.viewmodelAnimator.load_pose(key, priority, id);
- this.characterAnimator.load_pose(key, priority, id);
- }
-
- animation.finished_loading.Wait();
+ let viewmodelAnimation: CanimTracks;
+ let characterAnimation: CanimTracks;
+
+ if (animationType === "Animation") {
+ viewmodelAnimation = this.viewmodelAnimator.load_animation(key, priority, id);
+ characterAnimation = this.characterAnimator.load_animation(key, priority, id);
+ } else {
+ viewmodelAnimation = this.viewmodelAnimator.load_pose(key, priority, id);
+ characterAnimation = this.characterAnimator.load_pose(key, priority, id);
+ }
+
+ viewmodelAnimation.finished_loading.Wait();
+ characterAnimation.finished_loading.Wait();
globalChecksum.current += 1;
- animation.looped = looped;
- // eslint-disable-next-line camelcase
- animation.fade_time = fadeTime;
+ viewmodelAnimation.looped = looped;
+ characterAnimation.looped = looped;
+ // eslint-disable-next-line camelcase
+ viewmodelAnimation.fade_time = fadeTime;
+ // eslint-disable-next-line camelcase
+ characterAnimation.fade_time = fadeTime;
if (weights !== undefined) {
for (const [bone, weight] of pairs(weights)) {
- animation.bone_weights[bone] = weight;
+ viewmodelAnimation.bone_weights[bone] = weight;
+ characterAnimation.bone_weights[bone] = weight;
}
}
- loadedTracks[animationName] = { track: animation, trackType: animationType, rebased };
+ loadedTracks[animationName] = { track: viewmodelAnimation, trackType: animationType, rebased };Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/client/controllers/animation.ts around lines 95-118, the code only
captures and configures the viewmodel animation while calling
characterAnimator.load_* without keeping its return value, so the character
track never gets looped, fade_time or bone_weights configured and may still be
loading when the checksum is bumped; fix this by capturing the character
animation into a separate variable (e.g. characterAnimation) from
characterAnimator.load_animation/load_pose, wait on both
animation.finished_loading and characterAnimation.finished_loading before
incrementing globalChecksum.current, and apply looped, fade_time and any
bone_weights to both the viewmodel animation and the characterAnimation (and
store/configure both as needed).
| import { Input } from "./input"; | ||
| import { UserInputService } from "@rbxts/services"; | ||
| import { OnCharacterAdded } from "./core"; | ||
| import { smoothClamp } from "shared/utilities/number_utility"; |
There was a problem hiding this comment.
Remove unused import.
The smoothClamp import is added but never used in the code.
Apply this diff to remove the unused import:
-import { smoothClamp } from "shared/utilities/number_utility";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { smoothClamp } from "shared/utilities/number_utility"; |
🤖 Prompt for AI Agents
In src/client/controllers/freelook.ts around line 6, the file imports
smoothClamp from "shared/utilities/number_utility" but never uses it; remove
that unused import from the import list so the file no longer imports
smoothClamp (and reformat the remaining imports if necessary).
| const animations = { | ||
| reload: { | ||
| type: "Animation", | ||
| id: 14643007671, | ||
| weights: [ | ||
| [2, 2, 2], | ||
| [1, 1, 1], | ||
| ], | ||
| priority: 4, | ||
| }, | ||
| equip: { | ||
| type: "Animation", | ||
| id: 14642999480, | ||
| weights: [ | ||
| [2, 2, 2], | ||
| [1, 1, 1], | ||
| ], | ||
| priority: 3, | ||
| }, | ||
| chamberToReady: { | ||
| type: "Animation", | ||
| id: 14642991821, | ||
| weights: [ | ||
| [2, 2, 2], | ||
| [1, 1, 1], | ||
| ], | ||
| priority: 3, | ||
| }, | ||
| magCheck: { | ||
| type: "Animation", | ||
| id: 14643016064, | ||
| weights: [ | ||
| [2, 2, 2], | ||
| [1, 1, 1], | ||
| ], | ||
| priority: 3, | ||
| }, | ||
| idle: { | ||
| type: "Animation", | ||
| type: "Pose", | ||
| id: 14643032549, | ||
| priority: 1, | ||
| looped: true, | ||
| weights: [ | ||
| [2, 2, 2], | ||
| [1, 1, 1], | ||
| ], | ||
| }, | ||
| chamberCheck: { | ||
| type: "Animation", | ||
| id: 14643029100, | ||
| weights: [ | ||
| [2, 2, 2], | ||
| [1, 1, 1], | ||
| ], | ||
| priority: 3, | ||
| }, | ||
| run: { | ||
| type: "Animation", | ||
| id: 14449811036, | ||
| priority: 2, | ||
| fadeTime: 2, | ||
| rebased: false, | ||
| looped: true, | ||
| weights: [ | ||
| [1, 1, 1], | ||
| [2, 2, 2], | ||
| ], | ||
| }, | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Restore WeaponAnimations typing for this config.
Dropping the Partial<WeaponAnimations> annotation removes compile-time validation of the animation keys, so future typos (e.g. idlle) would slip through until runtime. Please keep the explicit type to preserve the guardrails.
Apply this diff:
-const animations = {
+const animations: Partial<WeaponAnimations> = {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const animations = { | |
| reload: { | |
| type: "Animation", | |
| id: 14643007671, | |
| weights: [ | |
| [2, 2, 2], | |
| [1, 1, 1], | |
| ], | |
| priority: 4, | |
| }, | |
| equip: { | |
| type: "Animation", | |
| id: 14642999480, | |
| weights: [ | |
| [2, 2, 2], | |
| [1, 1, 1], | |
| ], | |
| priority: 3, | |
| }, | |
| chamberToReady: { | |
| type: "Animation", | |
| id: 14642991821, | |
| weights: [ | |
| [2, 2, 2], | |
| [1, 1, 1], | |
| ], | |
| priority: 3, | |
| }, | |
| magCheck: { | |
| type: "Animation", | |
| id: 14643016064, | |
| weights: [ | |
| [2, 2, 2], | |
| [1, 1, 1], | |
| ], | |
| priority: 3, | |
| }, | |
| idle: { | |
| type: "Animation", | |
| type: "Pose", | |
| id: 14643032549, | |
| priority: 1, | |
| looped: true, | |
| weights: [ | |
| [2, 2, 2], | |
| [1, 1, 1], | |
| ], | |
| }, | |
| chamberCheck: { | |
| type: "Animation", | |
| id: 14643029100, | |
| weights: [ | |
| [2, 2, 2], | |
| [1, 1, 1], | |
| ], | |
| priority: 3, | |
| }, | |
| run: { | |
| type: "Animation", | |
| id: 14449811036, | |
| priority: 2, | |
| fadeTime: 2, | |
| rebased: false, | |
| looped: true, | |
| weights: [ | |
| [1, 1, 1], | |
| [2, 2, 2], | |
| ], | |
| }, | |
| }; | |
| const animations: Partial<WeaponAnimations> = { | |
| reload: { | |
| type: "Animation", | |
| id: 14643007671, | |
| priority: 4, | |
| }, | |
| equip: { | |
| type: "Animation", | |
| id: 14642999480, | |
| priority: 3, | |
| }, | |
| chamberToReady: { | |
| type: "Animation", | |
| id: 14642991821, | |
| priority: 3, | |
| }, | |
| magCheck: { | |
| type: "Animation", | |
| id: 14643016064, | |
| priority: 3, | |
| }, | |
| idle: { | |
| type: "Pose", | |
| id: 14643032549, | |
| priority: 1, | |
| looped: true, | |
| }, | |
| chamberCheck: { | |
| type: "Animation", | |
| id: 14643029100, | |
| priority: 3, | |
| }, | |
| run: { | |
| type: "Animation", | |
| id: 14449811036, | |
| priority: 2, | |
| fadeTime: 2, | |
| rebased: false, | |
| looped: true, | |
| }, | |
| }; |
🤖 Prompt for AI Agents
In src/shared/configurations/items/ak_47/animations.ts around lines 3 to 43, the
explicit WeaponAnimations typing was removed which disables compile-time
validation of animation keys; restore the explicit type by declaring the object
as const animations: Partial<WeaponAnimations> = { ... } (or const animations:
WeaponAnimations = { ... } if all keys present) and add an import for the
WeaponAnimations type at the top of the file if it isn't already imported so the
compiler can validate the keys.
| @@ -0,0 +1,7 @@ | |||
| export default (object: { [key in string]: unknown }) => { | |||
| let size = 0; | |||
| for (const [value, key] of pairs(object)) { | |||
There was a problem hiding this comment.
Fix the destructuring order for pairs().
In Lua/Luau, pairs() returns (key, value) not (value, key). The current destructuring order is reversed, which will cause the variables to hold incorrect values (though in this case they're unused, the code is still semantically wrong).
Apply this diff to fix the destructuring order:
- for (const [value, key] of pairs(object)) {
+ for (const [key, value] of pairs(object)) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (const [value, key] of pairs(object)) { | |
| for (const [key, value] of pairs(object)) { |
🤖 Prompt for AI Agents
In src/shared/getObjectSize.ts around line 3, the for-loop destructures
pairs(object) as [value, key] but Lua/Luau's pairs() yields (key, value); swap
the order to [key, value] so the loop variables match the API contract (even if
unused) to keep semantics correct.
Summary by CodeRabbit
Release Notes
New Features
Improvements