From 4acc3ea1533df85525fea9d9e8481e1864294dd8 Mon Sep 17 00:00:00 2001 From: Christopher Toth Date: Mon, 27 Jul 2026 00:37:51 -0600 Subject: [PATCH] Enforce LF line endings: .gitattributes + ruff format settings The repo was 123 files LF with 4 Python files mixed/CRLF and nothing enforcing endings. Add .gitattributes (* text=auto eol=lf, .are fixtures protected as binary), ruff format line-ending=lf, and renormalize the four offenders to LF. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01F8kWxXQUY73XMXyeUYhkLd --- .gitattributes | 5 + area_reader/__init__.py | 2530 +++++++++++++++++++------------------- area_reader/constants.py | 902 +++++++------- conftest.py | 20 +- pyproject.toml | 6 + test_area_reader.py | 4 +- 6 files changed, 1739 insertions(+), 1728 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6dfb973 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# All text files are stored and checked out with LF endings. +* text=auto eol=lf + +# MUD area fixtures are byte-exact test data; never touch their endings. +*.are -text diff --git a/area_reader/__init__.py b/area_reader/__init__.py index 4af9922..b171482 100644 --- a/area_reader/__init__.py +++ b/area_reader/__init__.py @@ -1,22 +1,22 @@ -#! /usr/bin/env python3.11 - -import logging -logger = logging.getLogger('area_reader') -logging.basicConfig(level=logging.INFO) - -from collections import OrderedDict -import enum -import html -import io -import json -import random -import re -import os -import sys -import xml.etree.ElementTree as ET -from typing import List, Dict, Optional -from attr import attr, attributes, Factory, fields -from cattr import converters +#! /usr/bin/env python3.11 + +import logging +logger = logging.getLogger('area_reader') +logging.basicConfig(level=logging.INFO) + +from collections import OrderedDict +import enum +import html +import io +import json +import random +import re +import os +import sys +import xml.etree.ElementTree as ET +from typing import List, Dict, Optional +from attr import attr, attributes, Factory, fields +from cattr import converters from operator import setitem from area_reader.constants import * @@ -38,126 +38,126 @@ def field(type=None, read=True, on_read=None, original_type=None, only_if=None, native=None, *args, **kwargs): metadata = dict(read=read) - if on_read: - metadata['on_read'] = on_read - if original_type: - metadata['original_type'] = original_type - if only_if: - metadata['only_if'] = only_if - if native: - metadata['native'] = native - return attr(type=type, metadata=metadata, *args, **kwargs) - -class ParseError(Exception): pass - -class Letter(str): - pass - -class Word(str): - pass - -class VNum(int): - pass - -class AreaFile(object): - area_type = None - MAX_TRADES = 5 - - def __init__(self, filename): - super(AreaFile, self).__init__() - self.file = io.open(filename, mode='rt', encoding='latin-1') - self.index = 0 - self.data = self.file.read() - self.filename = filename - self.file.close() - area_type = self.area_type or RomArea - self.area = area_type() - self.skipped_sections = [] - self.current_section_name = "N/A" - self.readers = { - Word: self.read_word, - Letter: self.read_letter, - VNum: self.read_number, - str: self.read_string, - int: self.read_number, - VNum: self.read_number, - enum.IntFlag: self.read_flag, - RomArmorClass: lambda: self.read_object_from_fields(RomArmorClass), - } - - def read_letter(self): - self.skip_whitespace() - result = self.current_char - self.advance() - return result - - def read_word(self): - self.skip_whitespace() - word = "" - end = '' - if self.current_char == '\'' or self.current_char == '"': - end = self.current_char - self.advance() - word = self.read_until(end) - if self.current_char != end: - self.parse_fail("Unterminated quoted word") - self.advance() - return word - while self.current_char != '\0' and not self.current_char.isspace(): - word += self.current_char - self.advance() - return word - - def read_string(self): - self.skip_whitespace() - res = self.read_until('~') - if self.current_char != '~': - self.parse_fail("Unterminated string") - self.advance() - return res - - def read_number(self): - self.skip_whitespace() - while self.current_char.isspace(): - self.advance() - number = 0 - sign = False - while self.current_char.isspace(): - self.advance() - if self.current_char == '+': - self.advance() - elif self.current_char == '-': - sign = True - self.advance() - if not self.current_char.isdigit(): - self.parse_fail("Expected number") - while self.current_char.isdigit(): - number = number * 10 + int(self.current_char) - self.advance() - if sign: - number *= -1 - if self.current_char == '|': - self.advance() - number += self.read_number() - return number - - def read_to_eol(self): - return self.read_until('\n') - + if on_read: + metadata['on_read'] = on_read + if original_type: + metadata['original_type'] = original_type + if only_if: + metadata['only_if'] = only_if + if native: + metadata['native'] = native + return attr(type=type, metadata=metadata, *args, **kwargs) + +class ParseError(Exception): pass + +class Letter(str): + pass + +class Word(str): + pass + +class VNum(int): + pass + +class AreaFile(object): + area_type = None + MAX_TRADES = 5 + + def __init__(self, filename): + super(AreaFile, self).__init__() + self.file = io.open(filename, mode='rt', encoding='latin-1') + self.index = 0 + self.data = self.file.read() + self.filename = filename + self.file.close() + area_type = self.area_type or RomArea + self.area = area_type() + self.skipped_sections = [] + self.current_section_name = "N/A" + self.readers = { + Word: self.read_word, + Letter: self.read_letter, + VNum: self.read_number, + str: self.read_string, + int: self.read_number, + VNum: self.read_number, + enum.IntFlag: self.read_flag, + RomArmorClass: lambda: self.read_object_from_fields(RomArmorClass), + } + + def read_letter(self): + self.skip_whitespace() + result = self.current_char + self.advance() + return result + + def read_word(self): + self.skip_whitespace() + word = "" + end = '' + if self.current_char == '\'' or self.current_char == '"': + end = self.current_char + self.advance() + word = self.read_until(end) + if self.current_char != end: + self.parse_fail("Unterminated quoted word") + self.advance() + return word + while self.current_char != '\0' and not self.current_char.isspace(): + word += self.current_char + self.advance() + return word + + def read_string(self): + self.skip_whitespace() + res = self.read_until('~') + if self.current_char != '~': + self.parse_fail("Unterminated string") + self.advance() + return res + + def read_number(self): + self.skip_whitespace() + while self.current_char.isspace(): + self.advance() + number = 0 + sign = False + while self.current_char.isspace(): + self.advance() + if self.current_char == '+': + self.advance() + elif self.current_char == '-': + sign = True + self.advance() + if not self.current_char.isdigit(): + self.parse_fail("Expected number") + while self.current_char.isdigit(): + number = number * 10 + int(self.current_char) + self.advance() + if sign: + number *= -1 + if self.current_char == '|': + self.advance() + number += self.read_number() + return number + + def read_to_eol(self): + return self.read_until('\n') + def skip_smaug_programs(self): self.skip_whitespace() - while self.current_char == '>': - program_end = self.data.find('\n|\n', self.index) - if program_end == -1: - program_end = self.data.find('\r\n|\r\n', self.index) - if program_end != -1: - self.index = program_end + 3 - self.skip_whitespace() - continue - next_record = self.data.find('\n#', self.index) - if next_record == -1: - self.index = len(self.data) - 1 - return + while self.current_char == '>': + program_end = self.data.find('\n|\n', self.index) + if program_end == -1: + program_end = self.data.find('\r\n|\r\n', self.index) + if program_end != -1: + self.index = program_end + 3 + self.skip_whitespace() + continue + next_record = self.data.find('\n#', self.index) + if next_record == -1: + self.index = len(self.data) - 1 + return self.index = next_record + 1 def read_smaug_programs(self): @@ -175,273 +175,273 @@ def read_smaug_programs(self): self.skip_whitespace() self.read_and_verify_letter('|') return programs - - def read_number_line(self): - self.skip_whitespace() - line = self.read_to_eol().strip() - if not line: - return [] - return [int(value) for value in line.split()] - - def read_until(self, endchar): - ahead = self.data.find(endchar, self.index) - if ahead == -1: - ahead = len(self.data) - result = self.data[self.index:ahead] - self.index = ahead - return result - - @property - def current_char(self): - if self.index >= len(self.data): - return '\0' - return self.data[self.index] - - def advance(self): - self.index += 1 - - def skip_whitespace(self): - while self.index < len(self.data) and self.current_char.isspace(): - self.advance() - - def read_flag(self): - words = [] - while True: - components = [] - while True: - negative = False - self.skip_whitespace() - if self.current_char == '+': - self.advance() - if self.current_char == '-': - negative = True - self.advance() - number = 0 - if not self.current_char.isdigit(): - while self.current_char.isalpha(): - number += flag_convert(self.current_char) - self.advance() - while self.current_char.isdigit(): - number = number * 10 + int(self.current_char) - self.advance() - components.append((negative, number)) - if self.current_char != '|': - break - self.advance() - word = 0 - for negative, number in reversed(components): - word += number - if negative: - word *= -1 - words.append(word) - if self.current_char != '&': - break - self.advance() - if len(words) == 1: - return words[0] - number = 0 - for index, word in enumerate(words): - number |= (word & 0xFFFFFFFF) << (index * 32) - return number - - - def read_and_verify_letter(self, verification): - letter = self.read_letter() - if letter != verification: - self.parse_fail("Expected %s got %s" % (verification, letter)) - return letter - - - def load_vnum_section(self, section_object_type): - while True: - vnum = self.read_vnum() - if vnum == 0: - break - yield self.read_object(section_object_type, vnum=vnum) - - def read_object(self, object_type, **kwargs): - if hasattr(object_type, 'read'): - return object_type.read(reader=self, **kwargs) - return self.read_object_by_fields(object_type, **kwargs) - - def read_object_by_fields(self, object_type, **kwargs): - f = fields(object_type) - read = {} - always_read = lambda context: True - unchanged = lambda value: value - for field in f: - field_type = field.metadata.get('original_type', field.type) - if field.metadata.get('read') == False: - continue - if issubclass(field_type, enum.IntFlag): - field_type = enum.IntFlag - reader = self.readers.get(field_type) - if reader is None: - self.parse_fail("Could not find a reader for field type %r" % field_type) - only_if = field.metadata.get('only_if', always_read) - should_read = only_if(context=read) - if not should_read: - continue - on_read = field.metadata.get('on_read', unchanged) - read[field.name] = on_read(reader()) - read.update(kwargs) - return object_type(**read) - - def read_vnum(self): - self.skip_whitespace() - self.read_and_verify_letter('#') - vnum = self.read_number() - return vnum - - def read_flat_section(self, object_type): - while True: - letter = self.read_letter() - if letter == 'S': - break - if letter == '*': # Comment - comment = self.read_to_eol() - yield object_type(comment=comment) - continue - yield object_type.read(reader=self, letter=letter) - - def read_area_metadata(self): - raise NotImplementedError - - def load_economy(self): - raise NotImplementedError - - def load_sections(self): - readers = { - 'area': self.read_area_metadata, - 'mobiles': self.load_mobiles, - 'rooms': self.load_rooms, - 'objects': self.load_objects, - 'helps': self.load_helps, - 'resets': self.load_resets, - 'shops': self.load_shops, - 'specials': self.load_specials, - 'economy': self.load_economy, - 'mobprogs': self.load_mobprogs, - } - while True: - section_name = self.read_section_name() - self.current_section_name = section_name - if section_name == '$': - return - reader = readers.get(section_name) - if reader is None: - self.skip_section(section_name) - else: - logger.info("Processing section %s" % section_name) - try: - readers[section_name]() - except Exception: - self.parse_fail("Error reading section %r" % section_name) - - def load_mobprogs(self): - while True: - vnum = self.read_vnum() - if vnum == 0: - break - self.area.mobprogs[vnum] = self.read_string() - - def skip_section(self, section_name): - logger.debug("Skipping section %s", section_name) - self.skipped_sections.append((section_name, self.read_until('#'))) - - def read_section_name(self): - self.read_and_verify_letter('#') - name = self.read_word() - return name.lower() - - def load_rooms(self): - for item in self.load_vnum_section(Room): - setitem(self.area.rooms, item.vnum, item) - - def load_objects(self): - for item in self.load_vnum_section(Item): - setitem(self.area.objects, item.vnum, item) - - def load_resets(self): - for reset in self.read_flat_section(Reset): - self.area.resets.append(reset) - - def load_specials(self): - for special in self.read_flat_section(Special): - self.area.specials.append(special) - - def load_shops(self): - while True: - keeper = self.read_number() - if keeper == 0: - break - logger.debug("Reading shop with keeper %d", keeper) - shop = RomShop(keeper=keeper) - for iTrade in range(self.MAX_TRADES): - shop.buy_type.append(self.read_number()) - shop.profit_buy = self.read_number() - shop.profit_sell = self.read_number() - shop.open_hour = self.read_number() - shop.close_hour = self.read_number() - self.area.shops.append(shop) - shop.comment = self.read_to_eol() - - def load_helps(self): - while True: - level = self.read_number() - keyword = self.read_string() - if keyword[0] == '$': - break - logger.debug("Reading help with keyword %s", keyword) - help = Help(level=level, keyword=keyword) - help.text = self.read_string() - self.area.helps.append(help) - - def jump_to_section(self, section_name): - self.index = self.data.find('#'+section_name.upper()) + len(section_name) + 1 - - def parse_fail(self, message): - backwards = self.data[:self.index] - lineno = backwards.count('\n') + 1 - col = backwards[::-1].find('\n') - message = str(self.filename) + " line " + str(lineno) + " col " + str(col) + " in section " + self.current_section_name + ": " + message - raise ParseError(message) - - def surrounding_text(self, window=50): - return self.data[self.index-window:self.index+window] - - def as_dict(self): - return EnumNameConverter().unstructure(self.area) - - def as_json(self, indent=None): - return json.dumps(self.as_dict(), indent=indent) - - def save_as_json(self): - fname = os.path.splitext(self.filename)[0] + '.json' - with open(fname, 'w') as f: - json.dump(self.as_dict(), f, indent=2) - -class RomAreaFile(AreaFile): - def dumps(self): - return render_document(self.area, self.area.NATIVE_SECTIONS, self.skipped_sections) - - def write(self, path): - with io.open(path, mode='wt', encoding='latin-1', newline='\n') as area_file: - area_file.write(self.dumps()) - - def load_mobiles(self): - for mob in self.load_vnum_section(RomMob): - setitem(self.area.mobs, mob.vnum, mob) - - def load_objects(self): - for item in self.load_vnum_section(RomItem): - setitem(self.area.objects, item.vnum, item) - - def read_area_metadata(self): - self.area.original_filename = self.read_string() - self.area.name = self.read_string() - self.area.metadata = self.read_string() - self.area.first_vnum = self.read_number() + + def read_number_line(self): + self.skip_whitespace() + line = self.read_to_eol().strip() + if not line: + return [] + return [int(value) for value in line.split()] + + def read_until(self, endchar): + ahead = self.data.find(endchar, self.index) + if ahead == -1: + ahead = len(self.data) + result = self.data[self.index:ahead] + self.index = ahead + return result + + @property + def current_char(self): + if self.index >= len(self.data): + return '\0' + return self.data[self.index] + + def advance(self): + self.index += 1 + + def skip_whitespace(self): + while self.index < len(self.data) and self.current_char.isspace(): + self.advance() + + def read_flag(self): + words = [] + while True: + components = [] + while True: + negative = False + self.skip_whitespace() + if self.current_char == '+': + self.advance() + if self.current_char == '-': + negative = True + self.advance() + number = 0 + if not self.current_char.isdigit(): + while self.current_char.isalpha(): + number += flag_convert(self.current_char) + self.advance() + while self.current_char.isdigit(): + number = number * 10 + int(self.current_char) + self.advance() + components.append((negative, number)) + if self.current_char != '|': + break + self.advance() + word = 0 + for negative, number in reversed(components): + word += number + if negative: + word *= -1 + words.append(word) + if self.current_char != '&': + break + self.advance() + if len(words) == 1: + return words[0] + number = 0 + for index, word in enumerate(words): + number |= (word & 0xFFFFFFFF) << (index * 32) + return number + + + def read_and_verify_letter(self, verification): + letter = self.read_letter() + if letter != verification: + self.parse_fail("Expected %s got %s" % (verification, letter)) + return letter + + + def load_vnum_section(self, section_object_type): + while True: + vnum = self.read_vnum() + if vnum == 0: + break + yield self.read_object(section_object_type, vnum=vnum) + + def read_object(self, object_type, **kwargs): + if hasattr(object_type, 'read'): + return object_type.read(reader=self, **kwargs) + return self.read_object_by_fields(object_type, **kwargs) + + def read_object_by_fields(self, object_type, **kwargs): + f = fields(object_type) + read = {} + always_read = lambda context: True + unchanged = lambda value: value + for field in f: + field_type = field.metadata.get('original_type', field.type) + if field.metadata.get('read') == False: + continue + if issubclass(field_type, enum.IntFlag): + field_type = enum.IntFlag + reader = self.readers.get(field_type) + if reader is None: + self.parse_fail("Could not find a reader for field type %r" % field_type) + only_if = field.metadata.get('only_if', always_read) + should_read = only_if(context=read) + if not should_read: + continue + on_read = field.metadata.get('on_read', unchanged) + read[field.name] = on_read(reader()) + read.update(kwargs) + return object_type(**read) + + def read_vnum(self): + self.skip_whitespace() + self.read_and_verify_letter('#') + vnum = self.read_number() + return vnum + + def read_flat_section(self, object_type): + while True: + letter = self.read_letter() + if letter == 'S': + break + if letter == '*': # Comment + comment = self.read_to_eol() + yield object_type(comment=comment) + continue + yield object_type.read(reader=self, letter=letter) + + def read_area_metadata(self): + raise NotImplementedError + + def load_economy(self): + raise NotImplementedError + + def load_sections(self): + readers = { + 'area': self.read_area_metadata, + 'mobiles': self.load_mobiles, + 'rooms': self.load_rooms, + 'objects': self.load_objects, + 'helps': self.load_helps, + 'resets': self.load_resets, + 'shops': self.load_shops, + 'specials': self.load_specials, + 'economy': self.load_economy, + 'mobprogs': self.load_mobprogs, + } + while True: + section_name = self.read_section_name() + self.current_section_name = section_name + if section_name == '$': + return + reader = readers.get(section_name) + if reader is None: + self.skip_section(section_name) + else: + logger.info("Processing section %s" % section_name) + try: + readers[section_name]() + except Exception: + self.parse_fail("Error reading section %r" % section_name) + + def load_mobprogs(self): + while True: + vnum = self.read_vnum() + if vnum == 0: + break + self.area.mobprogs[vnum] = self.read_string() + + def skip_section(self, section_name): + logger.debug("Skipping section %s", section_name) + self.skipped_sections.append((section_name, self.read_until('#'))) + + def read_section_name(self): + self.read_and_verify_letter('#') + name = self.read_word() + return name.lower() + + def load_rooms(self): + for item in self.load_vnum_section(Room): + setitem(self.area.rooms, item.vnum, item) + + def load_objects(self): + for item in self.load_vnum_section(Item): + setitem(self.area.objects, item.vnum, item) + + def load_resets(self): + for reset in self.read_flat_section(Reset): + self.area.resets.append(reset) + + def load_specials(self): + for special in self.read_flat_section(Special): + self.area.specials.append(special) + + def load_shops(self): + while True: + keeper = self.read_number() + if keeper == 0: + break + logger.debug("Reading shop with keeper %d", keeper) + shop = RomShop(keeper=keeper) + for iTrade in range(self.MAX_TRADES): + shop.buy_type.append(self.read_number()) + shop.profit_buy = self.read_number() + shop.profit_sell = self.read_number() + shop.open_hour = self.read_number() + shop.close_hour = self.read_number() + self.area.shops.append(shop) + shop.comment = self.read_to_eol() + + def load_helps(self): + while True: + level = self.read_number() + keyword = self.read_string() + if keyword[0] == '$': + break + logger.debug("Reading help with keyword %s", keyword) + help = Help(level=level, keyword=keyword) + help.text = self.read_string() + self.area.helps.append(help) + + def jump_to_section(self, section_name): + self.index = self.data.find('#'+section_name.upper()) + len(section_name) + 1 + + def parse_fail(self, message): + backwards = self.data[:self.index] + lineno = backwards.count('\n') + 1 + col = backwards[::-1].find('\n') + message = str(self.filename) + " line " + str(lineno) + " col " + str(col) + " in section " + self.current_section_name + ": " + message + raise ParseError(message) + + def surrounding_text(self, window=50): + return self.data[self.index-window:self.index+window] + + def as_dict(self): + return EnumNameConverter().unstructure(self.area) + + def as_json(self, indent=None): + return json.dumps(self.as_dict(), indent=indent) + + def save_as_json(self): + fname = os.path.splitext(self.filename)[0] + '.json' + with open(fname, 'w') as f: + json.dump(self.as_dict(), f, indent=2) + +class RomAreaFile(AreaFile): + def dumps(self): + return render_document(self.area, self.area.NATIVE_SECTIONS, self.skipped_sections) + + def write(self, path): + with io.open(path, mode='wt', encoding='latin-1', newline='\n') as area_file: + area_file.write(self.dumps()) + + def load_mobiles(self): + for mob in self.load_vnum_section(RomMob): + setitem(self.area.mobs, mob.vnum, mob) + + def load_objects(self): + for item in self.load_vnum_section(RomItem): + setitem(self.area.objects, item.vnum, item) + + def read_area_metadata(self): + self.area.original_filename = self.read_string() + self.area.name = self.read_string() + self.area.metadata = self.read_string() + self.area.first_vnum = self.read_number() self.area.last_vnum = self.read_number() @@ -608,330 +608,330 @@ def native_reset_arg3_suffix(owner): class ExtraDescription(object): keyword = field(default='', type=str, native=NativeField(1, native_tilde_string, prefix='E\n')) description = field(default='', type=str, native=NativeField(2, native_tilde_string)) - -@attributes -class MudBase(object): - vnum = field(default=0, type=VNum, read=False) - name = field(default="", type=str) - description = field(default='', type=str) - extra_descriptions = attr(default=Factory(list), type=List[ExtraDescription]) - -@attributes -class Item(MudBase): - short_desc = field(default='', type=str) - item_type = field(default=-1, type=int) - extra_flags = field(default=0, type=int) - wear_flags = field(default=0, type=WEAR_FLAGS, converter=WEAR_FLAGS) - cost = field(default=0, type=int) - level = field(default=0, type=int) - weight = field(default=0, type=int) - affected = attr(default=Factory(list)) - value = attr(default=Factory(list), type=List) - -@attributes + +@attributes +class MudBase(object): + vnum = field(default=0, type=VNum, read=False) + name = field(default="", type=str) + description = field(default='', type=str) + extra_descriptions = attr(default=Factory(list), type=List[ExtraDescription]) + +@attributes +class Item(MudBase): + short_desc = field(default='', type=str) + item_type = field(default=-1, type=int) + extra_flags = field(default=0, type=int) + wear_flags = field(default=0, type=WEAR_FLAGS, converter=WEAR_FLAGS) + cost = field(default=0, type=int) + level = field(default=0, type=int) + weight = field(default=0, type=int) + affected = attr(default=Factory(list)) + value = attr(default=Factory(list), type=List) + +@attributes class MercAffectData(object): type = attr(default=-1) duration = attr(default=-1) location = field(default=-1, native=NativeField(1, native_number, prefix='A\n')) modifier = field(default=-1, native=NativeField(2, native_number)) bitvector = attr(default=0) - -@attributes -class RomItem(Item): - - @staticmethod - def convert_condition(letter): - condition = -1 - conditions = { - 'P': 100, - 'G': 90, - 'A': 75, - 'W': 50, - 'D': 25, - 'B': 10, - 'R': 0, - } - condition = conditions[letter] - return condition - - vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) - name = field(default='', type=str, native=NativeField(1, native_tilde_string)) - short_desc = field(default='', type=str, native=NativeField(2, native_tilde_string)) - description = field(default='', type=str, native=NativeField(3, native_tilde_string)) - material = field(default='', type=str, native=NativeField(4, native_tilde_string)) - item_type = field(default=-1, type=int, native=NativeField(5, native_word)) - extra_flags = field(default=0, type=int, native=NativeField(6, native_flag)) - wear_flags = field(default=0, type=WEAR_FLAGS, converter=WEAR_FLAGS, native=NativeField(7, native_flag)) - value = field(default=Factory(list), type=List, native=NativeField(8, native_item_values)) - level = field(default=0, type=int, native=NativeField(9, native_number)) - weight = field(default=0, type=int, native=NativeField(10, native_number)) - cost = field(default=0, type=int, native=NativeField(11, native_number)) - condition = field(default=100, type=int, original_type=Letter, on_read=convert_condition, native=NativeField(12, native_condition)) - affected = field(default=Factory(list), native=NativeField(13, native_records, suffix='')) - extra_descriptions = field(default=Factory(list), type=List[ExtraDescription], native=NativeField(14, native_records, suffix='')) - - @classmethod - def read(cls, reader, vnum=None, **kwargs): - logger.debug("Reading object %d", vnum) - name = reader.read_string() - short_desc = reader.read_string() - description = reader.read_string() - material = reader.read_string() - item_type = reader.read_word() - extra_flags = reader.read_flag() - wear_flags = reader.read_flag() - if item_type == 'weapon': - value = [reader.read_word(), reader.read_number(), reader.read_number(), reader.read_word(), reader.read_flag(), ] - elif item_type == 'container': - value = [reader.read_number(), reader.read_flag(), reader.read_number(), reader.read_number(), reader.read_number(), ] - elif item_type == 'drink' or item_type == 'fountain': - value = [reader.read_number(), reader.read_number(), reader.read_word(), reader.read_number(), reader.read_number(), ] - elif item_type == 'wand' or item_type == 'staff': - value = [reader.read_number(), reader.read_number(), reader.read_number(), reader.read_word(), reader.read_number(), ] - elif item_type in ('potion', 'pill', 'scroll'): - value = [reader.read_number(), reader.read_word(), reader.read_word(), reader.read_word(), reader.read_word(), ] - else: - value = [reader.read_flag(), reader.read_flag(), reader.read_flag(), reader.read_flag(), reader.read_flag(), ] - level = reader.read_number() - weight = reader.read_number() - cost = reader.read_number() - condition = cls.convert_condition(reader.read_letter()) - affected = [] - extra_descriptions = [] - while True: - letter = reader.read_letter() - if letter == 'A': - af = RomAffectData() - af.where = 'TO_OBJECT' - af.type = -1 - af.level = level - af.duration = -1 - af.location = reader.read_number() - af.modifier = reader.read_number() - affected.append(af) - elif letter == 'F': - af = RomAffectData() - letter = reader.read_letter() - if letter == 'A': - af.where = 'TO_AFFECTS' - elif letter == 'I': - af.where = 'TO_IMMUNE' - elif letter == 'R': - af.where = 'TO_RESIST' - elif letter == 'V': - af.where = 'TO_VULN' - else: - reader.parse_fail("Bad where on flag set") - af.type = -1 - af.level = level - af.duration = -1 - af.location = reader.read_number() - af.modifier = reader.read_number() - af.bitvector = reader.read_flag() - affected.append(af) - elif letter == 'E': - extra_descriptions.append(reader.read_object(ExtraDescription)) - else: - reader.index -= 1 - break - return cls(vnum=vnum, name=name, short_desc=short_desc, description=description, material=material, item_type=item_type, extra_flags=extra_flags, wear_flags=wear_flags, value=value, level=level, weight=weight, cost=cost, condition=condition, affected=affected, extra_descriptions=extra_descriptions) - - -multiply_10 = lambda n: n * 10 - -@attributes -class RomArmorClass(object): - pierce = field(default=0, type=int, on_read=multiply_10, native=NativeField(1, native_divide_by_ten)) - bash = field(default=0, type=int, on_read=multiply_10, native=NativeField(2, native_divide_by_ten)) - slash = field(default=0, type=int, on_read=multiply_10, native=NativeField(3, native_divide_by_ten)) - exotic = field(default=0, type=int, on_read=multiply_10, native=NativeField(4, native_divide_by_ten)) - -@attributes -class Dice(object): - number = field(default=0, type=int, native=NativeField(1, native_number, suffix='')) - sides = field(default=0, type=int, native=NativeField(2, native_number, prefix='d', suffix='')) - bonus = field(default=0, type=int, native=NativeField(3, native_signed_number)) - - @classmethod - def read(cls, reader, **kwargs): - number = reader.read_number() - reader.read_letter() #D - sides = reader.read_number() - bonus = reader.read_number() - return cls(number=number, sides=sides, bonus=bonus, **kwargs) - - def roll(self): - # ROM's dice() (src/db.c) returns 0 for size 0, and its - # number_range() clamps a degenerate range to the low bound, - # so negative sides roll 1 per die. - if self.sides == 0: - return self.bonus - score = 0 - sides = max(self.sides, 1) - for roll in range(self.number): - score += random.randrange(1, sides + 1) - score += self.bonus - return score - -@attributes -class RomMobprog(object): - trig_type = field(default=None, type=Word, native=NativeField(1, native_word, prefix='M ', suffix=' ')) - vnum = field(default=-1, type=VNum, native=NativeField(2, native_number, suffix=' ')) - trig_phrase = field(default=None, type=str, native=NativeField(3, native_tilde_string)) - -@attributes -class RomCharacter(MudBase): - short_desc = field(default='', type=str) - long_desc = attr(default="", type=str) - level = field(default=0, type=int) - race = attr(default="", type=str) - group = attr(default=0, type=int) - hitroll = attr(default=0, type=int) - hit = attr(default=Factory(Dice), type=Dice) - mana = attr(default=Factory(Dice), type=Dice) - material = field(default='', type=str) - damage = attr(default=Factory(Dice), type=Dice) - damtype = attr(default='', type=Word) - ac = attr(default=Factory(RomArmorClass), type=RomArmorClass) - act = attr(default=ROM_ACT_TYPES.IS_NPC.value, type=ROM_ACT_TYPES, converter=ROM_ACT_TYPES) - affected_by = attr(default=0, type=AFFECTED_BY, converter=AFFECTED_BY) - -mark_as_npc = lambda act_flags: ROM_ACT_TYPES(act_flags) | ROM_ACT_TYPES.IS_NPC - -@attributes -class RomMob(RomCharacter): - vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) - name = field(default='', type=str, native=NativeField(1, native_tilde_string)) - short_desc = field(default='', type=str, native=NativeField(2, native_tilde_string)) - long_desc = field(default='', type=str, native=NativeField(3, native_tilde_string)) - description = field(default='', type=str, native=NativeField(4, native_tilde_string)) - race = field(default='', type=str, native=NativeField(5, native_tilde_string)) - act = field(default=0, type=ROM_ACT_TYPES, converter=ROM_ACT_TYPES, native=NativeField(6, native_flag)) - affected_by = field(default=0, type=AFFECTED_BY, converter=AFFECTED_BY, native=NativeField(7, native_flag)) - alignment = field(default=0, type=int, native=NativeField(8, native_number)) - group = field(default=0, type=int, native=NativeField(9, native_number)) - level = field(default=0, type=int, native=NativeField(10, native_number)) - hitroll = field(default=0, type=int, native=NativeField(11, native_number)) - hit = field(default=Factory(Dice), type=Dice, native=NativeField(12, native_nested)) - mana = field(default=Factory(Dice), type=Dice, native=NativeField(13, native_nested)) - damage = field(default=Factory(Dice), type=Dice, native=NativeField(14, native_nested)) - damtype = field(default='', type=Word, native=NativeField(15, native_word)) - ac = field(default=Factory(RomArmorClass), type=RomArmorClass, native=NativeField(16, native_nested)) - shop = field(default=None, read=False) - off_flags = field(default=0, type=OFFENSE, converter=OFFENSE, native=NativeField(17, native_flag)) - imm_flags = field(default=0, type=IMM_FLAGS, converter=IMM_FLAGS, native=NativeField(18, native_flag)) - res_flags = field(default=0, type=IMM_FLAGS, converter=IMM_FLAGS, native=NativeField(19, native_flag)) - vuln_flags = field(default=0, type=IMM_FLAGS, converter=IMM_FLAGS, native=NativeField(20, native_flag)) - start_pos = field(default=None, type=Word, native=NativeField(21, native_word)) - default_pos = field(default=None, type=Word, native=NativeField(22, native_word)) - sex = field(default='', type=Word, native=NativeField(23, native_word)) - wealth = field(default=0, type=int, native=NativeField(24, native_number)) - form = field(default=0, type=FORMS, converter=FORMS, native=NativeField(25, native_flag)) - parts = field(default=0, type=PARTS, converter=PARTS, native=NativeField(26, native_flag)) - size = field(default=None, type=Word, native=NativeField(27, native_word)) - material = field(default='', type=str, native=NativeField(28, native_word)) - mprogs = field(default=Factory(list), type=Optional[List[RomMobprog]], native=NativeField(29, native_records)) - - @classmethod - def read(cls, reader, vnum, **kwargs): - logger.debug("Reading mob %d" % vnum) - name = reader.read_string() - short_desc = reader.read_string() - long_desc = reader.read_string() - description = reader.read_string() - race = reader.read_string() - act = ROM_ACT_TYPES(reader.read_flag()) | ROM_ACT_TYPES.IS_NPC - affected_by = reader.read_flag() - alignment = reader.read_number() - group = reader.read_number() - level = reader.read_number() - hitroll = reader.read_number() - hit = Dice.read(reader=reader) - mana = Dice.read(reader=reader) - damage = Dice.read(reader=reader) - damtype = reader.read_word() - ac = reader.read_object(RomArmorClass) - off_flags = reader.read_flag() - imm_flags = reader.read_flag() - res_flags = reader.read_flag() - vuln_flags = reader.read_flag() - start_pos = reader.read_word() - default_pos = reader.read_word() - sex = reader.read_word() - wealth = reader.read_number() - form = reader.read_flag() - parts = reader.read_flag() - size = reader.read_word() - material = reader.read_word() - mprogs = [] - while True: - letter = reader.read_letter() - if letter == 'F': - word = reader.read_word() - vector = reader.read_flag() - if word.startswith('act'): - act = remove_bit(act, vector) - elif word.startswith('aff'): - affected_by = remove_bit(affected_by, vector) - elif word.startswith('off'): - off_flags = remove_bit(off_flags, vector) - elif word.startswith('imm'): - imm_flags = remove_bit(imm_flags, vector) - elif word.startswith('res'): - res_flags = remove_bit(res_flags, vector) - elif word.startswith('vul'): - vuln_flags = remove_bit(vuln_flags, vector) - elif word.startswith('for'): - form = remove_bit(form, vector) - elif word.startswith('par'): - parts = remove_bit(parts, vector) - else: - reader.parse_fail("Flag remove: flag not found: %s" % word) - elif letter == 'M': - mprogs.append(reader.read_object_by_fields(RomMobprog)) - else: - reader.index -= 1 - break - return cls(vnum=vnum, name=name, short_desc=short_desc, long_desc=long_desc, description=description, race=race, act=act, affected_by=affected_by, alignment=alignment, group=group, level=level, hitroll=hitroll, hit=hit, mana=mana, damage=damage, damtype=damtype, ac=ac, off_flags=off_flags, imm_flags=imm_flags, res_flags=res_flags, vuln_flags=vuln_flags, start_pos=start_pos, default_pos=default_pos, sex=sex, wealth=wealth, form=form, parts=parts, size=size, material=material, mprogs=mprogs) - - -def native_affect_prefix(owner): - if owner.where == 'TO_OBJECT': - return 'A\n' - letters = { - 'TO_AFFECTS': 'A', - 'TO_IMMUNE': 'I', - 'TO_RESIST': 'R', - 'TO_VULN': 'V', - } - try: - return 'F%s ' % letters[owner.where] - except KeyError: - raise NativeWriteError("ROM affect target %r has no native tag" % owner.where) - - -def native_affect_modifier_suffix(owner): - return '\n' if owner.where == 'TO_OBJECT' else ' ' - - -@attributes -class RomAffectData(object): - where = attr(default=None) - type = attr(default=None) - level = attr(default=None) - duration = attr(default=None) - location = field(default=None, native=NativeField(1, native_number, prefix=native_affect_prefix, suffix=' ')) - modifier = field(default=None, native=NativeField(2, native_number, suffix=native_affect_modifier_suffix)) - bitvector = field( - default=0, - native=NativeField( - 3, - native_flag, - when=lambda owner: owner.where != 'TO_OBJECT', - ), - ) - - + +@attributes +class RomItem(Item): + + @staticmethod + def convert_condition(letter): + condition = -1 + conditions = { + 'P': 100, + 'G': 90, + 'A': 75, + 'W': 50, + 'D': 25, + 'B': 10, + 'R': 0, + } + condition = conditions[letter] + return condition + + vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) + name = field(default='', type=str, native=NativeField(1, native_tilde_string)) + short_desc = field(default='', type=str, native=NativeField(2, native_tilde_string)) + description = field(default='', type=str, native=NativeField(3, native_tilde_string)) + material = field(default='', type=str, native=NativeField(4, native_tilde_string)) + item_type = field(default=-1, type=int, native=NativeField(5, native_word)) + extra_flags = field(default=0, type=int, native=NativeField(6, native_flag)) + wear_flags = field(default=0, type=WEAR_FLAGS, converter=WEAR_FLAGS, native=NativeField(7, native_flag)) + value = field(default=Factory(list), type=List, native=NativeField(8, native_item_values)) + level = field(default=0, type=int, native=NativeField(9, native_number)) + weight = field(default=0, type=int, native=NativeField(10, native_number)) + cost = field(default=0, type=int, native=NativeField(11, native_number)) + condition = field(default=100, type=int, original_type=Letter, on_read=convert_condition, native=NativeField(12, native_condition)) + affected = field(default=Factory(list), native=NativeField(13, native_records, suffix='')) + extra_descriptions = field(default=Factory(list), type=List[ExtraDescription], native=NativeField(14, native_records, suffix='')) + + @classmethod + def read(cls, reader, vnum=None, **kwargs): + logger.debug("Reading object %d", vnum) + name = reader.read_string() + short_desc = reader.read_string() + description = reader.read_string() + material = reader.read_string() + item_type = reader.read_word() + extra_flags = reader.read_flag() + wear_flags = reader.read_flag() + if item_type == 'weapon': + value = [reader.read_word(), reader.read_number(), reader.read_number(), reader.read_word(), reader.read_flag(), ] + elif item_type == 'container': + value = [reader.read_number(), reader.read_flag(), reader.read_number(), reader.read_number(), reader.read_number(), ] + elif item_type == 'drink' or item_type == 'fountain': + value = [reader.read_number(), reader.read_number(), reader.read_word(), reader.read_number(), reader.read_number(), ] + elif item_type == 'wand' or item_type == 'staff': + value = [reader.read_number(), reader.read_number(), reader.read_number(), reader.read_word(), reader.read_number(), ] + elif item_type in ('potion', 'pill', 'scroll'): + value = [reader.read_number(), reader.read_word(), reader.read_word(), reader.read_word(), reader.read_word(), ] + else: + value = [reader.read_flag(), reader.read_flag(), reader.read_flag(), reader.read_flag(), reader.read_flag(), ] + level = reader.read_number() + weight = reader.read_number() + cost = reader.read_number() + condition = cls.convert_condition(reader.read_letter()) + affected = [] + extra_descriptions = [] + while True: + letter = reader.read_letter() + if letter == 'A': + af = RomAffectData() + af.where = 'TO_OBJECT' + af.type = -1 + af.level = level + af.duration = -1 + af.location = reader.read_number() + af.modifier = reader.read_number() + affected.append(af) + elif letter == 'F': + af = RomAffectData() + letter = reader.read_letter() + if letter == 'A': + af.where = 'TO_AFFECTS' + elif letter == 'I': + af.where = 'TO_IMMUNE' + elif letter == 'R': + af.where = 'TO_RESIST' + elif letter == 'V': + af.where = 'TO_VULN' + else: + reader.parse_fail("Bad where on flag set") + af.type = -1 + af.level = level + af.duration = -1 + af.location = reader.read_number() + af.modifier = reader.read_number() + af.bitvector = reader.read_flag() + affected.append(af) + elif letter == 'E': + extra_descriptions.append(reader.read_object(ExtraDescription)) + else: + reader.index -= 1 + break + return cls(vnum=vnum, name=name, short_desc=short_desc, description=description, material=material, item_type=item_type, extra_flags=extra_flags, wear_flags=wear_flags, value=value, level=level, weight=weight, cost=cost, condition=condition, affected=affected, extra_descriptions=extra_descriptions) + + +multiply_10 = lambda n: n * 10 + +@attributes +class RomArmorClass(object): + pierce = field(default=0, type=int, on_read=multiply_10, native=NativeField(1, native_divide_by_ten)) + bash = field(default=0, type=int, on_read=multiply_10, native=NativeField(2, native_divide_by_ten)) + slash = field(default=0, type=int, on_read=multiply_10, native=NativeField(3, native_divide_by_ten)) + exotic = field(default=0, type=int, on_read=multiply_10, native=NativeField(4, native_divide_by_ten)) + +@attributes +class Dice(object): + number = field(default=0, type=int, native=NativeField(1, native_number, suffix='')) + sides = field(default=0, type=int, native=NativeField(2, native_number, prefix='d', suffix='')) + bonus = field(default=0, type=int, native=NativeField(3, native_signed_number)) + + @classmethod + def read(cls, reader, **kwargs): + number = reader.read_number() + reader.read_letter() #D + sides = reader.read_number() + bonus = reader.read_number() + return cls(number=number, sides=sides, bonus=bonus, **kwargs) + + def roll(self): + # ROM's dice() (src/db.c) returns 0 for size 0, and its + # number_range() clamps a degenerate range to the low bound, + # so negative sides roll 1 per die. + if self.sides == 0: + return self.bonus + score = 0 + sides = max(self.sides, 1) + for roll in range(self.number): + score += random.randrange(1, sides + 1) + score += self.bonus + return score + +@attributes +class RomMobprog(object): + trig_type = field(default=None, type=Word, native=NativeField(1, native_word, prefix='M ', suffix=' ')) + vnum = field(default=-1, type=VNum, native=NativeField(2, native_number, suffix=' ')) + trig_phrase = field(default=None, type=str, native=NativeField(3, native_tilde_string)) + +@attributes +class RomCharacter(MudBase): + short_desc = field(default='', type=str) + long_desc = attr(default="", type=str) + level = field(default=0, type=int) + race = attr(default="", type=str) + group = attr(default=0, type=int) + hitroll = attr(default=0, type=int) + hit = attr(default=Factory(Dice), type=Dice) + mana = attr(default=Factory(Dice), type=Dice) + material = field(default='', type=str) + damage = attr(default=Factory(Dice), type=Dice) + damtype = attr(default='', type=Word) + ac = attr(default=Factory(RomArmorClass), type=RomArmorClass) + act = attr(default=ROM_ACT_TYPES.IS_NPC.value, type=ROM_ACT_TYPES, converter=ROM_ACT_TYPES) + affected_by = attr(default=0, type=AFFECTED_BY, converter=AFFECTED_BY) + +mark_as_npc = lambda act_flags: ROM_ACT_TYPES(act_flags) | ROM_ACT_TYPES.IS_NPC + +@attributes +class RomMob(RomCharacter): + vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) + name = field(default='', type=str, native=NativeField(1, native_tilde_string)) + short_desc = field(default='', type=str, native=NativeField(2, native_tilde_string)) + long_desc = field(default='', type=str, native=NativeField(3, native_tilde_string)) + description = field(default='', type=str, native=NativeField(4, native_tilde_string)) + race = field(default='', type=str, native=NativeField(5, native_tilde_string)) + act = field(default=0, type=ROM_ACT_TYPES, converter=ROM_ACT_TYPES, native=NativeField(6, native_flag)) + affected_by = field(default=0, type=AFFECTED_BY, converter=AFFECTED_BY, native=NativeField(7, native_flag)) + alignment = field(default=0, type=int, native=NativeField(8, native_number)) + group = field(default=0, type=int, native=NativeField(9, native_number)) + level = field(default=0, type=int, native=NativeField(10, native_number)) + hitroll = field(default=0, type=int, native=NativeField(11, native_number)) + hit = field(default=Factory(Dice), type=Dice, native=NativeField(12, native_nested)) + mana = field(default=Factory(Dice), type=Dice, native=NativeField(13, native_nested)) + damage = field(default=Factory(Dice), type=Dice, native=NativeField(14, native_nested)) + damtype = field(default='', type=Word, native=NativeField(15, native_word)) + ac = field(default=Factory(RomArmorClass), type=RomArmorClass, native=NativeField(16, native_nested)) + shop = field(default=None, read=False) + off_flags = field(default=0, type=OFFENSE, converter=OFFENSE, native=NativeField(17, native_flag)) + imm_flags = field(default=0, type=IMM_FLAGS, converter=IMM_FLAGS, native=NativeField(18, native_flag)) + res_flags = field(default=0, type=IMM_FLAGS, converter=IMM_FLAGS, native=NativeField(19, native_flag)) + vuln_flags = field(default=0, type=IMM_FLAGS, converter=IMM_FLAGS, native=NativeField(20, native_flag)) + start_pos = field(default=None, type=Word, native=NativeField(21, native_word)) + default_pos = field(default=None, type=Word, native=NativeField(22, native_word)) + sex = field(default='', type=Word, native=NativeField(23, native_word)) + wealth = field(default=0, type=int, native=NativeField(24, native_number)) + form = field(default=0, type=FORMS, converter=FORMS, native=NativeField(25, native_flag)) + parts = field(default=0, type=PARTS, converter=PARTS, native=NativeField(26, native_flag)) + size = field(default=None, type=Word, native=NativeField(27, native_word)) + material = field(default='', type=str, native=NativeField(28, native_word)) + mprogs = field(default=Factory(list), type=Optional[List[RomMobprog]], native=NativeField(29, native_records)) + + @classmethod + def read(cls, reader, vnum, **kwargs): + logger.debug("Reading mob %d" % vnum) + name = reader.read_string() + short_desc = reader.read_string() + long_desc = reader.read_string() + description = reader.read_string() + race = reader.read_string() + act = ROM_ACT_TYPES(reader.read_flag()) | ROM_ACT_TYPES.IS_NPC + affected_by = reader.read_flag() + alignment = reader.read_number() + group = reader.read_number() + level = reader.read_number() + hitroll = reader.read_number() + hit = Dice.read(reader=reader) + mana = Dice.read(reader=reader) + damage = Dice.read(reader=reader) + damtype = reader.read_word() + ac = reader.read_object(RomArmorClass) + off_flags = reader.read_flag() + imm_flags = reader.read_flag() + res_flags = reader.read_flag() + vuln_flags = reader.read_flag() + start_pos = reader.read_word() + default_pos = reader.read_word() + sex = reader.read_word() + wealth = reader.read_number() + form = reader.read_flag() + parts = reader.read_flag() + size = reader.read_word() + material = reader.read_word() + mprogs = [] + while True: + letter = reader.read_letter() + if letter == 'F': + word = reader.read_word() + vector = reader.read_flag() + if word.startswith('act'): + act = remove_bit(act, vector) + elif word.startswith('aff'): + affected_by = remove_bit(affected_by, vector) + elif word.startswith('off'): + off_flags = remove_bit(off_flags, vector) + elif word.startswith('imm'): + imm_flags = remove_bit(imm_flags, vector) + elif word.startswith('res'): + res_flags = remove_bit(res_flags, vector) + elif word.startswith('vul'): + vuln_flags = remove_bit(vuln_flags, vector) + elif word.startswith('for'): + form = remove_bit(form, vector) + elif word.startswith('par'): + parts = remove_bit(parts, vector) + else: + reader.parse_fail("Flag remove: flag not found: %s" % word) + elif letter == 'M': + mprogs.append(reader.read_object_by_fields(RomMobprog)) + else: + reader.index -= 1 + break + return cls(vnum=vnum, name=name, short_desc=short_desc, long_desc=long_desc, description=description, race=race, act=act, affected_by=affected_by, alignment=alignment, group=group, level=level, hitroll=hitroll, hit=hit, mana=mana, damage=damage, damtype=damtype, ac=ac, off_flags=off_flags, imm_flags=imm_flags, res_flags=res_flags, vuln_flags=vuln_flags, start_pos=start_pos, default_pos=default_pos, sex=sex, wealth=wealth, form=form, parts=parts, size=size, material=material, mprogs=mprogs) + + +def native_affect_prefix(owner): + if owner.where == 'TO_OBJECT': + return 'A\n' + letters = { + 'TO_AFFECTS': 'A', + 'TO_IMMUNE': 'I', + 'TO_RESIST': 'R', + 'TO_VULN': 'V', + } + try: + return 'F%s ' % letters[owner.where] + except KeyError: + raise NativeWriteError("ROM affect target %r has no native tag" % owner.where) + + +def native_affect_modifier_suffix(owner): + return '\n' if owner.where == 'TO_OBJECT' else ' ' + + +@attributes +class RomAffectData(object): + where = attr(default=None) + type = attr(default=None) + level = attr(default=None) + duration = attr(default=None) + location = field(default=None, native=NativeField(1, native_number, prefix=native_affect_prefix, suffix=' ')) + modifier = field(default=None, native=NativeField(2, native_number, suffix=native_affect_modifier_suffix)) + bitvector = field( + default=0, + native=NativeField( + 3, + native_flag, + when=lambda owner: owner.where != 'TO_OBJECT', + ), + ) + + @attributes class MercArea(object): NATIVE_SECTIONS = ( @@ -946,56 +946,56 @@ class MercArea(object): ) metadata = field(default='', native=NativeField(1, native_tilde_string, section='area')) - helps = attr(default=Factory(list)) - rooms = attr(default=Factory(OrderedDict)) - mobs = attr(default=Factory(OrderedDict)) - objects = attr(default=Factory(OrderedDict)) - resets = attr(default=Factory(list)) - specials = attr(default=Factory(list)) - shops = attr(default=Factory(list)) - mobprogs = attr(default=Factory(OrderedDict)) - - -@attributes -class Help(object): - level = field(default=0, type=int, native=NativeField(1, native_number, suffix=' ')) - keyword = field(default='', type=Word, native=NativeField(2, native_tilde_string)) - text = field(default='', type=str, native=NativeField(3, native_tilde_string)) - -@attributes -class Exit(object): - keyword = field(default='', type=Word, native=NativeField(3, native_tilde_string)) - description = field(default='', type=str, native=NativeField(2, native_tilde_string)) - door = field( - default=None, - type=Optional[EXIT_DIRECTIONS], - converter=lambda value: None if value is None else EXIT_DIRECTIONS(value), - native=NativeField(1, native_number, prefix='D'), - ) - exit_info = field(default=0, type=EXIT_FLAGS, converter=EXIT_FLAGS, native=NativeField(4, native_exit_lock)) - rs_flags = attr(default=0, type=int) - key = field(default=0, type=int, native=NativeField(5, native_number)) - destination = field(default=None, type=int, native=NativeField(6, native_number)) - - @classmethod - def read(cls, reader, **kwargs): - logger.debug("Reading exit") - locks = 0 - door = reader.read_number() - description = reader.read_string() - keyword = reader.read_string() - exit_info = 0 - locks = reader.read_number() - key = reader.read_number() - destination = reader.read_number() - if locks == 1: - exit_info = EXIT_FLAGS.ISDOOR - elif locks == 2: - exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.PICKPROOF - elif locks == 3: - exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.NOPASS - elif locks == 4: - exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.NOPASS | EXIT_FLAGS.PICKPROOF + helps = attr(default=Factory(list)) + rooms = attr(default=Factory(OrderedDict)) + mobs = attr(default=Factory(OrderedDict)) + objects = attr(default=Factory(OrderedDict)) + resets = attr(default=Factory(list)) + specials = attr(default=Factory(list)) + shops = attr(default=Factory(list)) + mobprogs = attr(default=Factory(OrderedDict)) + + +@attributes +class Help(object): + level = field(default=0, type=int, native=NativeField(1, native_number, suffix=' ')) + keyword = field(default='', type=Word, native=NativeField(2, native_tilde_string)) + text = field(default='', type=str, native=NativeField(3, native_tilde_string)) + +@attributes +class Exit(object): + keyword = field(default='', type=Word, native=NativeField(3, native_tilde_string)) + description = field(default='', type=str, native=NativeField(2, native_tilde_string)) + door = field( + default=None, + type=Optional[EXIT_DIRECTIONS], + converter=lambda value: None if value is None else EXIT_DIRECTIONS(value), + native=NativeField(1, native_number, prefix='D'), + ) + exit_info = field(default=0, type=EXIT_FLAGS, converter=EXIT_FLAGS, native=NativeField(4, native_exit_lock)) + rs_flags = attr(default=0, type=int) + key = field(default=0, type=int, native=NativeField(5, native_number)) + destination = field(default=None, type=int, native=NativeField(6, native_number)) + + @classmethod + def read(cls, reader, **kwargs): + logger.debug("Reading exit") + locks = 0 + door = reader.read_number() + description = reader.read_string() + keyword = reader.read_string() + exit_info = 0 + locks = reader.read_number() + key = reader.read_number() + destination = reader.read_number() + if locks == 1: + exit_info = EXIT_FLAGS.ISDOOR + elif locks == 2: + exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.PICKPROOF + elif locks == 3: + exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.NOPASS + elif locks == 4: + exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.NOPASS | EXIT_FLAGS.PICKPROOF return cls(door=door, description=description, keyword=keyword, exit_info=exit_info, key=key, destination=destination) @@ -1049,120 +1049,120 @@ class SmaugExit(Exit): y = attr(default=0, type=int) pulltype = field(default=0, type=int, native=NativeField(8, native_number, suffix=' ')) pull = field(default=0, type=int, native=NativeField(9, native_number)) - -@attributes -class Room(MudBase): - NATIVE_SUFFIX = 'S\n' - - vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) - name = field(default='', type=str, native=NativeField(1, native_tilde_string)) - description = field(default='', type=str, native=NativeField(2, native_tilde_string)) - owner = field( - default=None, - type=str, - native=NativeField(10, native_tilde_string, prefix='O ', when=lambda owner: bool(owner.owner)), - ) - clan = field( - default='', - type=str, - native=NativeField(7, native_tilde_string, prefix='C ', when=lambda owner: bool(owner.clan)), - ) - area = attr(default=None) - area_number = field(default=0, type=int, native=NativeField(3, native_number)) - room_flags = field(default=0, type=ROM_ROOM_FLAGS, converter=ROM_ROOM_FLAGS, native=NativeField(4, native_flag)) - sector_type = field(default=0, type=SECTOR_TYPES, converter=SECTOR_TYPES, native=NativeField(5, native_number)) #FIXME - heal_rate = field( - default=100, - type=int, - native=NativeField(6, native_number, prefix='H ', when=lambda owner: owner.heal_rate != 100), - ) - mana_rate = field( - default=100, - type=int, - native=NativeField(7, native_number, prefix='M ', when=lambda owner: owner.mana_rate != 100), - ) - exits = field(default=Factory(list), type=List[Exit], native=NativeField(8, native_records, suffix='')) - extra_descriptions = field(default=Factory(list), type=List[ExtraDescription], native=NativeField(9, native_records, suffix='')) - - @classmethod - def read(cls, reader, vnum): - logger.debug("Reading room with vnum %d", vnum) - name = reader.read_string() - description = reader.read_string() - area_number = reader.read_number() - room_flags = reader.read_flag() - sector_type = reader.read_number() - if sector_type == -1: - sector_type = 0 - room = cls(vnum=vnum, name=name, description=description, area_number=area_number, room_flags=room_flags, sector_type=sector_type) - room.read_metadata(reader) - return room - - def read_metadata(self, reader): - while True: - letter = reader.read_letter() - if letter == 'S': - break - if letter == 'H': - self.heal_rate = reader.read_number() - elif letter == 'M': - self.mana_rate = reader.read_number() - elif letter == 'C': - self.clan = reader.read_string() - elif letter == 'D': - self.exits.append(Exit.read(reader=reader)) - elif letter == 'E': - self.extra_descriptions.append(reader.read_object(ExtraDescription)) - elif letter == 'O': - self.owner = reader.read_string() - else: - reader.parse_fail("Don't know how to process room attribute: %s" % letter) - - -@attributes -class Reset(object): - command = field(default=None, native=NativeField(1, native_reset_command, suffix=native_reset_command_suffix)) - if_flag = field( - default=0, - native=NativeField(2, native_number, suffix=' ', when=lambda owner: owner.command is not None), - ) - arg1 = field( - default=None, - type=Letter, - native=NativeField(3, native_number, suffix=' ', when=lambda owner: owner.command is not None), - ) - arg2 = field( - default=None, - native=NativeField(4, native_number, suffix=native_reset_arg2_suffix, when=lambda owner: owner.command is not None), - ) - arg3 = field( - default=None, - native=NativeField(5, native_number, suffix=native_reset_arg3_suffix, when=lambda owner: owner.command not in (None, 'G', 'R')), - ) - arg4 = field( - default=None, - native=NativeField(6, native_number, suffix='', when=lambda owner: owner.command in ('P', 'M')), - ) - comment = field(default=None, type=str, native=NativeField(7, native_comment)) - - @classmethod - def read(cls, reader, letter): - command = letter - if_flag = reader.read_number() - arg1 = reader.read_number() - arg2 = reader.read_number() - if letter == 'G' or letter == 'R': - arg3 = 0 - else: - arg3 = reader.read_number() - if letter == 'P' or letter == 'M': - arg4 = reader.read_number() - else: - arg4 = 0 - comment = reader.read_to_eol() - return cls(command=command, if_flag=if_flag, arg1=arg1, arg2=arg2, arg3=arg3, arg4=arg4, comment=comment) - -@attributes + +@attributes +class Room(MudBase): + NATIVE_SUFFIX = 'S\n' + + vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) + name = field(default='', type=str, native=NativeField(1, native_tilde_string)) + description = field(default='', type=str, native=NativeField(2, native_tilde_string)) + owner = field( + default=None, + type=str, + native=NativeField(10, native_tilde_string, prefix='O ', when=lambda owner: bool(owner.owner)), + ) + clan = field( + default='', + type=str, + native=NativeField(7, native_tilde_string, prefix='C ', when=lambda owner: bool(owner.clan)), + ) + area = attr(default=None) + area_number = field(default=0, type=int, native=NativeField(3, native_number)) + room_flags = field(default=0, type=ROM_ROOM_FLAGS, converter=ROM_ROOM_FLAGS, native=NativeField(4, native_flag)) + sector_type = field(default=0, type=SECTOR_TYPES, converter=SECTOR_TYPES, native=NativeField(5, native_number)) #FIXME + heal_rate = field( + default=100, + type=int, + native=NativeField(6, native_number, prefix='H ', when=lambda owner: owner.heal_rate != 100), + ) + mana_rate = field( + default=100, + type=int, + native=NativeField(7, native_number, prefix='M ', when=lambda owner: owner.mana_rate != 100), + ) + exits = field(default=Factory(list), type=List[Exit], native=NativeField(8, native_records, suffix='')) + extra_descriptions = field(default=Factory(list), type=List[ExtraDescription], native=NativeField(9, native_records, suffix='')) + + @classmethod + def read(cls, reader, vnum): + logger.debug("Reading room with vnum %d", vnum) + name = reader.read_string() + description = reader.read_string() + area_number = reader.read_number() + room_flags = reader.read_flag() + sector_type = reader.read_number() + if sector_type == -1: + sector_type = 0 + room = cls(vnum=vnum, name=name, description=description, area_number=area_number, room_flags=room_flags, sector_type=sector_type) + room.read_metadata(reader) + return room + + def read_metadata(self, reader): + while True: + letter = reader.read_letter() + if letter == 'S': + break + if letter == 'H': + self.heal_rate = reader.read_number() + elif letter == 'M': + self.mana_rate = reader.read_number() + elif letter == 'C': + self.clan = reader.read_string() + elif letter == 'D': + self.exits.append(Exit.read(reader=reader)) + elif letter == 'E': + self.extra_descriptions.append(reader.read_object(ExtraDescription)) + elif letter == 'O': + self.owner = reader.read_string() + else: + reader.parse_fail("Don't know how to process room attribute: %s" % letter) + + +@attributes +class Reset(object): + command = field(default=None, native=NativeField(1, native_reset_command, suffix=native_reset_command_suffix)) + if_flag = field( + default=0, + native=NativeField(2, native_number, suffix=' ', when=lambda owner: owner.command is not None), + ) + arg1 = field( + default=None, + type=Letter, + native=NativeField(3, native_number, suffix=' ', when=lambda owner: owner.command is not None), + ) + arg2 = field( + default=None, + native=NativeField(4, native_number, suffix=native_reset_arg2_suffix, when=lambda owner: owner.command is not None), + ) + arg3 = field( + default=None, + native=NativeField(5, native_number, suffix=native_reset_arg3_suffix, when=lambda owner: owner.command not in (None, 'G', 'R')), + ) + arg4 = field( + default=None, + native=NativeField(6, native_number, suffix='', when=lambda owner: owner.command in ('P', 'M')), + ) + comment = field(default=None, type=str, native=NativeField(7, native_comment)) + + @classmethod + def read(cls, reader, letter): + command = letter + if_flag = reader.read_number() + arg1 = reader.read_number() + arg2 = reader.read_number() + if letter == 'G' or letter == 'R': + arg3 = 0 + else: + arg3 = reader.read_number() + if letter == 'P' or letter == 'M': + arg4 = reader.read_number() + else: + arg4 = 0 + comment = reader.read_to_eol() + return cls(command=command, if_flag=if_flag, arg1=arg1, arg2=arg2, arg3=arg3, arg4=arg4, comment=comment) + +@attributes class Special(object): command = field(default=None, native=NativeField(1, native_reset_command, suffix=native_reset_command_suffix)) arg1 = field( @@ -1174,43 +1174,43 @@ class Special(object): native=NativeField(3, native_word, suffix='', when=lambda owner: owner.command is not None), ) comment = field(default=None, type=str, native=NativeField(4, native_comment)) - - @classmethod - def read(cls, reader, letter, **kwargs): - command = letter - arg1 = reader.read_number() - arg2 = reader.read_word() - comment = reader.read_to_eol() - return cls(command=command,arg1=arg1, arg2=arg2, comment=comment) - -@attributes -class RomArea(object): - NATIVE_SECTIONS = ( - NativeSection('AREA', owner_section='area'), - NativeSection('HELPS', collection='helps', end='0 $~\n'), - NativeSection('MOBILES', collection='mobs', end='#0\n', mapping=True), - NativeSection('OBJECTS', collection='objects', end='#0\n', mapping=True), - NativeSection('ROOMS', collection='rooms', end='#0\n', mapping=True), - NativeSection('RESETS', collection='resets', end='S\n'), - NativeSection('SHOPS', collection='shops', end='0\n'), - NativeSection('SPECIALS', collection='specials', end='S\n'), - ) - - name = field(default='', native=NativeField(2, native_tilde_string, section='area')) - metadata = field(default='', native=NativeField(3, native_tilde_string, section='area')) - original_filename = field(default='', native=NativeField(1, native_tilde_string, section='area')) - first_vnum = field(default=-1, native=NativeField(4, native_number, section='area')) - last_vnum = field(default=-1, native=NativeField(5, native_number, section='area')) - helps = attr(default=Factory(list), type=List[Help]) - rooms = attr(default=Factory(OrderedDict), type=Dict[int, Room]) - mobs = attr(default=Factory(OrderedDict)) - objects = attr(default=Factory(OrderedDict)) - resets = attr(default=Factory(list), type=List[Reset]) - specials = attr(default=Factory(list), type=List[Special]) - shops = attr(default=Factory(list)) - mobprogs = attr(default=Factory(OrderedDict)) - -@attributes + + @classmethod + def read(cls, reader, letter, **kwargs): + command = letter + arg1 = reader.read_number() + arg2 = reader.read_word() + comment = reader.read_to_eol() + return cls(command=command,arg1=arg1, arg2=arg2, comment=comment) + +@attributes +class RomArea(object): + NATIVE_SECTIONS = ( + NativeSection('AREA', owner_section='area'), + NativeSection('HELPS', collection='helps', end='0 $~\n'), + NativeSection('MOBILES', collection='mobs', end='#0\n', mapping=True), + NativeSection('OBJECTS', collection='objects', end='#0\n', mapping=True), + NativeSection('ROOMS', collection='rooms', end='#0\n', mapping=True), + NativeSection('RESETS', collection='resets', end='S\n'), + NativeSection('SHOPS', collection='shops', end='0\n'), + NativeSection('SPECIALS', collection='specials', end='S\n'), + ) + + name = field(default='', native=NativeField(2, native_tilde_string, section='area')) + metadata = field(default='', native=NativeField(3, native_tilde_string, section='area')) + original_filename = field(default='', native=NativeField(1, native_tilde_string, section='area')) + first_vnum = field(default=-1, native=NativeField(4, native_number, section='area')) + last_vnum = field(default=-1, native=NativeField(5, native_number, section='area')) + helps = attr(default=Factory(list), type=List[Help]) + rooms = attr(default=Factory(OrderedDict), type=Dict[int, Room]) + mobs = attr(default=Factory(OrderedDict)) + objects = attr(default=Factory(OrderedDict)) + resets = attr(default=Factory(list), type=List[Reset]) + specials = attr(default=Factory(list), type=List[Special]) + shops = attr(default=Factory(list)) + mobprogs = attr(default=Factory(OrderedDict)) + +@attributes class MercRoom(Room): owner = attr(default=None, type=str) clan = attr(default='', type=str) @@ -1223,30 +1223,30 @@ class MercRoom(Room): heal_rate = attr(default=100, type=int) mana_rate = attr(default=100, type=int) exits = field(default=Factory(list), type=List[MercExit], native=NativeField(8, native_records, suffix='')) - - def read_metadata(self, reader): - logger.debug("Reading room data for %d" % self.vnum) - while True: - letter = reader.read_letter() - if letter == 'S': - break + + def read_metadata(self, reader): + logger.debug("Reading room data for %d" % self.vnum) + while True: + letter = reader.read_letter() + if letter == 'S': + break if letter == 'D': self.exits.append(MercExit.read(reader=reader)) - elif letter == 'E': - self.extra_descriptions.append(reader.read_object(ExtraDescription)) - else: - reader.parse_fail("room %d has flag %s not DES" % (self.vnum, letter)) - -@attributes -class RomShop(object): - keeper = field(default=0, type=int, native=NativeField(1, native_number, suffix=' ')) - buy_type = field(default=Factory(list), type=list, native=NativeField(2, native_trade_types, suffix=' ')) - profit_buy = field(default=0, type=int, native=NativeField(3, native_number, suffix=' ')) - profit_sell = field(default=0, type=int, native=NativeField(4, native_number, suffix=' ')) - open_hour = field(default=0, type=int, native=NativeField(5, native_number, suffix=' ')) - close_hour = field(default=0, type=int, native=NativeField(6, native_number, suffix='')) - comment = field(default='', type=str, native=NativeField(7, native_comment)) - + elif letter == 'E': + self.extra_descriptions.append(reader.read_object(ExtraDescription)) + else: + reader.parse_fail("room %d has flag %s not DES" % (self.vnum, letter)) + +@attributes +class RomShop(object): + keeper = field(default=0, type=int, native=NativeField(1, native_number, suffix=' ')) + buy_type = field(default=Factory(list), type=list, native=NativeField(2, native_trade_types, suffix=' ')) + profit_buy = field(default=0, type=int, native=NativeField(3, native_number, suffix=' ')) + profit_sell = field(default=0, type=int, native=NativeField(4, native_number, suffix=' ')) + open_hour = field(default=0, type=int, native=NativeField(5, native_number, suffix=' ')) + close_hour = field(default=0, type=int, native=NativeField(6, native_number, suffix='')) + comment = field(default='', type=str, native=NativeField(7, native_comment)) + @attributes class SmaugMob(RomMob): vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) @@ -1284,54 +1284,54 @@ class SmaugMob(RomMob): material = attr(default='', type=str) mprogs = attr(default=Factory(list), type=Optional[List[RomMobprog]]) programs = field(default=Factory(list), type=List[SmaugProgram], native=NativeField(17, native_smaug_programs, suffix='')) - - @classmethod - def read(cls, reader, vnum): - logger.debug("Reading SMAUG mob %d", vnum) - name = reader.read_string() - short_desc = reader.read_string() - long_desc = reader.read_string() - description = reader.read_string() - act = reader.read_flag() | SMAUG_ACT_TYPES.IS_NPC - affected_by = reader.read_flag() - alignment = reader.read_number() - letter = reader.read_letter() - level = reader.read_number() - hitroll = reader.read_number() - ac = reader.read_number() - hit = Dice.read(reader=reader) - damage = Dice.read(reader=reader) - if letter not in ('S', 'C', 'V'): - reader.parse_fail("Reading SMAUG MOB vnum %d unexpected type %s" % (vnum, letter)) + + @classmethod + def read(cls, reader, vnum): + logger.debug("Reading SMAUG mob %d", vnum) + name = reader.read_string() + short_desc = reader.read_string() + long_desc = reader.read_string() + description = reader.read_string() + act = reader.read_flag() | SMAUG_ACT_TYPES.IS_NPC + affected_by = reader.read_flag() + alignment = reader.read_number() + letter = reader.read_letter() + level = reader.read_number() + hitroll = reader.read_number() + ac = reader.read_number() + hit = Dice.read(reader=reader) + damage = Dice.read(reader=reader) + if letter not in ('S', 'C', 'V'): + reader.parse_fail("Reading SMAUG MOB vnum %d unexpected type %s" % (vnum, letter)) numeric_lines = [] - while True: - reader.skip_whitespace() - if reader.current_char == '#' or reader.current_char == '>': - break - numeric_lines.append(reader.read_number_line()) - complex_line_count = 0 - if letter in ('C', 'V'): - complex_line_count = 4 - if letter == 'V': - complex_line_count = 5 - position_index = len(numeric_lines) - complex_line_count - 1 - if position_index < 0: - reader.parse_fail("SMAUG MOB vnum %d missing position line" % vnum) - position_line = numeric_lines[position_index] - if len(position_line) < 3: - reader.parse_fail("SMAUG MOB vnum %d malformed position line" % vnum) - money_values = [] - for line in numeric_lines[:position_index]: - money_values.extend(line) - wealth = money_values[0] if money_values else 0 - start_pos = position_line[0] - default_pos = position_line[1] - sex = position_line[2] + while True: + reader.skip_whitespace() + if reader.current_char == '#' or reader.current_char == '>': + break + numeric_lines.append(reader.read_number_line()) + complex_line_count = 0 + if letter in ('C', 'V'): + complex_line_count = 4 + if letter == 'V': + complex_line_count = 5 + position_index = len(numeric_lines) - complex_line_count - 1 + if position_index < 0: + reader.parse_fail("SMAUG MOB vnum %d missing position line" % vnum) + position_line = numeric_lines[position_index] + if len(position_line) < 3: + reader.parse_fail("SMAUG MOB vnum %d malformed position line" % vnum) + money_values = [] + for line in numeric_lines[:position_index]: + money_values.extend(line) + wealth = money_values[0] if money_values else 0 + start_pos = position_line[0] + default_pos = position_line[1] + sex = position_line[2] economy_lines = numeric_lines[:position_index] complex_lines = numeric_lines[position_index + 1:] programs = reader.read_smaug_programs() if reader.current_char == '>' else [] return cls(vnum=vnum, name=name, short_desc=short_desc, long_desc=long_desc, description=description, act=act, affected_by=affected_by, alignment=alignment, native_type=letter, level=level, hitroll=hitroll, ac=ac, hit=hit, damage=damage, economy_lines=economy_lines, position_line=position_line, complex_lines=complex_lines, wealth=wealth, start_pos=start_pos, default_pos=default_pos, sex=sex, programs=programs) - + @attributes class SmaugItem(Item): vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) @@ -1353,23 +1353,23 @@ class SmaugItem(Item): affected = field(default=Factory(list), native=NativeField(14, native_records, suffix='')) extra_descriptions = field(default=Factory(list), type=List[ExtraDescription], native=NativeField(15, native_records, suffix='')) programs = field(default=Factory(list), type=List[SmaugProgram], native=NativeField(16, native_smaug_programs, suffix='')) - - @classmethod - def read(cls, reader, vnum): - logger.debug("Reading SMAUG object %d", vnum) - name = reader.read_string() - short_desc = reader.read_string() - description = reader.read_string() + + @classmethod + def read(cls, reader, vnum): + logger.debug("Reading SMAUG object %d", vnum) + name = reader.read_string() + short_desc = reader.read_string() + description = reader.read_string() action_description = reader.read_string() - item_type = reader.read_number() - extra_flags = reader.read_flag() - wear_flags = reader.read_flag() - header = [int(value) for value in reader.read_to_eol().split()] - layers = header[0] if len(header) > 0 else 0 - level = header[1] if len(header) > 1 else 0 - reader.skip_whitespace() - value = [int(value) for value in reader.read_to_eol().split()] - reader.skip_whitespace() + item_type = reader.read_number() + extra_flags = reader.read_flag() + wear_flags = reader.read_flag() + header = [int(value) for value in reader.read_to_eol().split()] + layers = header[0] if len(header) > 0 else 0 + level = header[1] if len(header) > 1 else 0 + reader.skip_whitespace() + value = [int(value) for value in reader.read_to_eol().split()] + reader.skip_whitespace() cost_line = [int(value) for value in reader.read_to_eol().split()] weight = cost_line[0] if len(cost_line) > 0 else 0 cost = cost_line[1] if len(cost_line) > 1 else 0 @@ -1381,23 +1381,23 @@ def read(cls, reader, vnum): affected = [] extra_descriptions = [] programs = [] - while True: - letter = reader.read_letter() - if letter == 'A': - aff = MercAffectData() - affected.append(aff) - aff.type = -1 - aff.duration = -1 - aff.location = reader.read_number() - aff.modifier = reader.read_number() - elif letter == 'E': - extra_descriptions.append(reader.read_object(ExtraDescription)) + while True: + letter = reader.read_letter() + if letter == 'A': + aff = MercAffectData() + affected.append(aff) + aff.type = -1 + aff.duration = -1 + aff.location = reader.read_number() + aff.modifier = reader.read_number() + elif letter == 'E': + extra_descriptions.append(reader.read_object(ExtraDescription)) elif letter == '>': reader.index -= 1 programs = reader.read_smaug_programs() - else: - reader.index -= 1 - break + else: + reader.index -= 1 + break return cls(vnum=vnum, name=name, short_desc=short_desc, description=description, action_description=action_description, item_type=item_type, extra_flags=extra_flags, wear_flags=wear_flags, header_tail=header, value=value, level=level, weight=weight, cost=cost, cost_tail=cost_tail, spell_words=spell_words, affected=affected, extra_descriptions=extra_descriptions, layers=layers, programs=programs) @attributes @@ -1474,55 +1474,55 @@ class SmaugRoom(Room): maps = field(default=Factory(list), type=List[SmaugMap], native=NativeField(12, native_records, suffix='')) programs = field(default=Factory(list), type=List[SmaugProgram], native=NativeField(13, native_smaug_programs, suffix='')) light = attr(default=0) - - @classmethod - def read(cls, reader, vnum): - logger.debug("Reading SMAUG room with vnum %d", vnum) - name = reader.read_string() - description = reader.read_string() - area_number = reader.read_number() - room_flags = reader.read_flag() - line = reader.read_to_eol() - values = [int(value) for value in line.split()] - while len(values) < 5: - values.append(0) - room = cls(vnum=vnum, name=name, description=description, area_number=area_number, room_flags=room_flags, sector_type=values[0], tele_delay=values[1], tele_vnum=values[2], tunnel=values[3], max_weight=values[4]) - room.read_metadata(reader) - return room - - def read_metadata(self, reader): - while True: - letter = reader.read_letter() - if letter == 'S': - break - if letter == 'D': - self.exits.append(self.read_exit(reader)) - elif letter == 'E': - self.extra_descriptions.append(reader.read_object(ExtraDescription)) + + @classmethod + def read(cls, reader, vnum): + logger.debug("Reading SMAUG room with vnum %d", vnum) + name = reader.read_string() + description = reader.read_string() + area_number = reader.read_number() + room_flags = reader.read_flag() + line = reader.read_to_eol() + values = [int(value) for value in line.split()] + while len(values) < 5: + values.append(0) + room = cls(vnum=vnum, name=name, description=description, area_number=area_number, room_flags=room_flags, sector_type=values[0], tele_delay=values[1], tele_vnum=values[2], tunnel=values[3], max_weight=values[4]) + room.read_metadata(reader) + return room + + def read_metadata(self, reader): + while True: + letter = reader.read_letter() + if letter == 'S': + break + if letter == 'D': + self.exits.append(self.read_exit(reader)) + elif letter == 'E': + self.extra_descriptions.append(reader.read_object(ExtraDescription)) elif letter == 'M': self.maps.append(SmaugMap(vnum=reader.read_number(), x=reader.read_number(), y=reader.read_number(), entry=reader.read_letter())) elif letter == '>': reader.index -= 1 self.programs = reader.read_smaug_programs() - else: - reader.parse_fail("SMAUG room %d has unknown flag %s" % (self.vnum, letter)) - - def read_exit(self, reader): - door = reader.read_number() - description = reader.read_string() - keyword = reader.read_string() - reader.skip_whitespace() - line = reader.read_to_eol() - values = [int(value) for value in line.split()] - while len(values) < 6: - values.append(0) - locks, key, destination, distance, pulltype, pull = values[:6] - if locks == 1: - exit_info = EXIT_FLAGS.ISDOOR - elif locks == 2: - exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.PICKPROOF - else: - exit_info = locks + else: + reader.parse_fail("SMAUG room %d has unknown flag %s" % (self.vnum, letter)) + + def read_exit(self, reader): + door = reader.read_number() + description = reader.read_string() + keyword = reader.read_string() + reader.skip_whitespace() + line = reader.read_to_eol() + values = [int(value) for value in line.split()] + while len(values) < 6: + values.append(0) + locks, key, destination, distance, pulltype, pull = values[:6] + if locks == 1: + exit_info = EXIT_FLAGS.ISDOOR + elif locks == 2: + exit_info = EXIT_FLAGS.ISDOOR | EXIT_FLAGS.PICKPROOF + else: + exit_info = locks return SmaugExit(door=door, description=description, keyword=keyword, locks=locks, exit_info=exit_info, key=key, destination=destination, distance=distance, pulltype=pulltype, pull=pull) @attributes @@ -1745,20 +1745,20 @@ class MercReset(object): arg4 = attr(default=None) arg5 = attr(default=None) comment = field(default=None, native=NativeField(6, native_comment)) - - @classmethod - def read(cls, reader, letter): - command = letter - if_flag = reader.read_number() - arg1 = reader.read_number() - arg2 = reader.read_number() - if letter == 'G' or letter == 'R': - arg3 = 0 - else: - arg3 = reader.read_number() - comment = reader.read_to_eol() - return cls(command=command, if_flag=if_flag, arg1=arg1, arg2=arg2, arg3=arg3, comment=comment) - + + @classmethod + def read(cls, reader, letter): + command = letter + if_flag = reader.read_number() + arg1 = reader.read_number() + arg2 = reader.read_number() + if letter == 'G' or letter == 'R': + arg3 = 0 + else: + arg3 = reader.read_number() + comment = reader.read_to_eol() + return cls(command=command, if_flag=if_flag, arg1=arg1, arg2=arg2, arg3=arg3, comment=comment) + @attributes class MercMob(RomMob): vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) @@ -1798,32 +1798,32 @@ class MercMob(RomMob): parts = attr(default=0, type=PARTS, converter=PARTS) size = attr(default=None, type=Word) mprogs = field(default=Factory(list), type=Optional[List[RomMobprog]]) - - @classmethod - def read(cls, reader, vnum): - logger.debug("Reading Mob %d", vnum) - name = reader.read_string() - short_desc = reader.read_string() - long_desc = reader.read_string() - description = reader.read_string() - act = reader.read_flag() - affected_by = reader.read_flag() - alignment = reader.read_flag() - letter = reader.read_letter() - level = reader.read_number() - hitroll = reader.read_number() - ac = reader.read_number() + + @classmethod + def read(cls, reader, vnum): + logger.debug("Reading Mob %d", vnum) + name = reader.read_string() + short_desc = reader.read_string() + long_desc = reader.read_string() + description = reader.read_string() + act = reader.read_flag() + affected_by = reader.read_flag() + alignment = reader.read_flag() + letter = reader.read_letter() + level = reader.read_number() + hitroll = reader.read_number() + ac = reader.read_number() hit = Dice.read(reader=reader) damage = Dice.read(reader=reader) wealth = reader.read_number() xp = reader.read_number() - default_pos = reader.read_number() # position - start_pos = reader.read_number() # start pos - sex = reader.read_number() - if letter != 'S': - reader.parse_fail("Reading MOB vnum %d non S: %s" % (vnum, letter)) + default_pos = reader.read_number() # position + start_pos = reader.read_number() # start pos + sex = reader.read_number() + if letter != 'S': + reader.parse_fail("Reading MOB vnum %d non S: %s" % (vnum, letter)) return cls(vnum=vnum, name=name, short_desc=short_desc, long_desc=long_desc, description=description, act=act, affected_by=affected_by, alignment=alignment, level=level, hitroll=hitroll, ac=ac, hit=hit, damage=damage, wealth=wealth, xp=xp, start_pos=start_pos, default_pos=default_pos, sex=sex) - + @attributes class MercItem(Item): vnum = field(default=0, type=VNum, read=False, native=NativeField(0, native_number, prefix='#')) @@ -1843,37 +1843,37 @@ class MercItem(Item): extra_descriptions = field(default=Factory(list), type=List[ExtraDescription], native=NativeField(13, native_records, suffix='')) @classmethod - def read(cls, reader, vnum): - logger.debug("Reading object %d" % vnum) + def read(cls, reader, vnum): + logger.debug("Reading object %d" % vnum) name = reader.read_string() short_desc = reader.read_string() description = reader.read_string() action_description = reader.read_string() - item_type = reader.read_number() - extra_flags = reader.read_flag() - wear_flags = reader.read_flag() - value = [reader.read_number(), reader.read_number(), reader.read_number(), reader.read_number()] + item_type = reader.read_number() + extra_flags = reader.read_flag() + wear_flags = reader.read_flag() + value = [reader.read_number(), reader.read_number(), reader.read_number(), reader.read_number()] weight = reader.read_number() cost = reader.read_number() cost_per_day = reader.read_number() - affected = [] - extra_descriptions = [] - while True: - letter = reader.read_letter() - if letter == 'A': - aff = MercAffectData() - affected.append(aff) - aff.type = -1 - aff.duration = -1 - aff.location = reader.read_number() - aff.modifier = reader.read_number() - elif letter == 'E': - extra_descriptions.append(reader.read_object(ExtraDescription)) - else: - reader.index -= 1 - break + affected = [] + extra_descriptions = [] + while True: + letter = reader.read_letter() + if letter == 'A': + aff = MercAffectData() + affected.append(aff) + aff.type = -1 + aff.duration = -1 + aff.location = reader.read_number() + aff.modifier = reader.read_number() + elif letter == 'E': + extra_descriptions.append(reader.read_object(ExtraDescription)) + else: + reader.index -= 1 + break return cls(vnum=vnum, name=name, short_desc=short_desc, description=description, action_description=action_description, item_type=item_type, extra_flags=extra_flags, wear_flags=wear_flags, value=value, weight=weight, cost=cost, cost_per_day=cost_per_day, affected=affected, extra_descriptions=extra_descriptions) - + class MercAreaFile(AreaFile): area_type = MercArea @@ -1885,92 +1885,92 @@ def write(self, path): area_file.write(self.dumps()) def load_mobiles(self): - for mob in self.load_vnum_section(MercMob): - setitem(self.area.mobs, mob.vnum, mob) - - def load_objects(self): - for item in self.load_vnum_section(MercItem): - setitem(self.area.objects, item.vnum, item) - - def load_rooms(self): - for room in self.load_vnum_section(MercRoom): - setitem(self.area.rooms, room.vnum, room) - - def load_resets(self): - for reset in self.read_flat_section(MercReset): - self.area.resets.append(reset) - - def read_area_metadata(self): - self.area.metadata = self.read_string() - -class SmaugAreaFile(RomAreaFile): - area_type = SmaugArea - MAX_FIX = 3 - - def load_sections(self): - readers = { - 'area': self.read_area_metadata, - 'author': self.load_author, - 'credits': self.load_credits, - 'flags': self.load_flags, - 'ranges': self.load_ranges, - 'version': self.load_version, - 'resetmsg': self.load_resetmsg, - 'economy': self.load_economy, - 'helps': self.load_helps, - 'mobiles': self.load_mobiles, - 'objects': self.load_objects, - 'rooms': self.load_rooms, - 'resets': self.load_resets, - 'shops': self.load_shops, - 'specials': self.load_specials, + for mob in self.load_vnum_section(MercMob): + setitem(self.area.mobs, mob.vnum, mob) + + def load_objects(self): + for item in self.load_vnum_section(MercItem): + setitem(self.area.objects, item.vnum, item) + + def load_rooms(self): + for room in self.load_vnum_section(MercRoom): + setitem(self.area.rooms, room.vnum, room) + + def load_resets(self): + for reset in self.read_flat_section(MercReset): + self.area.resets.append(reset) + + def read_area_metadata(self): + self.area.metadata = self.read_string() + +class SmaugAreaFile(RomAreaFile): + area_type = SmaugArea + MAX_FIX = 3 + + def load_sections(self): + readers = { + 'area': self.read_area_metadata, + 'author': self.load_author, + 'credits': self.load_credits, + 'flags': self.load_flags, + 'ranges': self.load_ranges, + 'version': self.load_version, + 'resetmsg': self.load_resetmsg, + 'economy': self.load_economy, + 'helps': self.load_helps, + 'mobiles': self.load_mobiles, + 'objects': self.load_objects, + 'rooms': self.load_rooms, + 'resets': self.load_resets, + 'shops': self.load_shops, + 'specials': self.load_specials, 'repairs': self.load_repairs, 'continent': self.load_continent, 'climate': self.load_climate, 'spelllimit': self.load_spelllimit, - } - while True: - self.skip_whitespace() - if self.index >= len(self.data): - return - section_name = self.read_section_name() - self.current_section_name = section_name - if section_name == '$': - return - reader = readers.get(section_name) - if reader is None: - self.skip_section(section_name) - else: - logger.info("Processing section %s" % section_name) - try: - reader() - except Exception: - self.parse_fail("Error reading section %r" % section_name) - - def read_area_metadata(self): - self.area.name = self.read_string() - - def load_author(self): - self.area.author = self.read_string() - - def load_credits(self): - self.area.credits = self.read_string() - + } + while True: + self.skip_whitespace() + if self.index >= len(self.data): + return + section_name = self.read_section_name() + self.current_section_name = section_name + if section_name == '$': + return + reader = readers.get(section_name) + if reader is None: + self.skip_section(section_name) + else: + logger.info("Processing section %s" % section_name) + try: + reader() + except Exception: + self.parse_fail("Error reading section %r" % section_name) + + def read_area_metadata(self): + self.area.name = self.read_string() + + def load_author(self): + self.area.author = self.read_string() + + def load_credits(self): + self.area.credits = self.read_string() + def load_flags(self): self.area.flags = self.read_number() line = self.read_to_eol().strip() self.area.reset_frequency = int(line.split()[0]) if line else 0 - - def load_ranges(self): - self.area.low_soft_range = self.read_number() - self.area.high_soft_range = self.read_number() - self.area.low_hard_range = self.read_number() - self.area.high_hard_range = self.read_number() - self.read_word() # $ - - def load_version(self): - self.area.version = self.read_number() - + + def load_ranges(self): + self.area.low_soft_range = self.read_number() + self.area.high_soft_range = self.read_number() + self.area.low_hard_range = self.read_number() + self.area.high_hard_range = self.read_number() + self.read_word() # $ + + def load_version(self): + self.area.version = self.read_number() + def load_continent(self): self.area.continent = self.read_string() @@ -1979,38 +1979,38 @@ def load_climate(self): def load_spelllimit(self): self.area.spelllimit = self.read_number() - - def load_mobiles(self): - for mob in self.load_smaug_vnum_section(SmaugMob): - setitem(self.area.mobs, mob.vnum, mob) - - def load_objects(self): - for item in self.load_smaug_vnum_section(SmaugItem): - setitem(self.area.objects, item.vnum, item) - - def load_rooms(self): - for room in self.load_smaug_vnum_section(SmaugRoom): - setitem(self.area.rooms, room.vnum, room) - - def load_smaug_vnum_section(self, section_object_type): - while True: - self.skip_whitespace() - if self.index >= len(self.data): - break - if self.current_char != '#': - self.parse_fail("Expected # got %s" % self.current_char) - next_char = self.data[self.index + 1] - if not (next_char.isdigit() or next_char == '-'): - break - vnum = self.read_vnum() - if vnum == 0: - break - yield self.read_object(section_object_type, vnum=vnum) - - def load_resets(self): - for reset in self.read_flat_section(MercReset): - self.area.resets.append(reset) - + + def load_mobiles(self): + for mob in self.load_smaug_vnum_section(SmaugMob): + setitem(self.area.mobs, mob.vnum, mob) + + def load_objects(self): + for item in self.load_smaug_vnum_section(SmaugItem): + setitem(self.area.objects, item.vnum, item) + + def load_rooms(self): + for room in self.load_smaug_vnum_section(SmaugRoom): + setitem(self.area.rooms, room.vnum, room) + + def load_smaug_vnum_section(self, section_object_type): + while True: + self.skip_whitespace() + if self.index >= len(self.data): + break + if self.current_char != '#': + self.parse_fail("Expected # got %s" % self.current_char) + next_char = self.data[self.index + 1] + if not (next_char.isdigit() or next_char == '-'): + break + vnum = self.read_vnum() + if vnum == 0: + break + yield self.read_object(section_object_type, vnum=vnum) + + def load_resets(self): + for reset in self.read_flat_section(MercReset): + self.area.resets.append(reset) + def load_repairs(self): while True: keeper = self.read_number() @@ -2023,26 +2023,26 @@ def load_repairs(self): close_hour = self.read_number() comment = self.read_to_eol() self.area.repairs.append(SmaugRepair(keeper=keeper, fix_type=fix_type, profit_fix=profit_fix, shop_type=shop_type, open_hour=open_hour, close_hour=close_hour, comment=comment)) - - def load_resetmsg(self): - self.area.resetmsg = self.read_string() - - def load_economy(self): - self.area.high_economy = self.read_number() - self.area.low_economy = self.read_number() - - def load_room(self, vnum): - logger.debug("Reading room %d" % vnum) - room = Room(vnum=vnum) - room.name = self.read_string() - room.description = self.read_string() - room.area_number = self.read_number() - room.room_flags = self.read_flag() - line = self.read_line() - #room.sector_type, room.tele_delay, room.tele_vnum, room.tunnel, room.max_weight = map(int, line.split()) - self.read_room_data(room) - return room - + + def load_resetmsg(self): + self.area.resetmsg = self.read_string() + + def load_economy(self): + self.area.high_economy = self.read_number() + self.area.low_economy = self.read_number() + + def load_room(self, vnum): + logger.debug("Reading room %d" % vnum) + room = Room(vnum=vnum) + room.name = self.read_string() + room.description = self.read_string() + room.area_number = self.read_number() + room.room_flags = self.read_flag() + line = self.read_line() + #room.sector_type, room.tele_delay, room.tele_vnum, room.tunnel, room.max_weight = map(int, line.split()) + self.read_room_data(room) + return room + def read_line(self): return self.read_to_eol() @@ -3927,18 +3927,18 @@ def _unstructure_enum(self, obj): if name is None: name = str(obj.value) return obj.__class__.__name__ + "." + name - - -def print_area(area_file_path, area_type=RomAreaFile): - area_file = area_type(area_file_path) - area_file.load_sections() - print(area_file.as_json()) - -def main(): - if len(sys.argv) < 2: - print("Must supply an area") - sys.exit(1) - print_area(sys.argv[1]) - -if __name__ == '__main__': - main() + + +def print_area(area_file_path, area_type=RomAreaFile): + area_file = area_type(area_file_path) + area_file.load_sections() + print(area_file.as_json()) + +def main(): + if len(sys.argv) < 2: + print("Must supply an area") + sys.exit(1) + print_area(sys.argv[1]) + +if __name__ == '__main__': + main() diff --git a/area_reader/constants.py b/area_reader/constants.py index 70f9978..661bdee 100644 --- a/area_reader/constants.py +++ b/area_reader/constants.py @@ -1,366 +1,366 @@ -import enum -from attr import attr, attributes - -def flag_convert(letter): - if len(letter) > 1: - raise ValueError("Unable to convert flag " + letter) - bitsum = 0 - start = None - if letter >= 'A' and letter <= 'Z': - bitsum = 1 - start = 'A' - elif letter >= 'a' and letter <= 'z': - bitsum = 67108864 - start = 'a' - l2 = ord(letter) - while l2 > ord(start): - bitsum *= 2 - l2 -= 1 - return bitsum - -def remove_bit(var, bit): - return var & ~bit - - -BITFLAGS = dict( - A = 1, - B = 2, - C = 4, - D = 8, - E = 16, - F = 32, - G = 64, - H = 128, - I = 256, - J = 512, - K = 1024, - L = 2048, - M = 4096, - N = 8192, - O = 16384, - P = 32768, - Q = 65536, - R = 131072, - S = 262144, - T = 524288, - U = 1048576, - V = 2097152, - W = 4194304, - X = 8388608, - Y = 16777216, - Z = 33554432, - AA = 67108864, - BB = 134217728, - CC = 268435456, - DD = 536870912, - EE = 1073741824, -) - -class MERC_ACT_TYPES(enum.IntFlag): - IS_NPC= BITFLAGS['A'] # Auto set for mobs - SENTINEL_= BITFLAGS['B'] # Stays in one room - SCAVENGER_= BITFLAGS['C'] # Picks up objects - UNUSED1 = BITFLAGS['D'] - UNUSED2 = BITFLAGS['E'] - AGGRESSIVE_= BITFLAGS['F'] # Attacks PC s - STAY_AREA= BITFLAGS['G'] # Won't leave area - WIMPY = BITFLAGS['H'] - PET = BITFLAGS['I'] # Auto set for pets - TRAIN = BITFLAGS['J'] # Can train PC s - _PRACTICE = BITFLAGS['K'] # Can practice PC s - UNUSED3 = BITFLAGS['L'] - UNUSED4 = BITFLAGS['M'] - - -class ROM_ACT_TYPES(enum.IntFlag): - IS_NPC = BITFLAGS['A'] # Auto set for mobs - SENTINEL = BITFLAGS['B'] # Stays in one room - SCAVENGER = BITFLAGS['C'] # Picks up objects - UNUSED1 = BITFLAGS['D'] - UNUSED2 = BITFLAGS['E'] - AGGRESSIVE = BITFLAGS['F'] # Attacks PC s - STAY_AREA = BITFLAGS['G'] # Won't leave area - WIMPY = BITFLAGS['H'] # - PET = BITFLAGS['I'] # Auto set for pets - TRAIN = BITFLAGS['J'] # Can train PC s - _PRACTICE = BITFLAGS['K'] # Can practice PC s - UNUSED3 = BITFLAGS['L'] - UNUSED4 = BITFLAGS['M'] - UNUSED5 = BITFLAGS['N'] - _UNDEAD = BITFLAGS['O'] # - UNUSED6 = BITFLAGS['P'] - _CLERIC = BITFLAGS['Q'] # - MAGE = BITFLAGS['R'] # - THIEF = BITFLAGS['S'] # - WARRIOR = BITFLAGS['T'] # - NOALIGN = BITFLAGS['U'] # - NOPURGE = BITFLAGS['V'] # - OUTDOORS = BITFLAGS['W'] # - UNUSED7 = BITFLAGS['X'] - INDOORS = BITFLAGS['Y'] # - UNUSED8 = BITFLAGS['Z'] - IS_HEALER = BITFLAGS['AA'] # - GAIN = BITFLAGS['BB'] # - UPDATE_ALWAYS = BITFLAGS['CC'] # - IS_CHANGER = BITFLAGS['DD'] # - UNUSED9 = BITFLAGS['EE'] - -class AFFECTED_BY(enum.IntFlag): - NONE = 0 - BLIND = BITFLAGS['A'] - INVISIBLE = BITFLAGS['B'] - EVIL = BITFLAGS['C'] - INVIS = BITFLAGS['D'] - MAGIC = BITFLAGS['E'] - HIDDEN = BITFLAGS['F'] - GOOD = BITFLAGS['G'] - SANCTUARY = BITFLAGS['H'] - FIRE = BITFLAGS['I'] - INFRARED = BITFLAGS['J'] - CURSE = BITFLAGS['K'] - UNUSED_FLAG = BITFLAGS['L'] # Unused - POISON = BITFLAGS['M'] - PROTECT_EVIL = BITFLAGS['N'] - PROTECT_GOOD = BITFLAGS['O'] - SNEAK = BITFLAGS['P'] - HIDE = BITFLAGS['Q'] - SLEEP = BITFLAGS['R'] - CHARM = BITFLAGS['S'] - FLYING = BITFLAGS['T'] - PASS_DOOR = BITFLAGS['U'] - HASTE = BITFLAGS['V'] - CALM = BITFLAGS['W'] - PLAGUE = BITFLAGS['X'] - WEAKEN = BITFLAGS['Y'] - DARK_VISION = BITFLAGS['Z'] - BERSERK = BITFLAGS['AA'] - SWIM = BITFLAGS['BB'] - REGENERATION = BITFLAGS['CC'] - SLOW = BITFLAGS['DD'] - -class WEAR_FLAGS(enum.IntFlag): - NONE = 0 - TAKE = BITFLAGS['A'] - FINGER = BITFLAGS['B'] - NECK = BITFLAGS['C'] - BODY = BITFLAGS['D'] - HEAD = BITFLAGS['E'] - LEGS = BITFLAGS['F'] - FEET = BITFLAGS['G'] - HANDS = BITFLAGS['H'] - ARMS = BITFLAGS['I'] - SHIELD = BITFLAGS['J'] - ABOUT = BITFLAGS['K'] - WAIST = BITFLAGS['L'] - WRIST = BITFLAGS['M'] - WIELD = BITFLAGS['N'] - HOLD = BITFLAGS['O'] - SAC = BITFLAGS['P'] - FLOAT = BITFLAGS['Q'] - -class OFFENSE(enum.IntFlag): - NONE = 0 - AREA_ATTACK = BITFLAGS['A'] - BACKSTAB = BITFLAGS['B'] - BASH = BITFLAGS['C'] - BERSERK = BITFLAGS['D'] - DISARM = BITFLAGS['E'] - DODGE = BITFLAGS['F'] - FADE = BITFLAGS['G'] - FAST = BITFLAGS['H'] - KICK = BITFLAGS['I'] - KICK_DIRT = BITFLAGS['J'] - PARRY = BITFLAGS['K'] - RESCUE = BITFLAGS['L'] - TAIL_ = BITFLAGS['M'] - TRIP_ = BITFLAGS['N'] - CRUSH_ = BITFLAGS['O'] - ASSIST_ALL = BITFLAGS['P'] - ASSIST_ALIGN = BITFLAGS['Q'] - ASSIST_RACE = BITFLAGS['R'] - ASSIST_PLAYERS = BITFLAGS['S'] - ASSIST_GUARD = BITFLAGS['T'] - ASSIST_VNUM = BITFLAGS['U'] - -class IMM_FLAGS(enum.IntFlag): - NONE = 0 - SUMMON = BITFLAGS['A'] - CHARM = BITFLAGS['B'] - MAGIC = BITFLAGS['C'] - WEAPON = BITFLAGS['D'] - BASH = BITFLAGS['E'] - PIERCE = BITFLAGS['F'] - SLASH = BITFLAGS['G'] - FIRE = BITFLAGS['H'] - COLD = BITFLAGS['I'] - LIGHTNING = BITFLAGS['J'] - ACID = BITFLAGS['K'] - POISON = BITFLAGS['L'] - NEGATIVE = BITFLAGS['M'] - HOLY = BITFLAGS['N'] - ENERGY = BITFLAGS['O'] - MENTAL = BITFLAGS['P'] - DISEASE = BITFLAGS['Q'] - DROWNING = BITFLAGS['R'] - LIGHT = BITFLAGS['S'] - SOUND = BITFLAGS['T'] - WOOD = BITFLAGS['X'] - SILVER = BITFLAGS['Y'] - IRON = BITFLAGS['Z'] - -class FORMS(enum.IntFlag): - NONE = 0 - EDIBLE = BITFLAGS['A'] - POISON = BITFLAGS['B'] - MAGICAL = BITFLAGS['C'] - INSTANT_DECAY = BITFLAGS['D'] - OTHER = BITFLAGS['E'] # defined by material bit - UNUSED1 = enum.auto() - # actual form - ANIMAL = BITFLAGS['G'] - SENTIENT = BITFLAGS['H'] - UNDEAD = BITFLAGS['I'] - CONSTRUCT = BITFLAGS['J'] - MIST = BITFLAGS['K'] - INTANGIBLE = BITFLAGS['L'] - BIPED = BITFLAGS['M'] - CENTAUR = BITFLAGS['N'] - INSECT = BITFLAGS['O'] - SPIDER = BITFLAGS['P'] - CRUSTACEAN = BITFLAGS['Q'] - WORM = BITFLAGS['R'] - BLOB = BITFLAGS['S'] - UNUSED2 = enum.auto() - UNUSED3 = enum.auto() - MAMMAL = BITFLAGS['V'] - BIRD = BITFLAGS['W'] - REPTILE = BITFLAGS['X'] - SNAKE = BITFLAGS['Y'] - DRAGON = BITFLAGS['Z'] - AMPHIBIAN = BITFLAGS['AA'] - FISH = BITFLAGS['BB'] - COLD_BLOOD = BITFLAGS['CC'] - UNUSED4 = BITFLAGS['DD'] - UNUSED5 = BITFLAGS['EE'] - -class PARTS(enum.IntFlag): - NONE = 0 - HEAD = BITFLAGS['A'] - ARMS = BITFLAGS['B'] - LEGS = BITFLAGS['C'] - HEART = BITFLAGS['D'] - BRAINS = BITFLAGS['E'] - GUTS = BITFLAGS['F'] - HANDS = BITFLAGS['G'] - FEET = BITFLAGS['H'] - FINGERS = BITFLAGS['I'] - EAR = BITFLAGS['J'] - EYE = BITFLAGS['K'] - LONG_TONGUE = BITFLAGS['L'] - EYESTALKS = BITFLAGS['M'] - TENTACLES = BITFLAGS['N'] - FINS = BITFLAGS['O'] - WINGS = BITFLAGS['P'] - TAIL = BITFLAGS['Q'] - # for combat - CLAWS = BITFLAGS['U'] - FANGS = BITFLAGS['V'] - HORNS = BITFLAGS['W'] - SCALES = BITFLAGS['X'] - TUSKS = BITFLAGS['Y'] - -class WEAR_LOCATIONS(enum.Enum): - NONE = -1 - LIGHT = 0 - FINGER_L = 1 - FINGER_R = 2 - NECK_1 = 3 - NECK_2 = 4 - BODY = 5 - HEAD = 6 - LEGS = 7 - FEET = 8 - HANDS = 9 - ARMS = 10 - SHIELD = 11 - ABOUT = 12 - WAIST = 13 - WRIST_L = 14 - RIST_L = 14 # deprecated alias for WRIST_L (historical typo) - WRIST_R = 15 - WIELD = 16 - HOLD = 17 - FLOAT = 18 - MAX = 19 - -class ROM_ROOM_FLAGS(enum.IntFlag): - NONE = 0 - DARK = BITFLAGS['A'] - unused1 = enum.auto() - NO_MOB = BITFLAGS['C'] - INDOORS = BITFLAGS['D'] - unused2 = enum.auto() - UNUSED3 = enum.auto() - unused4 = enum.auto() - unused5 = enum.auto() - unused6 = enum.auto() - PRIVATE = BITFLAGS['J'] - SAFE = BITFLAGS['K'] - SOLITARY = BITFLAGS['L'] - PET_SHOP = BITFLAGS['M'] - NO_RECALL = BITFLAGS['N'] - IMP_ONLY = BITFLAGS['O'] - GODS_ONLY = BITFLAGS['P'] - HEROES_ONLY = BITFLAGS['Q'] - NEWBIES_ONLY = BITFLAGS['R'] - LAW = BITFLAGS['S'] - NOWHERE = BITFLAGS['T'] - -class MERC_ROOM_FLAGS(enum.IntFlag): - DARK = 1 - NO_MOB = 4 - INDOORS = 8 - PRIVATE = 512 - SAFE = 1024 - SOLITARY = 2048 - PETSHOP = 4096 - NO_RECALL = 8192 - -class EXIT_DIRECTIONS(enum.Enum): - NORTH = 0 - EAST = 1 - SOUTH = 2 - WEST = 3 - UP = 4 - DOWN = 5 - -class EXIT_FLAGS(enum.IntFlag): - NONE = 0 - ISDOOR = BITFLAGS['A'] - CLOSED = BITFLAGS['B'] - LOCKED = BITFLAGS['C'] - PICKPROOF = BITFLAGS['F'] - NOPASS = BITFLAGS['G'] - EASY = BITFLAGS['H'] - HARD = BITFLAGS['I'] - INFURIATING = BITFLAGS['J'] - NOCLOSE = BITFLAGS['K'] - NOLOCK = BITFLAGS['L'] - -class SECTOR_TYPES(enum.Enum): - INSIDE = 0 - CITY = 1 - FIELD = 2 - FOREST = 3 - HILLS = 4 - MOUNTAIN = 5 - WATER_SWIM = 6 - WATER_NOSWIM = 7 - UNUSED = 8 - AIR = 9 - DESERT = 10 - MAX = 11 - +import enum +from attr import attr, attributes + +def flag_convert(letter): + if len(letter) > 1: + raise ValueError("Unable to convert flag " + letter) + bitsum = 0 + start = None + if letter >= 'A' and letter <= 'Z': + bitsum = 1 + start = 'A' + elif letter >= 'a' and letter <= 'z': + bitsum = 67108864 + start = 'a' + l2 = ord(letter) + while l2 > ord(start): + bitsum *= 2 + l2 -= 1 + return bitsum + +def remove_bit(var, bit): + return var & ~bit + + +BITFLAGS = dict( + A = 1, + B = 2, + C = 4, + D = 8, + E = 16, + F = 32, + G = 64, + H = 128, + I = 256, + J = 512, + K = 1024, + L = 2048, + M = 4096, + N = 8192, + O = 16384, + P = 32768, + Q = 65536, + R = 131072, + S = 262144, + T = 524288, + U = 1048576, + V = 2097152, + W = 4194304, + X = 8388608, + Y = 16777216, + Z = 33554432, + AA = 67108864, + BB = 134217728, + CC = 268435456, + DD = 536870912, + EE = 1073741824, +) + +class MERC_ACT_TYPES(enum.IntFlag): + IS_NPC= BITFLAGS['A'] # Auto set for mobs + SENTINEL_= BITFLAGS['B'] # Stays in one room + SCAVENGER_= BITFLAGS['C'] # Picks up objects + UNUSED1 = BITFLAGS['D'] + UNUSED2 = BITFLAGS['E'] + AGGRESSIVE_= BITFLAGS['F'] # Attacks PC s + STAY_AREA= BITFLAGS['G'] # Won't leave area + WIMPY = BITFLAGS['H'] + PET = BITFLAGS['I'] # Auto set for pets + TRAIN = BITFLAGS['J'] # Can train PC s + _PRACTICE = BITFLAGS['K'] # Can practice PC s + UNUSED3 = BITFLAGS['L'] + UNUSED4 = BITFLAGS['M'] + + +class ROM_ACT_TYPES(enum.IntFlag): + IS_NPC = BITFLAGS['A'] # Auto set for mobs + SENTINEL = BITFLAGS['B'] # Stays in one room + SCAVENGER = BITFLAGS['C'] # Picks up objects + UNUSED1 = BITFLAGS['D'] + UNUSED2 = BITFLAGS['E'] + AGGRESSIVE = BITFLAGS['F'] # Attacks PC s + STAY_AREA = BITFLAGS['G'] # Won't leave area + WIMPY = BITFLAGS['H'] # + PET = BITFLAGS['I'] # Auto set for pets + TRAIN = BITFLAGS['J'] # Can train PC s + _PRACTICE = BITFLAGS['K'] # Can practice PC s + UNUSED3 = BITFLAGS['L'] + UNUSED4 = BITFLAGS['M'] + UNUSED5 = BITFLAGS['N'] + _UNDEAD = BITFLAGS['O'] # + UNUSED6 = BITFLAGS['P'] + _CLERIC = BITFLAGS['Q'] # + MAGE = BITFLAGS['R'] # + THIEF = BITFLAGS['S'] # + WARRIOR = BITFLAGS['T'] # + NOALIGN = BITFLAGS['U'] # + NOPURGE = BITFLAGS['V'] # + OUTDOORS = BITFLAGS['W'] # + UNUSED7 = BITFLAGS['X'] + INDOORS = BITFLAGS['Y'] # + UNUSED8 = BITFLAGS['Z'] + IS_HEALER = BITFLAGS['AA'] # + GAIN = BITFLAGS['BB'] # + UPDATE_ALWAYS = BITFLAGS['CC'] # + IS_CHANGER = BITFLAGS['DD'] # + UNUSED9 = BITFLAGS['EE'] + +class AFFECTED_BY(enum.IntFlag): + NONE = 0 + BLIND = BITFLAGS['A'] + INVISIBLE = BITFLAGS['B'] + EVIL = BITFLAGS['C'] + INVIS = BITFLAGS['D'] + MAGIC = BITFLAGS['E'] + HIDDEN = BITFLAGS['F'] + GOOD = BITFLAGS['G'] + SANCTUARY = BITFLAGS['H'] + FIRE = BITFLAGS['I'] + INFRARED = BITFLAGS['J'] + CURSE = BITFLAGS['K'] + UNUSED_FLAG = BITFLAGS['L'] # Unused + POISON = BITFLAGS['M'] + PROTECT_EVIL = BITFLAGS['N'] + PROTECT_GOOD = BITFLAGS['O'] + SNEAK = BITFLAGS['P'] + HIDE = BITFLAGS['Q'] + SLEEP = BITFLAGS['R'] + CHARM = BITFLAGS['S'] + FLYING = BITFLAGS['T'] + PASS_DOOR = BITFLAGS['U'] + HASTE = BITFLAGS['V'] + CALM = BITFLAGS['W'] + PLAGUE = BITFLAGS['X'] + WEAKEN = BITFLAGS['Y'] + DARK_VISION = BITFLAGS['Z'] + BERSERK = BITFLAGS['AA'] + SWIM = BITFLAGS['BB'] + REGENERATION = BITFLAGS['CC'] + SLOW = BITFLAGS['DD'] + +class WEAR_FLAGS(enum.IntFlag): + NONE = 0 + TAKE = BITFLAGS['A'] + FINGER = BITFLAGS['B'] + NECK = BITFLAGS['C'] + BODY = BITFLAGS['D'] + HEAD = BITFLAGS['E'] + LEGS = BITFLAGS['F'] + FEET = BITFLAGS['G'] + HANDS = BITFLAGS['H'] + ARMS = BITFLAGS['I'] + SHIELD = BITFLAGS['J'] + ABOUT = BITFLAGS['K'] + WAIST = BITFLAGS['L'] + WRIST = BITFLAGS['M'] + WIELD = BITFLAGS['N'] + HOLD = BITFLAGS['O'] + SAC = BITFLAGS['P'] + FLOAT = BITFLAGS['Q'] + +class OFFENSE(enum.IntFlag): + NONE = 0 + AREA_ATTACK = BITFLAGS['A'] + BACKSTAB = BITFLAGS['B'] + BASH = BITFLAGS['C'] + BERSERK = BITFLAGS['D'] + DISARM = BITFLAGS['E'] + DODGE = BITFLAGS['F'] + FADE = BITFLAGS['G'] + FAST = BITFLAGS['H'] + KICK = BITFLAGS['I'] + KICK_DIRT = BITFLAGS['J'] + PARRY = BITFLAGS['K'] + RESCUE = BITFLAGS['L'] + TAIL_ = BITFLAGS['M'] + TRIP_ = BITFLAGS['N'] + CRUSH_ = BITFLAGS['O'] + ASSIST_ALL = BITFLAGS['P'] + ASSIST_ALIGN = BITFLAGS['Q'] + ASSIST_RACE = BITFLAGS['R'] + ASSIST_PLAYERS = BITFLAGS['S'] + ASSIST_GUARD = BITFLAGS['T'] + ASSIST_VNUM = BITFLAGS['U'] + +class IMM_FLAGS(enum.IntFlag): + NONE = 0 + SUMMON = BITFLAGS['A'] + CHARM = BITFLAGS['B'] + MAGIC = BITFLAGS['C'] + WEAPON = BITFLAGS['D'] + BASH = BITFLAGS['E'] + PIERCE = BITFLAGS['F'] + SLASH = BITFLAGS['G'] + FIRE = BITFLAGS['H'] + COLD = BITFLAGS['I'] + LIGHTNING = BITFLAGS['J'] + ACID = BITFLAGS['K'] + POISON = BITFLAGS['L'] + NEGATIVE = BITFLAGS['M'] + HOLY = BITFLAGS['N'] + ENERGY = BITFLAGS['O'] + MENTAL = BITFLAGS['P'] + DISEASE = BITFLAGS['Q'] + DROWNING = BITFLAGS['R'] + LIGHT = BITFLAGS['S'] + SOUND = BITFLAGS['T'] + WOOD = BITFLAGS['X'] + SILVER = BITFLAGS['Y'] + IRON = BITFLAGS['Z'] + +class FORMS(enum.IntFlag): + NONE = 0 + EDIBLE = BITFLAGS['A'] + POISON = BITFLAGS['B'] + MAGICAL = BITFLAGS['C'] + INSTANT_DECAY = BITFLAGS['D'] + OTHER = BITFLAGS['E'] # defined by material bit + UNUSED1 = enum.auto() + # actual form + ANIMAL = BITFLAGS['G'] + SENTIENT = BITFLAGS['H'] + UNDEAD = BITFLAGS['I'] + CONSTRUCT = BITFLAGS['J'] + MIST = BITFLAGS['K'] + INTANGIBLE = BITFLAGS['L'] + BIPED = BITFLAGS['M'] + CENTAUR = BITFLAGS['N'] + INSECT = BITFLAGS['O'] + SPIDER = BITFLAGS['P'] + CRUSTACEAN = BITFLAGS['Q'] + WORM = BITFLAGS['R'] + BLOB = BITFLAGS['S'] + UNUSED2 = enum.auto() + UNUSED3 = enum.auto() + MAMMAL = BITFLAGS['V'] + BIRD = BITFLAGS['W'] + REPTILE = BITFLAGS['X'] + SNAKE = BITFLAGS['Y'] + DRAGON = BITFLAGS['Z'] + AMPHIBIAN = BITFLAGS['AA'] + FISH = BITFLAGS['BB'] + COLD_BLOOD = BITFLAGS['CC'] + UNUSED4 = BITFLAGS['DD'] + UNUSED5 = BITFLAGS['EE'] + +class PARTS(enum.IntFlag): + NONE = 0 + HEAD = BITFLAGS['A'] + ARMS = BITFLAGS['B'] + LEGS = BITFLAGS['C'] + HEART = BITFLAGS['D'] + BRAINS = BITFLAGS['E'] + GUTS = BITFLAGS['F'] + HANDS = BITFLAGS['G'] + FEET = BITFLAGS['H'] + FINGERS = BITFLAGS['I'] + EAR = BITFLAGS['J'] + EYE = BITFLAGS['K'] + LONG_TONGUE = BITFLAGS['L'] + EYESTALKS = BITFLAGS['M'] + TENTACLES = BITFLAGS['N'] + FINS = BITFLAGS['O'] + WINGS = BITFLAGS['P'] + TAIL = BITFLAGS['Q'] + # for combat + CLAWS = BITFLAGS['U'] + FANGS = BITFLAGS['V'] + HORNS = BITFLAGS['W'] + SCALES = BITFLAGS['X'] + TUSKS = BITFLAGS['Y'] + +class WEAR_LOCATIONS(enum.Enum): + NONE = -1 + LIGHT = 0 + FINGER_L = 1 + FINGER_R = 2 + NECK_1 = 3 + NECK_2 = 4 + BODY = 5 + HEAD = 6 + LEGS = 7 + FEET = 8 + HANDS = 9 + ARMS = 10 + SHIELD = 11 + ABOUT = 12 + WAIST = 13 + WRIST_L = 14 + RIST_L = 14 # deprecated alias for WRIST_L (historical typo) + WRIST_R = 15 + WIELD = 16 + HOLD = 17 + FLOAT = 18 + MAX = 19 + +class ROM_ROOM_FLAGS(enum.IntFlag): + NONE = 0 + DARK = BITFLAGS['A'] + unused1 = enum.auto() + NO_MOB = BITFLAGS['C'] + INDOORS = BITFLAGS['D'] + unused2 = enum.auto() + UNUSED3 = enum.auto() + unused4 = enum.auto() + unused5 = enum.auto() + unused6 = enum.auto() + PRIVATE = BITFLAGS['J'] + SAFE = BITFLAGS['K'] + SOLITARY = BITFLAGS['L'] + PET_SHOP = BITFLAGS['M'] + NO_RECALL = BITFLAGS['N'] + IMP_ONLY = BITFLAGS['O'] + GODS_ONLY = BITFLAGS['P'] + HEROES_ONLY = BITFLAGS['Q'] + NEWBIES_ONLY = BITFLAGS['R'] + LAW = BITFLAGS['S'] + NOWHERE = BITFLAGS['T'] + +class MERC_ROOM_FLAGS(enum.IntFlag): + DARK = 1 + NO_MOB = 4 + INDOORS = 8 + PRIVATE = 512 + SAFE = 1024 + SOLITARY = 2048 + PETSHOP = 4096 + NO_RECALL = 8192 + +class EXIT_DIRECTIONS(enum.Enum): + NORTH = 0 + EAST = 1 + SOUTH = 2 + WEST = 3 + UP = 4 + DOWN = 5 + +class EXIT_FLAGS(enum.IntFlag): + NONE = 0 + ISDOOR = BITFLAGS['A'] + CLOSED = BITFLAGS['B'] + LOCKED = BITFLAGS['C'] + PICKPROOF = BITFLAGS['F'] + NOPASS = BITFLAGS['G'] + EASY = BITFLAGS['H'] + HARD = BITFLAGS['I'] + INFURIATING = BITFLAGS['J'] + NOCLOSE = BITFLAGS['K'] + NOLOCK = BITFLAGS['L'] + +class SECTOR_TYPES(enum.Enum): + INSIDE = 0 + CITY = 1 + FIELD = 2 + FOREST = 3 + HILLS = 4 + MOUNTAIN = 5 + WATER_SWIM = 6 + WATER_NOSWIM = 7 + UNUSED = 8 + AIR = 9 + DESERT = 10 + MAX = 11 + class SMAUG_ACT_TYPES(enum.IntFlag): IS_NPC = 1 << 0 SENTINEL = 1 << 1 @@ -455,92 +455,92 @@ class SMAUG_AFFECTED_BY(enum.IntFlag): CHAIN_AGONY = 1 << 42 INFEST = 1 << 43 GRAPPLE = 1 << 44 - - -SMAUG_TRAP_TYPES = enum.Enum('SMAUG_TRAP_TYPES', 'POISON_DART POISON_NEEDLE POISON_DAGGER POISON_ARROW BLINDNESS_GAS SLEEPING_GAS FLAME EXPLOSION ACID_SPRAY ELECTRIC_SHOCK BLADE SEX_CHANGE') - -class WEAPON_TYPES(enum.IntFlag): - FLAMING = BITFLAGS['A'] - FROST = BITFLAGS['B'] - VAMPIRIC = BITFLAGS['C'] - SHARP = BITFLAGS['D'] - VORPAL = BITFLAGS['E'] - HANDS = BITFLAGS['F'] - SHOCKING = BITFLAGS['G'] - -class APPLY_TYPES(enum.Enum): - NONE = 0 - STR = 1 - DEX = 2 - INT = 3 - WIS = 4 - CON = 5 - SEX = 6 - CLASS = 7 - LEVEL = 8 - AGE = 9 - HEIGHT = 10 - WEIGHT = 11 - MANA = 12 - HIT = 13 - MOVE = 14 - GOLD = 15 - EXP = 16 - AC = 17 - HITROLL = 18 - DAMROLL = 19 - SAVES = 20 - SAVING_PARA = 20 - SAVING_ROD = 21 - SAVING_PETRI = 22 - SAVING_BREATH = 23 - SAVING_SPELL = 24 - AFFECT = 25 - -@attributes -class Liquid(object): - color = attr(default='', type=str) - proof = attr(default=0, type=int) - full = attr(default=0, type=int) - thurst = attr(default=0, type=int) - food = attr(default=0, type=int) - size = attr(default=0, type=int) - -ROM_LIQUIDS = { - 'water': Liquid('clear', 0, 1, 10, 0, 16), - 'beer': Liquid('amber', 12, 1, 8, 1, 12), - "red wine": Liquid("burgundy", 30, 1, 8, 1, 5), - 'ale': Liquid("brown", 15, 1, 8, 1, 12), - 'dark ale': Liquid("dark", 16, 1, 8, 1, 12), - 'whisky': Liquid("golden", 120, 1, 5, 0, 2), - 'lemonade': Liquid("pink", 0, 1, 9, 2, 12), - 'firebreather': Liquid("boiling", 190, 0, 4, 0, 2), - 'local specialty': Liquid("clear", 151, 1, 3, 0, 2), - 'slime mold juice': Liquid("green", 0, 2, -8, 1, 2), - 'milk': Liquid("white", 0, 2, 9, 3, 12), - 'tea': Liquid("tan", 0, 1, 8, 0, 6), - 'coffee': Liquid("black", 0, 1, 8, 0, 6), - 'blood': Liquid("red", 0, 2, -1, 2, 6), - 'salt water': Liquid("clear", 0, 1, -2, 0, 1), - 'coke': Liquid("brown", 0, 2, 9, 2, 12), - 'root beer': Liquid("brown", 0, 2, 9, 2, 12), - 'elvish wine': Liquid("green", 35, 2, 8, 1, 5), - 'white wine': Liquid("golden", 28, 1, 8, 1, 5), - 'champagne': Liquid("golden", 32, 1, 8, 1, 5), - 'mead': Liquid("honey-colored", 34, 2, 8, 2, 12), - 'rose wine': Liquid("pink", 26, 1, 8, 1, 5), - 'benedictine wine': Liquid("burgundy", 40, 1, 8, 1, 5), - 'vodka': Liquid("clear", 130, 1, 5, 0, 2), - 'cranberry juice': Liquid("red", 0, 1, 9, 2, 12), - 'orange juice': Liquid("orange", 0, 2, 9, 3, 12), - 'absinthe': Liquid("green", 200, 1, 4, 0, 2), - 'brandy': Liquid("golden", 80, 1, 5, 0, 4), - 'aquavit': Liquid("clear", 140, 1, 5, 0, 2), - 'schnapps': Liquid("clear", 90, 1, 5, 0, 2), - 'icewine': Liquid("purple", 50, 2, 6, 1, 5), - 'amontillado': Liquid("burgundy", 35, 2, 8, 1, 5), - 'sherry': Liquid("red", 38, 2, 7, 1, 5), - 'framboise': Liquid("red", 50, 1, 7, 1, 5), - 'rum': Liquid("amber", 151, 1, 4, 0, 2), - 'cordial': Liquid("clear", 100, 1, 5, 0, 2), + + +SMAUG_TRAP_TYPES = enum.Enum('SMAUG_TRAP_TYPES', 'POISON_DART POISON_NEEDLE POISON_DAGGER POISON_ARROW BLINDNESS_GAS SLEEPING_GAS FLAME EXPLOSION ACID_SPRAY ELECTRIC_SHOCK BLADE SEX_CHANGE') + +class WEAPON_TYPES(enum.IntFlag): + FLAMING = BITFLAGS['A'] + FROST = BITFLAGS['B'] + VAMPIRIC = BITFLAGS['C'] + SHARP = BITFLAGS['D'] + VORPAL = BITFLAGS['E'] + HANDS = BITFLAGS['F'] + SHOCKING = BITFLAGS['G'] + +class APPLY_TYPES(enum.Enum): + NONE = 0 + STR = 1 + DEX = 2 + INT = 3 + WIS = 4 + CON = 5 + SEX = 6 + CLASS = 7 + LEVEL = 8 + AGE = 9 + HEIGHT = 10 + WEIGHT = 11 + MANA = 12 + HIT = 13 + MOVE = 14 + GOLD = 15 + EXP = 16 + AC = 17 + HITROLL = 18 + DAMROLL = 19 + SAVES = 20 + SAVING_PARA = 20 + SAVING_ROD = 21 + SAVING_PETRI = 22 + SAVING_BREATH = 23 + SAVING_SPELL = 24 + AFFECT = 25 + +@attributes +class Liquid(object): + color = attr(default='', type=str) + proof = attr(default=0, type=int) + full = attr(default=0, type=int) + thurst = attr(default=0, type=int) + food = attr(default=0, type=int) + size = attr(default=0, type=int) + +ROM_LIQUIDS = { + 'water': Liquid('clear', 0, 1, 10, 0, 16), + 'beer': Liquid('amber', 12, 1, 8, 1, 12), + "red wine": Liquid("burgundy", 30, 1, 8, 1, 5), + 'ale': Liquid("brown", 15, 1, 8, 1, 12), + 'dark ale': Liquid("dark", 16, 1, 8, 1, 12), + 'whisky': Liquid("golden", 120, 1, 5, 0, 2), + 'lemonade': Liquid("pink", 0, 1, 9, 2, 12), + 'firebreather': Liquid("boiling", 190, 0, 4, 0, 2), + 'local specialty': Liquid("clear", 151, 1, 3, 0, 2), + 'slime mold juice': Liquid("green", 0, 2, -8, 1, 2), + 'milk': Liquid("white", 0, 2, 9, 3, 12), + 'tea': Liquid("tan", 0, 1, 8, 0, 6), + 'coffee': Liquid("black", 0, 1, 8, 0, 6), + 'blood': Liquid("red", 0, 2, -1, 2, 6), + 'salt water': Liquid("clear", 0, 1, -2, 0, 1), + 'coke': Liquid("brown", 0, 2, 9, 2, 12), + 'root beer': Liquid("brown", 0, 2, 9, 2, 12), + 'elvish wine': Liquid("green", 35, 2, 8, 1, 5), + 'white wine': Liquid("golden", 28, 1, 8, 1, 5), + 'champagne': Liquid("golden", 32, 1, 8, 1, 5), + 'mead': Liquid("honey-colored", 34, 2, 8, 2, 12), + 'rose wine': Liquid("pink", 26, 1, 8, 1, 5), + 'benedictine wine': Liquid("burgundy", 40, 1, 8, 1, 5), + 'vodka': Liquid("clear", 130, 1, 5, 0, 2), + 'cranberry juice': Liquid("red", 0, 1, 9, 2, 12), + 'orange juice': Liquid("orange", 0, 2, 9, 3, 12), + 'absinthe': Liquid("green", 200, 1, 4, 0, 2), + 'brandy': Liquid("golden", 80, 1, 5, 0, 4), + 'aquavit': Liquid("clear", 140, 1, 5, 0, 2), + 'schnapps': Liquid("clear", 90, 1, 5, 0, 2), + 'icewine': Liquid("purple", 50, 2, 6, 1, 5), + 'amontillado': Liquid("burgundy", 35, 2, 8, 1, 5), + 'sherry': Liquid("red", 38, 2, 7, 1, 5), + 'framboise': Liquid("red", 50, 1, 7, 1, 5), + 'rum': Liquid("amber", 151, 1, 4, 0, 2), + 'cordial': Liquid("clear", 100, 1, 5, 0, 2), } diff --git a/conftest.py b/conftest.py index 1a62345..1d21298 100644 --- a/conftest.py +++ b/conftest.py @@ -1,12 +1,12 @@ -import os - -def pytest_generate_tests(metafunc): - if "rom_path" in metafunc.fixturenames: +import os + +def pytest_generate_tests(metafunc): + if "rom_path" in metafunc.fixturenames: metafunc.parametrize("rom_path", list(files_in_directory('test/rom'))) - if "merc_path" in metafunc.fixturenames: + if "merc_path" in metafunc.fixturenames: metafunc.parametrize("merc_path", list(files_in_directory('test/merc'))) - -def files_in_directory(directory): - for f in os.listdir(directory): - path = os.path.join(directory, f) - yield path + +def files_in_directory(directory): + for f in os.listdir(directory): + path = os.path.join(directory, f) + yield path diff --git a/pyproject.toml b/pyproject.toml index 3ffc7a1..dcb5bfc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,12 @@ test = [ "pytest-cov", ] +[tool.ruff] +line-length = 120 + +[tool.ruff.format] +line-ending = "lf" + [tool.pytest.ini_options] addopts = "--cov=area_reader --cov-branch --cov-report=term-missing" diff --git a/test_area_reader.py b/test_area_reader.py index 7af54d3..b065f51 100644 --- a/test_area_reader.py +++ b/test_area_reader.py @@ -107,14 +107,14 @@ def circle_indexed_paths(family): continue paths.append(base / name) return paths - + def test_loading_rom_area(rom_path): af = area_reader.RomAreaFile(rom_path) af.load_sections() assert af.area assert af.as_dict() assert_jsonifies(af) - + def test_loading_merc_area(merc_path): af = area_reader.MercAreaFile(merc_path) af.load_sections()