forked from sirvaulterscoff/darktower-rl
/
critters.py
231 lines (203 loc) · 7.56 KB
/
critters.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import math
import gl
import util
from thirdparty.libtcod import libtcodpy as libtcod
WALKING = 1
FLYING = 2
UNDEAD = 1 << 3
COLD_BLOODED = 1 << 4
PASS_THRU_WALLS = 1 << 5
PASS_THRU_DOORS = 1 << 6
INTELLIGENT = 1 << 7
class Critter(object):
name = 'crit'
ALL = []
char = '@'
color = [255, 255, 255]
x = 0
y = 0
regen_rate = 1
flags = WALKING
description_past = 'Generic critter from the past'
description_present = 'Generic critter from present time'
description_future = 'Generic critter from distant future'
base_hd = 1
base_hp = 1
base_mp = 1
speed = 1
base_ac = 0
base_dmg = [(1, 1)]
dlvl = 1
inven = []
common = 10
__metaclass__ = util.AutoAdd
__meta_field__ = ALL
__meta_skip__ = "skip_register"
skip_register = True
fov_range = 5
ai = None
hp = base_hp
mp = base_mp
xp = 1
def __init__(self):
self.map = None
pass
def adjust_hd(self, new_hd):
#TODO implement HD rebasing
self.base_hd = new_hd
pass
def place(self, x, y, map):
self.x, self.y = x, y
self.map = map
def move(self, dx, dy):
newx, newy = self.x + dx, self.y + dy
if self.map.has_critter_at((newx, newy)):
return
player = self.map.player
if player.x == newx and player.y == newy:
self.attack(player)
return
next_tile = self.map[newy][newx]
if next_tile.passable():
self.map.critter_xy_cache.pop((self.x, self.y))
self.x, self.y = newx, newy
self.map.critter_xy_cache[(self.x, self.y)] = self
def attack(self, whom):
dmgs = []
for attack in self.base_dmg:
dmg = util.roll(*attack)
#let's roll 1d20 + HD for now, assuming that monster
# can hit player no lees than 30% of the time
# (thus checking it to be > 14)
#TODO add player's/monsters' evade to calculations
if util.roll(1, 20, self.base_hd) >= 14:
dmgs.append(dmg)
else:
gl.message(self.name.capitalize() + ' misses ' + whom.name, 1)
whom.take_damage(self, dmgs, attack)
def move_towards(self, target_x, target_y):
dx = target_x - self.x
dy = target_y - self.y
distance = math.sqrt(dx ** 2 + dy ** 2)
dx = int(round(dx / distance))
dy = int(round(dy / distance))
self.move(dx, dy)
def see_player(self):
player = self.map.player
see_range = self.fov_range
#if it's intelligent one - let it follow source of light
if self.flags & INTELLIGENT:
see_range += player.fov_range / 2
if libtcod.map_is_in_fov(self.map.fov_map, self.x, self.y):
d = util.distance(self.x, self.y, player.x, player.y)
if d <= see_range:
return d
return None
def take_turn(self):
if self.ai is None:
if self.see_player():
player = self.map.player
self.move_towards(player.x, player.y)
def take_damage(self, mob, dmgs, attack):
for dmg in dmgs:
for i in range(0, self.base_ac + 1):
if util.coinflip(): dmg -= 1
if dmg <= 0: break
if dmg > 0 and self.hp > 0:
gl.message(mob.name.capitalize() + ' hits ' + self.name + ' for ' + str(dmg) + ' damage.', 5)
self.hp -= dmg
if isinstance(self, Player):
if self.hp <= self.base_hp * gl.__hp_warning__:
gl.message('Low HP!!!', 'WARN')
elif self.hp > 0:
gl.message (mob.name.capitalize() + ' fails to harm ' + self.name, 1)
if self.hp <= 0:
self.die(mob)
def die(self, killer):
gl.message(self.name.capitalize() + ' dies', 1)
if isinstance(killer, Critter):
killer.earn_exp(self)
self.map.remove_critter(self)
#todo let's introduce an item class righ here and make mobs leave corpses=items
def earn_exp(self, src):
#method written beforehand, when we will have alies or whatever
#we're adjusting HD, taking in acount killed mob hd and dlvl
#basicaly we make mob.hd / self.hd, same for dlv
# then we multiply this values by 1/self.dlvl
#so that more advanced mob lvl-up slower (at 1/27 rate for killing mob of their lvl+hd)
hd_delta = src.base_hd / self.base_hd
dlvl_delta = src.dlvl / self.dlvl
self.adjust_hd(self.base_hd + (hd_delta + dlvl_delta - 1) * 1 / self.dlvl)
class Player(Critter):
x, y = 0, 0
char = '@'
color = [255, 255, 255]
skip_register = True
fov_range = 10
base_hp = 10
base_mp = 10
mp = 10
hp = base_hp
name = 'you'
xl = 1
xp = 0
def __init__(self):
self.map = None
def move(self, dx, dy):
newx, newy = self.x + dx, self.y + dy
if self.map.has_critter_at((newx, newy)):
self.attack(self.map.get_critter_at(newx, newy))
return True
next_tile = self.map[newy][newx]
if next_tile.passable():
self.x, self.y = newx, newy
return True
else:
gl.message("You bump into wall")
return False
def die(self, killer):
gl.message( 'You die...', 'CRITICAL')
gl.__game_state__ = "died"
def earn_exp(self, src):
self.xp += src.xp
if self.xp > util.xp_for_lvl(self.xl):
self.lvl_up()
def lvl_up(self):
self.xl += 1
gl.message ("Congratulations! Level up", 'INFO')
class Rat(Critter):
char = 'r'
name = 'rat'
color = [240, 240, 240]
description_past = 'Obesity makes this plague-bearing rats realy huge. Interesting, can you even kill one that big...'
description_present = 'Huge, fat rat somehow managed to leave sewers or households and now posess enourmous threat to unwary adventurer.'
description_future = 'Strange mental-wave-immune creature, you have not seen ever before. According to records in central creature database (CCDB) these should be called rat'
base_hd = 1
base_hp = 3
speed = 2
class Bat(Critter):
char = 'w'
name = 'bat'
flags = FLYING
color = [0, 255, 60]
description_past = 'Strange green glow comes from afar... Maybe it\'s a lost sool seeking exit from endless caverns... Wait! It\'s a bat?! Ouch, stop biting me!'
description_present = 'Tiny bat emmiting a strange green glow, fliting high above the ground. If you can only catch one alive it can become a nice lantern.'
description_future = 'Flying creature, glowing with strange mutagenic radiation - it would be better if you don\'t touch it'
base_hd = 1
base_hp = 3
base_dmg = [(1, 1, 0, 150), (1, 1)]
move_speed = 2
common = 3
class Orc(Critter):
char = 'o'
name = 'orc'
flags = WALKING | INTELLIGENT
color = [255, 0, 0]
description_past = 'Beware! This mighty ugly-looking humanoid will eat you for dinner. Nightmare comes to live. By the way, there should be it\' friends somewhere nearby'
description_present = 'Surely you have read about orcs (remember all this books, about hobbits, elves and others). This one looks exactly... dissimilary.'
description_future = 'This creature looks as if it managed to escape from mad-scientist lab, where it was created. Several scars are spread along it\' torso, giving you hint that this creature is not going to chat with you.'
base_hd = 3
base_hp = 10
base_ac = 1
base_dmg = [(1, 3)]
dlvl = 3