aboutsummaryrefslogtreecommitdiff
path: root/internal/game/exit_display.go
blob: 45406691fadd102401ecc0a0bce4ccc2b4f10fe9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package game

import (
	"thehouseoficarus/internal/net"
	"thehouseoficarus/internal/world"
)

// exitDisplay summarizes how an exit appears to a player: whether it is listed
// at all (and traversable by the map BFS / walk pathfinder), whether movement
// is refused (and the look listing should tag it "(blocked)"), and whether the
// listing should tag it "(HIDDEN)".
//
// It is the single source of truth shared by the look listing, the standalone
// `exits` command, `look <direction>`, movement, the map connector state, the
// `verbs` command, and the walk pathfinder — replacing the per-callsite inline
// checks that previously disagreed (e.g. gods saw a "(blocked)" tag in look
// even though they can traverse and the map renders the link open).
//
// Semantics:
//   - Hidden exit: visible only once discovered (gods always discover). When
//     visible it is tagged "(HIDDEN)". A nil player (background render) never
//     discovers, so hidden exits are absent.
//   - God mode: exits are visible and never blocked (matches map rendering).
//   - always_blocked: absolute — blocks movement/listing/map regardless of any
//     condition. The condition is NOT evaluated when always_blocked is set.
//   - condition: blocks when present and failing for this player.
type exitDisplay struct {
	Visible   bool
	Blocked   bool
	TagHidden bool
}

func (g *Game) exitDisplayState(sess *net.Session, roomID int, dir world.ExitDir, exit world.ExitDef) exitDisplay {
	var ed exitDisplay
	p := sessPlayer(sess)
	if exit.Hidden {
		// exitDiscovered already returns true for gods; nil player never discovers.
		if p != nil && g.exitDiscovered(p, roomID, dir) {
			ed.Visible = true
			ed.TagHidden = true
		}
		return ed
	}
	ed.Visible = true
	if p == nil || p.GodMode {
		return ed
	}
	if exit.AlwaysBlocked {
		ed.Blocked = true
		return ed
	}
	if exit.Condition != nil && !g.checkCondition(sess, exit.Condition) {
		ed.Blocked = true
	}
	return ed
}