Пример #1
0
class DataTableRow_NamedBits(DataTableRow):
    """ A generic table row representing a named bit in a bitmask. """

    __abstract__ = True

    _KEY_NAME = "bit_value"
    _KEY_FORMAT = "d"

    bit_value = _SQLA_Column(_SQLA_Integer, primary_key=True)
    bit_name = _SQLA_Column(_SQLA_String)

    @classmethod
    def from_dict(cls, attributes_dict):
        """ Creates an instance from an attributes dictionary. """

        return cls(bit_value=cls.key_from_dict(attributes_dict),
                   bit_name=attributes_dict["bit_name"])

    @classmethod
    def key_from_dict(cls, attributes_dict):
        """ Returns value of primary key from an attributes dictionary. """

        return int(attributes_dict[cls._KEY_NAME])

    def _pformat_object(self, tables, pformat_config=_PrettyFormatConfig()):
        """ Nicely formats the object data for display. """

        return self.bit_name
Пример #2
0
class ArmorProtection(_Armor_ForeignKey):
    """ A protection value for a zone of an armor. """

    __tablename__ = "protections_by_armor"

    _KEY_NAME = "zone_number"
    _KEY_FORMAT = "d"

    zone_number = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(ArmorProtectionZone.TABLE_NAME() + "." +
                         ArmorProtectionZone.KEY_NAME()),
        primary_key=True)
    protection = _SQLA_Column(_SQLA_Integer)

    def pformat_object(self, tables, pformat_config=_PrettyFormatConfig()):
        """ Nicely formats the object for display. """

        pformat_config_zone = pformat_config.clone(render_title=False)

        template = "{zone_name}: {protection}"
        args = {
            "zone_name":
            tables[ ArmorProtectionZones_DataTable.LABEL( ) ]\
            .pformat_table_lookup(
                self.zone_number, tables, pformat_config = pformat_config_zone
            ),
            "protection": self.protection
        }

        return template.format(**args)
Пример #3
0
class DataTableRow_NamedInteger(DataTableRow):
    """ A generic table row, naming an integer value. """

    __abstract__ = True

    _KEY_NAME = "number"
    _KEY_FORMAT = "d"

    number = _SQLA_Column(_SQLA_Integer, primary_key=True)
    name = _SQLA_Column(_SQLA_String)

    @classmethod
    def from_dict(cls, attributes_dict):
        """ Creates an instance from an attributes dictionary. """

        return cls(number=cls.key_from_dict(attributes_dict),
                   name=attributes_dict["name"])

    @classmethod
    def key_from_dict(cls, attributes_dict):
        """ Returns value of primary key from an attributes dictionary. """

        return int(attributes_dict[cls._KEY_NAME])

    def _pformat_object(self, tables, pformat_config=_PrettyFormatConfig()):
        """ Nicely formats the object data for display. """

        return self.name
Пример #4
0
    def _generated_SQLA_Table_columns( cls ):
        """ Returns additional SQLAlchemy columns for a table mapping. """

        return [
            _SQLA_Column( "damage_base", _SQLA_Integer ),
            _SQLA_Column( "damage_per_level", _SQLA_Integer ),
            _SQLA_Column( "totally_damage", _SQLA_Boolean )
        ]
Пример #5
0
    def _generated_SQLA_Table_columns( cls ):
        """ Returns additional SQLAlchemy columns for a table mapping. """

        return [
            # TODO: Add foreign key on monsters table.
            _SQLA_Column( "monster_number", _SQLA_Integer ),
            _SQLA_Column(
                "monster_group_tag",
                _SQLA_Integer,
                _SQLA_ForeignKey(
                    MonsterTag.TABLE_NAME( ) + "." + MonsterTag.KEY_NAME( )
                )
            ) 
        ]
Пример #6
0
    def generated_SQLA_Table( cls, argument_table_name ):
        """ Returns a SQLAlchemy table to be mapped to an object. """

        return _SQLA_Table(
            argument_table_name + "_by_effect", cls.metadata, 
            _SQLA_Column( "record_id",  _SQLA_Integer, primary_key = True ),
            _SQLA_Column(
                "effect_record_id", _SQLA_Integer,
                _SQLA_ForeignKey(
                    Effect.TABLE_NAME( ) + "." + Effect.KEY_NAME( )
                )
            ),
            *cls._generated_SQLA_Table_columns( ),
            keep_existing = True
        )
Пример #7
0
    def _generated_SQLA_Table_columns( cls ):
        """ Returns additional SQLAlchemy columns for a table mapping. """

        # TODO? Break out base value and per-level value.
        return [
            _SQLA_Column( "healing", _SQLA_Integer ) 
        ]
Пример #8
0
    def _generated_SQLA_Table_columns( cls ):
        """ Returns additional SQLAlchemy columns for a table mapping. """

        # TODO: Add foreign key against monster table.
        return [
            _SQLA_Column( "monster_number", _SQLA_Integer ) 
        ]
Пример #9
0
 def nation_number( cls ):
     return _SQLA_Column(
         _SQLA_Integer,
         _SQLA_ForeignKey(
             Nation.TABLE_NAME( ) + "." + Nation.KEY_NAME( )
         ),
         primary_key = True
     )
Пример #10
0
 def effect_record_id( cls ):
     return _SQLA_Column(
         _SQLA_Integer,
         _SQLA_ForeignKey(
             Effect.TABLE_NAME( ) + "." + Effect.KEY_NAME( )
         ),
         primary_key = True
     )
Пример #11
0
 def attribute_record_id( cls ):
     return _SQLA_Column(
         _SQLA_Integer,
         _SQLA_ForeignKey(
             Attribute.TABLE_NAME( ) + "." + Attribute.KEY_NAME( )
         ),
         primary_key = True
     )
Пример #12
0
 def spell_number( cls ):
     return _SQLA_Column(
         _SQLA_Integer,
         _SQLA_ForeignKey(
             Spell.TABLE_NAME( ) + "." + Spell.KEY_NAME( )
         ),
         primary_key = True
     )
Пример #13
0
 def spell_number( cls ):
     return _SQLA_Column(
         _SQLA_Integer,
         _SQLA_ForeignKey(
             Spell.TABLE_NAME( ) + "." + Spell.KEY_NAME( )
         ),
         primary_key = True
     )
Пример #14
0
    def generated_SQLA_Table( cls, value_table_name ):
        """ Returns a SQLAlchemy table to be mapped to an object. """

        return _SQLA_Table(
            value_table_name + "_by_attribute", cls.metadata, 
            _SQLA_Column( "record_id",  _SQLA_Integer, primary_key = True ),
            _SQLA_Column(
                "attribute_record_id", _SQLA_Integer,
                _SQLA_ForeignKey(
                    Attribute.TABLE_NAME( ) + "." + Attribute.KEY_NAME( )
                )
            ),
            _SQLA_Column(
                "attribute_number", _SQLA_Integer,
                _SQLA_ForeignKey(
                    AttributeKey.TABLE_NAME( ) + "." + AttributeKey.KEY_NAME( )
                )
            ),
            *cls._generated_SQLA_Table_columns( ),
            keep_existing = True
        )
Пример #15
0
class DataTableRow_UnknownField(DataTableRow):
    """ A generic table row representing an unknown field. """

    __abstract__ = True

    @_SQLA_declared_attr
    def offset(cls):
        return _SQLA_Column(_SQLA_Integer, primary_key=True)

    value = _SQLA_Column(_SQLA_Integer)

    _KEY_NAME = "offset"

    def pformat_object(self, tables, pformat_config=_PrettyFormatConfig()):
        """ Nicely formats the object for display. """

        template = "<Unknown> [Offset: {offset}]: {value}"
        args = {"offset": self.offset, "value": self.value}

        template = pformat_config.indent + template

        return template.format(**args)
Пример #16
0
 def monster_number(cls):
     return _SQLA_Column(_SQLA_Integer, primary_key=True)
Пример #17
0
    def _generated_SQLA_Table_columns( cls ):
        """ Returns additional SQLAlchemy columns for a table mapping. """

        return [
            _SQLA_Column( "value", _SQLA_Integer ) 
        ]
Пример #18
0
class Attribute( _DataTableRow ):
    """ An attribute of a Dominions object. """


    __tablename__   = "attributes"


    record_id           = _SQLA_Column(
        _SQLA_Integer, primary_key = True, autoincrement = "ignore_fk"
    )
    attribute_number    = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(
            AttributeKey.TABLE_NAME( ) + "." + AttributeKey.KEY_NAME( )
        )
    )
    object_type         = _SQLA_Column( _SQLA_String )
    raw_value           = _SQLA_Column( _SQLA_Integer )


    _KEY_NAME       = "record_id"


    @classmethod
    def __declare_last__( cls ):
        # Perform late binding against abstract concrete bases.
        cls.value = _SQLA_relationship( AttributeValue, uselist = False )


    @classmethod
    def from_raw_data( cls, attribute_number, object_type, raw_value ):
        """ Creates an instance from a set of raw arguments. """

        args = {
            "attribute_number": attribute_number,
            "object_type": object_type,
            "raw_value": raw_value,
        }

        self = cls( **args )

        self.value = eval(
            "Attribute{attribute_number}Value.from_raw_value".format(
                attribute_number = args[ "attribute_number" ]
            )
        )(
            attribute_record_id = self.record_id,
            attribute_number = attribute_number,
            raw_value = raw_value
        )

        return self


    def pformat_object( self,
        tables, pformat_config = _PrettyFormatConfig( )
    ):
        """ Nicely formats the object for display. """

        return self.value.pformat_object(
            tables, pformat_config = pformat_config
        )
Пример #19
0
class Nation(_DataTableRow_NamedInteger, _DataTableRow_ProgramImage):
    """ A nation. """

    __tablename__ = "nations"

    epithet = _SQLA_Column(_SQLA_String)
    abbreviation = _SQLA_Column(_SQLA_String)
    file_name_base = _SQLA_Column(_SQLA_String)

    # TODO: Add foreign key against monsters table.
    initial_scout = _SQLA_Column(_SQLA_Integer)
    # TODO: Add foreign key against monsters table.
    initial_leader = _SQLA_Column(_SQLA_Integer)
    # TODO: Add foreign key against monsters table.
    initial_troops_type_1 = _SQLA_Column(_SQLA_Integer)
    initial_troops_count_1 = _SQLA_Column(_SQLA_Integer)
    # TODO: Add foreign key against monsters table.
    initial_troops_type_2 = _SQLA_Column(_SQLA_Integer)
    initial_troops_count_2 = _SQLA_Column(_SQLA_Integer)

    pretender_types = _SQLA_relationship("NationPretenderType")
    unpretender_types = _SQLA_relationship("NationUnpretenderType")
    fort_leader_types = _SQLA_relationship("NationFortLeaderType")
    fort_troop_types = _SQLA_relationship("NationFortTroopType")
    nonfort_leader_types = _SQLA_relationship("NationNonfortLeaderType")
    nonfort_troop_types = _SQLA_relationship("NationNonfortTroopType")
    coast_troop_types = _SQLA_relationship("NationCoastTroopType")
    coast_leader_types = _SQLA_relationship("NationCoastLeaderType")

    attributes = _SQLA_relationship("_NationAttribute")
    unknown_fields = _SQLA_relationship("NationUnknownField")

    _TITLE = "Nation"
    _PROGRAM_IMAGE_RECORD_SIZES = {
        "4.03": 1108,
        "4.04": 1108,
        "4.05": 1108,
        "4.05b": 1108,
        "4.07": 1108,
        "4.10": 1108,
        "4.14": 1108,
        "4.16": 1108,
        "4.17": 1108,
        "4.20": 1108,
        "4.21": 1108,
        "4.22": 1108,
        "4.23": 1108,
        "4.24": 1108,
        "4.25": 1108,
        "4.26": 1108,
        "4.27": 1108,
        "4.28": 1108,
        "4.29": 1108,
        "4.30": 1108
    }

    @classmethod
    def from_program_image(cls, program_image, base_offset, number,
                           dominions_version):
        """ Creates an instance from a program image. """

        # TODO: Version these constants.
        NAME_LENGTH = 36
        EPITHET_LENGTH = 36
        GROUP_CODE_LENGTH = 5
        FILE_NAME_BASE_LENGTH = 63

        offset = base_offset

        args = {"number": number}
        unknowns = _OrderedDict()

        args["name"], __ = _from_string(program_image, offset, NAME_LENGTH)
        if "end" == args["name"]: raise StopIteration()
        offset += NAME_LENGTH

        args[ "epithet" ], __ \
        = _from_string( program_image, offset, EPITHET_LENGTH )
        offset += EPITHET_LENGTH

        args[ "abbreviation" ], __ \
        = _from_string( program_image, offset, GROUP_CODE_LENGTH )
        offset += GROUP_CODE_LENGTH

        args[ "file_name_base" ], __ \
        = _from_string( program_image, offset, FILE_NAME_BASE_LENGTH )
        offset += FILE_NAME_BASE_LENGTH

        # TEMP HACK: For decoding.
        for i in range(16):
            unknowns[ offset - base_offset ], offset \
            = _from_native_uint16( program_image, offset )

        attribute_keys = []
        for i in range(64):
            attribute_key, offset \
            = _from_native_uint32( program_image, offset )
            attribute_keys.append(attribute_key)

        attribute_values = []
        for i in range(64):
            attribute_value, offset \
            = _from_native_int32( program_image, offset )
            attribute_values.append(attribute_value)


#        unknowns[ offset - base_offset ], offset \
#        = _from_native_uint32( program_image, offset )

# 90 troop type slots at end

        args["fort_troop_types"] = []
        for slot_idx in range(90):
            monster_number, offset \
            = _from_native_int32( program_image, offset )
            if 0 >= monster_number: break
            troop_type = NationFortTroopType(nation_number=number,
                                             monster_number=monster_number)
            args["fort_troop_types"].append(troop_type)

        for slot_idx in range(slot_idx, 89):
            if -2 == monster_number:
                args["fort_leader_types"] = []
                found = 0
                for slot_idx in range(slot_idx, 89):
                    monster_number, offset \
                    = _from_native_int32( program_image, offset )
                    if 0 >= monster_number: break
                    troop_type = NationFortLeaderType(
                        nation_number=number, monster_number=monster_number)
                    if found == 1:
                        if monster_number == 2751: continue
                    args["fort_leader_types"].append(troop_type)
                    if number == 81:
                        if monster_number == 2751:
                            found = 1

            if -5 == monster_number:
                args["coast_troop_types"] = []
                for slot_idx in range(slot_idx, 89):
                    monster_number, offset \
                    = _from_native_int32( program_image, offset )
                    if 0 >= monster_number: break
                    troop_type = NationCoastTroopType(
                        nation_number=number, monster_number=monster_number)
                    args["coast_troop_types"].append(troop_type)

            if -6 == monster_number:
                args["coast_leader_types"] = []
                for slot_idx in range(slot_idx, 89):
                    monster_number, offset \
                    = _from_native_int32( program_image, offset )
                    if 0 >= monster_number: break
                    troop_type = NationCoastLeaderType(
                        nation_number=number, monster_number=monster_number)
                    args["coast_leader_types"].append(troop_type)

            if -3 == monster_number:
                args["nonfort_troop_types"] = []
                for slot_idx in range(slot_idx, 89):
                    monster_number, offset \
                    = _from_native_int32( program_image, offset )
                    if 0 >= monster_number: break
                    troop_type = NationNonfortTroopType(
                        nation_number=number, monster_number=monster_number)
                    args["nonfort_troop_types"].append(troop_type)

            if -4 == monster_number:
                args["nonfort_leader_types"] = []
                for slot_idx in range(slot_idx, 89):
                    monster_number, offset \
                    = _from_native_int32( program_image, offset )
                    if 0 >= monster_number: break
                    troop_type = NationNonfortLeaderType(
                        nation_number=number, monster_number=monster_number)
                    args["nonfort_leader_types"].append(troop_type)

            if -1 == monster_number:
                args["pretender_types"] = []
                args["unpretender_types"] = []
                monster_numbers = set()
                for slot_idx in range(slot_idx, 89):
                    monster_number, offset \
                    = _from_native_int32( program_image, offset )
                    if monster_number in monster_numbers:
                        continue
                    else:
                        monster_numbers.add(monster_number)
                    if -1 == monster_number: break
                    if 0 == monster_number: continue
                    # Skip 134, not a pretender
                    if 134 == monster_number: continue
                    if 0 < monster_number:
                        troop_type = NationPretenderType(
                            nation_number=number,
                            monster_number=monster_number)
                        args["pretender_types"].append(troop_type)
                    else:
                        troop_type = NationUnpretenderType(
                            nation_number=number,
                            monster_number=-monster_number)
                        args["unpretender_types"].append(troop_type)

        # Note: Should not have any non-zero values.
        for slot_idx in range(slot_idx, 89):
            unknowns[ offset - base_offset ], offset \
            = _from_native_int32( program_image, offset )

        attributes = []
        for key, value in zip(attribute_keys, attribute_values):
            if not key: continue
            attributes.append(
                _NationAttribute.from_raw_data(nation_number=number,
                                               attribute_number=key,
                                               raw_value=value))
        args["attributes"] = attributes

        args["unknown_fields"] = [
            NationUnknownField(nation_number=number,
                               offset=offset,
                               value=value)
            for offset, value in unknowns.items() if value
        ]

        return cls(**args)

    def pformat_row(self, tables, pformat_config=_PrettyFormatConfig()):
        """ Nicely formats the table row for display. """

        output = []
        pformat_config_no_key_padding = pformat_config.clone(
            key_format=self._KEY_FORMAT)
        pformat_config_1 = pformat_config.clone(indent=pformat_config.indent +
                                                4 * " ")
        pformat_config_2 = pformat_config_1.clone(
            indent=pformat_config_1.indent + 4 * " ")

        indent = pformat_config.indent + 4 * " "

        output.append(
            (pformat_config.indent + "{title} #{number}: {name}").format(
                title=self._TITLE,
                number=self.pformat_key(
                    tables, pformat_config=pformat_config_no_key_padding),
                name=self.name))
        if pformat_config.render_compactly:
            return output[0]

        if self.epithet:
            output.append(indent +
                          "Epithet: {epithet}".format(epithet=self.epithet))
        if self.abbreviation:
            output.append(indent + "Abbreviation: {abbreviation}".format(
                abbreviation=self.abbreviation))
        if self.file_name_base:
            output.append(indent + "File Name Base: {file_name_base}".format(
                file_name_base=self.file_name_base))

        if self.initial_scout:
            # TODO: Fill out via table lookup.
            output.append(indent + "Initial Scout {{#startscout}}: "
                          "{initial_scout}".format(
                              initial_scout=self.initial_scout))
        if self.initial_leader:
            # TODO: Fill out via table lookup.
            output.append(indent + "Initial Leader {{#startcom}}: "
                          "{initial_leader}".format(
                              initial_leader=self.initial_leader))
            # TODO: Fill out via table lookup.
            output.append(
                indent + "Initial Troops (Type I) {{#startunittype1}}: "
                "{initial_troops_type} "
                "(Count {{#startunitnbs1}}: {initial_troops_count})".format(
                    initial_troops_type=self.initial_troops_type_1,
                    initial_troops_count=self.initial_troops_count_1))
            # TODO: Fill out via table lookup.
            output.append(
                indent + "Initial Troops (Type II) {{#startunittype2}}: "
                "{initial_troops_type} "
                "(Count {{#startunitnbs2}}: {initial_troops_count})".format(
                    initial_troops_type=self.initial_troops_type_2,
                    initial_troops_count=self.initial_troops_count_2))

        indent_1 = indent + 4 * " "

        if self.unpretender_types:
            output.append(indent + "Excluded Pretenders {#delgod}")
            for troop_type in self.unpretender_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))
        if self.pretender_types:
            output.append(indent + "Pretenders {#addgod}")
            for troop_type in self.pretender_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))
        if self.fort_leader_types:
            output.append(indent +
                          "Recruitable Leaders (Fortification) {#addreccom}")
            for troop_type in self.fort_leader_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))
        if self.fort_troop_types:
            output.append(indent +
                          "Recruitable Troops (Fortification) {#addrecunit}")
            for troop_type in self.fort_troop_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))
        if self.nonfort_leader_types:
            output.append(indent +
                          "Recruitable Leaders (Foreign) {#addforeigncom}")
            for troop_type in self.nonfort_leader_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))
        if self.nonfort_troop_types:
            output.append(indent +
                          "Recruitable Troops (Foreign) {#addforeignunit}")
            for troop_type in self.nonfort_troop_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))
        if self.coast_troop_types:
            output.append(indent + "Coast Troops {#coastunit}")
            for troop_type in self.coast_troop_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))
        if self.coast_leader_types:
            output.append(indent + "Coast Leaders {#coastcom}")
            for troop_type in self.coast_leader_types:
                # TODO: Fill out via table lookup.
                output.append(indent_1 + str(troop_type.monster_number))

        if self.attributes:
            for attribute in self.attributes:
                output.append(
                    attribute.pformat_object(tables,
                                             pformat_config=pformat_config_1))

        if not pformat_config.suppress_unknowns and self.unknown_fields:
            output.append(indent + "Unknowns")
            for unknown_field in self.unknown_fields:
                output.append(
                    unknown_field.pformat_object(
                        tables, pformat_config=pformat_config_2))

        return "\n".join(output) + "\n"
Пример #20
0
 def armor_number( cls ):
     return _SQLA_Column(
         _SQLA_Integer,
         _SQLA_ForeignKey( Armor.TABLE_NAME( ) + "." + Armor.KEY_NAME( ) ),
         primary_key = True
     )
Пример #21
0
 def offset( cls ):
     return _SQLA_Column( _SQLA_Integer, primary_key = True )
Пример #22
0
 def weapon_number(cls):
     return _SQLA_Column(_SQLA_Integer,
                         _SQLA_ForeignKey(Weapon.TABLE_NAME() + "." +
                                          Weapon.KEY_NAME()),
                         primary_key=True)
Пример #23
0
 def monster_number( cls ):
     return _SQLA_Column( _SQLA_Integer, primary_key = True )
Пример #24
0
class Armor(_DataTableRow_NamedInteger, _DataTableRow_ProgramImage):
    """ An armor. """

    __tablename__ = "armors"

    _TITLE = "Armor"
    _PROGRAM_IMAGE_RECORD_SIZES = {
        "4.03": 96,
        "4.04": 96,
        "4.05": 96,
        "4.05b": 96,
        "4.07": 96,
        "4.10": 96,
        "4.14": 96,
        "4.16": 96,
        "4.17": 96,
        "4.20": 96,
        "4.21": 96,
        "4.22": 96,
        "4.23": 96,
        "4.24": 96,
        "4.25": 96,
        "4.26": 96,
        "4.27": 96,
        "4.28": 96,
        "4.29": 96,
        "4.30": 96
    }

    armor_type = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(ArmorType.TABLE_NAME() + "." + ArmorType.KEY_NAME()))
    # TODO: Add field for calculated protection value.
    defense = _SQLA_Column(_SQLA_Integer)
    encumbrance = _SQLA_Column(_SQLA_Integer)
    resource_cost = _SQLA_Column(_SQLA_Integer)

    protections = _SQLA_relationship("ArmorProtection")
    attributes = _SQLA_relationship("_ArmorAttribute")
    unknown_fields = _SQLA_relationship("ArmorUnknownField")

    @classmethod
    def from_program_image(cls, program_image, base_offset, number,
                           dominions_version):
        """ Creates an instance from a program image. """

        # TODO: Version this constant.
        NAME_LENGTH = 36

        offset = base_offset

        unknowns = _OrderedDict()

        name, __ = _from_string(program_image, offset, NAME_LENGTH)
        if "end" == name: raise StopIteration()
        offset += NAME_LENGTH

        protections = []
        for i in range(6):
            protection_zone, offset \
            = _from_native_uint16( program_image, offset )
            protection_amount, offset = _from_native_uint16(
                program_image, offset)
            if protection_zone:
                protections.append(
                    ArmorProtection(armor_number=number,
                                    zone_number=protection_zone,
                                    protection=protection_amount))

        unknowns[ offset ], offset \
        = _from_native_uint16( program_image, offset )

        defense, offset = _from_native_int16(program_image, offset)

        encumbrance, offset = _from_native_uint16(program_image, offset)

        armor_type, offset = _from_native_uint16(program_image, offset)

        resource_cost, offset = _from_native_uint16(program_image, offset)

        unknowns[ offset ], offset \
        = _from_native_uint16( program_image, offset )

        attribute_keys = []
        for i in range(3):
            attribute_key, offset \
            = _from_native_uint32( program_image, offset )
            attribute_keys.append(attribute_key)

        attribute_values = []
        for i in range(3):
            attribute_value, offset \
            = _from_native_uint32( program_image, offset )
            attribute_values.append(attribute_value)

        attributes = []
        for key, value in zip(attribute_keys, attribute_values):
            if not key: continue
            attributes.append(
                _ArmorAttribute.from_raw_data(armor_number=number,
                                              attribute_number=key,
                                              raw_value=value))

        unknown_fields = [
            ArmorUnknownField(armor_number=number, offset=offset, value=value)
            for offset, value in unknowns.items() if value
        ]

        return cls(number=number,
                   name=name,
                   armor_type=armor_type,
                   protections=protections,
                   defense=defense,
                   encumbrance=encumbrance,
                   resource_cost=resource_cost,
                   attributes=attributes,
                   unknown_fields=unknown_fields)

    def pformat_row(self, tables, pformat_config=_PrettyFormatConfig()):
        """ Nicely formats the table row for display. """

        output = []
        pformat_config_no_key_padding = pformat_config.clone(
            key_format=self._KEY_FORMAT)
        pformat_config_1 = pformat_config.clone(indent=pformat_config.indent +
                                                4 * " ")
        pformat_config_2 = pformat_config_1.clone(
            indent=pformat_config_1.indent + 4 * " ")

        indent = pformat_config.indent + 4 * " "

        output.append(
            (pformat_config.indent + "{title} #{number}: {name}").format(
                title=self._TITLE,
                number=self.pformat_key(
                    tables, pformat_config=pformat_config_no_key_padding),
                name=self.name))
        if pformat_config.render_compactly:
            return output[0]

        output.append(
            tables[ArmorTypes_DataTable.LABEL()].pformat_table_lookup(
                self.armor_type, tables, pformat_config=pformat_config_1))
        # TODO: Output actual protection.
        if self.protections:
            output.append(indent + "Protection by Zone")
            for protection in self.protections:
                output.append(
                    protection.pformat_object(tables,
                                              pformat_config=pformat_config_2))
        output.append(indent + "Defense {{Arm: #def}}: {defense}".format(
            defense=self.defense))
        output.append(indent +
                      "Encumbrance {{Arm: #enc}}: {encumbrance}".format(
                          encumbrance=self.encumbrance))
        output.append(indent +
                      "Resource Cost {{Arm: #rcost}}: {resource_cost}".format(
                          resource_cost=self.resource_cost))

        if self.attributes:
            for attribute in self.attributes:
                output.append(
                    attribute.pformat_object(tables,
                                             pformat_config=pformat_config_1))

        if not pformat_config.suppress_unknowns and self.unknown_fields:
            output.append(indent + "Unknowns")
            for unknown_field in self.unknown_fields:
                output.append(
                    unknown_field.pformat_object(
                        tables, pformat_config=pformat_config_2))

        return "\n".join(output) + "\n"
Пример #25
0
class Weapon(_DataTableRow_NamedInteger, _DataTableRow_ProgramImage):
    """ A weapon. """

    __tablename__ = "weapons"

    effect_record_id = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(_Effect.TABLE_NAME() + "." + _Effect.KEY_NAME()))
    effect = _SQLA_relationship(
        _Effect,
        uselist=False,
        primaryjoin=effect_record_id == _Effect.record_id)
    attack = _SQLA_Column(_SQLA_Integer)
    defense = _SQLA_Column(_SQLA_Integer)
    length = _SQLA_Column(_SQLA_Integer)
    attack_rate = _SQLA_Column(_SQLA_Integer)
    attacks_total = _SQLA_Column(_SQLA_Integer)
    secondary_effect_on_hit = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(__tablename__ + "." +
                         _DataTableRow_NamedInteger.KEY_NAME()))
    secondary_effect_always = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(__tablename__ + "." +
                         _DataTableRow_NamedInteger.KEY_NAME()))
    resource_cost = _SQLA_Column(_SQLA_Integer)

    attributes = _SQLA_relationship("_WeaponAttribute")
    unknown_fields = _SQLA_relationship("WeaponUnknownField")

    _TITLE = "Weapon"
    _PROGRAM_IMAGE_RECORD_SIZES = {
        "4.03": 112,
        "4.04": 112,
        "4.05": 112,
        "4.05b": 112,
        "4.07": 112,
        "4.10": 112,
        "4.14": 112,
        "4.16": 112,
        "4.17": 112,
        "4.20": 112,
        "4.21": 112,
        "4.22": 112,
        "4.23": 112,
        "4.24": 112,
        "4.25": 112,
        "4.26": 112,
        "4.27": 112,
        "4.28": 112,
        "4.29": 112,
        "4.30": 112
    }

    @classmethod
    def from_program_image(cls, program_image, base_offset, number,
                           dominions_version):
        """ Creates an instance from a program image. """

        # TODO: Version this constant.
        NAME_LENGTH = 36

        offset = base_offset

        unknowns = _OrderedDict()

        name, __ = _from_string(program_image, offset, NAME_LENGTH)
        if "end" == name: raise StopIteration()
        offset += NAME_LENGTH

        unknowns[ offset ], offset \
        = _from_native_uint32( program_image, offset )

        effect_argument, offset \
        = _from_native_int64( program_image, offset )

        attack, offset = _from_native_int16(program_image, offset)

        defense, offset = _from_native_int16(program_image, offset)

        effect_number, offset = _from_native_uint16(program_image, offset)

        length, offset = _from_native_uint16(program_image, offset)

        range_of_effect, offset = _from_native_int16(program_image, offset)

        attack_rate, offset = _from_native_int16(program_image, offset)

        attacks_total, offset = _from_native_uint16(program_image, offset)

        unknowns[ offset ], offset \
        = _from_native_uint16( program_image, offset )

        effect_modifiers, offset = _from_native_int64(program_image, offset)

        secondary_effect, offset = _from_native_int16(program_image, offset)
        if 0 > secondary_effect:
            secondary_effect_always = -secondary_effect
            secondary_effect_on_hit = 0
        else:
            secondary_effect_always = 0
            secondary_effect_on_hit = secondary_effect

        flight_sprite_number, offset \
        = _from_native_int16( program_image, offset )
        flight_sprite_length, offset \
        = _from_native_uint16( program_image, offset )

        explosion_sprite_number, offset \
        = _from_native_int16( program_image, offset )
        explosion_sprite_length, offset \
        = _from_native_uint16( program_image, offset )

        area_of_effect, offset = _from_native_uint16(program_image, offset)

        sound_number, offset = _from_native_uint16(program_image, offset)

        resource_cost, offset = _from_native_uint16(program_image, offset)

        attribute_keys = []
        for i in range(3):
            attribute_key, offset \
            = _from_native_uint32( program_image, offset )
            attribute_keys.append(attribute_key)

        attribute_values = []
        for i in range(3):
            attribute_value, offset \
            = _from_native_uint32( program_image, offset )
            attribute_values.append(attribute_value)

        attributes = []
        for key, value in zip(attribute_keys, attribute_values):
            if not key: continue
            attributes.append(
                _WeaponAttribute.from_raw_data(weapon_number=number,
                                               attribute_number=key,
                                               raw_value=value))

        unknown_fields = [
            WeaponUnknownField(weapon_number=number,
                               offset=offset,
                               value=value)
            for offset, value in unknowns.items() if value
        ]

        effect = _Effect.from_raw_data(
            effect_number=effect_number,
            object_type=cls.TITLE(),
            raw_argument=effect_argument,
            modifiers_mask=effect_modifiers,
            raw_range=range_of_effect,
            raw_area=area_of_effect,
            sound_number=sound_number,
            flight_sprite_number=flight_sprite_number,
            flight_sprite_length=flight_sprite_length,
            explosion_sprite_number=explosion_sprite_number,
            explosion_sprite_length=explosion_sprite_length)

        return cls(number=number,
                   name=name,
                   effect_record_id=effect.record_id,
                   effect=effect,
                   attack=attack,
                   defense=defense,
                   attack_rate=attack_rate,
                   attacks_total=attacks_total,
                   length=length,
                   secondary_effect_on_hit=secondary_effect_on_hit,
                   secondary_effect_always=secondary_effect_always,
                   resource_cost=resource_cost,
                   attributes=attributes,
                   unknown_fields=unknown_fields)

    def pformat_row(self, tables, pformat_config=_PrettyFormatConfig()):
        """ Nicely formats the table row for display. """

        output = []
        pformat_config_no_key_padding = pformat_config.clone(
            key_format=self._KEY_FORMAT)
        pformat_config_1 = pformat_config.clone(indent=pformat_config.indent +
                                                4 * " ")
        pformat_config_2 = pformat_config_1.clone(
            indent=pformat_config_1.indent + 4 * " ")
        pformat_config_compact = pformat_config.clone(indent="",
                                                      render_title=False,
                                                      render_compactly=True)

        indent = pformat_config.indent + 4 * " "

        output.append(
            (pformat_config.indent + "{title} #{number}: {name}").format(
                title=self._TITLE,
                number=self.pformat_key(
                    tables, pformat_config=pformat_config_no_key_padding),
                name=self.name))
        if pformat_config.render_compactly:
            return output[0]

        output.append(
            self.effect.pformat_object(tables,
                                       pformat_config=pformat_config_1))
        output.append(indent + "Attack {{Wpn: #att}}: {attack}".format(
            attack=self.attack))
        output.append(indent + "Defense {{Wpn: #def}}: {defense}".format(
            defense=self.defense))
        # TODO: Handle negative rates.
        output.append(indent +
                      "Attack Rate {{Wpn: #nratt}}: {attack_rate}".format(
                          attack_rate=self.attack_rate))
        if self.attacks_total:
            output.append(indent + "Attacks per Battle {{Wpn: #ammo}}: "
                          "{attacks_total}".format(
                              attacks_total=self.attacks_total))
        output.append(indent + "Length {{Wpn: #len}}: {length}".format(
            length=self.length))
        if self.secondary_effect_on_hit:
            output.append(
                indent + "On-Hit Secondary Effect {Wpn: #secondaryeffect}: " +
                tables[Weapons_DataTable.LABEL()].pformat_table_lookup(
                    self.secondary_effect_on_hit,
                    tables,
                    pformat_config=pformat_config_compact))
        if self.secondary_effect_always:
            output.append(
                indent + "Always Secondary Effect "
                "{Wpn: #secondaryeffectalways}: " +
                tables[Weapons_DataTable.LABEL()].pformat_table_lookup(
                    self.secondary_effect_always,
                    tables,
                    pformat_config=pformat_config_compact))
        output.append(indent +
                      "Resource Cost {{Wpn: #rcost}}: {resource_cost}".format(
                          resource_cost=self.resource_cost))

        if self.attributes:
            for attribute in self.attributes:
                output.append(
                    attribute.pformat_object(tables,
                                             pformat_config=pformat_config_1))

        if not pformat_config.suppress_unknowns and self.unknown_fields:
            output.append(indent + "Unknowns")
            for unknown_field in self.unknown_fields:
                output.append(
                    unknown_field.pformat_object(
                        tables, pformat_config=pformat_config_2))

        return "\n".join(output) + "\n"
Пример #26
0
 def armor_number(cls):
     return _SQLA_Column(_SQLA_Integer,
                         _SQLA_ForeignKey(Armor.TABLE_NAME() + "." +
                                          Armor.KEY_NAME()),
                         primary_key=True)
Пример #27
0
class Spell( _DataTableRow_NamedInteger, _DataTableRow_ProgramImage ):
    """ A spell. """


    __tablename__   = "spells"


    school                  = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(
            MagicSchool.TABLE_NAME( ) + "." + MagicSchool.KEY_NAME( )
        )
    )
    research_level          = _SQLA_Column( _SQLA_Integer )
    path_0                  = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(
            MagicPath.TABLE_NAME( ) + "." + MagicPath.KEY_NAME( )
        )
    )
    path_level_0            = _SQLA_Column( _SQLA_Integer )
    path_1                  = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(
            MagicPath.TABLE_NAME( ) + "." + MagicPath.KEY_NAME( )
        )
    )
    path_level_1            = _SQLA_Column( _SQLA_Integer )
    effect_record_id        = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey( _Effect.TABLE_NAME( ) + "." + _Effect.KEY_NAME( ) )
    )
    effect                  = _SQLA_relationship(
        _Effect, uselist = False, 
        primaryjoin = effect_record_id == _Effect.record_id
    )
    effects_count           = _SQLA_Column( _SQLA_Integer )
    precision               = _SQLA_Column( _SQLA_Integer )
    fatigue                 = _SQLA_Column( _SQLA_Integer )
    gem_cost                = _SQLA_Column( _SQLA_Integer )
    next_spell              = _SQLA_Column(
        _SQLA_Integer,
        _SQLA_ForeignKey(
            __tablename__ + "." + _DataTableRow_NamedInteger.KEY_NAME( )
        )
    )
    description             = _SQLA_Column( _SQLA_String )

    attributes              = _SQLA_relationship( "_SpellAttribute" )
    unknown_fields          = _SQLA_relationship( "SpellUnknownField" )


    _TITLE                      = "Spell"
    _PROGRAM_IMAGE_RECORD_SIZES = {
        "4.03": 200, "4.04": 200, "4.05": 200, "4.05b": 200, "4.07": 200, "4.10": 200,
        "4.14": 200, "4.16": 200, "4.17": 200, "4.20": 200, "4.21": 200, "4.22": 200,
        "4.23": 200, "4.24": 200, "4.25": 200, "4.26": 200, "4.27": 200, "4.28": 200,
        "4.29": 200, "4.30": 200
    }


    @classmethod
    def from_program_image(
        cls, program_image, base_offset, number, dominions_version
    ):
        """ Creates an instance from a program image. """

        # TODO: Version this constant.
        NAME_LENGTH     = 36

        offset = base_offset
        
        args = { "number": number }
        effect_args = { }
        unknowns = _OrderedDict( )
        
        args[ "name" ], __ = _from_string( program_image, offset, NAME_LENGTH )
        if "end" == args[ "name" ]: raise StopIteration( )
        offset += NAME_LENGTH

        args[ "school" ], offset = _from_native_int8( program_image, offset )
        args[ "research_level" ], offset \
        = _from_native_uint8( program_image, offset )

        path_mask, offset = _from_native_int16( program_image, offset )
        if 0 > path_mask:
            if -1 == path_mask:
                args[ "path_0" ] = -1
                args[ "path_1" ] = -1
            else:
                args[ "path_0" ] = 256 + path_mask
                args[ "path_1" ] = -1
        else:
            args[ "path_0" ] = 0x00ff & path_mask
            args[ "path_1" ] = (0xff00 & path_mask) >> 8
        path_level_mask, offset = _from_native_uint16( program_image, offset )
        args[ "path_level_0" ] = 0x00ff & path_level_mask
        args[ "path_level_1" ] = (0xff00 & path_level_mask) >> 8

        fatigue, offset = _from_native_uint16( program_image, offset )
        args[ "fatigue" ] = fatigue % 100
        args[ "gem_cost" ] = fatigue // 100

        effect_args[ "raw_area" ], offset \
        = _from_native_uint16( program_image, offset )

        effect_args[ "effect_number" ], offset \
        = _from_native_uint16( program_image, offset )

        effect_args[ "raw_range" ], offset \
        = _from_native_uint16( program_image, offset )

        args[ "precision" ], offset \
        = _from_native_int16( program_image, offset )

        unknowns[ offset ], offset \
        = _from_native_uint32( program_image, offset )

        effect_args[ "raw_argument" ], offset \
        = _from_native_int64( program_image, offset )

        args[ "effects_count" ], offset \
        = _from_native_uint16( program_image, offset )

        effect_args[ "flight_sprite_number" ], offset \
        = _from_native_int16( program_image, offset )
        effect_args[ "flight_sprite_length" ], offset \
        = _from_native_uint16( program_image, offset )

        effect_args[ "explosion_sprite_number" ], offset \
        = _from_native_int16( program_image, offset )
        effect_args[ "explosion_sprite_length" ], offset \
        = _from_native_uint16( program_image, offset )

        unknowns[ offset ], offset \
        = _from_native_uint32( program_image, offset )
        unknowns[ offset ], offset \
        = _from_native_uint16( program_image, offset )

        effect_args[ "modifiers_mask" ], offset \
        = _from_native_int64( program_image, offset )

        args[ "next_spell" ], offset \
        = _from_native_uint16( program_image, offset )

        effect_args[ "sound_number" ], offset \
        = _from_native_uint16( program_image, offset )

        attribute_keys = [ ]
        for i in range( 13 ):
            attribute_key, offset \
            = _from_native_uint32( program_image, offset )
            attribute_keys.append( attribute_key )

        attribute_values = [ ]
        for i in range( 13 ):
            attribute_value, offset \
            = _from_native_uint32( program_image, offset )
            attribute_values.append( attribute_value )

        unknowns[ offset ], offset \
        = _from_native_uint32( program_image, offset )

        attributes = [ ]
        for key, value in zip( attribute_keys, attribute_values ):
            if not key: continue
            attributes.append( _SpellAttribute.from_raw_data(
                spell_number = number,
                attribute_number = key,
                raw_value = value
            ) )
        args[ "attributes" ] = attributes

        args[ "unknown_fields" ] = [
            SpellUnknownField(
                spell_number = number,
                offset = offset, value = value
            )
            for offset, value in unknowns.items( ) if value
        ]

        effect_args[ "object_type" ] = cls.TITLE( )
        args[ "effect" ] = _Effect.from_raw_data( **effect_args )

        return cls( **args )


    def pformat_row( self,
        tables, pformat_config = _PrettyFormatConfig( )
    ):
        """ Nicely formats the table row for display. """

        output = [ ]
        pformat_config_no_key_padding = pformat_config.clone(
            key_format = self._KEY_FORMAT
        )
        pformat_config_1 = pformat_config.clone(
            indent = pformat_config.indent + 4 * " "
        )
        pformat_config_2 = pformat_config_1.clone(
            indent = pformat_config_1.indent + 4 * " "
        )
        pformat_config_compact = pformat_config.clone(
            indent = "", render_title = False, render_compactly = True
        )
        pformat_config_compact_no_key = pformat_config.clone(
            indent = "", render_title = False,
            render_key_with_object = False,
            render_compactly = True
        )

        indent = pformat_config.indent + 4 * " "

        output.append(
            (pformat_config.indent + "{title} #{number}: {name}").format(
                title = self._TITLE,
                number = self.pformat_key(
                    tables, pformat_config = pformat_config_no_key_padding
                ),
                name = self.name
            )
        )
        if pformat_config.render_compactly:
            return output[ 0 ]

        template = indent + "Research Requirement {{Spl: #school}}"
        args = {
            "school":
            tables[ MagicSchools_DataTable.LABEL( ) ].pformat_table_lookup(
                self.school, tables,
                pformat_config = pformat_config_compact_no_key
            )
        }
        if 0 > self.school:
            template += ": {school}"
        else:
            template += " {{Spl: #researchlevel}}: {school} {level}"
            args[ "level" ] = self.research_level
        output.append( template.format( **args ) )

        for idx in range( 2 ):
            path = eval( "self.path_{idx}".format( idx = idx ) )
            if 0 <= path:
                output.append(
                    indent + "Magic Path #{idx_plus_1} "
                    "{{Spl: #path {idx}}} {{Spl: #pathlevel {idx}}}: "
                    "{path} {level}".format(
                        idx = idx, idx_plus_1 = idx + 1,
                        path
                        = tables[ MagicPaths_DataTable.LABEL( ) ]\
                        .pformat_table_lookup(
                            path, tables,
                            pformat_config = pformat_config_compact_no_key
                        ),
                        level = eval(
                            "self.path_level_{idx}".format( idx = idx )
                        )
                    )
                )

        output.append( self.effect.pformat_object(
            tables, pformat_config = pformat_config_1
        ) )

        output.append(
            indent + "Number of Effects {{Spl: #nreff}}: "
            "{effects_count}".format(
                effects_count = self.effects_count
            )
        )

        if self.precision:
            output.append(
                indent + "Precision {{Spl: #precision}}: {precision}".format(
                    precision = self.precision
                )
            )
        if   self.fatigue:
            output.append(
                indent + "Fatigue {{Spl: #fatiguecost}}: {fatigue}".format(
                    fatigue = self.fatigue
                )
            )
        elif self.gem_cost:
            output.append(
                indent + "Gem Cost {{Spl: #fatiguecost}}: {gem_cost}".format(
                    gem_cost = self.gem_cost
                )
            )

        if self.next_spell:
            output.append(
                indent + "Next Spell {{Spl: #nextspell}}: "
                "{next_spell}".format(
                    next_spell
                    = tables[ Spells_DataTable.LABEL( ) ]\
                    .pformat_table_lookup(
                        self.next_spell, tables,
                        pformat_config = pformat_config_compact
                    )
                )
            )

        if self.attributes:
            for attribute in self.attributes:
                output.append( attribute.pformat_object(
                    tables, pformat_config = pformat_config_1
                ) )

        if not pformat_config.suppress_unknowns and self.unknown_fields:
            output.append( indent + "Unknowns" )
            for unknown_field in self.unknown_fields:
                output.append( unknown_field.pformat_object(
                    tables, pformat_config = pformat_config_2
                ) )

        if self.description:
            output.append( "" )
            text_wrapper = _TextWrapper(
                width = pformat_config.line_width,
                initial_indent = pformat_config_1.indent,
                subsequent_indent = pformat_config_1.indent
            )
            output.extend( text_wrapper.wrap( self.description ) )

        return "\n".join( output ) + "\n"
Пример #28
0
 def offset(cls):
     return _SQLA_Column(_SQLA_Integer, primary_key=True)