ae3b055b04466e158612bc91fb95596fc5ff5340
[roguelike.git] / creature.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # Copyright © 2008 Neil Moore <neil@s-z.org>.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #  
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 import random
21
22 import cacher
23 import loc
24 import thing
25 import ui
26
27 slotabbrevs = { 'hand': 'W', 'arm': 'A', 'body': 'B', 'head': 'H' }
28
29 class Creature(thing.Thing):
30     def __init__(self, location=None):
31         thing.Thing.__init__(self, location)
32
33         self.dead = False
34         self.hpmax = 10
35         self.hp = self.hpmax
36         self.oxygen = 10
37         
38         self.intrinsics = set()
39         self.extrinsics = set()
40
41         self.inv = loc.Inventory(self)
42         self.slots = { 'hand': None, 'arm': None, 'body': None, 'head': None }
43
44     def gain_intr(self, intrinsic, permanent = True):
45         if isinstance(intrinsic, thing.Intrinsic):
46             intrinsic = set((intrinsic,))
47         if isinstance(intrinsic, set):
48             if permanent:
49                 self.intrinsics |= intrinsic
50             else:
51                 self.extrinsics |= intrinsic
52         else:
53             raise Exception("Trying to gain non-intrinsic %s", intrinsic)
54     def lose_intr(self, intrinsic, permanent = True):
55         if isinstance(intrinsic, thing.Intrinsic):
56             intrinsic = set((intrinsic,))
57         if isinstance(intrinsic, set):
58             if permanent:
59                 self.intrinsics -= intrinsic
60             else:
61                 self.extrinsics -= intrinsic
62         else:
63             raise Exception("Trying to lose non-intrinsic %s", intrinsic)
64
65     def holding(self, name):
66         return self.slots[name]
67     def is_wielding(self, item):
68         return item in self.slots
69     def invalidate(self):
70         if self.location is not None:
71             self.location.invalidate(important = True)
72     def vis_radius(self):
73         return 5
74     def can_see(self, y, x):
75         loc = self.location
76         if (y - loc.y)**2 + (x - loc.x)**2 <= self.vis_radius()**2:
77             return self.level().visible_path(loc.y, loc.x, y, x)
78         return False
79     def visibles(self):
80         vis = []
81         py = self.location.y
82         px = self.location.x
83         rad = self.vis_radius()
84         (ymin,xmin) = self.level().clip(py - rad, px - rad)
85         (ymax,xmax) = self.level().clip(py + rad, px + rad)
86         for y in range(ymin, ymax+1):
87             for x in range(xmin, xmax+1):
88                 if self.can_see(y,x):
89                     vis.append((y,x))
90         return vis
91
92     def is_creature(self):
93         return True
94
95     def can_breathe(self):
96         if isinstance(self.location, loc.Water):
97             return thing.Intrinsic("waterbreathing") in (
98                     self.intrinsics | self.extrinsics)
99         else:
100             return True
101
102     def breathe(self):
103         if self.can_breathe():
104             if self.oxygen < 10:
105                 self.oxygen += 1
106         else:
107             if self.oxygen > 0:
108                 self.oxygen -= 1
109             else:
110                 self.losehp(1, "asphyxia")
111
112     def losehp(self, n, cause):
113         if n >= self.hp:
114             self.die(cause)
115         else:
116             self.hp -= n
117
118     def die(self, cause):
119         self.dead = cause
120         self.place(None)
121
122     def can_swim(self):
123         return False
124     def render(self, pic=None):
125         return ( "?", ui.Color('BrightMagenta').cp )
126     def move_north(self):
127         return self.try_move(self.location.north())
128     def move_south(self):
129         return self.try_move(self.location.south())
130     def move_west(self):
131         return self.try_move(self.location.west())
132     def move_east(self):
133         return self.try_move(self.location.east())
134     def pick_up(self):
135         it = self.location.top_item()
136         if it:
137             it.try_move(self.inv)
138             return True
139         else:
140             return False
141     def wield(self, item):
142         assert item in self.inv, (
143                 "%s is wielding item %s not in inventory" % (self, item))
144         assert item.wieldable(), (
145                 "%s trying to wield the unwieldable %s" % (self, item))
146         assert not item.wielded, (
147                 "%s trying to wield %s, already wielded by %s"
148                 % (self, item, item.wielded))
149         slot = item.slot
150         if self.holding(slot):
151             self.unwield(self.holding(slot))
152         self.slots[slot] = item
153         item.wielded = (self, slot)
154         intrs = item.intrinsics()
155         if intrs:
156             self.gain_intr(set(intrs), permanent = False)
157             
158         return True
159
160     def unwield(self, item):
161         assert item in self.inv, (
162                 "%s is unwielding item %s not in inventory" % (self, item))
163         assert item.wielded, (
164                 "%s trying to unwield already unwielded %s" % (self, item))
165
166         (creat, slot) = item.wielded
167         assert creat is self, "%s unwielding %s's %s" % (self, creat, item)
168         assert self.holding(slot) is item, (
169                 "%s unwielding %s from wrong hand %s?" % (self, item, slot))
170
171         item.wielded = None
172         self.slots[slot] = None
173         if item.intrinsics():
174             self.recompute_extrinsics()
175         return True
176
177     def recompute_extrinsics(self):
178         self.extrinsics.clear()
179         for equip in self.slots.values():
180             if equip and equip.intrinsics():
181                 self.gain_intr(set(equip.intrinsics()), permanent = False)
182
183
184
185 class Player(Creature):
186     def __init__(self, location=None):
187         Creature.__init__(self, location)
188         self.active = False
189     def render(self, pic=None):
190         return ("@", ui.Color(('Bright' if self.active else '') + 'Magenta').cp)
191     def can_swim(self):
192         return random.random() < .5
193     def format_intrinsics(self):
194         return " ".join([ intr.abbrev for intr in self.intrinsics ]
195                 + [ "/" ] + [ extr.abbrev for extr in self.extrinsics ])