diff --git a/Mopy/Docs/Wrye Bash Advanced Readme.html b/Mopy/Docs/Wrye Bash Advanced Readme.html index 9b4613df47..e2b77f0f42 100644 --- a/Mopy/Docs/Wrye Bash Advanced Readme.html +++ b/Mopy/Docs/Wrye Bash Advanced Readme.html @@ -4913,7 +4913,7 @@

Bash Tags Back to top
  • (SNAM) Sound - Open
  • (QNAM) Sound - Close
  • -
  • (RNAM) Sound - Looping/Random - Fallout: New Vegas only
  • +
  • (RNAM) Sound - Random/Looping - Fallout: New Vegas only
  • (CREA) Creature: @@ -6359,6 +6359,15 @@

    Merge Filtering Back Skyrim, Enderal: Forgotten Stories & Skyrim: Special Edition + + (COBJ) Constructible Object + +
      +
    • (FVPA) Components
    • +
    + + Fallout 4 + (CONT) Container @@ -6366,7 +6375,7 @@

    Merge Filtering Back
  • (INAM) Items
  • - All but Fallout 4 + All games (CREA) Creature diff --git a/Mopy/bash/brec/advanced_elements.py b/Mopy/bash/brec/advanced_elements.py index 39e4b54246..7582d33ecd 100644 --- a/Mopy/bash/brec/advanced_elements.py +++ b/Mopy/bash/brec/advanced_elements.py @@ -29,6 +29,7 @@ __author__ = u'Infernio' import copy +import operator from collections import OrderedDict from itertools import chain @@ -680,6 +681,25 @@ def _decide_common(self, record): return all(getattr(flags_val, flag_name) for flag_name in self._required_flags) +class FormVersionDecider(ACommonDecider): + """Decider that checks if the record's form version against a target form + version.""" + def __init__(self, comp_op, target_form_ver: int): + """Creates a new SinceFormVersionDecider with the specified target form + version. + + :param comp_op: A callable that takes two integers. The first will be + the record's form version and the second will be target_form_ver. + The return value of this callable will be what's returned by the + decider. operator.ge is an example of a valid callable here. + :param target_form_ver: The form version in which the change was + introduced.""" + self._comp_op = comp_op + self._target_form_ver = target_form_ver + + def _decide_common(self, record): + return self._comp_op(record.header.form_version, self._target_form_ver) + class PartialLoadDecider(ADecider): """Partially loads a subrecord using a given loader, then rewinds the input stream and delegates to a given decider. Can decide at dump-time diff --git a/Mopy/bash/brec/common_records.py b/Mopy/bash/brec/common_records.py index 5f6b0a177b..d2249f866f 100644 --- a/Mopy/bash/brec/common_records.py +++ b/Mopy/bash/brec/common_records.py @@ -27,19 +27,60 @@ from . import utils_constants from .advanced_elements import FidNotNullDecider, AttrValDecider, MelArray, \ - MelUnion, MelSorted + MelUnion, MelSorted, MelSimpleArray from .basic_elements import MelBase, MelFid, MelFids, MelFloat, MelGroups, \ MelLString, MelNull, MelStruct, MelUInt32, MelSInt32, MelFixedString, \ - MelUnicode, unpackSubHeader -from .common_subrecords import MelEdid + MelUnicode, unpackSubHeader, MelUInt32Flags, MelString +from .common_subrecords import MelEdid, MelDescription, MelColor, MelDebrData from .record_structs import MelRecord, MelSet from .utils_constants import FID, FormId from .. import bolt, exception -from ..bolt import decoder, FName, struct_pack, structs_cache, \ +from ..bolt import decoder, FName, struct_pack, structs_cache, Flags, \ remove_newlines, to_unix_newlines, sig_to_str, to_win_newlines #------------------------------------------------------------------------------ -class MreHeaderBase(MelRecord): +# Base classes ---------------------------------------------------------------- +#------------------------------------------------------------------------------ +class AMreWithItems(MelRecord): + """Base class for record types that contain a list of items (see + common_subrecords.AMelItems).""" + __slots__ = [] + + def mergeFilter(self, modSet): + self.items = [i for i in self.items if i.item.mod_id in modSet] + +#------------------------------------------------------------------------------ +class AMreActor(AMreWithItems): + """Base class for Creatures and NPCs.""" + __slots__ = [] + + def mergeFilter(self, modSet): + super().mergeFilter(modSet) + self.spells = [x for x in self.spells if x.mod_id in modSet] + self.factions = [x for x in self.factions if x.faction.mod_id in modSet] + +#------------------------------------------------------------------------------ +class AMreGmst(MelRecord): + """Game Setting record. Base class, each game should derive from this + class.""" + Ids = None + rec_sig = b'GMST' + + melSet = MelSet( + MelEdid(), + MelUnion({ + u'b': MelUInt32(b'DATA', u'value'), # actually a bool + u'f': MelFloat(b'DATA', u'value'), + u's': MelLString(b'DATA', u'value'), + }, decider=AttrValDecider( + u'eid', transformer=lambda e: e[0] if e else u'i'), + fallback=MelSInt32(b'DATA', u'value') + ), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class AMreHeader(MelRecord): """File header. Base class for all 'TES4' like records""" # Subrecords that can appear after the masters block - must be set per game _post_masters_sigs: set[bytes] @@ -168,134 +209,7 @@ def num_masters(self): return len(self.masters) __slots__ = [] #------------------------------------------------------------------------------ -class MreFlst(MelRecord): - """FormID List.""" - rec_sig = b'FLST' - - melSet = MelSet( - MelEdid(), - MelFids('formIDInList', MelFid(b'LNAM')), # do *not* sort! - ) - - __slots__ = melSet.getSlotsUsed() + [u'mergeOverLast', u'mergeSources', - u'items', u'de_records', - u're_records'] - - def __init__(self, header, ins=None, do_unpack=False): - super(MreFlst, self).__init__(header, ins, do_unpack=do_unpack) - self.mergeOverLast = False #--Merge overrides last mod merged - self.mergeSources = None #--Set to list by other functions - self.items = None #--Set of items included in list - #--Set of items deleted by list (Deflst mods) unused for Skyrim - self.de_records = None #--Set of items deleted by list (Deflst mods) - self.re_records = None # unused, needed by patcher - - def mergeFilter(self, modSet): - self.formIDInList = [f for f in self.formIDInList if - f.mod_id in modSet] - - def mergeWith(self,other,otherMod): - """Merges newLevl settings and entries with self. - Requires that: self.items, other.de_records be defined.""" - #--Remove items based on other.removes - if other.de_records: - removeItems = self.items & other.de_records - self.formIDInList = [fi for fi in self.formIDInList - if fi not in removeItems] - self.items |= other.de_records - #--Add new items from other - newItems = set() - formIDInListAppend = self.formIDInList.append - newItemsAdd = newItems.add - for fi in other.formIDInList: - if fi not in self.items: - formIDInListAppend(fi) - newItemsAdd(fi) - if newItems: - self.items |= newItems - #--Is merged list different from other? (And thus written to patch.) - if len(self.formIDInList) != len(other.formIDInList): - self.mergeOverLast = True - else: - for selfEntry, otherEntry in zip(self.formIDInList, - other.formIDInList): - if selfEntry != otherEntry: - self.mergeOverLast = True - break - else: - self.mergeOverLast = False - if self.mergeOverLast: - self.mergeSources.append(otherMod) - else: - self.mergeSources = [otherMod] - self.setChanged() - -#------------------------------------------------------------------------------ -class MreGlob(MelRecord): - """Global record. Rather stupidly all values, despite their designation - (short,long,float), are stored as floats -- which means that very large - integers lose precision.""" - rec_sig = b'GLOB' - - melSet = MelSet( - MelEdid(), - MelFixedString(b'FNAM', u'global_format', 1, u's'), - MelFloat(b'FLTV', u'global_value'), - ) - __slots__ = melSet.getSlotsUsed() - -#------------------------------------------------------------------------------ -class MreGmstBase(MelRecord): - """Game Setting record. Base class, each game should derive from this - class.""" - Ids = None - rec_sig = b'GMST' - - melSet = MelSet( - MelEdid(), - MelUnion({ - u'b': MelUInt32(b'DATA', u'value'), # actually a bool - u'f': MelFloat(b'DATA', u'value'), - u's': MelLString(b'DATA', u'value'), - }, decider=AttrValDecider( - u'eid', transformer=lambda e: e[0] if e else u'i'), - fallback=MelSInt32(b'DATA', u'value') - ), - ) - __slots__ = melSet.getSlotsUsed() - -#------------------------------------------------------------------------------ -class MreLand(MelRecord): - """Land structure. Part of exterior cells.""" - rec_sig = b'LAND' - - melSet = MelSet( - MelBase(b'DATA', u'unknown'), - MelBase(b'VNML', u'vertex_normals'), - MelBase(b'VHGT', u'vertex_height_map'), - MelBase(b'VCLR', u'vertex_colors'), - MelSorted(MelGroups(u'layers', - # Start a new layer each time we hit one of these - MelUnion({ - b'ATXT': MelStruct(b'ATXT', [u'I', u'B', u's', u'h'], (FID, u'atxt_texture'), - u'quadrant', u'unknown', u'layer'), - b'BTXT': MelStruct(b'BTXT', [u'I', u'B', u's', u'h'], (FID, u'btxt_texture'), - u'quadrant', u'unknown', u'layer'), - }), - # VTXT only exists for ATXT layers, i.e. if ATXT's FormID is valid - MelUnion({ - True: MelBase(b'VTXT', u'alpha_layer_data'), # sorted - False: MelNull(b'VTXT'), - }, decider=FidNotNullDecider(u'atxt_texture')), - ), sort_by_attrs=(u'quadrant', u'layer')), - MelArray(u'vertex_textures', - MelFid(b'VTEX', u'vertex_texture'), - ), - ) - __slots__ = melSet.getSlotsUsed() - -#------------------------------------------------------------------------------ -class MreLeveledListBase(MelRecord): +class AMreLeveledList(MelRecord): """Base type for leveled item/creature/npc/spells. it requires the base class to use the following: classAttributes: @@ -328,7 +242,7 @@ def __init__(self, header, ins=None, do_unpack=False): self.de_records = None #--Set of items deleted by list (Delev and Relev mods) self.re_records = None #--Set of items relevelled by list (Relev mods) - def mergeFilter(self,modSet): + def mergeFilter(self, modSet): self.entries = [entry for entry in self.entries if entry.listId.mod_id in modSet] @@ -412,19 +326,180 @@ def mergeWith(self,other,otherMod): self.setChanged(self.mergeOverLast) #------------------------------------------------------------------------------ -class MreWithItems(MelRecord): - """Base class for record types that contain a list of items (MelItems).""" - __slots__ = [] +# Full classes ---------------------------------------------------------------- +#------------------------------------------------------------------------------ +class MreColl(MelRecord): + """Collision Layer.""" + rec_sig = b'COLL' - def mergeFilter(self, modSet): - self.items = [i for i in self.items if i.item.mod_id in modSet] + _coll_flags = Flags.from_names('trigger_volume', 'sensor', + 'navmesh_obstacle') + + melSet = MelSet( + MelEdid(), + MelDescription(), + MelUInt32(b'BNAM', 'layer_index'), + MelColor(b'FNAM'), + MelUInt32Flags(b'GNAM', 'layer_flags', _coll_flags), + MelString(b'MNAM', 'layer_name'), + MelUInt32(b'INTV', 'interactables_count'), + MelSorted(MelSimpleArray('collides_with', MelFid(b'CNAM'))), + ) + __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreActorBase(MreWithItems): - """Base class for Creatures and NPCs.""" - __slots__ = [] +class MreDebr(MelRecord): + """Debris.""" + rec_sig = b'DEBR' + + melSet = MelSet( + MelEdid(), + MelGroups('debr_models', + MelDebrData(), + # Ignore texture hashes - they're only an optimization, plenty + # of records in Skyrim.esm are missing them + MelNull(b'MODT'), + ), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreDlbr(MelRecord): + """Dialog Branch.""" + rec_sig = b'DLBR' + + _dlbr_flags = Flags.from_names('top_level', 'blocking', 'exclusive') + + melSet = MelSet( + MelEdid(), + MelFid(b'QNAM', 'dlbr_quest'), + MelUInt32(b'TNAM', 'dlbr_category'), + MelUInt32Flags(b'DNAM', 'dlbr_flags', _dlbr_flags), + MelFid(b'SNAM', 'starting_topic'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreDlvw(MelRecord): + """Dialog View""" + rec_sig = b'DLVW' + + melSet = MelSet( + MelEdid(), + MelFid(b'QNAM', 'dlvw_quest'), + MelFids('dlvw_branches', MelFid(b'BNAM')), + MelGroups('unknown_tnam', + MelBase(b'TNAM', 'unknown1'), + ), + MelBase(b'ENAM', 'unknown_enam'), + MelBase(b'DNAM', 'unknown_dnam'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreFlst(MelRecord): + """FormID List.""" + rec_sig = b'FLST' + + melSet = MelSet( + MelEdid(), + MelFids('formIDInList', MelFid(b'LNAM')), # do *not* sort! + ) + + __slots__ = melSet.getSlotsUsed() + [u'mergeOverLast', u'mergeSources', + u'items', u'de_records', + u're_records'] + + def __init__(self, header, ins=None, do_unpack=False): + super(MreFlst, self).__init__(header, ins, do_unpack=do_unpack) + self.mergeOverLast = False #--Merge overrides last mod merged + self.mergeSources = None #--Set to list by other functions + self.items = None #--Set of items included in list + #--Set of items deleted by list (Deflst mods) unused for Skyrim + self.de_records = None #--Set of items deleted by list (Deflst mods) + self.re_records = None # unused, needed by patcher def mergeFilter(self, modSet): - super(MreActorBase, self).mergeFilter(modSet) - self.spells = [x for x in self.spells if x.mod_id in modSet] - self.factions = [x for x in self.factions if x.faction.mod_id in modSet] + self.formIDInList = [f for f in self.formIDInList if + f.mod_id in modSet] + + def mergeWith(self,other,otherMod): + """Merges newLevl settings and entries with self. + Requires that: self.items, other.de_records be defined.""" + #--Remove items based on other.removes + if other.de_records: + removeItems = self.items & other.de_records + self.formIDInList = [fi for fi in self.formIDInList + if fi not in removeItems] + self.items |= other.de_records + #--Add new items from other + newItems = set() + formIDInListAppend = self.formIDInList.append + newItemsAdd = newItems.add + for fi in other.formIDInList: + if fi not in self.items: + formIDInListAppend(fi) + newItemsAdd(fi) + if newItems: + self.items |= newItems + #--Is merged list different from other? (And thus written to patch.) + if len(self.formIDInList) != len(other.formIDInList): + self.mergeOverLast = True + else: + for selfEntry, otherEntry in zip(self.formIDInList, + other.formIDInList): + if selfEntry != otherEntry: + self.mergeOverLast = True + break + else: + self.mergeOverLast = False + if self.mergeOverLast: + self.mergeSources.append(otherMod) + else: + self.mergeSources = [otherMod] + self.setChanged() + +#------------------------------------------------------------------------------ +class MreGlob(MelRecord): + """Global.""" + rec_sig = b'GLOB' + + melSet = MelSet( + MelEdid(), + MelFixedString(b'FNAM', u'global_format', 1, u's'), + # Rather stupidly all values, despite their designation (short, long, + # float), are stored as floats -- which means that very large integers + # lose precision + MelFloat(b'FLTV', u'global_value'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreLand(MelRecord): + """Land.""" + rec_sig = b'LAND' + + melSet = MelSet( + MelBase(b'DATA', u'unknown'), + MelBase(b'VNML', u'vertex_normals'), + MelBase(b'VHGT', u'vertex_height_map'), + MelBase(b'VCLR', u'vertex_colors'), + MelSorted(MelGroups(u'layers', + # Start a new layer each time we hit one of these + MelUnion({ + b'ATXT': MelStruct(b'ATXT', [u'I', u'B', u's', u'h'], (FID, u'atxt_texture'), + u'quadrant', u'unknown', u'layer'), + b'BTXT': MelStruct(b'BTXT', [u'I', u'B', u's', u'h'], (FID, u'btxt_texture'), + u'quadrant', u'unknown', u'layer'), + }), + # VTXT only exists for ATXT layers, i.e. if ATXT's FormID is valid + MelUnion({ + True: MelBase(b'VTXT', u'alpha_layer_data'), # sorted + False: MelNull(b'VTXT'), + }, decider=FidNotNullDecider(u'atxt_texture')), + ), sort_by_attrs=(u'quadrant', u'layer')), + MelArray(u'vertex_textures', + MelFid(b'VTEX', u'vertex_texture'), + ), + ) + __slots__ = melSet.getSlotsUsed() diff --git a/Mopy/bash/brec/common_subrecords.py b/Mopy/bash/brec/common_subrecords.py index c5bf8e8008..2cc928d9e3 100644 --- a/Mopy/bash/brec/common_subrecords.py +++ b/Mopy/bash/brec/common_subrecords.py @@ -32,25 +32,56 @@ MelFloat, MelReadOnly, MelFids, MelUInt32Flags, MelUInt8Flags, MelSInt32, \ MelStrings, MelUInt8, MelUInt16Flags from .utils_constants import int_unpacker, FID, null1, ZERO_FID -from ..bolt import Flags, encode, struct_pack, struct_unpack, unpack_byte, \ - dict_sort, TrimmedFlags, structs_cache +from ..bolt import Flags, encode, struct_pack, dict_sort, TrimmedFlags, \ + structs_cache from ..exception import ModError, ModSizeError +#------------------------------------------------------------------------------ +class _MelCoed(MelOptStruct): + """Handles the COED (Owner Data) subrecord used for inventory items and + leveled lists since FO3.""" + ##: Needs custom unpacker to look at FormID type of owner. If owner is an + # NPC then it is followed by a FormID. If owner is a faction then it is + # followed by an signed integer or '=Iif' instead of '=IIf' - see #282 + def __init__(self): + super().__init__(b'COED', ['2I', 'f'], (FID, 'owner'), + (FID, 'glob'), 'itemCondition') + +#------------------------------------------------------------------------------ +class AMelItems(MelSequential): + """Base class for handling the CNTO (Items) subrecords defining items. Can + handle all games since Oblivion via the two kwargs.""" + def __init__(self, *, with_coed=True, with_counter=True): + items_elements = [MelStruct(b'CNTO', ['I', 'i'], (FID, 'item'), + 'count')] + items_sort_attrs = ('item', 'count') + if with_coed: + items_elements.append(_MelCoed()) + items_sort_attrs += ('itemCondition', 'owner', 'glob') + final_elements = [MelSorted(MelGroups('items', *items_elements), + sort_by_attrs=items_sort_attrs)] + if with_counter: + final_elements.insert(0, MelCounter( + MelUInt32(b'COCT', 'item_count'), counts='items')) + super().__init__(*final_elements) + #------------------------------------------------------------------------------ class AMelLLItems(MelSequential): - """Base class for handling the LVLO and LLCT subrecords defining leveled - list items in Skyrim and newer games.""" - def __init__(self, lvl_elements: list, with_coed=True): - final_elements = lvl_elements.copy() - final_sort_attrs = ('level', 'listId', 'count') + """Base class for handling the LVLO (and LLCT) subrecords defining leveled + list items. Can handle all games since Oblivion via the two kwargs.""" + def __init__(self, lvl_element: MelBase, *, with_coed=True, + with_counter=True): + lvl_elements = [lvl_element] + lvl_sort_attrs = ('level', 'listId', 'count') if with_coed: - final_elements.append(MelCoed()) - final_sort_attrs += ('itemCondition', 'owner', 'glob') - super().__init__( - MelCounter(MelUInt8(b'LLCT', 'entry_count'), counts='entries'), - MelSorted(MelGroups('entries', *final_elements), - sort_by_attrs=final_sort_attrs), - ) + lvl_elements.append(_MelCoed()) + lvl_sort_attrs += ('itemCondition', 'owner', 'glob') + final_elements = [MelSorted(MelGroups('entries', *lvl_elements), + sort_by_attrs=lvl_sort_attrs)] + if with_counter: + final_elements.insert(0, MelCounter( + MelUInt8(b'LLCT', 'entry_count'), counts='entries')) + super().__init__(*final_elements) #------------------------------------------------------------------------------ class MelActiFlags(MelUInt16Flags): @@ -269,15 +300,42 @@ def __init__(self): ) #------------------------------------------------------------------------------ -class MelCoed(MelOptStruct): - """Handles the COED (Owner Data) subrecord used for inventory items and - leveled lists since Skyrim.""" - ##: Needs custom unpacker to look at FormID type of owner. If owner is an - # NPC then it is followed by a FormID. If owner is a faction then it is - # followed by an signed integer or '=Iif' instead of '=IIf' - see #282 +class MelClmtTiming(MelStruct): + """Handles the CLMT subrecord TNAM (Timing).""" def __init__(self): - super().__init__(b'COED', ['I', 'I', 'f'], (FID, 'owner'), - (FID, 'glob'), 'itemCondition') + super().__init__(b'TNAM', ['6B'], 'rise_begin', 'rise_end', + 'set_begin', 'set_end', 'volatility', 'phase_length') + +#------------------------------------------------------------------------------ +class MelClmtTextures(MelSequential): + """Handles the CLMT subrecords FNAM and GNAM.""" + def __init__(self): + super().__init__( + MelString(b'FNAM', 'sun_texture'), + MelString(b'GNAM', 'sun_glare_texture'), + ) + +#------------------------------------------------------------------------------ +class MelClmtWeatherTypes(MelSorted): + """Handles the CLMT subrecord WLST (Weather Types).""" + def __init__(self, with_global=True): + weather_fmt = ['I', 'i'] + weather_elements = [(FID, 'weather'), 'chance'] + if with_global: + weather_fmt.append('I') + weather_elements.append((FID, 'global')) + super().__init__(MelArray('weather_types', + MelStruct(b'WLST', weather_fmt, *weather_elements), + ), sort_by_attrs='weather') + +#------------------------------------------------------------------------------ +class MelCobjOutput(MelSequential): + """Handles the COBJ subrecords CNAM and BNAM.""" + def __init__(self): + super().__init__( + MelFid(b'CNAM', 'created_object'), + MelFid(b'BNAM', 'workbench_keyword'), + ) #------------------------------------------------------------------------------ class MelColor(MelStruct): @@ -303,29 +361,55 @@ def __init__(self, color_sig=b'CNAM'): super().__init__(color_sig, ['4B'], 'red', 'green', 'blue', 'unused_alpha') +#------------------------------------------------------------------------------ +class MelContData(MelStruct): + """Handles the CONT subrecord DATA (Data).""" + # Flags 1 & 3 introduced in Skyrim, treat as unknown for earlier games + _cont_flags = Flags.from_names('allow_sounds_when_animation', + 'cont_respawns', 'show_owner') + + def __init__(self): + super().__init__(b'DATA', ['B', 'f'], (self._cont_flags, 'cont_flags'), + 'cont_weight') + +#------------------------------------------------------------------------------ +class MelCpthShared(MelSequential): + """Handles the CPTH subrecords ANAM, DATA and SNAM. Identical between all + games' CPTH records.""" + def __init__(self): + super().__init__( + MelSimpleArray('related_camera_paths', MelFid(b'ANAM')), + MelUInt8(b'DATA', 'camera_zoom'), + MelFids('camera_shots', MelFid(b'SNAM')), + ), + #------------------------------------------------------------------------------ class MelDebrData(MelStruct): + """Handles the DEBR subrecord DATA (Data).""" + _debr_flags = Flags.from_names('has_collision_data', 'collision') + def __init__(self): # Format doesn't matter, struct.Struct('') works! ##: MelStructured - super().__init__(b'DATA', [], 'percentage', ('modPath', null1), - 'flags') + super().__init__(b'DATA', [], 'debr_percentage', ('modPath', null1), + (self._debr_flags, 'debr_flags')) @staticmethod def _expand_formats(elements, struct_formats): return [0] * len(elements) - def load_mel(self, record, ins, sub_type, size_, *debug_strs): + def load_mel(self, record, ins, sub_type, size_, *debug_strs, + __unpack_byte=structs_cache['B'].unpack): byte_data = ins.read(size_, *debug_strs) - record.percentage = unpack_byte(ins, byte_data[0:1])[0] + record.debr_percentage = __unpack_byte(byte_data[0:1])[0] record.modPath = byte_data[1:-2] - if byte_data[-2] != null1: + if byte_data[-2:-1] != null1: raise ModError(ins.inName, f'Unexpected subrecord: {debug_strs}') - record.flags = struct_unpack('B', byte_data[-1])[0] + record.debr_flags = self._debr_flags(__unpack_byte(byte_data[-1:])[0]) def pack_subrecord_data(self, record): return b''.join( - [struct_pack('B', record.percentage), record.modPath, null1, - struct_pack('B', record.flags)]) + [struct_pack('B', record.debr_percentage), record.modPath, null1, + struct_pack('B', record.debr_flags.dump())]) #------------------------------------------------------------------------------ class MelDecalData(MelOptStruct): @@ -349,6 +433,33 @@ class MelDescription(MelLString): def __init__(self): super().__init__(b'DESC', 'description') +#------------------------------------------------------------------------------ +class MelDoorFlags(MelUInt8Flags): + _door_flags = Flags.from_names( + 'oblivion_gate' # Oblivion only + 'automatic', + 'hidden', + 'minimal_use', + 'sliding_door', # since FO3 + 'do_not_open_in_combat_search', # since Skyrim + 'no_to_text', # since FO4 + ) + + def __init__(self): + super().__init__(b'FNAM', 'door_flags', self._door_flags) + +#------------------------------------------------------------------------------ +class MelDualData(MelStruct): + """Handles the DUAL subrecord DATA (Data).""" + _inherit_scale_flags = Flags.from_names('hit_effect_art_scale', + 'projectile_scale', 'explosion_scale') + + def __init__(self): + super().__init__(b'DATA', ['6I'], (FID, 'projectile'), + (FID, 'explosion'), (FID, 'effect_shader'), + (FID, 'hit_effect_art'), (FID, 'impact_data_set'), + (self._inherit_scale_flags, 'inherit_scale_flags')), + #------------------------------------------------------------------------------ class MelEdid(MelString): """Handles an Editor ID (EDID) subrecord.""" @@ -445,6 +556,12 @@ class MelIco2(MelIcons2): def __init__(self, ico2_attr): super().__init__(ico2_attr=ico2_attr, mic2_attr='') +#------------------------------------------------------------------------------ +class MelImageSpaceMod(MelFid): + """Handles the common MNAM (Image Space Modifer) subrecord.""" + def __init__(self): + super().__init__(b'MNAM', 'image_space_modifier') + #------------------------------------------------------------------------------ class MelInteractionKeyword(MelFid): """Handles the KNAM (Interaction Keyword) subrecord of ACTI records.""" @@ -459,8 +576,7 @@ def __init__(self): #------------------------------------------------------------------------------ class MelKeywords(MelSequential): - """Wraps MelSequential for the common task of defining a list of keywords - and a corresponding counter.""" + """Handles the KSIZ/KWDA (Keywords) subrecords.""" def __init__(self): super().__init__( MelCounter(MelUInt32(b'KSIZ', 'keyword_count'), counts='keywords'), @@ -717,6 +833,12 @@ def pack_subrecord_data(self, record, *, __zero_fid=ZERO_FID): return super(MelRaceVoices, self).pack_subrecord_data(record) return None +#------------------------------------------------------------------------------ +class MelRandomTeleports(MelSorted): + """Handles the DOOR subrecord TNAM (Random Teleport Destinations).""" + def __init__(self): + super().__init__(MelFids('random_teleports', MelFid(b'TNAM'))) + #------------------------------------------------------------------------------ class MelRef3D(MelOptStruct): """3D position and rotation for a reference record (REFR, ACHR, etc.).""" @@ -820,17 +942,29 @@ def __init__(self, element): False: element, }, decider=FlagDecider('flags', ['isInterior'])) +#------------------------------------------------------------------------------ +class MelSound(MelFid): + """Handles the common SNAM (Sound) subrecord.""" + def __init__(self): + super().__init__(b'SNAM', 'sound') + #------------------------------------------------------------------------------ class MelSoundActivation(MelFid): - """Handles the VNAM (Sound - Activation) subrecord in ACTI records.""" + """Handles the ACTI subrecord VNAM (Sound - Activation).""" def __init__(self): super().__init__(b'VNAM', 'soundActivation') #------------------------------------------------------------------------------ -class MelSound(MelFid): - """Handles the common SNAM (Sound) subrecord.""" +class MelSoundClose(MelFid): + """Handles the CONT/DOOR subrecord QNAM/ANAM (Sound - Close).""" + def __init__(self, sc_sig=b'QNAM'): + super().__init__(sc_sig, 'sound_close') + +#------------------------------------------------------------------------------ +class MelSoundLooping(MelFid): + """Handles the DOOR subrecord BNAM (Sound - Looping).""" def __init__(self): - super().__init__(b'SNAM', 'sound') + super().__init__(b'BNAM', 'sound_looping') #------------------------------------------------------------------------------ class MelSoundPickupDrop(MelSequential): @@ -884,19 +1018,6 @@ class MelWaterType(MelFid): def __init__(self): super().__init__(b'WNAM', 'water_type') -#------------------------------------------------------------------------------ -class MelWeatherTypes(MelSorted): - """Handles the CLMT subrecord WLST (Weather Types).""" - def __init__(self, with_global=True): - weather_fmt = ['I', 'i'] - weather_elements = [(FID, 'weather'), 'chance'] - if with_global: - weather_fmt.append('I') - weather_elements.append((FID, 'global')) - super().__init__(MelArray('weather_types', - MelStruct(b'WLST', weather_fmt, *weather_elements), - ), sort_by_attrs='weather') - #------------------------------------------------------------------------------ class MelWeight(MelFloat): """Handles a common variant of the DATA subrecord that consists of a single diff --git a/Mopy/bash/brec/mod_io.py b/Mopy/bash/brec/mod_io.py index e1855c3b16..28d29f3631 100644 --- a/Mopy/bash/brec/mod_io.py +++ b/Mopy/bash/brec/mod_io.py @@ -60,6 +60,9 @@ class RecordHeader(object): valid_header_sigs = set() #--Plugin form version, we must pack this in the TES4 header plugin_form_version = 0 + # A set of record types for which to skip upgrading to the latest Form + # Version, usually because it's impossible + skip_form_version_upgrade = set() is_top_group_header = False __slots__ = (u'recType', u'size', u'extra') @@ -98,11 +101,13 @@ def pack_head(self, __rh=RecordHeader): pack_args = [__rh.rec_pack_format_str, self.recType, self.size, self.flags1, self.fid.dump(), self.flags2] if __rh.plugin_form_version: - extra1, extra2 = struct_unpack(u'=2h', - struct_pack(u'=I', self.extra)) - extra1 = __rh.plugin_form_version - self.extra = \ - struct_unpack(u'=I', struct_pack(u'=2h', extra1, extra2))[0] + # Upgrade to latest form version unless we were told to skip that + if self.recType not in __rh.skip_form_version_upgrade: + extra1, extra2 = struct_unpack('=2h', struct_pack( + '=I', self.extra)) + extra1 = __rh.plugin_form_version + self.extra = struct_unpack('=I', struct_pack( + '=2h', extra1, extra2))[0] pack_args.append(self.extra) return struct_pack(*pack_args) diff --git a/Mopy/bash/brec/record_structs.py b/Mopy/bash/brec/record_structs.py index 0135c812a8..9e5c0fe70c 100644 --- a/Mopy/bash/brec/record_structs.py +++ b/Mopy/bash/brec/record_structs.py @@ -392,7 +392,7 @@ def getTypeCopy(self): myCopy.data = None return myCopy - def mergeFilter(self,modSet): + def mergeFilter(self, modSet): """This method is called by the bashed patch mod merger. The intention is to allow a record to be filtered according to the specified modSet. E.g. for a list record, items coming from mods not diff --git a/Mopy/bash/game/enderal/__init__.py b/Mopy/bash/game/enderal/__init__.py index aa6163e979..56acdf239f 100644 --- a/Mopy/bash/game/enderal/__init__.py +++ b/Mopy/bash/game/enderal/__init__.py @@ -24,8 +24,6 @@ active game.""" from ..skyrim import SkyrimGameInfo -from ... import brec -from ...brec import MreFlst, MreGlob class EnderalGameInfo(SkyrimGameInfo): displayName = u'Enderal' @@ -121,25 +119,25 @@ class Bain(SkyrimGameInfo.Bain): _patcher_package = 'bash.game.enderal' # We need to override tweaks @classmethod def init(cls): - # Copy-pasted from Skyrim cls._dynamic_import_modules(__name__) + from ...brec import MreColl, MreDebr, MreDlbr, MreDlvw, MreFlst, \ + MreGlob from ..skyrim.records import MreCell, MreWrld, MreFact, MreAchr, \ MreInfo, MreCams, MreWthr, MreDual, MreMato, MreVtyp, MreMatt, \ - MreLvsp, MreEnch, MreProj, MreDlbr, MreRfct, MreMisc, MreActi, \ + MreLvsp, MreEnch, MreProj, MreRace, MreRfct, MreMisc, MreActi, \ MreEqup, MreCpth, MreDoor, MreAnio, MreHazd, MreIdlm, MreEczn, \ MreIdle, MreLtex, MreQust, MreMstt, MreNpc, MreIpds, MrePack, \ - MreGmst, MreRevb, MreClmt, MreDebr, MreSmbn, MreLvli, MreSpel, \ + MreGmst, MreRevb, MreClmt, MreDial, MreSmbn, MreLvli, MreSpel, \ MreKywd, MreLvln, MreAact, MreSlgm, MreRegn, MreFurn, MreGras, \ - MreAstp, MreWoop, MreMovt, MreCobj, MreShou, MreSmen, MreColl, \ + MreAstp, MreWoop, MreMovt, MreCobj, MreShou, MreSmen, MreNavm, \ MreArto, MreAddn, MreSopm, MreCsty, MreAppa, MreArma, MreArmo, \ MreKeym, MreTxst, MreHdpt, MreTes4, MreAlch, MreBook, MreSpgd, \ MreSndr, MreImgs, MreScrl, MreMust, MreFstp, MreFsts, MreMgef, \ MreLgtm, MreMusc, MreClas, MreLctn, MreTact, MreBptd, MreDobj, \ - MreLscr, MreDlvw, MreTree, MreWatr, MreFlor, MreEyes, MreWeap, \ + MreLscr, MrePerk, MreTree, MreWatr, MreFlor, MreEyes, MreWeap, \ MreIngr, MreClfm, MreMesg, MreLigh, MreExpl, MreLcrt, MreStat, \ MreAmmo, MreSmqn, MreImad, MreSoun, MreAvif, MreCont, MreIpct, \ - MreAspc, MreRela, MreEfsh, MreSnct, MreOtft, MrePerk, MreRace, \ - MreDial, MreNavm + MreAspc, MreRela, MreEfsh, MreSnct, MreOtft cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in (# MreAchr, MreDial, MreInfo, MreAact, MreActi, MreAddn, MreAlch, MreAmmo, MreAnio, MreAppa, MreArma, MreArmo, MreArto, MreAspc, MreAstp, MreAvif, MreBook, @@ -159,6 +157,7 @@ def init(cls): MreRace, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'KYWD', b'LCRT', b'AACT', b'TXST', b'GLOB', b'CLAS', diff --git a/Mopy/bash/game/enderalse/__init__.py b/Mopy/bash/game/enderalse/__init__.py index c86e12483f..2cc7e3d9ee 100644 --- a/Mopy/bash/game/enderalse/__init__.py +++ b/Mopy/bash/game/enderalse/__init__.py @@ -25,8 +25,6 @@ from ..enderal import EnderalGameInfo from ..skyrimse import SkyrimSEGameInfo -from ... import brec -from ...brec import MreFlst, MreGlob # We want the final chain of attribute lookups to be Enderal SE -> Enderal LE # -> Skyrim SE -> Skyrim LE -> Defaults, i.e. the narrower overrides first @@ -100,16 +98,15 @@ class Bain(EnderalGameInfo.Bain): _patcher_package = 'bash.game.enderalse' # We need to override tweaks @classmethod def init(cls): - # Copy-pasted from Skyrim cls._dynamic_import_modules(__name__) - # First import from skyrimse.records file + from ...brec import MreColl, MreDebr, MreDlbr, MreDlvw, MreFlst, \ + MreGlob from ..skyrimse.records import MreVoli, MreLens - # then import rest of records from skyrim.records from ..skyrim.records import MreAact, MreAchr, MreActi, MreAddn, \ MreAlch, MreAnio, MreAppa, MreArma, MreArmo, MreArto, MreAspc, \ MreAstp, MreAvif, MreBook, MreBptd, MreCams, MreCell, MreClas, \ - MreClfm, MreClmt, MreCobj, MreColl, MreCont, MreCpth, MreCsty, \ - MreDebr, MreDial, MreDlbr, MreDlvw, MreDobj, MreDoor, MreDual, \ + MreClfm, MreClmt, MreCobj, MreNavm, MreCont, MreCpth, MreCsty, \ + MreRace, MreDial, MreWthr, MreWeap, MreDobj, MreDoor, MreDual, \ MreEczn, MreEfsh, MreEnch, MreEqup, MreExpl, MreEyes, MreFact, \ MreFlor, MreFstp, MreFsts, MreFurn, MreGmst, MreGras, MrePack, \ MreHazd, MreHdpt, MreTes4, MreIdle, MreIdlm, MreImad, MreImgs, \ @@ -120,8 +117,7 @@ def init(cls): MreRela, MreRevb, MreRfct, MreScrl, MreShou, MreSlgm, MreSmbn, \ MreSmen, MreSmqn, MreSnct, MreSndr, MreSopm, MreSoun, MreSpel, \ MreSpgd, MreTact, MreTree, MreTxst, MreVtyp, MreWoop, MreWrld, \ - MreAmmo, MreLtex, MreMato, MreStat, MreWatr, MreWeap, MreWthr, \ - MreRace, MreNavm + MreAmmo, MreLtex, MreMato, MreStat, MreWatr cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in ( # MreAchr, MreDial, MreInfo, MreAact, MreActi, MreAddn, MreAlch, MreAmmo, MreAnio, MreAppa, @@ -142,6 +138,7 @@ def init(cls): MrePack, MreFact, MreRace, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'KYWD', b'LCRT', b'AACT', b'TXST', b'GLOB', b'CLAS', diff --git a/Mopy/bash/game/fallout3/__init__.py b/Mopy/bash/game/fallout3/__init__.py index d4ae37400b..9815ad1efc 100644 --- a/Mopy/bash/game/fallout3/__init__.py +++ b/Mopy/bash/game/fallout3/__init__.py @@ -25,8 +25,7 @@ from os.path import join as _j from ..patch_game import GameInfo, PatchGame -from ... import brec, bolt -from ...brec import MreFlst, MreGlob +from ... import bolt class Fallout3GameInfo(PatchGame): displayName = u'Fallout 3' @@ -296,10 +295,11 @@ class Esp(GameInfo.Esp): @classmethod def init(cls): cls._dynamic_import_modules(__name__) + from ...brec import MreDebr, MreFlst, MreGlob from .records import MreActi, MreAddn, MreAlch, MreAmmo, MreAnio, \ MreArma, MreArmo, MreAspc, MreAvif, MreBook, MreBptd, MreCams, \ MreClas, MreClmt, MreCobj, MreCont, MreCpth, MreCrea, MreCsty, \ - MreDebr, MreDobj, MreDoor, MreEczn, MreEfsh, MreEnch, MreExpl, \ + MreWrld, MreDobj, MreDoor, MreEczn, MreEfsh, MreEnch, MreExpl, \ MreEyes, MreFact, MreFurn, MreGras, MreHair, MreHdpt, MreTes4, \ MreIdle, MreIdlm, MreImad, MreImgs, MreIngr, MreIpct, MreIpds, \ MreKeym, MreLgtm, MreLigh, MreLscr, MreLtex, MreLvlc, MreLvli, \ @@ -308,8 +308,7 @@ def init(cls): MreRace, MreRads, MreRegn, MreRgdl, MreScol, MreScpt, MreSoun, \ MreSpel, MreStat, MreTact, MreTerm, MreTree, MreTxst, MreVtyp, \ MreWatr, MreWeap, MreWthr, MreAchr, MreAcre, MreCell, MreDial, \ - MreGmst, MreInfo, MreNavi, MreNavm, MrePgre, MrePmis, MreRefr, \ - MreWrld + MreGmst, MreInfo, MreNavi, MreNavm, MrePgre, MrePmis, MreRefr cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in ( MreActi, MreAddn, MreAlch, MreAmmo, MreAnio, MreArma, MreArmo, MreAspc, MreAvif, MreBook, MreBptd, MreCams, MreClas, MreClmt, @@ -325,6 +324,7 @@ def init(cls): MreWeap, MreWthr, MreGmst, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'TXST', b'MICN', b'GLOB', b'CLAS', b'FACT', b'HDPT', diff --git a/Mopy/bash/game/fallout3/patcher/__init__.py b/Mopy/bash/game/fallout3/patcher/__init__.py index 8280498c4e..db8d3e1dc3 100644 --- a/Mopy/bash/game/fallout3/patcher/__init__.py +++ b/Mopy/bash/game/fallout3/patcher/__init__.py @@ -342,9 +342,9 @@ b'ARMO': (u'pickupSound', u'dropSound'), b'ASPC': ('sound', 'use_sound_from_region'), b'COBJ': (u'pickupSound', u'dropSound'), - b'CONT': ('sound', 'soundClose'), + b'CONT': ('sound', 'sound_close'), b'CREA': (u'footWeight', u'inheritsSoundsFrom', u'sounds'), - b'DOOR': ('sound', 'soundClose', 'soundLoop'), + b'DOOR': ('sound', 'sound_close', 'sound_looping'), b'EXPL': (u'soundLevel', u'sound1', u'sound2'), b'IPCT': ('soundLevel', 'sound', 'sound2'), b'LIGH': (u'sound',), @@ -480,7 +480,7 @@ graphicsFidTypes = { b'CREA': ('bodyPartData',), b'EFSH': (u'addonModels',), - b'EXPL': ('imageSpaceModifier', 'light', 'impactDataset', + b'EXPL': ('image_space_modifier', 'light', 'impactDataset', 'placedImpactObject'), b'IPCT': (u'textureSet',), b'IPDS': (u'stone', u'dirt', u'grass', u'metal', u'wood', u'organic', diff --git a/Mopy/bash/game/fallout3/records.py b/Mopy/bash/game/fallout3/records.py index 53c0c1b816..8df1e64d88 100644 --- a/Mopy/bash/game/fallout3/records.py +++ b/Mopy/bash/game/fallout3/records.py @@ -28,8 +28,8 @@ from ... import bush from ...bolt import Flags, structs_cache, TrimmedFlags from ...brec import MelRecord, MelGroups, MelStruct, FID, MelGroup, \ - MelString, MelSet, MelFid, MelOptStruct, MelFids, MreHeaderBase, MelRace, \ - MelBase, MelSimpleArray, MreGmstBase, MelBodyParts, MelMODS, MelFactions, \ + MelString, MelSet, MelFid, MelOptStruct, MelFids, AMreHeader, MelRace, \ + MelBase, MelSimpleArray, AMreGmst, MelBodyParts, MelMODS, MelFactions, \ MelReferences, MelColorInterpolator, MelValueInterpolator, MelAnimations, \ MelUnion, AttrValDecider, MelRegnEntrySubrecord, SizeDecider, MelFloat, \ MelSInt8, MelSInt16, MelSInt32, MelUInt8, MelUInt16, MelUInt32, \ @@ -37,17 +37,19 @@ MelRaceVoices, MelBounds, null1, null2, MelScriptVars, MelSorted, \ MelSequential, MelTruncatedStruct, PartialLoadDecider, MelReadOnly, \ MelSkipInterior, MelIcons, MelIcons2, MelIcon, MelIco2, MelEdid, MelFull, \ - MelArray, MelWthrColors, MreLeveledListBase, MreActorBase, MreWithItems, \ + MelArray, MelWthrColors, AMreLeveledList, AMreActor, AMreWithItems, \ MelRef3D, MelXlod, MelNull, MelWorldBounds, MelEnableParent, MelPerkData, \ MelRefScale, MelMapMarker, MelActionFlags, MelEnchantment, MelScript, \ MelDecalData, MelDescription, MelLists, MelSoundPickupDrop, MelBookText, \ MelActivateParents, BipedFlags, MelSpells, MelUInt8Flags, MelUInt16Flags, \ - MelUInt32Flags, MelOwnership, MelDebrData, MelRaceData, MelRegions, \ - MelWeatherTypes, MelFactionRanks, perk_effect_key, MelLscrLocations, \ + MelUInt32Flags, MelOwnership, MelRaceData, MelRegions, MelDoorFlags, \ + MelClmtWeatherTypes, MelFactionRanks, perk_effect_key, MelLscrLocations, \ MelReflectedRefractedBy, MelValueWeight, SpellFlags, MelBaseR, MelExtra, \ MelSound, MelSoundActivation, MelWaterType, MelConditionsFo3, \ MelNodeIndex, MelAddnDnam, MelEffectsFo3, MelShortName, PerkEpdfDecider, \ - MelPerkParamsGroups, MelAspcRdat, MelUnorderedGroups + MelPerkParamsGroups, MelUnorderedGroups, MelImageSpaceMod, MelAspcRdat, \ + MelSoundClose, AMelItems, AMelLLItems, MelContData, MelCpthShared, \ + MelSoundLooping from ...exception import ModSizeError _is_fnv = bush.game.fsName == u'FalloutNV' @@ -121,7 +123,7 @@ def __init__(self): super().__init__(b'XATO', 'activation_prompt') #------------------------------------------------------------------------------ -class MreActor(MreActorBase): +class MreActor(AMreActor): """Creatures and NPCs.""" TemplateFlags = Flags.from_names( 'useTraits', @@ -216,14 +218,10 @@ def __init__(self): super().__init__(b'ETYP', 'equipment_type', -1) #------------------------------------------------------------------------------ -class MelItems(MelSorted): - """Wraps MelGroups for the common task of defining a list of items.""" +class MelItems(AMelItems): + """Handles the CNTO/COED subrecords defining items.""" def __init__(self): - super(MelItems, self).__init__(MelGroups(u'items', - MelStruct(b'CNTO', [u'I', u'i'], (FID, u'item'), (u'count', 1)), - MelOptStruct(b'COED', [u'2I', u'f'], (FID, u'owner'), (FID, u'glob'), - (u'condition', 1.0)), - ), sort_by_attrs=('item', 'count', 'condition', 'owner', 'glob')) + super().__init__(with_counter=False) #------------------------------------------------------------------------------ class MelLevListLvld(MelUInt8): @@ -251,6 +249,14 @@ def __init__(self): MelStruct(b'XDCR', ['2I'], (FID, 'reference'), 'unknown'), ), sort_by_attrs='reference') +#------------------------------------------------------------------------------ +class MelLLItems(AMelLLItems): + """Handles the LVLO/COED subrecords defining leveled list entries.""" + def __init__(self): + super().__init__(MelLevListLvlo(b'LVLO', ['h', '2s', 'I', 'h', '2s'], + 'level', 'unused1', (FID, 'listId'), ('count', 1), 'unused2', + old_versions={'iI'}), with_counter=False) + #------------------------------------------------------------------------------ class MelRaceHeadPart(MelGroup): """Implements special handling for ears, which can only contain an icon @@ -284,7 +290,14 @@ def dumpData(self, record, out): super(MelRaceHeadPart, self).dumpData(record, out) #------------------------------------------------------------------------------ -class MreLeveledList(MreLeveledListBase): +class MelSoundRandomLooping(MelFid): + """Handles the common RNAM (Sound - Random/Looping) subrecord introduced in + FNV.""" + def __init__(self): + super().__init__(b'RNAM', 'sound_random_looping') + +#------------------------------------------------------------------------------ +class MreLeveledList(AMreLeveledList): """Leveled item/creature/spell list..""" top_copy_attrs = (u'chanceNone', u'glob') entry_copy_attrs = (u'listId', u'level', u'count', u'owner', u'condition') @@ -293,16 +306,9 @@ class MreLeveledList(MreLeveledListBase): MelEdid(), MelBounds(), MelLevListLvld(b'LVLD', u'chanceNone'), - MelUInt8Flags(b'LVLF', u'flags', MreLeveledListBase._flags), + MelUInt8Flags(b'LVLF', u'flags', AMreLeveledList._flags), MelFid(b'LVLG', u'glob'), - MelSorted(MelGroups(u'entries', - MelLevListLvlo(b'LVLO', [u'h', u'2s', u'I', u'h', u'2s'], u'level', - u'unused1', (FID, u'listId'), (u'count', 1), - u'unused2', old_versions={u'iI'}), - MelOptStruct(b'COED', [u'2I', u'f'], (FID, u'owner'), (FID, u'glob'), - (u'condition', 1.0)), - ), sort_by_attrs=('level', 'listId', 'count', 'condition', 'owner', - 'glob')), + MelLLItems(), MelModel(), ) __slots__ = melSet.getSlotsUsed() @@ -310,7 +316,7 @@ class MreLeveledList(MreLeveledListBase): #------------------------------------------------------------------------------ # Fallout3 Records ------------------------------------------------------------ #------------------------------------------------------------------------------ -class MreTes4(MreHeaderBase): +class MreTes4(AMreHeader): """TES4 Record. File header.""" rec_sig = b'TES4' _post_masters_sigs = {b'ONAM', b'SCRN'} @@ -320,9 +326,9 @@ class MreTes4(MreHeaderBase): ('nextObject', 0x800)), MelNull(b'OFST'), # obsolete MelNull(b'DELE'), # obsolete - MreHeaderBase.MelAuthor(), - MreHeaderBase.MelDescription(), - MreHeaderBase.MelMasterNames(), + AMreHeader.MelAuthor(), + AMreHeader.MelDescription(), + AMreHeader.MelMasterNames(), MelSimpleArray('overrides', MelFid(b'ONAM')), MelBase(b'SCRN', 'screenshot'), ) @@ -585,7 +591,6 @@ class MreArmo(MelRecord): class MreAspc(MelRecord): """Acoustic Space.""" rec_sig = b'ASPC' - isKeyedByEid = True # NULL fids are acceptable melSet = MelSet( @@ -688,23 +693,18 @@ class MreCams(MelRecord): """Camera Shot.""" rec_sig = b'CAMS' - CamsFlagsFlags = Flags.from_names( - (0, 'positionFollowsLocation'), - (1, 'rotationFollowsTarget'), - (2, 'dontFollowBone'), - (3, 'firstPersonCamera'), - (4, 'noTracer'), - (5, 'startAtTimeZero'), - ) + _cams_flags = Flags.from_names('position_follows_location', + 'rotation_follows_target', 'dont_follow_bone', 'first_person_camera', + 'no_tracer', 'start_at_time_zero') melSet = MelSet( MelEdid(), MelModel(), - MelStruct(b'DATA', [u'4I', u'6f'],'action','location','target', - (CamsFlagsFlags, u'flags'),'timeMultPlayer', - 'timeMultTarget','timeMultGlobal','maxTime','minTime', - 'targetPctBetweenActors',), - MelFid(b'MNAM','imageSpaceModifier',), + MelStruct(b'DATA', ['4I', '6f'], 'cams_action', 'cams_location', + 'cams_target', (_cams_flags, 'cams_flags'), 'time_mult_player', + 'time_mult_target', 'time_mult_global', 'cams_max_time', + 'cams_min_time', 'target_pct_between_actors'), + MelImageSpaceMod(), ) __slots__ = melSet.getSlotsUsed() @@ -799,7 +799,7 @@ class MreClmt(MelRecord): melSet = MelSet( MelEdid(), - MelWeatherTypes(), + MelClmtWeatherTypes(), MelString(b'FNAM','sunPath'), MelString(b'GNAM','glarePath'), MelModel(), @@ -810,7 +810,7 @@ class MreClmt(MelRecord): #------------------------------------------------------------------------------ class MreCobj(MelRecord): - """Constructible Object (Recipes).""" + """Constructible Object.""" rec_sig = b'COBJ' melSet = MelSet( @@ -826,12 +826,10 @@ class MreCobj(MelRecord): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreCont(MreWithItems): +class MreCont(AMreWithItems): """Container.""" rec_sig = b'CONT' - _flags = Flags.from_names(None, 'respawns') - melSet = MelSet( MelEdid(), MelBounds(), @@ -840,10 +838,10 @@ class MreCont(MreWithItems): MelScript(), MelItems(), MelDestructible(), - MelStruct(b'DATA', [u'B', u'f'],(_flags, u'flags'),'weight'), + MelContData(), MelSound(), - MelFid(b'QNAM','soundClose'), - fnv_only(MelFid(b'RNAM', 'soundRandomLooping')), + MelSoundClose(), + fnv_only(MelSoundRandomLooping()), ) __slots__ = melSet.getSlotsUsed() @@ -855,9 +853,7 @@ class MreCpth(MelRecord): melSet = MelSet( MelEdid(), MelConditionsFo3(), - MelSimpleArray('relatedCameraPaths', MelFid(b'ANAM')), - MelUInt8(b'DATA', 'cameraZoom'), - MelFids('cameraShots', MelFid(b'SNAM')), + MelCpthShared(), ) __slots__ = melSet.getSlotsUsed() @@ -949,56 +945,45 @@ class MreCsty(MelRecord): """Combat Style.""" rec_sig = b'CSTY' - _flagsA = Flags.from_names( - (0, 'advanced'), - (1, 'useChanceForAttack'), - (2, 'ignoreAllies'), - (3, 'willYield'), - (4, 'rejectsYields'), - (5, 'fleeingDisabled'), - (6, 'prefersRanged'), - (7, 'meleeAlertOK'), - ) - - melSet = MelSet( - MelEdid(), - MelOptStruct(b'CSTD', [u'2B', u'2s', u'8f', u'2B', u'2s', u'3f', u'B', u'3s', u'2f', u'5B', u'3s', u'2f', u'H', u'2s', u'2B', u'2s', u'f'],'dodgeChance', - 'lrChance','unused1','lrTimerMin','lrTimerMax', - 'forTimerMin','forTimerMax','backTimerMin','backTimerMax', - 'idleTimerMin','idleTimerMax','blkChance','atkChance', - 'unused2','atkBRecoil','atkBunc','atkBh2h', - 'pAtkChance','unused3','pAtkBRecoil','pAtkBUnc', - 'pAtkNormal','pAtkFor','pAtkBack','pAtkL','pAtkR', - 'unused4','holdTimerMin','holdTimerMax', - (_flagsA,'flagsA'),'unused5','acroDodge', - ('rushChance',25),'unused6',('rushMult',1.0)), - MelOptStruct(b'CSAD', [u'21f'], 'dodgeFMult', 'dodgeFBase', 'encSBase', 'encSMult', - 'dodgeAtkMult', 'dodgeNAtkMult', 'dodgeBAtkMult', 'dodgeBNAtkMult', - 'dodgeFAtkMult', 'dodgeFNAtkMult', 'blockMult', 'blockBase', - 'blockAtkMult', 'blockNAtkMult', 'atkMult','atkBase', 'atkAtkMult', - 'atkNAtkMult', 'atkBlockMult', 'pAtkFBase', 'pAtkFMult'), - MelOptStruct(b'CSSD', [u'9f', u'4s', u'I', u'5f'], 'coverSearchRadius', 'takeCoverChance', - 'waitTimerMin', 'waitTimerMax', 'waitToFireTimerMin', - 'waitToFireTimerMax', 'fireTimerMin', 'fireTimerMax', - 'rangedWeaponRangeMultMin','unknown1','weaponRestrictions', - 'rangedWeaponRangeMultMax','maxTargetingFov','combatRadius', - 'semiAutomaticFireDelayMultMin','semiAutomaticFireDelayMultMax'), - ) - __slots__ = melSet.getSlotsUsed() - -#------------------------------------------------------------------------------ -class MreDebr(MelRecord): - """Debris.""" - rec_sig = b'DEBR' - - dataFlags = Flags.from_names(u'hasCollissionData') - - melSet = MelSet( - MelEdid(), - MelGroups(u'models', - MelDebrData(), - MelBase(b'MODT', u'modt_p'), - ), + _csty_flags = Flags.from_names( + 'advanced', + 'use_chance_for_attack', + 'ignore_allies', + 'will_yield', + 'rejects_yields', + 'fleeing_disabled', + 'prefers_ranged', + 'melee_alert_ok', + ) + + melSet = MelSet( + MelEdid(), + MelOptStruct(b'CSTD', + ['2B', '2s', '8f', '2B', '2s', '3f', 'B', '3s', '2f', '5B', '3s', + '2f', 'H', '2s', '2B', '2s', 'f'], 'dodge_chance', 'lr_chance', + 'unused1', 'lr_timer_min', 'lr_timer_max', 'for_timer_min', + 'for_timer_max', 'back_timer_min', 'back_timer_max', + 'idle_timer_min', 'idle_timer_max', 'blk_chance', 'atk_chance', + 'unused2', 'atk_brecoil', 'atk_bunc', 'atk_bh_2_h', 'p_atk_chance', + 'unused3', 'p_atk_brecoil', 'p_atk_bunc', 'p_atk_normal', + 'p_atk_for', 'p_atk_back', 'p_atk_l', 'p_atk_r', 'unused4', + 'hold_timer_min', 'hold_timer_max', (_csty_flags, 'csty_flags'), + 'unused5', 'acro_dodge', ('rush_chance', 25), 'unused6', + ('rush_mult', 1.0)), + MelOptStruct(b'CSAD', ['21f'], 'dodge_fmult', 'dodge_fbase', + 'enc_sbase', 'enc_smult', 'dodge_atk_mult', 'dodge_natk_mult', + 'dodge_batk_mult', 'dodge_bnatk_mult', 'dodge_fatk_mult', + 'dodge_fnatk_mult', 'block_mult', 'block_base', 'block_atk_mult', + 'block_natk_mult', 'atk_mult', 'atk_base', 'atk_atk_mult', + 'atk_natk_mult', 'atk_block_mult', 'p_atk_fbase', 'p_atk_fmult'), + MelOptStruct(b'CSSD', ['9f', '4s', 'I', '5f'], 'cover_search_radius', + 'take_cover_chance', 'wait_timer_min', 'wait_timer_max', + 'wait_to_fire_timer_min', 'wait_to_fire_timer_max', + 'fire_timer_min', 'fire_timer_max', 'ranged_weapon_range_mult_min', + 'unknown1', 'weapon_restrictions', 'ranged_weapon_range_mult_max', + 'max_targeting_fov', 'combat_radius', + 'semi_automatic_fire_delay_mult_min', + 'semi_automatic_fire_delay_mult_max'), ) __slots__ = melSet.getSlotsUsed() @@ -1056,13 +1041,6 @@ class MreDoor(MelRecord): """Door.""" rec_sig = b'DOOR' - _flags = Flags.from_names( - (1, 'automatic'), - (2, 'hidden'), - (3, 'minimalUse'), - (4, 'slidingDoor'), - ) - melSet = MelSet( MelEdid(), MelBounds(), @@ -1071,9 +1049,9 @@ class MreDoor(MelRecord): MelScript(), MelDestructible(), MelSound(), - MelFid(b'ANAM','soundClose'), - MelFid(b'BNAM','soundLoop'), - MelUInt8Flags(b'FNAM', u'flags', _flags), + MelSoundClose(b'ANAM'), + MelSoundLooping(), + MelDoorFlags(), ) __slots__ = melSet.getSlotsUsed() @@ -1190,7 +1168,7 @@ class MreExpl(MelRecord): MelFull(), MelModel(), MelEnchantment(), - MelFid(b'MNAM','imageSpaceModifier'), + MelImageSpaceMod(), MelStruct(b'DATA', [u'3f', u'3I', u'f', u'2I', u'3f', u'I'], u'force', u'damage', u'radius', (FID, u'light'), (FID, u'sound1'), (_flags, u'flags'), u'isRadius', (FID, u'impactDataset'), (FID, u'sound2'), @@ -1257,7 +1235,7 @@ class MreFurn(MelRecord): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreGmst(MreGmstBase): +class MreGmst(AMreGmst): """Game Setting.""" isKeyedByEid = True # NULL fids are acceptable. __slots__ = () @@ -1633,7 +1611,7 @@ class MreKeym(MelRecord): MelDestructible(), MelSoundPickupDrop(), MelValueWeight(), - fnv_only(MelFid(b'RNAM', 'soundRandomLooping')), + fnv_only(MelSoundRandomLooping()), ) __slots__ = melSet.getSlotsUsed() @@ -1836,7 +1814,7 @@ class MreMisc(MelRecord): MelDestructible(), MelSoundPickupDrop(), MelValueWeight(), - fnv_only(MelFid(b'RNAM', 'soundRandomLooping')), + fnv_only(MelSoundRandomLooping()), ) __slots__ = melSet.getSlotsUsed() @@ -2884,7 +2862,7 @@ class MreStat(MelRecord): MelBounds(), MelModel(), fnv_only(MelSInt8(b'BRUS', 'passthroughSound', -1)), - fnv_only(MelFid(b'RNAM', 'soundRandomLooping')), + fnv_only(MelSoundRandomLooping()), ) __slots__ = melSet.getSlotsUsed() diff --git a/Mopy/bash/game/fallout4/__init__.py b/Mopy/bash/game/fallout4/__init__.py index f848c4c617..f0e9db49f0 100644 --- a/Mopy/bash/game/fallout4/__init__.py +++ b/Mopy/bash/game/fallout4/__init__.py @@ -26,7 +26,7 @@ from ..patch_game import GameInfo, PatchGame from .. import WS_COMMON -from ... import brec, bolt +from ... import bolt class Fallout4GameInfo(PatchGame): displayName = u'Fallout 4' @@ -218,25 +218,31 @@ class Esp(GameInfo.Esp): @classmethod def init(cls): cls._dynamic_import_modules(__name__) + from ...brec import MreColl, MreDebr, MreDlbr, MreDlvw from .records import MreAact, MreActi, MreAddn, MreAech, MreAmdl, \ MreAnio, MreAoru, MreArma, MreArmo, MreArto, MreAstp, MreAvif, \ - MreBnds, MreBook, MreBptd, \ + MreBnds, MreBook, MreBptd, MreCams, MreClas, MreClfm, MreClmt, \ + MreCmpo, MreCobj, MreCont, MreCpth, MreCsty, MreDfob, MreDmgt, \ + MreDobj, MreDoor, MreDual, \ MreGmst, MreLvli, MreLvln, MrePerk, MreTes4 cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in ( MreAact, MreActi, MreAddn, MreAech, MreAmdl, MreAnio, MreAoru, MreArma, MreArmo, MreArto, MreAstp, MreAvif, MreBnds, MreBook, - MreBptd, + MreBptd, MreCams, MreClas, MreClfm, MreClmt, MreCmpo, MreCobj, + MreColl, MreCont, MreCpth, MreCsty, MreDebr, MreDfob, MreDlbr, + MreDlvw, MreDmgt, MreDobj, MreDoor, MreDual, MreGmst, MreLvli, MreLvln, MrePerk, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'KYWD', b'LCRT', b'AACT', b'TRNS', b'CMPO', b'TXST', - b'GLOB', b'DMGT', b'CLAS', b'FACT', b'HDPT', b'RACE', b'SOUN', - b'ASPC', b'MGEF', b'LTEX', b'ENCH', b'SPEL', b'ACTI', b'TACT', - b'ARMO', b'BOOK', b'CONT', b'DOOR', b'INGR', b'LIGH', b'MISC', - b'STAT', b'SCOL', b'MSTT', b'GRAS', b'TREE', b'FLOR', b'FURN', - b'WEAP', b'AMMO', b'NPC_', b'PLYR', b'LVLN', b'KEYM', b'ALCH', + b'GLOB', b'DMGT', b'CLAS', b'FACT', b'HDPT', b'EYES', b'RACE', + b'SOUN', b'ASPC', b'MGEF', b'LTEX', b'ENCH', b'SPEL', b'ACTI', + b'TACT', b'ARMO', b'BOOK', b'CONT', b'DOOR', b'INGR', b'LIGH', + b'MISC', b'STAT', b'SCOL', b'MSTT', b'GRAS', b'TREE', b'FLOR', + b'FURN', b'WEAP', b'AMMO', b'NPC_', b'LVLN', b'KEYM', b'ALCH', b'IDLM', b'NOTE', b'PROJ', b'HAZD', b'BNDS', b'TERM', b'LVLI', b'WTHR', b'CLMT', b'SPGD', b'RFCT', b'REGN', b'NAVI', b'CELL', b'WRLD', b'QUST', b'IDLE', b'PACK', b'CSTY', b'LSCR', b'LVSP', @@ -246,20 +252,26 @@ def init(cls): b'MESG', b'DOBJ', b'DFOB', b'LGTM', b'MUSC', b'FSTP', b'FSTS', b'SMBN', b'SMQN', b'SMEN', b'DLBR', b'MUST', b'DLVW', b'EQUP', b'RELA', b'SCEN', b'ASTP', b'OTFT', b'ARTO', b'MATO', b'MOVT', - b'SNDR', b'SNCT', b'SOPM', b'COLL', b'CLFM', b'REVB', b'PKIN', - b'RFGP', b'AMDL', b'LAYR', b'COBJ', b'OMOD', b'MSWP', b'ZOOM', - b'INNR', b'KSSM', b'AECH', b'SCCO', b'AORU', b'SCSN', b'STAG', - b'NOCM', b'LENS', b'GDRY', b'OVIS', + b'SNDR', b'DUAL', b'SNCT', b'SOPM', b'COLL', b'CLFM', b'REVB', + b'PKIN', b'RFGP', b'AMDL', b'LAYR', b'COBJ', b'OMOD', b'MSWP', + b'ZOOM', b'INNR', b'KSSM', b'AECH', b'SCCO', b'AORU', b'SCSN', + b'STAG', b'NOCM', b'LENS', b'GDRY', b'OVIS', ] header_type.valid_header_sigs = (set(header_type.top_grup_sigs) | {b'GRUP', b'TES4', b'REFR', b'ACHR', b'PMIS', b'PARW', b'PGRE', b'PBEA', b'PFLA', b'PCON', b'PBAR', b'PHZD', b'LAND', b'NAVM', b'DIAL', b'INFO'}) header_type.plugin_form_version = 131 + # DMGT\DNAM changed completely in Form Version 78 and it's not possible + # to upgrade it (unless someone reverse engineers what the game does to + # it when loading) + header_type.skip_form_version_upgrade = {b'DMGT'} brec.MreRecord.type_class = {x.rec_sig: x for x in ( MreAact, MreActi, MreAddn, MreAech, MreAmdl, MreAnio, MreAoru, MreArma, MreArmo, MreArto, MreAstp, MreAvif, MreBnds, MreBook, - MreBptd, + MreBptd, MreCams, MreClas, MreClfm, MreClmt, MreCmpo, MreCobj, + MreColl, MreCont, MreCpth, MreCsty, MreDebr, MreDfob, MreDlbr, + MreDlvw, MreDmgt, MreDobj, MreDoor, MreDual, MreGmst, MreLvli, MreLvln, MrePerk, MreTes4, )} brec.MreRecord.simpleTypes = ( diff --git a/Mopy/bash/game/fallout4/records.py b/Mopy/bash/game/fallout4/records.py index eea6910e00..6c4c88155f 100644 --- a/Mopy/bash/game/fallout4/records.py +++ b/Mopy/bash/game/fallout4/records.py @@ -21,11 +21,13 @@ # # ============================================================================= """This module contains the Fallout 4 record classes.""" +import operator + from ...bolt import Flags -from ...brec import MelBase, MelGroup, MreHeaderBase, MelSet, MelString, \ - MelStruct, MelNull, MelSimpleArray, MreLeveledListBase, MelFid, MelAttx, \ +from ...brec import MelBase, MelGroup, AMreHeader, MelSet, MelString, \ + MelStruct, MelNull, MelSimpleArray, AMreLeveledList, MelFid, MelAttx, \ FID, MelLString, MelUInt8, MelFloat, MelBounds, MelEdid, MelUnloadEvent, \ - MelArray, MreGmstBase, MelUInt8Flags, MelSorted, MelGroups, MelShortName, \ + MelArray, AMreGmst, MelUInt8Flags, MelSorted, MelGroups, MelShortName, \ MelUInt32, MelRecord, MelColorO, MelFull, MelBaseR, MelKeywords, MelRace, \ MelColor, MelSound, MelSoundActivation, MelWaterType, MelAlchEnit, \ MelActiFlags, MelInteractionKeyword, MelConditions, MelTruncatedStruct, \ @@ -38,7 +40,11 @@ MelAdditionalRaces, MelFootstepSound, MelArtObject, MelEnchantment, \ MelIcons2, MelBids, MelBamt, MelTemplateArmor, MelObjectTemplate, \ MelArtType, MelAspcRdat, MelAspcBnam, MelAstpTitles, MelAstpData, \ - MelBookText, MelBookDescription, MelInventoryArt, MelUnorderedGroups + MelBookText, MelBookDescription, MelInventoryArt, MelUnorderedGroups, \ + MelImageSpaceMod, MelClmtWeatherTypes, MelClmtTiming, MelClmtTextures, \ + MelCobjOutput, AMreWithItems, AMelItems, MelContData, MelSoundClose, \ + MelCpthShared, FormVersionDecider, MelSoundLooping, MelDoorFlags, \ + MelRandomTeleports, MelDualData ##: What about texture hashes? I carried discarding them forward from Skyrim, # but that was due to the 43-44 problems. See also #620. @@ -134,13 +140,13 @@ class MelDestructible(MelGroup): def __init__(self): super().__init__('destructible', MelStruct(b'DEST', ['i', '2B', '2s'], 'health', 'count', - (MelDestructible._dest_header_flags, 'dest_flags'), + (self._dest_header_flags, 'dest_flags'), 'dest_unknown'), MelResistances(b'DAMC'), MelGroups('stages', MelStruct(b'DSTD', ['4B', 'i', '2I', 'i'], 'health', 'index', 'damage_stage', - (MelDestructible._dest_stage_flags, 'stage_flags'), + (self._dest_stage_flags, 'stage_flags'), 'self_damage_per_second', (FID, 'explosion'), (FID, 'debris'), 'debris_count'), MelString(b'DSTA', 'sequence_name'), @@ -155,13 +161,17 @@ class MelFtyp(MelFid): def __init__(self): super().__init__(b'FTYP', 'forced_loc_ref_type') +#------------------------------------------------------------------------------ +class MelItems(AMelItems): + """Handles the COCT/CNTO/COED subrecords defining items.""" + #------------------------------------------------------------------------------ class MelLLItems(AMelLLItems): - """Handles the LVLO and LLCT subrecords defining leveled list items""" + """Handles the LLCT/LVLO/COED subrecords defining leveled list entries.""" def __init__(self): - super().__init__([MelStruct(b'LVLO', ['H', '2s', 'I', 'H', 'B', 's'], + super().__init__(MelStruct(b'LVLO', ['H', '2s', 'I', 'H', 'B', 's'], 'level', 'unused1', (FID, 'listId'), ('count', 1), 'chance_none', - 'unused2')]) + 'unused2')) #------------------------------------------------------------------------------ class MelNativeTerminal(MelFid): @@ -221,7 +231,7 @@ class _VmadContextFo4(AVmadContext): #------------------------------------------------------------------------------ # Fallout 4 Records ----------------------------------------------------------- #------------------------------------------------------------------------------ -class MreTes4(MreHeaderBase): +class MreTes4(AMreHeader): """TES4 Record. File header.""" rec_sig = b'TES4' _post_masters_sigs = {b'ONAM', b'SCRN', b'TNAM', b'INTV', b'INCC'} @@ -231,9 +241,9 @@ class MreTes4(MreHeaderBase): (u'nextObject', 0x001)), MelNull(b'OFST'), # obsolete MelNull(b'DELE'), # obsolete - MreHeaderBase.MelAuthor(), - MreHeaderBase.MelDescription(), - MreHeaderBase.MelMasterNames(), + AMreHeader.MelAuthor(), + AMreHeader.MelDescription(), + AMreHeader.MelMasterNames(), MelSimpleArray('overrides', MelFid(b'ONAM')), MelBase(b'SCRN', 'screenshot'), MelGroups('transient_types', @@ -675,13 +685,301 @@ class MreBptd(MelRecord): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreGmst(MreGmstBase): +class MreCams(MelRecord): + """Camera Shot.""" + rec_sig = b'CAMS' + + _cams_flags = Flags.from_names('position_follows_location', + 'rotation_follows_target', 'dont_follow_bone', 'first_person_camera', + 'no_tracer', 'start_at_time_zero', 'dont_reset_location_spring', + 'dont_reset_target_spring') + + melSet = MelSet( + MelEdid(), + MelModel(), + MelConditionList(), + MelTruncatedStruct(b'DATA', ['4I', '12f'], 'cams_action', + 'cams_location', 'cams_target', (_cams_flags, 'cams_flags'), + 'time_mult_player', 'time_mult_target', 'time_mult_global', + 'cams_max_time', 'cams_min_time', 'target_pct_between_actors', + 'near_target_distance', 'location_spring', 'target_spring', + 'rotation_offset_x', 'rotation_offset_y', 'rotation_offset_z', + old_versions={'4I9f', '4I7f'}), + MelImageSpaceMod(), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreClas(MelRecord): + """Class.""" + rec_sig = b'CLAS' + + melSet = MelSet( + MelEdid(), + MelFull(), + MelDescription(), + MelIcon(), + MelProperties(), + MelStruct(b'DATA', ['4s', 'f'], 'unknown1', 'bleedout_default'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreClfm(MelRecord): + """Color.""" + rec_sig = b'CLFM' + + _clfm_flags = Flags.from_names('playable', 'remapping_index', + 'extended_lut') + + melSet = MelSet( + MelEdid(), + MelFull(), + MelUInt32(b'CNAM', 'color_or_index'), + MelUInt32Flags(b'FNAM', 'clfm_flags', _clfm_flags), + MelConditionList(), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreClmt(MelRecord): + """Climate.""" + rec_sig = b'CLMT' + + melSet = MelSet( + MelEdid(), + MelClmtWeatherTypes(), + MelClmtTextures(), + MelModel(), + MelClmtTiming(), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreCmpo(MelRecord): + """Component.""" + rec_sig = b'CMPO' + + melSet = MelSet( + MelEdid(), + MelBounds(), + MelFull(), + MelSoundCrafting(), + MelUInt32(b'DATA', 'auto_calc_value'), + MelFid(b'MNAM', 'scrap_item'), + MelFid(b'GNAM', 'mod_scrap_scalar'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreCobj(MelRecord): + """Constructible Object.""" + rec_sig = b'COBJ' + ##: What about isKeyedByEid? + + melSet = MelSet( + MelEdid(), + MelSoundPickupDrop(), + MelSorted(MelArray('cobj_components', + MelStruct(b'FVPA', ['2I'], (FID, 'component_fid'), + 'component_count'), + ), sort_by_attrs='component_fid'), + MelDescription(), + MelConditionList(), + MelCobjOutput(), + MelBase(b'NAM1', 'unused1'), + MelBase(b'NAM2', 'unused2'), + MelBase(b'NAM3', 'unused3'), + MelFid(b'ANAM', 'menu_art_object'), + MelSorted(MelSimpleArray('category_keywords', MelFid(b'FNAM'))), + MelTruncatedStruct(b'INTV', ['2H'], 'created_object_count', + 'cobj_priority', old_versions={'H'}), + ) + __slots__ = melSet.getSlotsUsed() + + def mergeFilter(self, modSet): + self.cobj_components = [c for c in self.cobj_components + if c.component_fid.mod_id in modSet] + +#------------------------------------------------------------------------------ +class MreCont(AMreWithItems): + """Container.""" + rec_sig = b'CONT' + + melSet = MelSet( + MelEdid(), + MelVmad(), + MelBounds(), + MelPreviewTransform(), + MelFull(), + MelModel(), + MelItems(), + MelDestructible(), + MelContData(), + MelKeywords(), + MelFtyp(), + MelProperties(), + MelNativeTerminal(), + MelSound(), + MelSoundClose(), + MelFid(b'TNAM', 'sound_take_all'), + MelFid(b'ONAM', 'cont_filter_list'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreCpth(MelRecord): + """Camera Path.""" + rec_sig = b'CPTH' + + melSet = MelSet( + MelEdid(), + MelConditionList(), + MelCpthShared(), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreCsty(MelRecord): + rec_sig = b'CSTY' + + _csty_flags = Flags.from_names('dueling', 'flanking', + 'allow_dual_wielding', 'charging', 'retarget_any_nearby_melee_target') + + melSet = MelSet( + MelEdid(), + MelStruct(b'CSGD', ['12f'], 'general_offensive_mult', + 'general_defensive_mult', 'general_group_offensive_mult', + 'general_equipment_score_mult_melee', + 'general_equipment_score_mult_magic', + 'general_equipment_score_mult_ranged', + 'general_equipment_score_mult_shout', + 'general_equipment_score_mult_unarmed', + 'general_equipment_score_mult_staff', + 'general_avoid_threat_chance', 'general_dodge_threat_chance', + 'general_evade_threat_chance'), + MelBase(b'CSMD', 'unknown1'), + MelTruncatedStruct(b'CSME', ['10f'], 'melee_attack_staggered_mult', + 'melee_power_attack_staggered_mult', + 'melee_power_attack_blocking_mult', + 'melee_bash_mult', 'melee_bash_recoil_mult', + 'melee_bash_attack_mult', 'melee_bash_power_attack_mult', + 'melee_special_attack_mult', 'melee_block_when_staggered_mult', + 'melee_attack_when_staggered_mult', old_versions={'9f'}), + MelFloat(b'CSRA', 'ranged_accuracy_mult'), + MelStruct(b'CSCR', ['9f', 'I', 'f'], 'close_range_dueling_circle_mult', + 'close_range_dueling_fallback_mult', + 'close_range_flanking_flank_distance', + 'close_range_flanking_stalk_time', + 'close_range_charging_charge_distance', + 'close_range_charging_throw_probability', + 'close_range_charging_sprint_fast_probability', + 'close_range_charging_sideswipe_probability', + 'close_range_charging_disengage_probability', + 'close_range_charging_throw_max_targets', + 'close_range_flanking_flank_variance'), + MelTruncatedStruct(b'CSLR', ['5f'], 'long_range_strafe_mult', + 'long_range_adjust_range_mult', 'long_range_crouch_mult', + 'long_range_wait_mult', 'long_range_range_mult', + old_versions={'4f', '3f'}), + MelFloat(b'CSCV', 'cover_search_distance_mult'), + MelStruct(b'CSFL', ['8f'], 'flight_hover_chance', + 'flight_dive_bomb_chance', 'flight_ground_attack_chance', + 'flight_hover_time', 'flight_ground_attack_time', + 'flight_perch_attack_chance', 'flight_perch_attack_time', + 'flight_flying_attack_chance'), + MelUInt32Flags(b'DATA', 'csty_flags', _csty_flags), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreDfob(MelRecord): + """Default Object.""" + rec_sig = b'DFOB' + + melSet = MelSet( + MelEdid(), + MelFid(b'DATA', 'default_object'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreDmgt(MelRecord): + """Damage Type.""" + rec_sig = b'DMGT' + + melSet = MelSet( + MelEdid(), + MelUnion({ + True: MelArray('damage_types', + MelStruct(b'DNAM', ['2I'], (FID, 'dt_actor_value'), + (FID, 'dt_spell')), + ), + False: MelSimpleArray('damage_types', MelUInt32(b'DNAM')), + }, decider=FormVersionDecider(operator.ge, 78)), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreDobj(MelRecord): + """Default Object Manager.""" + rec_sig = b'DOBJ' + + melSet = MelSet( + MelEdid(), + MelSorted(MelArray('default_objects', + MelStruct(b'DNAM', ['2I'], 'default_object_use', + (FID, 'default_object_fid')), + ), sort_by_attrs='default_object_use'), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreDoor(MelRecord): + """Door.""" + rec_sig = b'DOOR' + + melSet = MelSet( + MelEdid(), + MelVmad(), + MelBounds(), + MelPreviewTransform(), + MelFull(), + MelModel(), + MelDestructible(), + MelKeywords(), + MelNativeTerminal(), + MelSound(), + MelSoundClose(b'ANAM'), + MelSoundLooping(), + MelDoorFlags(), + MelLString(b'ONAM', 'alternate_text_open'), + MelLString(b'CNAM', 'alternate_text_close'), + MelRandomTeleports(), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +# Not present in Fallout4.esm, but can be created in CK +class MreDual(MelRecord): + """Dual Cast Data.""" + rec_sig = b'DUAL' + + melSet = MelSet( + MelEdid(), + MelBounds(), + MelDualData(), + ) + __slots__ = melSet.getSlotsUsed() + +#------------------------------------------------------------------------------ +class MreGmst(AMreGmst): """Game Setting.""" isKeyedByEid = True # NULL fids are acceptable. __slots__ = () #------------------------------------------------------------------------------ -class MreLvli(MreLeveledListBase): +class MreLvli(AMreLeveledList): """Leveled Item.""" rec_sig = b'LVLI' @@ -693,7 +991,7 @@ class MreLvli(MreLeveledListBase): MelBounds(), MelUInt8(b'LVLD', 'chanceNone'), MelUInt8(b'LVLM', 'maxCount'), - MelUInt8Flags(b'LVLF', u'flags', MreLeveledListBase._flags), + MelUInt8Flags(b'LVLF', u'flags', AMreLeveledList._flags), MelFid(b'LVLG', 'glob'), MelLLItems(), MelArray('filterKeywordChances', @@ -705,7 +1003,7 @@ class MreLvli(MreLeveledListBase): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreLvln(MreLeveledListBase): +class MreLvln(AMreLeveledList): """Leveled NPC.""" rec_sig = b'LVLN' @@ -717,7 +1015,7 @@ class MreLvln(MreLeveledListBase): MelBounds(), MelUInt8(b'LVLD', 'chanceNone'), MelUInt8(b'LVLM', 'maxCount'), - MelUInt8Flags(b'LVLF', u'flags', MreLeveledListBase._flags), + MelUInt8Flags(b'LVLF', u'flags', AMreLeveledList._flags), MelFid(b'LVLG', 'glob'), MelLLItems(), MelArray('filterKeywordChances', diff --git a/Mopy/bash/game/fallout4vr/__init__.py b/Mopy/bash/game/fallout4vr/__init__.py index 9f8a2fde11..c7e2680fa0 100644 --- a/Mopy/bash/game/fallout4vr/__init__.py +++ b/Mopy/bash/game/fallout4vr/__init__.py @@ -24,7 +24,7 @@ necessary.""" from ..fallout4 import Fallout4GameInfo -from ... import brec, bolt +from ... import bolt class Fallout4VRGameInfo(Fallout4GameInfo): displayName = u'Fallout 4 VR' @@ -88,22 +88,21 @@ class Esp(Fallout4GameInfo.Esp): @classmethod def init(cls): cls._dynamic_import_modules(__name__) - # First import FO4VR-specific record classes from .records import MreTes4 - # Then import from fallout4.records file from ..fallout4.records import MreGmst, MreLvli, MreLvln cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in ( MreGmst, MreLvli, MreLvln )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'KYWD', b'LCRT', b'AACT', b'TRNS', b'CMPO', b'TXST', - b'GLOB', b'DMGT', b'CLAS', b'FACT', b'HDPT', b'RACE', b'SOUN', - b'ASPC', b'MGEF', b'LTEX', b'ENCH', b'SPEL', b'ACTI', b'TACT', - b'ARMO', b'BOOK', b'CONT', b'DOOR', b'INGR', b'LIGH', b'MISC', - b'STAT', b'SCOL', b'MSTT', b'GRAS', b'TREE', b'FLOR', b'FURN', - b'WEAP', b'AMMO', b'NPC_', b'PLYR', b'LVLN', b'KEYM', b'ALCH', + b'GLOB', b'DMGT', b'CLAS', b'FACT', b'HDPT', b'EYES', b'RACE', + b'SOUN', b'ASPC', b'MGEF', b'LTEX', b'ENCH', b'SPEL', b'ACTI', + b'TACT', b'ARMO', b'BOOK', b'CONT', b'DOOR', b'INGR', b'LIGH', + b'MISC', b'STAT', b'SCOL', b'MSTT', b'GRAS', b'TREE', b'FLOR', + b'FURN', b'WEAP', b'AMMO', b'NPC_', b'LVLN', b'KEYM', b'ALCH', b'IDLM', b'NOTE', b'PROJ', b'HAZD', b'BNDS', b'TERM', b'LVLI', b'WTHR', b'CLMT', b'SPGD', b'RFCT', b'REGN', b'NAVI', b'CELL', b'WRLD', b'QUST', b'IDLE', b'PACK', b'CSTY', b'LSCR', b'LVSP', @@ -113,10 +112,10 @@ def init(cls): b'MESG', b'DOBJ', b'DFOB', b'LGTM', b'MUSC', b'FSTP', b'FSTS', b'SMBN', b'SMQN', b'SMEN', b'DLBR', b'MUST', b'DLVW', b'EQUP', b'RELA', b'SCEN', b'ASTP', b'OTFT', b'ARTO', b'MATO', b'MOVT', - b'SNDR', b'SNCT', b'SOPM', b'COLL', b'CLFM', b'REVB', b'PKIN', - b'RFGP', b'AMDL', b'LAYR', b'COBJ', b'OMOD', b'MSWP', b'ZOOM', - b'INNR', b'KSSM', b'AECH', b'SCCO', b'AORU', b'SCSN', b'STAG', - b'NOCM', b'LENS', b'GDRY', b'OVIS', + b'SNDR', b'DUAL', b'SNCT', b'SOPM', b'COLL', b'CLFM', b'REVB', + b'PKIN', b'RFGP', b'AMDL', b'LAYR', b'COBJ', b'OMOD', b'MSWP', + b'ZOOM', b'INNR', b'KSSM', b'AECH', b'SCCO', b'AORU', b'SCSN', + b'STAG', b'NOCM', b'LENS', b'GDRY', b'OVIS', ] header_type.valid_header_sigs = (set(header_type.top_grup_sigs) | {b'GRUP', b'TES4', b'REFR', b'ACHR', b'PMIS', b'PARW', b'PGRE', diff --git a/Mopy/bash/game/fallout4vr/records.py b/Mopy/bash/game/fallout4vr/records.py index c9fd363e9f..5876b3b6f4 100644 --- a/Mopy/bash/game/fallout4vr/records.py +++ b/Mopy/bash/game/fallout4vr/records.py @@ -23,12 +23,12 @@ """This module contains only the overrides of record classes needed for FO4VR.""" -from ...brec import MreHeaderBase, MelSet, MelStruct, MelBase, MelFid, \ +from ...brec import AMreHeader, MelSet, MelStruct, MelBase, MelFid, \ MelSimpleArray, MelNull, MelGroups, MelUInt32 # Only difference from FO4 is the default version, but this seems less hacky # than adding a game var just for this and dynamically importing it in FO4 -class MreTes4(MreHeaderBase): +class MreTes4(AMreHeader): """TES4 Record. File header.""" rec_sig = b'TES4' _post_masters_sigs = {b'ONAM', b'SCRN', b'TNAM', b'INTV', b'INCC'} @@ -38,9 +38,9 @@ class MreTes4(MreHeaderBase): (u'nextObject', 0x800)), MelNull(b'OFST'), # obsolete MelNull(b'DELE'), # obsolete - MreHeaderBase.MelAuthor(), - MreHeaderBase.MelDescription(), - MreHeaderBase.MelMasterNames(), + AMreHeader.MelAuthor(), + AMreHeader.MelDescription(), + AMreHeader.MelMasterNames(), MelSimpleArray('overrides', MelFid(b'ONAM')), MelBase(b'SCRN', 'screenshot'), MelGroups('transient_types', diff --git a/Mopy/bash/game/falloutnv/__init__.py b/Mopy/bash/game/falloutnv/__init__.py index 7de8b5c0dd..84f4e5f94d 100644 --- a/Mopy/bash/game/falloutnv/__init__.py +++ b/Mopy/bash/game/falloutnv/__init__.py @@ -23,8 +23,7 @@ """GameInfo override for Fallout NV.""" from ..fallout3 import Fallout3GameInfo -from ... import brec, bolt -from ...brec import MreFlst, MreGlob +from ... import bolt class FalloutNVGameInfo(Fallout3GameInfo): displayName = u'Fallout New Vegas' @@ -141,16 +140,15 @@ def _dynamic_import_modules(cls, package_name): @classmethod def init(cls): cls._dynamic_import_modules(__name__) - # First import from our records file + from ...brec import MreDebr, MreFlst, MreGlob from .records import MreTes4, MreAloc, MreAmef, MreCcrd, MreCdck, \ MreChal, MreChip, MreCmny, MreCsno, MreDehy, MreDial, MreHung, \ MreImod, MreLsct, MreMset, MreRcct, MreRcpe, MreRepu, MreSlpd, \ MreWthr - # Then from fallout3.records from ..fallout3.records import MreCpth, MreIdle, MreMesg, MrePack, \ MrePerk, MreQust, MreSpel, MreTerm, MreNpc, MreAddn, MreAnio, \ MreAvif, MreBook, MreBptd, MreCams, MreClas, MreClmt, MreCobj, \ - MreCrea, MreDebr, MreDoor, MreEczn, MreEfsh, MreExpl, MreEyes, \ + MreCrea, MreWeap, MreDoor, MreEczn, MreEfsh, MreExpl, MreEyes, \ MreFurn, MreGras, MreHair, MreIdlm, MreImgs, MreIngr, MreRace, \ MreIpds, MreLgtm, MreLtex, MreLvlc, MreLvli, MreLvln, MreMgef, \ MreMicn, MreMstt, MreNavi, MreNavm, MreNote, MrePwat, MreRads, \ @@ -159,7 +157,7 @@ def init(cls): MreCont, MreAchr, MreAcre, MreCell, MreCsty, MreDobj, MreEnch, \ MreFact, MreGmst, MreHdpt, MreImad, MreInfo, MreIpct, MreKeym, \ MreLigh, MreLscr, MreMisc, MreMusc, MrePgre, MrePmis, MreProj, \ - MreRefr, MreRegn, MreSoun, MreStat, MreTact, MreWeap + MreRefr, MreRegn, MreSoun, MreStat, MreTact cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in ( MreActi, MreAddn, MreAlch, MreAloc, MreAmef, MreAmmo, MreAnio, MreArma, MreArmo, MreAspc, MreAvif, MreBook, MreBptd, MreCams, @@ -177,6 +175,7 @@ def init(cls): MreTxst, MreVtyp, MreWatr, MreWeap, MreWthr, MreGmst, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'TXST', b'MICN', b'GLOB', b'CLAS', b'FACT', b'HDPT', diff --git a/Mopy/bash/game/falloutnv/patcher/__init__.py b/Mopy/bash/game/falloutnv/patcher/__init__.py index 0168470ffd..784de173dc 100644 --- a/Mopy/bash/game/falloutnv/patcher/__init__.py +++ b/Mopy/bash/game/falloutnv/patcher/__init__.py @@ -238,7 +238,7 @@ # Import Sounds #------------------------------------------------------------------------------ soundsTypes.update({ - b'CONT': ('sound', 'soundClose', 'soundRandomLooping'), + b'CONT': ('sound', 'sound_close', 'sound_random_looping'), b'WEAP': ('pickupSound', 'dropSound', 'sound', 'soundGunShot2D', 'soundGunShot3DLooping', 'soundMeleeSwingGunNoAmmo', 'soundBlock', 'idleSound', 'equipSound', 'unequipSound', diff --git a/Mopy/bash/game/falloutnv/records.py b/Mopy/bash/game/falloutnv/records.py index dc4d04d76c..0f6c7e3834 100644 --- a/Mopy/bash/game/falloutnv/records.py +++ b/Mopy/bash/game/falloutnv/records.py @@ -24,7 +24,7 @@ from ..fallout3.records import MelDestructible, MelModel from ...bolt import Flags, struct_calcsize from ...brec import MelRecord, MelGroups, MelStruct, FID, MelString, MelSet, \ - MelFid, MelFids, MelBase, MelSimpleArray, MreHeaderBase, MelFloat, \ + MelFid, MelFids, MelBase, MelSimpleArray, AMreHeader, MelFloat, \ MelUInt32, MelBounds, null1, MelTruncatedStruct, MelIcons, MelIcon, \ MelIco2, MelEdid, MelFull, MelArray, MelObject, MelNull, MelScript, \ MelDescription, MelSoundPickupDrop, MelUInt8Flags, MelSInt32, \ @@ -34,7 +34,7 @@ #------------------------------------------------------------------------------ # FalloutNV Records ----------------------------------------------------------- #------------------------------------------------------------------------------ -class MreTes4(MreHeaderBase): +class MreTes4(AMreHeader): """TES4 Record. File header.""" rec_sig = b'TES4' _post_masters_sigs = {b'ONAM', b'SCRN'} @@ -44,9 +44,9 @@ class MreTes4(MreHeaderBase): ('nextObject', 0x800)), MelNull(b'OFST'), # obsolete MelNull(b'DELE'), # obsolete - MreHeaderBase.MelAuthor(), - MreHeaderBase.MelDescription(), - MreHeaderBase.MelMasterNames(), + AMreHeader.MelAuthor(), + AMreHeader.MelDescription(), + AMreHeader.MelMasterNames(), MelSimpleArray('overrides', MelFid(b'ONAM')), MelBase(b'SCRN', 'screenshot'), ) diff --git a/Mopy/bash/game/morrowind/__init__.py b/Mopy/bash/game/morrowind/__init__.py index d77feebe45..3154c7c457 100644 --- a/Mopy/bash/game/morrowind/__init__.py +++ b/Mopy/bash/game/morrowind/__init__.py @@ -25,7 +25,7 @@ from ..patch_game import GameInfo, PatchGame from .. import WS_COMMON -from ... import brec, bolt +from ... import bolt class MorrowindGameInfo(PatchGame): displayName = u'Morrowind' @@ -136,6 +136,7 @@ def init(cls): MreRepa, MreScpt, MreSkil, MreSndg, MreSoun, MreSpel, MreSscr, \ MreStat, MreTes3, MreWeap # Setting RecordHeader class variables - Morrowind is special + from ... import brec header_type = brec.RecordHeader header_type.rec_header_size = 16 header_type.rec_pack_format = [u'=4s', u'I', u'I', u'I'] diff --git a/Mopy/bash/game/morrowind/records.py b/Mopy/bash/game/morrowind/records.py index b0991f6d87..2214611f70 100644 --- a/Mopy/bash/game/morrowind/records.py +++ b/Mopy/bash/game/morrowind/records.py @@ -27,13 +27,13 @@ from ...bolt import Flags from ...brec import MelBase, MelSet, MelString, MelStruct, MelArray, \ - MreHeaderBase, MelUnion, SaveDecider, MelNull, MelSequential, MelRecord, \ + AMreHeader, MelUnion, SaveDecider, MelNull, MelSequential, MelRecord, \ MelGroup, MelGroups, MelUInt8, MelDescription, MelUInt32, MelColorO,\ MelOptStruct, MelCounter, MelRefScale, MelRef3D, MelBookText, \ - MelIcons, MelFloat, MelSInt32, MelEffectsTes3, \ - MelFixedString, FixedString, AutoFixedString, MreGmstBase, \ - MreLeveledListBase, MelUInt16, SizeDecider, MelLists, \ - MelTruncatedStruct, MelColor, MelStrings, MelUInt32Flags + MelIcons, MelFloat, MelSInt32, MelEffectsTes3, MelFixedString, \ + FixedString, AutoFixedString, AMreGmst, AMreLeveledList, MelUInt16, \ + SizeDecider, MelLists, MelTruncatedStruct, MelColor, MelStrings, \ + MelUInt32Flags #------------------------------------------------------------------------------ # Record Elements ------------------------------------------------------------- @@ -211,7 +211,7 @@ def __init__(self): ) #------------------------------------------------------------------------------ -class MreLeveledList(MreLeveledListBase): +class MreLeveledList(AMreLeveledList): """Base class for LEVC and LEVI.""" _lvl_flags = Flags.from_names( u'calcFromAllLevels', @@ -220,7 +220,7 @@ class MreLeveledList(MreLeveledListBase): top_copy_attrs = (u'chanceNone',) entry_copy_attrs = (u'listId', u'level') # no count - # Bad names to mirror the other games (needed by MreLeveledListBase) + # Bad names to mirror the other games (needed by AMreLeveledList) melSet = MelSet( MelMWId(), MelUInt32Flags(b'DATA', u'flags', _lvl_flags), @@ -236,7 +236,7 @@ class MreLeveledList(MreLeveledListBase): #------------------------------------------------------------------------------ # Shared (plugins + saves) record classes ------------------------------------- #------------------------------------------------------------------------------ -class MreTes3(MreHeaderBase): +class MreTes3(AMreHeader): """TES3 Record. File header.""" rec_sig = b'TES3' _post_masters_sigs = {b'GMDT', b'SCRD', b'SCRS'} @@ -245,7 +245,7 @@ class MreTes3(MreHeaderBase): MelStruct(b'HEDR', ['f', 'I', '32s', '256s', 'I'], ('version', 1.3), 'esp_flags', (AutoFixedString(32), 'author_pstr'), (AutoFixedString(256), 'description_pstr'), 'numRecords'), - MreHeaderBase.MelMasterNames(), + AMreHeader.MelMasterNames(), MelSavesOnly( # Wrye Mash calls unknown1 'day', but that seems incorrect? MelStruct(b'GMDT', [u'6f', u'64s', u'f', u'32s'], u'pc_curr_health', @@ -658,7 +658,7 @@ def _get_element_from_record(self, record): return self._get_element(self._fmt_mapping[format_char]) return super(MelGmstUnion, self)._get_element_from_record(record) -class MreGmst(MreGmstBase): +class MreGmst(AMreGmst): """Game Setting.""" melSet = MelSet( MelMWId(), diff --git a/Mopy/bash/game/nehrim/__init__.py b/Mopy/bash/game/nehrim/__init__.py index d7006f94f8..41a97137e3 100644 --- a/Mopy/bash/game/nehrim/__init__.py +++ b/Mopy/bash/game/nehrim/__init__.py @@ -24,8 +24,7 @@ import struct as _struct from ..oblivion import OblivionGameInfo -from ... import brec, bolt -from ...brec import MreGlob, MreLand +from ... import bolt class NehrimGameInfo(OblivionGameInfo): displayName = u'Nehrim' @@ -105,6 +104,7 @@ def _dynamic_import_modules(cls, package_name): @classmethod def init(cls): cls._dynamic_import_modules(__name__) + from ...brec import MreGlob, MreLand from ..oblivion.records import MreActi, MreAlch, MreAmmo, MreAnio, \ MreArmo, MreBook, MreBsgn, MreClas, MreClot, MreCont, MreCrea, \ MreDoor, MreEfsh, MreEnch, MreEyes, MreFact, MreFlor, MreFurn, \ @@ -129,6 +129,7 @@ def init(cls): cls.readClasses = (b'MGEF', b'SCPT') cls.writeClasses = (b'MGEF',) # Setting RecordHeader class variables - Oblivion is special + from ... import brec header_type = brec.RecordHeader header_type.rec_header_size = 20 header_type.rec_pack_format = [u'=4s', u'I', u'I', u'I', u'I'] diff --git a/Mopy/bash/game/oblivion/__init__.py b/Mopy/bash/game/oblivion/__init__.py index 3e1c5f9241..7f4f7336c5 100644 --- a/Mopy/bash/game/oblivion/__init__.py +++ b/Mopy/bash/game/oblivion/__init__.py @@ -26,8 +26,7 @@ from ..patch_game import GameInfo, PatchGame from .. import WS_COMMON -from ... import brec, bolt -from ...brec import MreGlob, MreLand +from ... import bolt class OblivionGameInfo(PatchGame): displayName = u'Oblivion' @@ -301,6 +300,7 @@ def _dynamic_import_modules(cls, package_name): @classmethod def init(cls): cls._dynamic_import_modules(__name__) + from ...brec import MreGlob, MreLand from .records import MreActi, MreAlch, MreAmmo, MreAnio, MreAppa, \ MreArmo, MreBook, MreBsgn, MreClas, MreClot, MreCont, MreCrea, \ MreDoor, MreEfsh, MreEnch, MreEyes, MreFact, MreFlor, MreFurn, \ @@ -325,6 +325,7 @@ def init(cls): cls.readClasses = (b'MGEF', b'SCPT') cls.writeClasses = (b'MGEF',) # Setting RecordHeader class variables - Oblivion is special + from ... import brec header_type = brec.RecordHeader header_type.rec_header_size = 20 header_type.rec_pack_format = [u'=4s', u'I', u'I', u'I', u'I'] diff --git a/Mopy/bash/game/oblivion/patcher/__init__.py b/Mopy/bash/game/oblivion/patcher/__init__.py index ca8ae84ccd..2448067142 100644 --- a/Mopy/bash/game/oblivion/patcher/__init__.py +++ b/Mopy/bash/game/oblivion/patcher/__init__.py @@ -297,9 +297,9 @@ #------------------------------------------------------------------------------ soundsTypes = { b'ACTI': ('sound',), - b'CONT': ('sound', 'soundClose'), + b'CONT': ('sound', 'sound_close'), b'CREA': (u'footWeight', u'inheritsSoundsFrom', u'sounds'), - b'DOOR': ('sound', 'soundClose', 'soundLoop'), + b'DOOR': ('sound', 'sound_close', 'sound_looping'), b'LIGH': (u'sound',), b'MGEF': (u'castingSound', u'boltSound', u'hitSound', u'areaSound'), # b'REGN': ('entries.sounds',), diff --git a/Mopy/bash/game/oblivion/records.py b/Mopy/bash/game/oblivion/records.py index 445f2b854c..fc5b83d096 100644 --- a/Mopy/bash/game/oblivion/records.py +++ b/Mopy/bash/game/oblivion/records.py @@ -28,21 +28,23 @@ from ...bolt import Flags, int_or_zero, structs_cache, str_or_none, \ int_or_none, str_to_sig, sig_to_str from ...brec import MelRecord, MelGroups, MelStruct, FID, MelGroup, MelString, \ - MreLeveledListBase, MelSet, MelFid, MelNull, MelOptStruct, MelFids, \ - MreHeaderBase, MelBase, MelSimpleArray, MelBodyParts, MelAnimations, \ - MreGmstBase, MelReferences, MelRegnEntrySubrecord, MelSorted, MelRegions, \ + AMreLeveledList, MelSet, MelFid, MelNull, MelOptStruct, MelFids, \ + AMreHeader, MelBase, MelSimpleArray, MelBodyParts, MelAnimations, \ + AMreGmst, MelReferences, MelRegnEntrySubrecord, MelSorted, MelRegions, \ MelFloat, MelSInt16, MelSInt32, MelUInt8, MelUInt16, MelUInt32, \ MelRaceParts, MelRaceVoices, null2, MelScriptVars, MelRelations, MelRace, \ MelSequential, MelUnion, FlagDecider, AttrValDecider, PartialLoadDecider, \ MelTruncatedStruct, MelSkipInterior, MelIcon, MelIco2, MelEdid, MelFull, \ - MelArray, MelWthrColors, MelEffectsTes4, MreActorBase, MreWithItems, \ + MelArray, MelWthrColors, MelEffectsTes4, AMreActor, AMreWithItems, \ MelReadOnly, MelRef3D, MelXlod, MelWorldBounds, MelEnableParent, MelObme, \ MelRefScale, MelMapMarker, MelActionFlags, MelPartialCounter, MelScript, \ MelDescription, BipedFlags, MelUInt8Flags, MelUInt32Flags, MelLists, \ MelConditionsTes4, MelRaceData, MelFactions, MelActorSounds, MelBaseR, \ - MelWeatherTypes, MelFactionRanks, MelLscrLocations, attr_csv_struct, \ + MelClmtWeatherTypes, MelFactionRanks, MelLscrLocations, attr_csv_struct, \ MelEnchantment, MelValueWeight, null4, SpellFlags, MelOwnership, \ - MelSound, MelWeight, MelEffectsTes4ObmeFull, MelBookText + MelSound, MelWeight, MelEffectsTes4ObmeFull, MelBookText, MelClmtTiming, \ + MelClmtTextures, MelSoundClose, AMelItems, AMelLLItems, MelContData, \ + MelDoorFlags, MelSoundLooping, MelRandomTeleports #------------------------------------------------------------------------------ # Record Elements ------------------------------------------------------------- @@ -116,12 +118,10 @@ def __init__(self, with_script_vars=False): super(MelEmbeddedScript, self).__init__(*seq_elements) #------------------------------------------------------------------------------ -class MelItems(MelSorted): - """Wraps MelGroups for the common task of defining a list of items.""" +class MelItems(AMelItems): + """Handles the CNTO subrecords defining items.""" def __init__(self): - super(MelItems, self).__init__(MelGroups(u'items', - MelStruct(b'CNTO', [u'I', u'i'], (FID, u'item'), u'count'), - ), sort_by_attrs='item') + super().__init__(with_coed=False, with_counter=False) #------------------------------------------------------------------------------ class MelLevListLvld(MelUInt8): @@ -145,6 +145,14 @@ def _pre_process_unpacked(self, unpacked_val): unpacked_val = (unpacked_val[0], null2, unpacked_val[1]) return super(MelLevListLvlo, self)._pre_process_unpacked(unpacked_val) +#------------------------------------------------------------------------------ +class MelLLItems(AMelLLItems): + """Handles the LVLO subrecords defining leveled list entries.""" + def __init__(self): + super().__init__(MelLevListLvlo(b'LVLO', ['h', '2s', 'I', 'h', '2s'], + 'level', 'unused1', (FID, 'listId'), ('count', 1), 'unused2', + old_versions={'iI'}), with_coed=False, with_counter=False) + #------------------------------------------------------------------------------ class MelOwnershipTes4(MelOwnership): """Handles XOWN, XRNK, and XGLB for cells and cell children.""" @@ -442,22 +450,17 @@ def is_harmful(self, cached_hostile): return True #------------------------------------------------------------------------------ -class MreLeveledList(MreLeveledListBase): +class MreLeveledList(AMreLeveledList): """Leveled item/creature/spell list.""" top_copy_attrs = ('script_fid','template','chanceNone',) melSet = MelSet( MelEdid(), MelLevListLvld(), - MelUInt8Flags(b'LVLF', u'flags', MreLeveledListBase._flags), + MelUInt8Flags(b'LVLF', u'flags', AMreLeveledList._flags), MelScript(), # LVLC only MelFid(b'TNAM','template'), - MelSorted(MelGroups('entries', - MelLevListLvlo(b'LVLO', [u'h', u'2s', u'I', u'h', u'2s'], - u'level', u'unused1', - (FID, u'listId'), (u'count', 1), - u'unused2', old_versions={u'iI'}), - ), sort_by_attrs=('level', 'listId', 'count')), + MelLLItems(), MelNull(b'DATA'), ) __slots__ = melSet.getSlotsUsed() @@ -465,7 +468,7 @@ class MreLeveledList(MreLeveledListBase): #------------------------------------------------------------------------------ # Oblivion Records ------------------------------------------------------------ #------------------------------------------------------------------------------ -class MreTes4(MreHeaderBase): +class MreTes4(AMreHeader): """TES4 Record. File header.""" rec_sig = b'TES4' _post_masters_sigs = set() @@ -475,12 +478,13 @@ class MreTes4(MreHeaderBase): (u'nextObject', 0x800)), MelNull(b'OFST'), # obsolete MelNull(b'DELE'), # obsolete - MreHeaderBase.MelAuthor(), - MreHeaderBase.MelDescription(), - MreHeaderBase.MelMasterNames(), + AMreHeader.MelAuthor(), + AMreHeader.MelDescription(), + AMreHeader.MelMasterNames(), ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreAchr(MelRecord): """Placed NPC.""" rec_sig = b'ACHR' @@ -501,6 +505,7 @@ class MreAchr(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreAcre(MelRecord): """Placed Creature.""" rec_sig = b'ACRE' @@ -516,6 +521,7 @@ class MreAcre(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreActi(MelRecord): """Activator.""" rec_sig = b'ACTI' @@ -529,6 +535,7 @@ class MreActi(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreAlch(MreHasEffects, MelRecord): """Potion.""" rec_sig = b'ALCH' @@ -549,6 +556,7 @@ class MreAlch(MreHasEffects, MelRecord): ).with_distributor(_effects_distributor) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreAmmo(MelRecord): """Ammunition.""" rec_sig = b'AMMO' @@ -567,6 +575,7 @@ class MreAmmo(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreAnio(MelRecord): """Animation Object.""" rec_sig = b'ANIO' @@ -578,6 +587,7 @@ class MreAnio(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreAppa(MelRecord): """Alchemical Apparatus.""" rec_sig = b'APPA' @@ -593,6 +603,7 @@ class MreAppa(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreArmo(MelRecord): """Armor.""" rec_sig = b'ARMO' @@ -617,6 +628,7 @@ class MreArmo(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreBook(MelRecord): """Book.""" rec_sig = b'BOOK' @@ -637,6 +649,7 @@ class MreBook(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreBsgn(MelRecord): """Birthsign.""" rec_sig = b'BSGN' @@ -650,6 +663,7 @@ class MreBsgn(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreCell(MelRecord): """Cell.""" rec_sig = b'CELL' @@ -687,6 +701,7 @@ class MreCell(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreClas(MelRecord): """Class.""" rec_sig = b'CLAS' @@ -707,21 +722,21 @@ class MreClas(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreClmt(MelRecord): """Climate.""" rec_sig = b'CLMT' melSet = MelSet( MelEdid(), - MelWeatherTypes(with_global=False), - MelString(b'FNAM','sunPath'), - MelString(b'GNAM','glarePath'), + MelClmtWeatherTypes(with_global=False), + MelClmtTextures(), MelModel(), - MelStruct(b'TNAM', [u'6B'], 'riseBegin', 'riseEnd', 'setBegin', 'setEnd', - 'volatility', 'phaseLength'), + MelClmtTiming(), ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreClot(MelRecord): """Clothing.""" rec_sig = b'CLOT' @@ -746,25 +761,25 @@ class MreClot(MelRecord): ) __slots__ = melSet.getSlotsUsed() -class MreCont(MreWithItems): +#------------------------------------------------------------------------------ +class MreCont(AMreWithItems): """Container.""" rec_sig = b'CONT' - _flags = Flags.from_names(None,'respawns') - melSet = MelSet( MelEdid(), MelFull(), MelModel(), MelScript(), MelItems(), - MelStruct(b'DATA', [u'B', u'f'],(_flags, u'flags'),'weight'), + MelContData(), MelSound(), - MelFid(b'QNAM','soundClose'), + MelSoundClose(), ) __slots__ = melSet.getSlotsUsed() -class MreCrea(MreActorBase): +#------------------------------------------------------------------------------ +class MreCrea(AMreActor): """Creature.""" rec_sig = b'CREA' @@ -825,55 +840,56 @@ class MreCrea(MreActorBase): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreCsty(MelRecord): """Combat Style.""" rec_sig = b'CSTY' - _flagsA = Flags.from_names( - ( 0,'advanced'), - ( 1,'useChanceForAttack'), - ( 2,'ignoreAllies'), - ( 3,'willYield'), - ( 4,'rejectsYields'), - ( 5,'fleeingDisabled'), - ( 6,'prefersRanged'), - ( 7,'meleeAlertOK'), + + _csty_flags1 = Flags.from_names( + 'advanced', + 'use_chance_for_attack', + 'ignore_allies', + 'will_yield', + 'rejects_yields', + 'fleeing_disabled', + 'prefers_ranged', + 'melee_alert_ok', ) - _flagsB = Flags.from_names('doNotAcquire') + _csty_flags2 = Flags.from_names('do_not_acquire') melSet = MelSet( MelEdid(), MelTruncatedStruct(b'CSTD', - [u'2B', u'2s', u'8f', u'2B', u'2s', u'3f', u'B', u'3s', u'2f', - u'5B', u'3s', u'2f', u'2B', u'2s', u'7f', u'B', u'3s', u'f', - u'I'], 'dodgeChance', - 'lrChance', 'unused1', 'lrTimerMin', 'lrTimerMax', - 'forTimerMin', 'forTimerMax', 'backTimerMin', 'backTimerMax', - 'idleTimerMin', 'idleTimerMax', 'blkChance', 'atkChance', - 'unused2', 'atkBRecoil', 'atkBunc', 'atkBh2h', - 'pAtkChance', 'unused3', 'pAtkBRecoil', 'pAtkBUnc', - 'pAtkNormal', 'pAtkFor', 'pAtkBack', 'pAtkL', 'pAtkR', - 'unused4', 'holdTimerMin', 'holdTimerMax', - (_flagsA, 'flagsA'), 'acroDodge', 'unused5', - ('rMultOpt', 1.0), ('rMultMax', 1.0), ('mDistance', 250.0), - ('rDistance', 1000.0), ('buffStand', 325.0), ('rStand', 500.0), - ('groupStand', 325.0), ('rushChance', 25), 'unused6', - ('rushMult', 1.0), (_flagsB, 'flagsB'), old_versions={ + ['2B', '2s', '8f', '2B', '2s', '3f', 'B', '3s', '2f', '5B', '3s', + '2f', '2B', '2s', '7f', 'B', '3s', 'f', 'I'], 'dodge_chance', + 'lr_chance', 'unused1', 'lr_timer_min', 'lr_timer_max', + 'for_timer_min', 'for_timer_max', 'back_timer_min', + 'back_timer_max', 'idle_timer_min', 'idle_timer_max', 'blk_chance', + 'atk_chance', 'unused2', 'atk_brecoil', 'atk_bunc', 'atk_bh_2_h', + 'p_atk_chance', 'unused3', 'p_atk_brecoil', 'p_atk_bunc', + 'p_atk_normal', 'p_atk_for', 'p_atk_back', 'p_atk_l', 'p_atk_r', + 'unused4', 'hold_timer_min', 'hold_timer_max', + (_csty_flags1, 'csty_flags1'), 'acro_dodge', 'unused5', + ('r_mult_opt', 1.0), ('r_mult_max', 1.0), ('m_distance', 250.0), + ('r_distance', 1000.0), ('buff_stand', 325.0), ('r_stand', 500.0), + ('group_stand', 325.0), ('rush_chance', 25), 'unused6', + ('rush_mult', 1.0), (_csty_flags2, 'csty_flags2'), old_versions={ '2B2s8f2B2s3fB3s2f5B3s2f2B2s7fB3sf', '2B2s8f2B2s3fB3s2f5B3s2f2B2s7f', '2B2s8f2B2s3fB3s2f5B3s2f2B2s5f', '2B2s8f2B2s3fB3s2f5B3s2f2B2s2f', '2B2s8f2B2s3fB3s2f5B3s2f2B2s', }), - MelOptStruct(b'CSAD', [u'21f'], 'dodgeFMult', 'dodgeFBase', 'encSBase', - 'encSMult', 'dodgeAtkMult', 'dodgeNAtkMult', - 'dodgeBAtkMult', 'dodgeBNAtkMult', 'dodgeFAtkMult', - 'dodgeFNAtkMult', 'blockMult', 'blockBase', - 'blockAtkMult', 'blockNAtkMult', 'atkMult', 'atkBase', - 'atkAtkMult', 'atkNAtkMult', 'atkBlockMult', 'pAtkFBase', - 'pAtkFMult'), + MelOptStruct(b'CSAD', ['21f'], 'dodge_fmult', 'dodge_fbase', + 'enc_sbase', 'enc_smult', 'dodge_atk_mult', 'dodge_natk_mult', + 'dodge_batk_mult', 'dodge_bnatk_mult', 'dodge_fatk_mult', + 'dodge_fnatk_mult', 'block_mult', 'block_base', 'block_atk_mult', + 'block_natk_mult', 'atk_mult', 'atk_base', 'atk_atk_mult', + 'atk_natk_mult', 'atk_block_mult', 'p_atk_fbase', 'p_atk_fmult'), ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreDial(MelRecord): """Dialogue.""" rec_sig = b'DIAL' @@ -887,26 +903,25 @@ class MreDial(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreDoor(MelRecord): """Door.""" rec_sig = b'DOOR' - _flags = Flags.from_names('oblivionGate', 'automatic', 'hidden', - 'minimalUse') - melSet = MelSet( MelEdid(), MelFull(), MelModel(), MelScript(), MelSound(), - MelFid(b'ANAM','soundClose'), - MelFid(b'BNAM','soundLoop'), - MelUInt8Flags(b'FNAM', u'flags', _flags), - MelSorted(MelFids('destinations', MelFid(b'TNAM'))), + MelSoundClose(b'ANAM'), + MelSoundLooping(), + MelDoorFlags(), + MelRandomTeleports(), ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreEfsh(MelRecord): """Effect Shader.""" rec_sig = b'EFSH' @@ -947,6 +962,7 @@ class MreEfsh(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreEnch(MreHasEffects, MelRecord): """Enchantment.""" rec_sig = b'ENCH' @@ -964,6 +980,7 @@ class MreEnch(MreHasEffects, MelRecord): ).with_distributor(_effects_distributor) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreEyes(MelRecord): """Eyes.""" rec_sig = b'EYES' @@ -978,6 +995,7 @@ class MreEyes(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreFact(MelRecord): """Faction.""" rec_sig = b'FACT' @@ -995,6 +1013,7 @@ class MreFact(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreFlor(MelRecord): """Flora.""" rec_sig = b'FLOR' @@ -1009,6 +1028,7 @@ class MreFlor(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreFurn(MelRecord): """Furniture.""" rec_sig = b'FURN' @@ -1026,10 +1046,12 @@ class MreFurn(MelRecord): ) __slots__ = melSet.getSlotsUsed() -class MreGmst(MreGmstBase): +#------------------------------------------------------------------------------ +class MreGmst(AMreGmst): """Game Setting.""" __slots__ = () +#------------------------------------------------------------------------------ class MreGras(MelRecord): """Grass.""" rec_sig = b'GRAS' @@ -1046,6 +1068,7 @@ class MreGras(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreHair(MelRecord): """Hair.""" rec_sig = b'HAIR' @@ -1061,6 +1084,7 @@ class MreHair(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreIdle(MelRecord): """Idle Animation.""" rec_sig = b'IDLE' @@ -1076,6 +1100,7 @@ class MreIdle(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreInfo(MelRecord): """Dialog Response.""" rec_sig = b'INFO' @@ -1103,6 +1128,7 @@ class MreInfo(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreIngr(MreHasEffects, MelRecord): """Ingredient.""" rec_sig = b'INGR' @@ -1123,6 +1149,7 @@ class MreIngr(MreHasEffects, MelRecord): ).with_distributor(_effects_distributor) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreKeym(MelRecord): """Key.""" rec_sig = b'KEYM' @@ -1137,6 +1164,7 @@ class MreKeym(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreLigh(MelRecord): """Light.""" rec_sig = b'LIGH' @@ -1161,6 +1189,7 @@ class MreLigh(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreLscr(MelRecord): """Load Screen.""" rec_sig = b'LSCR' @@ -1173,6 +1202,7 @@ class MreLscr(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreLtex(MelRecord): """Landscape Texture.""" rec_sig = b'LTEX' @@ -1205,21 +1235,25 @@ class MreLtex(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreLvlc(MreLeveledList): """Leveled Creature.""" rec_sig = b'LVLC' __slots__ = [] +#------------------------------------------------------------------------------ class MreLvli(MreLeveledList): """Leveled Item.""" rec_sig = b'LVLI' __slots__ = [] +#------------------------------------------------------------------------------ class MreLvsp(MreLeveledList): """Leveled Spell.""" rec_sig = b'LVSP' __slots__ = [] +#------------------------------------------------------------------------------ class MreMgef(MelRecord): """Magic Effect.""" rec_sig = b'MGEF' @@ -1512,6 +1546,7 @@ class MreMgef(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreMisc(MelRecord): """Misc. Item.""" rec_sig = b'MISC' @@ -1529,7 +1564,8 @@ class MreMisc(MelRecord): ) __slots__ = melSet.getSlotsUsed() -class MreNpc(MreActorBase): +#------------------------------------------------------------------------------ +class MreNpc(AMreActor): """Non-Player Character.""" rec_sig = b'NPC_' @@ -1614,6 +1650,7 @@ def setRace(self,race): } self.fnam = fnams.get(race, b'\x8e5') # default to Imperial +#------------------------------------------------------------------------------ class MrePack(MelRecord): """AI Package.""" rec_sig = b'PACK' @@ -1655,6 +1692,7 @@ class MrePack(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MrePgrd(MelRecord): """Path Grid.""" rec_sig = b'PGRD' @@ -1679,6 +1717,7 @@ class MrePgrd(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreQust(MelRecord): """Quest.""" rec_sig = b'QUST' @@ -1722,6 +1761,7 @@ class MreQust(MelRecord): }) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreRace(MelRecord): """Race.""" rec_sig = b'RACE' @@ -1817,6 +1857,7 @@ class MreRace(MelRecord): }) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreRefr(MelRecord): """Placed Object.""" rec_sig = b'REFR' @@ -1872,6 +1913,7 @@ def _pre_process_unpacked(self, unpacked_val): }) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreRegn(MelRecord): """Region.""" rec_sig = b'REGN' @@ -1934,6 +1976,7 @@ class MreRegn(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreRoad(MelRecord): """Road. Part of large worldspaces.""" ####Could probably be loaded via MelArray, @@ -1946,6 +1989,7 @@ class MreRoad(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreSbsp(MelRecord): """Subspace.""" rec_sig = b'SBSP' @@ -1956,6 +2000,7 @@ class MreSbsp(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreScpt(MelRecord): """Script.""" rec_sig = b'SCPT' @@ -1966,6 +2011,7 @@ class MreScpt(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreSgst(MreHasEffects, MelRecord): """Sigil Stone.""" rec_sig = b'SGST' @@ -1983,6 +2029,7 @@ class MreSgst(MreHasEffects, MelRecord): ).with_distributor(_effects_distributor) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreSkil(MelRecord): """Skill.""" rec_sig = b'SKIL' @@ -2000,6 +2047,7 @@ class MreSkil(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreSlgm(MelRecord): """Soul Gem.""" rec_sig = b'SLGM' @@ -2016,6 +2064,7 @@ class MreSlgm(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreSoun(MelRecord): """Sound.""" rec_sig = b'SOUN' @@ -2039,6 +2088,7 @@ class MreSoun(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreSpel(MreHasEffects, MelRecord): """Spell.""" rec_sig = b'SPEL' @@ -2093,6 +2143,7 @@ def parse_csv_line(cls, csv_fields, index_dict, reuse=False): """We are called for reading the 'detailed' attributes""" return attr_dict +#------------------------------------------------------------------------------ class MreStat(MelRecord): """Static.""" rec_sig = b'STAT' @@ -2103,6 +2154,7 @@ class MreStat(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreTree(MelRecord): """Tree.""" rec_sig = b'TREE' @@ -2128,6 +2180,7 @@ def _pre_process_unpacked(self, unpacked_val): unpacked_val = unpacked_val[:-1] return super(MelWatrData, self)._pre_process_unpacked(unpacked_val) +#------------------------------------------------------------------------------ class MreWatr(MelRecord): """Water.""" rec_sig = b'WATR' @@ -2163,6 +2216,7 @@ class MreWatr(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreWeap(MelRecord): """Weapon.""" rec_sig = b'WEAP' @@ -2182,6 +2236,7 @@ class MreWeap(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreWrld(MelRecord): """Worldspace.""" rec_sig = b'WRLD' @@ -2204,6 +2259,7 @@ class MreWrld(MelRecord): ) __slots__ = melSet.getSlotsUsed() +#------------------------------------------------------------------------------ class MreWthr(MelRecord): """Weather.""" rec_sig = b'WTHR' diff --git a/Mopy/bash/game/skyrim/__init__.py b/Mopy/bash/game/skyrim/__init__.py index ccc86893d8..38900a7468 100644 --- a/Mopy/bash/game/skyrim/__init__.py +++ b/Mopy/bash/game/skyrim/__init__.py @@ -25,8 +25,7 @@ from os.path import join as _j from ..patch_game import GameInfo, PatchGame -from ... import brec, bolt -from ...brec import MreFlst, MreGlob +from ... import bolt class SkyrimGameInfo(PatchGame): displayName = u'Skyrim' @@ -281,23 +280,24 @@ class Esp(GameInfo.Esp): @classmethod def init(cls): cls._dynamic_import_modules(__name__) + from ...brec import MreColl, MreDebr, MreDlbr, MreDlvw, MreFlst, \ + MreGlob from .records import MreCell, MreWrld, MreFact, MreAchr, MreDial, \ MreInfo, MreCams, MreWthr, MreDual, MreMato, MreVtyp, MreMatt, \ - MreLvsp, MreEnch, MreProj, MreDlbr, MreRfct, MreMisc, MreActi, \ + MreLvsp, MreEnch, MreProj, MrePerk, MreRfct, MreMisc, MreActi, \ MreEqup, MreCpth, MreDoor, MreAnio, MreHazd, MreIdlm, MreEczn, \ MreIdle, MreLtex, MreQust, MreMstt, MreNpc, MreIpds, MrePack, \ - MreGmst, MreRevb, MreClmt, MreDebr, MreSmbn, MreLvli, MreSpel, \ + MreGmst, MreRevb, MreClmt, MreRace, MreSmbn, MreLvli, MreSpel, \ MreKywd, MreLvln, MreAact, MreSlgm, MreRegn, MreFurn, MreGras, \ - MreAstp, MreWoop, MreMovt, MreCobj, MreShou, MreSmen, MreColl, \ + MreAstp, MreWoop, MreMovt, MreCobj, MreShou, MreSmen, MreNavm, \ MreArto, MreAddn, MreSopm, MreCsty, MreAppa, MreArma, MreArmo, \ MreKeym, MreTxst, MreHdpt, MreTes4, MreAlch, MreBook, MreSpgd, \ MreSndr, MreImgs, MreScrl, MreMust, MreFstp, MreFsts, MreMgef, \ MreLgtm, MreMusc, MreClas, MreLctn, MreTact, MreBptd, MreDobj, \ - MreLscr, MreDlvw, MreTree, MreWatr, MreFlor, MreEyes, MreWeap, \ + MreLscr, MreOtft, MreTree, MreWatr, MreFlor, MreEyes, MreWeap, \ MreIngr, MreClfm, MreMesg, MreLigh, MreExpl, MreLcrt, MreStat, \ MreAmmo, MreSmqn, MreImad, MreSoun, MreAvif, MreCont, MreIpct, \ - MreAspc, MreRela, MreEfsh, MreSnct, MreOtft, MrePerk, MreRace, \ - MreNavm + MreAspc, MreRela, MreEfsh, MreSnct cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in (# MreAchr, MreDial, MreInfo, MreAact, MreActi, MreAddn, MreAlch, MreAmmo, MreAnio, MreAppa, MreArma, MreArmo, MreArto, MreAspc, MreAstp, MreAvif, MreBook, @@ -317,6 +317,7 @@ def init(cls): MreRace, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'KYWD', b'LCRT', b'AACT', b'TXST', b'GLOB', b'CLAS', diff --git a/Mopy/bash/game/skyrim/patcher/__init__.py b/Mopy/bash/game/skyrim/patcher/__init__.py index 46d224d687..2fbdc1d6c4 100644 --- a/Mopy/bash/game/skyrim/patcher/__init__.py +++ b/Mopy/bash/game/skyrim/patcher/__init__.py @@ -495,8 +495,8 @@ b'ARMO': (u'pickupSound', u'dropSound'), b'ASPC': ('sound', 'use_sound_from_region', 'aspc_reverb'), b'BOOK': (u'pickupSound', u'dropSound'), - b'CONT': ('sound', 'soundClose'), - b'DOOR': ('sound', 'soundClose', 'soundLoop'), + b'CONT': ('sound', 'sound_close'), + b'DOOR': ('sound', 'sound_close', 'sound_looping'), b'EFSH': (u'ambientSound',), ##: This is also in graphicsTypes! b'EXPL': (u'sound1', u'sound2'), b'FLOR': ('sound',), diff --git a/Mopy/bash/game/skyrim/records.py b/Mopy/bash/game/skyrim/records.py index 0be10fa0f4..16a7c82656 100644 --- a/Mopy/bash/game/skyrim/records.py +++ b/Mopy/bash/game/skyrim/records.py @@ -24,23 +24,23 @@ from ... import bush from ...bolt import Flags, structs_cache, TrimmedFlags from ...brec import MelRecord, MelGroups, MelStruct, FID, MelAttx, MelRace, \ - MelGroup, MelString, MreLeveledListBase, MelSet, MelFid, MelNull, \ - MelOptStruct, MelFids, MreHeaderBase, MelBase, MelSimpleArray, MelWeight, \ - MreGmstBase, MelLString, MelMODS, MelColorInterpolator, MelRegions, \ + MelGroup, MelString, AMreLeveledList, MelSet, MelFid, MelNull, \ + MelOptStruct, MelFids, AMreHeader, MelBase, MelSimpleArray, MelWeight, \ + AMreGmst, MelLString, MelMODS, MelColorInterpolator, MelRegions, \ MelValueInterpolator, MelUnion, AttrValDecider, MelRegnEntrySubrecord, \ PartialLoadDecider, FlagDecider, MelFloat, MelSInt8, MelSInt32, MelUInt8, \ MelUInt16, MelUInt32, MelActionFlags, MelCounter, MelRaceData, MelBaseR, \ MelPartialCounter, MelBounds, null3, null4, MelSequential, MelKeywords, \ MelTruncatedStruct, MelIcons, MelIcons2, MelIcon, MelIco2, MelEdid, \ MelFull, MelArray, MelWthrColors, MelFactions, MelReadOnly, MelRelations, \ - MreActorBase, MreWithItems, MelRef3D, MelXlod, MelActiFlags, AMelNvnm, \ + AMreActor, AMreWithItems, MelRef3D, MelXlod, MelActiFlags, AMelNvnm, \ MelWorldBounds, MelEnableParent, MelRefScale, MelMapMarker, MelMdob, \ MelEnchantment, MelDecalData, MelDescription, MelSInt16, MelSkipInterior, \ MelSoundPickupDrop, MelActivateParents, BipedFlags, MelColor, \ MelColorO, MelSpells, MelFixedString, MelUInt8Flags, MelUInt16Flags, \ - MelUInt32Flags, MelOwnership, MelDebrData, MelWeatherTypes, AMelVmad, \ + MelUInt32Flags, MelOwnership, MelClmtWeatherTypes, AMelVmad, \ MelActorSounds, MelFactionRanks, MelSorted, MelReflectedRefractedBy, \ - perk_effect_key, MelValueWeight, MelCoed, MelSound, MelWaterType, \ + perk_effect_key, MelValueWeight, MelSound, MelWaterType, \ MelSoundActivation, MelInteractionKeyword, MelConditionList, MelAddnDnam, \ MelConditions, ANvnmContext, MelNodeIndex, MelEquipmentType, MelAlchEnit, \ MelEffects, AMelLLItems, MelUnloadEvent, MelShortName, AVmadContext, \ @@ -48,7 +48,10 @@ MelArmaDnam, MelArmaModels, MelArmaSkins, MelAdditionalRaces, MelBamt, \ MelFootstepSound, MelArtObject, MelTemplateArmor, MelArtType, \ MelAspcRdat, MelAspcBnam, MelAstpTitles, MelAstpData, MelBookText, \ - MelBookDescription, MelInventoryArt, MelUnorderedGroups, MelExtra + MelBookDescription, MelInventoryArt, MelUnorderedGroups, MelExtra, \ + MelImageSpaceMod, MelClmtTiming, MelClmtTextures, MelCobjOutput, \ + MelSoundClose, AMelItems, MelContData, MelCpthShared, MelDoorFlags, \ + MelRandomTeleports, MelSoundLooping, MelDualData from ...exception import ModSizeError _is_sse = bush.game.fsName in ( @@ -199,19 +202,8 @@ def __init__(self, attr): ) #------------------------------------------------------------------------------ -class MelItems(MelSorted): - """Wraps MelGroups for the common task of defining a list of items.""" - def __init__(self): - super(MelItems, self).__init__(MelGroups('items', - MelStruct(b'CNTO', [u'I', u'i'], (FID, u'item'), u'count'), - MelCoed(), - ), sort_by_attrs=('item', 'count', 'itemCondition', 'owner', 'glob')) - -class MelItemsCounter(MelCounter): - """Wraps MelCounter for the common task of defining an items counter.""" - def __init__(self): - super(MelItemsCounter, self).__init__(MelUInt32(b'COCT', 'item_count'), - counts='items') +class MelItems(AMelItems): + """Handles the COCT/CNTO/COED subrecords defining items.""" #------------------------------------------------------------------------------ class MelLinkedReferences(MelSorted): @@ -225,12 +217,11 @@ def __init__(self): #------------------------------------------------------------------------------ class MelLLItems(AMelLLItems): - """Handles the LVLO and LLCT subrecords defining leveled list items""" + """Handles the LLCT/LVLO/COED subrecords defining leveled list entries.""" def __init__(self, with_coed=True): - super().__init__([ - MelStruct(b'LVLO', ['H', '2s', 'I', 'H', '2s'], 'level', - 'unknown1', (FID, 'listId'), ('count', 1), 'unknown2')], - with_coed) + super().__init__(MelStruct(b'LVLO', ['H', '2s', 'I', 'H', '2s'], + 'level', 'unknown1', (FID, 'listId'), ('count', 1), 'unknown2'), + with_coed=with_coed) #------------------------------------------------------------------------------ class MelLocation(MelUnion): @@ -348,7 +339,7 @@ def __init__(self): #------------------------------------------------------------------------------ # Skyrim Records -------------------------------------------------------------- #------------------------------------------------------------------------------ -class MreTes4(MreHeaderBase): +class MreTes4(AMreHeader): """TES4 Record. File header.""" rec_sig = b'TES4' _post_masters_sigs = {b'SCRN', b'INTV', b'INCC', b'ONAM'} @@ -358,9 +349,9 @@ class MreTes4(MreHeaderBase): ('nextObject', 0x800)), MelNull(b'OFST'), # obsolete MelNull(b'DELE'), # obsolete - MreHeaderBase.MelAuthor(), - MreHeaderBase.MelDescription(), - MreHeaderBase.MelMasterNames(), + AMreHeader.MelAuthor(), + AMreHeader.MelDescription(), + AMreHeader.MelMasterNames(), MelSimpleArray('overrides', MelFid(b'ONAM')), MelBase(b'SCRN', 'screenshot'), MelBase(b'INTV', 'unknownINTV'), @@ -763,24 +754,19 @@ class MreCams(MelRecord): """Camera Shot.""" rec_sig = b'CAMS' - CamsFlagsFlags = Flags.from_names( - (0, 'positionFollowsLocation'), - (1, 'rotationFollowsTarget'), - (2, 'dontFollowBone'), - (3, 'firstPersonCamera'), - (4, 'noTracer'), - (5, 'startAtTimeZero'), - ) + _cams_flags = Flags.from_names('position_follows_location', + 'rotation_follows_target', 'dont_follow_bone', 'first_person_camera', + 'no_tracer', 'start_at_time_zero') melSet = MelSet( MelEdid(), MelModel(), - MelTruncatedStruct(b'DATA', [u'4I', u'7f'], 'action', 'location', 'target', - (CamsFlagsFlags, u'flags'), 'timeMultPlayer', - 'timeMultTarget', 'timeMultGlobal', 'maxTime', - 'minTime', 'targetPctBetweenActors', - 'nearTargetDistance', old_versions={'4I6f'}), - MelFid(b'MNAM','imageSpaceModifier',), + MelTruncatedStruct(b'DATA', ['4I', '7f'], 'cams_action', + 'cams_location', 'cams_target', (_cams_flags, 'cams_flags'), + 'time_mult_player', 'time_mult_target', 'time_mult_global', + 'cams_max_time', 'cams_min_time', 'target_pct_between_actors', + 'near_target_distance', old_versions={'4I6f'}), + MelImageSpaceMod(), ) __slots__ = melSet.getSlotsUsed() @@ -921,71 +907,44 @@ class MreClmt(MelRecord): melSet = MelSet( MelEdid(), - MelWeatherTypes(), - MelString(b'FNAM','sunPath',), - MelString(b'GNAM','glarePath',), + MelClmtWeatherTypes(), + MelClmtTextures(), MelModel(), - MelStruct(b'TNAM', [u'6B'],'riseBegin','riseEnd','setBegin','setEnd','volatility','phaseLength',), + MelClmtTiming(), ) __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreCobj(MreWithItems): - """Constructible Object (Recipes).""" +class MreCobj(AMreWithItems): + """Constructible Object.""" rec_sig = b'COBJ' isKeyedByEid = True # NULL fids are acceptable melSet = MelSet( MelEdid(), - MelItemsCounter(), MelItems(), MelConditionList(), - MelFid(b'CNAM','resultingItem'), - MelFid(b'BNAM','craftingStation'), - MelUInt16(b'NAM1', 'resultingQuantity'), - ) - __slots__ = melSet.getSlotsUsed() - -#------------------------------------------------------------------------------ -class MreColl(MelRecord): - """Collision Layer.""" - rec_sig = b'COLL' - - CollisionLayerFlags = Flags.from_names('triggerVolume', 'sensor', - 'navmeshObstacle') - - melSet = MelSet( - MelEdid(), - MelDescription(), - MelUInt32(b'BNAM', 'layerID'), - MelColor(b'FNAM'), - MelUInt32Flags(b'GNAM', u'flags', CollisionLayerFlags,), - MelString(b'MNAM', u'col_layer_name',), - MelUInt32(b'INTV', 'interactablesCount'), - MelSorted(MelSimpleArray('collidesWith', MelFid(b'CNAM'))), + MelCobjOutput(), + MelUInt16(b'NAM1', 'created_object_count'), ) __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreCont(MreWithItems): +class MreCont(AMreWithItems): """Container.""" rec_sig = b'CONT' - ContTypeFlags = Flags.from_names('allowSoundsWhenAnimation', 'respawns', - 'showOwner') - melSet = MelSet( MelEdid(), MelVmad(), MelBounds(), MelFull(), MelModel(), - MelItemsCounter(), MelItems(), MelDestructible(), - MelStruct(b'DATA', [u'B', u'f'],(ContTypeFlags, u'flags'),'weight'), + MelContData(), MelSound(), - MelFid(b'QNAM','soundClose'), + MelSoundClose(), ) __slots__ = melSet.getSlotsUsed() @@ -997,9 +956,7 @@ class MreCpth(MelRecord): melSet = MelSet( MelEdid(), MelConditionList(), - MelSimpleArray('relatedCameraPaths', MelFid(b'ANAM')), - MelUInt8(b'DATA', 'cameraZoom'), - MelFids('cameraShots', MelFid(b'SNAM')), + MelCpthShared(), ) __slots__ = melSet.getSlotsUsed() @@ -1008,39 +965,37 @@ class MreCsty(MelRecord): """Combat Style.""" rec_sig = b'CSTY' - CstyTypeFlags = Flags.from_names('dueling', 'flanking', - 'allowDualWielding') - - melSet = MelSet( - MelEdid(), - # esm = Equipment Score Mult - MelStruct(b'CSGD', [u'10f'],'offensiveMult','defensiveMult','groupOffensiveMult', - 'esmMelee','esmMagic','esmRanged','esmShout','esmUnarmed','esmStaff', - 'avoidThreatChance',), - MelBase(b'CSMD','unknownValue'), - MelStruct(b'CSME', [u'8f'],'atkStaggeredMult','powerAtkStaggeredMult','powerAtkBlockingMult', - 'bashMult','bashRecoilMult','bashAttackMult','bashPowerAtkMult','specialAtkMult',), - MelStruct(b'CSCR', [u'4f'],'circleMult','fallbackMult','flankDistance','stalkTime',), - MelFloat(b'CSLR', 'strafeMult'), - MelStruct(b'CSFL', [u'8f'],'hoverChance','diveBombChance','groundAttackChance','hoverTime', - 'groundAttackTime','perchAttackChance','perchAttackTime','flyingAttackChance',), - MelUInt32Flags(b'DATA', u'flags', CstyTypeFlags), - ) - __slots__ = melSet.getSlotsUsed() - -#------------------------------------------------------------------------------ -class MreDebr(MelRecord): - """Debris.""" - rec_sig = b'DEBR' - - dataFlags = Flags.from_names('hasCollissionData') + _csty_flags = Flags.from_names('dueling', 'flanking', + 'allow_dual_wielding') melSet = MelSet( MelEdid(), - MelGroups('models', - MelDebrData(), - MelBase(b'MODT','modt_p'), - ), + MelStruct(b'CSGD', ['10f'], 'general_offensive_mult', + 'general_defensive_mult', 'general_group_offensive_mult', + 'general_equipment_score_mult_melee', + 'general_equipment_score_mult_magic', + 'general_equipment_score_mult_ranged', + 'general_equipment_score_mult_shout', + 'general_equipment_score_mult_unarmed', + 'general_equipment_score_mult_staff', + 'general_avoid_threat_chance'), + MelBase(b'CSMD', 'unknown1'), + MelStruct(b'CSME', ['8f'], 'melee_attack_staggered_mult', + 'melee_power_attack_staggered_mult', + 'melee_power_attack_blocking_mult', + 'melee_bash_mult', 'melee_bash_recoil_mult', + 'melee_bash_attack_mult', 'melee_bash_power_attack_mult', + 'melee_special_attack_mult'), + MelStruct(b'CSCR', ['4f'], 'close_range_circle_mult', + 'close_range_fallback_mult', 'close_range_flank_distance', + 'close_range_stalk_time'), + MelFloat(b'CSLR', 'long_range_strafe_mult'), + MelStruct(b'CSFL', ['8f'], 'flight_hover_chance', + 'flight_dive_bomb_chance', 'flight_ground_attack_chance', + 'flight_hover_time', 'flight_ground_attack_time', + 'flight_perch_attack_chance', 'flight_perch_attack_time', + 'flight_flying_attack_chance'), + MelUInt32Flags(b'DATA', 'csty_flags', _csty_flags), ) __slots__ = melSet.getSlotsUsed() @@ -1064,39 +1019,6 @@ class MreDial(MelRecord): ) __slots__ = melSet.getSlotsUsed() -#------------------------------------------------------------------------------ -class MreDlbr(MelRecord): - """Dialog Branch.""" - rec_sig = b'DLBR' - - DialogBranchFlags = Flags.from_names('topLevel', 'blocking', 'exclusive') - - melSet = MelSet( - MelEdid(), - MelFid(b'QNAM','quest',), - MelUInt32(b'TNAM', u'category'), - MelUInt32Flags(b'DNAM', u'flags', DialogBranchFlags), - MelFid(b'SNAM','startingTopic',), - ) - __slots__ = melSet.getSlotsUsed() - -#------------------------------------------------------------------------------ -class MreDlvw(MelRecord): - """Dialog View""" - rec_sig = b'DLVW' - - melSet = MelSet( - MelEdid(), - MelFid(b'QNAM','quest',), - MelFids('branches', MelFid(b'BNAM')), - MelGroups('unknownTNAM', - MelBase(b'TNAM','unknown',), - ), - MelBase(b'ENAM','unknownENAM'), - MelBase(b'DNAM','unknownDNAM'), - ) - __slots__ = melSet.getSlotsUsed() - #------------------------------------------------------------------------------ class MreDobj(MelRecord): """Default Object Manager.""" @@ -1105,9 +1027,10 @@ class MreDobj(MelRecord): melSet = MelSet( MelEdid(), # This subrecord can have <=7 bytes of noise at the end - MelExtra(MelSorted(MelArray('objects', - MelStruct(b'DNAM', ['2I'], 'object_use', (FID, 'object_fid')), - ), sort_by_attrs='object_use'), extra_attr='unknown_dnam'), + MelExtra(MelSorted(MelArray('default_objects', + MelStruct(b'DNAM', ['2I'], 'default_object_use', + (FID, 'default_object_fid')), + ), sort_by_attrs='default_object_use'), extra_attr='unknown_dnam'), ) __slots__ = melSet.getSlotsUsed() @@ -1116,14 +1039,6 @@ class MreDoor(MelRecord): """Door.""" rec_sig = b'DOOR' - DoorTypeFlags = Flags.from_names( - (1, 'automatic'), - (2, 'hidden'), - (3, 'minimalUse'), - (4, 'slidingDoor'), - (5, 'doNotOpenInCombatSearch'), - ) - melSet = MelSet( MelEdid(), MelVmad(), @@ -1132,10 +1047,10 @@ class MreDoor(MelRecord): MelModel(), MelDestructible(), MelSound(), - MelFid(b'ANAM','soundClose'), - MelFid(b'BNAM','soundLoop'), - MelUInt8Flags(b'FNAM', u'flags', DoorTypeFlags), - MelSorted(MelFids('random_teleports', MelFid(b'TNAM'))), + MelSoundClose(b'ANAM'), + MelSoundLooping(), + MelDoorFlags(), + MelRandomTeleports(), ) __slots__ = melSet.getSlotsUsed() @@ -1144,14 +1059,10 @@ class MreDual(MelRecord): """Dual Cast Data.""" rec_sig = b'DUAL' - DualCastDataFlags = Flags.from_names('hitEffectArt', 'projectile', - 'explosion') - melSet = MelSet( MelEdid(), MelBounds(), - MelStruct(b'DATA', [u'6I'],(FID,'projectile'),(FID,'explosion'),(FID,'effectShader'), - (FID,'hitEffectArt'),(FID,'impactDataSet'),(DualCastDataFlags, u'flags'),), + MelDualData(), ) __slots__ = melSet.getSlotsUsed() @@ -1314,7 +1225,7 @@ class MreExpl(MelRecord): MelFull(), MelModel(), MelEnchantment(), - MelFid(b'MNAM','imageSpaceModifier'), + MelImageSpaceMod(), MelTruncatedStruct( b'DATA', [u'6I', u'5f', u'2I'], (FID, u'light'), (FID, u'sound1'), (FID, u'sound2'), (FID, u'impactDataset'), @@ -1518,7 +1429,7 @@ class MreFurn(MelRecord): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreGmst(MreGmstBase): +class MreGmst(AMreGmst): """Game Setting.""" isKeyedByEid = True # NULL fids are acceptable. __slots__ = () @@ -1561,7 +1472,7 @@ class MreHazd(MelRecord): MelBounds(), MelFull(), MelModel(), - MelFid(b'MNAM','imageSpaceModifier'), + MelImageSpaceMod(), MelStruct(b'DATA', [u'I', u'4f', u'5I'],'limit','radius','lifetime', 'imageSpaceRadius','targetInterval',(HazdTypeFlags, u'flags'), (FID,'spell'),(FID,'light'),(FID,'impactDataSet'),(FID,'sound'),), @@ -2124,7 +2035,7 @@ class MreLtex(MelRecord): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreLvli(MreLeveledListBase): +class MreLvli(AMreLeveledList): """Leveled Item.""" rec_sig = b'LVLI' top_copy_attrs = ('chanceNone','glob',) @@ -2133,14 +2044,14 @@ class MreLvli(MreLeveledListBase): MelEdid(), MelBounds(), MelUInt8(b'LVLD', 'chanceNone'), - MelUInt8Flags(b'LVLF', u'flags', MreLeveledListBase._flags), + MelUInt8Flags(b'LVLF', u'flags', AMreLeveledList._flags), MelFid(b'LVLG', 'glob'), MelLLItems(), ) __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreLvln(MreLeveledListBase): +class MreLvln(AMreLeveledList): """Leveled NPC.""" rec_sig = b'LVLN' top_copy_attrs = ('chanceNone','model','modt_p',) @@ -2149,7 +2060,7 @@ class MreLvln(MreLeveledListBase): MelEdid(), MelBounds(), MelUInt8(b'LVLD', 'chanceNone'), - MelUInt8Flags(b'LVLF', u'flags', MreLeveledListBase._flags), + MelUInt8Flags(b'LVLF', u'flags', AMreLeveledList._flags), MelFid(b'LVLG', 'glob'), MelLLItems(), MelString(b'MODL','model'), @@ -2158,7 +2069,7 @@ class MreLvln(MreLeveledListBase): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreLvsp(MreLeveledListBase): +class MreLvsp(AMreLeveledList): """Leveled Spell.""" rec_sig = b'LVSP' @@ -2168,7 +2079,7 @@ class MreLvsp(MreLeveledListBase): MelEdid(), MelBounds(), MelUInt8(b'LVLD', 'chanceNone'), - MelUInt8Flags(b'LVLF', u'flags', MreLeveledListBase._flags), + MelUInt8Flags(b'LVLF', u'flags', AMreLeveledList._flags), MelLLItems(with_coed=False), ) __slots__ = melSet.getSlotsUsed() @@ -2439,7 +2350,7 @@ class MreNavm(MelRecord): __slots__ = melSet.getSlotsUsed() #------------------------------------------------------------------------------ -class MreNpc(MreActorBase): +class MreNpc(AMreActor): """Non-Player Character.""" rec_sig = b'NPC_' @@ -2524,7 +2435,6 @@ class MreNpc(MreActorBase): MelSorted(MelGroups('perks', MelOptStruct(b'PRKR', [u'I', u'B', u'3s'],(FID, 'perk'),'rank','prkrUnused'), ), sort_by_attrs='perk'), - MelItemsCounter(), MelItems(), MelStruct(b'AIDT', [u'B', u'B', u'B', u'B', u'B', u'B', u'B', u'B', u'I', u'I', u'I'], 'aggression', 'confidence', 'energyLevel', 'responsibility', 'mood', 'assistance', @@ -2995,7 +2905,6 @@ class MreQust(MelRecord): ), MelConditionList(), MelKeywords(), - MelItemsCounter(), MelItems(), MelFid(b'SPOR','spectatorOverridePackageList'), MelFid(b'OCOR','observeDeadBodyOverridePackageList'), diff --git a/Mopy/bash/game/skyrimse/__init__.py b/Mopy/bash/game/skyrimse/__init__.py index ac8f8c5fbd..ad6d984858 100644 --- a/Mopy/bash/game/skyrimse/__init__.py +++ b/Mopy/bash/game/skyrimse/__init__.py @@ -24,8 +24,6 @@ from ..skyrim import SkyrimGameInfo from .. import WS_COMMON -from ... import brec -from ...brec import MreFlst, MreGlob class SkyrimSEGameInfo(SkyrimGameInfo): displayName = u'Skyrim Special Edition' @@ -273,14 +271,14 @@ class Esp(SkyrimGameInfo.Esp): @classmethod def init(cls): cls._dynamic_import_modules(__name__) - # First import from skyrimse.records file + from ...brec import MreColl, MreDebr, MreDlbr, MreDlvw, MreFlst, \ + MreGlob from .records import MreVoli, MreLens - # then import rest of records from skyrim.records from ..skyrim.records import MreAact, MreAchr, MreActi, MreAddn, \ MreAlch, MreAnio, MreAppa, MreArma, MreArmo, MreArto, MreAspc, \ MreAstp, MreAvif, MreBook, MreBptd, MreCams, MreCell, MreClas, \ - MreClfm, MreClmt, MreCobj, MreColl, MreCont, MreCpth, MreCsty, \ - MreDebr, MreDial, MreDlbr, MreDlvw, MreDobj, MreDoor, MreDual, \ + MreClfm, MreClmt, MreCobj, MreNavm, MreCont, MreCpth, MreCsty, \ + MreRace, MreDial, MreWthr, MreWeap, MreDobj, MreDoor, MreDual, \ MreEczn, MreEfsh, MreEnch, MreEqup, MreExpl, MreEyes, MreFact, \ MreFlor, MreFstp, MreFsts, MreFurn, MreGmst, MreGras, MrePack, \ MreHazd, MreHdpt, MreTes4, MreIdle, MreIdlm, MreImad, MreImgs, \ @@ -291,8 +289,7 @@ def init(cls): MreRela, MreRevb, MreRfct, MreScrl, MreShou, MreSlgm, MreSmbn, \ MreSmen, MreSmqn, MreSnct, MreSndr, MreSopm, MreSoun, MreSpel, \ MreSpgd, MreTact, MreTree, MreTxst, MreVtyp, MreWoop, MreWrld, \ - MreAmmo, MreLtex, MreMato, MreStat, MreWatr, MreWeap, MreWthr, \ - MreRace, MreNavm + MreAmmo, MreLtex, MreMato, MreStat, MreWatr cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in ( # MreAchr, MreDial, MreInfo, MreAact, MreActi, MreAddn, MreAlch, MreAmmo, MreAnio, MreAppa, @@ -313,6 +310,7 @@ def init(cls): MrePack, MreFact, MreRace, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'KYWD', b'LCRT', b'AACT', b'TXST', b'GLOB', b'CLAS', diff --git a/Mopy/bash/game/skyrimvr/__init__.py b/Mopy/bash/game/skyrimvr/__init__.py index fcd8f01017..709784eb96 100644 --- a/Mopy/bash/game/skyrimvr/__init__.py +++ b/Mopy/bash/game/skyrimvr/__init__.py @@ -20,12 +20,9 @@ # https://github.com/wrye-bash # # ============================================================================= - """GameInfo override for TES V: Skyrim VR.""" from ..skyrimse import SkyrimSEGameInfo -from ... import brec -from ...brec import MreFlst, MreGlob class SkyrimVRGameInfo(SkyrimSEGameInfo): displayName = u'Skyrim VR' @@ -77,14 +74,14 @@ class Bain(SkyrimSEGameInfo.Bain): @classmethod def init(cls): cls._dynamic_import_modules(__name__) - # First import from skyrimse.records file + from ...brec import MreColl, MreDebr, MreDlbr, MreDlvw, MreFlst, \ + MreGlob from ..skyrimse.records import MreVoli, MreLens - # then import rest of records from skyrim.records from ..skyrim.records import MreAact, MreAchr, MreActi, MreAddn, \ MreAlch, MreAnio, MreAppa, MreArma, MreArmo, MreArto, MreAspc, \ MreAstp, MreAvif, MreBook, MreBptd, MreCams, MreCell, MreClas, \ - MreClfm, MreClmt, MreCobj, MreColl, MreCont, MreCpth, MreCsty, \ - MreDebr, MreDial, MreDlbr, MreDlvw, MreDobj, MreDoor, MreDual, \ + MreClfm, MreClmt, MreCobj, MreNavm, MreCont, MreCpth, MreCsty, \ + MreRace, MreDial, MreWthr, MreWeap, MreDobj, MreDoor, MreDual, \ MreEczn, MreEfsh, MreEnch, MreEqup, MreExpl, MreEyes, MreFact, \ MreFlor, MreFstp, MreFsts, MreFurn, MreGmst, MreGras, MrePack, \ MreHazd, MreHdpt, MreTes4, MreIdle, MreIdlm, MreImad, MreImgs, \ @@ -95,8 +92,7 @@ def init(cls): MreRela, MreRevb, MreRfct, MreScrl, MreShou, MreSlgm, MreSmbn, \ MreSmen, MreSmqn, MreSnct, MreSndr, MreSopm, MreSoun, MreSpel, \ MreSpgd, MreTact, MreTree, MreTxst, MreVtyp, MreWoop, MreWrld, \ - MreAmmo, MreLtex, MreMato, MreStat, MreWatr, MreWeap, MreWthr, \ - MreRace, MreNavm + MreAmmo, MreLtex, MreMato, MreStat, MreWatr cls.mergeable_sigs = {clazz.rec_sig: clazz for clazz in ( # MreAchr, MreDial, MreInfo, MreAact, MreActi, MreAddn, MreAlch, MreAmmo, MreAnio, MreAppa, @@ -117,6 +113,7 @@ def init(cls): MrePack, MreFact, MreRace, )} # Setting RecordHeader class variables -------------------------------- + from ... import brec header_type = brec.RecordHeader header_type.top_grup_sigs = [ b'GMST', b'KYWD', b'LCRT', b'AACT', b'TXST', b'GLOB', b'CLAS',