adventure: let a player back out from the web, not only in Matrix
Three verbs to match gogobee's: call off an expedition, turn back out of somebody else's party, send the pet sitter home. Which one this page offers is derived here rather than pushed — leadership is already legible in the party seats and the sitter's standing is already in the babysit offer, so nothing new crosses the wire. Two things running it turned up that no test would have. An applied abandon left "Pull out of the run" sitting under a verdict saying the expedition was over, so an applied verb now also hides the other verbs it just made untrue. And a party member was being offered that same button in the first place, beside the one that actually works — Pete knows from the seat it just read that gogobee would refuse it, so it is withheld. Also: heal the Matrix handle onto push rows stored before the column existed, on its own endpoint rather than through the subscribe upsert, which resets both watermarks and would have silenced the digest for anybody who reads the site regularly. And stack the board row below sm — four flex columns that wrapped to six lines on a phone, pre-existing.
This commit is contained in:
@@ -110,6 +110,75 @@ func centiXP(centi int) string {
|
||||
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.2f", float64(centi)/100), "0"), ".")
|
||||
}
|
||||
|
||||
// rosterStatusExpedition is the one roster status that means "down there right
|
||||
// now". Spelled out here because W9's offers turn on it and a typo would silently
|
||||
// hide a button rather than fail.
|
||||
const rosterStatusExpedition = "expedition"
|
||||
|
||||
// offersToUndo decides which of the three W9 verbs this owner's page proposes.
|
||||
// Courtesy only, like every offer on this page: gogobee re-resolves all three and
|
||||
// its refusal is the real answer. What this buys is a page that does not put
|
||||
// "Leave the party" in front of somebody standing in town.
|
||||
//
|
||||
// Nothing new crosses the wire for it. Leadership is already legible in the party
|
||||
// seats gogobee pushes (W7), and the sitter's standing is already in the babysit
|
||||
// offer (W5b) — so the two facts the buttons need were both already here.
|
||||
// haveParty says whether the public detail blob decoded at all, and it is
|
||||
// load-bearing rather than defensive. An empty seat list means "solo" ONLY when
|
||||
// we have actually read the sheet; a blob that did not decode — a gogobee too old
|
||||
// to push seats, a truncated column, a shape change — produces the same empty
|
||||
// slice, and treating that as solo would offer a party MEMBER the button that
|
||||
// throws away everyone's day. Found by getting a fixture wrong: the page did
|
||||
// exactly that, silently and convincingly.
|
||||
func offersToUndo(token, status string, haveParty bool, party []partySeat, self storage.PlayerDetail) (abandon, leave, cancelSitter bool) {
|
||||
// The sitter first, because it is the only one of the three whose fact does
|
||||
// not go stale: an engagement is a property of the character, not of where
|
||||
// they are standing, so a two-minute-old snapshot is still right about it.
|
||||
cancelSitter = self.Babysit != nil && self.Babysit.Active
|
||||
|
||||
if status == rosterStatusExpedition && haveParty {
|
||||
if len(party) == 0 {
|
||||
// A SOLO run publishes no party at all — partySeatViews returns nil
|
||||
// below two seats — so an empty list here is not "we don't know", it is
|
||||
// "there is nobody else", which makes this player the leader.
|
||||
abandon = true
|
||||
} else {
|
||||
// With a party, offer strictly on the viewer's own seat, and offer
|
||||
// nothing at all if we cannot find it. Guessing in that case would mean
|
||||
// showing a member the button that throws away everyone's day.
|
||||
for _, seat := range party {
|
||||
if seat.Token != token {
|
||||
continue
|
||||
}
|
||||
switch seat.Kind {
|
||||
case "leader":
|
||||
abandon = true
|
||||
case "member":
|
||||
leave = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An extracted expedition is still the owner's to close, and it is the case
|
||||
// the status check above cannot see: its owner reads as idle in town, with no
|
||||
// party, and the resume offer is the only sign the run is still open. Without
|
||||
// this, a leader who wanted out had to pay to walk back in first — the exact
|
||||
// hole the game's own abandon path was widened to cover.
|
||||
//
|
||||
// Not while they are sitting in somebody ELSE'S party, though, and that is not
|
||||
// a hypothetical: a player who extracted their own run and then took a seat
|
||||
// has both facts true at once, about two different expeditions. Both buttons
|
||||
// on one page would be asking the reader to work out which run each meant,
|
||||
// and this page is about the one they are standing in. The abandon is still
|
||||
// there from town the moment they walk out of the party.
|
||||
if self.Resume != nil && !leave {
|
||||
abandon = true
|
||||
}
|
||||
return abandon, leave, cancelSitter
|
||||
}
|
||||
|
||||
// whoMap is the fog-of-war zone graph as gogobee cut it: visited rooms with
|
||||
// their true kind, plus the one-hop frontier of doors whose rooms are withheld
|
||||
// (kind "unknown"). Pete lays it out and draws it; it never receives node
|
||||
@@ -164,6 +233,13 @@ type whoPage struct {
|
||||
RunLog RunLogView
|
||||
HasSelf bool
|
||||
Self storage.PlayerDetail
|
||||
// The three W9 verbs that undo something. Unlike every offer above them these
|
||||
// are derived on Pete rather than pushed: leadership is already legible in the
|
||||
// party seats, and a sitter's standing is already in the babysit offer, so
|
||||
// there was nothing to add to the wire. See offersToUndo.
|
||||
CanAbandon bool
|
||||
CanLeave bool
|
||||
CanCancelSitter bool
|
||||
// The private panels, wrapped so a row knows where it is sitting. Bond state
|
||||
// only means something on a worn item — see itemRow.
|
||||
Worn []itemRow
|
||||
@@ -325,6 +401,8 @@ func (s *Server) handleAdventureWho(w http.ResponseWriter, r *http.Request) {
|
||||
page.BondsUsed++
|
||||
}
|
||||
}
|
||||
page.CanAbandon, page.CanLeave, page.CanCancelSitter =
|
||||
offersToUndo(token, entry.Status, page.HasDetail, page.Detail.Party, self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user