-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathbattle_model.py
193 lines (151 loc) · 5.93 KB
/
battle_model.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
from typing import Any
from data_types import Creature, Abilities, ConditionalAbility, CombatAbility
import random, math
class StatusEffect:
"""Very unfinished"""
def __init__(self, name: str, effect: Any, visible: bool, is_good: bool, conflicts_with: list[str] | None = None) -> None:
self.name = name
self.visible = visible
self.is_good = is_good
if conflicts_with is not None:
self.conflicts_with = conflicts_with
else:
self.conflicts_with = []
self.effect = effect # maybe dictionary, maybe callback function
class Unit:
"""A stack in battle"""
def __init__(self, creature: Creature, amount: int, is_illusion: bool = False, is_summon: bool = False) -> None:
self.creature = creature
c = creature
self.amount = amount
self.is_illusion = is_illusion
self.is_summon = is_summon
self.damage = c.damage
self.max_hp = c.hp
self.current_hp = c.hp
self.melee_attack = c.melee_attack
self.melee_defense = c.melee_defense
self.ranged_attack = c.ranged_attack
self.ranged_defense = c.ranged_defense
self.move = c.move
self.speed = c.speed
self.shots = c.shots
self.abilities = c.abilities
self.spells = c.spells
self.luck = c.luck
self.morale = c.morale
self.statuses: list[StatusEffect] = []
def add_status(self, status: StatusEffect) -> None:
# remove conflicting effects
statuses = [x for x in self.statuses if x.name not in status.conflicts_with]
# actually add it
statuses.append(status)
self.statuses = statuses
def update_state(self):
"""Apply effects, re-calculate all stat multipliers, etc"""
pass
def apply_melee_attack(source: Unit, target: Unit):
# damage = Attack x melee dmg / Defense
morale_probability = abs(source.morale * .1)
luck_probability = abs(target.luck * .1)
#luck is defensive
#morale effects turn order and offensive dmg
morale_strike = random.random() < morale_probability
lucky_strike = random.random() < luck_probability
attack = source.melee_attack
if morale_strike == True:
if source.morale > 0:
attack = source.melee_attack * 1.25
if source.morale < 0:
attack = source.melee_attack * .8
#calculate dmg range
min_damage, max_damage = source.damage
damage = 0.0
if source.amount <= 10:
for _ in range(source.amount):
damage += random.randint(min_damage, max_damage)
else:
multiplier = source.amount / 10
for _ in range(10):
damage += multiplier * random.randint(min_damage, max_damage)
#calculating damage
damage_dealt = attack * damage / target.melee_defense
if lucky_strike == True:
if target.luck > 0:
damage_dealt *= .67
if target.luck < 0:
damage_dealt *= 1.5
damage_dealt = int(damage_dealt) # damage rounding may not be exactly the same in original game
for ability in source.abilities:
if isinstance(ability, CombatAbility):
ability.apply_on_attack(source, target)
for ability in target.abilities:
if isinstance(ability, CombatAbility):
ability.apply_on_attack(source, target)
#Apply effective health pool change
total = target.current_hp + (target.max_hp * target.amount - 1)
remaining_hp = total - damage_dealt
remaining = math.ceil(remaining_hp/ source.max_hp)
top_health = remaining_hp % source.max_hp
# In this case im treating current_hp as top unit on the stacks hp
if remaining_hp <= 0:
target.amount = 0
target.current_hp = 0
else:
target.amount = remaining
target.current_hp = top_health
pass
def apply_range_attack(source: Unit, target: Unit):
#Attack x ragned dmg / defense
source.shots -= 1
morale_probability = abs(source.morale * .1)
luck_probability = abs(target.luck * .1)
#luck is defensive
#morale effects turn order and offensive dmg
morale_strike = random.random() < morale_probability
lucky_strike = random.random() < luck_probability
attack = source.ranged_attack
if morale_strike == True:
if source.morale > 0:
attack = source.ranged_attack * 1.25
if source.morale < 0:
attack = source.ranged_attack * .8
#calculate dmg range
min_damage, max_damage = source.damage
damage = 0.0
if source.amount <= 10:
for _ in range(source.amount):
damage += random.randint(min_damage, max_damage)
else:
multiplier = source.amount / 10
for _ in range(10):
damage += multiplier * random.randint(min_damage, max_damage)
#calculating damage
damage_dealt = attack * damage / target.ranged_defense
if lucky_strike == True:
if target.luck > 0:
damage_dealt *= .67
if target.luck < 0:
damage_dealt *= 1.5
damage_dealt = int(damage_dealt) # damage rounding may not be exactly the same in original game
for ability in source.abilities:
if isinstance(ability, CombatAbility):
ability.apply_on_attack(source, target)
for ability in target.abilities:
if isinstance(ability, CombatAbility):
ability.apply_on_attack(source, target)
#Apply effective health pool change
total = target.current_hp + (target.max_hp * target.amount - 1)
remaining_hp = total - damage_dealt
remaining = math.ceil(remaining_hp/ source.max_hp)
top_health = remaining_hp % source.max_hp
# In this case im treating current_hp as top unit on the stacks hp
if remaining_hp <= 0:
target.amount = 0
target.current_hp = 0
else:
target.amount = remaining
target.current_hp = top_health
pass
def apply_spell_attack(source: Unit, target: Unit):
pass