@@ -10,6 +10,8 @@ struct PetPhysics {
1010 var horizontalSpeed : CGFloat = 42
1111 private var preciseOrigin : CGPoint ?
1212 private var currentSupport : Surface ?
13+ private var stationaryLockMood : PetModel . Mood ?
14+ private var stationaryLockX : CGFloat ?
1315
1416 private struct Surface {
1517 enum Kind : Int {
@@ -32,6 +34,9 @@ struct PetPhysics {
3234
3335 mutating func resetOrigin( _ origin: CGPoint , clearSupport: Bool = true ) {
3436 preciseOrigin = origin
37+ if stationaryLockMood != nil {
38+ stationaryLockX = origin. x
39+ }
3540 if clearSupport {
3641 currentSupport = nil
3742 }
@@ -65,6 +70,18 @@ struct PetPhysics {
6570 || model. mood == . resting
6671 || model. mood == . special
6772 || model. mood == . happy
73+ let shouldLockHorizontal = isStationaryMood || now < model. resumeWalkingAt
74+ if shouldLockHorizontal {
75+ if stationaryLockMood != model. mood {
76+ stationaryLockMood = model. mood
77+ stationaryLockX = frame. origin. x
78+ }
79+ frame. origin. x = stationaryLockX ?? frame. origin. x
80+ model. velocity. dx = 0
81+ } else {
82+ stationaryLockMood = nil
83+ stationaryLockX = nil
84+ }
6885
6986 if abs ( model. velocity. dy) < 1 , let support = currentSupport {
7087 if let updatedSupport = updatedSurface (
@@ -79,6 +96,9 @@ struct PetPhysics {
7996 var shiftedFrame = frame
8097 shiftedFrame. origin. x += supportDeltaX
8198 shiftedFrame. origin. y += supportDeltaY
99+ if shouldLockHorizontal, let lockedX = stationaryLockX {
100+ stationaryLockX = lockedX + supportDeltaX
101+ }
82102 if abs ( supportDeltaX) < 96 ,
83103 abs ( supportDeltaY) < 96 ,
84104 supportsFeet ( of: shiftedFrame, contactInset: contactInset, on: updatedSupport, screen: screen) {
@@ -93,25 +113,33 @@ struct PetPhysics {
93113 }
94114 }
95115
96- if isStationaryMood {
97- model. velocity. dx *= 0.86
98- if abs ( model. velocity. dx) < 1 {
99- model. velocity. dx = 0
100- }
101- } else if abs ( model. velocity. dx) < horizontalSpeed * 0.35 {
116+ if shouldLockHorizontal {
117+ model. velocity. dx = 0
118+ frame. origin. x = stationaryLockX ?? frame. origin. x
119+ } else if now >= model. resumeWalkingAt, abs ( model. velocity. dx) < horizontalSpeed * 0.35 {
102120 model. velocity. dx = model. facingLeft ? - horizontalSpeed : horizontalSpeed
103121 }
104122
105123 if frame. minX <= bounds. minX + edgeInset {
106124 frame. origin. x = bounds. minX + edgeInset
107- model. velocity. dx = isStationaryMood ? 0 : abs ( horizontalSpeed)
125+ if shouldLockHorizontal {
126+ stationaryLockX = frame. origin. x
127+ }
128+ model. velocity. dx = shouldLockHorizontal ? 0 : abs ( horizontalSpeed)
108129 } else if frame. maxX >= bounds. maxX - edgeInset {
109130 frame. origin. x = bounds. maxX - frame. width - edgeInset
110- model. velocity. dx = isStationaryMood ? 0 : - abs( horizontalSpeed)
131+ if shouldLockHorizontal {
132+ stationaryLockX = frame. origin. x
133+ }
134+ model. velocity. dx = shouldLockHorizontal ? 0 : - abs( horizontalSpeed)
111135 }
112136
113137 model. velocity. dy -= gravity * CGFloat( dt)
114- frame. origin. x += model. velocity. dx * CGFloat( dt)
138+ if shouldLockHorizontal {
139+ frame. origin. x = stationaryLockX ?? frame. origin. x
140+ } else {
141+ frame. origin. x += model. velocity. dx * CGFloat( dt)
142+ }
115143 frame. origin. y += model. velocity. dy * CGFloat( dt)
116144
117145 let support = landingSurface (
@@ -123,11 +151,21 @@ struct PetPhysics {
123151 )
124152 let contactY = frame. minY + contactInset
125153 if model. velocity. dy <= 0 , support. isFloor, contactY <= support. topY {
126- frame. origin. y = support. topY - contactInset
154+ frame. origin. y = settledY (
155+ currentY: frame. origin. y,
156+ targetY: support. topY - contactInset,
157+ wasSupported: currentSupport != nil ,
158+ dt: CGFloat ( dt)
159+ )
127160 model. velocity. dy = 0
128161 currentSupport = support
129162 } else if model. velocity. dy <= 0 , contactY <= support. topY, previousContactY >= support. topY - 34 {
130- frame. origin. y = support. topY - contactInset
163+ frame. origin. y = settledY (
164+ currentY: frame. origin. y,
165+ targetY: support. topY - contactInset,
166+ wasSupported: currentSupport != nil ,
167+ dt: CGFloat ( dt)
168+ )
131169 model. velocity. dy = 0
132170 currentSupport = support
133171 } else if currentSupport? . isFloor == false , support. isFloor {
@@ -138,10 +176,16 @@ struct PetPhysics {
138176
139177 if frame. minX <= bounds. minX + edgeInset {
140178 frame. origin. x = bounds. minX + edgeInset
141- model. velocity. dx = isStationaryMood ? 0 : abs ( horizontalSpeed)
179+ if shouldLockHorizontal {
180+ stationaryLockX = frame. origin. x
181+ }
182+ model. velocity. dx = shouldLockHorizontal ? 0 : abs ( horizontalSpeed)
142183 } else if frame. maxX >= bounds. maxX - edgeInset {
143184 frame. origin. x = bounds. maxX - frame. width - edgeInset
144- model. velocity. dx = isStationaryMood ? 0 : - abs( horizontalSpeed)
185+ if shouldLockHorizontal {
186+ stationaryLockX = frame. origin. x
187+ }
188+ model. velocity. dx = shouldLockHorizontal ? 0 : - abs( horizontalSpeed)
145189 }
146190
147191 if abs ( model. velocity. dx) > 1 {
@@ -156,13 +200,22 @@ struct PetPhysics {
156200 window. setFrameOrigin ( frame. origin)
157201 }
158202
203+ private func settledY( currentY: CGFloat , targetY: CGFloat , wasSupported: Bool , dt: CGFloat ) -> CGFloat {
204+ let delta = targetY - currentY
205+ guard wasSupported, abs ( delta) > 0.5 , abs ( delta) < 90 else {
206+ return targetY
207+ }
208+
209+ let maxStep = max ( 4 , 720 * dt)
210+ return currentY + min( abs ( delta) , maxStep) * ( delta < 0 ? - 1 : 1 )
211+ }
212+
159213 private func pickNextIdleAction( model: PetModel , onWindowSurface: Bool ) {
160214 if model. mood != . idle {
161- model. mood = . idle
162- if abs ( model. velocity. dx) < horizontalSpeed * 0.35 {
163- model. velocity. dx = model. facingLeft ? - horizontalSpeed : horizontalSpeed
164- }
215+ model. resumeWalkingAt = Date ( ) . addingTimeInterval ( 1.6 )
216+ model. velocity. dx = 0
165217 model. nextMoodChange = Date ( ) . addingTimeInterval ( TimeInterval . random ( in: 8 ... 14 ) )
218+ model. mood = . idle
166219 return
167220 }
168221
0 commit comments