Quick answer: Negative scale anywhere in the parent chain mirrors the text. Reset scales to positive; use rotation for facing, not negative scale.

Floating damage numbers use Label3D with billboard enabled. When the enemy faces left (flipped via negative X scale), the numbers render mirrored too.

Cause: Inherited Negative Scale

Label3D billboards to face the camera, but the parent’s negative scale still applies to the text geometry. The character is flipped via scale.x = -1; the label inherits it.

Detach From Flipped Parent

# Don't parent the label under the flipped sprite
CharacterRoot (scale positive)
  - Sprite3D (flip via rotation, not scale)
  - DamageLabel (Label3D)

Keep the label outside the flipped subtree. Or flip the sprite by rotating 180° on Y instead of negative scale.

Counter-Scale

If you must keep it parented:

func _process(_d):
    # cancel out parent's negative scale
    label.scale.x = abs(label.scale.x) * sign(get_parent().scale.x)

Hacky but works if architecture demands it.

Billboard Modes

None of these fix mirrored geometry from scale — the fix is the scale itself.

Verifying

Enemy faces both directions; damage numbers always readable. No mirrored text.

“Negative scale mirrors everything below it. Flip sprites with rotation; keep labels out of the flipped subtree.”

As a rule: never use negative scale for character flipping in 3D. Rotation is cleaner and doesn’t poison child transforms.