diff options
| author | historia <[not public]> | 2026-06-24 20:58:25 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-24 20:58:25 -0400 |
| commit | 9d4b799db4868bcab291b83599ff088763cd4ed6 (patch) | |
| tree | 252f0fcc779acf35243983d643d6399ee2e0cd0b /internal/game/condition_test.go | |
| parent | d25638d98fe63efdeab570ef77e6a6a97f2d7a60 (diff) | |
| download | thehouseoficarus-9d4b799db4868bcab291b83599ff088763cd4ed6.tar.gz | |
feat: new type of non-violent 'combat': work
Diffstat (limited to 'internal/game/condition_test.go')
| -rw-r--r-- | internal/game/condition_test.go | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/internal/game/condition_test.go b/internal/game/condition_test.go new file mode 100644 index 0000000..5e5967d --- /dev/null +++ b/internal/game/condition_test.go @@ -0,0 +1,155 @@ +package game + +import ( + "testing" + + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" +) + +func sessWithFlags(flags map[string]any) *net.Session { + return &net.Session{Player: &player.Player{Flags: flags}} +} + +func TestIsTruthy(t *testing.T) { + cases := []struct { + v any + want bool + }{ + {nil, false}, + {true, true}, + {false, false}, + {0, false}, + {1, true}, + {int64(0), false}, + {int64(2), true}, + {0.0, false}, + {1.5, true}, + {"", false}, + {"x", true}, + } + for _, c := range cases { + if got := isTruthy(c.v); got != c.want { + t.Errorf("isTruthy(%v) = %v, want %v", c.v, got, c.want) + } + } +} + +func TestCheckConditionPlayerFlagTruthy(t *testing.T) { + g := &Game{WorldFlags: map[string]any{}} + + set := sessWithFlags(map[string]any{"x": true}) + unset := sessWithFlags(map[string]any{}) + + // {player_flag: x} -> set means present+truthy + posCond := &action.Condition{PlayerFlag: "x"} + if !g.checkCondition(set, posCond) { + t.Error("expected positive flag check to pass when flag is true") + } + if g.checkCondition(unset, posCond) { + t.Error("expected positive flag check to fail when flag is unset") + } + + // {player_flag: x, not: true} -> negative + negCond := &action.Condition{PlayerFlag: "x", Not: true} + if g.checkCondition(set, negCond) { + t.Error("expected negative flag check to fail when flag is true") + } + if !g.checkCondition(unset, negCond) { + t.Error("expected negative flag check to pass when flag is unset") + } + + // flag explicitly set to false counts as not-set for truthy checks + falseSet := sessWithFlags(map[string]any{"x": false}) + if g.checkCondition(falseSet, posCond) { + t.Error("expected positive check to fail when flag is false") + } + if !g.checkCondition(falseSet, negCond) { + t.Error("expected negative check to pass when flag is false") + } +} + +func TestCheckConditionValueComparisonPreserved(t *testing.T) { + g := &Game{WorldFlags: map[string]any{}} + sess := sessWithFlags(map[string]any{"n": 1}) + + if !g.checkCondition(sess, &action.Condition{PlayerFlag: "n", Value: 1}) { + t.Error("value match should pass for equal int") + } + if g.checkCondition(sess, &action.Condition{PlayerFlag: "n", Value: 2}) { + t.Error("value match should fail for unequal int") + } + if !g.checkCondition(sess, &action.Condition{PlayerFlag: "n", Value: 2, Not: true}) { + t.Error("negated value match should pass for unequal int") + } +} + +func TestCheckConditionWorldFlag(t *testing.T) { + g := &Game{WorldFlags: map[string]any{"gate_open": true}} + sess := sessWithFlags(map[string]any{}) + + if !g.checkCondition(sess, &action.Condition{Flag: "gate_open"}) { + t.Error("world flag truthy check should pass") + } + if g.checkCondition(sess, &action.Condition{Flag: "gate_open", Not: true}) { + t.Error("negated world flag should fail when set") + } + if g.checkCondition(sess, &action.Condition{Flag: "missing"}) { + t.Error("missing world flag should not pass") + } +} + +func TestResolveObjDescPresence(t *testing.T) { + g := &Game{WorldFlags: map[string]any{}} + + def := &object.ObjectDef{ + Descriptions: []object.ConditionalDesc{ + {Text: "lined", Condition: &action.Condition{PlayerFlag: "lined_up"}}, + {Text: "milling", Condition: &action.Condition{PlayerFlag: "lined_up", Not: true}}, + }, + } + + // not lined up -> milling, present + if text, present := g.resolveObjDesc(sessWithFlags(map[string]any{}), def); !present || text != "milling" { + t.Errorf("expected milling/present, got %q/%v", text, present) + } + // lined up -> lined, present + if text, present := g.resolveObjDesc(sessWithFlags(map[string]any{"lined_up": true}), def); !present || text != "lined" { + t.Errorf("expected lined/present, got %q/%v", text, present) + } + + // gated entirely off -> absent + gated := &object.ObjectDef{ + Descriptions: []object.ConditionalDesc{ + {Text: "x", Condition: &action.Condition{PlayerFlag: "done", Not: true}}, + }, + } + if _, present := g.resolveObjDesc(sessWithFlags(map[string]any{"done": true}), gated); present { + t.Error("expected object to be absent when no variant matches") + } + + // plain object (no Descriptions) always present + plain := &object.ObjectDef{Description: "plain"} + if text, present := g.resolveObjDesc(sessWithFlags(nil), plain); !present || text != "plain" { + t.Errorf("expected plain/present, got %q/%v", text, present) + } +} + +func TestRoomDescriptionSelection(t *testing.T) { + g := &Game{WorldFlags: map[string]any{}} + room := &world.Room{ + Description: "empty pad", + Descriptions: []world.RoomDesc{ + {Text: "crowd", Condition: &action.Condition{PlayerFlag: "done", Not: true}}, + }, + } + if got := g.roomDescription(sessWithFlags(map[string]any{}), room); got != "crowd" { + t.Errorf("first visit: want crowd, got %q", got) + } + if got := g.roomDescription(sessWithFlags(map[string]any{"done": true}), room); got != "empty pad" { + t.Errorf("revisit: want empty pad, got %q", got) + } +} |
