twistile

The beginnings of a server for a tile-based game using Crossfire assets.
git clone https://code.literati.org/twistile.git
Log | Files | Refs

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()