Skip to content

Commit 655517f

Browse files
committed
Implemented teleporting mages.
1 parent 71405c6 commit 655517f

File tree

4 files changed

+93
-9
lines changed

4 files changed

+93
-9
lines changed

simalq/geometry.hy

+4-1
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,11 @@
119119
#(@x @y))
120120

121121
(defmeth __add__ [direction]
122+
(@+n 1 direction))
123+
124+
(defmeth +n [n direction]
122125
(try
123-
(Pos @map (+ @x direction.x) (+ @y direction.y))
126+
(Pos @map (+ @x (* n direction.x)) (+ @y (* n direction.y)))
124127
(except [GeometryError]))))
125128

126129
(hy.repr-register Pos str)

simalq/tile/monster.hy

+50-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
fractions [Fraction :as f/]
88
enum [Enum]
99
toolz [unique]
10-
simalq.util [DamageType StatusEffect next-in-cycle mixed-number]
10+
simalq.util [DamageType StatusEffect next-in-cycle mixed-number seq]
1111
simalq.geometry [Direction at dist adjacent? dir-to turn-and-pos-seed ray]
1212
simalq.game-state [G]
1313
simalq.tile [Tile Actor Damageable]
@@ -65,15 +65,16 @@
6565
(not (adjacent? (or mon-pos @pos) G.player.pos))
6666
(not @sees-invisible)))
6767

68-
(defmeth try-to-attack-player [[dry-run F] [shots-ignore-obstacles F]]
68+
(defmeth try-to-attack-player [[dry-run F] [shots-ignore-obstacles F] [pos None]]
6969
"Try to melee or shoot the player, if the monster can. Return true
7070
if it succeeded. If `dry-run` is true, the attack isn't actually
7171
made."
7272

73-
(setv d (dir-to @pos G.player.pos))
73+
(setv pos (or pos @pos))
74+
(setv d (dir-to pos G.player.pos))
7475
(setv attack None)
7576

76-
(when (= @pos G.player.pos)
77+
(when (= pos G.player.pos)
7778
; If we're on the player's square, we can't attack her.
7879
(return F))
7980

@@ -83,14 +84,14 @@
8384
; Try a melee attack first.
8485
(when (and
8586
@damage-melee
86-
(adjacent? @pos G.player.pos)
87-
(in (get (walkability @pos d :monster? T) 1)
87+
(adjacent? pos G.player.pos)
88+
(in (get (walkability pos d :monster? T) 1)
8889
['bump 'walk]))
8990
(setv attack 'melee))
9091

9192
; Otherwise, try a ranged attack.
9293
(when (and (not attack) @damage-shot)
93-
(for [target (ray :pos @pos :direction d :length
94+
(for [target (ray :pos pos :direction d :length
9495
(min (or @shot-range Inf) G.rules.reality-bubble-size))]
9596
(when (= target G.player.pos)
9697
(setv attack 'shot)
@@ -747,6 +748,48 @@
747748
:flavor "A wretched organic structure, fashioned from fiendish flesh. It twists about to aim its toothy maw, from which it belches flame. It's immobile, but dark magics make it almost invulnerable… almost.")
748749
749750
751+
(deftile "W " "a teleporting mage" Monster
752+
:color 'purple
753+
:iq-ix 186 ; invisible mage
754+
:destruction-points 100
755+
756+
:field-defaults (dict
757+
:shot-power (f/ 0))
758+
:mutable-fields #("shot_power")
759+
:info-bullets (meth [#* extra]
760+
(.info-bullets (super)
761+
#("Shot power" @shot-power)))
762+
763+
:damage-shot 10
764+
:!shot-frequency (f/ 3 4)
765+
766+
:act (meth []
767+
(doc f"Teleport Attack — If the monster has line of sight to you, it adds {@shot-frequency} to its shot power. If this is ≥1, it subtracts 1 to shoot you. If it doesn't shoot you, it tries to teleport into line of sight, preferring to be as close as possible to you without being adjacent. Its destination must lie in the reality bubble.")
768+
; Teleporting mages' movement is much smarter than that of IQ's
769+
; invisible mages, which compensates for the loss of their main
770+
; ability.
771+
(when (@player-invisible-to?)
772+
(return))
773+
(when (@try-to-attack-player :dry-run T)
774+
(+= @shot-power @shot-frequency)
775+
(when (pop-integer-part @shot-power)
776+
(@try-to-attack-player)
777+
(return)))
778+
(for [
779+
dist-from-player [#* (seq 2 G.rules.reality-bubble-size) 1]
780+
direction Direction.all
781+
:if (setx target (.+n G.player.pos dist-from-player direction))]
782+
(when (and
783+
(or
784+
(= target @pos)
785+
(not (nogo? target :monster? T :ethereal-to #())))
786+
(@try-to-attack-player :dry-run T :pos target))
787+
(@move target)
788+
(return))))
789+
790+
:flavor "This academic has perfected the art of avoiding faculty meetings, thesis committees, institutional review boards, classes, and his own office hours, preferring instead to transport himself to conferences in tropical destinations. His strategy for evaluating students (such as yourself) is to observe them from afar and see how they perform under pressure.")
791+
792+
750793
(deftile "W " "an archmage" Approacher
751794
:iq-ix 163
752795
:destruction-points 75

simalq/tile/unimplemented.hy

-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
[183 "magical_key"]
4545
[184 "giant_ant"]
4646
[185 "dark_brain"]
47-
[186 "invisible_mage"]
4847
[187 "magical_mirror"]
4948
[190 "wall_generator_north"]
5049
[191 "wall_generator_south"]

tests/test_monster.hy

+39
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,45 @@
777777
(assert (= G.score 150)))
778778

779779

780+
(defn test-teleporting-mage []
781+
(defn check [text-map]
782+
(init [:map text-map :map-marks {
783+
"1 " "teleporting mage"
784+
"2 " 'floor}])
785+
(wait)
786+
(assert-textmap text-map :map-marks {
787+
"1 " 'floor
788+
"2 " "teleporting mage"}))
789+
790+
; Teleporting mages prefer to be distance 2 to the north.
791+
(check "
792+
. . . . . . .
793+
. . . 2 . . .
794+
. . . . . . .
795+
. . . @ . . .
796+
. 1 . . . . .
797+
. . . . . . .
798+
. . . . . . .")
799+
; They'll try farther distances if they can't get distance 2.
800+
(check "
801+
. . . . . . .
802+
. ██. ██. ██.
803+
. . . . . . .
804+
. ██. @ . ██.
805+
. 1 . . . . .
806+
. ██. ██. ◀▶.
807+
. . . . . . 2")
808+
; And adjacency if they can't get farther.
809+
(check "
810+
. . . . . . .
811+
. ██. ██. ██.
812+
. . . ◀▶◀▶. .
813+
. ██2 @ ◀▶██.
814+
. 1 ◀▶◀▶◀▶. .
815+
. ██. ██. ██.
816+
. . . . . . ."))
817+
818+
780819
(defn test-archmage []
781820
(init [:tiles [
782821
"archmage"

0 commit comments

Comments
 (0)