compile_arc.py (10827B)
1 #!/usr/bin/env python 2 import json 3 import os 4 import re 5 import sys 6 7 from itertools import chain 8 from operator import attrgetter 9 10 import archetypes 11 12 13 class Image(object): 14 pattern = re.compile('^(?P<name>[^\.]+\.x\d\d)\.(?P<width>\d+)x' 15 '(?P<height>\d+)\.png$') 16 def __init__(self, path, name, width, height): 17 self.path = path 18 self.name = '' 19 self.width = width 20 self.height = height 21 if width != 32 or height != 32: 22 print >> sys.stderr, path, width, height 23 24 @classmethod 25 def from_path(klass, path): 26 filename = os.path.basename(path) 27 m = klass.pattern.search(filename) 28 if m is None: 29 return None 30 31 return klass(path, m.group('name'), int(m.group('width')), 32 int(m.group('height'))) 33 34 35 def cmds(fp): 36 for line in fp: 37 line = line.strip() 38 if not line or line.startswith('#'): 39 continue 40 l = line.split(' ', 1) 41 yield l if len(l) == 2 else (l[0], '') 42 43 44 class Obj(object): 45 flags = ( 46 'activate_on_push', 47 'activate_on_release', 48 'alive', 49 'applied', 50 'auto_apply', 51 'berserk', 52 'blocksview', 53 'can_cast_spell', 54 'can_roll', 55 'can_see_in_dark', 56 'can_use_armour', 57 'can_use_bow', 58 'can_use_horn', 59 'can_use_ring', 60 'can_use_rod', 61 'can_use_scroll', 62 'can_use_shield', 63 'can_use_skill', 64 'can_use_wand', 65 'can_use_weapon', 66 'changing', 67 'cursed', 68 'damned', 69 'friendly', 70 'generator', 71 'hitback', 72 'identified', 73 'invisible', 74 'is_animated', 75 'is_blind', 76 'is_cauldron', 77 'is_door', 78 'is_floor', 79 'is_hilly', 80 'is_lightable', 81 'is_quad', 82 'is_thrown', 83 'is_transparent_floor', 84 'is_turnable', 85 'is_used_up', 86 'is_water', 87 'is_wooded', 88 'known_cursed', 89 'known_magical', 90 'lifesave', 91 'make_invisible', 92 'monster', 93 'need_an', 94 'neutral', 95 'no_attack', 96 'no_drop', 97 'no_magic', 98 'no_pick', 99 'no_steal', 100 'no_strength', 101 'one_hit', 102 'only_attack', 103 'precious', 104 'random_movement', 105 'reflect_missile', 106 'reflect_spell', 107 'reflecting', 108 'scared', 109 'see_anywhere', 110 'see_invisible', 111 'sleep', 112 'splitting', 113 'stand_still', 114 'startequip', 115 'stealth', 116 'tear_down', 117 'treasure_env', 118 'unaggressive', 119 'undead', 120 'xrays') 121 float_props = ('anim_speed', 'con', 'pow', 'speed', 'speed_left') 122 int_props = ( 123 'ac', 124 'attack_movement', 125 'attacktype', 126 'armour', 127 'body_arm', 128 'body_combat', 129 'body_finger', 130 'body_foot', 131 'body_hand', 132 'body_head', 133 'body_neck', 134 'body_range', 135 'body_shield', 136 'body_shoulder', 137 'body_skill', 138 'body_torso', 139 'body_waist', 140 'body_wrist', 141 'carrying', 142 'casting_time', 143 'cha', 144 'client_type', 145 'container', 146 'dam', 147 'dam_modifier', 148 'dex', 149 'direction', 150 'duration', 151 'duration_modifier', 152 'editable', 153 'exp', 154 'food', 155 'gen_sp_armour', 156 'glow_radius', 157 'grace', 158 'hp', 159 'int', 160 'item_power', 161 'last_eat', 162 'last_grace', 163 'last_heal', 164 'last_sp', 165 'level', 166 'luck', 167 'magic', 168 'maxgrace', 169 'maxhp', 170 'maxsp', 171 'movement_type', 172 'nrof', 173 'path_attuned', 174 'path_denied', 175 'path_repelled', 176 'pick_up', 177 'range', 178 'range_modifier', 179 'resist_acid', 180 'resist_blind', 181 'resist_cancellation', 182 'resist_chaos', 183 'resist_cold', 184 'resist_confusion', 185 'resist_counterspell', 186 'resist_death', 187 'resist_deplete', 188 'resist_disease', 189 'resist_drain', 190 'resist_electricity', 191 'resist_fear', 192 'resist_fire', 193 'resist_ghosthit', 194 'resist_godpower', 195 'resist_holyword', 196 'resist_magic', 197 'resist_paralyze', 198 'resist_physical', 199 'resist_poison', 200 'resist_slow', 201 'resist_turn_undead', 202 'resist_weaponmagic', 203 'run_away', 204 'smoothlevel', 205 'sp', 206 'str', 207 'subtype', 208 'value', 209 'wc', 210 'weapontype', 211 'weight', 212 'will_apply', 213 'wis', 214 'x', 215 'y') 216 json_props = ('attach',) 217 str_props = ( 218 'animation', 219 'editor_folder', 220 'face', 221 'move_block', 222 'move_type', 223 'name', 224 'name_pl', 225 'on_use_yield', 226 'other_arch', 227 'race', 228 'randomitems', 229 'materialname', 230 'move_allow', 231 'move_off', 232 'move_on', 233 'move_slow', 234 'move_slow_penalty', 235 'slaying', 236 'sound', 237 'sound_destroy', 238 'skill', 239 'title') 240 list_props = ('smoothface',) 241 242 def __init__(self, shortname, fp): 243 self.shortname = shortname 244 self.tiles = [] 245 for cmd, arg in cmds(fp): 246 if cmd == 'anim': 247 assert not hasattr(self, 'anim') 248 self.parse_anim(fp) 249 elif cmd == 'end': 250 break 251 elif cmd == 'inherit': 252 self.parent = arg 253 elif cmd == 'msg': 254 self.parse_msg(fp) 255 elif cmd == 'type': 256 self.type = archetypes.types[int(arg)] 257 elif cmd in self.flags: 258 assert not hasattr(self, cmd) 259 if arg == '0': 260 setattr(self, cmd, False) 261 elif arg == '1': 262 setattr(self, cmd, True) 263 else: 264 raise ValueError, 'Value for {0!r} not 0 or 1: {1!r}'.format(cmd, arg) 265 elif cmd in self.float_props: 266 assert not hasattr(self, cmd) 267 setattr(self, cmd, float(arg)) 268 elif cmd in self.json_props: 269 assert not hasattr(self, cmd) 270 setattr(self, cmd, json.loads(arg)) 271 elif cmd in self.str_props: 272 assert not hasattr(self, cmd) 273 setattr(self, cmd, arg) 274 elif cmd in self.int_props: 275 assert not hasattr(self, cmd) 276 try: 277 v = int(arg) 278 except ValueError: 279 raise ValueError, 'Got something other than an int for {0!r}: {1!r}'.format(cmd, arg) 280 else: 281 setattr(self, cmd, v) 282 elif cmd in self.list_props: 283 assert not hasattr(self, cmd) 284 setattr(self, cmd, arg.split()) 285 else: 286 raise ValueError, 'unknown keyword {0!r} {1!r}'.format(cmd, arg) 287 288 @property 289 def props(self): 290 return {p: getattr(self, p) for p in chain( 291 self.flags, self.float_props, self.int_props, self.str_props, 292 self.list_props) if hasattr(self, p)} 293 294 def add_tile(self, obj): 295 self.tiles.append(obj) 296 297 def parse_msg(self, fp): 298 assert not hasattr(self, 'msg') 299 lines = [] 300 for line in fp: 301 line = line.strip() 302 if line == 'endmsg': 303 self.msg = ' '.join(lines) 304 break 305 306 lines.append(line) 307 else: 308 raise ValueError, 'No endmsg!' 309 310 def parse_anim(self, fp): 311 frames = []; 312 for cmd, arg in cmds(fp): 313 if cmd == 'facings': 314 self.facings = int(arg) 315 elif cmd == 'mina': 316 break 317 else: 318 assert arg == '' 319 _, attrs = cmd.split('.') 320 assert attrs[0] == 'x' 321 frames.append(arg) 322 323 self.anim = frames 324 325 @classmethod 326 def parse_arc(klass, path): 327 objects = [] 328 fp = open(path) 329 more = None 330 prev_more = None 331 o = None 332 for cmd, arg in cmds(fp): 333 if cmd == 'object': 334 o = klass(arg, fp) 335 objects.append(o) 336 if more is not None: 337 more.add_tile(o) 338 prev_more = more 339 more = None 340 elif cmd == 'more': 341 assert more is None 342 if prev_more is not None: 343 more = prev_more 344 elif o is not None: 345 more = o 346 else: 347 raise ValueError, 'Bad more' 348 else: 349 raise ValueError, 'Unknown keyword {0} {1}'.format(cmd, arg) 350 351 return objects 352 353 @property 354 def source(self): 355 if hasattr(self, 'parent'): 356 assert not hasattr(self, 'type') 357 base = self.parent 358 else: 359 base = '_a.' + self.type.__name__ 360 361 s = ['class {name}({base}):'.format(name=self.shortname, 362 base=base)] 363 for k in sorted(self.props): 364 s.append(' {0} = {1!r}'.format(k, self.props[k])) 365 366 s.append('\n') 367 368 return '\n'.join(s) 369 370 371 def write_objs(objs, waiting_objs): 372 for obj in sorted(objs, key=attrgetter('shortname')): 373 print obj.source 374 name = obj.shortname 375 if name in waiting_objs: 376 write_objs(waiting_objs[name]) 377 del waiting_objs[name] 378 379 380 def main(): 381 images = {} 382 print 'import archetypes as _a' 383 print 384 objs = [] 385 waiting_objs = {} 386 for dirpath, dirnames, filenames in os.walk(sys.argv[1]): 387 for filename in filenames: 388 path = os.path.join(dirpath, filename) 389 _, ext = os.path.splitext(filename) 390 if ext == '.png': 391 im = Image.from_path(path) 392 if im is not None: 393 images[im.name] = im 394 elif ext == '.arc': 395 #print >> sys.stderr, 'parsing', path 396 for o in Obj.parse_arc(path): 397 if hasattr(o, 'parent'): 398 assert not hasattr(o, 'type') 399 waiting_objs.setdefault(o.parent, []).append(o) 400 else: 401 objs.append(o) 402 403 if 'CVS' in dirnames: 404 dirnames.remove('CVS') 405 406 if 'dev' in dirnames: 407 dirnames.remove('dev') 408 409 write_objs(objs, waiting_objs) 410 411 412 if __name__ == '__main__': 413 main()