Okay, I think I see what's going on. Two things: First, what the docs don't make clear at all is the sprite system moves the drawing offset in the draw callback so that (0,0) is the top left of the sprite bounds. I'm pretty sure that's regardless of the sprite center, which was.. ehhhhh not a great decision on my part, but here we are and if I change it now it'll break too much stuff. So in the sprite draw callback you'll want
self.fgImg:draw(0,0,playdate.graphics.kImageUnflipped,self.x,self.y,self.width,self.height)
instead. BUT! It looks like there's also a bug in image:draw() when using both a source rect and a drawing offset, it goofs up the clip rect. But the system sets up a clip rect to the sprite bounds before calling your draw callback, so we can just draw the whole image offset to the screen origin:
self.fgImg:draw(-self.x,-self.y,playdate.graphics.kImageUnflipped)
That works the same under the hood, you're not taking a penalty here by not using a source rect.
Let me know if that does the trick!