I wouldn’t say that euler.z is bugged. It just doesn’t behave the way you expect it to behave
If you print(go.get(".", "euler")) you will see that for certain angles it also rotates around x and y (ie flipping). This means that you can’t use euler.z as it is. You also need to account for the flipping. You could replace the update function in my example with this to get the same result without using your own facing variable:
local euler = go.get(".","euler")
local facing = math.rad(euler.x == 0 and euler.z or 180 - euler.z)
if self.rotate ~= 0 then
facing = (facing + self.angular_velocity * self.rotate * dt) % math.rad(360)
go.set_rotation(vmath.quat_axis_angle(vmath.vector3(0, 0, 1), facing))
end
if self.move ~= 0 then
local distance_to_move = self.linear_velocity * self.move * dt
go.set_position(go.get_position() + vmath.vector3(-math.sin(facing) * distance_to_move, math.cos(facing) * distance_to_move, 0))
end