-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspell.py
140 lines (112 loc) · 3.35 KB
/
spell.py
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
from enum import Enum
from utils import *
from projectile import SpellProjectile
class SpellCategory(Enum):
BENEFICIAL = 1
HARMFUL = -1
class SpellAOEType(Enum):
NONE = 0
SINGLE = 1
RADIUS = 2
class Spell:
g = None
def __init__(self, name, category, spell_range=5,
att_save=None, power=10, proj_name=None,
u_resist_msg="", mon_resist_msg="", aoe_type=SpellAOEType.NONE
):
#Category can be one of: Beneficial, Harmful
self.name = name
self.proj_name = proj_name
self.category = category
self.power = power
self.range = spell_range
self.u_resist_msg = u_resist_msg
self.mon_resist_msg = mon_resist_msg
self.att_save = att_save
def saving_throw(self, target):
if not self.att_save:
return False
match self.att_save:
case "DEX":
stat = target.DEX
case "WIS":
stat = target.WIS
case _:
return False
save_roll = gauss_roll(stat_mod(stat))
return save_roll >= self.power
def chance_to_hit(self, caster, target):
if (proj := self.get_projectile(stat_mod(caster.INT))):
return proj.to_hit_prob(caster, target)
elif self.att_save == "DEX":
return gauss_roll_prob(stat_mod(target.DEX), self.power)
else:
return 100.0
def chance_to_affect(self, caster, target):
if self.att_save == "WIS":
return gauss_roll_prob(stat_mod(target.WIS), self.power)
else:
return 100.0
def do_spell_effect(self, attacker, pos):
g = self.g
ent = g.entity_at(pos)
if ent:
self.on_hit(attacker, ent)
def on_hit(self, caster, ent):
if not self.can_affect(ent):
ent.add_msg_u_or_mons("You are unaffected.", f"{ent.get_name(True)} is unaffected.")
elif self.saving_throw(ent):
mon_msg = self.mon_resist_msg.replace("<monster>", ent.get_name())
if mon_msg.startswith(ent.get_name()):
mon_msg = mon_msg.capitalize()
ent.add_msg_u_or_mons(self.u_resist_msg, mon_msg)
else:
self.do_effect(caster, ent)
def can_affect(self, target):
return True
def get_projectile(self, acc=0):
if self.att_save == "ranged":
return SpellProjectile(self, accuracy=acc, name=self.proj_name, max_range=self.range)
return None
def cast(self, caster, target):
g = self.g
use_projectile = self.att_save == "ranged"
caster.use_energy(100)
if use_projectile:
acc = stat_mod(caster.INT)
proj = self.get_projectile(acc)
caster.shoot_projectile_at(target.pos, proj)
else:
g.display_projectile_animation(caster.pos, target.pos)
self.do_spell_effect(caster, target.pos)
def do_effect(self, caster, target):
pass
class FlameSpell(Spell):
def __init__(self):
super().__init__(
"Flame",
proj_name="flame",
category=SpellCategory.HARMFUL,
spell_range=7,
att_save="ranged"
)
def do_effect(self, caster, target):
damage = target.apply_armor(dice(1, 8))
msg = make_damage_msg(f"The flame hits {target.get_name()}.", caster, damage)
target.combat_msg(msg)
target.take_damage(damage, caster)
class ConfusionSpell(Spell):
def __init__(self):
super().__init__(
"Confusion",
category=SpellCategory.HARMFUL,
spell_range=16,
att_save="WIS",
power=11,
u_resist_msg="You resist the effect.",
mon_resist_msg="<monster> resists the effect."
)
def can_affect(self, target):
return not target.is_immune_status("Confused")
def do_effect(self, caster, target):
target.add_status("Confused", rng(10, 30))