Skip to content
Merged
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
14 changes: 11 additions & 3 deletions xsofy/status.lg
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
;; :ttl — turns remaining
;; :dmg — damage per tick (optional)
;; :skip-turn — entity can't act (optional)
;; :no-decay — ttl is owned by a dedicated system; the generic tick
;; leaves it alone (optional)
;; :color — tint for rendering

(def status-defs
Expand All @@ -15,7 +17,11 @@
:confused {:color [200 100 255] :bg [40 20 50] :name "confused"}
:slowed {:color [140 140 180] :bg [30 30 40] :name "slowed"}
:haste {:color [255 255 100] :bg [40 40 15] :name "haste"}
:webbed {:color [200 200 210] :bg [30 30 32] :name "webbed"}
;; :webbed ttl counts remaining struggles; the movement struggle branches
;; (world/player-move, ai/try-move) decrement it and trample the web on
;; the last one. Generic decay raced them and freed the creature with the
;; web intact (#161), so the tick must not touch it.
:webbed {:color [200 200 210] :bg [30 30 32] :name "webbed" :no-decay true}
:swimming {:color [60 100 180] :bg [10 20 50] :name "swimming"}})

;; --- Apply / Remove ---
Expand Down Expand Up @@ -51,8 +57,10 @@
world
(reduce
(fn [w [stype sdata]]
;; Bail out if a prior tick (or external code) removed the entity.
(if (nil? (get-in w [:entities entity-id]))
;; Bail out if a prior tick (or external code) removed the entity,
;; or if the status decays through its own system (:no-decay).
(if (or (nil? (get-in w [:entities entity-id]))
(:no-decay (get status-defs stype)))
w
(let [def_ (get status-defs stype)
ttl (dec (or (:ttl sdata) 1))
Expand Down
1 change: 1 addition & 0 deletions xsofy/test/run.lg
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
[xsofy.test.dag-test]
[xsofy.test.container-trigger-test]
[xsofy.test.fire-test]
[xsofy.test.status-test]
[xsofy.test.seed-test]
[xsofy.test.mapgen-audit-test]
[xsofy.test.replay-test]
Expand Down
40 changes: 40 additions & 0 deletions xsofy/test/status_test.lg
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
(ns xsofy.test.status-test
(:require [test :refer [deftest is testing]]
[xsofy.terrain :as terrain]
[xsofy.fire :as fire]
[xsofy.status :as status]
[xsofy.world :as world]))

;; Regression tests for #161: webs no longer trampled.
;;
;; :webbed ttl is decremented by the movement struggle branches, which
;; trample the web on the last struggle. The generic status tick also
;; decremented it, so the ttl drained twice per turn and expired inside
;; the tick — which removes the status silently, without trampling. The
;; struggle branch then never saw ttl <= 1 and the web survived every
;; escape. :webbed is now :no-decay: the struggle branches own its ttl.

Comment thread
mparrett marked this conversation as resolved.
(deftest generic-tick-leaves-webbed-alone
(testing ":no-decay statuses keep their ttl through the generic tick"
(let [w {:entities {:e {:id :e :pos [1 1]}}}
w (status/apply-status w :e :webbed 2)
w (status/apply-status w :e :confused 2)
Comment thread
mparrett marked this conversation as resolved.
w (status/tick-entity-statuses w :e)]
;; still webbed, ttl untouched
(is (= 2 (get-in w [:entities :e :statuses :webbed :ttl])))
;; the normal decay path still works
(is (= 1 (get-in w [:entities :e :statuses :confused :ttl])))
(let [w (status/tick-entity-statuses w :e)]
(is (status/has-status? w :e :webbed))
(is (not (status/has-status? w :e :confused)))))))

(deftest webbed-player-break-free-tramples-web
(testing "player struggling free of a web leaves ash, not an intact web"
(let [w (world/make-world 30 20 1)
[px py] (get-in w [:entities :player :pos])
_ (terrain/tset! (:terrain w) (:width w) px py fire/web-tile)
w (status/apply-status w :player :webbed 2)
w (world/player-move w 1 0) ;; struggle: ttl 2 -> 1
w (world/player-move w 1 0)] ;; final struggle: break free + trample
(is (not (status/has-status? w :player :webbed)))
(is (= fire/ash-tile (terrain/tget (:terrain w) (:width w) px py))))))
Loading