(supertux-sprite
(action
- (hitbox 12 12 0 0)
+ (hitbox 2 2 12 12)
(fps 20)
(images "fire_bullet-0.png"
"fire_bullet-1.png"
(speed 1)
(width 100)
(height 35)
- (tiles 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 0 28 0 28 0 28 0 28 0 28 0 0 28 0 28 0 28 28 28 28 28 28 28 28 28 28 28 28 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
+ (tiles 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 0 28 0 28 0 28 0 28 0 28 0 0 28 0 28 0 28 28 28 28 28 28 28 28 28 28 28 28 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 48 48 48 48 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 48 0 0 0 0 0 0 0 0 0 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 102 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 48 48 48 48 48 48 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 48 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 48 48 48 48 48 48 48 48 48 48 48 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
)
(tilemap
(z-pos 100)
(speed 1)
(width 150)
(height 35)
- (tiles 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1001 1002 1001 1002 1001 1002 1052 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1005 1006 1005 1006 1005 1006 1054 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1055 1009 1009 1009 1009 1009 1009 1056 1052 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1057 1009 1009 1009 1009 1009 1009 1058 1054 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1055 1009 1009 1009 1009 1009 1009 1009 1009 1056 1052 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1057 1009 1009 1009 1009 1009 1009 1009 1009 1058 1054 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1001 1002 1001 1003 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1055 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1056 1046 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1005 1006 1005 1007 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1047 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1058 1048 1046 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1055 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1047 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1050 1048 1046 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1001 1002 1001 1003 0 0 0 0 0 0 0 0 0 0 0 0 1001 1002 1001 1003 0 0 0 0 0 0 0 0 0 0 0 0 1053 1057 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1047 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1050 1048 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1005 1006 1005 1007 0 0 0 0 0 0 0 0 0 0 1045 1047 1005 1006 1005 1007 0 0 0 0 0 1051 1052 0 0 0 0 1051 1055 1013 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1047 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1050 1065 1066 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1061 0 0 0 0 1060 1063 1064 1013 1013 1013 1013 1013 1011 0 0 0 0 0 1045 1046 0 0 1045 1047 1049 1013 1013 1013 1011 0 0 0 0 0 1053 1054 0 0 0 0 1053 1057 1013 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1069 1070 1065 1066 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1001 1002 1001 1002 1001 1002 1001 1063 1064 1065 1066 1002 1001 1063 1064 1067 1013 1013 1013 1013 1013 1013 1035 1001 1002 1001 1002 1001 1047 1048 1001 1002 1047 1049 1013 1013 1013 1013 1035 1001 1002 1001 1002 1001 1055 1056 1002 1001 1002 1001 1055 1013 1013 1013 1013 1013 1035 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1063 1064 1067 1068 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1070 1065 1066 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1005 1006 1005 1006 1005 1006 1005 1067 1068 1069 1070 1006 1005 1067 1068 1069 1013 1013 1013 1013 1013 1013 1043 1005 1006 1005 1006 1005 1049 1050 1005 1006 1049 1013 1013 1013 1013 1013 1043 1005 1006 1005 1006 1005 1057 1058 1006 1005 1006 1005 1057 1013 1013 1013 1013 1013 1043 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1067 1068 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1069 1070 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1061 0 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1035 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1065 1066 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1043 1065 1066 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 1069 1070 1065 1066 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1001 1002 1001 1002 1001 1001 1002 1001 1002 1001 1063 1064 1013 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 1009 1009 1009 1070 1065 1066 1002 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1005 1006 1005 1006 1005 1005 1006 1005 1006 1005 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 1009 1009 1009 1009 1069 1070 1006 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
+ (tiles 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1001 1002 1001 1002 1001 1002 1052 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1005 1006 1005 1006 1005 1006 1054 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1055 1009 1009 1009 1009 1009 1009 1056 1052 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1057 1009 1009 1009 1009 1009 1009 1058 1054 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1055 1009 1009 1009 1009 1009 1009 1009 1009 1056 1052 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1057 1009 1009 1009 1009 1009 1009 1009 1009 1058 1054 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1001 1002 1001 1003 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1055 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1056 1046 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1053 1005 1006 1005 1007 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1047 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1058 1048 1046 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1051 1055 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1047 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1050 1048 1046 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1001 1002 1001 1003 0 0 0 0 0 0 0 0 0 0 0 0 1001 1002 1001 1003 0 0 0 0 0 0 0 0 0 0 0 0 1053 1057 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1045 1047 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1050 1048 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1005 1006 1005 1007 0 0 0 0 0 0 0 0 0 0 1045 1047 1005 1006 1005 1007 0 0 0 0 0 1051 1052 0 0 0 0 1051 1055 1013 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1047 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1050 1065 1066 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1061 0 0 0 0 1060 1063 1064 1013 1013 1013 1013 1013 1011 0 0 0 0 0 1045 1046 0 0 1045 1047 1049 1013 1013 1013 1011 0 0 0 0 0 1053 1054 0 0 0 0 1053 1057 1013 1013 1013 1013 1011 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1049 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1069 1070 1065 1066 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1001 1002 1001 1002 1001 1002 1001 1063 1064 1065 1066 1002 1001 1063 1064 1067 1013 1013 1013 1013 1013 1013 1035 1001 1002 1001 1002 1001 1047 1048 1001 1002 1047 1049 1013 1013 1013 1013 1035 1001 1002 1001 1002 1001 1055 1056 1002 1001 1002 1001 1055 1013 1013 1013 1013 1013 1035 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1063 1064 1067 1068 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1070 1065 1066 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1005 1006 1005 1006 1005 1006 1005 1067 1068 1069 1070 1006 1005 1067 1068 1069 1013 1013 1013 1013 1013 1013 1043 1005 1006 1005 1006 1005 1049 1050 1005 1006 1049 1013 1013 1013 1013 1013 1043 1006 1006 1005 1006 1005 1057 1058 1006 1005 1006 1005 1057 1013 1013 1013 1013 1013 1043 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1067 1068 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1009 1069 1070 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1061 0 1013 1013 0 0 0 0 0 1060 1061 0 0 0 0 0 0 1060 1063 1064 1013 1013 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1035 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1065 1066 1013 1013 0 0 0 1060 1063 1064 1065 1066 1061 0 0 1060 1063 1064 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1043 1065 1066 1061 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1060 1063 1064 1013 1013 1013 1013 1013 1013 0 1060 1063 1064 1013 1013 1013 1013 1065 1066 1063 1064 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 1069 1070 1065 1066 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1001 1002 1001 1002 1001 1001 1002 1001 1002 1001 1063 1064 1013 1013 1013 1013 1013 1013 1013 1013 1063 1064 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 1009 1009 1009 1070 1065 1066 1002 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 1002 1001 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1005 1006 1005 1006 1005 1005 1006 1005 1006 1005 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 1013 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1009 1009 1009 1009 1009 1009 1009 1009 1009 1069 1070 1006 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 1006 1005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
)
(tilemap
(z-pos 100)
sprite->set_action("idle");
}
-HitResponse
-AngryStone::collision_solid(GameObject& , const CollisionHit& hit)
+void
+AngryStone::collision_solid(const CollisionHit& hit)
{
- if ((state == ATTACKING) && (hit.normal.x == -attackDirection.x) && (hit.normal.y == attackDirection.y)) {
+ // TODO
+ (void) hit;
+#if 0
+ if ((state == ATTACKING) &&
+ (hit.normal.x == -attackDirection.x) && (hit.normal.y == attackDirection.y)) {
state = IDLE;
sprite->set_action("idle");
physic.set_velocity_x(0);
oldWallDirection.x = attackDirection.x;
oldWallDirection.y = attackDirection.y;
}
-
- return CONTINUE;
+#endif
}
void
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
void active_update(float elapsed_time);
void kill_fall();
void
BadGuy::save(lisp::Writer& )
{
- log_warning << "tried to write out a generic badguy" << std::endl;
+ log_warning << "tried to write out a generic badguy" << std::endl;
}
void
case STATE_INACTIVE:
return ABORT_MOVE;
case STATE_ACTIVE: {
- if(other.get_flags() & FLAG_SOLID)
- return collision_solid(other, hit);
-
BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
if(badguy && badguy->state == STATE_ACTIVE && badguy->get_group() == COLGROUP_MOVING)
return collision_badguy(*badguy, hit);
return FORCE_MOVE;
}
case STATE_SQUISHED:
- if(other.get_flags() & FLAG_SOLID)
- return CONTINUE;
return FORCE_MOVE;
case STATE_FALLING:
return FORCE_MOVE;
return ABORT_MOVE;
}
-HitResponse
-BadGuy::collision_solid(GameObject& , const CollisionHit& )
+void
+BadGuy::collision_solid(const CollisionHit& )
{
- return FORCE_MOVE;
}
HitResponse
BadGuy::collision_player(Player& player, const CollisionHit& )
{
- /*
- printf("PlayerHit: GT %3.1f PM: %3.1f %3.1f BM: %3.1f %3.1f Hit: %3.1f %3.1f\n",
- game_time,
- player.get_movement().x, player.get_movement().y,
- get_movement().x, get_movement().y,
- hit.normal.x, hit.normal.y);
- */
-
// hit from above?
if(player.get_bbox().p2.y < (bbox.p1.y + 16)) {
// if it's not possible to squish us, then this will hurt
- if(collision_squished(player))
+ if(collision_squished(player)) {
return ABORT_MOVE;
+ }
}
if(player.is_invincible()) {
* implemetnation calls collision_player, collision_solid, collision_badguy
* and collision_squished
*/
- virtual HitResponse collision(GameObject& other,
- const CollisionHit& hit);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
/** Called when a collision with tile with special attributes occured */
virtual void collision_tile(uint32_t tile_attributes);
};
/** Called when the badguy collided with a player */
- virtual HitResponse collision_player(Player& player,
- const CollisionHit& hit);
+ virtual HitResponse collision_player(Player& player, const CollisionHit& hit);
/** Called when the badguy collided with solid ground */
- virtual HitResponse collision_solid(GameObject& other,
- const CollisionHit& hit);
+ virtual void collision_solid(const CollisionHit& hit);
/** Called when the badguy collided with another badguy */
- virtual HitResponse collision_badguy(BadGuy& other,
- const CollisionHit& hit);
+ virtual HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
/** Called when the player hit the badguy from above. You should return true
* if the badguy was squished, false if squishing wasn't possible
virtual bool collision_squished(Player& player);
/** Called when the badguy collided with a bullet */
- virtual HitResponse collision_bullet(Bullet& bullet,
- const CollisionHit& hit);
+ virtual HitResponse collision_bullet(Bullet& bullet, const CollisionHit& hit);
/** called each frame when the badguy is activated. */
virtual void active_update(float elapsed_time);
// bombs are only temporarily so don't write them out...
}
-HitResponse
-Bomb::collision_solid(GameObject& , const CollisionHit& hit)
+void
+Bomb::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5)
+ if(hit.bottom)
physic.set_velocity_y(0);
-
- return CONTINUE;
}
HitResponse
Bomb(const Bomb& bomb);
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_player(Player& player, const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
void active_update(float elapsed_time);
void kill_fall();
void explode();
- virtual Bomb* clone() const { return new Bomb(*this); }
-
private:
enum State {
STATE_TICKING,
State state;
std::auto_ptr<SoundSource> ticking;
-
};
#endif
return true;
}
-HitResponse
-BouncingSnowball::collision_solid(GameObject& , const CollisionHit& hit)
+void
+BouncingSnowball::collision_solid(const CollisionHit& hit)
{
- if(hit.normal.y < -.5) { // hit floor
- physic.set_velocity_y(JUMPSPEED);
- } else if(hit.normal.y > .5) { // bumped on roof
+ if(hit.bottom) {
+ if(get_state() == STATE_ACTIVE) {
+ physic.set_velocity_y(JUMPSPEED);
+ } else {
+ physic.set_velocity_y(0);
+ }
+ } else if(hit.top) {
physic.set_velocity_y(0);
- } else { // left or right collision
+ }
+
+ if(hit.left || hit.right) { // left or right collision
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
BouncingSnowball::collision_badguy(BadGuy& , const CollisionHit& hit)
{
- if(fabsf(hit.normal.x) > .8) { // left/right?
- dir = dir == LEFT ? RIGHT : LEFT;
- sprite->set_action(dir == LEFT ? "left" : "right");
- physic.set_velocity_x(-physic.get_velocity_x());
- } else if(hit.normal.y < -.8) { // grounf
- physic.set_velocity_y(JUMPSPEED);
- }
-
+ collision_solid(hit);
return CONTINUE;
}
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
virtual BouncingSnowball* clone() const { return new BouncingSnowball(*this); }
sound_source->set_position(get_pos());
}
-
-HitResponse
-Dart::collision_solid(GameObject& , const CollisionHit& )
+void
+Dart::collision_solid(const CollisionHit& )
{
sound_manager->play("sounds/darthit.wav", get_pos());
remove_me();
- return ABORT_MOVE;
}
HitResponse
void active_update(float elapsed_time);
- HitResponse collision_solid(GameObject& object, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
HitResponse collision_player(Player& player, const CollisionHit& hit);
writer.end_list("fish");
}
-HitResponse
-Fish::collision_solid(GameObject& , const CollisionHit& chit)
+void
+Fish::collision_solid(const CollisionHit& chit)
{
- return hit(chit);
+ hit(chit);
}
HitResponse
}
HitResponse
-Fish::hit(const CollisionHit& chit)
+Fish::hit(const CollisionHit& hit)
{
- if(chit.normal.y < .5) { // hit ceiling
+ if(hit.top) {
physic.set_velocity_y(0);
}
void draw(DrawingContext& context);
- HitResponse collision_solid(GameObject& , const CollisionHit& );
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& , const CollisionHit& );
void collision_tile(uint32_t tile_attributes);
return true;
}
-HitResponse
-FlyingSnowBall::collision_solid(GameObject& , const CollisionHit& hit)
+void
+FlyingSnowBall::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.top || hit.bottom) {
physic.set_velocity_y(0);
}
-
- return CONTINUE;
}
void
void activate();
void write(lisp::Writer& writer);
void active_update(float elapsed_time);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
virtual FlyingSnowBall* clone() const { return new FlyingSnowBall(*this); }
BadGuy::active_update(elapsed_time);
}
-HitResponse
-Igel::collision_solid(GameObject& , const CollisionHit& hit)
+void
+Igel::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // floor or roof
+ if(hit.top || hit.bottom) { // floor or roof
physic.set_velocity_y(0);
- return CONTINUE;
+ return;
}
// hit left or right
switch(state) {
-
case STATE_NORMAL:
turn_around();
break;
-
}
-
- return CONTINUE;
}
HitResponse
Igel::collision_badguy(BadGuy& , const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // floor or roof
+ if(hit.top || hit.bottom) { // floor or roof
physic.set_velocity_y(0);
return CONTINUE;
}
Igel::collision_bullet(Bullet& , const CollisionHit& hit)
{
// die if hit on front side
- if (((dir == LEFT) && (hit.normal.x > 0)) || ((dir == RIGHT) && (hit.normal.x < 0))) {
+ if (((dir == LEFT) && hit.left) || ((dir == RIGHT) && hit.right)) {
kill_fall();
return ABORT_MOVE;
}
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& object, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
HitResponse collision_bullet(Bullet& bullet, const CollisionHit& hit);
writer.end_list("jumpy");
}
-HitResponse
-Jumpy::collision_solid(GameObject& , const CollisionHit& chit)
+void
+Jumpy::collision_solid(const CollisionHit& chit)
{
- return hit(chit);
+ hit(chit);
}
HitResponse
HitResponse
Jumpy::hit(const CollisionHit& chit)
{
- // hit floor?
- if(chit.normal.y < -.5) {
+ if(chit.bottom) {
if (!groundhit_pos_set)
{
pos_groundhit = get_pos();
physic.set_velocity_y(JUMPSPEED);
// TODO create a nice sound for this...
//sound_manager->play("sounds/skid.wav");
- } else if(chit.normal.y < .5) { // bumped on roof
+ } else if(chit.top) {
physic.set_velocity_y(0);
}
public:
Jumpy(const lisp::Lisp& reader);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
void write(lisp::Writer& writer);
dying = false;
}
-HitResponse
-Kugelblitz::collision_solid(GameObject& , const CollisionHit& chit)
+void
+Kugelblitz::collision_solid(const CollisionHit& chit)
{
- return hit(chit);
+ hit(chit);
}
HitResponse
}
HitResponse
-Kugelblitz::hit(const CollisionHit& chit)
+Kugelblitz::hit(const CollisionHit& hit)
{
// hit floor?
- if(chit.normal.y < -.5) {
+ if(hit.bottom) {
if (!groundhit_pos_set)
{
pos_groundhit = get_pos();
movement_timer.start(MOVETIME);
lifetime.start(LIFETIME);
- } else if(chit.normal.y < .5) { // bumped on roof
+ } else if(hit.top) { // bumped on roof
physic.set_velocity_y(0);
}
void activate();
HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_player(Player& player, const CollisionHit& hit);
void write(lisp::Writer& writer);
return true;
}
-HitResponse
-MrBomb::collision_solid(GameObject& , const CollisionHit& hit)
+void
+MrBomb::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.bottom || hit.top) {
physic.set_velocity_y(0);
- } else { // hit right or left
+ }
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
-MrBomb::collision_badguy(BadGuy& , const CollisionHit& hit)
+MrBomb::collision_badguy(BadGuy& )
{
+#if 0
if(fabsf(hit.normal.x) > .8) { // left or right
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
+#endif
return CONTINUE;
}
void activate();
void active_update(float elapsed_time);
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
- HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_badguy(BadGuy& badguy);
void kill_fall();
virtual MrBomb* clone() const { return new MrBomb(*this); }
BadGuy::active_update(elapsed_time);
}
-HitResponse
-MrIceBlock::collision_solid(GameObject& object, const CollisionHit& hit)
+void
+MrIceBlock::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // floor or roof
+ if(hit.top || hit.bottom) { // floor or roof
physic.set_velocity_y(0);
- return CONTINUE;
+ return;
}
+
// hit left or right
switch(ice_state) {
case ICESTATE_NORMAL:
physic.set_velocity_x(-physic.get_velocity_x());
break;
case ICESTATE_KICKED: {
+#if 0
+ // TODO move these into bonusblock class
BonusBlock* bonusblock = dynamic_cast<BonusBlock*> (&object);
if(bonusblock) {
bonusblock->try_open();
if(brick) {
brick->try_break();
}
+#endif
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
physic.set_velocity_x(0);
break;
case ICESTATE_GRABBED:
- return FORCE_MOVE;
+ break;
}
-
- return CONTINUE;
}
HitResponse
// handle kicks from left or right side
if(ice_state == ICESTATE_FLAT && get_state() == STATE_ACTIVE) {
- // hit from left side
- if(hit.normal.x > 0.7) {
+ if(hit.left) {
dir = RIGHT;
player.kick();
set_state(ICESTATE_KICKED);
return FORCE_MOVE;
- } else if(hit.normal.x < -0.7) {
+ } else if(hit.right) {
dir = LEFT;
player.kick();
set_state(ICESTATE_KICKED);
{
switch(ice_state) {
case ICESTATE_NORMAL:
- if(fabsf(hit.normal.x) > .8) {
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
void activate();
void write(lisp::Writer& writer);
HitResponse collision(GameObject& object, const CollisionHit& hit);
- HitResponse collision_solid(GameObject& object, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
HitResponse collision_player(Player& player, const CollisionHit& hit);
return true;
}
-HitResponse
-MrRocket::collision_solid(GameObject& , const CollisionHit& hit)
+void
+MrRocket::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.top || hit.bottom) {
physic.set_velocity_y(0);
- } else { // hit right or left
+ } else if(hit.left || hit.right) {
sprite->set_action(dir == LEFT ? "collision-left" : "collision-right");
physic.set_velocity_x(0);
collision_timer.start(0.2, true);
}
-
- return CONTINUE;
}
IMPLEMENT_FACTORY(MrRocket, "mrrocket")
void activate();
void active_update(float elapsed_time);
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
virtual MrRocket* clone() const { return new MrRocket(*this); }
return true;
}
-HitResponse
-MrTree::collision_solid(GameObject& , const CollisionHit& hit)
+void
+MrTree::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) {
+ if(hit.top || hit.bottom) {
physic.set_velocity_y(0);
} else {
dir = dir == LEFT ? RIGHT : LEFT;
activate();
}
-
- return CONTINUE;
}
HitResponse
MrTree::collision_badguy(BadGuy& , const CollisionHit& hit)
{
- if(fabsf(hit.normal.x) > .8) { // left or right hit
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
activate();
}
-
return CONTINUE;
}
void activate();
void active_update(float elapsed_time);
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
virtual MrTree* clone() const { return new MrTree(*this); }
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include <config.h>
-
-#include "nolok_01.hpp"
-#include "badguy/snail.hpp"
-#include "trigger/door.hpp"
-
-#define WALK_TIME 2.5
-#define SHOOT_TIME 0.4
-#define JUMP_TIME 0.5
-#define INITIAL_HITPOINTS 3
-#define INITIAL_BULLET_HP 10
-
-static const float WALKSPEED = 90;
-
-//TODO: Create sprite, limit max number of snowballs
-Nolok_01::Nolok_01(const lisp::Lisp& reader)
- : BadGuy(reader, "images/creatures/nolok/nolok.sprite")
-{
- countMe = false;
-}
-
-Nolok_01::Nolok_01(const Vector& pos)
- : BadGuy(pos, "images/creatures/nolok/nolok.sprite")
-{
- countMe = false;
-}
-
-void
-Nolok_01::write(lisp::Writer& writer)
-{
- writer.start_list("nolok_01");
-
- writer.write_float("x", start_position.x);
- writer.write_float("y", start_position.y);
-
- writer.end_list("nolok_01");
-}
-
-void
-Nolok_01::activate()
-{
- //hitpoints = INITIAL_HITPOINTS;
- //bullet_hitpoints = INITIAL_BULLET_HP;
- physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
- sprite->set_action(dir == LEFT ? "left" : "right");
- action = WALKING;
- action_timer.start(WALK_TIME);
-}
-
-void
-Nolok_01::active_update(float elapsed_time)
-{
- if (action_timer.check()) {
- switch (action) {
- case WALKING:
- {
- sprite->set_action("jump");
- physic.set_velocity_y(-700);
- action = JUMPING;
- action_timer.start(JUMP_TIME);
- break;
- }
- case JUMPING:
- {
- sprite->set_action("throw");
- action = SHOOTING;
- action_timer.start(SHOOT_TIME);
- break;
- }
- case SHOOTING:
- {
- Sector::current()->add_object(new Snail(Vector(get_pos().x - 64, get_pos().y), LEFT));
- Sector::current()->add_object(new Snail(Vector(get_pos().x + 64, get_pos().y), RIGHT));
- physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
- sprite->set_action(dir == LEFT ? "left" : "right");
- action = WALKING;
- action_timer.start(WALK_TIME);
- break;
- }
- }
- }
- movement = physic.get_movement(elapsed_time);
-}
-
-bool
-Nolok_01::collision_squished(Player& player)
-{
- bool result = false;
- player.bounce(*this);
-#if 0
- if (hitpoints <= 0) {
- bullet_hitpoints = 0;
- sprite->set_action("dead");
- kill_squished(player);
- Sector::current()->add_object(new Door((int)get_pos().x+32, 512, "sector1", "main2"));
- result = true;
- }
-#endif
- return result;
-}
-
-HitResponse
-Nolok_01::collision_solid(GameObject& , const CollisionHit& hit)
-{
- if(fabsf(hit.normal.y) > .5){ // hit floor or roof?
- if (action != JUMPING) physic.set_velocity_y(0);
- } else { // hit right or left
- dir = dir == LEFT ? RIGHT : LEFT;
- sprite->set_action(dir == LEFT ? "left" : "right");
- physic.set_velocity_x(-physic.get_velocity_x());
- }
-
- return CONTINUE;
-}
-
-//TODO: Hitpoint count incorrect when combining squishing and shooting
-void
-Nolok_01::kill_fall()
-{
-#if 0
- bullet_hitpoints--;
- if (bullet_hitpoints <= 0) {
- hitpoints = 0;
- sound_manager->play("sounds/fall.wav", this,
- this->get_pos());
- physic.set_velocity_y(0);
- physic.enable_gravity(true);
- set_state(STATE_FALLING);
- Sector::current()->add_object(new Door((int)get_pos().x+32, 512, "sector1", "main2"));
- }
-#endif
-}
-
-IMPLEMENT_FACTORY(Nolok_01, "nolok_01")
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef __NOLOK01_H__
-#define __NOLOK01_H__
-
-#include "badguy.hpp"
-#include "timer.hpp"
-
-class Nolok_01 : public BadGuy
-{
-public:
- Nolok_01(const lisp::Lisp& reader);
- Nolok_01(const Vector& pos);
-
- void activate();
- void write(lisp::Writer& writer);
- void active_update(float elapsed_time);
- void kill_fall();
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
-
- virtual Nolok_01* clone() const { return new Nolok_01(*this); }
-
-protected:
- bool collision_squished(Player& player);
- Timer action_timer;
- enum Actions { WALKING, JUMPING, SHOOTING };
- Actions action;
-};
-
-#endif
-
sprite->set_action(dir == LEFT ? "sleeping-left" : "sleeping-right");
}
-HitResponse
-Plant::collision_solid(GameObject& , const CollisionHit& hit)
+void
+Plant::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.top || hit.bottom) {
physic.set_velocity_y(0);
- } else { // hit right or left
+ } else if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
{
if(state != PLANT_WALKING) return CONTINUE;
- if(fabsf(hit.normal.x) > .8) { // left or right
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
void active_update(float elapsed_time);
return true;
}
-HitResponse
-PoisonIvy::collision_solid(GameObject& , const CollisionHit& hit)
+void
+PoisonIvy::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.top || hit.bottom) {
physic.set_velocity_y(0);
- } else { // hit right or left
+ } else if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
PoisonIvy::collision_badguy(BadGuy& , const CollisionHit& hit)
{
- if(fabsf(hit.normal.x) > .8) { // left or right hit
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
virtual PoisonIvy* clone() const { return new PoisonIvy(*this); }
}
HitResponse
-RocketExplosion::collision_solid(GameObject& , const CollisionHit& hit)
-{
- if(fabsf(hit.normal.y) > .5)
- physic.set_velocity_y(0);
-
- return CONTINUE;
-}
-
-HitResponse
RocketExplosion::collision_player(Player& player, const CollisionHit& )
{
player.kill(false);
RocketExplosion(const Vector& pos, Direction dir);
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
HitResponse collision_player(Player& player, const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
void active_update(float elapsed_time);
return true;
}
-HitResponse
-SkullyHop::collision_solid(GameObject& , const CollisionHit& hit)
+void
+SkullyHop::collision_solid(const CollisionHit& hit)
{
// ignore collisions while standing still
- if(state != JUMPING) return CONTINUE;
+ if(state != JUMPING)
+ return;
// check if we hit the floor while falling
- if ((hit.normal.y < 0) && (physic.get_velocity_y() > 0)) {
+ if(hit.bottom) {
set_state(STANDING);
}
-
// check if we hit the roof while climbing
- if ((hit.normal.y > 0) && (physic.get_velocity_y() < 0)) {
+ if(hit.top) {
physic.set_velocity_y(0);
}
// check if we hit left or right while moving in either direction
- if ((hit.normal.x != 0) && (physic.get_velocity_x() != 0)) {
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
physic.set_velocity_x(-0.25*physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
-SkullyHop::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+SkullyHop::collision_badguy(BadGuy& , const CollisionHit& hit)
{
// behaviour for badguy collisions is the same as for collisions with solids
- return collision_solid(badguy, hit);
+ collision_solid(hit);
+
+ return CONTINUE;
}
void
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
bool collision_squished(Player& player);
void active_update(float elapsed_time);
BadGuy::active_update(elapsed_time);
}
-HitResponse
-Snail::collision_solid(GameObject& object, const CollisionHit& hit)
+void
+Snail::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // floor or roof
+ if(hit.top || hit.bottom) { // floor or roof
physic.set_velocity_y(0);
switch (state) {
break;
}
- return CONTINUE;
+ return;
}
// hit left or right
switch(state) {
case STATE_KICKED: {
sound_manager->play("sounds/iceblock_bump.wav", get_pos());
-
+
+#if 0
+ // TODO move this into BonusBlock code
// open bonusblocks, crash bricks
BonusBlock* bonusblock = dynamic_cast<BonusBlock*> (&object);
if(bonusblock) {
if(brick) {
brick->try_break();
}
+#endif
dir = (dir == LEFT) ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "flat-left" : "flat-right");
}
}
-
- return CONTINUE;
}
HitResponse
{
switch(state) {
case STATE_NORMAL:
- if(fabsf(hit.normal.x) > .5) {
+ if(hit.left || hit.right) {
dir = (dir == LEFT) ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& object, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
void active_update(float elapsed_time);
static const float WALKSPEED = 80;
SnowBall::SnowBall(const lisp::Lisp& reader)
- : BadGuy(reader, "images/creatures/snowball/snowball.sprite")
+ : BadGuy(reader, "images/creatures/snowball/snowball.sprite")
{
}
SnowBall::SnowBall(const Vector& pos, Direction d)
- : BadGuy(pos, d, "images/creatures/snowball/snowball.sprite")
+ : BadGuy(pos, d, "images/creatures/snowball/snowball.sprite")
{
}
SnowBall::write(lisp::Writer& writer)
{
writer.start_list("snowball");
-
writer.write_float("x", start_position.x);
writer.write_float("y", start_position.y);
- /*
- if (fluffy) { // don't give us away at every snowball
- writer.write_bool("fluffy", true);
- }
- */
writer.end_list("snowball");
}
return true;
}
-HitResponse
-SnowBall::collision_solid(GameObject& , const CollisionHit& hit)
+void
+SnowBall::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.top || hit.bottom) {
physic.set_velocity_y(0);
- } else { // hit right or left
+ }
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
SnowBall::collision_badguy(BadGuy& , const CollisionHit& hit)
{
- if(fabsf(hit.normal.x) > .8) { // left or right hit
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(0);
+ }
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit);
virtual SnowBall* clone() const { return new SnowBall(*this); }
return true;
}
-HitResponse
-SpiderMite::collision_solid(GameObject& , const CollisionHit& hit)
+void
+SpiderMite::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > 1.5) { // hit floor or roof?
+ if(hit.top || hit.bottom) { // hit floor or roof?
physic.set_velocity_y(0);
}
-
- return CONTINUE;
}
void
void activate();
void write(lisp::Writer& writer);
void active_update(float elapsed_time);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
virtual SpiderMite* clone() const { return new SpiderMite(*this); }
}
}
-HitResponse
-Spiky::collision_solid(GameObject& , const CollisionHit& hit)
+void
+Spiky::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.top || hit.bottom) { // hit floor or roof?
physic.set_velocity_y(0);
} else { // hit right or left
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
Spiky::collision_badguy(BadGuy& , const CollisionHit& hit)
{
- if(fabsf(hit.normal.x) > .8) { // left or right
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
void activate();
void write(lisp::Writer& writer);
void active_update(float elapsed_time);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
virtual Spiky* clone() const { return new Spiky(*this); }
sprite->set_action(dir == LEFT ? "sleeping-left" : "sleeping-right");
}
-
-
-HitResponse
-SSpiky::collision_solid(GameObject& , const CollisionHit& hit)
+void
+SSpiky::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
+ if(hit.top || hit.bottom) { // hit floor or roof?
physic.set_velocity_y(0);
} else { // hit right or left
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
}
-
- return CONTINUE;
}
HitResponse
SSpiky::collision_badguy(BadGuy& , const CollisionHit& hit)
{
- if(state != SSPIKY_WALKING) return CONTINUE;
+ if(state != SSPIKY_WALKING)
+ return CONTINUE;
- if(fabsf(hit.normal.x) > .8) { // left or right
+ if(hit.left || hit.right) {
dir = dir == LEFT ? RIGHT : LEFT;
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(-physic.get_velocity_x());
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
void active_update(float elapsed_time);
}
}
-HitResponse
-Stalactite::collision_solid(GameObject& , const CollisionHit& hit)
+void
+Stalactite::collision_solid(const CollisionHit& hit)
{
if(state != STALACTITE_FALLING && state != STALACTITE_SQUISHED)
- return FORCE_MOVE;
+ return;
- if(hit.normal.y < .9) { // hit floor?
+ if(hit.bottom) { // hit floor?
state = STALACTITE_SQUISHED;
set_group(COLGROUP_MOVING_ONLY_STATIC);
physic.set_velocity_y(0);
if(!timer.started())
timer.start(SQUISH_TIME);
}
-
- return CONTINUE;
}
HitResponse
-Stalactite::collision_player(Player& player, const CollisionHit& )
+Stalactite::collision_player(Player& player)
{
if(state != STALACTITE_SQUISHED) {
player.kill(false);
void active_update(float elapsed_time);
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
- HitResponse collision_player(Player& player, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
+ HitResponse collision_player(Player& player);
void kill_fall();
void draw(DrawingContext& context);
return true;
}
-HitResponse
-Totem::collision_solid(GameObject& object, const CollisionHit& hit)
+void
+Totem::collision_solid(const CollisionHit& hit)
{
// if we are being carried around, pass event to bottom of stack and ignore it
if (carried_by) {
- carried_by->collision_solid(object, hit);
- return CONTINUE;
+ carried_by->collision_solid(hit);
+ return;
}
// If we hit something from above or below: stop moving in this direction
- if (hit.normal.y != 0) {
+ if (hit.top || hit.bottom) {
physic.set_velocity_y(0);
}
// If we are hit from the direction we are facing: turn around
- if ((hit.normal.x > .8) && (dir == LEFT)) {
+ if (hit.left && (dir == LEFT)) {
dir = RIGHT;
activate();
}
- if ((hit.normal.x < -.8) && (dir == RIGHT)) {
+ if (hit.right && (dir == RIGHT)) {
dir = LEFT;
activate();
}
-
- return CONTINUE;
}
HitResponse
}
// If we are hit from the direction we are facing: turn around
- if ((hit.normal.x > .8) && (dir == LEFT)) {
+ if(hit.left && (dir == LEFT)) {
dir = RIGHT;
activate();
}
- if ((hit.normal.x < -.8) && (dir == RIGHT)) {
+ if(hit.right && (dir == RIGHT)) {
dir = LEFT;
activate();
}
void activate();
void active_update(float elapsed_time);
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit);
virtual Totem* clone() const { return new Totem(*this); }
nearest->start_shaking();
}
-HitResponse
-Yeti::collision_solid(GameObject& , const CollisionHit& hit)
+void
+Yeti::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) {
+ if(hit.top || hit.bottom) {
// hit floor or roof
physic.set_velocity_y(0);
switch (state) {
case SQUISHED:
break;
}
- } else
- if(fabsf(hit.normal.x) > .5) {
+ } else if(hit.left || hit.right) {
// hit wall
jump_up();
}
-
- return CONTINUE;
}
IMPLEMENT_FACTORY(Yeti, "yeti")
void write(lisp::Writer& writer);
void activate();
void active_update(float elapsed_time);
- HitResponse collision_solid(GameObject& object, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
bool collision_squished(Player& player);
void kill_squished(Player& player);
void kill_fall();
dir = (dir == LEFT ? RIGHT : LEFT);
sprite->set_action(dir == LEFT ? "left" : "right");
physic.set_velocity_x(dir == LEFT ? -speed : speed);
+ } else {
+ assert(false);
}
}
}
}
-HitResponse
-Zeekling::collision_solid(GameObject& , const CollisionHit& hit)
+void
+Zeekling::collision_solid(const CollisionHit& hit)
{
- if(fabsf(hit.normal.y) > .5) {
+ if(hit.top || hit.bottom) {
onBumpVertical();
- } else {
+ } else if(hit.left || hit.right) {
onBumpHorizontal();
}
-
- return CONTINUE;
}
/**
void
Zeekling::active_update(float elapsed_time) {
- BadGuy::active_update(elapsed_time);
-
if (state == FLYING) {
if (should_we_dive()) {
state = DIVING;
physic.set_velocity_y(2*fabsf(physic.get_velocity_x()));
sprite->set_action(dir == LEFT ? "diving-left" : "diving-right");
}
+ BadGuy::active_update(elapsed_time);
return;
- }
-
- if (state == DIVING) {
+ } else if (state == DIVING) {
+ BadGuy::active_update(elapsed_time);
return;
- }
-
- if (state == CLIMBING) {
+ } else if (state == CLIMBING) {
// stop climbing when we're back at initial height
if (get_pos().y <= start_position.y) {
state = FLYING;
physic.set_velocity_y(0);
}
+ BadGuy::active_update(elapsed_time);
return;
+ } else {
+ assert(false);
}
-
}
IMPLEMENT_FACTORY(Zeekling, "zeekling")
void activate();
void write(lisp::Writer& writer);
- HitResponse collision_solid(GameObject& other, const CollisionHit& hit);
+ void collision_solid(const CollisionHit& hit);
void active_update(float elapsed_time);
virtual Zeekling* clone() const { return new Zeekling(*this); }
#include "math/aatriangle.hpp"
#include "math/rect.hpp"
#include "collision_hit.hpp"
+#include "log.hpp"
-static const float DELTA = .0001;
-
-bool
-Collision::intersects(const Rect& r1, const Rect& r2)
+namespace collision
{
- if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
- return false;
- if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
- return false;
- return true;
-}
-
-bool
-Collision::rectangle_rectangle(CollisionHit& hit, const Rect& r1,
- const Vector& movement, const Rect& r2)
+bool intersects(const Rect& r1, const Rect& r2)
{
if(r1.p2.x < r2.p1.x || r1.p1.x > r2.p2.x)
return false;
if(r1.p2.y < r2.p1.y || r1.p1.y > r2.p2.y)
return false;
- if(movement.x > DELTA) {
- hit.depth = r1.p2.x - r2.p1.x;
- hit.time = hit.depth / movement.x;
- hit.normal.x = -1;
- hit.normal.y = 0;
- } else if(movement.x < -DELTA) {
- hit.depth = r2.p2.x - r1.p1.x;
- hit.time = hit.depth / -movement.x;
- hit.normal.x = 1;
- hit.normal.y = 0;
- } else {
- if(movement.y > -DELTA && movement.y < DELTA) {
- hit.time = 0;
- hit.depth = 0;
- hit.normal.x = 1;
- hit.normal.y = 0;
- return true;
- }
- hit.time = FLT_MAX;
- }
-
- if(movement.y > DELTA) {
- float ydepth = r1.p2.y - r2.p1.y;
- float yt = ydepth / movement.y;
- if(yt < hit.time) {
- hit.depth = ydepth;
- hit.time = yt;
- hit.normal.x = 0;
- hit.normal.y = -1;
- }
- } else if(movement.y < -DELTA) {
- float ydepth = r2.p2.y - r1.p1.y;
- float yt = ydepth / -movement.y;
- if(yt < hit.time) {
- hit.depth = ydepth;
- hit.time = yt;
- hit.normal.x = 0;
- hit.normal.y = 1;
- }
- }
-
return true;
}
//---------------------------------------------------------------------------
-static void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
-{
- n = Vector(p2.y-p1.y, p1.x-p2.x);
- c = -(p2 * n);
- float nval = n.norm();
- n /= nval;
- c /= nval;
+namespace {
+ inline void makePlane(const Vector& p1, const Vector& p2, Vector& n, float& c)
+ {
+ n = Vector(p2.y-p1.y, p1.x-p2.x);
+ c = -(p2 * n);
+ float nval = n.norm();
+ n /= nval;
+ c /= nval;
+ }
+
+ static const float DELTA = .0001;
}
-bool
-Collision::rectangle_aatriangle(CollisionHit& hit, const Rect& rect,
- const Vector& movement, const AATriangle& triangle)
+bool rectangle_aatriangle(Constraints* constraints, const Rect& rect,
+ const AATriangle& triangle)
{
- if(!rectangle_rectangle(hit, rect, movement, (const Rect&) triangle))
+ if(!intersects(rect, (const Rect&) triangle))
return false;
Vector normal;
float c;
Vector p1;
- Vector tp1, tp2;
+ Rect area;
switch(triangle.dir & AATriangle::DEFORM_MASK) {
case 0:
- tp1 = triangle.p1;
- tp2 = triangle.p2;
+ area.p1 = triangle.p1;
+ area.p2 = triangle.p2;
break;
case AATriangle::DEFORM1:
- tp1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
- tp2 = triangle.p2;
+ area.p1 = Vector(triangle.p1.x, triangle.p1.y + triangle.get_height()/2);
+ area.p2 = triangle.p2;
break;
case AATriangle::DEFORM2:
- tp1 = triangle.p1;
- tp2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
+ area.p1 = triangle.p1;
+ area.p2 = Vector(triangle.p2.x, triangle.p1.y + triangle.get_height()/2);
break;
case AATriangle::DEFORM3:
- tp1 = triangle.p1;
- tp2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
+ area.p1 = triangle.p1;
+ area.p2 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p2.y);
break;
case AATriangle::DEFORM4:
- tp1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
- tp2 = triangle.p2;
+ area.p1 = Vector(triangle.p1.x + triangle.get_width()/2, triangle.p1.y);
+ area.p2 = triangle.p2;
break;
default:
assert(false);
switch(triangle.dir & AATriangle::DIRECTION_MASK) {
case AATriangle::SOUTHWEST:
p1 = Vector(rect.p1.x, rect.p2.y);
- makePlane(tp1, tp2, normal, c);
+ makePlane(area.p1, area.p2, normal, c);
break;
case AATriangle::NORTHEAST:
p1 = Vector(rect.p2.x, rect.p1.y);
- makePlane(tp2, tp1, normal, c);
+ makePlane(area.p2, area.p1, normal, c);
break;
case AATriangle::SOUTHEAST:
p1 = rect.p2;
- makePlane(Vector(tp1.x, tp2.y),
- Vector(tp2.x, tp1.y), normal, c);
+ makePlane(Vector(area.p1.x, area.p2.y),
+ Vector(area.p2.x, area.p1.y), normal, c);
break;
case AATriangle::NORTHWEST:
p1 = rect.p1;
- makePlane(Vector(tp2.x, tp1.y),
- Vector(tp1.x, tp2.y), normal, c);
+ makePlane(Vector(area.p2.x, area.p1.y),
+ Vector(area.p1.x, area.p2.y), normal, c);
break;
default:
assert(false);
float depth = n_p1 - c;
if(depth < 0)
return false;
- float time = depth / -(normal * movement);
- if(time < hit.time) {
- hit.depth = depth;
- hit.time = time;
- hit.normal = normal;
- }
+#if 0
+ std::cout << "R: " << rect << " Tri: " << triangle << "\n";
+ std::cout << "Norm: " << normal << " Depth: " << depth << "\n";
+#endif
+
+ Vector outvec = normal * (depth + 0.2);
+
+ const float RDELTA = 3;
+ if(p1.x < area.p1.x - RDELTA || p1.x > area.p2.x + RDELTA
+ || p1.y < area.p1.y - RDELTA || p1.y > area.p2.y + RDELTA) {
+ set_rectangle_rectangle_constraints(constraints, rect, area);
+ constraints->hit.left = false;
+ constraints->hit.right = false;
+ } else {
+ if(outvec.x < 0) {
+ constraints->right = rect.get_right() + outvec.x;
+ } else {
+ constraints->left = rect.get_left() + outvec.x;
+ }
+
+ if(outvec.y < 0) {
+ constraints->bottom = rect.get_bottom() + outvec.y;
+ constraints->hit.bottom = true;
+ } else {
+ constraints->top = rect.get_top() + outvec.y;
+ constraints->hit.top = true;
+ }
+ constraints->hit.slope_normal = normal;
+ }
+
return true;
}
+void set_rectangle_rectangle_constraints(Constraints* constraints,
+ const Rect& r1, const Rect& r2)
+{
+ float itop = r1.get_bottom() - r2.get_top();
+ float ibottom = r2.get_bottom() - r1.get_top();
+ float ileft = r1.get_right() - r2.get_left();
+ float iright = r2.get_right() - r1.get_left();
+
+ float vert_penetration = std::min(itop, ibottom);
+ float horiz_penetration = std::min(ileft, iright);
+ if(vert_penetration < horiz_penetration) {
+ if(itop < ibottom) {
+ constraints->bottom = std::min(constraints->bottom, r2.get_top());
+ constraints->hit.bottom = true;
+ } else {
+ constraints->top = std::max(constraints->top, r2.get_bottom());
+ constraints->hit.top = true;
+ }
+ } else {
+ if(ileft < iright) {
+ constraints->right = std::min(constraints->right, r2.get_left());
+ constraints->hit.right = true;
+ } else {
+ constraints->left = std::max(constraints->left, r2.get_right());
+ constraints->hit.left = true;
+ }
+ }
+}
+
+}
#ifndef __COLLISION_H__
#define __COLLISION_H__
+#include <float.h>
+#include "collision_hit.hpp"
+
class Vector;
class Rect;
class AATriangle;
-class CollisionHit;
-class Collision
+namespace collision
+{
+
+class Constraints
{
public:
- /** checks if 2 rectangle intersect each other */
- static bool intersects(const Rect& r1, const Rect& r2);
-
- /** does collision detection between 2 rectangles. Returns true in case of
- * collision and fills in the hit structure then.
- */
- static bool rectangle_rectangle(CollisionHit& hit, const Rect& r1,
- const Vector& movement, const Rect& r2);
-
- /** does collision detection between a rectangle and an axis aligned triangle
- * Returns true in case of a collision and fills in the hit structure then.
- */
- static bool rectangle_aatriangle(CollisionHit& hit, const Rect& rect,
- const Vector& movement, const AATriangle& triangle);
+ Constraints() {
+ left = -INFINITY;
+ right = INFINITY;
+ top = -INFINITY;
+ bottom = INFINITY;
+ }
+
+ bool has_constraints() const {
+ return left > -INFINITY || right < INFINITY
+ || top > -INFINITY || bottom < INFINITY;
+ }
+
+ float left;
+ float right;
+ float top;
+ float bottom;
+ Vector ground_movement;
+ CollisionHit hit;
};
+/** checks if 2 rectangle intersect each other */
+bool intersects(const Rect& r1, const Rect& r2);
+
+/** does collision detection between a rectangle and an axis aligned triangle
+ * Returns true in case of a collision and fills in the hit structure then.
+ */
+bool rectangle_aatriangle(Constraints* constraints, const Rect& rect,
+ const AATriangle& triangle);
+
+void set_rectangle_rectangle_constraints(Constraints* constraints,
+ const Rect& r1, const Rect& r2);
+
+}
+
#endif
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-// 02111-1307, USA.
-#include <config.h>
-
-#include <iostream>
-#include "collision_grid.hpp"
-#include "log.hpp"
-#include "collision.hpp"
-#include "sector.hpp"
-#include "collision_grid_iterator.hpp"
-
-static const float DELTA = .001;
-
-CollisionGrid::CollisionGrid(float newwidth, float newheight)
- : width(newwidth), height(newheight), cell_width(128), cell_height(128),
- iterator_timestamp(0)
-{
- cells_x = size_t(width / cell_width) + 1;
- cells_y = size_t(height / cell_height) + 1;
- grid.resize(cells_x * cells_y);
-}
-
-CollisionGrid::~CollisionGrid()
-{
- for(GridEntries::iterator i = grid.begin(); i != grid.end(); ++i) {
- GridEntry* entry = *i;
- while(entry) {
- GridEntry* nextentry = entry->next;
- delete entry;
- entry = nextentry;
- }
- }
-}
-
-void
-CollisionGrid::add_object(MovingObject* object)
-{
-#ifdef DEBUG
- // make sure the object isn't already in the grid
- for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
- ObjectWrapper* wrapper = *i;
- if(wrapper->object == object)
- assert(false);
- }
- assert(object != 0);
-#endif
-
- ObjectWrapper* wrapper = new ObjectWrapper;
- wrapper->object = object;
- wrapper->timestamp = 0;
- wrapper->dest = object->bbox;
- objects.push_back(wrapper);
- wrapper->id = objects.size()-1;
-
- const Rect& bbox = object->bbox;
- for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
- for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
- int gridx = int(x / cell_width);
- int gridy = int(y / cell_height);
- if(gridx < 0 || gridy < 0
- || gridx >= int(cells_x) || gridy >= int(cells_y)) {
- log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
- continue;
- }
- GridEntry* entry = new GridEntry;
- entry->object_wrapper = wrapper;
- entry->next = grid[gridy*cells_x + gridx];
- grid[gridy*cells_x + gridx] = entry;
- }
- }
-}
-
-void
-CollisionGrid::remove_object(MovingObject* object)
-{
- ObjectWrapper* wrapper = 0;
- for(Objects::iterator i = objects.begin(); i != objects.end(); ++i) {
- if((*i)->object == object) {
- wrapper = *i;
- objects.erase(i);
- break;
- }
- }
-#ifdef DEBUG
- assert(wrapper != 0);
-#else
- if(wrapper == 0) {
- log_warning << "Tried to remove nonexistant object" << std::endl;
- return;
- }
-#endif
-
- const Rect& bbox = wrapper->dest;
- for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) {
- for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) {
- int gridx = int(x / cell_width);
- int gridy = int(y / cell_height);
- if(gridx < 0 || gridy < 0
- || gridx >= int(cells_x) || gridy >= int(cells_y)) {
- log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
- continue;
- }
- remove_object_from_gridcell(gridy*cells_x + gridx, wrapper);
- }
- }
-
- delete wrapper;
-}
-
-void
-CollisionGrid::move_object(ObjectWrapper* wrapper)
-{
- // FIXME not optimal yet... should leave the gridcells untouched that don't
- // need to be changed.
- const Rect& obbox = wrapper->dest;
- for(float y = obbox.p1.y; y < obbox.p2.y; y += cell_height) {
- for(float x = obbox.p1.x; x < obbox.p2.x; x += cell_width) {
- int gridx = int(x / cell_width);
- int gridy = int(y / cell_height);
- if(gridx < 0 || gridy < 0 ||
- gridx >= int(cells_x) || gridy >= int(cells_y)) {
- log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
- continue;
- }
- remove_object_from_gridcell(gridy*cells_x + gridx, wrapper);
- }
- }
-
- const Rect& nbbox = wrapper->object->bbox;
- for(float y = nbbox.p1.y; y < nbbox.p2.y; y += cell_height) {
- for(float x = nbbox.p1.x; x < nbbox.p2.x; x += cell_width) {
- int gridx = int(x / cell_width);
- int gridy = int(y / cell_height);
- if(gridx < 0 || gridy < 0
- || gridx >= int(cells_x) || gridy >= int(cells_y)) {
- log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
- continue;
- }
-
- GridEntry* entry = new GridEntry;
- entry->object_wrapper = wrapper;
- entry->next = grid[gridy*cells_x + gridx];
- grid[gridy*cells_x + gridx] = entry;
- }
- }
-
- wrapper->dest = nbbox;
-}
-
-void
-CollisionGrid::check_collisions()
-{
- std::vector<ObjectWrapper*> moved_objects;
-
-#if 0
- CollisionGridIterator iter(*this, Sector::current()->get_active_region());
- while(ObjectWrapper* wrapper = iter.next_wrapper()) {
- MovingObject* object = wrapper->object;
- if(!object->is_valid())
- continue;
- if(object->get_group() == COLGROUP_DISABLED) {
- object->bbox.move(object->movement);
- object->movement = Vector(0, 0);
- moved_objects.push_back(wrapper);
- continue;
- }
-
- // hack for now...
- Sector::current()->collision_tilemap(object, 0);
-
- collide_object(wrapper);
-
- if(object->movement != Vector(0, 0)) {
- object->bbox.move(object->movement);
- object->movement = Vector(0, 0);
- moved_objects.push_back(wrapper);
- }
- }
-#endif
-
- for(std::vector<ObjectWrapper*>::iterator i = moved_objects.begin();
- i != moved_objects.end(); ++i) {
- move_object(*i);
- }
-}
-
-void
-CollisionGrid::collide_object(ObjectWrapper* wrapper)
-{
- iterator_timestamp++;
-
- const Rect& bbox = wrapper->object->bbox;
- for(float y = bbox.p1.y - cell_height; y < bbox.p2.y + cell_height; y += cell_height) {
- for(float x = bbox.p1.x - cell_width; x < bbox.p2.x + cell_width; x += cell_width) {
- int gridx = int(x / cell_width);
- int gridy = int(y / cell_height);
- if(gridx < 0 || gridy < 0
- || gridx >= int(cells_x) || gridy >= int(cells_y)) {
- //log_warning << "Object out of range: " << gridx << ", " << gridy << std::endl;
- continue;
- }
-
- for(GridEntry* entry = grid[gridy*cells_x + gridx]; entry;
- entry = entry->next) {
- ObjectWrapper* wrapper2 = entry->object_wrapper;
- // only check each object once (even if it is in multiple cells)
- if(wrapper2->timestamp == iterator_timestamp)
- continue;
- // don't collide with objects we already collided with
- if(wrapper2->id <= wrapper->id)
- continue;
-
- wrapper->timestamp = iterator_timestamp;
- collide_object_object(wrapper, wrapper2);
- }
- }
- }
-}
-
-void
-CollisionGrid::collide_object_object(ObjectWrapper* wrapper,
- ObjectWrapper* wrapper2)
-{
- CollisionHit hit;
- MovingObject* object1 = wrapper->object;
- MovingObject* object2 = wrapper2->object;
-
- Rect dest1 = object1->get_bbox();
- dest1.move(object1->get_movement());
- Rect dest2 = object2->get_bbox();
- dest2.move(object2->get_movement());
-
- Vector movement = object1->get_movement() - object2->get_movement();
- if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
- HitResponse response1 = object1->collision(*object2, hit);
- hit.normal *= -1;
- HitResponse response2 = object2->collision(*object1, hit);
-
- if(response1 != CONTINUE) {
- if(response1 == ABORT_MOVE)
- object1->movement = Vector(0, 0);
- if(response2 == CONTINUE)
- object2->movement += hit.normal * (hit.depth + DELTA);
- } else if(response2 != CONTINUE) {
- if(response2 == ABORT_MOVE)
- object2->movement = Vector(0, 0);
- if(response1 == CONTINUE)
- object1->movement += -hit.normal * (hit.depth + DELTA);
- } else {
- object1->movement += -hit.normal * (hit.depth/2 + DELTA);
- object2->movement += hit.normal * (hit.depth/2 + DELTA);
- }
- }
-}
-
-void
-CollisionGrid::remove_object_from_gridcell(int gridcell, ObjectWrapper* wrapper)
-{
- GridEntry* lastentry = 0;
- GridEntry* entry = grid[gridcell];
-
- while(entry) {
- if(entry->object_wrapper == wrapper) {
- if(lastentry == 0) {
- grid[gridcell] = entry->next;
- } else {
- lastentry->next = entry->next;
- }
- delete entry;
- return;
- }
-
- lastentry = entry;
- entry = entry->next;
- };
-
- log_warning << "Couldn't find object in cell" << std::endl;
-}
-
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-// 02111-1307, USA.
-#ifndef __COLLISION_GRID_H__
-#define __COLLISION_GRID_H__
-
-#include <vector>
-#include "moving_object.hpp"
-
-class CollisionGridIterator;
-
-/**
- * A rectangular grid to keep track of all moving game objects. It allows fast
- * queries for all objects in a rectangular area.
- */
-class CollisionGrid
-{
-public:
- CollisionGrid(float width, float height);
- ~CollisionGrid();
-
- void add_object(MovingObject* object);
- void remove_object(MovingObject* object);
-
- void check_collisions();
-
-private:
- friend class CollisionGridIterator;
-
- struct ObjectWrapper
- {
- MovingObject* object;
- Rect dest;
- /** (pseudo) timestamp. When reading from the grid the timestamp is
- * changed so that you can easily avoid reading an object multiple times
- * when it is in several cells that you check.
- */
- int timestamp;
- /// index in the objects vector
- int id;
- };
-
- /** Element for the single linked list in each grid cell */
- struct GridEntry
- {
- GridEntry* next;
- ObjectWrapper* object_wrapper;
- };
-
- void remove_object_from_gridcell(int gridcell, ObjectWrapper* wrapper);
- void collide_object(ObjectWrapper* wrapper);
- void collide_object_object(ObjectWrapper* wrapper, ObjectWrapper* wrapper2);
- void move_object(ObjectWrapper* wrapper);
-
- typedef std::vector<GridEntry*> GridEntries;
- GridEntries grid;
- typedef std::vector<ObjectWrapper*> Objects;
- Objects objects;
- size_t cells_x, cells_y;
- float width;
- float height;
- float cell_width;
- float cell_height;
- int iterator_timestamp;
-};
-
-extern CollisionGrid* bla;
-
-#endif
-
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-// 02111-1307, USA.
-#ifndef __COLLISION_GRID_ITERATOR_H__
-#define __COLLISION_GRID_ITERATOR_H__
-
-#include "math/rect.hpp"
-#include "log.hpp"
-
-class CollisionGrid;
-
-class CollisionGridIterator
-{
-public:
- CollisionGridIterator(CollisionGrid& newgrid, const Rect& bbox)
- : grid(newgrid)
- {
- start_x = int(bbox.p1.x / grid.cell_width) - 2;
- if(start_x < 0)
- start_x = 0;
- x = start_x;
-
- y = int(bbox.p1.y / grid.cell_height) - 2;
- if(y < 0)
- y = 0;
-
- end_x = int(bbox.p2.x / grid.cell_width) + 2;
- if(end_x > (int) grid.cells_x)
- end_x = grid.cells_x;
-
- end_y = int(bbox.p2.y / grid.cell_height) + 2;
- if(end_y > (int) grid.cells_y)
- end_y = grid.cells_y;
-
- timestamp = grid.iterator_timestamp++;
- entry = 0;
-
- if(start_x >= end_x) {
- log_debug << "bad region" << std::endl;
- y = 0;
- end_y = 0;
- return;
- }
- }
-
- MovingObject* next()
- {
- CollisionGrid::ObjectWrapper* wrapper = next_wrapper();
- if(wrapper == 0)
- return 0;
-
- return wrapper->object;
- }
-
-private:
- friend class CollisionGrid;
-
- CollisionGrid::ObjectWrapper* next_wrapper()
- {
- CollisionGrid::ObjectWrapper* wrapper;
-
- do {
- while(entry == 0) {
- if(y >= end_y)
- return 0;
-
- entry = grid.grid[y*grid.cells_x + x];
- x++;
- if(x >= end_x) {
- x = start_x;
- y++;
- }
- }
-
- wrapper = entry->object_wrapper;
- entry = entry->next;
- } while(wrapper->timestamp == timestamp);
-
- wrapper->timestamp = timestamp;
-
- return wrapper;
- }
-
- CollisionGrid& grid;
- CollisionGrid::GridEntry* entry;
- int x, y;
- int start_x, end_x, end_y;
- int timestamp;
-};
-
-#endif
-
#ifndef SUPERTUX_COLLISION_HIT_H
#define SUPERTUX_COLLISION_HIT_H
+#include <float.h>
+#include <math.h>
#include "math/vector.hpp"
/**
/// do the move ignoring the collision
FORCE_MOVE,
/// passes movement to collided object
- PASS_MOVEMENT
+ PASS_MOVEMENT,
+
+ /// the object should not appear solid
+ PASSTHROUGH,
+ /// the object should appear solid
+ SOLID,
};
/**
class CollisionHit
{
public:
- /// penetration depth
- float depth;
- /// time of the collision (between 0 and 1 in relation to movement)
- float time;
- /// The normal of the side we collided with
- Vector normal;
+ CollisionHit() {
+ left = false;
+ right = false;
+ top = false;
+ bottom = false;
+ crush = false;
+ }
+
+ bool left, right;
+ bool top, bottom;
+ bool crush;
+
+ Vector slope_normal;
};
#endif
#include "log.hpp"
#include "math/vector.hpp"
+#include "math/rect.hpp"
std::ostream& operator<<(std::ostream& out, const Vector& vector)
{
- out << '[' << vector.x << ',' << vector.y << ']';
- return out;
+ out << '[' << vector.x << ',' << vector.y << ']';
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Rect& rect)
+{
+ out << "[" << rect.get_left() << "," << rect.get_top() << " "
+ << rect.get_right() << "," << rect.get_bottom() << "]";
+ return out;
}
class Vector;
std::ostream& operator<< (std::ostream& str, const Vector& vector);
+class Rect;
+std::ostream& operator<< (std::ostream& str, const Rect& rect);
#endif
p2 += v;
}
- bool inside(const Vector& v) const
+ bool contains(const Vector& v) const
{
return v.x >= p1.x && v.y >= p1.y && v.x < p2.x && v.y < p2.y;
}
- bool inside(const Rect& other) const
+ bool contains(const Rect& other) const
{
if(p1.x >= other.p2.x || other.p1.x >= p2.x)
return false;
public:
MovingObject();
virtual ~MovingObject();
-
- /** this function is called when the object collided with any other object
- */
- virtual HitResponse collision(GameObject& other,
- const CollisionHit& hit) = 0;
+
+ /** this function is called when the object collided with something solid */
+ virtual void collision_solid(const CollisionHit& hit)
+ {
+ (void) hit;
+ }
+ /** this function is called when the object collided with any other object */
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit) = 0;
/** called when tiles with special attributes have been touched */
virtual void collision_tile(uint32_t tile_attributes)
{
}
HitResponse
-Block::collision(GameObject& other, const CollisionHit& hitdata)
+Block::collision(GameObject& other, const CollisionHit& )
{
Player* player = dynamic_cast<Player*> (&other);
if(player) {
- // collided from below?
- if(hitdata.normal.x == 0 && hitdata.normal.y < 0) {
+ if(player->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) {
hit(*player);
}
}
}
}
- return FORCE_MOVE;
+ return SOLID;
}
void
sector->add_object(riser);
} else {
SpecialRiser* riser = new SpecialRiser(
- get_pos(), new Flower(Flower::FIREFLOWER));
+ get_pos(), new Flower(FIRE_BONUS));
sector->add_object(riser);
}
sound_manager->play("sounds/upgrade.wav");
sector->add_object(riser);
} else {
SpecialRiser* riser = new SpecialRiser(
- get_pos(), new Flower(Flower::ICEFLOWER));
+ get_pos(), new Flower(ICE_BONUS));
sector->add_object(riser);
}
sound_manager->play("sounds/upgrade.wav");
sector->add_object(riser);
sound_manager->play("sounds/upgrade.wav");
break;
-
- //default:
- //assert(false);
}
start_bounce();
#include "badguy/badguy.hpp"
#include "main.hpp"
-static const float BULLET_XM = 600;
-static const float BULLET_STARTING_YM = 0;
+namespace {
+ const float BULLET_XM = 600;
+ const float BULLET_STARTING_YM = 0;
+}
-Bullet::Bullet(const Vector& pos, float xm, int dir, int kind_)
- : kind(kind_), life_count(3), sprite(0)
+Bullet::Bullet(const Vector& pos, float xm, int dir)
+ : life_count(3)
{
+ sprite.reset(sprite_manager->create("images/objects/bullets/firebullet.sprite"));
+
bbox.set_pos(pos);
- bbox.set_size(4, 4);
+ bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
float speed = dir == RIGHT ? BULLET_XM : -BULLET_XM;
physic.set_velocity_x(speed + xm);
physic.set_velocity_y(BULLET_STARTING_YM);
-
- if (kind == ICE_BULLET) {
- life_count = 6; //ice-bullets get "extra lives" for bumping off walls
- sprite = sprite_manager->create("images/objects/bullets/icebullet.sprite");
- } else if(kind == FIRE_BULLET) {
- sprite = sprite_manager->create("images/objects/bullets/firebullet.sprite");
- }
}
Bullet::~Bullet()
{
- delete sprite;
}
void
Bullet::update(float elapsed_time)
{
- if(kind == FIRE_BULLET) {
- // @not completely framerate independant :-/
- physic.set_velocity_y(physic.get_velocity_y() + 50 * elapsed_time);
- }
+ // @not completely framerate independant :-/
+ physic.set_velocity_y(physic.get_velocity_y() + 50 * elapsed_time);
+
if(physic.get_velocity_y() > 900)
physic.set_velocity_y(900);
else if(physic.get_velocity_y() < -900)
sprite->draw(context, get_pos(), LAYER_OBJECTS);
}
-HitResponse
-Bullet::collision(GameObject& other, const CollisionHit& hit)
+void
+Bullet::collision_solid(const CollisionHit& hit)
{
- if(other.get_flags() & FLAG_SOLID) {
- if(fabsf(hit.normal.y) > .5) { // roof or floor bump
- physic.set_velocity_y(-physic.get_velocity_y());
- life_count -= 1;
- } else { // bumped left or right
- if(kind == FIRE_BULLET)
- remove_me();
- else
- physic.set_velocity_x(-physic.get_velocity_x());
- }
-
- return CONTINUE;
- }
-
- // hit a Badguy
- BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
- if(badguy) {
+ if(hit.top || hit.bottom) {
+ physic.set_velocity_y(-physic.get_velocity_y());
+ life_count--;
+ } else if(hit.left || hit.right) {
remove_me();
- return FORCE_MOVE;
}
-
+}
+
+HitResponse
+Bullet::collision(GameObject& , const CollisionHit& )
+{
return FORCE_MOVE;
}
#include "physic.hpp"
#include "sprite/sprite.hpp"
-enum BulletsKind {
- FIRE_BULLET,
- ICE_BULLET
-};
-
class Bullet : public MovingObject
{
public:
- Bullet(const Vector& pos, float xm, int dir, int kind);
+ Bullet(const Vector& pos, float xm, int dir);
~Bullet();
void update(float elapsed_time);
void draw(DrawingContext& context);
+ void collision_solid(const CollisionHit& hit);
HitResponse collision(GameObject& other, const CollisionHit& hit);
- int kind;
-
private:
int life_count;
Physic physic;
- Sprite* sprite;
+ std::auto_ptr<Sprite> sprite;
};
#endif
}
HitResponse
-Candle::collision(GameObject& , const CollisionHit& )
+Candle::collision(GameObject&, const CollisionHit& )
{
return FORCE_MOVE;
}
Coin::Coin(const Vector& pos)
: MovingSprite(pos, "images/objects/coin/coin.sprite", LAYER_TILES, COLGROUP_TOUCHABLE)
{
+ sound_manager->preload("sounds/coin.wav");
}
Coin::Coin(const lisp::Lisp& reader)
: MovingSprite(reader, "images/objects/coin/coin.sprite", LAYER_TILES, COLGROUP_TOUCHABLE)
{
+ sound_manager->preload("sounds/coin.wav");
}
void
public:
Coin(const Vector& pos);
Coin(const lisp::Lisp& reader);
- virtual Coin* clone() const { return new Coin(*this); }
HitResponse collision(GameObject& other, const CollisionHit& hit);
#include <config.h>
#include <math.h>
+#include <assert.h>
#include "flower.hpp"
#include "resources.hpp"
#include "camera.hpp"
#include "audio/sound_manager.hpp"
#include "sprite/sprite_manager.hpp"
-Flower::Flower(Type _type)
+Flower::Flower(BonusType _type)
: type(_type)
{
bbox.set_size(32, 32);
- if(_type == FIREFLOWER){
+ if(type == FIRE_BONUS) {
sprite = sprite_manager->create("images/powerups/fireflower/fireflower.sprite");
sound_manager->preload("sounds/fire-flower.wav");
}
- else
+ else if(type == ICE_BONUS) {
sprite = sprite_manager->create("images/powerups/iceflower/iceflower.sprite");
+ } else {
+ assert(false);
+ }
set_group(COLGROUP_TOUCHABLE);
}
if(!player)
return ABORT_MOVE;
- if(type == FIREFLOWER)
- player->add_bonus(FIRE_BONUS, true);
- else
- player->add_bonus(ICE_BONUS, true);
+ if(!player->add_bonus(type, true))
+ return FORCE_MOVE;
sound_manager->play("sounds/fire-flower.wav");
remove_me();
#include "moving_object.hpp"
#include "sprite/sprite.hpp"
#include "physic.hpp"
+#include "player_status.hpp"
class Flower : public MovingObject
{
public:
- enum Type {
- FIREFLOWER, ICEFLOWER
- };
- Flower(Type type);
+ Flower(BonusType type);
~Flower();
virtual void update(float elapsed_time);
virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
private:
- Type type;
+ BonusType type;
Sprite* sprite;
};
movement = physic.get_movement(elapsed_time);
}
-HitResponse
-GrowUp::collision(GameObject& other, const CollisionHit& hit)
+void
+GrowUp::collision_solid(const CollisionHit& hit)
{
- if(other.get_flags() & FLAG_SOLID) {
- if(fabsf(hit.normal.y) > .5) { // roof or ground
- physic.set_velocity_y(0);
- } else { // bumped left or right
- physic.set_velocity_x(-physic.get_velocity_x());
- }
+ if(hit.top || hit.bottom)
+ physic.set_velocity_y(0);
+ if(hit.left || hit.right)
+ physic.set_velocity_x(-physic.get_velocity_x());
+}
- return CONTINUE;
- }
-
+HitResponse
+GrowUp::collision(GameObject& other, const CollisionHit& )
+{
Player* player = dynamic_cast<Player*>(&other);
if(player != 0) {
- player->add_bonus(GROWUP_BONUS, true);
+ if(!player->add_bonus(GROWUP_BONUS, true))
+ return FORCE_MOVE;
+
sound_manager->play("sounds/grow.wav");
remove_me();
virtual GrowUp* clone() const { return new GrowUp(*this); }
virtual void update(float elapsed_time);
+ virtual void collision_solid(const CollisionHit& hit);
virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
private:
-// $Id: hurtingplatform.cpp 3506 2006-05-12 01:41:09Z sommer $
+// $Id$
//
// SuperTux - Hurting Platform
// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
HurtingPlatform::HurtingPlatform(const lisp::Lisp& reader)
: Platform(reader)
{
+ set_group(COLGROUP_TOUCHABLE);
}
HitResponse
-HurtingPlatform::collision(GameObject& other, const CollisionHit& hit)
+HurtingPlatform::collision(GameObject& other, const CollisionHit& )
{
Player* player = dynamic_cast<Player*>(&other);
if (player) {
if (badguy) {
badguy->kill_fall();
}
- return Platform::collision(other, hit);
+
+ return FORCE_MOVE;
}
IMPLEMENT_FACTORY(HurtingPlatform, "hurting_platform");
#include "video/drawing_context.hpp"
#include "audio/sound_manager.hpp"
#include "object_factory.hpp"
+#include "object/player.hpp"
InvisibleBlock::InvisibleBlock(const Vector& pos)
: Block(sprite_manager->create("images/objects/bonus_block/invisibleblock.sprite")), visible(false)
{
bbox.set_pos(pos);
- flags &= ~FLAG_SOLID;
- set_group(COLGROUP_MOVING);
+ sound_manager->preload("sounds/brick.wav");
sound_manager->preload("sounds/brick.wav");
}
sprite->draw(context, get_pos(), LAYER_OBJECTS);
}
+HitResponse
+InvisibleBlock::collision(GameObject& other, const CollisionHit& hit)
+{
+ if(!visible) {
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player) {
+ if(player->get_movement().y > 0 ||
+ player->get_bbox().get_top() <= get_bbox().get_bottom() - 7.0) {
+ return PASSTHROUGH;
+ }
+ }
+ }
+
+ return Block::collision(other, hit);
+}
+
void
InvisibleBlock::hit(Player& )
{
+ sound_manager->play("sounds/brick.wav");
+
if(visible)
return;
sprite->set_action("empty");
- sound_manager->play("sounds/brick.wav");
start_bounce();
flags |= FLAG_SOLID;
set_group(COLGROUP_STATIC);
InvisibleBlock(const Vector& pos);
virtual void draw(DrawingContext& context);
+ virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
protected:
virtual void hit(Player& player);
int
ParticleSystem_Interactive::collision(Particle* object, Vector movement)
{
+ using namespace collision;
+
TileMap* solids = Sector::current()->solids;
// calculate rectangle where the object will move
float x1, x2;
int max_x = int(x2+1);
int max_y = int(y2+1);
- CollisionHit temphit, hit;
Rect dest = Rect(x1, y1, x2, y2);
dest.move(movement);
- hit.time = -1; // represents an invalid value
+ Constraints constraints;
for(int x = starttilex; x*32 < max_x; ++x) {
for(int y = starttiley; y*32 < max_y; ++y) {
const Tile* tile = solids->get_tile(x, y);
if(!tile)
continue;
// skip non-solid tiles, except water
- if (tile->getAttributes() & Tile::WATER)
- water = true;
- if(!water && !(tile->getAttributes() & Tile::SOLID))
+ if(! (tile->getAttributes() & (Tile::WATER | Tile::SOLID)))
continue;
if(tile->getAttributes() & Tile::SLOPE) { // slope tile
Vector p2((x+1)*32, (y+1)*32);
triangle = AATriangle(p1, p2, tile->getData());
- if(Collision::rectangle_aatriangle(temphit, dest, movement,
- triangle)) {
- if(temphit.time > hit.time)
- hit = temphit;
+ if(rectangle_aatriangle(&constraints, dest, triangle)) {
+ if(tile->getAttributes() & Tile::WATER)
+ water = true;
}
} else { // normal rectangular tile
Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
- if(Collision::rectangle_rectangle(temphit, dest,
- movement, rect)) {
- if(temphit.time > hit.time)
- hit = temphit;
+ if(intersects(dest, rect)) {
+ if(tile->getAttributes() & Tile::WATER)
+ water = true;
+ set_rectangle_rectangle_constraints(&constraints, dest, rect);
}
}
}
}
+
+ // TODO don't use magic numbers here...
// did we collide at all?
- if(hit.time < 0) {
- return -1; //no collision
- }
- else {
- if (water)
- return 0; //collision with water tile - don't draw splash
- else {
- if ((hit.normal.x == 1) && (hit.normal.y == 0))
- return 2; //collision from right
- else return 1; //collision from above
+ if(!constraints.has_constraints())
+ return -1;
+
+ const CollisionHit& hit = constraints.hit;
+ if (water) {
+ return 0; //collision with water tile - don't draw splash
+ } else {
+ if (hit.right || hit.left) {
+ return 2; //collision from right
+ } else {
+ return 1; //collision from above
}
}
+
+ return 0;
}
RainParticleSystem::RainParticleSystem()
void CometParticleSystem::update(float elapsed_time)
{
+ (void) elapsed_time;
+#if 0
std::vector<Particle*>::iterator i;
for(
i = particles.begin(); i != particles.end(); ++i) {
particle->pos.y = new_y;
}
}
+#endif
}
walker->path = &*path;
}
-//TODO: Squish Tux when standing between platform and solid tile/object
-// Improve collision handling
-// Move all MovingObjects lying on the platform instead of only the player
HitResponse
-Platform::collision(GameObject& other, const CollisionHit& hit)
+Platform::collision(GameObject& , const CollisionHit& )
{
- if (typeid(other) == typeid(Player)) {
- if (hit.normal.y >= 0.9) {
- //Tux is standing on the platform
- //Player* player = (Player*) &other;
- //player->add_velocity(speed * 1.5);
- return PASS_MOVEMENT;
- }
- }
- if(other.get_flags() & FLAG_SOLID) {
- //Collision with a solid tile
- return ABORT_MOVE;
- }
return FORCE_MOVE;
}
on_ground_flag = false;
grabbed_object = NULL;
- floor_normal = Vector(0,-1);
-
physic.reset();
}
Rect bbox2 = bbox;
bbox2.move(Vector(0, bbox.get_height() - new_height));
bbox2.set_height(new_height);
- if (!Sector::current()->is_free_space(bbox2)) return false;
+ if (!Sector::current()->is_free_space(bbox2))
+ return false;
+
// adjust bbox accordingly
// note that we use members of moving_object for this, so we can run this during CD, too
set_pos(bbox2.p1);
set_width(31.8);
}
- // on downward slopes, adjust vertical velocity to match slope angle
+ // on downward slopes, adjust vertical velocity so tux walks smoothly down
if (on_ground()) {
- if (floor_normal.y != 0) {
- if ((floor_normal.x * physic.get_velocity_x()) > 0) {
- // we overdo it a little, just to be on the safe side
- physic.set_velocity_y(-physic.get_velocity_x() * (floor_normal.x / floor_normal.y) * 2);
+ if(floor_normal.y != 0) {
+ if ((floor_normal.x * physic.get_velocity_x()) >= 0) {
+ physic.set_velocity_y(250);
}
}
}
}
/* When Down is not held anymore, disable butt jump */
- if(butt_jump && !controller->hold(Controller::DOWN)) butt_jump = false;
-
- /** jumping is only allowed if we're about to touch ground soon and if the
- * button has been up in between the last jump
- */
- // FIXME
-#if 0
- if ( (issolid(get_pos().x + bbox.get_width() / 2,
- get_pos().y + bbox.get_height() + 64) ||
- issolid(get_pos().x + 1, get_pos().y + bbox.get_height() + 64) ||
- issolid(get_pos().x + bbox.get_width() - 1,
- get_pos().y + bbox.get_height() + 64))
- && jumping == false
- && can_jump == false
- && input.jump && !input.old_jump)
- {
- can_jump = true;
- }
-#endif
+ if(butt_jump && !controller->hold(Controller::DOWN))
+ butt_jump = false;
}
void
player_status->add_coins(count);
}
-void
+bool
Player::add_bonus(const std::string& bonustype)
{
+ BonusType type = NO_BONUS;
+
if(bonustype == "grow") {
- add_bonus(GROWUP_BONUS);
+ type = GROWUP_BONUS;
} else if(bonustype == "fireflower") {
- add_bonus(FIRE_BONUS);
+ type = FIRE_BONUS;
} else if(bonustype == "iceflower") {
- add_bonus(ICE_BONUS);
+ type = ICE_BONUS;
} else if(bonustype == "none") {
- add_bonus(NO_BONUS);
+ type = NO_BONUS;
} else {
std::ostringstream msg;
msg << "Unknown bonus type " << bonustype;
throw std::runtime_error(msg.str());
}
+
+ return add_bonus(type);
}
-void
+bool
Player::add_bonus(BonusType type, bool animate)
{
// always ignore NO_BONUS
if (type == NO_BONUS) {
- return;
+ return true;
}
// ignore GROWUP_BONUS if we're already big
if (type == GROWUP_BONUS) {
- if (player_status->bonus == GROWUP_BONUS) return;
- if (player_status->bonus == FIRE_BONUS) return;
- if (player_status->bonus == ICE_BONUS) return;
+ if (player_status->bonus == GROWUP_BONUS)
+ return true;
+ if (player_status->bonus == FIRE_BONUS)
+ return true;
+ if (player_status->bonus == ICE_BONUS)
+ return true;
}
- set_bonus(type, animate);
+ return set_bonus(type, animate);
}
-void
+bool
Player::set_bonus(BonusType type, bool animate)
{
if(player_status->bonus == NO_BONUS) {
- if (!adjust_height(62.8)) return;
+ if (!adjust_height(62.8)) {
+ printf("can't adjust\n");
+ return false;
+ }
if(animate)
growing_timer.start(GROWING_TIME);
}
if (type == ICE_BONUS) player_status->max_ice_bullets++;
player_status->bonus = type;
+ return true;
}
void
kill(false);
}
+void
+Player::collision_solid(const CollisionHit& hit)
+{
+ if(hit.bottom) {
+ if(physic.get_velocity_y() > 0)
+ physic.set_velocity_y(0);
+
+ on_ground_flag = true;
+ floor_normal = hit.slope_normal;
+ } else if(hit.top) {
+ if(physic.get_velocity_y() < 0)
+ physic.set_velocity_y(.2);
+ }
+
+ if(hit.left || hit.right) {
+ physic.set_velocity_x(0);
+ }
+
+ // crushed?
+ if(hit.crush) {
+ if(hit.left || hit.right) {
+ kill(true);
+ } else if(hit.top || hit.bottom) {
+ kill(false);
+ }
+ }
+}
+
HitResponse
-Player::collision(GameObject& other, const CollisionHit& hit)
+Player::collision(GameObject& other, const CollisionHit& )
{
Bullet* bullet = dynamic_cast<Bullet*> (&other);
if(bullet) {
assert(portable != NULL);
if(portable && grabbed_object == NULL
&& controller->hold(Controller::ACTION)
- && fabsf(hit.normal.x) > .9) {
+ /*&& fabsf(hit.normal.x) > .9*/) {
grabbed_object = portable;
grabbed_object->grab(*this, get_pos(), dir);
return CONTINUE;
}
}
-
- if(other.get_flags() & FLAG_SOLID) {
- /*
- printf("Col %p: HN: %3.1f %3.1f D %.1f P: %3.1f %3.1f M: %3.1f %3.1f\n",
- &other,
- hit.normal.x, hit.normal.y, hit.depth,
- get_pos().x, get_pos().y,
- movement.x, movement.y);
- */
-
- if(hit.normal.y < 0) { // landed on floor?
- if(physic.get_velocity_y() > 0)
- physic.set_velocity_y(0);
-
- on_ground_flag = true;
-
- // remember normal of this tile
- if (hit.normal.y > -0.9) {
- floor_normal.x = hit.normal.x;
- floor_normal.y = hit.normal.y;
- } else {
- // slowly adjust to unisolid tiles.
- // Necessary because our bounding box sometimes reaches through slopes and thus hits unisolid tiles
- floor_normal.x = (floor_normal.x * 0.9) + (hit.normal.x * 0.1);
- floor_normal.y = (floor_normal.y * 0.9) + (hit.normal.y * 0.1);
- }
-
- // hack platforms so that we stand normally on them when going down...
- Platform* platform = dynamic_cast<Platform*> (&other);
- if(platform != NULL) {
- if(platform->get_speed().y > 0)
- physic.set_velocity_y(platform->get_speed().y);
- //physic.set_velocity_x(platform->get_speed().x);
- }
- } else if(hit.normal.y > 0) { // bumped against the roof
- physic.set_velocity_y(-.1);
-
- // hack platform so that we are not glued to it from below
- Platform* platform = dynamic_cast<Platform*> (&other);
- if(platform != NULL) {
- physic.set_velocity_y(platform->get_speed().y);
- }
- }
-
- if(fabsf(hit.normal.x) > .9) { // hit on the side?
- physic.set_velocity_x(0);
- }
-
- MovingObject* omov = dynamic_cast<MovingObject*> (&other);
- if(omov != NULL) {
- Vector mov = movement - omov->get_movement();
- /*
- printf("W %p - HITN: %3.1f %3.1f D:%3.1f TM: %3.1f %3.1f TD: %3.1f %3.1f PM: %3.2f %3.1f\n",
- omov,
- hit.normal.x, hit.normal.y,
- hit.depth,
- movement.x, movement.y,
- dest.p1.x, dest.p1.y,
- omov->get_movement().x, omov->get_movement().y);
- */
- }
-
- return CONTINUE;
- }
#ifdef DEBUG
assert(dynamic_cast<MovingObject*> (&other) != NULL);
virtual void update(float elapsed_time);
virtual void draw(DrawingContext& context);
+ virtual void collision_solid(const CollisionHit& hit);
virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
virtual void collision_tile(uint32_t tile_attributes);
void check_bounds(Camera* camera);
void move(const Vector& vector);
- virtual void add_bonus(const std::string& bonus);
+ virtual bool add_bonus(const std::string& bonus);
virtual void add_coins(int count);
- void add_bonus(BonusType type, bool animate = false); /**< picks up a bonus, taking care not to pick up lesser bonus items than we already have */
- void set_bonus(BonusType type, bool animate = false); /**< like add_bonus, but can also downgrade the bonus items carried */
+
+ /**
+ * picks up a bonus, taking care not to pick up lesser bonus items than we already have
+ *
+ * @returns true if the bonus has been set (or was already good enough)
+ * false if the bonus could not be set (for example no space for big tux)
+ */
+ bool add_bonus(BonusType type, bool animate = false);
+ /**
+ * like add_bonus, but can also downgrade the bonus items carried
+ */
+ bool set_bonus(BonusType type, bool animate = false);
+
PlayerStatus* get_status()
{
return player_status;
Sprite* smalltux_gameover;
Sprite* smalltux_star;
Sprite* bigtux_star;
+
Vector floor_normal;
bool ghost_mode; /**< indicates if Tux should float around and through solid objects */
#include "log.hpp"
PowerUp::PowerUp(const lisp::Lisp& lisp)
- : MovingSprite(lisp, LAYER_OBJECTS, COLGROUP_MOVING)
+ : MovingSprite(lisp, LAYER_OBJECTS, COLGROUP_MOVING)
{
lisp.get("script", script);
no_physics = false;
sound_manager->preload("sounds/fire-flower.wav");
}
-HitResponse
-PowerUp::collision(GameObject& other, const CollisionHit& hit)
+void
+PowerUp::collision_solid(const CollisionHit& hit)
{
- if(other.get_flags() & FLAG_SOLID) {
- if(fabsf(hit.normal.y) > .5) { // roof or ground
- physic.set_velocity_y(0);
- } else { // bumped left or right
- physic.set_velocity_x(-physic.get_velocity_x());
- }
-
- return CONTINUE;
+ if(hit.bottom) {
+ physic.set_velocity_y(0);
}
-
+ if(hit.right || hit.left) {
+ physic.set_velocity_x(-physic.get_velocity_x());
+ }
+}
+
+HitResponse
+PowerUp::collision(GameObject& other, const CollisionHit&)
+{
Player* player = dynamic_cast<Player*>(&other);
if(player == 0)
return FORCE_MOVE;
- remove_me();
-
if (script != "") {
std::istringstream stream(script);
Sector::current()->run_script(stream, "powerup-script");
+ remove_me();
return ABORT_MOVE;
}
// some defaults if no script has been set
if (sprite_name == "images/powerups/egg/egg.sprite") {
- player->add_bonus(GROWUP_BONUS, true);
+ if(!player->add_bonus(GROWUP_BONUS, true))
+ return FORCE_MOVE;
sound_manager->play("sounds/grow.wav");
} else if (sprite_name == "images/powerups/fireflower/fireflower.sprite") {
- player->add_bonus(FIRE_BONUS, true);
+ if(!player->add_bonus(FIRE_BONUS, true))
+ return FORCE_MOVE;
sound_manager->play("sounds/fire-flower.wav");
} else if (sprite_name == "images/powerups/star/star.sprite") {
player->make_invincible();
} else if (sprite_name == "images/powerups/1up/1up.sprite") {
player->get_status()->add_coins(100);
}
+
+ remove_me();
return ABORT_MOVE;
}
{
public:
PowerUp(const lisp::Lisp& lisp);
- virtual PowerUp* clone() const { return new PowerUp(*this); }
virtual void update(float elapsed_time);
+ virtual void collision_solid(const CollisionHit& hit);
virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
private:
#include "object_factory.hpp"
Rock::Rock(const lisp::Lisp& reader)
- : MovingSprite(reader, "images/objects/rock/rock.sprite", LAYER_OBJECTS+1, COLGROUP_MOVING)
+ : MovingSprite(reader, "images/objects/rock/rock.sprite", LAYER_OBJECTS+1, COLGROUP_STATIC)
{
grabbed = false;
flags |= FLAG_SOLID | FLAG_PORTABLE;
{
if(!grabbed) {
flags |= FLAG_SOLID;
- set_group(COLGROUP_MOVING);
+ set_group(COLGROUP_STATIC);
movement = physic.get_movement(elapsed_time);
} else {
physic.set_velocity(0, 0);
*/
}
+void
+Rock::collision_solid(const CollisionHit& )
+{
+ physic.set_velocity(0, 0);
+}
+
HitResponse
-Rock::collision(GameObject& object, const CollisionHit& )
+Rock::collision(GameObject& , const CollisionHit& )
{
if(grabbed) {
- return FORCE_MOVE;
- }
-
- if(object.get_flags() & FLAG_SOLID) {
- physic.set_velocity(0, 0);
- return CONTINUE;
+ return PASSTHROUGH;
}
- return FORCE_MOVE;
+ return SOLID;
}
void
Rock(const lisp::Lisp& reader);
virtual Rock* clone() const { return new Rock(*this); }
+ void collision_solid(const CollisionHit& hit);
HitResponse collision(GameObject& other, const CollisionHit& hit);
void update(float elapsed_time);
void write(lisp::Writer& writer);
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
#include <config.h>
#include <stdexcept>
sprite->draw(context, get_pos(), layer);
}
-HitResponse
-ScriptedObject::collision(GameObject& other, const CollisionHit& hit)
+void
+ScriptedObject::collision_solid(const CollisionHit& hit)
{
if(!physic_enabled)
- return FORCE_MOVE;
-
- if(!(other.get_flags() & FLAG_SOLID))
- return FORCE_MOVE;
-
- if(other.get_flags() & FLAG_SOLID) {
- if(hit.normal.y < 0) { // landed on floor
- if(physic.get_velocity_y() > 0)
- physic.set_velocity_y(0);
- } else if(hit.normal.y > 0) { // bumped against roof
- physic.set_velocity_y(.1);
- }
-
- if(fabsf(hit.normal.x) > .9) { // hit on side?
- physic.set_velocity_x(0);
- }
-
- return CONTINUE;
+ return;
+
+ if(hit.bottom) {
+ if(physic.get_velocity_y() > 0)
+ physic.set_velocity_y(0);
+ } else if(hit.top) {
+ physic.set_velocity_y(.1);
}
+ if(hit.left || hit.right) {
+ physic.set_velocity_x(0);
+ }
+}
+
+HitResponse
+ScriptedObject::collision(GameObject& , const CollisionHit& )
+{
return FORCE_MOVE;
}
void update(float elapsed_time);
void draw(DrawingContext& context);
+
+ void collision_solid(const CollisionHit& hit);
HitResponse collision(GameObject& other, const CollisionHit& hit);
// --- Scripting Interface stuff ---
}
HitResponse
-SkullTile::collision(GameObject& other, const CollisionHit& hitdata)
+SkullTile::collision(GameObject& other, const CollisionHit& )
{
- if(hitdata.normal.y < 0.8)
- return FORCE_MOVE;
-
Player* player = dynamic_cast<Player*> (&other);
if(player)
hit = true;
movement = physic.get_movement(elapsed_time);
}
-HitResponse
-Star::collision(GameObject& other, const CollisionHit& hit)
+void
+Star::collision_solid(const CollisionHit& hit)
{
- if(other.get_flags() & FLAG_SOLID) {
- if(hit.normal.y < -.5) { // ground
- physic.set_velocity_y(JUMPSPEED);
- } else if(hit.normal.y > .5) { // roof
- physic.set_velocity_y(0);
- } else { // bumped left or right
- physic.set_velocity_x(-physic.get_velocity_x());
- }
-
- return CONTINUE;
+ if(hit.bottom) {
+ physic.set_velocity_y(JUMPSPEED);
+ } else if(hit.top) {
+ physic.set_velocity_y(0);
+ } else if(hit.left || hit.right) {
+ physic.set_velocity_x(-physic.get_velocity_x());
}
-
+}
+
+HitResponse
+Star::collision(GameObject& other, const CollisionHit& )
+{
Player* player = dynamic_cast<Player*> (&other);
if(player) {
player->make_invincible();
virtual Star* clone() const { return new Star(*this); }
virtual void update(float elapsed_time);
+ virtual void collision_solid(const CollisionHit& hit);
virtual HitResponse collision(GameObject& other, const CollisionHit& hit);
private:
: MovingSprite(lisp, LAYER_TILES, COLGROUP_STATIC), state(STATE_NORMAL)
{
sprite->set_action("normal");
- flags |= FLAG_SOLID;
}
HitResponse
-UnstableTile::collision(GameObject& other, const CollisionHit& hit)
+UnstableTile::collision(GameObject& other, const CollisionHit& )
{
- switch (state) {
-
- case STATE_NORMAL:
- if ((hit.normal.y >= 0.8 ) && (dynamic_cast<Player*>(&other))) {
- state = STATE_CRUMBLING;
- sprite->set_action("crumbling", 1);
- return FORCE_MOVE;
- }
- return FORCE_MOVE;
- break;
-
- case STATE_CRUMBLING:
- return FORCE_MOVE;
- break;
-
- case STATE_DISINTEGRATING:
- return FORCE_MOVE;
- break;
-
+ if(state == STATE_NORMAL) {
+ Player* player = dynamic_cast<Player*> (&other);
+ if(player != NULL &&
+ player->get_bbox().get_bottom() < get_bbox().get_top() + 7.0) {
+ state = STATE_CRUMBLING;
+ sprite->set_action("crumbling", 1);
+ }
}
-
- log_debug << "unhandled state" << std::endl;
- return FORCE_MOVE;
+ return SOLID;
}
void
return;
}
break;
-
}
}
-// $Id: weak_block.hpp 3327 2006-04-13 15:02:40Z ravu_al_hemio $
+// $Id$
//
// SuperTux - Weak Block
// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
{
public:
WeakBlock(const lisp::Lisp& lisp);
- virtual WeakBlock* clone() const { return new WeakBlock(*this); }
HitResponse collision(GameObject& other, const CollisionHit& hit);
void update(float elapsed_time);
* Set tux bonus.
* This can be "grow", "fireflower" or "iceflower" at the moment
*/
- virtual void add_bonus(const std::string& bonus) = 0;
+ virtual bool add_bonus(const std::string& bonus) = 0;
/**
* Give tux more coins
*/
}
try {
- _this->add_bonus(arg0);
+ bool return_value = _this->add_bonus(arg0);
- return 0;
+ sq_pushbool(vm, return_value);
+ return 1;
} catch(std::exception& e) {
sq_throwerror(vm, e.what());
#include <sstream>
#include <stdexcept>
#include <float.h>
+#include <math.h>
#include "sector.hpp"
-#include "player_status.hpp"
+#include "object/player.hpp"
#include "object/gameobjs.hpp"
#include "object/camera.hpp"
#include "object/background.hpp"
#include "game_session.hpp"
#include "resources.hpp"
#include "statistics.hpp"
-#include "collision_grid.hpp"
-#include "collision_grid_iterator.hpp"
#include "object_factory.hpp"
#include "collision.hpp"
#include "spawn_point.hpp"
add_object(new DisplayEffect());
add_object(new TextObject());
-#ifdef USE_GRID
- grid.reset(new CollisionGrid(32000, 32000));
-#endif
-
// create a new squirrel table for the sector
using namespace Scripting;
{
player->check_bounds(camera);
-#if 0
- CollisionGridIterator iter(*grid, get_active_region());
- while(MovingObject* object = iter.next()) {
- if(!object->is_valid())
- continue;
-
- object->update(elapsed_time);
- }
-#else
/* update objects */
for(GameObjects::iterator i = gameobjects.begin();
i != gameobjects.end(); ++i) {
object->update(elapsed_time);
}
-#endif
/* Handle all possible collisions. */
handle_collisions();
continue;
}
-#ifdef USE_GRID
- grid->remove_object(moving_object);
-#endif
-
i = moving_objects.erase(i);
}
for(std::vector<GameObject*>::iterator i = gameobjects.begin();
MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
if(movingobject) {
moving_objects.push_back(movingobject);
-#ifdef USE_GRID
- grid->add_object(movingobject);
-#endif
}
TileMap* tilemap = dynamic_cast<TileMap*> (object);
context.pop_transform();
}
+/*-------------------------------------------------------------------------
+ * Collision Detection
+ *-------------------------------------------------------------------------*/
+
+static const float SHIFT_DELTA = 7.0f;
+
+/** r1 is supposed to be moving, r2 a solid object */
+void check_collisions(collision::Constraints* constraints,
+ const Vector& movement, const Rect& r1, const Rect& r2,
+ GameObject* object = NULL, MovingObject* other = NULL)
+{
+ if(!collision::intersects(r1, r2))
+ return;
+
+ // calculate intersection
+ float itop = r1.get_bottom() - r2.get_top();
+ float ibottom = r2.get_bottom() - r1.get_top();
+ float ileft = r1.get_right() - r2.get_left();
+ float iright = r2.get_right() - r1.get_left();
+
+ if(fabsf(movement.y) > fabsf(movement.x)) {
+ if(ileft < SHIFT_DELTA) {
+ constraints->right = std::min(constraints->right, r2.get_left());
+ return;
+ } else if(iright < SHIFT_DELTA) {
+ constraints->left = std::max(constraints->left, r2.get_right());
+ return;
+ }
+ } else {
+ // shiftout bottom/top
+ if(itop < SHIFT_DELTA) {
+ constraints->bottom = std::min(constraints->bottom, r2.get_top());
+ return;
+ } else if(ibottom < SHIFT_DELTA) {
+ constraints->top = std::max(constraints->top, r2.get_bottom());
+ return;
+ }
+ }
+
+ if(other != NULL) {
+ CollisionHit dummy;
+ HitResponse response = other->collision(*object, dummy);
+ if(response == PASSTHROUGH)
+ return;
+ if(other->get_movement() != Vector(0, 0)) {
+ // TODO what todo when we collide with 2 moving objects?!?
+ constraints->ground_movement = other->get_movement();
+ }
+ }
+
+ float vert_penetration = std::min(itop, ibottom);
+ float horiz_penetration = std::min(ileft, iright);
+ if(vert_penetration < horiz_penetration) {
+ if(itop < ibottom) {
+ constraints->bottom = std::min(constraints->bottom, r2.get_top());
+ constraints->hit.bottom = true;
+ } else {
+ constraints->top = std::max(constraints->top, r2.get_bottom());
+ constraints->hit.top = true;
+ }
+ } else {
+ if(ileft < iright) {
+ constraints->right = std::min(constraints->right, r2.get_left());
+ constraints->hit.right = true;
+ } else {
+ constraints->left = std::max(constraints->left, r2.get_right());
+ constraints->hit.left = true;
+ }
+ }
+}
+
static const float DELTA = .001;
void
-Sector::collision_tilemap(const Rect& dest, const Vector& movement,
- CollisionHit& hit) const
+Sector::collision_tilemap(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest) const
{
// calculate rectangle where the object will move
float x1 = dest.get_left();
// test with all tiles in this rectangle
int starttilex = int(x1) / 32;
int starttiley = int(y1) / 32;
- int max_x = int(x2 + (1 - DELTA));
- int max_y = int(y2 + (1 - DELTA));
+ int max_x = int(x2);
+ int max_y = int(y2+1);
- CollisionHit temphit;
for(int x = starttilex; x*32 < max_x; ++x) {
for(int y = starttiley; y*32 < max_y; ++y) {
const Tile* tile = solids->get_tile(x, y);
if(!tile)
continue;
// skip non-solid tiles
- if(tile->getAttributes() == 0)
+ if((tile->getAttributes() & Tile::SOLID) == 0)
continue;
// only handle unisolid when the player is falling down and when he was
// above the tile before
if(tile->getAttributes() & Tile::UNISOLID) {
- if(movement.y <= 0 || dest.get_top() - movement.y > y*32)
+ if(movement.y <= 0 || dest.get_bottom() - movement.y - SHIFT_DELTA > y*32)
continue;
}
Vector p2((x+1)*32, (y+1)*32);
triangle = AATriangle(p1, p2, tile->getData());
- if(Collision::rectangle_aatriangle(temphit, dest, movement,
- triangle)) {
- if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) {
- hit = temphit;
- }
- }
+ collision::rectangle_aatriangle(constraints, dest, triangle);
} else { // normal rectangular tile
Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
- if(Collision::rectangle_rectangle(temphit, dest, movement, rect)) {
- if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) {
- hit = temphit;
- }
- }
+ check_collisions(constraints, movement, dest, rect);
}
}
}
return result;
}
+/** fills in CollisionHit and Normal vector of 2 intersecting rectangle */
+static void get_hit_normal(const Rect& r1, const Rect& r2, CollisionHit& hit,
+ Vector& normal)
+{
+ float itop = r1.get_bottom() - r2.get_top();
+ float ibottom = r2.get_bottom() - r1.get_top();
+ float ileft = r1.get_right() - r2.get_left();
+ float iright = r2.get_right() - r1.get_left();
+
+ float vert_penetration = std::min(itop, ibottom);
+ float horiz_penetration = std::min(ileft, iright);
+ if(vert_penetration < horiz_penetration) {
+ if(itop < ibottom) {
+ hit.bottom = true;
+ normal.y = vert_penetration;
+ } else {
+ hit.top = true;
+ normal.y = -vert_penetration;
+ }
+ } else {
+ if(ileft < iright) {
+ hit.right = true;
+ normal.x = horiz_penetration;
+ } else {
+ hit.left = true;
+ normal.x = -horiz_penetration;
+ }
+ }
+}
+
void
Sector::collision_object(MovingObject* object1, MovingObject* object2) const
{
+ using namespace collision;
+
+ const Rect& r1 = object1->dest;
+ const Rect& r2 = object2->dest;
+
CollisionHit hit;
+ if(intersects(object1->dest, object2->dest)) {
+ Vector normal;
+ get_hit_normal(r1, r2, hit, normal);
- Vector movement = object1->get_movement() - object2->get_movement();
- if(Collision::rectangle_rectangle(hit, object1->dest, movement, object2->dest)) {
HitResponse response1 = object1->collision(*object2, hit);
- hit.normal *= -1;
HitResponse response2 = object2->collision(*object1, hit);
-
- if(response1 != CONTINUE) {
- if(response1 == ABORT_MOVE)
- object1->dest = object1->get_bbox();
- if(response2 == CONTINUE)
- object2->dest.move(hit.normal * (hit.depth + DELTA));
- } else if(response2 != CONTINUE) {
- if(response2 == ABORT_MOVE)
- object2->dest = object2->get_bbox();
- if(response1 == CONTINUE)
- object1->dest.move(-hit.normal * (hit.depth + DELTA));
- } else {
- object1->dest.move(-hit.normal * (hit.depth/2 + DELTA));
- object2->dest.move(hit.normal * (hit.depth/2 + DELTA));
+ if(response1 == CONTINUE || response2 == CONTINUE) {
+ normal *= (0.5 + DELTA);
+ object1->dest.move(-normal);
+ object2->dest.move(normal);
}
}
}
-bool
-Sector::collision_static(MovingObject* object, const Vector& movement)
+void
+Sector::collision_static(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest,
+ GameObject& object)
{
- GameObject* collided_with = solids;
- CollisionHit hit;
- hit.time = -1;
-
- collision_tilemap(object->dest, movement, hit);
+ collision_tilemap(constraints, movement, dest);
// collision with other (static) objects
- CollisionHit temphit;
- for(MovingObjects::iterator i2 = moving_objects.begin();
- i2 != moving_objects.end(); ++i2) {
- MovingObject* moving_object_2 = *i2;
- if(moving_object_2->get_group() != COLGROUP_STATIC
- || !moving_object_2->is_valid())
+ for(MovingObjects::iterator i = moving_objects.begin();
+ i != moving_objects.end(); ++i) {
+ MovingObject* moving_object = *i;
+ if(moving_object->get_group() != COLGROUP_STATIC
+ || !moving_object->is_valid())
continue;
-
- Rect dest = moving_object_2->dest;
-
- Vector rel_movement
- = movement - moving_object_2->get_movement();
-
- if(Collision::rectangle_rectangle(temphit, object->dest, rel_movement, dest)
- && temphit.time > hit.time) {
- hit = temphit;
- collided_with = moving_object_2;
- }
- }
-
- if(hit.time < 0)
- return true;
-
- HitResponse response = object->collision(*collided_with, hit);
- hit.normal *= -1;
- if(collided_with != solids) {
- MovingObject* moving_object = (MovingObject*) collided_with;
- HitResponse other_response = moving_object->collision(*object, hit);
- if(other_response == ABORT_MOVE) {
- moving_object->dest = moving_object->get_bbox();
- } else if(other_response == FORCE_MOVE) {
- // the static object "wins" move tux out of the collision
- object->dest.move(-hit.normal * (hit.depth + DELTA));
- return false;
- } else if(other_response == PASS_MOVEMENT) {
- object->dest.move(moving_object->get_movement());
- //object->movement += moving_object->get_movement();
- }
- }
-
- if(response == CONTINUE) {
- object->dest.move(-hit.normal * (hit.depth + DELTA));
- return false;
- } else if(response == ABORT_MOVE) {
- object->dest = object->get_bbox();
- return true;
- }
- // force move
- return false;
+ check_collisions(constraints, movement, dest, moving_object->dest,
+ &object, moving_object);
+ }
}
void
Sector::handle_collisions()
{
+ using namespace collision;
+
// calculate destination positions of the objects
for(MovingObjects::iterator i = moving_objects.begin();
i != moving_objects.end(); ++i) {
}
// part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
- // we do this up to 4 times and have to sort all results for the smallest
- // one before we can continue here
for(MovingObjects::iterator i = moving_objects.begin();
i != moving_objects.end(); ++i) {
MovingObject* moving_object = *i;
|| !moving_object->is_valid())
continue;
+ Constraints constraints;
Vector movement = moving_object->get_movement();
-
- // test if x or y movement is dominant
- if(fabsf(moving_object->get_movement().x) < fabsf(moving_object->get_movement().y)) {
-
- // test in x direction first, then y direction
- moving_object->dest.move(Vector(0, -movement.y));
- for(int i = 0; i < 2; ++i) {
- bool res = collision_static(moving_object, Vector(movement.x, 0));
- if(res)
- break;
+ Rect& dest = moving_object->dest;
+ float owidth = moving_object->get_bbox().get_width();
+ float oheight = moving_object->get_bbox().get_height();
+
+ for(int i = 0; i < 2; ++i) {
+ collision_static(&constraints, Vector(0, movement.y), dest, *moving_object);
+ if(!constraints.has_constraints())
+ break;
+
+ // apply calculated horizontal constraints
+ if(constraints.bottom < INFINITY) {
+ float height = constraints.bottom - constraints.top;
+ if(height < oheight) {
+ // we're crushed, but ignore this for now, we'll get this again
+ // later if we're really crushed or things will solve itself when
+ // looking at the vertical constraints
+ }
+ dest.p2.y = constraints.bottom - DELTA;
+ dest.p1.y = dest.p2.y - oheight;
+ } else if(constraints.top > -INFINITY) {
+ dest.p1.y = constraints.top + DELTA;
+ dest.p2.y = dest.p1.y + oheight;
}
- moving_object->dest.move(Vector(0, movement.y));
- for(int i = 0; i < 2; ++i) {
- bool res = collision_static(moving_object, Vector(0, movement.y));
- if(res)
- break;
+ }
+ if(constraints.has_constraints()) {
+ if(constraints.hit.bottom) {
+ dest.move(constraints.ground_movement);
}
-
- } else {
+ if(constraints.hit.top || constraints.hit.bottom) {
+ constraints.hit.left = false;
+ constraints.hit.right = false;
+ moving_object->collision_solid(constraints.hit);
+ }
+ }
- // test in y direction first, then x direction
- moving_object->dest.move(Vector(-movement.x, 0));
- for(int i = 0; i < 2; ++i) {
- bool res = collision_static(moving_object, Vector(0, movement.y));
- if(res)
- break;
+ constraints = Constraints();
+ for(int i = 0; i < 2; ++i) {
+ collision_static(&constraints, movement, dest, *moving_object);
+ if(!constraints.has_constraints())
+ break;
+
+ // apply calculated vertical constraints
+ if(constraints.right < INFINITY) {
+ float width = constraints.right - constraints.left;
+ if(width + SHIFT_DELTA < owidth) {
+ printf("Object %p crushed horizontally... L:%f R:%f\n", moving_object,
+ constraints.left, constraints.right);
+ CollisionHit h;
+ h.left = true;
+ h.right = true;
+ h.crush = true;
+ moving_object->collision_solid(h);
+ } else {
+ dest.p2.x = constraints.right - DELTA;
+ dest.p1.x = dest.p2.x - owidth;
+ }
+ } else if(constraints.left > -INFINITY) {
+ dest.p1.x = constraints.left + DELTA;
+ dest.p2.x = dest.p1.x + owidth;
}
- moving_object->dest.move(Vector(movement.x, 0));
- for(int i = 0; i < 2; ++i) {
- bool res = collision_static(moving_object, Vector(movement.x, 0));
- if(res)
- break;
+ }
+
+ if(constraints.has_constraints()) {
+ moving_object->collision_solid(constraints.hit);
+ }
+
+ // an extra pass to make sure we're not crushed horizontally
+ constraints = Constraints();
+ collision_static(&constraints, movement, dest, *moving_object);
+ if(constraints.bottom < INFINITY) {
+ float height = constraints.bottom - constraints.top;
+ if(height + SHIFT_DELTA < oheight) {
+ printf("Object %p crushed vertically...\n", moving_object);
+ CollisionHit h;
+ h.top = true;
+ h.bottom = true;
+ h.crush = true;
+ moving_object->collision_solid(h);
}
}
}
|| !moving_object_2->is_valid())
continue;
- collision_object(moving_object, moving_object_2);
+ if(intersects(moving_object->dest, moving_object_2->dest)) {
+ Vector normal;
+ CollisionHit hit;
+ get_hit_normal(moving_object->dest, moving_object_2->dest,
+ hit, normal);
+ moving_object->collision(*moving_object_2, hit);
+ moving_object_2->collision(*moving_object, hit);
+ }
}
}
bool
Sector::is_free_space(const Rect& rect) const
{
+ using namespace collision;
+
// test with all tiles in this rectangle
int starttilex = int(rect.p1.x) / 32;
int starttiley = int(rect.p1.y) / 32;
|| !moving_object->is_valid())
continue;
- if(Collision::intersects(rect, moving_object->get_bbox()))
+ if(intersects(rect, moving_object->get_bbox()))
return false;
}
// TODO remove this function and move these checks elsewhere...
Bullet* new_bullet = 0;
- if(player_status->bonus == FIRE_BONUS) {
- if((int)bullets.size() >= player_status->max_fire_bullets)
- return false;
- new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
- } else if(player_status->bonus == ICE_BONUS) {
- if((int)bullets.size() >= player_status->max_ice_bullets)
- return false;
- new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
- } else {
+ if((int)bullets.size() >= player_status->max_fire_bullets)
return false;
- }
+ new_bullet = new Bullet(pos, xm, dir);
add_object(new_bullet);
sound_manager->play("sounds/shoot.wav");
}
void
-Sector::add_floating_text(const Vector& pos, const std::string& text)
-{
- add_object(new FloatingText(pos, text));
-}
-
-void
Sector::play_music(MusicType type)
{
currentmusic = type;
class Lisp;
class Writer;
}
+namespace collision {
+class Constraints;
+}
class Rect;
class Sprite;
class Camera;
class TileMap;
class Bullet;
-class CollisionGrid;
class ScriptInterpreter;
class SpawnPoint;
class MovingObject;
bool add_bullet(const Vector& pos, float xm, Direction dir);
bool add_smoke_cloud(const Vector& pos);
- void add_floating_text(const Vector& pos, const std::string& text);
-
+
/** get currently activated sector. */
static Sector* current()
{ return _current; }
return total;
}
- void collision_tilemap(const Rect& dest, const Vector& movement, CollisionHit& hit) const;
+ void collision_tilemap(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest) const;
/** Checks if at the specified rectangle are gameobjects with STATIC flag set
* (or solid tiles from the tilemap).
* returns true if the collision detection should be aborted for this object
* (because of ABORT_MOVE in the collision response or no collisions)
*/
- bool collision_static(MovingObject* object, const Vector& movement);
-
+ void collision_static(collision::Constraints* constraints,
+ const Vector& movement, const Rect& dest, GameObject& object);
GameObject* parse_object(const std::string& name, const lisp::Lisp& lisp);
MusicType currentmusic;
- std::auto_ptr<CollisionGrid> grid;
-
HSQOBJECT sector_table;
/// sector scripts
typedef std::vector<HSQOBJECT> ScriptList;