|
|
|
@ -451,6 +451,7 @@ func (hm *HeroMovement) ShiftGameDeadlines(d time.Duration, now time.Time) {
|
|
|
|
hm.TownLeaveAt = shift(hm.TownLeaveAt)
|
|
|
|
hm.TownLeaveAt = shift(hm.TownLeaveAt)
|
|
|
|
hm.WanderingMerchantDeadline = shift(hm.WanderingMerchantDeadline)
|
|
|
|
hm.WanderingMerchantDeadline = shift(hm.WanderingMerchantDeadline)
|
|
|
|
hm.Excursion.StartedAt = shift(hm.Excursion.StartedAt)
|
|
|
|
hm.Excursion.StartedAt = shift(hm.Excursion.StartedAt)
|
|
|
|
|
|
|
|
hm.Excursion.OutUntil = shift(hm.Excursion.OutUntil)
|
|
|
|
hm.Excursion.WildUntil = shift(hm.Excursion.WildUntil)
|
|
|
|
hm.Excursion.WildUntil = shift(hm.Excursion.WildUntil)
|
|
|
|
hm.Excursion.ReturnUntil = shift(hm.Excursion.ReturnUntil)
|
|
|
|
hm.Excursion.ReturnUntil = shift(hm.Excursion.ReturnUntil)
|
|
|
|
hm.LastExcursionEndedAt = shift(hm.LastExcursionEndedAt)
|
|
|
|
hm.LastExcursionEndedAt = shift(hm.LastExcursionEndedAt)
|
|
|
|
@ -665,7 +666,7 @@ func (hm *HeroMovement) AdminStopRest(now time.Time) bool {
|
|
|
|
if hm.ActiveRestKind != model.RestKindRoadside && hm.ActiveRestKind != model.RestKindAdventureInline {
|
|
|
|
if hm.ActiveRestKind != model.RestKindRoadside && hm.ActiveRestKind != model.RestKindAdventureInline {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if hm.ActiveRestKind == model.RestKindAdventureInline && hm.Excursion.Active() {
|
|
|
|
if hm.Excursion.Active() {
|
|
|
|
hm.endExcursion(now)
|
|
|
|
hm.endExcursion(now)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hm.ActiveRestKind = model.RestKindNone
|
|
|
|
hm.ActiveRestKind = model.RestKindNone
|
|
|
|
@ -751,13 +752,12 @@ func (hm *HeroMovement) roadForwardUnit() (float64, float64) {
|
|
|
|
func (hm *HeroMovement) displayOffset(now time.Time) (float64, float64) {
|
|
|
|
func (hm *HeroMovement) displayOffset(now time.Time) (float64, float64) {
|
|
|
|
exc := &hm.Excursion
|
|
|
|
exc := &hm.Excursion
|
|
|
|
if exc.Active() {
|
|
|
|
if exc.Active() {
|
|
|
|
cfg := tuning.Get()
|
|
|
|
|
|
|
|
perpX, perpY := hm.roadPerpendicularUnit()
|
|
|
|
perpX, perpY := hm.roadPerpendicularUnit()
|
|
|
|
depth := exc.DepthWorldUnits
|
|
|
|
depth := exc.DepthWorldUnits
|
|
|
|
var t float64
|
|
|
|
var t float64
|
|
|
|
switch exc.Phase {
|
|
|
|
switch exc.Phase {
|
|
|
|
case model.ExcursionOut:
|
|
|
|
case model.ExcursionOut:
|
|
|
|
outMs := float64(cfg.AdventureOutDurationMs)
|
|
|
|
outMs := float64(exc.OutUntil.Sub(exc.StartedAt).Milliseconds())
|
|
|
|
if outMs > 0 {
|
|
|
|
if outMs > 0 {
|
|
|
|
elapsed := float64(now.Sub(exc.StartedAt).Milliseconds())
|
|
|
|
elapsed := float64(now.Sub(exc.StartedAt).Milliseconds())
|
|
|
|
t = smoothstep(clamp01(elapsed / outMs))
|
|
|
|
t = smoothstep(clamp01(elapsed / outMs))
|
|
|
|
@ -765,10 +765,9 @@ func (hm *HeroMovement) displayOffset(now time.Time) (float64, float64) {
|
|
|
|
case model.ExcursionWild:
|
|
|
|
case model.ExcursionWild:
|
|
|
|
t = 1.0
|
|
|
|
t = 1.0
|
|
|
|
case model.ExcursionReturn:
|
|
|
|
case model.ExcursionReturn:
|
|
|
|
retMs := float64(cfg.AdventureReturnDurationMs)
|
|
|
|
retMs := float64(exc.ReturnUntil.Sub(exc.WildUntil).Milliseconds())
|
|
|
|
if retMs > 0 {
|
|
|
|
if retMs > 0 {
|
|
|
|
returnStart := exc.ReturnUntil.Add(-time.Duration(cfg.AdventureReturnDurationMs) * time.Millisecond)
|
|
|
|
elapsed := float64(now.Sub(exc.WildUntil).Milliseconds())
|
|
|
|
elapsed := float64(now.Sub(returnStart).Milliseconds())
|
|
|
|
|
|
|
|
t = 1.0 - smoothstep(clamp01(elapsed / retMs))
|
|
|
|
t = 1.0 - smoothstep(clamp01(elapsed / retMs))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -776,12 +775,6 @@ func (hm *HeroMovement) displayOffset(now time.Time) (float64, float64) {
|
|
|
|
return perpX * d, perpY * d
|
|
|
|
return perpX * d, perpY * d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if hm.State == model.StateResting && hm.ActiveRestKind == model.RestKindRoadside {
|
|
|
|
|
|
|
|
perpX, perpY := hm.roadPerpendicularUnit()
|
|
|
|
|
|
|
|
const roadsideDepth = 2.0
|
|
|
|
|
|
|
|
return perpX * roadsideDepth, perpY * roadsideDepth
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0, 0
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -1004,6 +997,10 @@ func (hm *HeroMovement) excursionPersisted() *model.ExcursionPersisted {
|
|
|
|
t := s.StartedAt
|
|
|
|
t := s.StartedAt
|
|
|
|
ep.StartedAt = &t
|
|
|
|
ep.StartedAt = &t
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.OutUntil.IsZero() {
|
|
|
|
|
|
|
|
t := s.OutUntil
|
|
|
|
|
|
|
|
ep.OutUntil = &t
|
|
|
|
|
|
|
|
}
|
|
|
|
if !s.WildUntil.IsZero() {
|
|
|
|
if !s.WildUntil.IsZero() {
|
|
|
|
t := s.WildUntil
|
|
|
|
t := s.WildUntil
|
|
|
|
ep.WildUntil = &t
|
|
|
|
ep.WildUntil = &t
|
|
|
|
@ -1062,6 +1059,9 @@ func (hm *HeroMovement) applyExcursionFromBlob(ep *model.ExcursionPersisted) {
|
|
|
|
if ep.StartedAt != nil {
|
|
|
|
if ep.StartedAt != nil {
|
|
|
|
hm.Excursion.StartedAt = *ep.StartedAt
|
|
|
|
hm.Excursion.StartedAt = *ep.StartedAt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ep.OutUntil != nil {
|
|
|
|
|
|
|
|
hm.Excursion.OutUntil = *ep.OutUntil
|
|
|
|
|
|
|
|
}
|
|
|
|
if ep.WildUntil != nil {
|
|
|
|
if ep.WildUntil != nil {
|
|
|
|
hm.Excursion.WildUntil = *ep.WildUntil
|
|
|
|
hm.Excursion.WildUntil = *ep.WildUntil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1269,32 +1269,50 @@ func (hm *HeroMovement) mayStartExcursion(now time.Time) bool {
|
|
|
|
|
|
|
|
|
|
|
|
func (hm *HeroMovement) beginExcursion(now time.Time) {
|
|
|
|
func (hm *HeroMovement) beginExcursion(now time.Time) {
|
|
|
|
cfg := tuning.Get()
|
|
|
|
cfg := tuning.Get()
|
|
|
|
|
|
|
|
depth := cfg.AdventureDepthWorldUnits
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hm.refreshSpeed(now)
|
|
|
|
|
|
|
|
speed := hm.Speed
|
|
|
|
|
|
|
|
if speed < 0.1 {
|
|
|
|
|
|
|
|
speed = 0.1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
outDur := time.Duration(depth / speed * float64(time.Second))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
outEnd := now.Add(outDur)
|
|
|
|
|
|
|
|
wildDur := randomDurationBetweenMs(cfg.AdventureWildMinMs, cfg.AdventureWildMaxMs)
|
|
|
|
|
|
|
|
wildEnd := outEnd.Add(wildDur)
|
|
|
|
|
|
|
|
returnDur := time.Duration(depth / speed * float64(time.Second))
|
|
|
|
|
|
|
|
|
|
|
|
hm.Excursion = model.ExcursionSession{
|
|
|
|
hm.Excursion = model.ExcursionSession{
|
|
|
|
Phase: model.ExcursionOut,
|
|
|
|
Phase: model.ExcursionOut,
|
|
|
|
StartedAt: now,
|
|
|
|
StartedAt: now,
|
|
|
|
DepthWorldUnits: cfg.AdventureDepthWorldUnits,
|
|
|
|
OutUntil: outEnd,
|
|
|
|
|
|
|
|
WildUntil: wildEnd,
|
|
|
|
|
|
|
|
ReturnUntil: wildEnd.Add(returnDur),
|
|
|
|
|
|
|
|
DepthWorldUnits: depth,
|
|
|
|
RoadFreezeWaypoint: hm.WaypointIndex,
|
|
|
|
RoadFreezeWaypoint: hm.WaypointIndex,
|
|
|
|
RoadFreezeFraction: hm.WaypointFraction,
|
|
|
|
RoadFreezeFraction: hm.WaypointFraction,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
outEnd := now.Add(time.Duration(cfg.AdventureOutDurationMs) * time.Millisecond)
|
|
|
|
|
|
|
|
wildDur := randomDurationBetweenMs(cfg.AdventureWildMinMs, cfg.AdventureWildMaxMs)
|
|
|
|
|
|
|
|
wildEnd := outEnd.Add(wildDur)
|
|
|
|
|
|
|
|
hm.Excursion.WildUntil = wildEnd
|
|
|
|
|
|
|
|
hm.Excursion.ReturnUntil = wildEnd.Add(time.Duration(cfg.AdventureReturnDurationMs) * time.Millisecond)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// advanceExcursionPhases progresses through out->wild->return and returns true when complete.
|
|
|
|
// advanceExcursionPhases progresses through out->wild->return and returns true when complete.
|
|
|
|
func (hm *HeroMovement) advanceExcursionPhases(now time.Time) (ended bool) {
|
|
|
|
func (hm *HeroMovement) advanceExcursionPhases(now time.Time) (ended bool) {
|
|
|
|
exc := &hm.Excursion
|
|
|
|
exc := &hm.Excursion
|
|
|
|
cfg := tuning.Get()
|
|
|
|
if exc.Phase == model.ExcursionOut && !now.Before(exc.OutUntil) {
|
|
|
|
if exc.Phase == model.ExcursionOut {
|
|
|
|
exc.Phase = model.ExcursionWild
|
|
|
|
outEnd := exc.StartedAt.Add(time.Duration(cfg.AdventureOutDurationMs) * time.Millisecond)
|
|
|
|
|
|
|
|
if !now.Before(outEnd) {
|
|
|
|
|
|
|
|
exc.Phase = model.ExcursionWild
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if exc.Phase == model.ExcursionWild && !now.Before(exc.WildUntil) {
|
|
|
|
if exc.Phase == model.ExcursionWild && !now.Before(exc.WildUntil) {
|
|
|
|
exc.Phase = model.ExcursionReturn
|
|
|
|
exc.Phase = model.ExcursionReturn
|
|
|
|
|
|
|
|
// Only recalculate return duration if we haven't already passed the original deadline
|
|
|
|
|
|
|
|
// (handles large time jumps from offline catch-up or timer-based exits).
|
|
|
|
|
|
|
|
if now.Before(exc.ReturnUntil) {
|
|
|
|
|
|
|
|
speed := hm.Speed
|
|
|
|
|
|
|
|
if speed < 0.1 {
|
|
|
|
|
|
|
|
speed = 0.1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
exc.WildUntil = now
|
|
|
|
|
|
|
|
exc.ReturnUntil = now.Add(time.Duration(exc.DepthWorldUnits / speed * float64(time.Second)))
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if exc.Phase == model.ExcursionReturn && !now.Before(exc.ReturnUntil) {
|
|
|
|
if exc.Phase == model.ExcursionReturn && !now.Before(exc.ReturnUntil) {
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
@ -1321,8 +1339,34 @@ func (hm *HeroMovement) beginRoadsideRest(now time.Time) {
|
|
|
|
hm.Hero.State = model.StateResting
|
|
|
|
hm.Hero.State = model.StateResting
|
|
|
|
hm.ActiveRestKind = model.RestKindRoadside
|
|
|
|
hm.ActiveRestKind = model.RestKindRoadside
|
|
|
|
hm.RestHealRemainder = 0
|
|
|
|
hm.RestHealRemainder = 0
|
|
|
|
dur := randomDurationBetweenMs(cfg.RoadsideRestMinMs, cfg.RoadsideRestMaxMs)
|
|
|
|
|
|
|
|
hm.RestUntil = now.Add(dur)
|
|
|
|
depth := cfg.RoadsideRestDepthWorldUnits
|
|
|
|
|
|
|
|
if depth <= 0 {
|
|
|
|
|
|
|
|
depth = 12.0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hm.refreshSpeed(now)
|
|
|
|
|
|
|
|
speed := hm.Speed
|
|
|
|
|
|
|
|
if speed < 0.1 {
|
|
|
|
|
|
|
|
speed = 0.1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
moveDur := time.Duration(depth / speed * float64(time.Second))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
outUntil := now.Add(moveDur)
|
|
|
|
|
|
|
|
restDur := randomDurationBetweenMs(cfg.RoadsideRestMinMs, cfg.RoadsideRestMaxMs)
|
|
|
|
|
|
|
|
wildUntil := outUntil.Add(restDur)
|
|
|
|
|
|
|
|
returnUntil := wildUntil.Add(moveDur)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hm.Excursion = model.ExcursionSession{
|
|
|
|
|
|
|
|
Phase: model.ExcursionOut,
|
|
|
|
|
|
|
|
StartedAt: now,
|
|
|
|
|
|
|
|
OutUntil: outUntil,
|
|
|
|
|
|
|
|
WildUntil: wildUntil,
|
|
|
|
|
|
|
|
ReturnUntil: returnUntil,
|
|
|
|
|
|
|
|
DepthWorldUnits: depth,
|
|
|
|
|
|
|
|
RoadFreezeWaypoint: hm.WaypointIndex,
|
|
|
|
|
|
|
|
RoadFreezeFraction: hm.WaypointFraction,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hm.RestUntil = returnUntil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (hm *HeroMovement) beginAdventureInlineRest(now time.Time) {
|
|
|
|
func (hm *HeroMovement) beginAdventureInlineRest(now time.Time) {
|
|
|
|
@ -1422,16 +1466,30 @@ func ProcessSingleHeroMovementTick(
|
|
|
|
|
|
|
|
|
|
|
|
switch hm.ActiveRestKind {
|
|
|
|
switch hm.ActiveRestKind {
|
|
|
|
case model.RestKindRoadside:
|
|
|
|
case model.RestKindRoadside:
|
|
|
|
hm.applyRestHealTick(dt)
|
|
|
|
excursionEnded := hm.advanceExcursionPhases(now)
|
|
|
|
cfg := tuning.Get()
|
|
|
|
if hm.Excursion.Phase == model.ExcursionWild {
|
|
|
|
hpFrac := float64(hm.Hero.HP) / float64(hm.Hero.MaxHP)
|
|
|
|
hm.applyRestHealTick(dt)
|
|
|
|
if now.After(hm.RestUntil) || hpFrac >= cfg.RoadsideRestExitHp {
|
|
|
|
}
|
|
|
|
|
|
|
|
if excursionEnded {
|
|
|
|
|
|
|
|
hm.endExcursion(now)
|
|
|
|
hm.ActiveRestKind = model.RestKindNone
|
|
|
|
hm.ActiveRestKind = model.RestKindNone
|
|
|
|
hm.RestUntil = time.Time{}
|
|
|
|
hm.RestUntil = time.Time{}
|
|
|
|
hm.RestHealRemainder = 0
|
|
|
|
hm.RestHealRemainder = 0
|
|
|
|
hm.State = model.StateWalking
|
|
|
|
hm.State = model.StateWalking
|
|
|
|
hm.Hero.State = model.StateWalking
|
|
|
|
hm.Hero.State = model.StateWalking
|
|
|
|
hm.refreshSpeed(now)
|
|
|
|
hm.refreshSpeed(now)
|
|
|
|
|
|
|
|
} else if hm.Excursion.Phase == model.ExcursionWild {
|
|
|
|
|
|
|
|
cfg := tuning.Get()
|
|
|
|
|
|
|
|
hpFrac := float64(hm.Hero.HP) / float64(hm.Hero.MaxHP)
|
|
|
|
|
|
|
|
if hpFrac >= cfg.RoadsideRestExitHp {
|
|
|
|
|
|
|
|
hm.Excursion.Phase = model.ExcursionReturn
|
|
|
|
|
|
|
|
speed := hm.Speed
|
|
|
|
|
|
|
|
if speed < 0.1 {
|
|
|
|
|
|
|
|
speed = 0.1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hm.Excursion.WildUntil = now
|
|
|
|
|
|
|
|
hm.Excursion.ReturnUntil = now.Add(time.Duration(hm.Excursion.DepthWorldUnits / speed * float64(time.Second)))
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hm.SyncToHero()
|
|
|
|
hm.SyncToHero()
|
|
|
|
if sender != nil && hm.Hero != nil {
|
|
|
|
if sender != nil && hm.Hero != nil {
|
|
|
|
|