@@ -148,7 +148,8 @@ private static VersionData BuildVersionData(ItemStatCostEntry[] itemStatCost, It
148148 ItemStatCost = itemStatCost ,
149149 ItemTypes = itemTypes ,
150150 Items = items ,
151- ItemCodeToIndex = BuildItemCodeIndex ( items )
151+ ItemCodeToIndex = BuildItemCodeIndex ( items ) ,
152+ TypeDescendants = BuildTypeDescendants ( itemTypes )
152153 } ;
153154 }
154155
@@ -507,54 +508,86 @@ public ItemTypeInfo GetItemInfo(int itemIndex, uint saveVersion)
507508 hasVariableGfx = data . ItemTypes [ typeIndex ] . VarInvGfx > 0 ;
508509 }
509510
511+ uint typeCode = typeIndex < data . ItemTypes . Length ? data . ItemTypes [ typeIndex ] . Code : 0 ;
512+
510513 return new ItemTypeInfo (
511514 CompactSave : item . CompactSave != 0 ,
512515 HasVariableGfx : hasVariableGfx ,
513- IsArmor : IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . Armor ) ,
514- IsWeapon : IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . Weapon ) ,
515- IsGold : IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . Gold ) ,
516+ IsArmor : data . IsDescendant ( ItemTypeCodes . Armor , typeCode ) ,
517+ IsWeapon : data . IsDescendant ( ItemTypeCodes . Weapon , typeCode ) ,
518+ IsGold : data . IsDescendant ( ItemTypeCodes . Gold , typeCode ) ,
516519 IsStackable : item . Stackable != 0 ,
517- IsCharm : IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . Charm ) ,
518- IsBodyPart : IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . BodyPart ) ,
519- IsPlayerBodyPart : IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . PlayerBodyPart ) ,
520- IsScrollOrBook : IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . Scroll ) ||
521- IsTypeOrDescendant ( data . ItemTypes , typeIndex , ItemTypeCodes . Book ) ,
520+ IsCharm : data . IsDescendant ( ItemTypeCodes . Charm , typeCode ) ,
521+ IsBodyPart : data . IsDescendant ( ItemTypeCodes . BodyPart , typeCode ) ,
522+ IsPlayerBodyPart : data . IsDescendant ( ItemTypeCodes . PlayerBodyPart , typeCode ) ,
523+ IsScrollOrBook : data . IsDescendant ( ItemTypeCodes . Scroll , typeCode ) ||
524+ data . IsDescendant ( ItemTypeCodes . Book , typeCode ) ,
522525 IsQuest : item . Quest != 0 ,
523526 QuestDiffCheck : item . QuestDiffCheck != 0
524527 ) ;
525528 }
526529
527- /// <summary>
528- /// Checks if the given type index is or descends from the target type code.
529- /// Traverses the equiv1/equiv2 parent chain.
530- /// </summary>
531- private static bool IsTypeOrDescendant ( ItemTypeEntry [ ] itemTypes , int typeIndex , uint targetTypeCode )
530+ private static Dictionary < uint , HashSet < uint > > BuildTypeDescendants ( ItemTypeEntry [ ] itemTypes )
532531 {
533- // Use a visited set to prevent infinite loops
534- var visited = new HashSet < int > ( ) ;
535- var stack = new Stack < int > ( ) ;
536- stack . Push ( typeIndex ) ;
532+ var children = new List < int > [ itemTypes . Length ] ;
533+ for ( int i = 0 ; i < itemTypes . Length ; i ++ )
534+ {
535+ children [ i ] = [ ] ;
536+ }
537537
538- while ( stack . Count > 0 )
538+ for ( int i = 0 ; i < itemTypes . Length ; i ++ )
539539 {
540- var current = stack . Pop ( ) ;
541- if ( current < 0 || current >= itemTypes . Length || ! visited . Add ( current ) )
542- continue ;
540+ ref readonly var itemType = ref itemTypes [ i ] ;
541+ if ( itemType . Equiv1 != 0 && itemType . Equiv1 != 0xFFFF )
542+ children [ itemType . Equiv1 ] . Add ( i ) ;
543+ if ( itemType . Equiv2 != 0 && itemType . Equiv2 != 0xFFFF )
544+ children [ itemType . Equiv2 ] . Add ( i ) ;
545+ }
543546
544- ref readonly var itemType = ref itemTypes [ current ] ;
547+ var descendantsByIndex = new HashSet < uint > [ itemTypes . Length ] ;
545548
546- // Check if this type matches the target
547- if ( itemType . Code == targetTypeCode )
548- return true ;
549+ HashSet < uint > ComputeDescendants ( int index )
550+ {
551+ var cached = descendantsByIndex [ index ] ;
552+ if ( cached != null )
553+ return cached ;
549554
550- // Add parent types to check
551- if ( itemType . Equiv1 != 0 && itemType . Equiv1 != 0xFFFF )
552- stack . Push ( itemType . Equiv1 ) ;
553- if ( itemType . Equiv2 != 0 && itemType . Equiv2 != 0xFFFF )
554- stack . Push ( itemType . Equiv2 ) ;
555+ var set = new HashSet < uint > ( ) ;
556+ uint code = itemTypes [ index ] . Code ;
557+ if ( code != 0 )
558+ set . Add ( code ) ;
559+
560+ foreach ( int child in children [ index ] )
561+ {
562+ uint childCode = itemTypes [ child ] . Code ;
563+ if ( childCode != 0 )
564+ set . Add ( childCode ) ;
565+ set . UnionWith ( ComputeDescendants ( child ) ) ;
566+ }
567+
568+ descendantsByIndex [ index ] = set ;
569+ return set ;
555570 }
556571
557- return false ;
572+ var result = new Dictionary < uint , HashSet < uint > > ( ) ;
573+ for ( int i = 0 ; i < itemTypes . Length ; i ++ )
574+ {
575+ uint code = itemTypes [ i ] . Code ;
576+ if ( code == 0 )
577+ continue ;
578+
579+ var set = ComputeDescendants ( i ) ;
580+ if ( ! result . TryGetValue ( code , out var existing ) )
581+ {
582+ result [ code ] = set ;
583+ }
584+ else
585+ {
586+ existing . UnionWith ( set ) ;
587+ }
588+ }
589+
590+ return result ;
558591 }
559592
560593 // Internal data structures
@@ -564,6 +597,14 @@ private class VersionData
564597 public required ItemTypeEntry [ ] ItemTypes { get ; init ; }
565598 public required ItemEntry [ ] Items { get ; init ; }
566599 public required Dictionary < uint , int > ItemCodeToIndex { get ; init ; }
600+ public required Dictionary < uint , HashSet < uint > > TypeDescendants { get ; init ; }
601+
602+ public bool IsDescendant ( uint targetTypeCode , uint typeCode )
603+ {
604+ return typeCode != 0 &&
605+ TypeDescendants . TryGetValue ( targetTypeCode , out var set ) &&
606+ set . Contains ( typeCode ) ;
607+ }
567608 }
568609
569610 private struct ItemStatCostEntry
0 commit comments