Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions LilAgents/LilAgentsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ class LilAgentsController {
char1.flipXOffset = 0
char2.flipXOffset = -9

char1.positionProgress = 0.3
char2.positionProgress = 0.7
// Start near the ends of the track so the first walk tends to spread them along the dock.
char1.positionProgress = 0.1
char2.positionProgress = 0.9

char1.pauseEndTime = CACurrentMediaTime() + Double.random(in: 0.5...2.0)
char2.pauseEndTime = CACurrentMediaTime() + Double.random(in: 8.0...14.0)
Expand Down Expand Up @@ -129,6 +130,12 @@ class LilAgentsController {

// Small fudge factor for dock edge padding
dockWidth *= 1.15

// Ensure a minimum width so characters aren't bunched together when
// dock defaults under-report the icon count (common in sandboxed apps).
let minDockWidth = screenWidth * 0.5
dockWidth = max(dockWidth, minDockWidth)

let dockX = (screenWidth - dockWidth) / 2.0
return (dockX, dockWidth)
}
Expand Down
82 changes: 66 additions & 16 deletions LilAgents/WalkerCharacter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,43 @@ class WalkerCharacter {

// MARK: - Walking

/// Other dock characters whose positions we use to avoid landing stacked (no teleporting — only the planned walk endpoint moves).
private func peerCharactersForSeparation() -> [WalkerCharacter] {
guard let all = controller?.characters else { return [] }
return all.filter { other in
other !== self && other.window.isVisible && other.isManuallyVisible && !other.isIdleForPopover
}
}

private var minWalkSeparationPixels: CGFloat {
max(displayWidth * 0.35, 72)
}

/// Nudges `end` along the same direction as `start → end` so the stop stays at least `minWalkSeparationPixels` from each peer’s current progress.
private func applyPeerWalkEndSeparation(start: CGFloat, end: CGFloat) -> CGFloat {
let peers = peerCharactersForSeparation()
guard !peers.isEmpty, currentTravelDistance > 1 else { return end }
let minProg = minWalkSeparationPixels / currentTravelDistance
var e = end
for _ in 0..<6 {
var changed = false
for peer in peers {
let p = peer.positionProgress
guard abs(e - p) < minProg else { continue }
if e >= start {
let n = max(e, p + minProg)
if n != e { e = n; changed = true }
} else {
let n = min(e, p - minProg)
if n != e { e = n; changed = true }
}
}
e = min(max(e, 0), 1)
if !changed { break }
}
return e
}

func startWalk() {
isPaused = false
isWalking = true
Expand All @@ -860,36 +897,49 @@ class WalkerCharacter {
goingRight = true
} else {
goingRight = Bool.random()
let peers = peerCharactersForSeparation()
if currentTravelDistance > 1,
let nearest = peers.min(by: {
abs($0.positionProgress - positionProgress) < abs($1.positionProgress - positionProgress)
}) {
let sep = abs(nearest.positionProgress - positionProgress) * currentTravelDistance
if sep < minWalkSeparationPixels {
// Peer is to the left on the dock → go right (increase progress), and vice versa.
goingRight = nearest.positionProgress < positionProgress
}
}
}

walkStartPos = positionProgress
// Walk a fixed pixel distance (~200-325px) regardless of screen width.
let referenceWidth: CGFloat = 500.0
let walkPixels = CGFloat.random(in: walkAmountRange) * referenceWidth
let walkAmount = currentTravelDistance > 0 ? walkPixels / currentTravelDistance : 0.3
let tentativeEnd: CGFloat
if goingRight {
walkEndPos = min(walkStartPos + walkAmount, 1.0)
tentativeEnd = min(walkStartPos + walkAmount, 1.0)
} else {
walkEndPos = max(walkStartPos - walkAmount, 0.0)
tentativeEnd = max(walkStartPos - walkAmount, 0.0)
}
walkEndPos = applyPeerWalkEndSeparation(start: walkStartPos, end: tentativeEnd)

// If separation pinned us to essentially no move, try the other direction with the same stride length.
let minStrideProg = 8 / max(currentTravelDistance, 1)
if abs(walkEndPos - walkStartPos) < minStrideProg {
goingRight.toggle()
let altEnd: CGFloat
if goingRight {
altEnd = min(walkStartPos + walkAmount, 1.0)
} else {
altEnd = max(walkStartPos - walkAmount, 0.0)
}
walkEndPos = applyPeerWalkEndSeparation(start: walkStartPos, end: altEnd)
}

// Store pixel positions so walk speed stays consistent if screen changes mid-walk
walkStartPixel = walkStartPos * currentTravelDistance
walkEndPixel = walkEndPos * currentTravelDistance

let minSeparation: CGFloat = 0.12
if let siblings = controller?.characters {
for sibling in siblings where sibling !== self {
let sibPos = sibling.positionProgress
if abs(walkEndPos - sibPos) < minSeparation {
if goingRight {
walkEndPos = max(walkStartPos, sibPos - minSeparation)
} else {
walkEndPos = min(walkStartPos, sibPos + minSeparation)
}
}
}
}

updateFlip()
queuePlayer.seek(to: .zero)
queuePlayer.play()
Expand Down