Browse code
Merge remote-tracking branch 'remotes/fiddlerwoaroof/master'
Conflicts:
README.md
Showing 12 changed files
- README.md
- data/items/base.yml
- data/main.yml
- game.py
- items.py
- levels.py
- main.py
- maps.py
- mods.py
- objects.py
- player.py
- weapons.py
... | ... |
@@ -1,6 +1,20 @@ |
1 |
+<<<<<<< HEAD |
|
1 | 2 |
Here it is... |
2 | 3 |
|
3 | 4 |
Requirements: |
4 | 5 |
Python 2.7 |
5 | 6 |
libtcod 1.5.0 (relies on SDL and zlib) |
6 | 7 |
pyYAML 3.10 |
8 |
+======= |
|
9 |
+Homepage: http://fiddlerwoaroof.github.com/yinjar/ |
|
10 |
+ |
|
11 |
+If you want to modify the code, the files contained under data/ might be a good place to start |
|
12 |
+ |
|
13 |
+### Prereqs: |
|
14 |
+ |
|
15 |
+- Python (included on OS X and usually on Linux, install from http://python.org for Windows) |
|
16 |
+- PyYAML |
|
17 |
+- libTCOD (included but if on 64-bit linux download from here: doryen.eptalys.net/libtcod/download/) |
|
18 |
+- SDL (included with libTCOD) |
|
19 |
+ |
|
20 |
+>>>>>>> remotes/fiddlerwoaroof/master |
... | ... |
@@ -21,51 +21,22 @@ class Cursor(object): |
21 | 21 |
libtcod.console_put_char(self.con, self.x,self.y, ' ', libtcod.BKGND_NONE) |
22 | 22 |
|
23 | 23 |
class GameBase: |
24 |
- #actual size of the window |
|
25 |
- SCREEN_WIDTH, SCREEN_HEIGHT = 50,70 |
|
26 |
- |
|
27 |
- MAP_WIDTH, MAP_HEIGHT = SCREEN_WIDTH, SCREEN_HEIGHT - 17 |
|
28 |
- |
|
29 |
- INVENTORY_WIDTH = 50 |
|
30 |
- BAR_WIDTH = 25 |
|
31 |
- |
|
32 |
- PANEL_HEIGHT = SCREEN_HEIGHT - MAP_HEIGHT - 2 |
|
33 |
- PANEL_Y = SCREEN_HEIGHT - PANEL_HEIGHT |
|
34 |
- |
|
35 |
- MSG_X = BAR_WIDTH + 2 |
|
36 |
- MSG_WIDTH, MSG_HEIGHT = SCREEN_WIDTH - BAR_WIDTH - 2, PANEL_HEIGHT - 1 |
|
37 |
- |
|
38 |
- ROOM_MIN_SIZE, ROOM_MAX_SIZE = 7, 19 |
|
39 |
- |
|
40 |
- MAX_ROOMS = 51 |
|
41 |
- |
|
42 |
- MAX_ROOM_MONSTERS, MAX_ROOM_ITEMS = 9, 6 |
|
43 |
- |
|
44 |
- CONFUSE_NUM_TURNS = 17 |
|
45 |
- |
|
46 |
- LIMIT_FPS = 20 #20 frames-per-second maximum |
|
47 |
- |
|
48 |
- color_dark_wall = libtcod.Color(60, 60, 60) |
|
49 |
- color_light_wall = libtcod.Color(127,127,127) |
|
50 |
- color_dark_ground = libtcod.Color(150,150,150) |
|
51 |
- color_light_ground = libtcod.Color(200,200,200) |
|
52 |
- |
|
53 |
- |
|
54 | 24 |
def message(self, msg, color=None): |
55 |
- if color is not None: |
|
56 |
- utilities.message(self.game_msgs, self.MSG_HEIGHT, self.MSG_WIDTH, msg, color) |
|
57 |
- else: |
|
58 |
- utilities.message(self.game_msgs, self.MSG_HEIGHT, self.MSG_WIDTH, msg) |
|
25 |
+ if color is None: |
|
26 |
+ color = libtcod.white |
|
27 |
+ utilities.message(self.game_msgs, self.MSG_HEIGHT, self.MSG_WIDTH, msg) |
|
59 | 28 |
|
60 |
- def __init__(self, app_name='test app', screen_width=SCREEN_WIDTH, screen_height=SCREEN_HEIGHT): |
|
29 |
+ def __init__(self, app_name='test app', screen_width=None, screen_height=None): |
|
61 | 30 |
print '__init__' |
31 |
+ if screen_width is None: |
|
32 |
+ screen_width, screen_height = self.SCREEN_WIDTH, self.SCREEN_HEIGHT |
|
62 | 33 |
libtcod.console_init_root( |
63 | 34 |
screen_width, screen_height, app_name, False |
64 | 35 |
) |
65 | 36 |
|
66 | 37 |
self.game_msgs = [] |
67 | 38 |
global message |
68 |
- message = functools.partial(utilities.message, self.game_msgs, self.MSG_HEIGHT, self.MSG_WIDTH) |
|
39 |
+ message = self.message |
|
69 | 40 |
|
70 | 41 |
self.game_state = 'playing' |
71 | 42 |
self.player_action = 'didnt-take-turn' |
... | ... |
@@ -136,30 +107,24 @@ class GameBase: |
136 | 107 |
libtcod.console_set_default_background(self.panel, libtcod.black) |
137 | 108 |
libtcod.console_clear(self.panel) |
138 | 109 |
|
139 |
- #utilities.render_bar(self.panel, 1,1, self.BAR_WIDTH, 'HP', |
|
140 |
- # self.player.fighter.hp, |
|
141 |
- # self.player.fighter.max_hp, |
|
142 |
- # libtcod.red, |
|
143 |
- # libtcod.darker_red |
|
144 |
- #) |
|
145 |
- |
|
146 |
- #y = 1 |
|
147 |
- #for line, color in self.game_msgs: |
|
148 |
- # libtcod.console_set_default_foreground(self.panel, color) |
|
149 |
- # libtcod.console_print_ex(self.panel, self.MSG_X, y, libtcod.BKGND_NONE, libtcod.LEFT, line) |
|
150 |
- # y += 1 |
|
151 |
- |
|
152 |
- |
|
153 | 110 |
libtcod.console_blit(self.panel, 0,0, self.SCREEN_WIDTH,self.PANEL_HEIGHT, 0,0, self.PANEL_Y) |
154 | 111 |
|
155 |
- def inventory_menu(self, header): |
|
156 |
- data = [(item.display_name, item.ident) for item in self.player.get_items()] |
|
112 |
+ def Inventory_menu(self, header, items): |
|
113 |
+ data = [(item.display_name, item.ident) for item in items] |
|
157 | 114 |
display = [x[0] for x in data] |
158 |
- index = self.menu(self.con, header, display, self.INVENTORY_WIDTH) |
|
115 |
+ index = self.menu(header, display, self.INVENTORY_WIDTH) |
|
116 |
+ return index, data |
|
159 | 117 |
|
118 |
+ def inventory_menu(self, header): |
|
119 |
+ index, data = self.Inventory_menu(header, self.player.get_items()) |
|
160 | 120 |
if index is not None: |
161 | 121 |
return self.player.get_item(data[index][1]) |
162 | 122 |
|
123 |
+ def mod_menu(self, header): |
|
124 |
+ index, data = self.Inventory_menu(header, self.player.get_mods()) |
|
125 |
+ if index is not None: |
|
126 |
+ return self.player.get_mod(data[index][1]) |
|
127 |
+ |
|
163 | 128 |
def get_names_under_mouse(self): |
164 | 129 |
x,y = self.mouse.cx, self.mouse.cy |
165 | 130 |
names = ', '.join( |
... | ... |
@@ -174,16 +139,25 @@ class GameBase: |
174 | 139 |
|
175 | 140 |
|
176 | 141 |
|
177 |
- def menu(self, con, header, options, width): |
|
142 |
+ def menu(self, header, options, width, back_color=libtcod.black, fore_color=libtcod.white): |
|
143 |
+ import debug |
|
144 |
+ print '------\n' |
|
145 |
+ print debug._get_last_module(100) |
|
146 |
+ print 'menu(', self, header, options, width, back_color, fore_color, ')' |
|
147 |
+ print '------\n' |
|
148 |
+ |
|
149 |
+ if self.con is None: self.con = 0 |
|
178 | 150 |
if len(options) > 26: raise ValueError('too many items') |
179 | 151 |
|
152 |
+ con = self.con |
|
180 | 153 |
|
181 |
- print con |
|
182 | 154 |
header_height = libtcod.console_get_height_rect(con, 0,0, width, self.SCREEN_HEIGHT, header) |
183 | 155 |
height = len(options) + header_height |
184 | 156 |
window = libtcod.console_new(width, height) |
157 |
+ print 'window id is:', window |
|
158 |
|
|
185 | 159 |
|
186 |
- libtcod.console_set_default_foreground(window, libtcod.white) |
|
160 |
+ libtcod.console_set_default_foreground(window, fore_color) |
|
187 | 161 |
libtcod.console_print_rect(window, 0,0, width,height, header) |
188 | 162 |
|
189 | 163 |
y = header_height |
... | ... |
@@ -194,13 +168,18 @@ class GameBase: |
194 | 168 |
|
195 | 169 |
x = self.SCREEN_WIDTH/2 - width/2 |
196 | 170 |
y = self.SCREEN_HEIGHT/2 - height/2 |
197 |
- libtcod.console_blit(window, 0,0, width,height, 0, x,y, 1.0, 0.7) |
|
171 |
+ libtcod.console_blit(window, 0,0, width,height, 0, x,y, 1.0, 0.9) |
|
198 | 172 |
|
199 | 173 |
key = libtcod.Key() |
200 | 174 |
mouse = libtcod.Mouse() |
201 | 175 |
libtcod.console_flush() |
202 | 176 |
libtcod.sys_wait_for_event(libtcod.KEY_PRESSED, key, mouse, True) |
203 | 177 |
|
178 |
+ libtcod.console_clear(window) |
|
179 |
+ libtcod.console_blit(window, 0,0, width,height, 0, x,y, 1.0, 0.9) |
|
180 |
+ libtcod.console_delete(window) |
|
181 |
+ libtcod.console_flush() |
|
182 |
+ |
|
204 | 183 |
index = key.c - ord('a') |
205 | 184 |
if index >= 0 and index < len(options): return index |
206 | 185 |
return None |
... | ... |
@@ -1,4 +1,5 @@ |
1 | 1 |
from __future__ import division |
2 |
+import collections |
|
2 | 3 |
import os.path |
3 | 4 |
import glob |
4 | 5 |
import yaml |
... | ... |
@@ -13,10 +14,14 @@ from main import Game |
13 | 14 |
|
14 | 15 |
|
15 | 16 |
class Item(object): |
17 |
+ stack_limit = 5 |
|
18 |
+ potency = None |
|
19 |
+ item_class = None |
|
20 |
+ distance = None |
|
21 |
+ probability = 1 |
|
22 |
+ |
|
16 | 23 |
def __init__(self, stackable=False): |
17 |
- self.stackable = stackable |
|
18 |
- self.stacks_with = [] |
|
19 |
- self.stack_limit = 5 |
|
24 |
+ self.mods = collections.defaultdict(set) |
|
20 | 25 |
|
21 | 26 |
def __new__(*args): |
22 | 27 |
res = object.__new__(*args) |
... | ... |
@@ -30,6 +35,24 @@ class Item(object): |
30 | 35 |
self.user = None |
31 | 36 |
return self.owner |
32 | 37 |
|
38 |
+ def modify(self, mod): |
|
39 |
+ undo = mod.modify(self) |
|
40 |
+ self.mods[mod.name].add(mod) |
|
41 |
+ self.owner.name = self.name |
|
42 |
+ |
|
43 |
+ def unmodify(self, mod): |
|
44 |
+ if hasattr(mod, 'upper'): |
|
45 |
+ mods = self.mods[mod] |
|
46 |
+ else: |
|
47 |
+ mods = self.mods[mod.name] |
|
48 |
+ if mods != set(): |
|
49 |
+ mod = mods.pop() |
|
50 |
+ mod.revert(self) |
|
51 |
+ self.owner.name = self.name |
|
52 |
+ if mods == set(): |
|
53 |
+ self.mods.pop(mod.name) |
|
54 |
+ print 'self.mods', self.mods |
|
55 |
+ |
|
33 | 56 |
class ItemLoader(object): |
34 | 57 |
def __init__(self, dir): |
35 | 58 |
self.dir = dir |
... | ... |
@@ -41,6 +64,8 @@ class ItemLoader(object): |
41 | 64 |
self.load_item(doc) |
42 | 65 |
|
43 | 66 |
def load_item(self, doc): |
67 |
+ if doc is None: return |
|
68 |
+ |
|
44 | 69 |
_color = doc.get('color', None) |
45 | 70 |
if _color is None: |
46 | 71 |
_color = libtcod.green |
... | ... |
@@ -61,6 +86,9 @@ class ItemLoader(object): |
61 | 86 |
name = doc.get('item_description') |
62 | 87 |
char = doc.get('char', '!') |
63 | 88 |
color = _color |
89 |
+ stack_limit = doc.get('stack_limit', Item.stack_limit) |
|
90 |
+ potency = doc.get('potency') |
|
91 |
+ distance = doc.get('distance') |
|
64 | 92 |
|
65 | 93 |
|
66 | 94 |
@Game.register_item_type(5) |
... | ... |
@@ -68,16 +96,18 @@ class HealingPotion(Item): |
68 | 96 |
name = 'Healing potion' |
69 | 97 |
char = '\x03' |
70 | 98 |
color = libtcod.violet |
99 |
+ potency = 10 |
|
100 |
+ item_class = 'healing' |
|
71 | 101 |
def use(self): |
72 | 102 |
fighter = self.user.fighter |
73 | 103 |
|
74 | 104 |
result = True |
75 | 105 |
if fighter.hp == fighter.max_hp: |
76 |
- self.game.message('You\'re full, can\'t heal', libtcod.red) |
|
106 |
+ self.game.message('Full health, can\'t heal', libtcod.red) |
|
77 | 107 |
result = False |
78 | 108 |
else: |
79 | 109 |
self.game.message('Healing...') |
80 |
- fighter.heal(10) |
|
110 |
+ fighter.heal(self.potency) |
|
81 | 111 |
|
82 | 112 |
return result |
83 | 113 |
|
... | ... |
@@ -86,11 +116,14 @@ class SuperHealingPotion(Item): |
86 | 116 |
name = 'Super healing potion' |
87 | 117 |
char = '\x03' |
88 | 118 |
color = libtcod.yellow |
119 |
+ probability = .5 |
|
120 |
+ potency = 10 |
|
121 |
+ item_class = 'healing' |
|
89 | 122 |
def use(self): |
90 | 123 |
fighter = self.user.fighter |
91 |
- if random.random() < .75: |
|
92 |
- fighter.max_hp += 10 |
|
93 |
- fighter.heal(10) |
|
124 |
+ if random.random() < self.probability: |
|
125 |
+ fighter.max_hp += self.potency |
|
126 |
+ fighter.heal(self.potency) |
|
94 | 127 |
return True |
95 | 128 |
|
96 | 129 |
@Game.register_item_type(1) |
... | ... |
@@ -98,6 +131,7 @@ class Confusion(Item): |
98 | 131 |
name = 'Confusion' |
99 | 132 |
char = 'c' |
100 | 133 |
color=libtcod.dark_chartreuse |
134 |
+ item_class = 'monster defense' |
|
101 | 135 |
def use(self): |
102 | 136 |
monster = monsters.get_closest_monster(self.user) |
103 | 137 |
|
... | ... |
@@ -116,10 +150,12 @@ class Strengthen(Item): |
116 | 150 |
name = 'Strengthen' |
117 | 151 |
char = 's' |
118 | 152 |
color = libtcod.chartreuse |
153 |
+ item_class = 'attack' |
|
154 |
+ potency = 20 |
|
119 | 155 |
def use(self): |
120 | 156 |
if self.user.fighter: |
121 | 157 |
self.game.message('%s feels a surge of strength' % self.user.name) |
122 |
- self.user.fighter.stat_adjust(20, self.adj) |
|
158 |
+ self.user.fighter.stat_adjust(self.potency, self.adj) |
|
123 | 159 |
return True |
124 | 160 |
|
125 | 161 |
def adj(self, owner): |
... | ... |
@@ -134,10 +170,12 @@ class Protect(Item): |
134 | 170 |
name = 'Protect' |
135 | 171 |
char = 'p' |
136 | 172 |
color = libtcod.chartreuse |
173 |
+ item_class = 'defense' |
|
174 |
+ potency = 15 |
|
137 | 175 |
def use(self): |
138 | 176 |
if self.user.fighter: |
139 | 177 |
self.game.message('%s is surrounded by a protecting aura' % self.user.name) |
140 |
- self.user.fighter.stat_adjust(15, self.adj) |
|
178 |
+ self.user.fighter.stat_adjust(self.potency, self.adj) |
|
141 | 179 |
return True |
142 | 180 |
|
143 | 181 |
def adj(self, owner): |
... | ... |
@@ -152,12 +190,14 @@ class LightningBolt(Item): |
152 | 190 |
name = 'Lightning Bolt' |
153 | 191 |
char = 'z' |
154 | 192 |
color = libtcod.darkest_red |
193 |
+ item_class = 'attack' |
|
194 |
+ potency = 13 |
|
155 | 195 |
def use(self): |
156 | 196 |
monster = monsters.get_closest_monster(self.user) |
157 | 197 |
result = False |
158 | 198 |
if monster and self.user.can_see(monster.x, monster.y): |
159 | 199 |
self.game.message('Monster %s has been struck by lightning' % monster.name) |
160 |
- monster.fighter.take_damage(13) |
|
200 |
+ monster.fighter.take_damage(self.potency) |
|
161 | 201 |
result = True |
162 | 202 |
else: |
163 | 203 |
self.game.message('No target') |
... | ... |
@@ -168,22 +208,23 @@ class Jump(Item): |
168 | 208 |
name = 'Jump' |
169 | 209 |
char = 'j' |
170 | 210 |
color= libtcod.dark_green |
171 |
- jump_distance = 3 |
|
211 |
+ distance = 3 |
|
212 |
+ item_class = 'movement' |
|
172 | 213 |
def use(self): |
173 | 214 |
self.game.select(self.jump) |
174 | 215 |
return True |
175 | 216 |
def jump(self, x,y): |
176 | 217 |
dist = self.user.distance(x,y) |
177 | 218 |
|
178 |
- if dist <= self.jump_distance: |
|
219 |
+ if dist <= self.distance: |
|
179 | 220 |
self.user.x, self.user.y = x,y |
180 | 221 |
self.game.message('you are transported to a new place') |
181 |
- elif random.random() < self.jump_distance/dist: |
|
222 |
+ elif random.random() < self.distance/dist: |
|
182 | 223 |
self.user.x, self.user.y = x,y |
183 | 224 |
self.game.message('you strain all your power to move %d squares' % int(dist)) |
184 | 225 |
else: |
185 | 226 |
self.game.message('you didn\'t make it') |
186 |
- self.user.fighter.take_damage( int(round(2 * dist/self.jump_distance)) ) |
|
227 |
+ self.user.fighter.take_damage( int(round(2 * dist/self.distance)) ) |
|
187 | 228 |
|
188 | 229 |
@Game.register_item_type(3) |
189 | 230 |
class Acquire(Item): |
... | ... |
@@ -191,6 +232,7 @@ class Acquire(Item): |
191 | 232 |
char = 'a' |
192 | 233 |
color= libtcod.dark_green |
193 | 234 |
effect_distance = 5 |
235 |
+ item_class = 'pickup' |
|
194 | 236 |
def use(self): |
195 | 237 |
self.game.message('what do you want?') |
196 | 238 |
self.game.select(self.get) |
... | ... |
@@ -205,6 +247,8 @@ class Smite(Item): |
205 | 247 |
name = 'Smite' |
206 | 248 |
char = '\x0f' |
207 | 249 |
color = libtcod.red |
250 |
+ item_class = 'attack' |
|
251 |
+ potency = 10 |
|
208 | 252 |
def use(self): |
209 | 253 |
self.game.select(self.smite) |
210 | 254 |
return True |
... | ... |
@@ -212,7 +256,7 @@ class Smite(Item): |
212 | 256 |
def smite(self, x,y): |
213 | 257 |
monster = monsters.monster_at(x,y) |
214 | 258 |
if monster: |
215 |
- monster.fighter.take_damage(10) |
|
259 |
+ monster.fighter.take_damage(self.potency) |
|
216 | 260 |
if monster.fighter: |
217 | 261 |
self.game.message('%s is smitten, he only retains %s hp' % (monster.name, monster.fighter.hp)) |
218 | 262 |
else: |
... | ... |
@@ -224,7 +268,9 @@ class Fireball(Item): |
224 | 268 |
name = 'Fireball' |
225 | 269 |
char = '*' |
226 | 270 |
color = libtcod.darker_red |
227 |
- effect_radius = 3 |
|
271 |
+ effect_radius = 5 |
|
272 |
+ potency = (20,6) |
|
273 |
+ item_class = 'splash attack' |
|
228 | 274 |
|
229 | 275 |
def use(self): |
230 | 276 |
self.game.select(self.smite) |
... | ... |
@@ -232,16 +278,18 @@ class Fireball(Item): |
232 | 278 |
|
233 | 279 |
def smite(self, x,y): |
234 | 280 |
if random.random() < .1: |
281 |
+ self.game.message('the fireball is amazingly effective', libtcod.green) |
|
235 | 282 |
self.effect_radius *= 2 |
236 | 283 |
|
284 |
+ direct_damage, splash_damage = self.potency |
|
237 | 285 |
strikes = [] |
238 | 286 |
for obj in self.owner.level.objects: |
239 | 287 |
if obj.fighter and obj is not self.user: |
240 | 288 |
if (obj.x, obj.y) == (x,y): |
241 | 289 |
self.game.message('%s takes a direct hit from the fireball' % obj.name) |
242 |
- obj.fighter.take_damage(20) |
|
290 |
+ obj.fighter.take_damage(direct_damage) |
|
243 | 291 |
elif obj.distance(x,y) < self.effect_radius: |
244 |
- obj.fighter.take_damage(6) |
|
292 |
+ obj.fighter.take_damage(splash_damage) |
|
245 | 293 |
if obj.fighter: |
246 | 294 |
strikes.append('%s %s' % (obj.name, obj.fighter.hp)) |
247 | 295 |
else: |
... | ... |
@@ -1,43 +1,96 @@ |
1 | 1 |
import os.path |
2 |
+import yaml |
|
2 | 3 |
import textwrap |
3 | 4 |
import math |
4 | 5 |
import libtcodpy as libtcod |
5 | 6 |
import glob |
6 | 7 |
libtcod.console_set_keyboard_repeat(500, 50) |
7 |
-for file in glob.glob('./data/namegen/*.cfg'): |
|
8 |
- libtcod.namegen_parse(file) |
|
8 |
+for fil in glob.glob('./data/namegen/*.cfg'): |
|
9 |
+ libtcod.namegen_parse(fil) |
|
10 |
+ |
|
11 |
+help = ''' |
|
12 |
+ 'i': Inventory |
|
13 |
+ 'd': Drop |
|
14 |
+ 'g': Get item (Pick up) |
|
15 |
+ '?': Help |
|
16 |
+ Alt+Escape: Exit |
|
17 |
+ |
|
18 |
+ Arrow Keys for movement / selecting |
|
19 |
+ Name of item under the mouse shown |
|
20 |
+ above the health bar |
|
21 |
+''' |
|
9 | 22 |
|
10 | 23 |
from game import GameBase |
11 | 24 |
import levels |
12 | 25 |
import objects |
13 | 26 |
import utilities |
14 | 27 |
if __name__ == 'main': |
28 |
+ class Null: pass |
|
29 |
+ class SettingsObject(object): |
|
30 |
+ def __init__(self, setting_name, default=Null): |
|
31 |
+ self.setting_name = setting_name |
|
32 |
+ self.default = default |
|
33 |
+ |
|
34 |
+ def __get__(self, instance, owner): |
|
35 |
+ result = instance.settings.get(self.setting_name, self.default) |
|
36 |
+ if result is Null: |
|
37 |
+ raise KeyError('%s is not specified in the configuration' % self.setting_name) |
|
38 |
+ return result |
|
39 |
+ |
|
15 | 40 |
class Game(GameBase): |
16 | 41 |
#actual size of the window |
17 |
- SCREEN_WIDTH, SCREEN_HEIGHT = 155, 90 |
|
42 |
+ def load_settings(self): |
|
43 |
+ self.settings = yaml.safe_load( |
|
44 |
+ file(os.path.join('./data/main.yml')) |
|
45 |
+ ) |
|
46 |
+ if self.settings == None: |
|
47 |
+ self.settings = {} |
|
48 |
+ print self.settings |
|
18 | 49 |
|
19 |
- MAP_WIDTH, MAP_HEIGHT = SCREEN_WIDTH, SCREEN_HEIGHT - 17 |
|
50 |
+ SCREEN_WIDTH = SettingsObject('screen_width', 80) |
|
51 |
+ SCREEN_HEIGHT = SettingsObject('screen_height', 50) |
|
52 |
+ |
|
53 |
+ PANEL_HEIGHT = 15 |
|
20 | 54 |
|
21 | 55 |
INVENTORY_WIDTH = 50 |
22 |
- BAR_WIDTH = 25 |
|
23 | 56 |
|
24 |
- PANEL_HEIGHT = SCREEN_HEIGHT - MAP_HEIGHT - 2 |
|
25 |
- PANEL_Y = SCREEN_HEIGHT - PANEL_HEIGHT |
|
57 |
+ @property |
|
58 |
+ def MAP_WIDTH(self): |
|
59 |
+ return self.SCREEN_WIDTH |
|
60 |
+ @property |
|
61 |
+ def MAP_HEIGHT(self): |
|
62 |
+ return self.SCREEN_HEIGHT - (self.PANEL_HEIGHT + 2) |
|
63 |
+ |
|
26 | 64 |
|
65 |
+ BAR_WIDTH = 25 |
|
27 | 66 |
MSG_X = BAR_WIDTH + 2 |
28 |
- MSG_WIDTH, MSG_HEIGHT = SCREEN_WIDTH - BAR_WIDTH - 2, PANEL_HEIGHT - 1 |
|
67 |
+ MSG_HEIGHT = PANEL_HEIGHT |
|
68 |
+ |
|
69 |
+ @property |
|
70 |
+ def PANEL_Y(self): |
|
71 |
+ return self.SCREEN_HEIGHT - self.PANEL_HEIGHT |
|
72 |
+ |
|
73 |
+ @property |
|
74 |
+ def MSG_WIDTH(self): |
|
75 |
+ return self.SCREEN_WIDTH - self.MSG_X |
|
29 | 76 |
|
30 |
- ROOM_MIN_SIZE, ROOM_MAX_SIZE = 7, 19 |
|
77 |
+ @property |
|
78 |
+ def MSG_HEIGHT(self): |
|
79 |
+ return self.PANEL_HEIGHT - 1 |
|
31 | 80 |
|
32 |
- MAX_ROOMS = 51 |
|
81 |
+ ROOM_MIN_SIZE = SettingsObject('room_min_wall_length', 4) |
|
82 |
+ ROOM_MAX_SIZE = SettingsObject('room_max_wall_length', 7) |
|
33 | 83 |
|
34 |
- MAX_ROOM_MONSTERS, MAX_ROOM_ITEMS = 9, 6 |
|
84 |
+ MAX_ROOMS = SettingsObject('max_number_rooms', 10) |
|
85 |
+ MAX_ROOM_MONSTERS = SettingsObject('max_number_room_monsters', 6) |
|
86 |
+ MAX_ROOM_ITEMS = SettingsObject('max_number_room_items', 3) |
|
35 | 87 |
|
36 | 88 |
CONFUSE_NUM_TURNS = 17 |
37 | 89 |
|
38 | 90 |
LIMIT_FPS = 20 #20 frames-per-second maximum |
39 | 91 |
|
40 | 92 |
def __init__(self): |
93 |
+ self.load_settings() |
|
41 | 94 |
GameBase.__init__(self, 'caer flinding', self.SCREEN_WIDTH, self.SCREEN_HEIGHT) |
42 | 95 |
|
43 | 96 |
self.select_cb = None |
... | ... |
@@ -197,17 +250,34 @@ if __name__ == 'main': |
197 | 250 |
|
198 | 251 |
@mvkeyhandler.handle('i') |
199 | 252 |
def mvkeyhandler(self): |
200 |
- item = self.inventory_menu('choose item\n') |
|
253 |
+ item = self.inventory_menu('Choose item to use\n') |
|
201 | 254 |
if item is not None: |
202 | 255 |
item.bind_game(self) |
203 | 256 |
self.player.use(item) |
204 | 257 |
|
205 | 258 |
@mvkeyhandler.handle('d') |
206 | 259 |
def mvkeyhandler(self): |
207 |
- chosen_item = self.inventory_menu('Choose the item to drop:') |
|
260 |
+ chosen_item = self.inventory_menu('Choose item to drop\n') |
|
208 | 261 |
if chosen_item is not None: |
209 | 262 |
self.player.drop(chosen_item.owner) |
210 | 263 |
|
264 |
+ @mvkeyhandler.handle('n') |
|
265 |
+ def mvkeyhandler(self): |
|
266 |
+ chosen_item = self.inventory_menu('Choose item to unmod\n') |
|
267 |
+ if chosen_item is not None: |
|
268 |
+ data = chosen_item.mods.keys() |
|
269 |
+ index = self.menu('Choose \nmod to \nundo\n', data, self.INVENTORY_WIDTH) |
|
270 |
+ if index is not None: |
|
271 |
+ self.player.unmodify(chosen_item.name, data[index]) |
|
272 |
+ |
|
273 |
+ @mvkeyhandler.handle('m') |
|
274 |
+ def mvkeyhandler(self): |
|
275 |
+ chosen_item = self.inventory_menu('Choose item to mod\n') |
|
276 |
+ if chosen_item is not None: |
|
277 |
+ chosen_mod = self.mod_menu('Choose mod to apply\n') |
|
278 |
+ if chosen_mod is not None: |
|
279 |
+ self.player.modify(chosen_item.name, chosen_mod.name) |
|
280 |
+ |
|
211 | 281 |
@mvkeyhandler.handle('g') |
212 | 282 |
def mvkeyhandler(self): |
213 | 283 |
for obj in self.level.iter_objects(): |
... | ... |
@@ -222,6 +292,10 @@ if __name__ == 'main': |
222 | 292 |
def mvkeyhandler(self): |
223 | 293 |
self.change_level(down=True) |
224 | 294 |
|
295 |
+ @mvkeyhandler.handle('?') |
|
296 |
+ def mvkeyhandler(self): |
|
297 |
+ self.menu(help, [], 50) |
|
298 |
+ |
|
225 | 299 |
selectkeyhandler = utilities.MovementKeyListener() |
226 | 300 |
@selectkeyhandler.up |
227 | 301 |
def selectkeyhandler(self): |
... | ... |
@@ -284,6 +358,19 @@ if __name__ == 'main': |
284 | 358 |
|
285 | 359 |
libtcod.console_blit(self.panel, 0,0, self.SCREEN_WIDTH,self.PANEL_HEIGHT, 0,0, self.PANEL_Y) |
286 | 360 |
|
361 |
+ def main_menu(self): |
|
362 |
+ message = ( |
|
363 |
+ 'Welcome to YinJAR: is not Just Another Roguelike (WIP)', |
|
364 |
+ '', |
|
365 |
+ 'Choose an option:', |
|
366 |
+ '', |
|
367 |
+ ) |
|
368 |
+ |
|
369 |
+ options = ['Play', 'Exit'] |
|
370 |
+ return options[ |
|
371 |
+ self.menu('\n'.join(message), options, len(message[0])) |
|
372 |
+ ] |
|
373 |
+ |
|
287 | 374 |
game_instance = Game() |
288 | 375 |
from monsters import MonsterLoader |
289 | 376 |
|
... | ... |
@@ -306,6 +393,9 @@ if __name__ == '__main__': |
306 | 393 |
il = ItemLoader(os.path.join('.','data','items')) |
307 | 394 |
il.load_items() |
308 | 395 |
|
309 |
- game_instance.setup_map() |
|
310 |
- game_instance.main() |
|
396 |
+ game_instance.load_settings() |
|
397 |
+ action = game_instance.main_menu() |
|
398 |
+ if action.lower() == 'play': |
|
399 |
+ game_instance.setup_map() |
|
400 |
+ game_instance.main() |
|
311 | 401 |
|
... | ... |
@@ -151,6 +151,7 @@ class MazeGen(AutomataEngine): |
151 | 151 |
|
152 | 152 |
cx, cy = point |
153 | 153 |
lx, ty = cx-left_offset, cy-up_offset |
154 |
+ |
|
154 | 155 |
if lx < 0: |
155 | 156 |
max_width += lx |
156 | 157 |
lx = 0 |
... | ... |
@@ -160,10 +161,11 @@ class MazeGen(AutomataEngine): |
160 | 161 |
w, h = random.randrange(1,max_width+1), random.randrange(1, max_height+1) |
161 | 162 |
|
162 | 163 |
if lx + w >= self.width: |
163 |
- lx -= (lx+w) - self.width |
|
164 |
+ w -= (lx+w) - self.width |
|
164 | 165 |
if ty + h >= self.height: |
165 |
- ty -= (ty+h) - self.height |
|
166 |
+ h -= (ty+h) - self.height |
|
166 | 167 |
|
168 |
+ print '(',lx,ty, ')', w, h |
|
167 | 169 |
room = Rect(lx,ty, w,h) |
168 | 170 |
success = True |
169 | 171 |
for o_room in self.rooms: |
170 | 172 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,27 @@ |
1 |
+import math |
|
2 |
+class Mod(object): |
|
3 |
+ '''An undoable change to some object or item''' |
|
4 |
+ |
|
5 |
+ def __init__(self): |
|
6 |
+ pass |
|
7 |
+ |
|
8 |
+ def modify(self, target): |
|
9 |
+ pass |
|
10 |
+ |
|
11 |
+ def revert(self, target): |
|
12 |
+ pass |
|
13 |
+ |
|
14 |
+class Boost(Mod): |
|
15 |
+ name = 'boost' |
|
16 |
+ |
|
17 |
+ def modify(self, target): |
|
18 |
+ target.potency *= 1.5 |
|
19 |
+ result = math.ceil(target.potency) |
|
20 |
+ target.potency = int(result) |
|
21 |
+ target.name = 'boosted %s' % target.name |
|
22 |
+ def revert(self, target): |
|
23 |
+ target.potency /= 1.5 |
|
24 |
+ result = math.ceil(target.potency) |
|
25 |
+ target.potency = int(result) |
|
26 |
+ target.name = target.name.split(' ', 1)[1] |
|
27 |
+ |
... | ... |
@@ -4,6 +4,7 @@ import libtcodpy as libtcod |
4 | 4 |
import maps |
5 | 5 |
|
6 | 6 |
class Object(object): |
7 |
+ # FIXME: map argument unused, remove |
|
7 | 8 |
def __init__(self, map, con, x,y, char, name, color, blocks=False, level=None, fighter=None, ai=None, item=None): |
8 | 9 |
self.name = name |
9 | 10 |
self.x, self.y = x,y |
... | ... |
@@ -1,5 +1,7 @@ |
1 |
+import contextlib |
|
1 | 2 |
import libtcodpy as libtcod |
2 | 3 |
from objects import Object, Fighter |
4 |
+import mods |
|
3 | 5 |
|
4 | 6 |
def get_pos_pair(x,y): |
5 | 7 |
if y is None: |
... | ... |
@@ -83,6 +85,7 @@ class Inventory(object): |
83 | 85 |
self.add_item(v) |
84 | 86 |
|
85 | 87 |
def add_item(self, item): |
88 |
+ print 'add_item', item.name |
|
86 | 89 |
slot = self.objects.setdefault(item.name, [Slot()]) |
87 | 90 |
while not slot[-1].add_item(item): |
88 | 91 |
print 'add slot' |
... | ... |
@@ -111,6 +114,13 @@ class Player(Object): |
111 | 114 |
|
112 | 115 |
map.player = self |
113 | 116 |
self.inventory = Inventory() |
117 |
+ self.mods = Inventory() |
|
118 |
+ |
|
119 |
+ class Item: |
|
120 |
+ stack_limit = 5 |
|
121 |
+ obj = Object(None, con, None,None, 'b', 'boost', color, item=Item()) |
|
122 |
+ obj.mod = mods.Boost() |
|
123 |
+ self.mods.add_item(obj) |
|
114 | 124 |
|
115 | 125 |
def draw(self, player=None): |
116 | 126 |
if player is None: |
... | ... |
@@ -141,10 +151,34 @@ class Player(Object): |
141 | 151 |
if success: |
142 | 152 |
del self.inventory[item.name] |
143 | 153 |
|
154 |
+ @contextlib.contextmanager |
|
155 |
+ def recategorize_item(self, item_name): |
|
156 |
+ item = self.inventory[item_name] |
|
157 |
+ yield item |
|
158 |
+ del self.inventory[item_name] |
|
159 |
+ self.inventory.add_item(item) |
|
160 |
+ |
|
161 |
+ def modify(self, item_name, mod_name): |
|
162 |
+ mod = self.mods[mod_name] |
|
163 |
+ with self.recategorize_item(item_name) as item: |
|
164 |
+ item.item.modify(mod.mod) |
|
165 |
+ del self.mods[mod_name] |
|
166 |
+ print list(self.mods) |
|
167 |
+ |
|
168 |
+ def unmodify(self, item_name, mod_name): |
|
169 |
+ with self.recategorize_item(item_name) as item: |
|
170 |
+ item.item.unmodify(mod_name) |
|
171 |
+ print list(self.mods) |
|
172 |
+ |
|
173 |
+ def get_mod(self, index): |
|
174 |
+ return self.mods[index].mod |
|
175 |
+ def get_mod_names(self): |
|
176 |
+ return [item.display_name for item in self.mods] |
|
177 |
+ def get_mods(self): |
|
178 |
+ return [item for item in self.mods] |
|
144 | 179 |
|
145 | 180 |
def get_item(self, index): |
146 | 181 |
return self.inventory[index].item |
147 |
- |
|
148 | 182 |
def get_item_names(self): |
149 | 183 |
return [item.display_name for item in self.inventory] |
150 | 184 |
def get_items(self): |
151 | 185 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,26 @@ |
1 |
+ |
|
2 |
+class Weapon(object): |
|
3 |
+ def __init__(self, power_boost=0, chance_to_hit=100): |
|
4 |
+ self.power_boost = power_boost |
|
5 |
+ self.chance_to_hit = chance_to_hit |
|
6 |
+ |
|
7 |
+ self.ammo = [] |
|
8 |
+ self.mods = [] |
|
9 |
+ self.user = None |
|
10 |
+ |
|
11 |
+ def modify(self, mod): |
|
12 |
+ if mod.modify(self): |
|
13 |
+ self.mods.append(mod) |
|
14 |
+ def remove_mod(self, mod): |
|
15 |
+ if mod.undo(self): |
|
16 |
+ self.mods.remove(mod) |
|
17 |
+ |
|
18 |
+ def load(self, ammo): |
|
19 |
+ self.ammo.append(ammo) |
|
20 |
+ |
|
21 |
+ def equip(self, user): |
|
22 |
+ self.user = user |
|
23 |
+ |
|
24 |
+ def attack(self, target): |
|
25 |
+ pass |
|
26 |
+ |