File size: 3,974 Bytes
bb3fbf9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import logging
from typing import Any, Dict, Iterator

try:
    pass
except:
    pass

logger = logging.getLogger(__name__)


class StateMixin:
    """Base class for checking equality of state objects."""

    def __eq__(self, other):
        return isinstance(self, type(other)) and self.__dict__ == other.__dict__

    def copy_slots_to(self, target: "StateMixin") -> None:
        """Copy all fields defined in __slots__ to the target object."""
        if not hasattr(self, "__slots__"):
            return

        for name in self.__slots__:
            if hasattr(self, name):
                val = getattr(self, name)
                # Handle mutable types that need explicit copying
                if isinstance(val, list):
                    setattr(target, name, val[:])
                elif isinstance(val, set):
                    setattr(target, name, set(val))
                elif isinstance(val, dict):
                    setattr(target, name, dict(val))
                elif hasattr(val, "copy"):
                    setattr(target, name, val.copy())
                else:
                    setattr(target, name, val)


# ----------------------------------------------------------------------------
# Unique ID (UID) System
# ----------------------------------------------------------------------------
# Masks: 20 bits for Base ID, 12 bits for Instance
BASE_ID_MASK = 0xFFFFF
INSTANCE_SHIFT = 20


def get_base_id(uid: int) -> int:
    """Extract the base card definition ID from a potentially combined UID."""
    return uid & BASE_ID_MASK


def get_instance_index(uid: int) -> int:
    """Extract the instance index from a UID."""
    return uid >> INSTANCE_SHIFT


def create_uid(base_id: int, instance_index: int) -> int:
    """Create a unique instance ID from a base ID and an index."""
    # Safety check: base_id must fit in 20 bits
    if base_id > BASE_ID_MASK:
        logger.warning(f"Base ID {base_id} exceeds mask {BASE_ID_MASK}!")
    return (base_id & BASE_ID_MASK) | (instance_index << INSTANCE_SHIFT)


class MaskedDB(dict):
    """

    A dictionary wrapper that automatically masks unique instance IDs (UIDs)

    to retrieve data associated with the base ID.



    This allows the game state to essentially 'ignore' the instance part of an ID

    when looking up static card data.

    """

    def __init__(self, data: Dict[int, Any]):
        super().__init__(data)
        self._data = data

    def __getitem__(self, key: int) -> Any:
        # Resolve UID to base ID
        try:
            base_id = int(key) & BASE_ID_MASK
            if base_id in self._data:
                return self._data[base_id]
            # Fallback to string key for JSON-loaded data
            return self._data[str(base_id)]
        except (ValueError, TypeError):
            raise KeyError(key)

    def __contains__(self, key: object) -> bool:
        try:
            base_id = int(key) & BASE_ID_MASK
            return base_id in self._data or str(base_id) in self._data
        except (ValueError, TypeError):
            return False

    def get(self, key: int, default: Any = None) -> Any:
        try:
            if key is None:
                return default
            base_id = int(key) & BASE_ID_MASK
            if base_id in self._data:
                return self._data[base_id]
            # Fallback to string key
            return self._data.get(str(base_id), default)
        except (ValueError, TypeError):
            return default

    def __len__(self) -> int:
        return len(self._data)

    def __iter__(self) -> Iterator[int]:
        # Iterate over ORIGINAL base IDs (keys of _data)
        return iter(self._data)

    def keys(self):
        return self._data.keys()

    def values(self):
        return self._data.values()

    def items(self):
        return self._data.items()