diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..c6c7dfc7 --- /dev/null +++ b/404.html @@ -0,0 +1,2057 @@ + + + + + + + + + + + + + + + + + + + AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/_assets/css/api_docs_extra.css b/_assets/css/api_docs_extra.css new file mode 100644 index 00000000..d11574dd --- /dev/null +++ b/_assets/css/api_docs_extra.css @@ -0,0 +1,39 @@ +h5.doc.doc-heading { + font-size: 20px; +} + +.class-attribute-entry { + padding: 10px 10px !important; +} + +.class-attribute-entry .highlight.language-python { + font-size: 14px; +} + +.class-attribute-entry .doc-label-instance-attribute { + font-size: 14px; +} + +/* + | Overwrite certain CSS attributes on source code blocks inside tables to fix styling issues. + */ + +.inner-table-code-block-overwrite details { + margin: 1.5625em 0 !important; +} + +.inner-table-code-block-overwrite table.highlighttable tr { + display: flex !important; +} + +.inner-table-code-block-overwrite table.highlighttable tr td { + border-top: none !important; +} + +.inner-table-code-block-overwrite table.highlighttable tr td:first-child { + padding: .7720588235em 0 .7720588235em 1.1764705882em !important; +} + +.inner-table-code-block-overwrite table.highlighttable tr td:not(:first-child) { + padding: 0 !important; +} diff --git a/_assets/css/api_docs_toc.css b/_assets/css/api_docs_toc.css new file mode 100644 index 00000000..f8ac0072 --- /dev/null +++ b/_assets/css/api_docs_toc.css @@ -0,0 +1,16 @@ +/** + | + | Hide nested content (1) and rotate arrows (2) to make menus look entirely closed. + | This stays until JS takes over and actually closes them and disables these styles using: `.disable-instant-css` + | + */ + +/* (1) */ +.md-nav__toggle:not(.disable-instant-css) ~ .md-nav:not([data-md-level="0"]):not([data-md-level="1"]) { + display: none; +} + +/* (2) */ +.md-nav__item--nested .md-nav__toggle:not(.disable-instant-css) ~ .md-nav__link .md-nav__icon.md-icon { + transform: rotate(0deg); +} diff --git a/_assets/js/api_docs_toc.js b/_assets/js/api_docs_toc.js new file mode 100644 index 00000000..48512ba2 --- /dev/null +++ b/_assets/js/api_docs_toc.js @@ -0,0 +1,58 @@ +const collapseToC = () => { + if (window.screen.width < 1220) { + document.querySelectorAll('.md-nav__toggle').forEach((e) => e.classList.add('disable-instant-css')) + document.querySelectorAll('.md-nav__item--nested .md-nav__toggle').forEach((e) => e.classList.add('disable-instant-css')) + + return; + } + + /* + | Update window path name to only useful part + | + | '/AoE2ScenarioParser/api_docs/player/player/' + | ['','AoE2ScenarioParser','api_docs','player','player',''] + | ['player','player'] + */ + const path = window.location.pathname.split('/').slice(3, -1); + console.log("Path", path); + + const primary = '.md-nav--primary .md-nav__item.md-nav__item--nested'; + const selector = '.md-nav__toggle ~ .md-nav:not([data-md-level="0"]):not([data-md-level="1"])'; + + const completeSelector = `${primary} ${selector}`; + console.log(`Selector: [${completeSelector}]`) + + let index = 0; + document.querySelectorAll(completeSelector) + .forEach((e) => { + const input = e.parentNode.children[0]; + const label = e.parentNode.children[1]; + + console.log(label, input) + + if (!label || !('click' in label) || index >= path.length) + return; + + const labelName = label.innerText.toLowerCase(); /* Do not trim */ + const pathName = path[index].toLowerCase().replace('_', ''); + + console.log(`\tLabel: [${labelName}] // Path: [${pathName}]`) + + const collapseElement = labelName !== pathName; + + if (collapseElement) { + label.click(); /* Close the menu item */ + } else { + index++; + console.log(`Match! Continue index to: ${index}`) + } + + if (input) { + input.classList.add('disable-instant-css'); + } + }); +} + +window.addEventListener('load', function() { + collapseToC(); +}); diff --git a/api_docs/aoe2_de_scenario/index.html b/api_docs/aoe2_de_scenario/index.html new file mode 100644 index 00000000..111a32a0 --- /dev/null +++ b/api_docs/aoe2_de_scenario/index.html @@ -0,0 +1,2966 @@ + + + + + + + + + + + + + + + + + + + + + + + + + AoE2DEScenario - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

AoE2DEScenario

+ +
+ + + + +
+ + + +
+ + + + + + +

Attributes

+ + + + +

Classes

+ +
+ + + +

+ AoE2DEScenario + + +

+ + +
+

+ Bases: AoE2Scenario

+ + +

Used to represent a scenario with version >= 1.36 (DE). It is the main class that is exposed to the user of the API.

+ +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_de_scenario.py +
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
class AoE2DEScenario(AoE2Scenario):
+    """
+    Used to represent a scenario with version >= 1.36 (DE). It is the main class that is exposed to the user of the API.
+    """
+
+    @property
+    def trigger_manager(self) -> TriggerManagerDE:
+        """The trigger manager of the scenario"""
+        return self._object_manager.managers['Trigger']
+
+    @property
+    def unit_manager(self) -> UnitManagerDE:
+        """The unit manager of the scenario"""
+        return self._object_manager.managers['Unit']
+
+    @property
+    def map_manager(self) -> MapManagerDE:
+        """The map manager of the scenario"""
+        return self._object_manager.managers['Map']
+
+    @property
+    def xs_manager(self) -> XsManagerDE:
+        """The XS manager of the scenario"""
+        return self._object_manager.managers['Xs']
+
+    @property
+    def player_manager(self) -> PlayerManager:
+        """The player manager of the scenario"""
+        return self._object_manager.managers['Player']
+
+    @property
+    def message_manager(self) -> MessageManager:
+        return self._object_manager.managers['Message']
+
+    @property
+    def option_manager(self) -> OptionManager:
+        return self._object_manager.managers['Option']
+
+    @classmethod
+    def from_file(cls: Type[S], path: str, game_version: str = "DE", name: str = "") -> S:
+        """
+        Creates and returns an instance of the AoE2DEScenario class from the given scenario file
+
+        Args:
+            path: The path to the scenario file to create the object from
+            game_version: The version of the game to create the object for
+            name: The name given to this scenario (defaults to the filename without extension)
+
+        Returns:
+            An instance of the AoE2DEScenario class which is the object representation of the given scenario file
+        """
+        return super().from_file(path=path, game_version=game_version, name=name)
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+map_manager: MapManagerDE + + + property + + +
+ +
+ Type: +MapManagerDE + +
+ + + + + +
+ +

The map manager of the scenario

+
+ +
+ +
+ + + + +
+message_manager: MessageManager + + + property + + +
+ +
+ Type: +MessageManager + +
+ + + + + +
+
+ +
+ +
+ + + + +
+option_manager: OptionManager + + + property + + +
+ +
+ Type: +OptionManager + +
+ + + + + +
+
+ +
+ +
+ + + + +
+player_manager: PlayerManager + + + property + + +
+ +
+ Type: +PlayerManager + +
+ + + + + +
+ +

The player manager of the scenario

+
+ +
+ +
+ + + + +
+trigger_manager: TriggerManagerDE + + + property + + +
+ +
+ Type: +TriggerManagerDE + +
+ + + + + +
+ +

The trigger manager of the scenario

+
+ +
+ +
+ + + + +
+unit_manager: UnitManagerDE + + + property + + +
+ +
+ Type: +UnitManagerDE + +
+ + + + + +
+ +

The unit manager of the scenario

+
+ +
+ +
+ + + + +
+xs_manager: XsManagerDE + + + property + + +
+ +
+ Type: +XsManagerDE + +
+ + + + + +
+ +

The XS manager of the scenario

+
+ +
+ + + +

Functions

+ +
+ + +
+def from_file(...) + + + classmethod + + +
+ + +
+ +

Creates and returns an instance of the AoE2DEScenario class from the given scenario file

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
path + str + +
+

The path to the scenario file to create the object from

+
+
+ required +
game_version + str + +
+

The version of the game to create the object for

+
+
+ 'DE' +
name + str + +
+

The name given to this scenario (defaults to the filename without extension)

+
+
+ '' +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ S + +
+

An instance of the AoE2DEScenario class which is the object representation of the given scenario file

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_de_scenario.py +
53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
@classmethod
+def from_file(cls: Type[S], path: str, game_version: str = "DE", name: str = "") -> S:
+    """
+    Creates and returns an instance of the AoE2DEScenario class from the given scenario file
+
+    Args:
+        path: The path to the scenario file to create the object from
+        game_version: The version of the game to create the object for
+        name: The name given to this scenario (defaults to the filename without extension)
+
+    Returns:
+        An instance of the AoE2DEScenario class which is the object representation of the given scenario file
+    """
+    return super().from_file(path=path, game_version=game_version, name=name)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/aoe2_scenario/index.html b/api_docs/aoe2_scenario/index.html new file mode 100644 index 00000000..b59898a2 --- /dev/null +++ b/api_docs/aoe2_scenario/index.html @@ -0,0 +1,5435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + AoE2Scenario - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

AoE2Scenario

+ +
+ + + + +
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +

+Func: TypeVar = TypeVar('Func', bound='Callable[[Any], Any]') + + + module-attribute + + +

+ +
+ Type: +TypeVar +
+ + Value: +TypeVar('Func', bound='Callable[[Any], Any]')
+ + + + + +
+ +

A type variable (generic) that represents a function

+
+ +
+ +
+ + + + +

+S: TypeVar = TypeVar('S', bound='AoE2Scenario') + + + module-attribute + + +

+ +
+ Type: +TypeVar +
+ + Value: +TypeVar('S', bound='AoE2Scenario')
+ + + + + +
+ +

A type variable (generic) that represents an instance of the AoE2Scenario class or any of its +subclasses (e.g. AoE2DEScenario)

+
+ +
+ + +

Classes

+ +
+ + + +

+ AoE2Scenario + + +

+ + +
+ + +

All scenario objects are derived from this class

+ +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
class AoE2Scenario:
+    """All scenario objects are derived from this class"""
+
+    @property
+    def trigger_manager(self) -> TriggerManager:
+        """The trigger manager of the scenario"""
+        return self._object_manager.managers['Trigger']
+
+    @property
+    def unit_manager(self) -> UnitManager:
+        """The unit manager of the scenario"""
+        return self._object_manager.managers['Unit']
+
+    @property
+    def map_manager(self) -> MapManager:
+        """The map manager of the scenario"""
+        return self._object_manager.managers['Map']
+
+    @property
+    def player_manager(self) -> PlayerManager:
+        """The player manager of the scenario"""
+        return self._object_manager.managers['Player']
+
+    @property
+    def message_manager(self) -> MessageManager:
+        """The message manager of the scenario"""
+        return self._object_manager.managers['Message']
+
+    def on_write(self, func: Func) -> Func:
+        """Register a function to be called on write (intended to be used as a decorator)"""
+        self._on_write_funcs.append(func)
+        return func
+
+    @property
+    def scenario_version_tuple(self) -> tuple[int, ...]:
+        return tuple(map(int, self.scenario_version.split('.')))
+
+    def __init__(
+        self,
+        game_version: str,
+        scenario_version: str,
+        source_location: str,
+        name: str,
+        variant: ScenarioVariant | None = None
+    ):
+        # Scenario meta info
+        self.game_version: str = game_version
+        self.scenario_version: str = scenario_version
+        self.source_location: str = source_location
+        self._variant: ScenarioVariant | None = variant
+        self._time_start: float = time.time()
+
+        # Actual scenario content
+        self.structure: Dict = {}
+        self.sections: Dict[str, AoE2FileSection] = {}
+        self._object_manager: AoE2ObjectManager | None = None
+
+        # For Scenario Store functionality
+        self.name: str = name
+        self.uuid: UUID = uuid4()
+        store.register_scenario(self)
+
+        # Actions through the scenario
+        self.new: ObjectFactory = ObjectFactory(self.uuid)
+        self.actions: ScenarioActions = ScenarioActions(self.uuid)
+
+        # Used in debug functions
+        self._file = None
+        self._file_header = None
+        self._decompressed_file_data = None
+
+        # Callbacks
+        self._on_write_funcs = []
+
+    @classmethod
+    def from_file(
+        cls: Type[S],
+        path: str,
+        game_version: str,
+        name: str = ""
+    ) -> S:
+        """
+        Creates and returns an instance of the AoE2Scenario class from the given scenario file
+
+        Args:
+            path: The path to the scenario file to create the object from
+            game_version: The version of the game to create the object for
+            name: The name given to this scenario (defaults to the filename without extension)
+
+        Returns:
+            An instance of the AoE2Scenario class (or any of its subclasses) which is the object representation of
+                the given scenario file
+        """
+        python_version_check()
+
+        filepath = Path(path)
+        if not filepath.is_file():
+            raise ValueError(f"Unable to read file from path '{filepath}'")
+
+        name = name or filepath.stem
+
+        s_print(f"Reading file: " + color_string(f"'{path}'", "magenta"), final=True, time=True, newline=True)
+        s_print("Reading scenario file...")
+        igenerator = IncrementalGenerator.from_file(path)
+        s_print("Reading scenario file finished successfully.", final=True, time=True)
+
+        scenario_version = _get_file_version(igenerator)
+        scenario_variant = _get_scenario_variant(igenerator)
+
+        scenario: S = cls(game_version, scenario_version, source_location=path, name=name, variant=scenario_variant)
+
+        variant: str = 'Unknown' if scenario.variant is None else scenario_variant.to_display_name()
+
+        # Log game and scenario version
+        s_print("\n############### Attributes ###############", final=True, color="blue")
+        s_print(f">>> Game version: '{scenario.game_version}'", final=True, color="blue")
+        s_print(f">>> Scenario version: {scenario.scenario_version}", final=True, color="blue")
+        s_print(f">>> Scenario variant: '{variant}'", final=True, color= "blue")
+        s_print("##########################################", final=True, color="blue")
+
+        s_print(f"Loading scenario structure...", time=True, newline=True)
+        scenario._load_structure()
+        _initialise_version_dependencies(scenario.game_version, scenario.scenario_version)
+        s_print(f"Loading scenario structure finished successfully.", final=True, time=True)
+
+        # scenario._initialize(igenerator)
+        s_print("Parsing scenario file...", final=True, time=True)
+        scenario._load_header_section(igenerator)
+        scenario._load_content_sections(igenerator)
+        s_print(f"Parsing scenario file finished successfully.", final=True, time=True)
+        scenario.igenerator = igenerator
+
+        scenario._object_manager = AoE2ObjectManager(scenario.uuid)
+        scenario._object_manager.setup()
+
+        return scenario
+
+    @staticmethod
+    def get_scenario(
+        uuid: UUID = None,
+        obj: 'AoE2Object' = None,
+        name: str = None
+    ) -> S:
+        """
+        Get scenario through a UUID, a related object or the name of a scenario.
+
+        Args:
+            uuid: The UUID of the scenario
+            obj: An object related to a scenario
+            name: The name of a scenario
+
+        Returns:
+            The scenario based on the given identifier, or `None`
+        """
+        return store.get_scenario(uuid=uuid, obj=obj, name=name)
+
+    def _load_structure(self) -> None:
+        """
+        Loads the structure json for the scenario and game version specified into self.structure
+
+        Raises:
+            ValueError: if the game or scenario versions are not set
+        """
+        if self.game_version == "???" or self.scenario_version == "???":
+            raise ValueError("Both game and scenario version need to be set to load structure")
+        self.structure = _get_structure(self.game_version, self.scenario_version)
+
+    def _load_header_section(self, raw_file_igenerator: IncrementalGenerator) -> None:
+        """
+        Reads and adds the header file section to the sections dict of the scenario.
+
+        The header is stored decompressed and is the first thing in the scenario file. It is meta data for the scenario
+        file and thus needs to be read before everything else (also the reason why its stored decompressed).
+
+        Args:
+            raw_file_igenerator: The generator to read the header section from
+        """
+        header = self._create_and_load_section('FileHeader', raw_file_igenerator)
+        self._file_header = raw_file_igenerator.file_content[:raw_file_igenerator.progress]
+        self._add_to_sections(header)
+
+    def _load_content_sections(self, raw_file_igenerator: IncrementalGenerator) -> None:
+        """
+        Reads and adds all the remaining file sections from the structure file to the sections dict of the
+        scenario.
+
+        The sections after the header are compressed and are first decompressed using the -zlib.MAX_WBITS algorithm.
+
+        Args:
+            raw_file_igenerator: The generator to read the file sections from
+        """
+        self._decompressed_file_data = _decompress_bytes(raw_file_igenerator.get_remaining_bytes())
+
+        data_igenerator = IncrementalGenerator(name='Scenario Data', file_content=self._decompressed_file_data)
+
+        for section_name in self.structure.keys():
+            if section_name == "FileHeader":
+                continue
+            try:
+                section = self._create_and_load_section(section_name, data_igenerator)
+                self._add_to_sections(section)
+            except Exception as e:
+                print(f"\n[{e.__class__.__name__}] AoE2Scenario.parse_file: \n\tSection: {section_name}\n")
+                self.write_error_file(trail_generator=data_igenerator)
+                raise e
+
+    def _create_and_load_section(self, name: str, igenerator: IncrementalGenerator) -> AoE2FileSection:
+        """
+        Initialises a file section from its name and fills its retrievers with data from the given generator
+
+        Args:
+            name: The name of the file section
+            igenerator: The generator to fill the data from
+
+        Returns:
+            An AoE2FileSection representing the given section name with its data initialised from the generator
+        """
+        s_print(f"\t🔄 Parsing {name}...", color="yellow")
+        section = AoE2FileSection.from_structure(name, self.structure.get(name), self.uuid)
+        s_print(f"\t🔄 Gathering {name} data...", color="yellow")
+        section.set_data_from_generator(igenerator)
+        s_print(f"\t{name}", final=True, color="green")
+        return section
+
+    def _add_to_sections(self, section: AoE2FileSection) -> None:
+        """
+        Adds the given section to the sections dictionary
+
+        Args:
+            section: The section to add to the sections dictionary
+        """
+        self.sections[section.name] = section
+
+    def remove_store_reference(self) -> None:
+        """
+        This function is **DEPRECATED**. No replacement is necessary as the store now uses weak references.
+        You can safely remove the call to this function.
+
+        --- Legacy docstring ---
+
+        Removes the reference to this scenario object from the scenario store. Useful (~a must) when reading many
+        scenarios in a row without needing earlier ones. Python likes to take up a lot of memory.
+        Removing all references to an object will cause the memory to be cleared up.
+
+        Warning: Remove all other references too!
+            When using this function it's important to remove all other references to the scenario.
+            So if save it in a dict or list, remove it from it.
+            If you have variables referencing this scenario that you won't need anymore (and won't overwrite) delete
+            them using: `del varname`.
+        """
+        warn("This function is DEPRECATED as the store now uses weak references. \n"
+             "You can safely remove the call to this function.", DeprecationWarning)
+        store.remove_scenario(self.uuid)
+
+    def commit(self) -> None:
+        """Commit the changes to the retriever backend made within the managers."""
+        self._object_manager.reconstruct()
+
+    """ ##########################################################################################
+    ####################################### Write functions ######################################
+    ########################################################################################## """
+
+    def write_to_file(self, filename: str, skip_reconstruction: bool = False) -> None:
+        """
+        Writes the scenario to a new file with the given filename
+
+        Args:
+            filename: The location to write the file to
+            skip_reconstruction: If reconstruction should be skipped. If true, this will ignore all changes made
+                using the managers (For example all changes made using trigger_manager).
+
+        Raises:
+            ValueError: if the setting DISABLE_ERROR_ON_OVERWRITING_SOURCE is not disabled and the source filename is
+                the same as the filename being written to
+        """
+        self._write_from_structure(filename, skip_reconstruction)
+
+    def _write_from_structure(self, filename: str, skip_reconstruction: bool = False) -> None:
+        """
+        Writes the scenario to a new file with the given filename
+
+        Args:
+            filename: The location to write the file to
+            skip_reconstruction: If reconstruction should be skipped. If true, this will ignore all changes made
+                using the managers (For example all changes made using trigger_manager).
+
+        Raises:
+            ValueError: if the setting DISABLE_ERROR_ON_OVERWRITING_SOURCE is not disabled and the source filename is
+                the same as the filename being written to
+        """
+        if settings.ALLOW_OVERWRITING_SOURCE and self.source_location == filename:
+            raise ValueError("Overwriting the source scenario file is discouraged & disallowed. ")
+
+        for func in self._on_write_funcs:
+            func(self)
+
+        # Update the internal file name to match the output filename
+        self._update_internal_filename(filename)
+
+        if not skip_reconstruction:
+            self.commit()
+
+        self._validate_scenario_variant()
+
+        s_print("File writing from structure started...", final=True, time=True, newline=True)
+        binary = _get_file_section_data(self.sections.get('FileHeader'))
+
+        binary_list_to_be_compressed = []
+        for file_part in self.sections.values():
+            if file_part.name == "FileHeader":
+                continue
+            binary_list_to_be_compressed.append(_get_file_section_data(file_part))
+
+        compressed = _compress_bytes(b''.join(binary_list_to_be_compressed))
+
+        with open(filename, 'wb') as f:
+            f.write(binary + compressed)
+
+        etime = round(time.time() - self._time_start, 2)
+        s_print("File writing finished successfully.", final=True, time=True)
+        s_print(f"File successfully written to: " + color_string(f"'{filename}'", "magenta"), final=True, time=True)
+        s_print(f"Execution time from scenario read: {etime}s", final=True, time=True)
+
+    def write_error_file(self, filename: str = "error_file.txt", trail_generator: IncrementalGenerator = None) -> None:
+        """
+        Outputs the contents of the entire scenario file in a readable format. An example of the format is given below::
+
+            ########################### units (1954 * struct:UnitStruct)
+            ############ UnitStruct ############  [STRUCT]
+            00 00 70 42                 x (1 * f32): 60.0
+            00 00 70 42                 y (1 * f32): 60.0
+            00 00 00 00                 z (1 * f32): 0.0
+            52 05 00 00                 reference_id (1 * s32): 1362
+            89 02                       unit_const (1 * u16): 649
+            02                          status (1 * u8): 2
+            00 00 00 00                 rotation (1 * f32): 0.0
+            00 00                       initial_animation_frame (1 * u16): 0
+            ff ff ff ff                 garrisoned_in_id (1 * s32): -1
+
+        Args:
+            filename: The filename to write the error file to
+            trail_generator: Write all the bytes remaining in this generator as a trail
+        """
+        self._debug_byte_structure_to_file(filename=filename, trail_generator=trail_generator)
+
+    """ #############################################
+    ############### Variant functions ###############
+    ############################################# """
+
+    @property
+    def variant(self) -> ScenarioVariant:
+        return self._variant
+
+    @variant.setter
+    def variant(self, value: None | str | int | ScenarioVariant):
+        if value is None:
+            self._variant = None
+            return
+        elif isinstance(value, ScenarioVariant):
+            self._variant = value
+        elif isinstance(value, int):
+            self._variant = ScenarioVariant(value)
+        elif isinstance(value, str):
+            self._variant = ScenarioVariant[value.upper()]
+        else:
+            raise ValueError(f"Incorrect value used for setting scenario variant: '{value}'")
+
+        self._update_variant_retrievers()
+        self._validate_scenario_variant()
+
+    def _validate_scenario_variant(self):
+        if self.variant is None:
+            self._warn_variant_unknown()
+            return
+
+        # If the header has been adjusted manually (solution before this functionality went live)
+        if self.sections["FileHeader"].unknown_value_2 != self.variant.value:
+            self.variant = self.sections["FileHeader"].unknown_value_2
+
+        if self.variant == ScenarioVariant.ROR and self.scenario_version_tuple < (1, 49):
+            raise UnsupportedVersionError(
+                f"\n\nScenarios with a version below 1.49 (currently: {self.scenario_version}) cannot be written as "
+                f"Return of Rome scenarios.\n"
+                "Upgrade the scenario by saving it in the in-game editor before converting it."
+            )
+
+        if self.variant not in [ScenarioVariant.AOE2, ScenarioVariant.ROR] and settings.SHOW_VARIANT_WARNINGS:
+            applicants = self.variant.applicants()
+            name = self.variant.name
+
+            warn(
+                f"Having the scenario variant set to '{name}' (applies to: '{applicants}') will cause the scenario "
+                f"to not be visible within the Definitive Edition.\n"
+                f"If this is unintentional, you can set `scenario.variant` to 'aoe2' or 'ror'`\n"
+                f"If this is intentional, you can disable this warning using the "
+                f"setting: `SHOW_VARIANT_WARNINGS`", IncorrectVariantWarning
+            )
+
+    def _warn_variant_unknown(self):
+        if not settings.SHOW_VARIANT_WARNINGS:
+            return
+
+        warn(
+            f"The current scenario variant is unknown. It's possible this will cause the scenario to be hidden.\n"
+            f"If this is unintentional, you can set `scenario.variant` to 'aoe2' or 'ror' to make sure it's visible.\n"
+            f"If this is intentional, you can disable this warning using the "
+            f"setting: `SHOW_VARIANT_WARNINGS`", IncorrectVariantWarning
+        )
+
+    def _update_variant_retrievers(self):
+        if self.variant.value == self.sections["FileHeader"].unknown_value_2:
+            return
+
+        dlcs = {
+            ScenarioVariant.LEGACY: [],
+            ScenarioVariant.AOE2: [2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
+            ScenarioVariant.ROR: [11]
+        }[self.variant]
+
+        self.sections["FileHeader"].unknown_value_2 = self.variant.value
+        self.sections["FileHeader"].amount_of_unknown_numbers = len(dlcs)
+        self.sections["FileHeader"].unknown_numbers = dlcs
+
+    def _update_internal_filename(self, filename: str) -> None:
+        self.sections['DataHeader'].filename = Path(filename).stem
+
+    """ #############################################
+    ################ Debug functions ################
+    ############################################# """
+
+    def _debug_compare(
+            self,
+            other: AoE2Scenario,
+            filename: str = "differences.txt",
+            commit: bool = False,
+            *,
+            allow_multiple_versions: bool = False
+    ) -> None:
+        """
+        Compare a scenario to a given scenario and report the differences found
+
+        Args:
+            other: The scenario to compare it to
+            filename: The debug file to write the differences to (Defaults to "differences.txt")
+            commit: If the scenarios need to commit their manager changes before comparing (Defaults to False)
+            allow_multiple_versions: Allow comparison between multiple versions. (Please note that this is not tested
+                thoroughly at all)
+        """
+        debug_compare(self, other, filename, commit, allow_multiple_versions=allow_multiple_versions)
+
+    def _debug_write_from_source(self, filename: str, datatype: str, write_bytes: bool = True) -> None:
+        """
+        Writes the decompressed scenario file as bytes or as hex text
+
+        Args:
+            filename: The filename to write to
+            datatype: these are flags that indicate which parts of the file to include in the output. 'd' for
+                decompressed file data, 'f' for the file, and 'h' for the header. Note: Only 'd' actually works at this
+                time
+            write_bytes: boolean to determine if the file needs to be written as bytes or hex text form
+        """
+        s_print("File writing from source started with attributes " + datatype + "...")
+        file = open(filename, "wb" if write_bytes else "w")
+        selected_parts = []
+        for t in datatype:
+            if t == "f":
+                selected_parts.append(self._file)
+            elif t == "h":
+                selected_parts.append(self._file_header)
+            elif t == "d":
+                selected_parts.append(self._decompressed_file_data)
+        parts = None
+        for part in selected_parts:
+            if parts is None:
+                parts = part
+                continue
+            parts += part
+        file.write(parts if write_bytes else create_textual_hex(parts.hex()))
+        file.close()
+        s_print("File writing finished successfully.")
+
+    def _debug_byte_structure_to_file(self, filename, trail_generator: IncrementalGenerator = None, commit=False):
+        """
+        Outputs the contents of the entire scenario file in a readable format. An example of the format is given below::
+
+            ########################### units (1954 * struct:UnitStruct)
+            ############ UnitStruct ############  [STRUCT]
+            00 00 70 42                 x (1 * f32): 60.0
+            00 00 70 42                 y (1 * f32): 60.0
+            00 00 00 00                 z (1 * f32): 0.0
+            52 05 00 00                 reference_id (1 * s32): 1362
+            89 02                       unit_const (1 * u16): 649
+            02                          status (1 * u8): 2
+            00 00 00 00                 rotation (1 * f32): 0.0
+            00 00                       initial_animation_frame (1 * u16): 0
+            ff ff ff ff                 garrisoned_in_id (1 * s32): -1
+
+        Args:
+            filename: The filename to write the error file to
+            trail_generator: Write all the bytes remaining in this generator as a trail
+            commit: If the managers should commit their changes before writing this file.
+        """
+        if commit and hasattr(self, '_object_manager'):
+            self.commit()
+
+        s_print("Writing structure to file...", final=True, time=True, newline=True)
+
+        result = []
+        for section in self.sections.values():
+            s_print(f"\t🔄 Writing {section.name}...", color="yellow")
+            result.append(section.get_byte_structure_as_string())
+            s_print(f"\t{section.name}", final=True, color="green")
+
+        if trail_generator is not None:
+            s_print("\tWriting trail...")
+            trail = trail_generator.get_remaining_bytes()
+
+            result.append(f"\n\n{'#' * 27} TRAIL ({len(trail)})\n\n")
+            result.append(create_textual_hex(trail.hex(), space_distance=2, enter_distance=24))
+            s_print("\tWriting trail finished successfully.", final=True)
+
+        with open(filename, 'w', encoding=settings.MAIN_CHARSET) as f:
+            f.write(''.join(result))
+        s_print("Writing structure to file finished successfully.", final=True, time=True)
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+actions: ScenarioActions = ScenarioActions(self.uuid) + + + instance-attribute + + +
+ +
+ Type: +ScenarioActions +
+ + Value: +ScenarioActions(self.uuid)
+ + + + + +
+
+ +
+ +
+ + + + +
+game_version: str = game_version + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +game_version
+ + + + + +
+
+ +
+ +
+ + + + +
+map_manager: MapManager + + + property + + +
+ +
+ Type: +MapManager + +
+ + + + + +
+ +

The map manager of the scenario

+
+ +
+ +
+ + + + +
+message_manager: MessageManager + + + property + + +
+ +
+ Type: +MessageManager + +
+ + + + + +
+ +

The message manager of the scenario

+
+ +
+ +
+ + + + +
+name: str = name + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +name
+ + + + + +
+
+ +
+ +
+ + + + +
+new: ObjectFactory = ObjectFactory(self.uuid) + + + instance-attribute + + +
+ +
+ Type: +ObjectFactory +
+ + Value: +ObjectFactory(self.uuid)
+ + + + + +
+
+ +
+ +
+ + + + +
+player_manager: PlayerManager + + + property + + +
+ +
+ Type: +PlayerManager + +
+ + + + + +
+ +

The player manager of the scenario

+
+ +
+ +
+ + + + +
+scenario_version: str = scenario_version + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +scenario_version
+ + + + + +
+
+ +
+ +
+ + + + +
+scenario_version_tuple: tuple[int, ...] + + + property + + +
+ +
+ Type: +tuple[int, ...] + +
+ + + + + +
+
+ +
+ +
+ + + + +
+sections: Dict[str, AoE2FileSection] = {} + + + instance-attribute + + +
+ +
+ Type: +Dict[str, AoE2FileSection] +
+ + Value: +{}
+ + + + + +
+
+ +
+ +
+ + + + +
+source_location: str = source_location + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +source_location
+ + + + + +
+
+ +
+ +
+ + + + +
+structure: Dict = {} + + + instance-attribute + + +
+ +
+ Type: +Dict +
+ + Value: +{}
+ + + + + +
+
+ +
+ +
+ + + + +
+trigger_manager: TriggerManager + + + property + + +
+ +
+ Type: +TriggerManager + +
+ + + + + +
+ +

The trigger manager of the scenario

+
+ +
+ +
+ + + + +
+unit_manager: UnitManager + + + property + + +
+ +
+ Type: +UnitManager + +
+ + + + + +
+ +

The unit manager of the scenario

+
+ +
+ +
+ + + + +
+uuid: UUID = uuid4() + + + instance-attribute + + +
+ +
+ Type: +UUID +
+ + Value: +uuid4()
+ + + + + +
+
+ +
+ +
+ + + + +
+variant: ScenarioVariant + + + property + writable + + +
+ +
+ Type: +ScenarioVariant + +
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
game_version + str + - + required +
scenario_version + str + - + required +
source_location + str + - + required +
name + str + - + required +
variant + ScenarioVariant | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
 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
def __init__(
+    self,
+    game_version: str,
+    scenario_version: str,
+    source_location: str,
+    name: str,
+    variant: ScenarioVariant | None = None
+):
+    # Scenario meta info
+    self.game_version: str = game_version
+    self.scenario_version: str = scenario_version
+    self.source_location: str = source_location
+    self._variant: ScenarioVariant | None = variant
+    self._time_start: float = time.time()
+
+    # Actual scenario content
+    self.structure: Dict = {}
+    self.sections: Dict[str, AoE2FileSection] = {}
+    self._object_manager: AoE2ObjectManager | None = None
+
+    # For Scenario Store functionality
+    self.name: str = name
+    self.uuid: UUID = uuid4()
+    store.register_scenario(self)
+
+    # Actions through the scenario
+    self.new: ObjectFactory = ObjectFactory(self.uuid)
+    self.actions: ScenarioActions = ScenarioActions(self.uuid)
+
+    # Used in debug functions
+    self._file = None
+    self._file_header = None
+    self._decompressed_file_data = None
+
+    # Callbacks
+    self._on_write_funcs = []
+
+
+
+ +
+ +
+ + +
+def commit(...) + +
+ + +
+ +

Commit the changes to the retriever backend made within the managers.

+ + + + + +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
300
+301
+302
def commit(self) -> None:
+    """Commit the changes to the retriever backend made within the managers."""
+    self._object_manager.reconstruct()
+
+
+
+ +
+ +
+ + +
+def from_file(...) + + + classmethod + + +
+ + +
+ +

Creates and returns an instance of the AoE2Scenario class from the given scenario file

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
path + str + +
+

The path to the scenario file to create the object from

+
+
+ required +
game_version + str + +
+

The version of the game to create the object for

+
+
+ required +
name + str + +
+

The name given to this scenario (defaults to the filename without extension)

+
+
+ '' +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ S + +
+

An instance of the AoE2Scenario class (or any of its subclasses) which is the object representation of +the given scenario file

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
@classmethod
+def from_file(
+    cls: Type[S],
+    path: str,
+    game_version: str,
+    name: str = ""
+) -> S:
+    """
+    Creates and returns an instance of the AoE2Scenario class from the given scenario file
+
+    Args:
+        path: The path to the scenario file to create the object from
+        game_version: The version of the game to create the object for
+        name: The name given to this scenario (defaults to the filename without extension)
+
+    Returns:
+        An instance of the AoE2Scenario class (or any of its subclasses) which is the object representation of
+            the given scenario file
+    """
+    python_version_check()
+
+    filepath = Path(path)
+    if not filepath.is_file():
+        raise ValueError(f"Unable to read file from path '{filepath}'")
+
+    name = name or filepath.stem
+
+    s_print(f"Reading file: " + color_string(f"'{path}'", "magenta"), final=True, time=True, newline=True)
+    s_print("Reading scenario file...")
+    igenerator = IncrementalGenerator.from_file(path)
+    s_print("Reading scenario file finished successfully.", final=True, time=True)
+
+    scenario_version = _get_file_version(igenerator)
+    scenario_variant = _get_scenario_variant(igenerator)
+
+    scenario: S = cls(game_version, scenario_version, source_location=path, name=name, variant=scenario_variant)
+
+    variant: str = 'Unknown' if scenario.variant is None else scenario_variant.to_display_name()
+
+    # Log game and scenario version
+    s_print("\n############### Attributes ###############", final=True, color="blue")
+    s_print(f">>> Game version: '{scenario.game_version}'", final=True, color="blue")
+    s_print(f">>> Scenario version: {scenario.scenario_version}", final=True, color="blue")
+    s_print(f">>> Scenario variant: '{variant}'", final=True, color= "blue")
+    s_print("##########################################", final=True, color="blue")
+
+    s_print(f"Loading scenario structure...", time=True, newline=True)
+    scenario._load_structure()
+    _initialise_version_dependencies(scenario.game_version, scenario.scenario_version)
+    s_print(f"Loading scenario structure finished successfully.", final=True, time=True)
+
+    # scenario._initialize(igenerator)
+    s_print("Parsing scenario file...", final=True, time=True)
+    scenario._load_header_section(igenerator)
+    scenario._load_content_sections(igenerator)
+    s_print(f"Parsing scenario file finished successfully.", final=True, time=True)
+    scenario.igenerator = igenerator
+
+    scenario._object_manager = AoE2ObjectManager(scenario.uuid)
+    scenario._object_manager.setup()
+
+    return scenario
+
+
+
+ +
+ +
+ + +
+def get_scenario(...) + + + staticmethod + + +
+ + +
+ +

Get scenario through a UUID, a related object or the name of a scenario.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
uuid + UUID + +
+

The UUID of the scenario

+
+
+ None +
obj + 'AoE2Object' + +
+

An object related to a scenario

+
+
+ None +
name + str + +
+

The name of a scenario

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ S + +
+

The scenario based on the given identifier, or None

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
@staticmethod
+def get_scenario(
+    uuid: UUID = None,
+    obj: 'AoE2Object' = None,
+    name: str = None
+) -> S:
+    """
+    Get scenario through a UUID, a related object or the name of a scenario.
+
+    Args:
+        uuid: The UUID of the scenario
+        obj: An object related to a scenario
+        name: The name of a scenario
+
+    Returns:
+        The scenario based on the given identifier, or `None`
+    """
+    return store.get_scenario(uuid=uuid, obj=obj, name=name)
+
+
+
+ +
+ +
+ + +
+def on_write(...) + +
+ + +
+ +

Register a function to be called on write (intended to be used as a decorator)

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
func + Func + - + required +
+ +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
74
+75
+76
+77
def on_write(self, func: Func) -> Func:
+    """Register a function to be called on write (intended to be used as a decorator)"""
+    self._on_write_funcs.append(func)
+    return func
+
+
+
+ +
+ +
+ + +
+def remove_store_reference(...) + +
+ + +
+ +

This function is DEPRECATED. No replacement is necessary as the store now uses weak references. +You can safely remove the call to this function.

+

--- Legacy docstring ---

+

Removes the reference to this scenario object from the scenario store. Useful (~a must) when reading many +scenarios in a row without needing earlier ones. Python likes to take up a lot of memory. +Removing all references to an object will cause the memory to be cleared up.

+ + +
+ Remove all other references too! +

When using this function it's important to remove all other references to the scenario. +So if save it in a dict or list, remove it from it. +If you have variables referencing this scenario that you won't need anymore (and won't overwrite) delete +them using: del varname.

+
+ + + + +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
def remove_store_reference(self) -> None:
+    """
+    This function is **DEPRECATED**. No replacement is necessary as the store now uses weak references.
+    You can safely remove the call to this function.
+
+    --- Legacy docstring ---
+
+    Removes the reference to this scenario object from the scenario store. Useful (~a must) when reading many
+    scenarios in a row without needing earlier ones. Python likes to take up a lot of memory.
+    Removing all references to an object will cause the memory to be cleared up.
+
+    Warning: Remove all other references too!
+        When using this function it's important to remove all other references to the scenario.
+        So if save it in a dict or list, remove it from it.
+        If you have variables referencing this scenario that you won't need anymore (and won't overwrite) delete
+        them using: `del varname`.
+    """
+    warn("This function is DEPRECATED as the store now uses weak references. \n"
+         "You can safely remove the call to this function.", DeprecationWarning)
+    store.remove_scenario(self.uuid)
+
+
+
+ +
+ +
+ + +
+def write_error_file(...) + +
+ + +
+ +

Outputs the contents of the entire scenario file in a readable format. An example of the format is given below::

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
########################### units (1954 * struct:UnitStruct)
+############ UnitStruct ############  [STRUCT]
+00 00 70 42                 x (1 * f32): 60.0
+00 00 70 42                 y (1 * f32): 60.0
+00 00 00 00                 z (1 * f32): 0.0
+52 05 00 00                 reference_id (1 * s32): 1362
+89 02                       unit_const (1 * u16): 649
+02                          status (1 * u8): 2
+00 00 00 00                 rotation (1 * f32): 0.0
+00 00                       initial_animation_frame (1 * u16): 0
+ff ff ff ff                 garrisoned_in_id (1 * s32): -1
+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
filename + str + +
+

The filename to write the error file to

+
+
+ 'error_file.txt' +
trail_generator + IncrementalGenerator + +
+

Write all the bytes remaining in this generator as a trail

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
def write_error_file(self, filename: str = "error_file.txt", trail_generator: IncrementalGenerator = None) -> None:
+    """
+    Outputs the contents of the entire scenario file in a readable format. An example of the format is given below::
+
+        ########################### units (1954 * struct:UnitStruct)
+        ############ UnitStruct ############  [STRUCT]
+        00 00 70 42                 x (1 * f32): 60.0
+        00 00 70 42                 y (1 * f32): 60.0
+        00 00 00 00                 z (1 * f32): 0.0
+        52 05 00 00                 reference_id (1 * s32): 1362
+        89 02                       unit_const (1 * u16): 649
+        02                          status (1 * u8): 2
+        00 00 00 00                 rotation (1 * f32): 0.0
+        00 00                       initial_animation_frame (1 * u16): 0
+        ff ff ff ff                 garrisoned_in_id (1 * s32): -1
+
+    Args:
+        filename: The filename to write the error file to
+        trail_generator: Write all the bytes remaining in this generator as a trail
+    """
+    self._debug_byte_structure_to_file(filename=filename, trail_generator=trail_generator)
+
+
+
+ +
+ +
+ + +
+def write_to_file(...) + +
+ + +
+ +

Writes the scenario to a new file with the given filename

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
filename + str + +
+

The location to write the file to

+
+
+ required +
skip_reconstruction + bool + +
+

If reconstruction should be skipped. If true, this will ignore all changes made +using the managers (For example all changes made using trigger_manager).

+
+
+ False +
+ + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if the setting DISABLE_ERROR_ON_OVERWRITING_SOURCE is not disabled and the source filename is +the same as the filename being written to

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/scenarios/aoe2_scenario.py +
308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
def write_to_file(self, filename: str, skip_reconstruction: bool = False) -> None:
+    """
+    Writes the scenario to a new file with the given filename
+
+    Args:
+        filename: The location to write the file to
+        skip_reconstruction: If reconstruction should be skipped. If true, this will ignore all changes made
+            using the managers (For example all changes made using trigger_manager).
+
+    Raises:
+        ValueError: if the setting DISABLE_ERROR_ON_OVERWRITING_SOURCE is not disabled and the source filename is
+            the same as the filename being written to
+    """
+    self._write_from_structure(filename, skip_reconstruction)
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/map/map_manager/index.html b/api_docs/map/map_manager/index.html new file mode 100644 index 00000000..04037f77 --- /dev/null +++ b/api_docs/map/map_manager/index.html @@ -0,0 +1,4097 @@ + + + + + + + + + + + + + + + + + + + + + + + + + MapManager - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

MapManager

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ MapManager + + +

+ + +
+

+ Bases: AoE2Object

+ + +

Manager of everything map related. +This class does not include the logic for DE specific features. +For those see: MapManagerDE

+ +
+ Source code in AoE2ScenarioParser/objects/managers/map_manager.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
class MapManager(AoE2Object):
+    """
+    Manager of everything map related.
+    This class does not include the logic for DE specific features.
+    For those see: `MapManagerDE`
+    """
+
+    _link_list = [
+        RetrieverObjectLinkGroup("Map", group=[
+            RetrieverObjectLink("map_width", "map_width"),
+            RetrieverObjectLink("map_height", "map_height"),
+            RetrieverObjectLink("terrain", "terrain_data", process_as_object=TerrainTile),
+        ])
+    ]
+
+    def __init__(
+            self,
+            map_width: int,
+            map_height: int,
+            terrain: List[TerrainTile],
+            **kwargs
+    ):
+        super().__init__(**kwargs)
+
+        self.terrain: List[TerrainTile] = terrain
+        self._map_width: int = map_width
+        self._map_height: int = map_height
+
+    @property
+    def map_width(self) -> int:
+        return self._map_width
+
+    @property
+    def map_height(self) -> int:
+        return self._map_height
+
+    @property
+    def map_size(self) -> int:
+        if self._map_height == self._map_width:
+            return self._map_height
+        else:
+            raise ValueError("Map is not a square. Use the attributes 'map_width' and 'map_height' instead.")
+
+    @map_size.setter
+    def map_size(self, new_size: int):
+        old_size = self._map_width
+        difference = new_size - old_size
+
+        if difference == 0:
+            return
+
+        self._map_width = new_size
+        self._map_height = new_size
+
+        new_terrain = []
+        if difference < 0:
+            # Remove ends of rows (x) & remove final rows entirely (y)
+            for index, chunk in enumerate(list_chuncks(self.terrain, old_size)):
+                if index == new_size:
+                    break
+                new_terrain.extend(chunk[:new_size])
+        elif difference > 0:
+            # Add ends to rows (x) & add entirely new rows  (y)
+            chunk_gen = list_chuncks(self.terrain, old_size)
+            for index in range(new_size):
+                if index < old_size:
+                    row = next(chunk_gen) + [TerrainTile(uuid=self._uuid) for _ in range(difference)]
+                else:
+                    row = [TerrainTile(uuid=self._uuid) for _ in range(new_size)]
+                new_terrain.extend(row)
+        self.terrain = new_terrain
+
+    @property
+    def terrain(self) -> List[TerrainTile]:
+        return self._terrain
+
+    @terrain.setter
+    def terrain(self, value: List[TerrainTile]):
+        sqrt = math.sqrt(len(value))
+        if sqrt % 1 != 0:
+            raise ValueError(f"Tiles do not represent a square map. (Given tile count: {len(value)})")
+
+        def reset_indices(lst):
+            tile: TerrainTile
+            for index, tile in enumerate(lst):
+                tile._reset_terrain_index(index)
+
+        reset_indices(value)
+
+        if value is not None:
+            self._terrain = UuidList(
+                uuid=self._uuid,
+                seq=value,
+                on_update_execute_list=reset_indices
+            )
+        self._map_width = int(sqrt)
+        self._map_height = int(sqrt)
+
+    def get_tile(self, x: int = None, y: int = None, i: int = None) -> TerrainTile:
+        """
+        Get a tile on the map based on xy coordinates or using the index (`i`)
+
+        Args:
+            x: The x coordinate of the wanted tile (used together with y)
+            y: The y coordinate of the wanted tile (used together with x)
+            i: The index of the wanted tile
+
+        Raises:
+            ValueError: If parameters (`x` and/or `y`) and `i` are all set
+            ValueError: If the index requested is outside the index range of the terrain tiles
+
+        Returns:
+            The requested tile
+        """
+        if i and (x or y):
+            raise ValueError("Cannot use both xy and i. Choose or XY or I.")
+        if i is not None:
+            if 0 <= i < self.map_size:
+                return self.terrain[i]
+            else:
+                raise ValueError("Parameter i needs to be: 0 <= i < map_size")
+        return self.terrain[xy_to_i(x, y, self.map_size)]
+
+    def get_tile_safe(self, x: int = None, y: int = None, i: int = None) -> TerrainTile | None:
+        """
+        Get a tile on the map based on xy coordinates or using the index (`i`).
+        If the index is outside the map, `None` is returned instead of an error
+
+        Args:
+            x: The x coordinate of the wanted tile (used together with y)
+            y: The y coordinate of the wanted tile (used together with x)
+            i: The index of the wanted tile
+
+        Returns:
+            The requested tile or `None` if it doesn't exist
+        """
+        try:
+            return self.get_tile(x, y, i)
+        except (IndexError, ValueError):
+            return None
+
+    def get_square_1d(self, x1: int, y1: int, x2: int, y2: int) -> List[TerrainTile]:
+        """
+        Get a square of tiles from the map
+
+        Args:
+            x1: The x1 coordinate of the square
+            y1: The y1 coordinate of the square
+            x2: The x2 coordinate of the square
+            y2: The y2 coordinate of the square
+
+        Returns:
+            1D list of terrain tiles based on given coordinates
+        """
+        result = []
+        for row in self._get_square_rows(x1, y1, x2, y2):
+            result.extend(row)
+        return result
+
+    def get_square_2d(self, x1: int, y1: int, x2: int, y2: int) -> List[List[TerrainTile]]:
+        """
+        Get a square of tiles from the map
+
+        Args:
+            x1: The x1 coordinate of the square
+            y1: The y1 coordinate of the square
+            x2: The x2 coordinate of the square
+            y2: The y2 coordinate of the square
+
+        Returns:
+            2D list of lists with terrain tiles based on given coordinates
+        """
+        result = []
+        for row in self._get_square_rows(x1, y1, x2, y2):
+            result.append(row)
+        return result
+
+    def _get_square_rows(self, x1, y1, x2, y2):
+        row_nums = range(y1, y2 + 1)
+        for row in row_nums:
+            i1 = xy_to_i(x1, row, self.map_size)
+            i2 = xy_to_i(x2, row, self.map_size)
+            tiles = self.terrain[i1:i2 + 1]
+            yield tiles
+
+    def set_elevation(
+            self,
+            elevation: int,
+            x1: int,
+            y1: int,
+            x2: int | None = None,
+            y2: int | None = None
+    ) -> None:
+        """
+        Sets elevation like the in-game elevation mechanics. Can set a hill (or single point) to a certain height and
+        all tiles around it are adjusted accordingly.
+
+        If you find that the in-game mechanics work differently than this function please report it.
+
+        Args:
+            elevation: The elevation to create at the coordinates
+            x1: The x coordinate of the west corner
+            y1: The y coordinate of the west corner
+            x2: The x coordinate of the east corner
+            y2: The y coordinate of the east corner
+        """
+        x2 = x1 if x2 is None else x2
+        y2 = y1 if y2 is None else y2
+
+        if x1 == x2 and y1 == y2:
+            edge_tiles = source_tiles = [self.get_tile(x1, y1)]
+            xys = [source_tiles[0].xy]
+        else:
+            source_tiles = self.get_square_2d(x1, y1, x2, y2)
+
+            # Reset current elevation within area
+            for row in source_tiles:
+                for tile in row:
+                    tile.elevation = elevation
+
+            xys = set(t.xy for row in source_tiles for t in row)
+            edge_tiles = source_tiles[0] + source_tiles[-1]
+            for i in range(1, len(source_tiles) - 1):
+                edge_tiles.extend([source_tiles[i][0], source_tiles[i][-1]])
+
+        for tile in edge_tiles:
+            self._elevation_tile_recursion(tile, xys)
+
+    def _elevation_tile_recursion(
+            self,
+            source_tile: TerrainTile,
+            xys: Set[Tuple[int, int]],
+            visited: Set[Tuple[int, int]] = None
+    ):
+        """
+        Elevation recursive function. Used in the set_elevation function
+
+        Args:
+            source_tile: The tile to check around
+            xys: The XY tuples from the initial square
+            visited: The visited XY tuples with this recursion tree path
+        """
+        visited = set() if visited is None else visited.copy()
+        x, y = source_tile.xy
+        visited.add((x, y))
+        for nx, ny in itertools.product(range(-1, 2), repeat=2):
+            new_x, new_y = x + nx, y + ny
+            if (nx or ny) and (new_x, new_y) not in xys and (new_x, new_y) not in visited:
+                other = self.get_tile_safe(new_x, new_y)
+                if other is None:
+                    continue
+                behind = self.get_tile_safe(x + nx * 2, y + ny * 2)
+                if behind is not None and other.elevation < source_tile.elevation == behind.elevation:
+                    other.elevation = source_tile.elevation
+                elif abs(other.elevation - source_tile.elevation) > 1:
+                    other.elevation = source_tile.elevation + int(sign(other.elevation, source_tile.elevation))
+                    self._elevation_tile_recursion(other, xys, visited)
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+map_height: int + + + property + + +
+ +
+ Type: +int + +
+ + + + + +
+
+ +
+ +
+ + + + +
+map_size: int + + + property + writable + + +
+ +
+ Type: +int + +
+ + + + + +
+
+ +
+ +
+ + + + +
+map_width: int + + + property + + +
+ +
+ Type: +int + +
+ + + + + +
+
+ +
+ +
+ + + + +
+terrain: List[TerrainTile] + + + property + writable + + +
+ +
+ Type: +List[TerrainTile] + +
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
map_width + int + - + required +
map_height + int + - + required +
terrain + List[TerrainTile] + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/map_manager.py +
32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
def __init__(
+        self,
+        map_width: int,
+        map_height: int,
+        terrain: List[TerrainTile],
+        **kwargs
+):
+    super().__init__(**kwargs)
+
+    self.terrain: List[TerrainTile] = terrain
+    self._map_width: int = map_width
+    self._map_height: int = map_height
+
+
+
+ +
+ +
+ + +
+def get_square_1d(...) + +
+ + +
+ +

Get a square of tiles from the map

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x1 + int + +
+

The x1 coordinate of the square

+
+
+ required +
y1 + int + +
+

The y1 coordinate of the square

+
+
+ required +
x2 + int + +
+

The x2 coordinate of the square

+
+
+ required +
y2 + int + +
+

The y2 coordinate of the square

+
+
+ required +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[TerrainTile] + +
+

1D list of terrain tiles based on given coordinates

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/map_manager.py +
158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
def get_square_1d(self, x1: int, y1: int, x2: int, y2: int) -> List[TerrainTile]:
+    """
+    Get a square of tiles from the map
+
+    Args:
+        x1: The x1 coordinate of the square
+        y1: The y1 coordinate of the square
+        x2: The x2 coordinate of the square
+        y2: The y2 coordinate of the square
+
+    Returns:
+        1D list of terrain tiles based on given coordinates
+    """
+    result = []
+    for row in self._get_square_rows(x1, y1, x2, y2):
+        result.extend(row)
+    return result
+
+
+
+ +
+ +
+ + +
+def get_square_2d(...) + +
+ + +
+ +

Get a square of tiles from the map

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x1 + int + +
+

The x1 coordinate of the square

+
+
+ required +
y1 + int + +
+

The y1 coordinate of the square

+
+
+ required +
x2 + int + +
+

The x2 coordinate of the square

+
+
+ required +
y2 + int + +
+

The y2 coordinate of the square

+
+
+ required +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[List[TerrainTile]] + +
+

2D list of lists with terrain tiles based on given coordinates

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/map_manager.py +
176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
def get_square_2d(self, x1: int, y1: int, x2: int, y2: int) -> List[List[TerrainTile]]:
+    """
+    Get a square of tiles from the map
+
+    Args:
+        x1: The x1 coordinate of the square
+        y1: The y1 coordinate of the square
+        x2: The x2 coordinate of the square
+        y2: The y2 coordinate of the square
+
+    Returns:
+        2D list of lists with terrain tiles based on given coordinates
+    """
+    result = []
+    for row in self._get_square_rows(x1, y1, x2, y2):
+        result.append(row)
+    return result
+
+
+
+ +
+ +
+ + +
+def get_tile(...) + +
+ + +
+ +

Get a tile on the map based on xy coordinates or using the index (i)

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x + int + +
+

The x coordinate of the wanted tile (used together with y)

+
+
+ None +
y + int + +
+

The y coordinate of the wanted tile (used together with x)

+
+
+ None +
i + int + +
+

The index of the wanted tile

+
+
+ None +
+ + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If parameters (x and/or y) and i are all set

+
+
+ ValueError + +
+

If the index requested is outside the index range of the terrain tiles

+
+
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ TerrainTile + +
+

The requested tile

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/map_manager.py +
115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
def get_tile(self, x: int = None, y: int = None, i: int = None) -> TerrainTile:
+    """
+    Get a tile on the map based on xy coordinates or using the index (`i`)
+
+    Args:
+        x: The x coordinate of the wanted tile (used together with y)
+        y: The y coordinate of the wanted tile (used together with x)
+        i: The index of the wanted tile
+
+    Raises:
+        ValueError: If parameters (`x` and/or `y`) and `i` are all set
+        ValueError: If the index requested is outside the index range of the terrain tiles
+
+    Returns:
+        The requested tile
+    """
+    if i and (x or y):
+        raise ValueError("Cannot use both xy and i. Choose or XY or I.")
+    if i is not None:
+        if 0 <= i < self.map_size:
+            return self.terrain[i]
+        else:
+            raise ValueError("Parameter i needs to be: 0 <= i < map_size")
+    return self.terrain[xy_to_i(x, y, self.map_size)]
+
+
+
+ +
+ +
+ + +
+def get_tile_safe(...) + +
+ + +
+ +

Get a tile on the map based on xy coordinates or using the index (i). +If the index is outside the map, None is returned instead of an error

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x + int + +
+

The x coordinate of the wanted tile (used together with y)

+
+
+ None +
y + int + +
+

The y coordinate of the wanted tile (used together with x)

+
+
+ None +
i + int + +
+

The index of the wanted tile

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ TerrainTile | None + +
+

The requested tile or None if it doesn't exist

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/map_manager.py +
140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
def get_tile_safe(self, x: int = None, y: int = None, i: int = None) -> TerrainTile | None:
+    """
+    Get a tile on the map based on xy coordinates or using the index (`i`).
+    If the index is outside the map, `None` is returned instead of an error
+
+    Args:
+        x: The x coordinate of the wanted tile (used together with y)
+        y: The y coordinate of the wanted tile (used together with x)
+        i: The index of the wanted tile
+
+    Returns:
+        The requested tile or `None` if it doesn't exist
+    """
+    try:
+        return self.get_tile(x, y, i)
+    except (IndexError, ValueError):
+        return None
+
+
+
+ +
+ +
+ + +
+def set_elevation(...) + +
+ + +
+ +

Sets elevation like the in-game elevation mechanics. Can set a hill (or single point) to a certain height and +all tiles around it are adjusted accordingly.

+

If you find that the in-game mechanics work differently than this function please report it.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
elevation + int + +
+

The elevation to create at the coordinates

+
+
+ required +
x1 + int + +
+

The x coordinate of the west corner

+
+
+ required +
y1 + int + +
+

The y coordinate of the west corner

+
+
+ required +
x2 + int | None + +
+

The x coordinate of the east corner

+
+
+ None +
y2 + int | None + +
+

The y coordinate of the east corner

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/map_manager.py +
202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
def set_elevation(
+        self,
+        elevation: int,
+        x1: int,
+        y1: int,
+        x2: int | None = None,
+        y2: int | None = None
+) -> None:
+    """
+    Sets elevation like the in-game elevation mechanics. Can set a hill (or single point) to a certain height and
+    all tiles around it are adjusted accordingly.
+
+    If you find that the in-game mechanics work differently than this function please report it.
+
+    Args:
+        elevation: The elevation to create at the coordinates
+        x1: The x coordinate of the west corner
+        y1: The y coordinate of the west corner
+        x2: The x coordinate of the east corner
+        y2: The y coordinate of the east corner
+    """
+    x2 = x1 if x2 is None else x2
+    y2 = y1 if y2 is None else y2
+
+    if x1 == x2 and y1 == y2:
+        edge_tiles = source_tiles = [self.get_tile(x1, y1)]
+        xys = [source_tiles[0].xy]
+    else:
+        source_tiles = self.get_square_2d(x1, y1, x2, y2)
+
+        # Reset current elevation within area
+        for row in source_tiles:
+            for tile in row:
+                tile.elevation = elevation
+
+        xys = set(t.xy for row in source_tiles for t in row)
+        edge_tiles = source_tiles[0] + source_tiles[-1]
+        for i in range(1, len(source_tiles) - 1):
+            edge_tiles.extend([source_tiles[i][0], source_tiles[i][-1]])
+
+    for tile in edge_tiles:
+        self._elevation_tile_recursion(tile, xys)
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/map/map_manager_de/index.html b/api_docs/map/map_manager_de/index.html new file mode 100644 index 00000000..2b8fc3b0 --- /dev/null +++ b/api_docs/map/map_manager_de/index.html @@ -0,0 +1,2754 @@ + + + + + + + + + + + + + + + + + + + + + + + + + MapManagerDE - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

MapManagerDE

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ MapManagerDE + + +

+ + +
+

+ Bases: MapManager

+ + +

Manager of all DE map related features

+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/map_manager_de.py +
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
class MapManagerDE(MapManager):
+    """Manager of all DE map related features"""
+    _link_list = [
+        RetrieverObjectLinkGroup("Map", group=[
+            RetrieverObjectLink("map_color_mood"),
+            RetrieverObjectLink("collide_and_correct"),
+            RetrieverObjectLink("villager_force_drop", support=Support(since=1.37)),
+            RetrieverObjectLink("map_width"),
+            RetrieverObjectLink("map_height"),
+            RetrieverObjectLink("terrain", link="terrain_data", process_as_object=TerrainTile),
+        ])
+    ]
+
+    def __init__(self,
+                 map_color_mood: str,
+                 collide_and_correct: bool,
+                 villager_force_drop: bool,
+                 map_width: int,
+                 map_height: int,
+                 terrain: List[TerrainTile],
+                 **kwargs,
+                 ):
+        super().__init__(map_width, map_height, terrain, **kwargs)
+
+        self.map_color_mood: str = map_color_mood
+        self.collide_and_correct: bool = collide_and_correct
+        self.villager_force_drop: bool = villager_force_drop
+
+    @property
+    def collide_and_correct(self):
+        warn("Moved to the OptionManager. Use: `option_manager.collide_and_correct` instead", DeprecationWarning)
+        return self._collide_and_correct
+
+    @collide_and_correct.setter
+    def collide_and_correct(self, value):
+        warn("Moved to the OptionManager. Use: `option_manager.collide_and_correct` instead", DeprecationWarning)
+        self._collide_and_correct = value
+
+    @property
+    def villager_force_drop(self):
+        warn("Moved to the OptionManager. Use: `option_manager.villager_force_drop` instead", DeprecationWarning)
+        return self._villager_force_drop
+
+    @villager_force_drop.setter
+    def villager_force_drop(self, value):
+        warn("Moved to the OptionManager. Use: `option_manager.villager_force_drop` instead", DeprecationWarning)
+        self._villager_force_drop = value
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+collide_and_correct + + + property + writable + + +
+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +
+map_color_mood: str = map_color_mood + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +map_color_mood
+ + + + + +
+
+ +
+ +
+ + + + +
+villager_force_drop + + + property + writable + + +
+ +
+ + +
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
map_color_mood + str + - + required +
collide_and_correct + bool + - + required +
villager_force_drop + bool + - + required +
map_width + int + - + required +
map_height + int + - + required +
terrain + List[TerrainTile] + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/map_manager_de.py +
24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
def __init__(self,
+             map_color_mood: str,
+             collide_and_correct: bool,
+             villager_force_drop: bool,
+             map_width: int,
+             map_height: int,
+             terrain: List[TerrainTile],
+             **kwargs,
+             ):
+    super().__init__(map_width, map_height, terrain, **kwargs)
+
+    self.map_color_mood: str = map_color_mood
+    self.collide_and_correct: bool = collide_and_correct
+    self.villager_force_drop: bool = villager_force_drop
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/map/terrain_tile/index.html b/api_docs/map/terrain_tile/index.html new file mode 100644 index 00000000..55252788 --- /dev/null +++ b/api_docs/map/terrain_tile/index.html @@ -0,0 +1,2973 @@ + + + + + + + + + + + + + + + + + + + + + + + + + TerrainTile - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

TerrainTile

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ TerrainTile + + +

+ + +
+

+ Bases: AoE2Object

+ + +
+ Source code in AoE2ScenarioParser/objects/data_objects/terrain_tile.py +
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
class TerrainTile(AoE2Object):
+
+    _link_list = [
+        RetrieverObjectLinkGroup("Map", "terrain_data[__index__]", group=[
+            RetrieverObjectLink("terrain_id"),
+            RetrieverObjectLink("elevation"),
+            RetrieverObjectLink("layer"),
+        ]),
+        RetrieverObjectLink("_index", retrieve_history_number=0),
+    ]
+
+    def __init__(self, terrain_id: int = TerrainId.GRASS_1, elevation: int = 0, layer: int = -1, _index: int = - 1,
+                 **kwargs):
+        self.terrain_id: int = terrain_id
+        self.elevation: int = elevation
+        self.layer: int = layer
+        self._index: int = _index
+        self._xy: Optional[Tuple[int, int]] = None
+
+        super().__init__(**kwargs)
+
+    @property
+    def x(self) -> int:
+        """The X coordinate of this tile on the map"""
+        return self.xy[0]
+
+    @property
+    def y(self) -> int:
+        """The Y coordinate of this tile on the map"""
+        return self.xy[1]
+
+    @property
+    def i(self) -> int:
+        """The index of this tile on the map"""
+        return self._index
+
+    @property
+    def xy(self) -> Tuple[int, int]:
+        """
+        The X,Y coordinate of this tile on the map
+
+        Returns:
+            A tuple containing two integers representing the XY coordinates
+        """
+        if not self._xy:
+            self._xy = i_to_xy(self._index, getters.get_map_size(self._uuid))
+        return self._xy
+
+    def _reset_terrain_index(self, new_index: int):
+        """Reset the current terrain index"""
+        self._index = new_index
+        self._xy = None
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+elevation: int = elevation + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +elevation
+ + + + + +
+
+ +
+ +
+ + + + +
+i: int + + + property + + +
+ +
+ Type: +int + +
+ + + + + +
+ +

The index of this tile on the map

+
+ +
+ +
+ + + + +
+layer: int = layer + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +layer
+ + + + + +
+
+ +
+ +
+ + + + +
+terrain_id: int = terrain_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +terrain_id
+ + + + + +
+
+ +
+ +
+ + + + +
+x: int + + + property + + +
+ +
+ Type: +int + +
+ + + + + +
+ +

The X coordinate of this tile on the map

+
+ +
+ +
+ + + + +
+xy: Tuple[int, int] + + + property + + +
+ +
+ Type: +Tuple[int, int] + +
+ + + + + +
+ +

The X,Y coordinate of this tile on the map

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Tuple[int, int] + +
+

A tuple containing two integers representing the XY coordinates

+
+
+
+ +
+ +
+ + + + +
+y: int + + + property + + +
+ +
+ Type: +int + +
+ + + + + +
+ +

The Y coordinate of this tile on the map

+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
terrain_id + int + - + GRASS_1 +
elevation + int + - + 0 +
layer + int + - + -1 +
_index + int + - + -1 +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/terrain_tile.py +
22
+23
+24
+25
+26
+27
+28
+29
+30
def __init__(self, terrain_id: int = TerrainId.GRASS_1, elevation: int = 0, layer: int = -1, _index: int = - 1,
+             **kwargs):
+    self.terrain_id: int = terrain_id
+    self.elevation: int = elevation
+    self.layer: int = layer
+    self._index: int = _index
+    self._xy: Optional[Tuple[int, int]] = None
+
+    super().__init__(**kwargs)
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/messages/message_manager/index.html b/api_docs/messages/message_manager/index.html new file mode 100644 index 00000000..699744b0 --- /dev/null +++ b/api_docs/messages/message_manager/index.html @@ -0,0 +1,3277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + MessageManager - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

MessageManager

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ MessageManager + + +

+ + +
+

+ Bases: AoE2Object

+ + +

Manager of the everything message tab related.

+ +
+ Source code in AoE2ScenarioParser/objects/managers/message_manager.py +
 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
class MessageManager(AoE2Object):
+    """Manager of the everything message tab related."""
+
+    _link_list = [
+        RetrieverObjectLinkGroup("Messages", group=[
+            RetrieverObjectLink("instructions", link="ascii_instructions"),
+            RetrieverObjectLink("hints", link="ascii_hints"),
+            RetrieverObjectLink("victory", link="ascii_victory"),
+            RetrieverObjectLink("loss", link="ascii_loss"),
+            RetrieverObjectLink("history", link="ascii_history"),
+            RetrieverObjectLink("scouts", link="ascii_scouts"),
+
+            RetrieverObjectLink("instructions_string_table_id", link="instructions"),
+            RetrieverObjectLink("hints_string_table_id", link="hints"),
+            RetrieverObjectLink("victory_string_table_id", link="victory"),
+            RetrieverObjectLink("loss_string_table_id", link="loss"),
+            RetrieverObjectLink("history_string_table_id", link="history"),
+            RetrieverObjectLink("scouts_string_table_id", link="scouts"),
+        ]),
+    ]
+
+    def __init__(self,
+                 instructions: str,
+                 hints: str,
+                 victory: str,
+                 loss: str,
+                 history: str,
+                 scouts: str,
+                 instructions_string_table_id: int,
+                 hints_string_table_id: int,
+                 victory_string_table_id: int,
+                 loss_string_table_id: int,
+                 history_string_table_id: int,
+                 scouts_string_table_id: int,
+                 **kwargs
+                 ):
+        super().__init__(**kwargs)
+
+        self.instructions: str = instructions
+        self.hints: str = hints
+        self.victory: str = victory
+        self.loss: str = loss
+        self.history: str = history
+        self.scouts: str = scouts
+        self.instructions_string_table_id: int = instructions_string_table_id
+        self.hints_string_table_id: int = hints_string_table_id
+        self.victory_string_table_id: int = victory_string_table_id
+        self.loss_string_table_id: int = loss_string_table_id
+        self.history_string_table_id: int = history_string_table_id
+        self.scouts_string_table_id: int = scouts_string_table_id
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+hints: str = hints + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +hints
+ + + + + +
+
+ +
+ +
+ + + + +
+hints_string_table_id: int = hints_string_table_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +hints_string_table_id
+ + + + + +
+
+ +
+ +
+ + + + +
+history: str = history + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +history
+ + + + + +
+
+ +
+ +
+ + + + +
+history_string_table_id: int = history_string_table_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +history_string_table_id
+ + + + + +
+
+ +
+ +
+ + + + +
+instructions: str = instructions + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +instructions
+ + + + + +
+
+ +
+ +
+ + + + +
+instructions_string_table_id: int = instructions_string_table_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +instructions_string_table_id
+ + + + + +
+
+ +
+ +
+ + + + +
+loss: str = loss + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +loss
+ + + + + +
+
+ +
+ +
+ + + + +
+loss_string_table_id: int = loss_string_table_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +loss_string_table_id
+ + + + + +
+
+ +
+ +
+ + + + +
+scouts: str = scouts + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +scouts
+ + + + + +
+
+ +
+ +
+ + + + +
+scouts_string_table_id: int = scouts_string_table_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +scouts_string_table_id
+ + + + + +
+
+ +
+ +
+ + + + +
+victory: str = victory + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +victory
+ + + + + +
+
+ +
+ +
+ + + + +
+victory_string_table_id: int = victory_string_table_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +victory_string_table_id
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
instructions + str + - + required +
hints + str + - + required +
victory + str + - + required +
loss + str + - + required +
history + str + - + required +
scouts + str + - + required +
instructions_string_table_id + int + - + required +
hints_string_table_id + int + - + required +
victory_string_table_id + int + - + required +
loss_string_table_id + int + - + required +
history_string_table_id + int + - + required +
scouts_string_table_id + int + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/message_manager.py +
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
def __init__(self,
+             instructions: str,
+             hints: str,
+             victory: str,
+             loss: str,
+             history: str,
+             scouts: str,
+             instructions_string_table_id: int,
+             hints_string_table_id: int,
+             victory_string_table_id: int,
+             loss_string_table_id: int,
+             history_string_table_id: int,
+             scouts_string_table_id: int,
+             **kwargs
+             ):
+    super().__init__(**kwargs)
+
+    self.instructions: str = instructions
+    self.hints: str = hints
+    self.victory: str = victory
+    self.loss: str = loss
+    self.history: str = history
+    self.scouts: str = scouts
+    self.instructions_string_table_id: int = instructions_string_table_id
+    self.hints_string_table_id: int = hints_string_table_id
+    self.victory_string_table_id: int = victory_string_table_id
+    self.loss_string_table_id: int = loss_string_table_id
+    self.history_string_table_id: int = history_string_table_id
+    self.scouts_string_table_id: int = scouts_string_table_id
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/player/player/index.html b/api_docs/player/player/index.html new file mode 100644 index 00000000..d83ce091 --- /dev/null +++ b/api_docs/player/player/index.html @@ -0,0 +1,4506 @@ + + + + + + + + + + + + + + + + + + + + + + + + + PlayerData - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

PlayerData

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ Player + + +

+ + +
+

+ Bases: AoE2Object

+ + +

A class for handling all player information.

+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/player/player.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
class Player(AoE2Object):
+    """A class for handling all player information."""
+
+    _object_attributes = [
+        'player_id',
+        'starting_age',
+        'lock_civ',
+        'food',
+        'wood',
+        'gold',
+        'stone',
+        'color',
+        'active',
+        'human',
+        'civilization',
+        'architecture_set',
+    ]
+    _object_attributes_non_gaia = [
+        'population_cap',
+        'diplomacy',
+        'initial_camera_x',
+        'initial_camera_y',
+        'allied_victory',
+        'disabled_techs',
+        'disabled_buildings',
+        'disabled_units',
+        'base_priority',
+        'tribe_name',
+        'string_table_name_id',
+        'initial_player_view_x',
+        'initial_player_view_y',
+    ]
+
+    def __init__(
+            self,
+            player_id: int,
+            starting_age: int,
+            lock_civ: int,
+            lock_personality: int,
+            food: int,
+            wood: int,
+            gold: int,
+            stone: int,
+            color: int,
+            active: bool,
+            human: bool,
+            civilization: int,
+            architecture_set: int,
+
+            # Optionals due to GAIA not having such value
+            population_cap: Optional[int] = None,
+            diplomacy: Optional[List[int]] = None,
+            initial_camera_x: Optional[int] = None,
+            initial_camera_y: Optional[int] = None,
+            allied_victory: Optional[int] = None,
+            disabled_techs: Optional[List[int]] = None,
+            disabled_buildings: Optional[List[int]] = None,
+            disabled_units: Optional[List[int]] = None,
+            tribe_name: Optional[str] = None,
+            base_priority: Optional[int] = None,
+            string_table_name_id: Optional[int] = None,
+            initial_player_view_x: Optional[int] = None,
+            initial_player_view_y: Optional[int] = None,
+            **kwargs
+    ):
+        super().__init__(**kwargs)
+
+        self._player_id: int = player_id
+        self._active: bool = active
+        self.starting_age: int = dataset_or_value(StartingAge, starting_age)
+        self.lock_civ: bool = bool(lock_civ)
+        self.lock_personality: bool = bool(lock_personality)
+        self.food: int = food
+        self.wood: int = wood
+        self.gold: int = gold
+        self.stone: int = stone
+        self.color: int = color
+        self.human: bool = human
+        self.civilization: int | Civilization = dataset_or_value(Civilization, civilization)
+        self.architecture_set: int | Civilization = dataset_or_value(Civilization, architecture_set)
+
+        # Optionals due to GAIA not having such value
+        self.population_cap: Optional[int] = population_cap
+        self.diplomacy: Optional[List[int]] = diplomacy
+        self._initial_camera_x: Optional[int] = initial_camera_x  # ← Deprecated
+        self._initial_camera_y: Optional[int] = initial_camera_y  # ← Deprecated
+        self.allied_victory: Optional[bool] = bool(allied_victory) if allied_victory is not None else None
+        self.disabled_techs: Optional[List[int]] = disabled_techs
+        self.disabled_buildings: Optional[List[int]] = disabled_buildings
+        self.disabled_units: Optional[List[int]] = disabled_units
+        self.tribe_name: Optional[str] = tribe_name
+        self.base_priority: Optional[int] = base_priority
+        self.string_table_name_id: Optional[int] = string_table_name_id
+        self.initial_player_view_x: Optional[int] = initial_player_view_x
+        self.initial_player_view_y: Optional[int] = initial_player_view_y
+
+    @property
+    def initial_camera_x(self):
+        warn("Unused by scenario. Use: `initial_player_view_x` instead", DeprecationWarning)
+        return self._initial_camera_x
+
+    @initial_camera_x.setter
+    def initial_camera_x(self, value):
+        warn("Unused by scenario. Use: `initial_player_view_x` instead", DeprecationWarning)
+        self._initial_camera_x = value
+
+    @property
+    def initial_camera_y(self):
+        warn("Unused by scenario. Use: `initial_player_view_y` instead", DeprecationWarning)
+        return self._initial_camera_y
+
+    @initial_camera_y.setter
+    def initial_camera_y(self, value):
+        warn("Unused by scenario. Use: `initial_player_view_y` instead", DeprecationWarning)
+        self._initial_camera_y = value
+
+    @property
+    def player_id(self):
+        """Read-only value of the player ID"""
+        return self._player_id
+
+    @property
+    def active(self):
+        """Read-only value if this player is active or not"""
+        return self._active
+
+    def set_player_diplomacy(self, players: PlayerId | int | List[PlayerId | int], diplomacy: DiplomacyState):
+        """
+        Set the diplomacy of this player to other players.
+
+        Note: This sets the player diplomacy ONE WAY!
+            This does NOT set the other player's diplomacy to this player to the same diplomacy
+
+        Args:
+            players: The player(s) to change
+            diplomacy: The diplomacy setting to set the player to
+        """
+        players: List[PlayerId | int] = listify(players)
+
+        if self.player_id in players:
+            raise ValueError("Cannot set diplomacy from and to the same player")
+
+        for player in players:
+            self.diplomacy[player - 1] = diplomacy
+
+    def _get_object_attrs(self):
+        attrs = self._object_attributes
+        if self.player_id != PlayerId.GAIA:
+            attrs.extend(self._object_attributes_non_gaia)
+        return super()._get_object_attrs() + attrs
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+active + + + property + + +
+ +
+ + +
+ + + + + +
+ +

Read-only value if this player is active or not

+
+ +
+ +
+ + + + +
+allied_victory: Optional[bool] = bool(allied_victory) if allied_victory is not None else None + + + instance-attribute + + +
+ +
+ Type: +Optional[bool] +
+ + Value: +bool(allied_victory) if allied_victory is not None else None
+ + + + + +
+
+ +
+ +
+ + + + +
+architecture_set: int | Civilization = dataset_or_value(Civilization, architecture_set) + + + instance-attribute + + +
+ +
+ Type: +int | Civilization +
+ + Value: +dataset_or_value(Civilization, architecture_set)
+ + + + + +
+
+ +
+ +
+ + + + +
+base_priority: Optional[int] = base_priority + + + instance-attribute + + +
+ +
+ Type: +Optional[int] +
+ + Value: +base_priority
+ + + + + +
+
+ +
+ +
+ + + + +
+civilization: int | Civilization = dataset_or_value(Civilization, civilization) + + + instance-attribute + + +
+ +
+ Type: +int | Civilization +
+ + Value: +dataset_or_value(Civilization, civilization)
+ + + + + +
+
+ +
+ +
+ + + + +
+color: int = color + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +color
+ + + + + +
+
+ +
+ +
+ + + + +
+diplomacy: Optional[List[int]] = diplomacy + + + instance-attribute + + +
+ +
+ Type: +Optional[List[int]] +
+ + Value: +diplomacy
+ + + + + +
+
+ +
+ +
+ + + + +
+disabled_buildings: Optional[List[int]] = disabled_buildings + + + instance-attribute + + +
+ +
+ Type: +Optional[List[int]] +
+ + Value: +disabled_buildings
+ + + + + +
+
+ +
+ +
+ + + + +
+disabled_techs: Optional[List[int]] = disabled_techs + + + instance-attribute + + +
+ +
+ Type: +Optional[List[int]] +
+ + Value: +disabled_techs
+ + + + + +
+
+ +
+ +
+ + + + +
+disabled_units: Optional[List[int]] = disabled_units + + + instance-attribute + + +
+ +
+ Type: +Optional[List[int]] +
+ + Value: +disabled_units
+ + + + + +
+
+ +
+ +
+ + + + +
+food: int = food + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +food
+ + + + + +
+
+ +
+ +
+ + + + +
+gold: int = gold + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +gold
+ + + + + +
+
+ +
+ +
+ + + + +
+human: bool = human + + + instance-attribute + + +
+ +
+ Type: +bool +
+ + Value: +human
+ + + + + +
+
+ +
+ +
+ + + + +
+initial_camera_x + + + property + writable + + +
+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +
+initial_camera_y + + + property + writable + + +
+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +
+initial_player_view_x: Optional[int] = initial_player_view_x + + + instance-attribute + + +
+ +
+ Type: +Optional[int] +
+ + Value: +initial_player_view_x
+ + + + + +
+
+ +
+ +
+ + + + +
+initial_player_view_y: Optional[int] = initial_player_view_y + + + instance-attribute + + +
+ +
+ Type: +Optional[int] +
+ + Value: +initial_player_view_y
+ + + + + +
+
+ +
+ +
+ + + + +
+lock_civ: bool = bool(lock_civ) + + + instance-attribute + + +
+ +
+ Type: +bool +
+ + Value: +bool(lock_civ)
+ + + + + +
+
+ +
+ +
+ + + + +
+lock_personality: bool = bool(lock_personality) + + + instance-attribute + + +
+ +
+ Type: +bool +
+ + Value: +bool(lock_personality)
+ + + + + +
+
+ +
+ +
+ + + + +
+player_id + + + property + + +
+ +
+ + +
+ + + + + +
+ +

Read-only value of the player ID

+
+ +
+ +
+ + + + +
+population_cap: Optional[int] = population_cap + + + instance-attribute + + +
+ +
+ Type: +Optional[int] +
+ + Value: +population_cap
+ + + + + +
+
+ +
+ +
+ + + + +
+starting_age: int = dataset_or_value(StartingAge, starting_age) + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +dataset_or_value(StartingAge, starting_age)
+ + + + + +
+
+ +
+ +
+ + + + +
+stone: int = stone + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +stone
+ + + + + +
+
+ +
+ +
+ + + + +
+string_table_name_id: Optional[int] = string_table_name_id + + + instance-attribute + + +
+ +
+ Type: +Optional[int] +
+ + Value: +string_table_name_id
+ + + + + +
+
+ +
+ +
+ + + + +
+tribe_name: Optional[str] = tribe_name + + + instance-attribute + + +
+ +
+ Type: +Optional[str] +
+ + Value: +tribe_name
+ + + + + +
+
+ +
+ +
+ + + + +
+wood: int = wood + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +wood
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
player_id + int + - + required +
starting_age + int + - + required +
lock_civ + int + - + required +
lock_personality + int + - + required +
food + int + - + required +
wood + int + - + required +
gold + int + - + required +
stone + int + - + required +
color + int + - + required +
active + bool + - + required +
human + bool + - + required +
civilization + int + - + required +
architecture_set + int + - + required +
population_cap + Optional[int] + - + None +
diplomacy + Optional[List[int]] + - + None +
initial_camera_x + Optional[int] + - + None +
initial_camera_y + Optional[int] + - + None +
allied_victory + Optional[int] + - + None +
disabled_techs + Optional[List[int]] + - + None +
disabled_buildings + Optional[List[int]] + - + None +
disabled_units + Optional[List[int]] + - + None +
tribe_name + Optional[str] + - + None +
base_priority + Optional[int] + - + None +
string_table_name_id + Optional[int] + - + None +
initial_player_view_x + Optional[int] + - + None +
initial_player_view_y + Optional[int] + - + None +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/player/player.py +
 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
def __init__(
+        self,
+        player_id: int,
+        starting_age: int,
+        lock_civ: int,
+        lock_personality: int,
+        food: int,
+        wood: int,
+        gold: int,
+        stone: int,
+        color: int,
+        active: bool,
+        human: bool,
+        civilization: int,
+        architecture_set: int,
+
+        # Optionals due to GAIA not having such value
+        population_cap: Optional[int] = None,
+        diplomacy: Optional[List[int]] = None,
+        initial_camera_x: Optional[int] = None,
+        initial_camera_y: Optional[int] = None,
+        allied_victory: Optional[int] = None,
+        disabled_techs: Optional[List[int]] = None,
+        disabled_buildings: Optional[List[int]] = None,
+        disabled_units: Optional[List[int]] = None,
+        tribe_name: Optional[str] = None,
+        base_priority: Optional[int] = None,
+        string_table_name_id: Optional[int] = None,
+        initial_player_view_x: Optional[int] = None,
+        initial_player_view_y: Optional[int] = None,
+        **kwargs
+):
+    super().__init__(**kwargs)
+
+    self._player_id: int = player_id
+    self._active: bool = active
+    self.starting_age: int = dataset_or_value(StartingAge, starting_age)
+    self.lock_civ: bool = bool(lock_civ)
+    self.lock_personality: bool = bool(lock_personality)
+    self.food: int = food
+    self.wood: int = wood
+    self.gold: int = gold
+    self.stone: int = stone
+    self.color: int = color
+    self.human: bool = human
+    self.civilization: int | Civilization = dataset_or_value(Civilization, civilization)
+    self.architecture_set: int | Civilization = dataset_or_value(Civilization, architecture_set)
+
+    # Optionals due to GAIA not having such value
+    self.population_cap: Optional[int] = population_cap
+    self.diplomacy: Optional[List[int]] = diplomacy
+    self._initial_camera_x: Optional[int] = initial_camera_x  # ← Deprecated
+    self._initial_camera_y: Optional[int] = initial_camera_y  # ← Deprecated
+    self.allied_victory: Optional[bool] = bool(allied_victory) if allied_victory is not None else None
+    self.disabled_techs: Optional[List[int]] = disabled_techs
+    self.disabled_buildings: Optional[List[int]] = disabled_buildings
+    self.disabled_units: Optional[List[int]] = disabled_units
+    self.tribe_name: Optional[str] = tribe_name
+    self.base_priority: Optional[int] = base_priority
+    self.string_table_name_id: Optional[int] = string_table_name_id
+    self.initial_player_view_x: Optional[int] = initial_player_view_x
+    self.initial_player_view_y: Optional[int] = initial_player_view_y
+
+
+
+ +
+ +
+ + +
+def set_player_diplomacy(...) + +
+ + +
+ +

Set the diplomacy of this player to other players.

+ + +
+ This sets the player diplomacy ONE WAY! +

This does NOT set the other player's diplomacy to this player to the same diplomacy

+
+ +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
players + PlayerId | int | List[PlayerId | int] + +
+

The player(s) to change

+
+
+ required +
diplomacy + DiplomacyState + +
+

The diplomacy setting to set the player to

+
+
+ required +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/player/player.py +
140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
def set_player_diplomacy(self, players: PlayerId | int | List[PlayerId | int], diplomacy: DiplomacyState):
+    """
+    Set the diplomacy of this player to other players.
+
+    Note: This sets the player diplomacy ONE WAY!
+        This does NOT set the other player's diplomacy to this player to the same diplomacy
+
+    Args:
+        players: The player(s) to change
+        diplomacy: The diplomacy setting to set the player to
+    """
+    players: List[PlayerId | int] = listify(players)
+
+    if self.player_id in players:
+        raise ValueError("Cannot set diplomacy from and to the same player")
+
+    for player in players:
+        self.diplomacy[player - 1] = diplomacy
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/player/player_manager/index.html b/api_docs/player/player_manager/index.html new file mode 100644 index 00000000..7fb1f74c --- /dev/null +++ b/api_docs/player/player_manager/index.html @@ -0,0 +1,3763 @@ + + + + + + + + + + + + + + + + + + + + + + + + + PlayerManager - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

PlayerManager

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ PlayerManager + + +

+ + +
+

+ Bases: AoE2Object

+ + +

Manager of everything player related.

+ +
+ Source code in AoE2ScenarioParser/objects/managers/player_manager.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
class PlayerManager(AoE2Object):
+    """Manager of everything player related."""
+
+    # Todo: Implement a DE version separate of this.
+    #  I'll be dealing with this IF support for other game versions will ever happen.
+
+    _link_list = [
+        RetrieverObjectLink("_player_count", "FileHeader", "player_count"),
+
+        RetrieverObjectLinkGroup("DataHeader", group=[
+            RetrieverObjectLink("_tribe_names", link="tribe_names"),
+            RetrieverObjectLink("_string_table_player_names", link="string_table_player_names"),
+            RetrieverObjectLink("_metadata", link="player_data_1", process_as_object=PlayerMetaData),
+            RetrieverObjectLink("_lock_civilizations", link="per_player_lock_civilization"),
+            RetrieverObjectLink("_lock_personalities", link="per_player_lock_personality", support=Support(since=1.53)),
+        ]),
+
+        RetrieverObjectLink("_resources", "PlayerDataTwo", "resources", process_as_object=PlayerResources),
+
+        RetrieverObjectLinkGroup("Diplomacy", group=[
+            RetrieverObjectLink("_diplomacy", link="per_player_diplomacy", process_as_object=PlayerDiplomacy),
+            RetrieverObjectLink("_allied_victories", link="per_player_allied_victory"),
+        ]),
+
+        RetrieverObjectLinkGroup("Options", group=[
+            *[
+                RetrieverObjectLink(f"_disabled_{type_}_ids_player_{i}", link=f"disabled_{type_}_ids_player_{i}")
+                for type_ in ["tech", "building", "unit"] for i in range(1, 9)
+            ],
+            RetrieverObjectLink("_starting_ages", link="per_player_starting_age"),
+            RetrieverObjectLink("_base_priorities", link="per_player_base_priority"),
+        ]),
+
+        RetrieverObjectLink("_pop_caps", "Map", "per_player_population_cap", support=Support(since=1.44)),
+        RetrieverObjectLink("_initial_player_views", "Map", "initial_player_views", support=Support(since=1.40), process_as_object=PlayerInitialView),
+
+        RetrieverObjectLinkGroup("Units", group=[
+            RetrieverObjectLink("_player_data_4", link="player_data_4", process_as_object=PlayerDataFour),
+            RetrieverObjectLink("_player_data_3", link="player_data_3", process_as_object=PlayerDataThree),
+        ]),
+    ]
+
+    def __init__(
+            self,
+            _player_count: int,
+            _tribe_names: List[str],
+            _string_table_player_names: List[int],
+            _metadata: List[PlayerMetaData],
+            _lock_civilizations: List[int],
+            _lock_personalities: List[int],
+            _resources: List[PlayerResources],
+            _diplomacy: List[PlayerDiplomacy],
+            _allied_victories: List[int],
+            _starting_ages: List[int],
+            _base_priorities: List[int],
+            _pop_caps: List[int],
+            _initial_player_views: List[PlayerInitialView],
+            _player_data_4: List[PlayerDataFour],
+            _player_data_3: List[PlayerDataThree],
+            **kwargs
+    ):
+        super().__init__(**kwargs)
+
+        disables = {}
+        for type_ in ['tech', 'building', 'unit']:
+            disables[type_] = [kwargs[f'_disabled_{type_}_ids_player_{p}'] for p in range(1, 9)]
+
+        gaia_first_params = {
+            'initial_player_view_x': [ipv.location_x for ipv in _initial_player_views or []],
+            'initial_player_view_y': [ipv.location_y for ipv in _initial_player_views or []],
+        }
+        no_gaia_params = {
+            'population_cap': [int(pd.population_limit) for pd in _player_data_4],
+            'tribe_name': _tribe_names,
+            'string_table_name_id': _string_table_player_names,
+            'base_priority': _base_priorities,
+            'allied_victory': _allied_victories,
+            'disabled_techs': disables['tech'],
+            'disabled_buildings': disables['building'],
+            'disabled_units': disables['unit'],
+            'initial_camera_x': [pd.initial_camera_x for pd in _player_data_3],
+            'initial_camera_y': [pd.initial_camera_y for pd in _player_data_3],
+            'diplomacy': [d.diplomacy_stance for d in _diplomacy],
+        }
+        gaia_last_params = {
+            'starting_age': _starting_ages,
+            'lock_civ': _lock_civilizations,
+            'lock_personality': _lock_personalities,
+            'food': [r.food for r in _resources],
+            'wood': [r.wood for r in _resources],
+            'gold': [r.gold for r in _resources],
+            'stone': [r.stone for r in _resources],
+            'color': [r.color for r in _resources],
+            'active': [r.active for r in _metadata],
+            'human': [r.human for r in _metadata],
+            'civilization': [r.civilization for r in _metadata],
+            'architecture_set': [r.architecture_set for r in _metadata],
+        }
+
+        param_sets = [(no_gaia_params, None), (gaia_first_params, True), (gaia_last_params, False)]
+        player_attributes: Dict[int, Dict] = {i: {'player_id': PlayerId(i)} for i in range(9)}
+        for param_set, gaia_first in param_sets:
+            for key, lst in param_set.items():
+                # If a property is not supported, fill it with Nones and skip it
+                if lst is None or isinstance(lst, list) and len(lst) == 0:
+                    _spread_player_attributes(player_attributes, key, [None] * 16, gaia_first)
+                    continue
+                _spread_player_attributes(player_attributes, key, lst, gaia_first)
+
+        self.players = [Player(**player_attributes[p]) for p in PlayerId.all()]
+
+    @property
+    def active_players(self) -> int:
+        """The amount of players that are active within the scenario"""
+        return len([player for player in self.players if player.active])
+
+    @active_players.setter
+    def active_players(self, value: int):
+        if not 1 <= value <= 8:
+            raise ValueError("Active players value has to be between 1 and 8")
+        for player_id in PlayerId.all(exclude_gaia=True):
+            setattr(self.players[player_id], '_active', player_id <= value)
+
+    @property
+    def players(self) -> List[Player]:
+        """Returns all player objects"""
+        return self._players
+
+    @players.setter
+    def players(self, value: List[Player]) -> None:
+        """Sets player objects"""
+        self._players = UuidList(self._uuid, value)
+
+    def set_default_starting_resources(self, players: List[PlayerId] = None) -> None:
+        """
+        Sets the default starting resources for all players
+
+        Warning: Does NOT take civilizations into account
+            This does not take the current selected civ of this player into account. For example, a player with the
+            Chinese civ selected will still be set to 200 food. Generally speaking, it's recommended to not use this for
+            competitive, normal play. You can select `low` resources in the lobby menu to get 'normal' resources for
+            every civ.
+
+        Args:
+            players: A list of players, defaults to all players (incl GAIA) when left out
+        """
+        if players is None:
+            players = PlayerId.all()
+        for player in players:
+            self.players[player].food = 200
+            self.players[player].wood = 200
+            self.players[player].gold = 100
+            self.players[player].stone = 200
+
+    def set_diplomacy_teams(self, *args: List[PlayerId | int], diplomacy: DiplomacyState = DiplomacyState.ALLY) \
+            -> None:
+        """
+        Sets all players in list allied with all others in the same list.
+
+        Args:
+            *args: List(s) with player IDs that'll be set to the given diplomacy value
+            diplomacy: The diplomacy to set the teams to. Defaults to ally.
+
+        Examples:
+            To set diplomacy like a 4v4 in ranked. Two teams of 4 with alternating IDs.
+
+                set_diplomacy_teams([1,3,5,7], [2,4,6,8], diplomacy=DiplomacyState.ALLY)
+        """
+        for team in args:
+            for player in team:
+                if player == PlayerId.GAIA:
+                    raise ValueError("Gaia cannot be in a team")
+                self.players[player].set_player_diplomacy([p for p in team if p != player], diplomacy)
+
+    # ###############################################################################################
+    # ################################# Functions for reconstruction ################################
+    # ###############################################################################################
+
+    def __getattribute__(self, name: str) -> Any:
+        if name.startswith('_disabled_'):
+            type_ = name.split('_')[2]
+            return getattr(self.players[int(name[-1])], f"disabled_{type_}s")
+
+        return super().__getattribute__(name)
+
+    @property
+    def _player_count(self):
+        """Returns number of active players to be stored in the FileHeader"""
+        return self.active_players
+
+    @property
+    def _allied_victories(self):
+        """Returns the allied victory of all players"""
+        return self._player_attributes_to_list("allied_victory", None, default=0, fill_empty=8)
+
+    @property
+    def _starting_ages(self):
+        """Returns the starting age of all players"""
+        return self._player_attributes_to_list("starting_age", False, default=2, fill_empty=7)
+
+    @property
+    def _lock_civilizations(self):
+        """Returns the civ lock bool of all players"""
+        return self._player_attributes_to_list("lock_civ", False, default=0, fill_empty=7)
+
+    @property
+    def _lock_personalities(self):
+        """Returns the civ lock bool of all players"""
+        return self._player_attributes_to_list("lock_personality", False, default=0, fill_empty=7)
+
+    @property
+    def _pop_caps(self):
+        """Returns the population cap of all players"""
+        return self._player_attributes_to_list("population_cap", None, default=200, fill_empty=8)
+
+    @property
+    def _diplomacy(self):
+        """Returns the diplomacy of all players"""
+        diplomacies = self._player_attributes_to_list("diplomacy", None)
+
+        player_diplomacies = UuidList(self._uuid, [
+            PlayerDiplomacy(diplomacy_stance=diplomacies[i]) for i in range(8)
+        ])
+        player_diplomacies.extend([
+            PlayerDiplomacy(diplomacy_stance=[3] * 16) for _ in range(8)
+        ])
+        return player_diplomacies
+
+    @property
+    def _player_data_4(self):
+        """Returns the resource duplicates for all players"""
+        population_limit = self._player_attributes_to_list("population_cap", None, default=200)
+        food = self._player_attributes_to_list("food", None, default=0)
+        wood = self._player_attributes_to_list("wood", None, default=0)
+        gold = self._player_attributes_to_list("gold", None, default=0)
+        stone = self._player_attributes_to_list("stone", None, default=0)
+
+        return UuidList(self._uuid, [
+            PlayerDataFour(
+                population_limit=float(population_limit[i]),
+                food_duplicate=float(food[i]),
+                wood_duplicate=float(wood[i]),
+                gold_duplicate=float(gold[i]),
+                stone_duplicate=float(stone[i]),
+            ) for i in range(8)
+        ])
+
+    @property
+    def _player_data_3(self) -> List[PlayerDataThree]:
+        """Returns the resource objects for all players"""
+        original_map: Dict[int, str] = {0: 'ally', 1: 'neutral', 3: 'enemy'}
+        mappings: Dict[str, Dict[str, int]] = {
+            'diplomacy_for_interaction': {'self': 0, 'ally': 0, 'neutral': 1, 'enemy': 3, 'gaia': 3},
+            'diplomacy_for_ai_system': {'self': 1, 'ally': 2, 'neutral': 3, 'enemy': 4, 'gaia': 0},
+        }
+
+        initial_camera_x = self._player_attributes_to_list("initial_camera_x", None, default=72)
+        initial_camera_y = self._player_attributes_to_list("initial_camera_y", None, default=72)
+        aok_allied_victory = self._player_attributes_to_list("allied_victory", None, default=0)
+        color = self._player_attributes_to_list("color", False, default=1)
+        diplomacies: List[List[int]] = self._player_attributes_to_list("diplomacy", None)
+
+        other_diplomacies: Dict[str, List[List[int]]] = {}
+        for player in range(8):
+            diplomacy = diplomacies[player][:8]
+            for key, mapping in mappings.items():
+                lst = other_diplomacies.setdefault(key, [])
+                temp_lst = [mapping['gaia']] + [mapping[original_map[n]] for n in diplomacy]
+                temp_lst[player + 1] = mapping['self']
+                lst.append(temp_lst)
+
+        return UuidList(self._uuid, [
+            PlayerDataThree(
+                initial_camera_x[i],
+                initial_camera_y[i],
+                aok_allied_victory[i],
+                other_diplomacies['diplomacy_for_interaction'][i],
+                other_diplomacies['diplomacy_for_ai_system'][i],
+                color[i],
+            ) for i in range(len(initial_camera_x))
+        ])
+
+    @property
+    def _resources(self) -> List[PlayerResources]:
+        """Returns the resource objects for all players"""
+        food = self._player_attributes_to_list("food", False, default=0, fill_empty=7)
+        wood = self._player_attributes_to_list("wood", False, default=0, fill_empty=7)
+        gold = self._player_attributes_to_list("gold", False, default=0, fill_empty=7)
+        stone = self._player_attributes_to_list("stone", False, default=0, fill_empty=7)
+        color = self._player_attributes_to_list("color", False, default=0)
+        color.extend(range(9, 16))
+
+        return UuidList(self._uuid, [
+            PlayerResources(food[i], wood[i], gold[i], stone[i], color[i]) for i in range(len(food))
+        ])
+
+    @property
+    def _metadata(self) -> List[PlayerMetaData]:
+        """Returns the metadata objects for all players"""
+        active = self._player_attributes_to_list("active", False, default=0, fill_empty=7)
+        human = self._player_attributes_to_list("human", False, default=1, fill_empty=7)
+        civilization = self._player_attributes_to_list("civilization", False, default=Civilization.RANDOM, fill_empty=7)
+        architecture_set = self._player_attributes_to_list("architecture_set", False, default=Civilization.RANDOM, fill_empty=7)
+        return UuidList(self._uuid, [
+            PlayerMetaData(active[i], human[i], civilization[i], architecture_set[i]) for i in range(len(active))
+        ])
+
+    @property
+    def _base_priorities(self) -> List[int]:
+        """Returns the base priorities of all players"""
+        return self._player_attributes_to_list("base_priority", None, default=0)
+
+    @property
+    def _tribe_names(self) -> List[str]:
+        """Returns the tribe names of all players"""
+        return self._player_attributes_to_list("tribe_name", None, default="", fill_empty=8)
+
+    @property
+    def _string_table_player_names(self) -> List[int]:
+        """Returns the string table player names of all players"""
+        return self._player_attributes_to_list("string_table_name_id", None, default=-2, fill_empty=8)
+
+    @property
+    def _initial_player_views(self):
+        x = self._player_attributes_to_list("initial_player_view_x", gaia_first=True, default=-1, fill_empty=7)
+        y = self._player_attributes_to_list("initial_player_view_y", gaia_first=True, default=-1, fill_empty=7)
+
+        return UuidList(self._uuid, [PlayerInitialView(x[i], y[i]) for i in range(len(x))])
+
+    def _player_attributes_to_list(
+            self,
+            attribute: str,
+            gaia_first: bool | None = True,
+            default: str | int = 0,
+            fill_empty: int = 0
+    ) -> List[Any]:
+        """
+        The list to store in the scenario structure with values from all players.
+
+        Args:
+            attribute: The attribute to get from the players
+            gaia_first: If the list has gaia first, last or not at all
+            default: The default value to fill the empty fields and what to end to an 16 field list
+            fill_empty: How many empty elements have to be filled with the default value
+
+        Returns:
+            The list of values
+        """
+        players = _player_list(gaia_first)
+        default_list = [default] * fill_empty
+        values = []
+        for p in players:
+            try:
+                v = getattr(self.players[p], attribute)
+                if v is None and gaia_first is None:
+                    v = default
+            except UnsupportedAttributeError:
+                v = None
+            values.append(v)
+        return values + default_list
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+active_players: int + + + property + writable + + +
+ +
+ Type: +int + +
+ + + + + +
+ +

The amount of players that are active within the scenario

+
+ +
+ +
+ + + + +
+players: List[Player] + + + property + writable + + +
+ +
+ Type: +List[Player] + +
+ + + + + +
+ +

Returns all player objects

+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
_player_count + int + - + required +
_tribe_names + List[str] + - + required +
_string_table_player_names + List[int] + - + required +
_metadata + List[PlayerMetaData] + - + required +
_lock_civilizations + List[int] + - + required +
_lock_personalities + List[int] + - + required +
_resources + List[PlayerResources] + - + required +
_diplomacy + List[PlayerDiplomacy] + - + required +
_allied_victories + List[int] + - + required +
_starting_ages + List[int] + - + required +
_base_priorities + List[int] + - + required +
_pop_caps + List[int] + - + required +
_initial_player_views + List[PlayerInitialView] + - + required +
_player_data_4 + List[PlayerDataFour] + - + required +
_player_data_3 + List[PlayerDataThree] + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/player_manager.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
def __init__(
+        self,
+        _player_count: int,
+        _tribe_names: List[str],
+        _string_table_player_names: List[int],
+        _metadata: List[PlayerMetaData],
+        _lock_civilizations: List[int],
+        _lock_personalities: List[int],
+        _resources: List[PlayerResources],
+        _diplomacy: List[PlayerDiplomacy],
+        _allied_victories: List[int],
+        _starting_ages: List[int],
+        _base_priorities: List[int],
+        _pop_caps: List[int],
+        _initial_player_views: List[PlayerInitialView],
+        _player_data_4: List[PlayerDataFour],
+        _player_data_3: List[PlayerDataThree],
+        **kwargs
+):
+    super().__init__(**kwargs)
+
+    disables = {}
+    for type_ in ['tech', 'building', 'unit']:
+        disables[type_] = [kwargs[f'_disabled_{type_}_ids_player_{p}'] for p in range(1, 9)]
+
+    gaia_first_params = {
+        'initial_player_view_x': [ipv.location_x for ipv in _initial_player_views or []],
+        'initial_player_view_y': [ipv.location_y for ipv in _initial_player_views or []],
+    }
+    no_gaia_params = {
+        'population_cap': [int(pd.population_limit) for pd in _player_data_4],
+        'tribe_name': _tribe_names,
+        'string_table_name_id': _string_table_player_names,
+        'base_priority': _base_priorities,
+        'allied_victory': _allied_victories,
+        'disabled_techs': disables['tech'],
+        'disabled_buildings': disables['building'],
+        'disabled_units': disables['unit'],
+        'initial_camera_x': [pd.initial_camera_x for pd in _player_data_3],
+        'initial_camera_y': [pd.initial_camera_y for pd in _player_data_3],
+        'diplomacy': [d.diplomacy_stance for d in _diplomacy],
+    }
+    gaia_last_params = {
+        'starting_age': _starting_ages,
+        'lock_civ': _lock_civilizations,
+        'lock_personality': _lock_personalities,
+        'food': [r.food for r in _resources],
+        'wood': [r.wood for r in _resources],
+        'gold': [r.gold for r in _resources],
+        'stone': [r.stone for r in _resources],
+        'color': [r.color for r in _resources],
+        'active': [r.active for r in _metadata],
+        'human': [r.human for r in _metadata],
+        'civilization': [r.civilization for r in _metadata],
+        'architecture_set': [r.architecture_set for r in _metadata],
+    }
+
+    param_sets = [(no_gaia_params, None), (gaia_first_params, True), (gaia_last_params, False)]
+    player_attributes: Dict[int, Dict] = {i: {'player_id': PlayerId(i)} for i in range(9)}
+    for param_set, gaia_first in param_sets:
+        for key, lst in param_set.items():
+            # If a property is not supported, fill it with Nones and skip it
+            if lst is None or isinstance(lst, list) and len(lst) == 0:
+                _spread_player_attributes(player_attributes, key, [None] * 16, gaia_first)
+                continue
+            _spread_player_attributes(player_attributes, key, lst, gaia_first)
+
+    self.players = [Player(**player_attributes[p]) for p in PlayerId.all()]
+
+
+
+ +
+ +
+ + +
+def set_default_starting_resources(...) + +
+ + +
+ +

Sets the default starting resources for all players

+ + +
+ Does NOT take civilizations into account +

This does not take the current selected civ of this player into account. For example, a player with the +Chinese civ selected will still be set to 200 food. Generally speaking, it's recommended to not use this for +competitive, normal play. You can select low resources in the lobby menu to get 'normal' resources for +every civ.

+
+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
players + List[PlayerId] + +
+

A list of players, defaults to all players (incl GAIA) when left out

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/player_manager.py +
156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
def set_default_starting_resources(self, players: List[PlayerId] = None) -> None:
+    """
+    Sets the default starting resources for all players
+
+    Warning: Does NOT take civilizations into account
+        This does not take the current selected civ of this player into account. For example, a player with the
+        Chinese civ selected will still be set to 200 food. Generally speaking, it's recommended to not use this for
+        competitive, normal play. You can select `low` resources in the lobby menu to get 'normal' resources for
+        every civ.
+
+    Args:
+        players: A list of players, defaults to all players (incl GAIA) when left out
+    """
+    if players is None:
+        players = PlayerId.all()
+    for player in players:
+        self.players[player].food = 200
+        self.players[player].wood = 200
+        self.players[player].gold = 100
+        self.players[player].stone = 200
+
+
+
+ +
+ +
+ + +
+def set_diplomacy_teams(...) + +
+ + +
+ +

Sets all players in list allied with all others in the same list.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
*args + List[PlayerId | int] + +
+

List(s) with player IDs that'll be set to the given diplomacy value

+
+
+ () +
diplomacy + DiplomacyState + +
+

The diplomacy to set the teams to. Defaults to ally.

+
+
+ ALLY +
+ + +

Examples:

+

To set diplomacy like a 4v4 in ranked. Two teams of 4 with alternating IDs.

+
1
set_diplomacy_teams([1,3,5,7], [2,4,6,8], diplomacy=DiplomacyState.ALLY)
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/player_manager.py +
177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
def set_diplomacy_teams(self, *args: List[PlayerId | int], diplomacy: DiplomacyState = DiplomacyState.ALLY) \
+        -> None:
+    """
+    Sets all players in list allied with all others in the same list.
+
+    Args:
+        *args: List(s) with player IDs that'll be set to the given diplomacy value
+        diplomacy: The diplomacy to set the teams to. Defaults to ally.
+
+    Examples:
+        To set diplomacy like a 4v4 in ranked. Two teams of 4 with alternating IDs.
+
+            set_diplomacy_teams([1,3,5,7], [2,4,6,8], diplomacy=DiplomacyState.ALLY)
+    """
+    for team in args:
+        for player in team:
+            if player == PlayerId.GAIA:
+                raise ValueError("Gaia cannot be in a team")
+            self.players[player].set_player_diplomacy([p for p in team if p != player], diplomacy)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/settings/index.html b/api_docs/settings/index.html new file mode 100644 index 00000000..a7569e07 --- /dev/null +++ b/api_docs/settings/index.html @@ -0,0 +1,2568 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Settings - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Settings

+ +
+ + + + +
+ + + +
+ IMPORT THE MODULE, NOT THE VALUES! +

To change these settings, import the module: from AoE2ScenarioParser import settings. +Then you can change the settings like: settings.RAISE_ERROR_ON_WARNING = True

+

If you import the values directly like: from ... import RAISE_ERROR_ON_WARNING +(not written out because it wouldn't work), change the value would overwrite the reference and not the value which +means the change wouldn't transfer to the settings module.

+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +

+NOTIFY_UNKNOWN_BYTES = True + + + module-attribute + + +

+ +
+ + + Value: +True
+ + + + + +
+ +

Show a notification of extra bytes being available at the end of the file, so you can notify the maintainer

+
+ +
+ +
+ + + + +

+PRINT_STATUS_UPDATES = True + + + module-attribute + + +

+ +
+ + + Value: +True
+ + + + + +
+ +

If status updates of what is being read and written should be printed to console

+
+ +
+ +
+ + + + +

+ALLOW_OVERWRITING_SOURCE = False + + + module-attribute + + +

+ +
+ + + Value: +False
+ + + + + +
+ +

Disable the error being raised when overwriting source scenario

+
+ +
+ +
+ + + + +

+ALLOW_DIRTY_RETRIEVER_OVERWRITE = False + + + module-attribute + + +

+ +
+ + + Value: +False
+ + + + + +
+ +

If it is allowed to overwrite a retriever that is dirty (it has been changed manually)

+
+ +
+ +
+ + + + +

+SHOW_VARIANT_WARNINGS = True + + + module-attribute + + +

+ +
+ + + Value: +True
+ + + + + +
+ +

If warnings about incorrect variants should be shown or not

+
+ +
+ +
+ + + + +

+MAIN_CHARSET = 'utf-8' + + + module-attribute + + +

+ +
+ + + Value: +'utf-8'
+ + + + + +
+ +

The charset used to decode the text in the scenario. If it fails, will try the settings.FALLBACK_CHARSET

+
+ +
+ +
+ + + + +

+FALLBACK_CHARSET = 'latin-1' + + + module-attribute + + +

+ +
+ + + Value: +'latin-1'
+ + + + + +
+ +

The charset used to decode the text in the scenario when the settings.MAIN_CHARSET fails

+
+ +
+ + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/support/area/index.html b/api_docs/support/area/index.html new file mode 100644 index 00000000..74db5efd --- /dev/null +++ b/api_docs/support/area/index.html @@ -0,0 +1,9699 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Area - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Area

+ +
+ + + + +
+ + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
class Area:
+    _recursion_steps = [Tile(0, -1), Tile(1, 0), Tile(0, 1), Tile(-1, 0)]
+    """Values used for recursion steps"""
+
+    def __init__(
+            self,
+            map_size: int = None,
+            uuid: UUID = None,
+            x1: int = None,
+            y1: int = None,
+            x2: int = None,
+            y2: int = None,
+            corner1: Tile = None,
+            corner2: Tile = None,
+    ) -> None:
+        """
+        Object to easily select an area on the map. Uses method chaining for ease of use.
+
+        **Please note**: Setting a ``uuid`` will always overwrite the ``map_size`` attribute, even if it's not ``None``.
+
+        Args:
+            map_size: The size of the map this area object will handle
+            uuid: The UUID of the scenario this area belongs to
+            x1: The X location of the left corner
+            y1: The Y location of the left corner
+            x2: The X location of the right corner
+            y2: The Y location of the right corner
+            corner1: The location of the left corner
+            corner2: The location of the right corner
+        """
+        if map_size is None and uuid is None:
+            if corner1 is None and (x1 is None or y1 is None):
+                raise ValueError("Cannot create area object without knowing the map size or a UUID from a scenario.")
+
+        self.uuid: UUID = uuid
+        if map_size is not None:
+            self._map_size_value = map_size
+        else:
+            self._map_size_value = None
+
+        if values_are_valid(x1, y1) or value_is_valid(corner1):
+            x1, y1, x2, y2 = validate_coords(x1, y1, x2, y2, corner1, corner2)
+        else:
+            x1 = y1 = x2 = y2 = math.floor(self._map_size / 2)  # Select the center tile
+
+        self.state: AreaState = AreaState.FULL
+        self.inverted: bool = False
+
+        self.x1: int = x1
+        self.y1: int = y1
+        self.x2: int = x2
+        self.y2: int = y2
+
+        self.gap_size_x: int = 1
+        self.gap_size_y: int = 1
+        self.line_width_x: int = 1
+        self.line_width_y: int = 1
+        self.block_size_x: int = 1
+        self.block_size_y: int = 1
+
+        self.axis: str = ""
+
+        self.corner_size_x: int = 1
+        self.corner_size_y: int = 1
+
+    # ============================ Class methods ============================
+
+    @classmethod
+    def from_uuid(cls, uuid: UUID) -> Area:
+        return cls(uuid=uuid)
+
+    @classmethod
+    def from_tiles(cls, corner1: Tile, corner2: Tile = None):
+        return cls(corner1=corner1, corner2=corner2)
+
+    # ============================ Properties ============================
+
+    @property
+    def x1(self) -> int:
+        return self._minmax_val(self._x1)
+
+    @x1.setter
+    def x1(self, value):
+        self._x1 = value
+
+    @property
+    def y1(self) -> int:
+        return self._minmax_val(self._y1)
+
+    @y1.setter
+    def y1(self, value):
+        self._y1 = value
+
+    @property
+    def x2(self) -> int:
+        return self._minmax_val(self._x2)
+
+    @x2.setter
+    def x2(self, value):
+        self._x2 = value
+
+    @property
+    def y2(self) -> int:
+        return self._minmax_val(self._y2)
+
+    @y2.setter
+    def y2(self, value):
+        self._y2 = value
+
+    @property
+    def corner1(self):
+        return Tile(self.x1, self.y1)
+
+    @corner1.setter
+    def corner1(self, value: Tile):
+        self.x1, self.y1 = value.x, value.y
+
+    @property
+    def corner2(self):
+        return Tile(self.x2, self.y2)
+
+    @corner2.setter
+    def corner2(self, value: Tile):
+        self.x2, self.y2 = value.x, value.y
+
+    @property
+    def map_size(self):
+        return self._map_size
+
+    @map_size.setter
+    def map_size(self, value):
+        if self.uuid is not None:
+            warn("Overriding the map size of a scenario linked Area object. Area.uuid was set to None.",
+                 category=UuidForcedUnlinkWarning)
+            self.uuid = None
+        self._map_size_value = value
+
+    @property
+    def _map_size(self) -> int:
+        if self.uuid is not None:
+            return getters.get_map_size(self.uuid)
+        elif self._map_size_value is not None:
+            return self._map_size_value
+        else:
+            self._map_size_error()
+
+    @property
+    def _map_size_safe(self) -> int:
+        try:
+            return self._map_size
+        except ValueError:
+            return 999_999_999
+
+    @property
+    def maximum_coordinate(self) -> int:
+        """The maximum coordinate for the X or Y axis (like how 0 is the minimum)"""
+        return self._map_size_safe - 1
+
+    def associate_scenario(self, scenario: AoE2Scenario) -> None:
+        """
+        Associate area with scenario. Saves scenario UUID in this area object.
+
+        Args:
+            scenario: The scenario to associate with
+        """
+        self.uuid = scenario.uuid
+
+    def _force_association(self) -> None:
+        """Raise ValueError if UUID is not set"""
+        if self.uuid is None:
+            raise ValueError("Area object not associated with scenario. Cannot request terrain information")
+
+    def _force_map_size(self) -> int:
+        """
+        Raise ValueError if map_size isn't set. Error handling is done within self._map_size.
+        This just causes the error from within the `_map_size` if it isn't set.
+        Useful when a function conditionally uses the `_map_size`.
+        Where it 'sometimes' will and 'sometimes' won't throw the error
+        """
+        return self._map_size
+
+    def _map_size_error(self) -> None:
+        raise ValueError("No UUID or map_size was set. "
+                         "Set a map_size or associate with a scenario to use map size related functionality")
+
+    # ============================ Conversion functions ============================
+
+    def to_coords(self, as_terrain: bool = False) -> OrderedSet[Tile | 'TerrainTile']:
+        """
+        Converts the selection to an OrderedSet of (x, y) coordinates
+
+        Args:
+            as_terrain: If the returning coordinates should be Tile objects or Terrain Tiles. If `True` the coordinates
+                are returned as TerrainTiles.
+
+        Returns:
+            An OrderedSet of Tiles ((x, y) named tuple) of the selection.
+
+        Examples:
+            The selection: ``((3,3), (5,5))`` would result in an OrderedSet with a length of 9::
+
+                [
+                    (3,3), (4,3)  ...,
+                    ...,   ...,   ...,
+                    ...,   (4,5), (5,5)
+                ]
+        """
+        tiles = OrderedSet(
+            Tile(x, y) for y in self.get_range_y() for x in self.get_range_x() if self.is_within_selection(x, y)
+        )
+        return self._tiles_to_terrain_tiles(tiles) if as_terrain else tiles
+
+    def to_chunks(
+            self,
+            as_terrain: bool = False
+    ) -> List[OrderedSet[Tile | 'TerrainTile']]:
+        """
+        Converts the selection to a list of OrderedSets with Tile NamedTuples with (x, y) coordinates.
+        The separation between chunks is based on if they're connected to each other.
+        So the tiles must share an edge (i.e. they should be non-diagonal).
+
+        Args:
+            as_terrain: If the returning coordinates should be Tile objects or Terrain Tiles. If `True` the coordinates
+                are returned as TerrainTiles.
+
+        Returns:
+            A list of OrderedSets of Tiles ((x, y) named tuple) of the selection.
+        """
+        tiles = self.to_coords()
+
+        # Shortcut for states that CANNOT be more than one chunk
+        if self.state in AreaState.unchunkables():
+            return [tiles]
+
+        chunks: Dict[int, List[Tile]] = {}
+        for tile in tiles:
+            chunk_id = self._get_chunk_id(tile)
+            chunks.setdefault(chunk_id, []).append(tile)
+
+        map_size = self._map_size
+        chunks_ordered: List[OrderedSet[Tile | 'TerrainTile']] = []
+        for chunk_id, chunk_tiles in chunks.items():
+            tiles = self._tiles_to_terrain_tiles(chunk_tiles) if as_terrain else chunk_tiles
+            chunks_ordered.append(
+                OrderedSet(sorted(tiles, key=lambda t: t.y * map_size + t.x))
+            )
+
+        return chunks_ordered
+
+    def to_dict(self, prefix: str = "area_") -> Dict[str, int]:
+        """
+        Converts the 2 corners of the selection to area keys for use in effects etc.
+        This can be used by adding double stars (**) before this function.
+
+        Usage:
+            The selection: ``((3,3), (5,5))`` would result in a dict that looks like:
+                ``{'area_x1': 3, 'area_y1': 3, 'area_x2': 5, 'area_y2': 5}``
+            Then do: ``**area.to_dict()`` in a function that accepts area tiles
+
+        Args:
+            prefix: The prefix of the string before 'x1' (e.g. prefix="coord_" will result in: "coord_x1" as key)
+
+        Returns:
+            A dict with area_x1, area_y1, area_x2, area_y2 as keys and their respective values (if prefix is unchanged).
+        """
+        return {f"{prefix}{key}": getattr(self, key) for key in ['x1', 'y1', 'x2', 'y2']}
+
+    # ============================ Getters ============================
+
+    def get_selection(self) -> Tuple[Tuple[int, int], Tuple[int, int]]:
+        """Get the four values of the selection as: ((x1, y1), (x2, y2))"""
+        return (self.x1, self.y1), (self.x2, self.y2)
+
+    def get_raw_selection(self) -> Tuple[Tuple[int, int], Tuple[int, int]]:
+        """Get the four values of the selection (even if they are outside the map) as: ((x1, y1), (x2, y2))"""
+        return (self._x1, self._y1), (self._x2, self._y2)
+
+    def get_center(self) -> Tuple[float, float]:
+        """Get center of current selection"""
+        return (self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2
+
+    def get_center_int(self) -> Tuple[int, int]:
+        """Get center of current selection, coords can only be integers. If even length, the value is ceiled"""
+        return math.ceil((self.x1 + self.x2) / 2), math.ceil((self.y1 + self.y2) / 2)
+
+    def get_range_x(self) -> range:
+        """Returns a range object for the x coordinates."""
+        return range(self.x1, self.x2 + 1)
+
+    def get_range_y(self) -> range:
+        """Returns a range object for the y coordinates."""
+        return range(self.y1, self.y2 + 1)
+
+    def get_width(self) -> int:
+        """Returns the length of the x side of the selection."""
+        return self.x2 + 1 - self.x1
+
+    def get_height(self) -> int:
+        """Returns the length of the y side of the selection."""
+        return self.y2 + 1 - self.y1
+
+    def get_dimensions(self) -> Tuple[int, int]:
+        """Returns the lengths of the x & y side of the selection (in that order)."""
+        return self.get_width(), self.get_height()
+
+    # ============================ Use functions ============================
+
+    def use_full(self) -> Area:
+        """Sets the area object to use the entire selection"""
+        self.state = AreaState.FULL
+        return self
+
+    def use_only_edge(self, line_width: int = None, line_width_x: int = None, line_width_y: int = None) -> Area:
+        """
+        Sets the area object to only use the edge of the selection
+
+        Args:
+            line_width: The width of the x & y edge line
+            line_width_x: The width of the x edge line
+            line_width_y: The width of the y edge line
+
+        Returns:
+            This area object
+        """
+        self.attrs(line_width=line_width, line_width_x=line_width_x, line_width_y=line_width_y)
+        self.state = AreaState.EDGE
+        return self
+
+    def use_only_corners(self, corner_size: int = None, corner_size_x: int = None, corner_size_y: int = None) -> Area:
+        """
+        Sets the area object to only use the corners pattern within the selection.
+
+        Args:
+            corner_size: The size along both the x and y-axis of the corner areas
+            corner_size_x: The size along the x-axis of the corner areas
+            corner_size_y: The size along the y-axis of the corner areas
+
+        Returns:
+            This area object
+        """
+        self.attrs(corner_size=corner_size, corner_size_x=corner_size_x, corner_size_y=corner_size_y)
+        self.state = AreaState.CORNERS
+        return self
+
+    def use_pattern_grid(
+            self,
+            block_size: int = None,
+            gap_size: int = None,
+            block_size_x: int = None,
+            block_size_y: int = None,
+            gap_size_x: int = None,
+            gap_size_y: int = None
+    ) -> Area:
+        """
+        Sets the area object to use a grid pattern within the selection.
+
+        Args:
+            block_size: The size of the gaps between lines
+            gap_size: The width of the grid lines
+            block_size_x: The size of the x gaps between lines
+            block_size_y: The size of the y gaps between lines
+            gap_size_x: The width of the x grid lines
+            gap_size_y: The width of the y grid lines
+
+        Returns:
+            This area object
+        """
+        self.attrs(block_size=block_size, gap_size=gap_size,
+                   block_size_x=block_size_x, gap_size_x=gap_size_x,
+                   block_size_y=block_size_y, gap_size_y=gap_size_y)
+        self.state = AreaState.GRID
+        return self
+
+    def use_pattern_lines(self, axis: str = None, gap_size: int = None, line_width: int = None) -> Area:
+        """
+        Sets the area object to use a lines pattern within the selection.
+
+        Args:
+            axis: The axis the lines should follow. Can either be "x" or "y"
+            gap_size: The size of the gaps between lines
+            line_width: The width of the x & y lines
+
+        Returns:
+            This area object
+        """
+        if axis is not None:
+            axis = axis.lower()
+        self.attrs(axis=axis, gap_size=gap_size, line_width=line_width)
+        self.state = AreaState.LINES
+        return self
+
+    # ============================ Adjustment functions ============================
+
+    def invert(self) -> Area:
+        """
+        Inverts the inverted boolean. Causes the `to_coords` to return the inverted selection. This function is
+        especially useful for the grid state. It's not as useful for the edge which would be the same as shrinking the
+        selection. When used with the fill state an empty set is returned.
+
+        **Please note:** This inverts the INTERNAL selection. Tiles OUTSIDE the selection will NOT be returned.
+        """
+        self.inverted = not self.inverted
+        return self
+
+    def along_axis(self, axis: str) -> Area:
+        """Sets the axis. Can be either "x" or "y". """
+        self.axis = axis
+        return self
+
+    def attr(self, key: str | AreaAttr, value: int) -> Area:
+        """Sets the attribute to the given value. AreaAttr or str can be used as key"""
+        if isinstance(key, AreaAttr):
+            key = key.value
+
+        keys: List[str] = [key]
+        if key in ['line_width', 'gap_size', 'corner_size', 'block_size']:
+            keys = [key + '_x', key + '_y']
+
+        for key in keys:
+            setattr(self, key, value)
+        return self
+
+    def attrs(
+            self,
+            x1: int = None,
+            y1: int = None,
+            x2: int = None,
+            y2: int = None,
+            gap_size: int = None,
+            gap_size_x: int = None,
+            gap_size_y: int = None,
+            line_width: int = None,
+            line_width_x: int = None,
+            line_width_y: int = None,
+            axis: str = None,
+            corner_size: int = None,
+            corner_size_x: int = None,
+            corner_size_y: int = None,
+            block_size: int = None,
+            block_size_x: int = None,
+            block_size_y: int = None,
+    ) -> Area:
+        """
+        Sets multiple attributes to the corresponding values.
+
+        Returns:
+            This area object
+        """
+        for key, value in locals().items():
+            if value is None or key == 'self':
+                continue
+            self.attr(key, value)
+        return self
+
+    def move(self, offset_x: int = 0, offset_y: int = 0):
+        """Moves the selection area in a given direction relative to its current position"""
+        self.x1 += offset_x
+        self.y1 += offset_y
+        self.x2 += offset_x
+        self.y2 += offset_y
+        return self
+
+    def move_to(self, corner: Literal['west', 'north', 'east', 'south'], x: int, y: int):
+        """
+        Moves the selection area to the given coordinate by placing the given corner of this area on the coordinate.
+
+        For center placement, use ``.center(...)``
+        """
+        width = self.get_width() - 1
+        height = self.get_height() - 1
+
+        if corner == 'west':
+            self.x1, self.y1, self.x2, self.y2 = x, y, x + width, y + height
+        elif corner == 'north':
+            self.x1, self.y1, self.x2, self.y2 = x - width, y, x, y + height
+        elif corner == 'east':
+            self.x1, self.y1, self.x2, self.y2 = x - width, y - height, x, y
+        elif corner == 'south':
+            self.x1, self.y1, self.x2, self.y2 = x, y - height, x + width, y
+
+        return self
+
+    def size(self, n: int) -> Area:
+        """
+        Sets the selection to a size around the center. If center is (4,4) with a size of 3 the selection will become
+        ``((3,3), (5,5))``
+        """
+        center_x, center_y = self.get_center_int()
+        n -= 1  # Ignore center tile
+        self.x1 = center_x - math.ceil(n / 2)
+        self.y1 = center_y - math.ceil(n / 2)
+        self.x2 = center_x + math.floor(n / 2)
+        self.y2 = center_y + math.floor(n / 2)
+        return self
+
+    def height(self, n: int) -> Area:
+        """
+        Sets the height (y-axis) of the selection. Shrinks/Expands both sides equally.
+        If the expansion hits the edge of the map, it'll expand on the other side.
+        """
+        c1, c2 = self._get_length_change(n, self.get_height(), self.y1, self.y2)
+
+        self.y1 = self._y1 + c1
+        self.y2 = self._y2 + c2
+        return self
+
+    def width(self, n: int) -> Area:
+        """
+        Sets the width (x-axis) of the selection. Shrinks/Expands both sides equally.
+        If the expansion hits the edge of the map, it'll expand on the other side.
+        """
+        c1, c2 = self._get_length_change(n, self.get_width(), self.x1, self.x2)
+
+        self.x1 = self._x1 + c1
+        self.x2 = self._x2 + c2
+        return self
+
+    def center(self, x: int, y: int) -> Area:
+        """
+        Moves the selection center to a given position. When the given center forces the selection of the edge of the
+        map the off-map tiles will not be returned. When moving the selection back into the map the tiles will be
+        returned again.
+
+        If you want to limit moving the center without changing the selection box size, use: ``center_bounded``
+        """
+        center_x, center_y = self.get_center()
+        diff_x, diff_y = math.floor(x - center_x), math.floor(y - center_y)
+        self.x1 = self._x1 + diff_x
+        self.y1 = self._y1 + diff_y
+        self.x2 = self._x2 + diff_x
+        self.y2 = self._y2 + diff_y
+        return self
+
+    def center_bounded(self, x: int, y: int) -> Area:
+        """
+        Moves the selection center to a given position on the map. This function makes sure it cannot go over the edge
+        of the map. The selection will be forced against the edge of the map and the selection will not be decreased in
+        size.
+        """
+        self._force_map_size()
+
+        center_x, center_y = self.get_center()
+        diff_x, diff_y = math.floor(x - center_x), math.floor(y - center_y)
+        if diff_x < 0 and abs(diff_x) > self.x1:
+            diff_x = -self.x1
+        elif diff_x > 0 and diff_x > (distance_x := self.maximum_coordinate - self.x2):
+            diff_x = distance_x
+        if diff_y < 0 and abs(diff_y) > self.y1:
+            diff_y = -self.y1
+        elif diff_y > 0 and diff_y > (distance_y := self.maximum_coordinate - self.y2):
+            diff_y = distance_y
+        self.x1 += diff_x
+        self.y1 += diff_y
+        self.x2 += diff_x
+        self.y2 += diff_y
+        return self
+
+    def select_entire_map(self) -> Area:
+        """Sets the selection to the entire map"""
+        self.x1, self.y1, self.x2, self.y2 = 0, 0, self._map_size, self._map_size
+        return self
+
+    def select(self, x1: int, y1: int, x2: int = None, y2: int = None) -> Area:
+        """Sets the selection to the given coordinates"""
+        x2, y2 = self._negative_coord(x2, y2)
+
+        self.x1, self.y1, self.x2, self.y2 = validate_coords(x1, y1, x2, y2)
+
+        return self
+
+    def select_centered(self, x: int, y: int, dx: int = 1, dy: int = 1) -> Area:
+        """Sets the selection to the given coordinates"""
+        half_x, half_y = (dx - 1) / 2, (dy - 1) / 2
+        self.select(
+            x1=x - math.ceil(half_x),
+            y1=y - math.ceil(half_y),
+            x2=x + math.floor(half_x),
+            y2=y + math.floor(half_y),
+        )
+        return self
+
+    def shrink(self, n: int) -> Area:
+        """Shrinks the selection from all sides"""
+        self.shrink_x1(n)
+        self.shrink_y1(n)
+        self.shrink_x2(n)
+        self.shrink_y2(n)
+        return self
+
+    def shrink_x1(self, n: int) -> Area:
+        """Shrinks the selection from the first corner on the X axis by n"""
+        self.x1 = min(self._x1 + n, self._x2)
+        return self
+
+    def shrink_y1(self, n: int) -> Area:
+        """Shrinks the selection from the first corner on the Y axis by n"""
+        self.y1 = min(self._y1 + n, self._y2)
+        return self
+
+    def shrink_x2(self, n: int) -> Area:
+        """Shrinks the selection from the second corner on the X axis by n"""
+        self.x2 = max(self._x1, self._x2 - n)
+        return self
+
+    def shrink_y2(self, n: int) -> Area:
+        """Shrinks the selection from the second corner on the Y axis by n"""
+        self.y2 = max(self._y1, self._y2 - n)
+        return self
+
+    def expand(self, n: int) -> Area:
+        """Expands the selection from all sides"""
+        self.expand_x1(n)
+        self.expand_y1(n)
+        self.expand_x2(n)
+        self.expand_y2(n)
+        return self
+
+    def expand_x1(self, n: int) -> Area:
+        """Expands the selection from the first corner on the X axis by n"""
+        self.x1 = self.x1 - n
+        return self
+
+    def expand_y1(self, n: int) -> Area:
+        """Expands the selection from the first corner on the Y axis by n"""
+        self.y1 = self.y1 - n
+        return self
+
+    def expand_x2(self, n: int) -> Area:
+        """Expands the selection from the second corner on the X axis by n"""
+        self.x2 = self.x2 + n
+        return self
+
+    def expand_y2(self, n: int) -> Area:
+        """Expands the selection from the second corner on the Y axis by n"""
+        self.y2 = self.y2 + n
+        return self
+
+    # ============================ Test against ... functions ============================
+
+    def is_within_selection(self, x: int = -1, y: int = -1, tile: Tile = None) -> bool:
+        """
+        If a given (x,y) location is within the selection.
+
+        Args:
+            x: The X coordinate
+            y: The Y coordinate
+            tile: A Tile object, replacing the x & y coordinates
+
+        Returns:
+            `True` if (x,y) is within the selection, `False` otherwise
+        """
+        if tile is not None:
+            x, y = tile
+
+        if not (self._x1 <= x <= self._x2 and self._y1 <= y <= self._y2):
+            return False
+
+        is_within: bool
+        if self.state == AreaState.EDGE:
+            is_within = self._is_edge_tile(x, y)
+        elif self.state == AreaState.GRID:
+            is_within = self._is_a_grid_tile(x, y)
+        elif self.state == AreaState.LINES:
+            is_within = self._is_a_line_tile(x, y)
+        elif self.state == AreaState.CORNERS:
+            is_within = self._is_a_corner_tile(x, y)
+        else:
+            is_within = True
+        return self._invert_if_inverted(is_within)
+
+    def is_within_bounds(self) -> bool:
+        """Check if the current selection is within the map"""
+        self._force_map_size()
+
+        return 0 <= self._x1 < self.map_size \
+            and 0 <= self._y1 < self.map_size \
+            and 0 <= self._x2 < self.map_size \
+            and 0 <= self._y2 < self.map_size
+
+    # ============================ Miscellaneous functions ============================
+
+    def copy(self) -> Area:
+        """
+        Copy this instance of an Area. Useful for when you want to do multiple extractions (to_...) from the same source
+        with small tweaks.
+
+        Examples:
+
+            Get a grid and the surrounding edge::
+
+                area = Area.select(10,10,20,20)
+                edge = area.copy().expand(1).use_only_edge().to_coords()
+                # Without copy you'd have to undo all changes above. In this case that'd be: `.shrink(1)`
+                grid = area.use_pattern_grid().to_coords()
+
+        Returns:
+            A copy of this Area object
+        """
+        return copy.copy(self)
+
+    # ============================ Support functions ============================
+
+    def _get_length_change(self, new_len: int, cur_len: int, first_coord: int, second_coord) -> Tuple[int, int]:
+        """
+        Calculate the differences in tiles for the 2 points (x1 & x2) or (y1 & y2) when the length of an edge is changed
+
+        Args:
+            new_len: The new length
+            cur_len: The current length
+            first_coord: Coord of the first corner (x1 or y1)
+            second_coord: Coord of the first corner (x2 or y2)
+
+        Returns:
+            The differences for the first and second coordinate. Can be negative and positive ints.
+        """
+        half: float = (new_len - cur_len) / 2
+        half1, half2 = -half, half
+        if half > 0:
+            if half > first_coord:
+                half1 = -first_coord
+                half2 += half - first_coord
+            if half > (dist := self._map_size_safe - second_coord):
+                half2 = dist
+                half1 += half - dist
+            return math.floor(half1), math.floor(half2)
+        return math.ceil(half1), math.ceil(half2)
+
+    def _is_edge_tile(self, x: int, y: int) -> bool:
+        """ Returns if a given tile (x,y) is an edge tile of the set selection given a certain edge width."""
+        return any((
+            0 <= x - self.x1 < self.line_width_x,
+            0 <= y - self.y1 < self.line_width_y,
+            0 <= self.x2 - x < self.line_width_x,
+            0 <= self.y2 - y < self.line_width_y
+        ))
+
+    def _invert_if_inverted(self, bool_: bool) -> bool:
+        """Inverts the boolean if the area is in inverted state."""
+        return not bool_ if self.inverted else bool_
+
+    def _is_a_corner_tile(self, x: int, y: int) -> bool:
+        """If a given (x,y) location is a corner tile."""
+        return ((self.x1 <= x < self.x1 + self.corner_size_x) or (self.x2 - self.corner_size_x < x <= self.x2)) and \
+            ((self.y1 <= y < self.y1 + self.corner_size_y) or (self.y2 - self.corner_size_y < y <= self.y2))
+
+    def _is_a_grid_tile(self, x: int, y: int) -> bool:
+        """If a given (x,y) location is within the grid selection."""
+        return (x - self.x1) % (self.block_size_x + self.gap_size_x) < self.block_size_x and \
+            (y - self.y1) % (self.block_size_y + self.gap_size_y) < self.block_size_y
+
+    def _is_a_line_tile(self, x: int, y: int) -> bool:
+        """If a given (x,y) location is within the grid selection."""
+        if self.axis == "x":
+            return (y - self.y1) % (self.gap_size_y + self.line_width_y) < self.line_width_y
+        elif self.axis == "y":
+            return (x - self.x1) % (self.gap_size_x + self.line_width_x) < self.line_width_x
+        raise ValueError("Invalid axis value. Should be either x or y")
+
+    def _minmax_val(self, val: int | float) -> int | float:
+        """Keeps a given value within the bounds of ``0 <= val < map_size``."""
+        return max(0, min(val, self.maximum_coordinate))
+
+    def _negative_coord(self, *args: int) -> List[int]:
+        """Converts negative coordinates to the non negative value. Like: ``-1 == 119`` when ``map_size = 119``"""
+        return [
+            (self._map_size + coord) if coord and coord < 0 else coord
+            for coord in args
+        ]
+
+    def _tiles_to_terrain_tiles(self, tiles: Iterable[Tile]) -> OrderedSet['TerrainTile']:
+        """
+        Converts the selection to an OrderedSet of terrain tile objects from the map manager.
+        Can only be used if the area has been associated with a scenario.
+
+        Returns:
+            An OrderedSet of terrain tiles from the map manager based on the selection.
+        """
+        self._force_association()
+        terrain = getters.get_terrain(self.uuid)
+        map_size = self._map_size
+        return OrderedSet(terrain[xy_to_i(x, y, map_size)] for (x, y) in tiles)
+
+    def _get_chunk_id(self, tile: Tile) -> int:
+        """
+        This function gets the Chunk id of a tile based on the current state and configs. The chunk ID identifies which
+        chunk the given tile is in. This is useful for separating chunks that are connected but shouldn't be in the same
+        chunk (like when creating a checker or stripe pattern)
+
+        Args:
+            tile: The tile to check as Tile object
+
+        Returns:
+            The int ID of the chunk, or, -1 when it's not in a selection, or 0 when the selection cannot be split into
+                chunks.
+
+        Raises:
+            ValueError: if the area configuration isn't supported by this function.
+        """
+        if not self.is_within_selection(tile=tile):
+            return -1
+
+        if self.state in AreaState.unchunkables():
+            return 0
+
+        elif self.state == AreaState.GRID:
+            if self.inverted:
+                return 0
+            per_row = math.ceil(self.get_height() / (self.block_size_x + self.gap_size_x))
+            return (tile.x - self.x1) // (self.block_size_x + self.gap_size_x) + \
+                (tile.y - self.y1) // (self.block_size_y + self.gap_size_y) * per_row
+
+        elif self.state == AreaState.LINES:
+            if self.axis == "x":
+                return (tile.y - self.y1) // (self.line_width_y + self.gap_size_y)
+            elif self.axis == "y":
+                return (tile.x - self.x1) // (self.line_width_x + self.gap_size_x)
+
+        elif self.state == AreaState.CORNERS:
+            # 0 Left, 1 Top, 2 Right, 3 Bottom
+            if self.x1 <= tile.x < self.x1 + self.corner_size_x and self.y1 <= tile.y < self.y1 + self.corner_size_y:
+                return 0
+            if self.x2 - self.corner_size_x < tile.x <= self.x2 and self.y1 <= tile.y < self.y1 + self.corner_size_y:
+                return 1
+            if self.x2 - self.corner_size_x < tile.x <= self.x2 and self.y2 - self.corner_size_y < tile.y <= self.y2:
+                return 2
+            if self.x1 <= tile.x < self.x1 + self.corner_size_x and self.y2 - self.corner_size_y < tile.y <= self.y2:
+                return 3
+        raise ValueError(f"Invalid area configuration for getting the Chunk ID. If you believe this is an error, "
+                         f"please raise an issue on github or in the Discord server")
+
+    def __repr__(self) -> str:
+        return f"Area(x1={self.x1}, y1={self.y1}, x2={self.x2}, y2={self.y2}, state={self.state.name})"
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +

+axis: str = '' + + + instance-attribute + + +

+ +
+ Type: +str +
+ + Value: +''
+ + + + + +
+
+ +
+ +
+ + + + +

+block_size_x: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+block_size_y: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+corner1 + + + property + writable + + +

+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +

+corner2 + + + property + writable + + +

+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +

+corner_size_x: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+corner_size_y: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+gap_size_x: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+gap_size_y: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+inverted: bool = False + + + instance-attribute + + +

+ +
+ Type: +bool +
+ + Value: +False
+ + + + + +
+
+ +
+ +
+ + + + +

+line_width_x: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+line_width_y: int = 1 + + + instance-attribute + + +

+ +
+ Type: +int +
+ + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+map_size + + + property + writable + + +

+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +

+maximum_coordinate: int + + + property + + +

+ +
+ Type: +int + +
+ + + + + +
+ +

The maximum coordinate for the X or Y axis (like how 0 is the minimum)

+
+ +
+ +
+ + + + +

+state: AreaState = AreaState.FULL + + + instance-attribute + + +

+ +
+ Type: +AreaState +
+ + Value: +AreaState.FULL
+ + + + + +
+
+ +
+ +
+ + + + +

+uuid: UUID = uuid + + + instance-attribute + + +

+ +
+ Type: +UUID +
+ + Value: +uuid
+ + + + + +
+
+ +
+ +
+ + + + +

+x1: int + + + property + writable + + +

+ +
+ Type: +int + +
+ + + + + +
+
+ +
+ +
+ + + + +

+x2: int + + + property + writable + + +

+ +
+ Type: +int + +
+ + + + + +
+
+ +
+ +
+ + + + +

+y1: int + + + property + writable + + +

+ +
+ Type: +int + +
+ + + + + +
+
+ +
+ +
+ + + + +

+y2: int + + + property + writable + + +

+ +
+ Type: +int + +
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +

+def __init__(...) + +

+ + +
+ +

Object to easily select an area on the map. Uses method chaining for ease of use.

+

Please note: Setting a uuid will always overwrite the map_size attribute, even if it's not None.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
map_size + int + +
+

The size of the map this area object will handle

+
+
+ None +
uuid + UUID + +
+

The UUID of the scenario this area belongs to

+
+
+ None +
x1 + int + +
+

The X location of the left corner

+
+
+ None +
y1 + int + +
+

The Y location of the left corner

+
+
+ None +
x2 + int + +
+

The X location of the right corner

+
+
+ None +
y2 + int + +
+

The Y location of the right corner

+
+
+ None +
corner1 + Tile + +
+

The location of the left corner

+
+
+ None +
corner2 + Tile + +
+

The location of the right corner

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
 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
def __init__(
+        self,
+        map_size: int = None,
+        uuid: UUID = None,
+        x1: int = None,
+        y1: int = None,
+        x2: int = None,
+        y2: int = None,
+        corner1: Tile = None,
+        corner2: Tile = None,
+) -> None:
+    """
+    Object to easily select an area on the map. Uses method chaining for ease of use.
+
+    **Please note**: Setting a ``uuid`` will always overwrite the ``map_size`` attribute, even if it's not ``None``.
+
+    Args:
+        map_size: The size of the map this area object will handle
+        uuid: The UUID of the scenario this area belongs to
+        x1: The X location of the left corner
+        y1: The Y location of the left corner
+        x2: The X location of the right corner
+        y2: The Y location of the right corner
+        corner1: The location of the left corner
+        corner2: The location of the right corner
+    """
+    if map_size is None and uuid is None:
+        if corner1 is None and (x1 is None or y1 is None):
+            raise ValueError("Cannot create area object without knowing the map size or a UUID from a scenario.")
+
+    self.uuid: UUID = uuid
+    if map_size is not None:
+        self._map_size_value = map_size
+    else:
+        self._map_size_value = None
+
+    if values_are_valid(x1, y1) or value_is_valid(corner1):
+        x1, y1, x2, y2 = validate_coords(x1, y1, x2, y2, corner1, corner2)
+    else:
+        x1 = y1 = x2 = y2 = math.floor(self._map_size / 2)  # Select the center tile
+
+    self.state: AreaState = AreaState.FULL
+    self.inverted: bool = False
+
+    self.x1: int = x1
+    self.y1: int = y1
+    self.x2: int = x2
+    self.y2: int = y2
+
+    self.gap_size_x: int = 1
+    self.gap_size_y: int = 1
+    self.line_width_x: int = 1
+    self.line_width_y: int = 1
+    self.block_size_x: int = 1
+    self.block_size_y: int = 1
+
+    self.axis: str = ""
+
+    self.corner_size_x: int = 1
+    self.corner_size_y: int = 1
+
+
+
+ +
+ +
+ + +

+def __repr__(...) + +

+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
887
+888
def __repr__(self) -> str:
+    return f"Area(x1={self.x1}, y1={self.y1}, x2={self.x2}, y2={self.y2}, state={self.state.name})"
+
+
+
+ +
+ +
+ + +

+def along_axis(...) + +

+ + +
+ +

Sets the axis. Can be either "x" or "y".

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
axis + str + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
461
+462
+463
+464
def along_axis(self, axis: str) -> Area:
+    """Sets the axis. Can be either "x" or "y". """
+    self.axis = axis
+    return self
+
+
+
+ +
+ +
+ + +

+def associate_scenario(...) + +

+ + +
+ +

Associate area with scenario. Saves scenario UUID in this area object.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
scenario + AoE2Scenario + +
+

The scenario to associate with

+
+
+ required +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
215
+216
+217
+218
+219
+220
+221
+222
def associate_scenario(self, scenario: AoE2Scenario) -> None:
+    """
+    Associate area with scenario. Saves scenario UUID in this area object.
+
+    Args:
+        scenario: The scenario to associate with
+    """
+    self.uuid = scenario.uuid
+
+
+
+ +
+ +
+ + +

+def attr(...) + +

+ + +
+ +

Sets the attribute to the given value. AreaAttr or str can be used as key

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
key + str | AreaAttr + - + required +
value + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
def attr(self, key: str | AreaAttr, value: int) -> Area:
+    """Sets the attribute to the given value. AreaAttr or str can be used as key"""
+    if isinstance(key, AreaAttr):
+        key = key.value
+
+    keys: List[str] = [key]
+    if key in ['line_width', 'gap_size', 'corner_size', 'block_size']:
+        keys = [key + '_x', key + '_y']
+
+    for key in keys:
+        setattr(self, key, value)
+    return self
+
+
+
+ +
+ +
+ + +

+def attrs(...) + +

+ + +
+ +

Sets multiple attributes to the corresponding values.

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Area + +
+

This area object

+
+
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x1 + int + - + None +
y1 + int + - + None +
x2 + int + - + None +
y2 + int + - + None +
gap_size + int + - + None +
gap_size_x + int + - + None +
gap_size_y + int + - + None +
line_width + int + - + None +
line_width_x + int + - + None +
line_width_y + int + - + None +
axis + str + - + None +
corner_size + int + - + None +
corner_size_x + int + - + None +
corner_size_y + int + - + None +
block_size + int + - + None +
block_size_x + int + - + None +
block_size_y + int + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
def attrs(
+        self,
+        x1: int = None,
+        y1: int = None,
+        x2: int = None,
+        y2: int = None,
+        gap_size: int = None,
+        gap_size_x: int = None,
+        gap_size_y: int = None,
+        line_width: int = None,
+        line_width_x: int = None,
+        line_width_y: int = None,
+        axis: str = None,
+        corner_size: int = None,
+        corner_size_x: int = None,
+        corner_size_y: int = None,
+        block_size: int = None,
+        block_size_x: int = None,
+        block_size_y: int = None,
+) -> Area:
+    """
+    Sets multiple attributes to the corresponding values.
+
+    Returns:
+        This area object
+    """
+    for key, value in locals().items():
+        if value is None or key == 'self':
+            continue
+        self.attr(key, value)
+    return self
+
+
+
+ +
+ +
+ + +

+def center(...) + +

+ + +
+ +

Moves the selection center to a given position. When the given center forces the selection of the edge of the +map the off-map tiles will not be returned. When moving the selection back into the map the tiles will be +returned again.

+

If you want to limit moving the center without changing the selection box size, use: center_bounded

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x + int + - + required +
y + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
def center(self, x: int, y: int) -> Area:
+    """
+    Moves the selection center to a given position. When the given center forces the selection of the edge of the
+    map the off-map tiles will not be returned. When moving the selection back into the map the tiles will be
+    returned again.
+
+    If you want to limit moving the center without changing the selection box size, use: ``center_bounded``
+    """
+    center_x, center_y = self.get_center()
+    diff_x, diff_y = math.floor(x - center_x), math.floor(y - center_y)
+    self.x1 = self._x1 + diff_x
+    self.y1 = self._y1 + diff_y
+    self.x2 = self._x2 + diff_x
+    self.y2 = self._y2 + diff_y
+    return self
+
+
+
+ +
+ +
+ + +

+def center_bounded(...) + +

+ + +
+ +

Moves the selection center to a given position on the map. This function makes sure it cannot go over the edge +of the map. The selection will be forced against the edge of the map and the selection will not be decreased in +size.

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x + int + - + required +
y + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
def center_bounded(self, x: int, y: int) -> Area:
+    """
+    Moves the selection center to a given position on the map. This function makes sure it cannot go over the edge
+    of the map. The selection will be forced against the edge of the map and the selection will not be decreased in
+    size.
+    """
+    self._force_map_size()
+
+    center_x, center_y = self.get_center()
+    diff_x, diff_y = math.floor(x - center_x), math.floor(y - center_y)
+    if diff_x < 0 and abs(diff_x) > self.x1:
+        diff_x = -self.x1
+    elif diff_x > 0 and diff_x > (distance_x := self.maximum_coordinate - self.x2):
+        diff_x = distance_x
+    if diff_y < 0 and abs(diff_y) > self.y1:
+        diff_y = -self.y1
+    elif diff_y > 0 and diff_y > (distance_y := self.maximum_coordinate - self.y2):
+        diff_y = distance_y
+    self.x1 += diff_x
+    self.y1 += diff_y
+    self.x2 += diff_x
+    self.y2 += diff_y
+    return self
+
+
+
+ +
+ +
+ + +

+def copy(...) + +

+ + +
+ +

Copy this instance of an Area. Useful for when you want to do multiple extractions (to_...) from the same source +with small tweaks.

+

Examples:

+
1
+2
+3
+4
+5
+6
Get a grid and the surrounding edge::
+
+    area = Area.select(10,10,20,20)
+    edge = area.copy().expand(1).use_only_edge().to_coords()
+    # Without copy you'd have to undo all changes above. In this case that'd be: `.shrink(1)`
+    grid = area.use_pattern_grid().to_coords()
+
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Area + +
+

A copy of this Area object

+
+
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
def copy(self) -> Area:
+    """
+    Copy this instance of an Area. Useful for when you want to do multiple extractions (to_...) from the same source
+    with small tweaks.
+
+    Examples:
+
+        Get a grid and the surrounding edge::
+
+            area = Area.select(10,10,20,20)
+            edge = area.copy().expand(1).use_only_edge().to_coords()
+            # Without copy you'd have to undo all changes above. In this case that'd be: `.shrink(1)`
+            grid = area.use_pattern_grid().to_coords()
+
+    Returns:
+        A copy of this Area object
+    """
+    return copy.copy(self)
+
+
+
+ +
+ +
+ + +

+def expand(...) + +

+ + +
+ +

Expands the selection from all sides

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
666
+667
+668
+669
+670
+671
+672
def expand(self, n: int) -> Area:
+    """Expands the selection from all sides"""
+    self.expand_x1(n)
+    self.expand_y1(n)
+    self.expand_x2(n)
+    self.expand_y2(n)
+    return self
+
+
+
+ +
+ +
+ + +

+def expand_x1(...) + +

+ + +
+ +

Expands the selection from the first corner on the X axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
674
+675
+676
+677
def expand_x1(self, n: int) -> Area:
+    """Expands the selection from the first corner on the X axis by n"""
+    self.x1 = self.x1 - n
+    return self
+
+
+
+ +
+ +
+ + +

+def expand_x2(...) + +

+ + +
+ +

Expands the selection from the second corner on the X axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
684
+685
+686
+687
def expand_x2(self, n: int) -> Area:
+    """Expands the selection from the second corner on the X axis by n"""
+    self.x2 = self.x2 + n
+    return self
+
+
+
+ +
+ +
+ + +

+def expand_y1(...) + +

+ + +
+ +

Expands the selection from the first corner on the Y axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
679
+680
+681
+682
def expand_y1(self, n: int) -> Area:
+    """Expands the selection from the first corner on the Y axis by n"""
+    self.y1 = self.y1 - n
+    return self
+
+
+
+ +
+ +
+ + +

+def expand_y2(...) + +

+ + +
+ +

Expands the selection from the second corner on the Y axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
689
+690
+691
+692
def expand_y2(self, n: int) -> Area:
+    """Expands the selection from the second corner on the Y axis by n"""
+    self.y2 = self.y2 + n
+    return self
+
+
+
+ +
+ +
+ + +

+def from_tiles(...) + + + classmethod + + +

+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
corner1 + Tile + - + required +
corner2 + Tile + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
128
+129
+130
@classmethod
+def from_tiles(cls, corner1: Tile, corner2: Tile = None):
+    return cls(corner1=corner1, corner2=corner2)
+
+
+
+ +
+ +
+ + +

+def from_uuid(...) + + + classmethod + + +

+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
uuid + UUID + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
124
+125
+126
@classmethod
+def from_uuid(cls, uuid: UUID) -> Area:
+    return cls(uuid=uuid)
+
+
+
+ +
+ +
+ + +

+def get_center(...) + +

+ + +
+ +

Get center of current selection

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
334
+335
+336
def get_center(self) -> Tuple[float, float]:
+    """Get center of current selection"""
+    return (self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2
+
+
+
+ +
+ +
+ + +

+def get_center_int(...) + +

+ + +
+ +

Get center of current selection, coords can only be integers. If even length, the value is ceiled

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
338
+339
+340
def get_center_int(self) -> Tuple[int, int]:
+    """Get center of current selection, coords can only be integers. If even length, the value is ceiled"""
+    return math.ceil((self.x1 + self.x2) / 2), math.ceil((self.y1 + self.y2) / 2)
+
+
+
+ +
+ +
+ + +

+def get_dimensions(...) + +

+ + +
+ +

Returns the lengths of the x & y side of the selection (in that order).

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
358
+359
+360
def get_dimensions(self) -> Tuple[int, int]:
+    """Returns the lengths of the x & y side of the selection (in that order)."""
+    return self.get_width(), self.get_height()
+
+
+
+ +
+ +
+ + +

+def get_height(...) + +

+ + +
+ +

Returns the length of the y side of the selection.

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
354
+355
+356
def get_height(self) -> int:
+    """Returns the length of the y side of the selection."""
+    return self.y2 + 1 - self.y1
+
+
+
+ +
+ +
+ + +

+def get_range_x(...) + +

+ + +
+ +

Returns a range object for the x coordinates.

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
342
+343
+344
def get_range_x(self) -> range:
+    """Returns a range object for the x coordinates."""
+    return range(self.x1, self.x2 + 1)
+
+
+
+ +
+ +
+ + +

+def get_range_y(...) + +

+ + +
+ +

Returns a range object for the y coordinates.

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
346
+347
+348
def get_range_y(self) -> range:
+    """Returns a range object for the y coordinates."""
+    return range(self.y1, self.y2 + 1)
+
+
+
+ +
+ +
+ + +

+def get_raw_selection(...) + +

+ + +
+ +

Get the four values of the selection (even if they are outside the map) as: ((x1, y1), (x2, y2))

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
330
+331
+332
def get_raw_selection(self) -> Tuple[Tuple[int, int], Tuple[int, int]]:
+    """Get the four values of the selection (even if they are outside the map) as: ((x1, y1), (x2, y2))"""
+    return (self._x1, self._y1), (self._x2, self._y2)
+
+
+
+ +
+ +
+ + +

+def get_selection(...) + +

+ + +
+ +

Get the four values of the selection as: ((x1, y1), (x2, y2))

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
326
+327
+328
def get_selection(self) -> Tuple[Tuple[int, int], Tuple[int, int]]:
+    """Get the four values of the selection as: ((x1, y1), (x2, y2))"""
+    return (self.x1, self.y1), (self.x2, self.y2)
+
+
+
+ +
+ +
+ + +

+def get_width(...) + +

+ + +
+ +

Returns the length of the x side of the selection.

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
350
+351
+352
def get_width(self) -> int:
+    """Returns the length of the x side of the selection."""
+    return self.x2 + 1 - self.x1
+
+
+
+ +
+ +
+ + +

+def height(...) + +

+ + +
+ +

Sets the height (y-axis) of the selection. Shrinks/Expands both sides equally. +If the expansion hits the edge of the map, it'll expand on the other side.

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
552
+553
+554
+555
+556
+557
+558
+559
+560
+561
def height(self, n: int) -> Area:
+    """
+    Sets the height (y-axis) of the selection. Shrinks/Expands both sides equally.
+    If the expansion hits the edge of the map, it'll expand on the other side.
+    """
+    c1, c2 = self._get_length_change(n, self.get_height(), self.y1, self.y2)
+
+    self.y1 = self._y1 + c1
+    self.y2 = self._y2 + c2
+    return self
+
+
+
+ +
+ +
+ + +

+def invert(...) + +

+ + +
+ +

Inverts the inverted boolean. Causes the to_coords to return the inverted selection. This function is +especially useful for the grid state. It's not as useful for the edge which would be the same as shrinking the +selection. When used with the fill state an empty set is returned.

+

Please note: This inverts the INTERNAL selection. Tiles OUTSIDE the selection will NOT be returned.

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
450
+451
+452
+453
+454
+455
+456
+457
+458
+459
def invert(self) -> Area:
+    """
+    Inverts the inverted boolean. Causes the `to_coords` to return the inverted selection. This function is
+    especially useful for the grid state. It's not as useful for the edge which would be the same as shrinking the
+    selection. When used with the fill state an empty set is returned.
+
+    **Please note:** This inverts the INTERNAL selection. Tiles OUTSIDE the selection will NOT be returned.
+    """
+    self.inverted = not self.inverted
+    return self
+
+
+
+ +
+ +
+ + +

+def is_within_bounds(...) + +

+ + +
+ +

Check if the current selection is within the map

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
727
+728
+729
+730
+731
+732
+733
+734
def is_within_bounds(self) -> bool:
+    """Check if the current selection is within the map"""
+    self._force_map_size()
+
+    return 0 <= self._x1 < self.map_size \
+        and 0 <= self._y1 < self.map_size \
+        and 0 <= self._x2 < self.map_size \
+        and 0 <= self._y2 < self.map_size
+
+
+
+ +
+ +
+ + +

+def is_within_selection(...) + +

+ + +
+ +

If a given (x,y) location is within the selection.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x + int + +
+

The X coordinate

+
+
+ -1 +
y + int + +
+

The Y coordinate

+
+
+ -1 +
tile + Tile + +
+

A Tile object, replacing the x & y coordinates

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ bool + +
+

True if (x,y) is within the selection, False otherwise

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
def is_within_selection(self, x: int = -1, y: int = -1, tile: Tile = None) -> bool:
+    """
+    If a given (x,y) location is within the selection.
+
+    Args:
+        x: The X coordinate
+        y: The Y coordinate
+        tile: A Tile object, replacing the x & y coordinates
+
+    Returns:
+        `True` if (x,y) is within the selection, `False` otherwise
+    """
+    if tile is not None:
+        x, y = tile
+
+    if not (self._x1 <= x <= self._x2 and self._y1 <= y <= self._y2):
+        return False
+
+    is_within: bool
+    if self.state == AreaState.EDGE:
+        is_within = self._is_edge_tile(x, y)
+    elif self.state == AreaState.GRID:
+        is_within = self._is_a_grid_tile(x, y)
+    elif self.state == AreaState.LINES:
+        is_within = self._is_a_line_tile(x, y)
+    elif self.state == AreaState.CORNERS:
+        is_within = self._is_a_corner_tile(x, y)
+    else:
+        is_within = True
+    return self._invert_if_inverted(is_within)
+
+
+
+ +
+ +
+ + +

+def move(...) + +

+ + +
+ +

Moves the selection area in a given direction relative to its current position

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
offset_x + int + - + 0 +
offset_y + int + - + 0 +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
511
+512
+513
+514
+515
+516
+517
def move(self, offset_x: int = 0, offset_y: int = 0):
+    """Moves the selection area in a given direction relative to its current position"""
+    self.x1 += offset_x
+    self.y1 += offset_y
+    self.x2 += offset_x
+    self.y2 += offset_y
+    return self
+
+
+
+ +
+ +
+ + +

+def move_to(...) + +

+ + +
+ +

Moves the selection area to the given coordinate by placing the given corner of this area on the coordinate.

+

For center placement, use .center(...)

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
corner + Literal['west', 'north', 'east', 'south'] + - + required +
x + int + - + required +
y + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
def move_to(self, corner: Literal['west', 'north', 'east', 'south'], x: int, y: int):
+    """
+    Moves the selection area to the given coordinate by placing the given corner of this area on the coordinate.
+
+    For center placement, use ``.center(...)``
+    """
+    width = self.get_width() - 1
+    height = self.get_height() - 1
+
+    if corner == 'west':
+        self.x1, self.y1, self.x2, self.y2 = x, y, x + width, y + height
+    elif corner == 'north':
+        self.x1, self.y1, self.x2, self.y2 = x - width, y, x, y + height
+    elif corner == 'east':
+        self.x1, self.y1, self.x2, self.y2 = x - width, y - height, x, y
+    elif corner == 'south':
+        self.x1, self.y1, self.x2, self.y2 = x, y - height, x + width, y
+
+    return self
+
+
+
+ +
+ +
+ + +

+def select(...) + +

+ + +
+ +

Sets the selection to the given coordinates

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x1 + int + - + required +
y1 + int + - + required +
x2 + int + - + None +
y2 + int + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
619
+620
+621
+622
+623
+624
+625
def select(self, x1: int, y1: int, x2: int = None, y2: int = None) -> Area:
+    """Sets the selection to the given coordinates"""
+    x2, y2 = self._negative_coord(x2, y2)
+
+    self.x1, self.y1, self.x2, self.y2 = validate_coords(x1, y1, x2, y2)
+
+    return self
+
+
+
+ +
+ +
+ + +

+def select_centered(...) + +

+ + +
+ +

Sets the selection to the given coordinates

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x + int + - + required +
y + int + - + required +
dx + int + - + 1 +
dy + int + - + 1 +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
627
+628
+629
+630
+631
+632
+633
+634
+635
+636
def select_centered(self, x: int, y: int, dx: int = 1, dy: int = 1) -> Area:
+    """Sets the selection to the given coordinates"""
+    half_x, half_y = (dx - 1) / 2, (dy - 1) / 2
+    self.select(
+        x1=x - math.ceil(half_x),
+        y1=y - math.ceil(half_y),
+        x2=x + math.floor(half_x),
+        y2=y + math.floor(half_y),
+    )
+    return self
+
+
+
+ +
+ +
+ + +

+def select_entire_map(...) + +

+ + +
+ +

Sets the selection to the entire map

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
614
+615
+616
+617
def select_entire_map(self) -> Area:
+    """Sets the selection to the entire map"""
+    self.x1, self.y1, self.x2, self.y2 = 0, 0, self._map_size, self._map_size
+    return self
+
+
+
+ +
+ +
+ + +

+def shrink(...) + +

+ + +
+ +

Shrinks the selection from all sides

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
638
+639
+640
+641
+642
+643
+644
def shrink(self, n: int) -> Area:
+    """Shrinks the selection from all sides"""
+    self.shrink_x1(n)
+    self.shrink_y1(n)
+    self.shrink_x2(n)
+    self.shrink_y2(n)
+    return self
+
+
+
+ +
+ +
+ + +

+def shrink_x1(...) + +

+ + +
+ +

Shrinks the selection from the first corner on the X axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
646
+647
+648
+649
def shrink_x1(self, n: int) -> Area:
+    """Shrinks the selection from the first corner on the X axis by n"""
+    self.x1 = min(self._x1 + n, self._x2)
+    return self
+
+
+
+ +
+ +
+ + +

+def shrink_x2(...) + +

+ + +
+ +

Shrinks the selection from the second corner on the X axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
656
+657
+658
+659
def shrink_x2(self, n: int) -> Area:
+    """Shrinks the selection from the second corner on the X axis by n"""
+    self.x2 = max(self._x1, self._x2 - n)
+    return self
+
+
+
+ +
+ +
+ + +

+def shrink_y1(...) + +

+ + +
+ +

Shrinks the selection from the first corner on the Y axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
651
+652
+653
+654
def shrink_y1(self, n: int) -> Area:
+    """Shrinks the selection from the first corner on the Y axis by n"""
+    self.y1 = min(self._y1 + n, self._y2)
+    return self
+
+
+
+ +
+ +
+ + +

+def shrink_y2(...) + +

+ + +
+ +

Shrinks the selection from the second corner on the Y axis by n

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
661
+662
+663
+664
def shrink_y2(self, n: int) -> Area:
+    """Shrinks the selection from the second corner on the Y axis by n"""
+    self.y2 = max(self._y1, self._y2 - n)
+    return self
+
+
+
+ +
+ +
+ + +

+def size(...) + +

+ + +
+ +

Sets the selection to a size around the center. If center is (4,4) with a size of 3 the selection will become +((3,3), (5,5))

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
def size(self, n: int) -> Area:
+    """
+    Sets the selection to a size around the center. If center is (4,4) with a size of 3 the selection will become
+    ``((3,3), (5,5))``
+    """
+    center_x, center_y = self.get_center_int()
+    n -= 1  # Ignore center tile
+    self.x1 = center_x - math.ceil(n / 2)
+    self.y1 = center_y - math.ceil(n / 2)
+    self.x2 = center_x + math.floor(n / 2)
+    self.y2 = center_y + math.floor(n / 2)
+    return self
+
+
+
+ +
+ +
+ + +

+def to_chunks(...) + +

+ + +
+ +

Converts the selection to a list of OrderedSets with Tile NamedTuples with (x, y) coordinates. +The separation between chunks is based on if they're connected to each other. +So the tiles must share an edge (i.e. they should be non-diagonal).

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
as_terrain + bool + +
+

If the returning coordinates should be Tile objects or Terrain Tiles. If True the coordinates +are returned as TerrainTiles.

+
+
+ False +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[OrderedSet[Tile | 'TerrainTile']] + +
+

A list of OrderedSets of Tiles ((x, y) named tuple) of the selection.

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
def to_chunks(
+        self,
+        as_terrain: bool = False
+) -> List[OrderedSet[Tile | 'TerrainTile']]:
+    """
+    Converts the selection to a list of OrderedSets with Tile NamedTuples with (x, y) coordinates.
+    The separation between chunks is based on if they're connected to each other.
+    So the tiles must share an edge (i.e. they should be non-diagonal).
+
+    Args:
+        as_terrain: If the returning coordinates should be Tile objects or Terrain Tiles. If `True` the coordinates
+            are returned as TerrainTiles.
+
+    Returns:
+        A list of OrderedSets of Tiles ((x, y) named tuple) of the selection.
+    """
+    tiles = self.to_coords()
+
+    # Shortcut for states that CANNOT be more than one chunk
+    if self.state in AreaState.unchunkables():
+        return [tiles]
+
+    chunks: Dict[int, List[Tile]] = {}
+    for tile in tiles:
+        chunk_id = self._get_chunk_id(tile)
+        chunks.setdefault(chunk_id, []).append(tile)
+
+    map_size = self._map_size
+    chunks_ordered: List[OrderedSet[Tile | 'TerrainTile']] = []
+    for chunk_id, chunk_tiles in chunks.items():
+        tiles = self._tiles_to_terrain_tiles(chunk_tiles) if as_terrain else chunk_tiles
+        chunks_ordered.append(
+            OrderedSet(sorted(tiles, key=lambda t: t.y * map_size + t.x))
+        )
+
+    return chunks_ordered
+
+
+
+ +
+ +
+ + +

+def to_coords(...) + +

+ + +
+ +

Converts the selection to an OrderedSet of (x, y) coordinates

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
as_terrain + bool + +
+

If the returning coordinates should be Tile objects or Terrain Tiles. If True the coordinates +are returned as TerrainTiles.

+
+
+ False +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ OrderedSet[Tile | 'TerrainTile'] + +
+

An OrderedSet of Tiles ((x, y) named tuple) of the selection.

+
+
+ + +

Examples:

+

The selection: ((3,3), (5,5)) would result in an OrderedSet with a length of 9::

+
1
+2
+3
+4
+5
[
+    (3,3), (4,3)  ...,
+    ...,   ...,   ...,
+    ...,   (4,5), (5,5)
+]
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
def to_coords(self, as_terrain: bool = False) -> OrderedSet[Tile | 'TerrainTile']:
+    """
+    Converts the selection to an OrderedSet of (x, y) coordinates
+
+    Args:
+        as_terrain: If the returning coordinates should be Tile objects or Terrain Tiles. If `True` the coordinates
+            are returned as TerrainTiles.
+
+    Returns:
+        An OrderedSet of Tiles ((x, y) named tuple) of the selection.
+
+    Examples:
+        The selection: ``((3,3), (5,5))`` would result in an OrderedSet with a length of 9::
+
+            [
+                (3,3), (4,3)  ...,
+                ...,   ...,   ...,
+                ...,   (4,5), (5,5)
+            ]
+    """
+    tiles = OrderedSet(
+        Tile(x, y) for y in self.get_range_y() for x in self.get_range_x() if self.is_within_selection(x, y)
+    )
+    return self._tiles_to_terrain_tiles(tiles) if as_terrain else tiles
+
+
+
+ +
+ +
+ + +

+def to_dict(...) + +

+ + +
+ +

Converts the 2 corners of the selection to area keys for use in effects etc. +This can be used by adding double stars (**) before this function.

+ + +
+ Usage +

The selection: ((3,3), (5,5)) would result in a dict that looks like: + {'area_x1': 3, 'area_y1': 3, 'area_x2': 5, 'area_y2': 5} +Then do: **area.to_dict() in a function that accepts area tiles

+
+ +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
prefix + str + +
+

The prefix of the string before 'x1' (e.g. prefix="coord_" will result in: "coord_x1" as key)

+
+
+ 'area_' +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Dict[str, int] + +
+

A dict with area_x1, area_y1, area_x2, area_y2 as keys and their respective values (if prefix is unchanged).

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
def to_dict(self, prefix: str = "area_") -> Dict[str, int]:
+    """
+    Converts the 2 corners of the selection to area keys for use in effects etc.
+    This can be used by adding double stars (**) before this function.
+
+    Usage:
+        The selection: ``((3,3), (5,5))`` would result in a dict that looks like:
+            ``{'area_x1': 3, 'area_y1': 3, 'area_x2': 5, 'area_y2': 5}``
+        Then do: ``**area.to_dict()`` in a function that accepts area tiles
+
+    Args:
+        prefix: The prefix of the string before 'x1' (e.g. prefix="coord_" will result in: "coord_x1" as key)
+
+    Returns:
+        A dict with area_x1, area_y1, area_x2, area_y2 as keys and their respective values (if prefix is unchanged).
+    """
+    return {f"{prefix}{key}": getattr(self, key) for key in ['x1', 'y1', 'x2', 'y2']}
+
+
+
+ +
+ +
+ + +

+def use_full(...) + +

+ + +
+ +

Sets the area object to use the entire selection

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
364
+365
+366
+367
def use_full(self) -> Area:
+    """Sets the area object to use the entire selection"""
+    self.state = AreaState.FULL
+    return self
+
+
+
+ +
+ +
+ + +

+def use_only_corners(...) + +

+ + +
+ +

Sets the area object to only use the corners pattern within the selection.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
corner_size + int + +
+

The size along both the x and y-axis of the corner areas

+
+
+ None +
corner_size_x + int + +
+

The size along the x-axis of the corner areas

+
+
+ None +
corner_size_y + int + +
+

The size along the y-axis of the corner areas

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Area + +
+

This area object

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
def use_only_corners(self, corner_size: int = None, corner_size_x: int = None, corner_size_y: int = None) -> Area:
+    """
+    Sets the area object to only use the corners pattern within the selection.
+
+    Args:
+        corner_size: The size along both the x and y-axis of the corner areas
+        corner_size_x: The size along the x-axis of the corner areas
+        corner_size_y: The size along the y-axis of the corner areas
+
+    Returns:
+        This area object
+    """
+    self.attrs(corner_size=corner_size, corner_size_x=corner_size_x, corner_size_y=corner_size_y)
+    self.state = AreaState.CORNERS
+    return self
+
+
+
+ +
+ +
+ + +

+def use_only_edge(...) + +

+ + +
+ +

Sets the area object to only use the edge of the selection

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
line_width + int + +
+

The width of the x & y edge line

+
+
+ None +
line_width_x + int + +
+

The width of the x edge line

+
+
+ None +
line_width_y + int + +
+

The width of the y edge line

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Area + +
+

This area object

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
def use_only_edge(self, line_width: int = None, line_width_x: int = None, line_width_y: int = None) -> Area:
+    """
+    Sets the area object to only use the edge of the selection
+
+    Args:
+        line_width: The width of the x & y edge line
+        line_width_x: The width of the x edge line
+        line_width_y: The width of the y edge line
+
+    Returns:
+        This area object
+    """
+    self.attrs(line_width=line_width, line_width_x=line_width_x, line_width_y=line_width_y)
+    self.state = AreaState.EDGE
+    return self
+
+
+
+ +
+ +
+ + +

+def use_pattern_grid(...) + +

+ + +
+ +

Sets the area object to use a grid pattern within the selection.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
block_size + int + +
+

The size of the gaps between lines

+
+
+ None +
gap_size + int + +
+

The width of the grid lines

+
+
+ None +
block_size_x + int + +
+

The size of the x gaps between lines

+
+
+ None +
block_size_y + int + +
+

The size of the y gaps between lines

+
+
+ None +
gap_size_x + int + +
+

The width of the x grid lines

+
+
+ None +
gap_size_y + int + +
+

The width of the y grid lines

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Area + +
+

This area object

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
def use_pattern_grid(
+        self,
+        block_size: int = None,
+        gap_size: int = None,
+        block_size_x: int = None,
+        block_size_y: int = None,
+        gap_size_x: int = None,
+        gap_size_y: int = None
+) -> Area:
+    """
+    Sets the area object to use a grid pattern within the selection.
+
+    Args:
+        block_size: The size of the gaps between lines
+        gap_size: The width of the grid lines
+        block_size_x: The size of the x gaps between lines
+        block_size_y: The size of the y gaps between lines
+        gap_size_x: The width of the x grid lines
+        gap_size_y: The width of the y grid lines
+
+    Returns:
+        This area object
+    """
+    self.attrs(block_size=block_size, gap_size=gap_size,
+               block_size_x=block_size_x, gap_size_x=gap_size_x,
+               block_size_y=block_size_y, gap_size_y=gap_size_y)
+    self.state = AreaState.GRID
+    return self
+
+
+
+ +
+ +
+ + +

+def use_pattern_lines(...) + +

+ + +
+ +

Sets the area object to use a lines pattern within the selection.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
axis + str + +
+

The axis the lines should follow. Can either be "x" or "y"

+
+
+ None +
gap_size + int + +
+

The size of the gaps between lines

+
+
+ None +
line_width + int + +
+

The width of the x & y lines

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Area + +
+

This area object

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
def use_pattern_lines(self, axis: str = None, gap_size: int = None, line_width: int = None) -> Area:
+    """
+    Sets the area object to use a lines pattern within the selection.
+
+    Args:
+        axis: The axis the lines should follow. Can either be "x" or "y"
+        gap_size: The size of the gaps between lines
+        line_width: The width of the x & y lines
+
+    Returns:
+        This area object
+    """
+    if axis is not None:
+        axis = axis.lower()
+    self.attrs(axis=axis, gap_size=gap_size, line_width=line_width)
+    self.state = AreaState.LINES
+    return self
+
+
+
+ +
+ +
+ + +

+def width(...) + +

+ + +
+ +

Sets the width (x-axis) of the selection. Shrinks/Expands both sides equally. +If the expansion hits the edge of the map, it'll expand on the other side.

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
n + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
563
+564
+565
+566
+567
+568
+569
+570
+571
+572
def width(self, n: int) -> Area:
+    """
+    Sets the width (x-axis) of the selection. Shrinks/Expands both sides equally.
+    If the expansion hits the edge of the map, it'll expand on the other side.
+    """
+    c1, c2 = self._get_length_change(n, self.get_width(), self.x1, self.x2)
+
+    self.x1 = self._x1 + c1
+    self.x2 = self._x2 + c2
+    return self
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/support/area_attr/index.html b/api_docs/support/area_attr/index.html new file mode 100644 index 00000000..2aadb4e9 --- /dev/null +++ b/api_docs/support/area_attr/index.html @@ -0,0 +1,3078 @@ + + + + + + + + + + + + + + + + + + + + + + + + + AreaAttr - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

AreaAttr

+ +
+ + + + +
+

+ Bases: Enum

+ + +

Enum to show the supported attributes that can be edited using Area.attr(k, v)

+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
class AreaAttr(Enum):
+    """Enum to show the supported attributes that can be edited using ``Area.attr(k, v)``"""
+    X1 = "x1"
+    Y1 = "y1"
+    X2 = "x2"
+    Y2 = "y2"
+    GAP_SIZE = "gap_size"
+    GAP_SIZE_X = "gap_size_x"
+    GAP_SIZE_Y = "gap_size_y"
+    LINE_WIDTH = "line_width"
+    LINE_WIDTH_X = "line_width_x"
+    LINE_WIDTH_Y = "line_width_y"
+    AXIS = "axis"
+    CORNER_SIZE = "corner_size"
+    CORNER_SIZE_X = "corner_size_x"
+    CORNER_SIZE_Y = "corner_size_y"
+    BLOCK_SIZE = "block_size"
+    BLOCK_SIZE_X = "block_size_x"
+    BLOCK_SIZE_Y = "block_size_y"
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +

+AXIS = 'axis' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'axis'
+ + + + + +
+
+ +
+ +
+ + + + +

+BLOCK_SIZE = 'block_size' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'block_size'
+ + + + + +
+
+ +
+ +
+ + + + +

+BLOCK_SIZE_X = 'block_size_x' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'block_size_x'
+ + + + + +
+
+ +
+ +
+ + + + +

+BLOCK_SIZE_Y = 'block_size_y' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'block_size_y'
+ + + + + +
+
+ +
+ +
+ + + + +

+CORNER_SIZE = 'corner_size' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'corner_size'
+ + + + + +
+
+ +
+ +
+ + + + +

+CORNER_SIZE_X = 'corner_size_x' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'corner_size_x'
+ + + + + +
+
+ +
+ +
+ + + + +

+CORNER_SIZE_Y = 'corner_size_y' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'corner_size_y'
+ + + + + +
+
+ +
+ +
+ + + + +

+GAP_SIZE = 'gap_size' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'gap_size'
+ + + + + +
+
+ +
+ +
+ + + + +

+GAP_SIZE_X = 'gap_size_x' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'gap_size_x'
+ + + + + +
+
+ +
+ +
+ + + + +

+GAP_SIZE_Y = 'gap_size_y' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'gap_size_y'
+ + + + + +
+
+ +
+ +
+ + + + +

+LINE_WIDTH = 'line_width' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'line_width'
+ + + + + +
+
+ +
+ +
+ + + + +

+LINE_WIDTH_X = 'line_width_x' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'line_width_x'
+ + + + + +
+
+ +
+ +
+ + + + +

+LINE_WIDTH_Y = 'line_width_y' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'line_width_y'
+ + + + + +
+
+ +
+ +
+ + + + +

+X1 = 'x1' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'x1'
+ + + + + +
+
+ +
+ +
+ + + + +

+X2 = 'x2' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'x2'
+ + + + + +
+
+ +
+ +
+ + + + +

+Y1 = 'y1' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'y1'
+ + + + + +
+
+ +
+ +
+ + + + +

+Y2 = 'y2' + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +'y2'
+ + + + + +
+
+ +
+ + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/support/area_state/index.html b/api_docs/support/area_state/index.html new file mode 100644 index 00000000..37e674d1 --- /dev/null +++ b/api_docs/support/area_state/index.html @@ -0,0 +1,2569 @@ + + + + + + + + + + + + + + + + + + + + + + + AreaState - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

AreaState

+ +
+ + + + +
+

+ Bases: Enum

+ + +

Enum to show the state of the Area object

+ +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
class AreaState(Enum):
+    """Enum to show the state of the Area object"""
+    FULL = 0
+    EDGE = 1
+    GRID = 2
+    LINES = 3
+    CORNERS = 4
+
+    @staticmethod
+    def unchunkables() -> List[AreaState]:
+        """Returns the states that cannot be split into chunks"""
+        return [AreaState.FULL, AreaState.EDGE]
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +

+CORNERS = 4 + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +4
+ + + + + +
+
+ +
+ +
+ + + + +

+EDGE = 1 + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +1
+ + + + + +
+
+ +
+ +
+ + + + +

+FULL = 0 + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +0
+ + + + + +
+
+ +
+ +
+ + + + +

+GRID = 2 + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +2
+ + + + + +
+
+ +
+ +
+ + + + +

+LINES = 3 + + + class-attribute + instance-attribute + + +

+ +
+ + + Value: +3
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +

+def unchunkables(...) + + + staticmethod + + +

+ + +
+ +

Returns the states that cannot be split into chunks

+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/area.py +
30
+31
+32
+33
@staticmethod
+def unchunkables() -> List[AreaState]:
+    """Returns the states that cannot be split into chunks"""
+    return [AreaState.FULL, AreaState.EDGE]
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/conditions/condition/index.html b/api_docs/trigger/conditions/condition/index.html new file mode 100644 index 00000000..b7fcd3c7 --- /dev/null +++ b/api_docs/trigger/conditions/condition/index.html @@ -0,0 +1,4635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Condition - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Condition

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ Condition + + +

+ + +
+

+ Bases: AoE2Object, TriggerComponent

+ + +

Object for handling a condition.

+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/condition.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
class Condition(AoE2Object, TriggerComponent):
+    """Object for handling a condition."""
+    hidden_attribute = 'condition_type'
+
+    _link_list = [
+        RetrieverObjectLinkGroup("Triggers", "trigger_data[__index__].condition_data[__index__]", group=[
+            RetrieverObjectLink("condition_type"),
+            RetrieverObjectLink("quantity"),
+            RetrieverObjectLink("attribute"),
+            RetrieverObjectLink("unit_object"),
+            RetrieverObjectLink("next_object"),
+            RetrieverObjectLink("object_list"),
+            RetrieverObjectLink("source_player"),
+            RetrieverObjectLink("technology"),
+            RetrieverObjectLink("timer"),
+            RetrieverObjectLink("area_x1"),
+            RetrieverObjectLink("area_y1"),
+            RetrieverObjectLink("area_x2"),
+            RetrieverObjectLink("area_y2"),
+            RetrieverObjectLink("object_group"),
+            RetrieverObjectLink("object_type"),
+            RetrieverObjectLink("ai_signal"),
+            RetrieverObjectLink("inverted"),
+            RetrieverObjectLink("variable"),
+            RetrieverObjectLink("comparison"),
+            RetrieverObjectLink("target_player"),
+            RetrieverObjectLink("unit_ai_action", support=Support(since=1.40)),
+            RetrieverObjectLink("object_state", support=Support(since=1.42)),
+            RetrieverObjectLink("timer_id", support=Support(since=1.46)),
+            RetrieverObjectLink("victory_timer_type", support=Support(since=1.46)),
+            RetrieverObjectLink("include_changeable_weapon_objects", support=Support(since=1.46)),
+            RetrieverObjectLink("xs_function", support=Support(since=1.40)),
+        ])
+    ]
+
+    def __init__(
+            self,
+            condition_type: int = None,
+            quantity: int = None,
+            attribute: int = None,
+            unit_object: int = None,
+            next_object: int = None,
+            object_list: int = None,
+            source_player: int | IntEnum = None,
+            technology: int | IntEnum = None,
+            timer: int = None,
+            area_x1: int = None,
+            area_y1: int = None,
+            area_x2: int = None,
+            area_y2: int = None,
+            object_group: int = None,
+            object_type: int = None,
+            ai_signal: int = None,
+            inverted: int = None,
+            variable: int = None,
+            comparison: int = None,
+            target_player: int | IntEnum = None,
+            unit_ai_action: int = None,
+            object_state: int = None,
+            timer_id: int = None,
+            victory_timer_type: int = None,
+            include_changeable_weapon_objects: int = None,
+            xs_function: str = None,
+            **kwargs
+    ):
+        raise_if_not_int_subclass([object_list, technology])
+        area_x1, area_y1, area_x2, area_y2 = validate_coords(area_x1, area_y1, area_x2, area_y2)
+
+        self.condition_type: int = condition_type
+        self.quantity: int = quantity
+        self.attribute: int = attribute
+        self.unit_object: int = unit_object
+        self.next_object: int = next_object
+        self.object_list: int = object_list
+        self.source_player: int = source_player
+        self.technology: int = technology
+        self.timer: int = timer
+        self.area_x1: int = area_x1
+        self.area_y1: int = area_y1
+        self.area_x2: int = area_x2
+        self.area_y2: int = area_y2
+        self.object_group: int = object_group
+        self.object_type: int = object_type
+        self.ai_signal: int = ai_signal
+        self.inverted: int = inverted
+        self.variable: int = variable
+        self.comparison: int = comparison
+        self.target_player: int = target_player
+        self.unit_ai_action: int = unit_ai_action
+        self.object_state: int = object_state
+        self.timer_id: int = timer_id
+        self.victory_timer_type: int = victory_timer_type
+        self.include_changeable_weapon_objects: int = include_changeable_weapon_objects
+        self.xs_function: str = xs_function
+
+        super().__init__(**kwargs)
+
+    def _should_be_displayed(self, attr: str, val: Any) -> bool:
+        # Include the only exception to the -1 == invalid rule
+        if self.condition_type == ConditionId.DIFFICULTY_LEVEL and attr == 'quantity' and val == -1:
+            return True
+
+        return super()._should_be_displayed(attr, val)
+
+    def get_content_as_string(self, include_condition_definition: bool = False) -> str:
+        """
+        Create a human-readable string showcasing all content of this condition.
+
+        This is also the function that is called when doing: `print(condition)`
+
+        Args:
+            include_condition_definition: If the condition meta-data should be added by this function
+
+        Returns:
+            The created string
+        """
+        if self.condition_type not in conditions.attributes:
+            attributes_list = conditions.empty_attributes
+        else:
+            attributes_list = conditions.attributes[self.condition_type]
+
+        return_string = ""
+        for attribute in attributes_list:
+            val = getattr(self, attribute)
+            if not self._should_be_displayed(attribute, val):
+                continue
+
+            value_string = transform_condition_attr_value(self.condition_type, attribute, val, self._uuid)
+            return_string += f"{attribute}: {value_string}\n"
+
+        if return_string == "":
+            return "<< No Attributes >>\n"
+
+        if include_condition_definition:
+            return f"{conditions.condition_names[self.condition_type]}:\n{add_tabs(return_string, 1)}"
+
+        return return_string
+
+    def __str__(self):
+        return f"[Condition] {self.get_content_as_string(include_condition_definition=True)}"
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+ai_signal: int = ai_signal + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +ai_signal
+ + + + + +
+
+ +
+ +
+ + + + +
+area_x1: int = area_x1 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_x1
+ + + + + +
+
+ +
+ +
+ + + + +
+area_x2: int = area_x2 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_x2
+ + + + + +
+
+ +
+ +
+ + + + +
+area_y1: int = area_y1 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_y1
+ + + + + +
+
+ +
+ +
+ + + + +
+area_y2: int = area_y2 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_y2
+ + + + + +
+
+ +
+ +
+ + + + +
+attribute: int = attribute + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +attribute
+ + + + + +
+
+ +
+ +
+ + + + +
+comparison: int = comparison + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +comparison
+ + + + + +
+
+ +
+ +
+ + + + +
+condition_type: int = condition_type + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +condition_type
+ + + + + +
+
+ +
+ +
+ + + + +
+hidden_attribute = 'condition_type' + + + class-attribute + instance-attribute + + +
+ +
+ + + Value: +'condition_type'
+ + + + + +
+
+ +
+ +
+ + + + +
+include_changeable_weapon_objects: int = include_changeable_weapon_objects + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +include_changeable_weapon_objects
+ + + + + +
+
+ +
+ +
+ + + + +
+inverted: int = inverted + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +inverted
+ + + + + +
+
+ +
+ +
+ + + + +
+next_object: int = next_object + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +next_object
+ + + + + +
+
+ +
+ +
+ + + + +
+object_group: int = object_group + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_group
+ + + + + +
+
+ +
+ +
+ + + + +
+object_list: int = object_list + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_list
+ + + + + +
+
+ +
+ +
+ + + + +
+object_state: int = object_state + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_state
+ + + + + +
+
+ +
+ +
+ + + + +
+object_type: int = object_type + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_type
+ + + + + +
+
+ +
+ +
+ + + + +
+quantity: int = quantity + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +quantity
+ + + + + +
+
+ +
+ +
+ + + + +
+source_player: int = source_player + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +source_player
+ + + + + +
+
+ +
+ +
+ + + + +
+target_player: int = target_player + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +target_player
+ + + + + +
+
+ +
+ +
+ + + + +
+technology: int = technology + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +technology
+ + + + + +
+
+ +
+ +
+ + + + +
+timer: int = timer + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +timer
+ + + + + +
+
+ +
+ +
+ + + + +
+timer_id: int = timer_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +timer_id
+ + + + + +
+
+ +
+ +
+ + + + +
+unit_ai_action: int = unit_ai_action + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +unit_ai_action
+ + + + + +
+
+ +
+ +
+ + + + +
+unit_object: int = unit_object + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +unit_object
+ + + + + +
+
+ +
+ +
+ + + + +
+variable: int = variable + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +variable
+ + + + + +
+
+ +
+ +
+ + + + +
+victory_timer_type: int = victory_timer_type + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +victory_timer_type
+ + + + + +
+
+ +
+ +
+ + + + +
+xs_function: str = xs_function + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +xs_function
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
condition_type + int + - + None +
quantity + int + - + None +
attribute + int + - + None +
unit_object + int + - + None +
next_object + int + - + None +
object_list + int + - + None +
source_player + int | IntEnum + - + None +
technology + int | IntEnum + - + None +
timer + int + - + None +
area_x1 + int + - + None +
area_y1 + int + - + None +
area_x2 + int + - + None +
area_y2 + int + - + None +
object_group + int + - + None +
object_type + int + - + None +
ai_signal + int + - + None +
inverted + int + - + None +
variable + int + - + None +
comparison + int + - + None +
target_player + int | IntEnum + - + None +
unit_ai_action + int + - + None +
object_state + int + - + None +
timer_id + int + - + None +
victory_timer_type + int + - + None +
include_changeable_weapon_objects + int + - + None +
xs_function + str + - + None +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/condition.py +
 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
def __init__(
+        self,
+        condition_type: int = None,
+        quantity: int = None,
+        attribute: int = None,
+        unit_object: int = None,
+        next_object: int = None,
+        object_list: int = None,
+        source_player: int | IntEnum = None,
+        technology: int | IntEnum = None,
+        timer: int = None,
+        area_x1: int = None,
+        area_y1: int = None,
+        area_x2: int = None,
+        area_y2: int = None,
+        object_group: int = None,
+        object_type: int = None,
+        ai_signal: int = None,
+        inverted: int = None,
+        variable: int = None,
+        comparison: int = None,
+        target_player: int | IntEnum = None,
+        unit_ai_action: int = None,
+        object_state: int = None,
+        timer_id: int = None,
+        victory_timer_type: int = None,
+        include_changeable_weapon_objects: int = None,
+        xs_function: str = None,
+        **kwargs
+):
+    raise_if_not_int_subclass([object_list, technology])
+    area_x1, area_y1, area_x2, area_y2 = validate_coords(area_x1, area_y1, area_x2, area_y2)
+
+    self.condition_type: int = condition_type
+    self.quantity: int = quantity
+    self.attribute: int = attribute
+    self.unit_object: int = unit_object
+    self.next_object: int = next_object
+    self.object_list: int = object_list
+    self.source_player: int = source_player
+    self.technology: int = technology
+    self.timer: int = timer
+    self.area_x1: int = area_x1
+    self.area_y1: int = area_y1
+    self.area_x2: int = area_x2
+    self.area_y2: int = area_y2
+    self.object_group: int = object_group
+    self.object_type: int = object_type
+    self.ai_signal: int = ai_signal
+    self.inverted: int = inverted
+    self.variable: int = variable
+    self.comparison: int = comparison
+    self.target_player: int = target_player
+    self.unit_ai_action: int = unit_ai_action
+    self.object_state: int = object_state
+    self.timer_id: int = timer_id
+    self.victory_timer_type: int = victory_timer_type
+    self.include_changeable_weapon_objects: int = include_changeable_weapon_objects
+    self.xs_function: str = xs_function
+
+    super().__init__(**kwargs)
+
+
+
+ +
+ +
+ + +
+def __str__(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/condition.py +
156
+157
def __str__(self):
+    return f"[Condition] {self.get_content_as_string(include_condition_definition=True)}"
+
+
+
+ +
+ +
+ + +
+def get_content_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing all content of this condition.

+

This is also the function that is called when doing: print(condition)

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
include_condition_definition + bool + +
+

If the condition meta-data should be added by this function

+
+
+ False +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/condition.py +
122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
def get_content_as_string(self, include_condition_definition: bool = False) -> str:
+    """
+    Create a human-readable string showcasing all content of this condition.
+
+    This is also the function that is called when doing: `print(condition)`
+
+    Args:
+        include_condition_definition: If the condition meta-data should be added by this function
+
+    Returns:
+        The created string
+    """
+    if self.condition_type not in conditions.attributes:
+        attributes_list = conditions.empty_attributes
+    else:
+        attributes_list = conditions.attributes[self.condition_type]
+
+    return_string = ""
+    for attribute in attributes_list:
+        val = getattr(self, attribute)
+        if not self._should_be_displayed(attribute, val):
+            continue
+
+        value_string = transform_condition_attr_value(self.condition_type, attribute, val, self._uuid)
+        return_string += f"{attribute}: {value_string}\n"
+
+    if return_string == "":
+        return "<< No Attributes >>\n"
+
+    if include_condition_definition:
+        return f"{conditions.condition_names[self.condition_type]}:\n{add_tabs(return_string, 1)}"
+
+    return return_string
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/conditions/new_condition/index.html b/api_docs/trigger/conditions/new_condition/index.html new file mode 100644 index 00000000..bd73b09d --- /dev/null +++ b/api_docs/trigger/conditions/new_condition/index.html @@ -0,0 +1,7179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + NewCondition - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+
+ + + + + + + +

NewCondition

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ NewConditionSupport + + +

+ + +
+ + +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
  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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
class NewConditionSupport:
+    def __init__(self, trigger_ref):
+        self._trigger_ref = trigger_ref
+
+    def none(
+            self,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.NONE,
+        )
+
+    def bring_object_to_area(
+            self,
+            unit_object: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.BRING_OBJECT_TO_AREA,
+            unit_object=unit_object,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            inverted=inverted,
+        )
+
+    def bring_object_to_object(
+            self,
+            unit_object: int | None = None,
+            next_object: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.BRING_OBJECT_TO_OBJECT,
+            unit_object=unit_object,
+            next_object=next_object,
+            inverted=inverted,
+        )
+
+    def own_objects(
+            self,
+            quantity: int | None = None,
+            object_list: int | None = None,
+            source_player: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            include_changeable_weapon_objects: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OWN_OBJECTS,
+            quantity=quantity,
+            object_list=object_list,
+            source_player=source_player,
+            object_group=object_group,
+            object_type=object_type,
+            include_changeable_weapon_objects=include_changeable_weapon_objects,
+        )
+
+    def own_fewer_objects(
+            self,
+            quantity: int | None = None,
+            object_list: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            include_changeable_weapon_objects: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OWN_FEWER_OBJECTS,
+            quantity=quantity,
+            object_list=object_list,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            include_changeable_weapon_objects=include_changeable_weapon_objects,
+        )
+
+    def objects_in_area(
+            self,
+            quantity: int | None = None,
+            object_list: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            inverted: int | None = None,
+            object_state: int | None = None,
+            include_changeable_weapon_objects: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECTS_IN_AREA,
+            quantity=quantity,
+            object_list=object_list,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            inverted=inverted,
+            object_state=object_state,
+            include_changeable_weapon_objects=include_changeable_weapon_objects,
+        )
+
+    def destroy_object(
+            self,
+            unit_object: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.DESTROY_OBJECT,
+            unit_object=unit_object,
+            inverted=inverted,
+        )
+
+    def capture_object(
+            self,
+            unit_object: int | None = None,
+            source_player: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.CAPTURE_OBJECT,
+            unit_object=unit_object,
+            source_player=source_player,
+            inverted=inverted,
+        )
+
+    def accumulate_attribute(
+            self,
+            quantity: int | None = None,
+            attribute: int | None = None,
+            source_player: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.ACCUMULATE_ATTRIBUTE,
+            quantity=quantity,
+            attribute=attribute,
+            source_player=source_player,
+            inverted=inverted,
+        )
+
+    def research_technology(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.RESEARCH_TECHNOLOGY,
+            source_player=source_player,
+            technology=technology,
+            inverted=inverted,
+        )
+
+    def timer(
+            self,
+            timer: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.TIMER,
+            timer=timer,
+            inverted=inverted,
+        )
+
+    def object_selected(
+            self,
+            unit_object: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_SELECTED,
+            unit_object=unit_object,
+            inverted=inverted,
+        )
+
+    def ai_signal(
+            self,
+            ai_signal: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.AI_SIGNAL,
+            ai_signal=ai_signal,
+            inverted=inverted,
+        )
+
+    def player_defeated(
+            self,
+            source_player: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.PLAYER_DEFEATED,
+            source_player=source_player,
+            inverted=inverted,
+        )
+
+    def object_has_target(
+            self,
+            unit_object: int | None = None,
+            next_object: int | None = None,
+            object_list: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_HAS_TARGET,
+            unit_object=unit_object,
+            next_object=next_object,
+            object_list=object_list,
+            object_group=object_group,
+            object_type=object_type,
+            inverted=inverted,
+        )
+
+    def object_visible(
+            self,
+            unit_object: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_VISIBLE,
+            unit_object=unit_object,
+        )
+
+    def object_not_visible(
+            self,
+            unit_object: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_NOT_VISIBLE,
+            unit_object=unit_object,
+        )
+
+    def researching_tech(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.RESEARCHING_TECH,
+            source_player=source_player,
+            technology=technology,
+            inverted=inverted,
+        )
+
+    def units_garrisoned(
+            self,
+            quantity: int | None = None,
+            unit_object: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.UNITS_GARRISONED,
+            quantity=quantity,
+            unit_object=unit_object,
+            inverted=inverted,
+        )
+
+    def difficulty_level(
+            self,
+            quantity: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.DIFFICULTY_LEVEL,
+            quantity=quantity,
+            inverted=inverted,
+        )
+
+    def chance(
+            self,
+            quantity: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.CHANCE,
+            quantity=quantity,
+        )
+
+    def technology_state(
+            self,
+            quantity: int | None = None,
+            source_player: int | None = None,
+            technology: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.TECHNOLOGY_STATE,
+            quantity=quantity,
+            source_player=source_player,
+            technology=technology,
+            inverted=inverted,
+        )
+
+    def variable_value(
+            self,
+            quantity: int | None = None,
+            inverted: int | None = None,
+            variable: int | None = None,
+            comparison: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.VARIABLE_VALUE,
+            quantity=quantity,
+            inverted=inverted,
+            variable=variable,
+            comparison=comparison,
+        )
+
+    def object_hp(
+            self,
+            quantity: int | None = None,
+            unit_object: int | None = None,
+            inverted: int | None = None,
+            comparison: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_HP,
+            quantity=quantity,
+            unit_object=unit_object,
+            inverted=inverted,
+            comparison=comparison,
+        )
+
+    def diplomacy_state(
+            self,
+            quantity: int | None = None,
+            source_player: int | None = None,
+            inverted: int | None = None,
+            target_player: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.DIPLOMACY_STATE,
+            quantity=quantity,
+            source_player=source_player,
+            inverted=inverted,
+            target_player=target_player,
+        )
+
+    def script_call(
+            self,
+            xs_function: str | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.SCRIPT_CALL,
+            xs_function=xs_function,
+        )
+
+    def object_visible_multiplayer(
+            self,
+            unit_object: int | None = None,
+            source_player: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_VISIBLE_MULTIPLAYER,
+            unit_object=unit_object,
+            source_player=source_player,
+            inverted=inverted,
+        )
+
+    def object_selected_multiplayer(
+            self,
+            unit_object: int | None = None,
+            source_player: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_SELECTED_MULTIPLAYER,
+            unit_object=unit_object,
+            source_player=source_player,
+            inverted=inverted,
+        )
+
+    def object_has_action(
+            self,
+            unit_object: int | None = None,
+            next_object: int | None = None,
+            object_list: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            inverted: int | None = None,
+            unit_ai_action: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OBJECT_HAS_ACTION,
+            unit_object=unit_object,
+            next_object=next_object,
+            object_list=object_list,
+            object_group=object_group,
+            object_type=object_type,
+            inverted=inverted,
+            unit_ai_action=unit_ai_action,
+        )
+
+    def or_(
+            self,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.OR,
+        )
+
+    def ai_signal_multiplayer(
+            self,
+            ai_signal: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.AI_SIGNAL_MULTIPLAYER,
+            ai_signal=ai_signal,
+            inverted=inverted,
+        )
+
+    def and_(
+            self,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.AND,
+        )
+
+    def building_is_trading(
+            self,
+            unit_object: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.BUILDING_IS_TRADING,
+            unit_object=unit_object,
+            inverted=inverted,
+        )
+
+    def display_timer_triggered(
+            self,
+            timer_id: int | None = None,
+            inverted: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.DISPLAY_TIMER_TRIGGERED,
+            timer_id=timer_id,
+            inverted=inverted,
+        )
+
+    def victory_timer(
+            self,
+            quantity: int | None = None,
+            source_player: int | None = None,
+            inverted: int | None = None,
+            comparison: int | None = None,
+            victory_timer_type: int | None = None,
+    ) -> Condition:
+        return self._trigger_ref._add_condition(
+            ConditionId.VICTORY_TIMER,
+            quantity=quantity,
+            source_player=source_player,
+            inverted=inverted,
+            comparison=comparison,
+            victory_timer_type=victory_timer_type,
+        )
+
+
+ + + +
+ + + + + + + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_ref + ? + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
8
+9
def __init__(self, trigger_ref):
+    self._trigger_ref = trigger_ref
+
+
+
+ +
+ +
+ + +
+def accumulate_attribute(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
attribute + int | None + - + None +
source_player + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
def accumulate_attribute(
+        self,
+        quantity: int | None = None,
+        attribute: int | None = None,
+        source_player: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.ACCUMULATE_ATTRIBUTE,
+        quantity=quantity,
+        attribute=attribute,
+        source_player=source_player,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def ai_signal(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
ai_signal + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
201
+202
+203
+204
+205
+206
+207
+208
+209
+210
def ai_signal(
+        self,
+        ai_signal: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.AI_SIGNAL,
+        ai_signal=ai_signal,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def ai_signal_multiplayer(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
ai_signal + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
429
+430
+431
+432
+433
+434
+435
+436
+437
+438
def ai_signal_multiplayer(
+        self,
+        ai_signal: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.AI_SIGNAL_MULTIPLAYER,
+        ai_signal=ai_signal,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def and_(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
440
+441
+442
+443
+444
+445
def and_(
+        self,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.AND,
+    )
+
+
+
+ +
+ +
+ + +
+def bring_object_to_area(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
def bring_object_to_area(
+        self,
+        unit_object: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.BRING_OBJECT_TO_AREA,
+        unit_object=unit_object,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def bring_object_to_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
next_object + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
def bring_object_to_object(
+        self,
+        unit_object: int | None = None,
+        next_object: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.BRING_OBJECT_TO_OBJECT,
+        unit_object=unit_object,
+        next_object=next_object,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def building_is_trading(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
447
+448
+449
+450
+451
+452
+453
+454
+455
+456
def building_is_trading(
+        self,
+        unit_object: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.BUILDING_IS_TRADING,
+        unit_object=unit_object,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def capture_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
source_player + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
def capture_object(
+        self,
+        unit_object: int | None = None,
+        source_player: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.CAPTURE_OBJECT,
+        unit_object=unit_object,
+        source_player=source_player,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def chance(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
297
+298
+299
+300
+301
+302
+303
+304
def chance(
+        self,
+        quantity: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.CHANCE,
+        quantity=quantity,
+    )
+
+
+
+ +
+ +
+ + +
+def destroy_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
127
+128
+129
+130
+131
+132
+133
+134
+135
+136
def destroy_object(
+        self,
+        unit_object: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.DESTROY_OBJECT,
+        unit_object=unit_object,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def difficulty_level(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
286
+287
+288
+289
+290
+291
+292
+293
+294
+295
def difficulty_level(
+        self,
+        quantity: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.DIFFICULTY_LEVEL,
+        quantity=quantity,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def diplomacy_state(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
source_player + int | None + - + None +
inverted + int | None + - + None +
target_player + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
def diplomacy_state(
+        self,
+        quantity: int | None = None,
+        source_player: int | None = None,
+        inverted: int | None = None,
+        target_player: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.DIPLOMACY_STATE,
+        quantity=quantity,
+        source_player=source_player,
+        inverted=inverted,
+        target_player=target_player,
+    )
+
+
+
+ +
+ +
+ + +
+def display_timer_triggered(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
timer_id + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
458
+459
+460
+461
+462
+463
+464
+465
+466
+467
def display_timer_triggered(
+        self,
+        timer_id: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.DISPLAY_TIMER_TRIGGERED,
+        timer_id=timer_id,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def none(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
11
+12
+13
+14
+15
+16
def none(
+        self,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.NONE,
+    )
+
+
+
+ +
+ +
+ + +
+def object_has_action(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
next_object + int | None + - + None +
object_list + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
inverted + int | None + - + None +
unit_ai_action + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
def object_has_action(
+        self,
+        unit_object: int | None = None,
+        next_object: int | None = None,
+        object_list: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        inverted: int | None = None,
+        unit_ai_action: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_HAS_ACTION,
+        unit_object=unit_object,
+        next_object=next_object,
+        object_list=object_list,
+        object_group=object_group,
+        object_type=object_type,
+        inverted=inverted,
+        unit_ai_action=unit_ai_action,
+    )
+
+
+
+ +
+ +
+ + +
+def object_has_target(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
next_object + int | None + - + None +
object_list + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
def object_has_target(
+        self,
+        unit_object: int | None = None,
+        next_object: int | None = None,
+        object_list: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_HAS_TARGET,
+        unit_object=unit_object,
+        next_object=next_object,
+        object_list=object_list,
+        object_group=object_group,
+        object_type=object_type,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def object_hp(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
unit_object + int | None + - + None +
inverted + int | None + - + None +
comparison + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
def object_hp(
+        self,
+        quantity: int | None = None,
+        unit_object: int | None = None,
+        inverted: int | None = None,
+        comparison: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_HP,
+        quantity=quantity,
+        unit_object=unit_object,
+        inverted=inverted,
+        comparison=comparison,
+    )
+
+
+
+ +
+ +
+ + +
+def object_not_visible(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
251
+252
+253
+254
+255
+256
+257
+258
def object_not_visible(
+        self,
+        unit_object: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_NOT_VISIBLE,
+        unit_object=unit_object,
+    )
+
+
+
+ +
+ +
+ + +
+def object_selected(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
190
+191
+192
+193
+194
+195
+196
+197
+198
+199
def object_selected(
+        self,
+        unit_object: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_SELECTED,
+        unit_object=unit_object,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def object_selected_multiplayer(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
source_player + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
def object_selected_multiplayer(
+        self,
+        unit_object: int | None = None,
+        source_player: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_SELECTED_MULTIPLAYER,
+        unit_object=unit_object,
+        source_player=source_player,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def object_visible(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
242
+243
+244
+245
+246
+247
+248
+249
def object_visible(
+        self,
+        unit_object: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_VISIBLE,
+        unit_object=unit_object,
+    )
+
+
+
+ +
+ +
+ + +
+def object_visible_multiplayer(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_object + int | None + - + None +
source_player + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
def object_visible_multiplayer(
+        self,
+        unit_object: int | None = None,
+        source_player: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECT_VISIBLE_MULTIPLAYER,
+        unit_object=unit_object,
+        source_player=source_player,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def objects_in_area(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
inverted + int | None + - + None +
object_state + int | None + - + None +
include_changeable_weapon_objects + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
 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
+124
+125
def objects_in_area(
+        self,
+        quantity: int | None = None,
+        object_list: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        inverted: int | None = None,
+        object_state: int | None = None,
+        include_changeable_weapon_objects: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OBJECTS_IN_AREA,
+        quantity=quantity,
+        object_list=object_list,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        inverted=inverted,
+        object_state=object_state,
+        include_changeable_weapon_objects=include_changeable_weapon_objects,
+    )
+
+
+
+ +
+ +
+ + +
+def or_(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
422
+423
+424
+425
+426
+427
def or_(
+        self,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OR,
+    )
+
+
+
+ +
+ +
+ + +
+def own_fewer_objects(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
include_changeable_weapon_objects + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
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
def own_fewer_objects(
+        self,
+        quantity: int | None = None,
+        object_list: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        include_changeable_weapon_objects: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OWN_FEWER_OBJECTS,
+        quantity=quantity,
+        object_list=object_list,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        include_changeable_weapon_objects=include_changeable_weapon_objects,
+    )
+
+
+
+ +
+ +
+ + +
+def own_objects(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list + int | None + - + None +
source_player + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
include_changeable_weapon_objects + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
def own_objects(
+        self,
+        quantity: int | None = None,
+        object_list: int | None = None,
+        source_player: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        include_changeable_weapon_objects: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.OWN_OBJECTS,
+        quantity=quantity,
+        object_list=object_list,
+        source_player=source_player,
+        object_group=object_group,
+        object_type=object_type,
+        include_changeable_weapon_objects=include_changeable_weapon_objects,
+    )
+
+
+
+ +
+ +
+ + +
+def player_defeated(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
212
+213
+214
+215
+216
+217
+218
+219
+220
+221
def player_defeated(
+        self,
+        source_player: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.PLAYER_DEFEATED,
+        source_player=source_player,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def research_technology(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
def research_technology(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.RESEARCH_TECHNOLOGY,
+        source_player=source_player,
+        technology=technology,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def researching_tech(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
def researching_tech(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.RESEARCHING_TECH,
+        source_player=source_player,
+        technology=technology,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def script_call(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
xs_function + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
366
+367
+368
+369
+370
+371
+372
+373
def script_call(
+        self,
+        xs_function: str | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.SCRIPT_CALL,
+        xs_function=xs_function,
+    )
+
+
+
+ +
+ +
+ + +
+def technology_state(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
source_player + int | None + - + None +
technology + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
def technology_state(
+        self,
+        quantity: int | None = None,
+        source_player: int | None = None,
+        technology: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.TECHNOLOGY_STATE,
+        quantity=quantity,
+        source_player=source_player,
+        technology=technology,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def timer(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
timer + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
179
+180
+181
+182
+183
+184
+185
+186
+187
+188
def timer(
+        self,
+        timer: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.TIMER,
+        timer=timer,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def units_garrisoned(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
unit_object + int | None + - + None +
inverted + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
def units_garrisoned(
+        self,
+        quantity: int | None = None,
+        unit_object: int | None = None,
+        inverted: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.UNITS_GARRISONED,
+        quantity=quantity,
+        unit_object=unit_object,
+        inverted=inverted,
+    )
+
+
+
+ +
+ +
+ + +
+def variable_value(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
inverted + int | None + - + None +
variable + int | None + - + None +
comparison + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
def variable_value(
+        self,
+        quantity: int | None = None,
+        inverted: int | None = None,
+        variable: int | None = None,
+        comparison: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.VARIABLE_VALUE,
+        quantity=quantity,
+        inverted=inverted,
+        variable=variable,
+        comparison=comparison,
+    )
+
+
+
+ +
+ +
+ + +
+def victory_timer(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
source_player + int | None + - + None +
inverted + int | None + - + None +
comparison + int | None + - + None +
victory_timer_type + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_condition.py +
469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
def victory_timer(
+        self,
+        quantity: int | None = None,
+        source_player: int | None = None,
+        inverted: int | None = None,
+        comparison: int | None = None,
+        victory_timer_type: int | None = None,
+) -> Condition:
+    return self._trigger_ref._add_condition(
+        ConditionId.VICTORY_TIMER,
+        quantity=quantity,
+        source_player=source_player,
+        inverted=inverted,
+        comparison=comparison,
+        victory_timer_type=victory_timer_type,
+    )
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/effects/effect/index.html b/api_docs/trigger/effects/effect/index.html new file mode 100644 index 00000000..c5c5f923 --- /dev/null +++ b/api_docs/trigger/effects/effect/index.html @@ -0,0 +1,7627 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Effect - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Effect

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ Effect + + +

+ + +
+

+ Bases: AoE2Object, TriggerComponent

+ + +

Object for handling an effect.

+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/effect.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
class Effect(AoE2Object, TriggerComponent):
+    """Object for handling an effect."""
+    hidden_attribute = 'effect_type'
+
+    _link_list = [
+        RetrieverObjectLinkGroup("Triggers", "trigger_data[__index__].effect_data[__index__]", group=[
+            RetrieverObjectLink("effect_type"),
+            RetrieverObjectLink("ai_script_goal"),
+            RetrieverObjectLink("quantity"),
+            RetrieverObjectLink("tribute_list"),
+            RetrieverObjectLink("diplomacy"),
+            RetrieverObjectLink("legacy_location_object_reference"),
+            RetrieverObjectLink("object_list_unit_id"),
+            RetrieverObjectLink("source_player"),
+            RetrieverObjectLink("target_player"),
+            RetrieverObjectLink("technology"),
+            RetrieverObjectLink("string_id"),
+            RetrieverObjectLink("display_time"),
+            RetrieverObjectLink("trigger_id"),
+            RetrieverObjectLink("location_x"),
+            RetrieverObjectLink("location_y"),
+            RetrieverObjectLink("location_object_reference"),
+            RetrieverObjectLink("area_x1"),
+            RetrieverObjectLink("area_y1"),
+            RetrieverObjectLink("area_x2"),
+            RetrieverObjectLink("area_y2"),
+            RetrieverObjectLink("object_group"),
+            RetrieverObjectLink("object_type"),
+            RetrieverObjectLink("instruction_panel_position"),
+            RetrieverObjectLink("attack_stance"),
+            RetrieverObjectLink("time_unit"),
+            RetrieverObjectLink("enabled"),
+            RetrieverObjectLink("food"),
+            RetrieverObjectLink("wood"),
+            RetrieverObjectLink("stone"),
+            RetrieverObjectLink("gold"),
+            RetrieverObjectLink("item_id"),
+            RetrieverObjectLink("flash_object"),
+            RetrieverObjectLink("force_research_technology"),
+            RetrieverObjectLink("visibility_state"),
+            RetrieverObjectLink("scroll"),
+            RetrieverObjectLink("operation"),
+            RetrieverObjectLink("object_list_unit_id_2"),
+            RetrieverObjectLink("button_location"),
+            RetrieverObjectLink("ai_signal_value"),
+            RetrieverObjectLink("object_attributes"),
+            RetrieverObjectLink("_variable_ref", link="variable"),
+            RetrieverObjectLink("timer"),
+            RetrieverObjectLink("facet"),
+            RetrieverObjectLink("play_sound"),
+            RetrieverObjectLink("player_color", support=Support(since=1.40)),
+            RetrieverObjectLink("color_mood", support=Support(since=1.42)),
+            RetrieverObjectLink("reset_timer", support=Support(since=1.44)),
+            RetrieverObjectLink("object_state", support=Support(since=1.44)),
+            RetrieverObjectLink("action_type", support=Support(since=1.44)),
+            RetrieverObjectLink("resource_1", support=Support(since=1.54)),
+            RetrieverObjectLink("resource_1_quantity", support=Support(since=1.54)),
+            RetrieverObjectLink("resource_2", support=Support(since=1.54)),
+            RetrieverObjectLink("resource_2_quantity", support=Support(since=1.54)),
+            RetrieverObjectLink("resource_3", support=Support(since=1.54)),
+            RetrieverObjectLink("resource_3_quantity", support=Support(since=1.54)),
+            RetrieverObjectLink("message", commit_callback=_add_trail_if_string_attr_is_used_in_effect),
+            RetrieverObjectLink("sound_name", commit_callback=_add_trail_if_string_attr_is_used_in_effect),
+            RetrieverObjectLink("selected_object_ids"),
+            RetrieverObjectLink("unused_string_1", support=Support(since=1.54)),
+            RetrieverObjectLink("unused_string_2", support=Support(since=1.54)),
+        ])
+    ]
+
+    def __init__(
+            self,
+            effect_type: int = None,
+            ai_script_goal: int = None,
+            armour_attack_quantity: int = None,
+            armour_attack_class: int = None,
+            quantity: int = None,
+            tribute_list: int = None,
+            diplomacy: int = None,
+            legacy_location_object_reference: int = None,
+            object_list_unit_id: int = None,
+            source_player: int = None,
+            target_player: int = None,
+            technology: int = None,
+            string_id: int = None,
+            display_time: int = None,
+            trigger_id: int = None,
+            location_x: int = None,
+            location_y: int = None,
+            location_object_reference: int = None,
+            area_x1: int = None,
+            area_y1: int = None,
+            area_x2: int = None,
+            area_y2: int = None,
+            object_group: int = None,
+            object_type: int = None,
+            instruction_panel_position: int = None,
+            attack_stance: int = None,
+            time_unit: int = None,
+            enabled: int = None,
+            food: int = None,
+            wood: int = None,
+            stone: int = None,
+            gold: int = None,
+            item_id: int = None,  # Unused (?)
+            flash_object: int = None,
+            force_research_technology: int = None,
+            visibility_state: int = None,
+            scroll: int = None,
+            operation: int = None,
+            object_list_unit_id_2: int = None,
+            button_location: int = None,
+            ai_signal_value: int = None,
+            object_attributes: int = None,
+            variable: int = None,
+            timer: int = None,
+            facet: int = None,
+            play_sound: int = None,
+            player_color: int = None,
+            color_mood: int = None,
+            reset_timer: int = None,
+            object_state: int = None,
+            action_type: int = None,
+            resource_1: int = None,
+            resource_1_quantity: int = None,
+            resource_2: int = None,
+            resource_2_quantity: int = None,
+            resource_3: int = None,
+            resource_3_quantity: int = None,
+            message: str = None,
+            sound_name: str = None,
+            selected_object_ids: List[int] = None,
+            unused_string_1: str = None,
+            unused_string_2: str = None,
+            # Used for variable retrieval in armour/attack effects (source=variable only)
+            _variable_ref: int = None,
+            **kwargs
+    ):
+        super().__init__(**kwargs)
+
+        raise_if_not_int_subclass([object_list_unit_id, technology, object_list_unit_id_2])
+
+        if selected_object_ids is None:
+            selected_object_ids = []
+
+        # Set armour/attack flags
+        self._armour_attack_source = _get_armour_attack_source(effect_type, object_attributes)
+
+        if self._armour_attack_source == 'variable':
+            # If effect created through reading scenario file
+            if _variable_ref is not None and variable is None and armour_attack_class is None:
+                armour_attack_class, variable = self._split_aa_value(_variable_ref)
+            # If created through new_effect with variable and armour_attack_class values
+            else:
+                armour_attack_class = armour_attack_class or 0
+        elif self._armour_attack_source == 'quantity':
+            # If effect created through reading scenario file
+            if quantity is not None and armour_attack_class is None and armour_attack_quantity is None:
+                armour_attack_class, armour_attack_quantity = self._split_aa_value(quantity)
+                quantity = None
+            # If effect created through new_effect with aa values defined
+            elif value_is_valid(armour_attack_class) or value_is_valid(armour_attack_quantity):
+                quantity = None
+            # If created through new_effect with quantity defined instead of the aa values. Handled by quantity property
+            else:
+                pass
+        else:
+            armour_attack_class = armour_attack_quantity = None
+
+        area_x1, area_y1, area_x2, area_y2 = validate_coords(area_x1, area_y1, area_x2, area_y2)
+
+        if value_is_valid(legacy_location_object_reference):
+            location_object_reference = legacy_location_object_reference
+
+        # Bypass the @property which causes: self._update_armour_attack_flag()
+        self._effect_type: int = effect_type
+        self.ai_script_goal: int = ai_script_goal
+        self.armour_attack_quantity: int = armour_attack_quantity
+        self.armour_attack_class: int = armour_attack_class
+        self.quantity: int = quantity
+        self.tribute_list: int = tribute_list
+        self.diplomacy: int = diplomacy
+        self.object_list_unit_id: int = object_list_unit_id
+        self.source_player: int = source_player
+        self.target_player: int = target_player
+        self.technology: int = technology
+        self.string_id: int = string_id
+        self.display_time: int = display_time
+        self.trigger_id: int = trigger_id
+        self.location_x: int = location_x
+        self.location_y: int = location_y
+        self.location_object_reference: int = location_object_reference
+        self.area_x1: int = area_x1
+        self.area_y1: int = area_y1
+        self.area_x2: int = area_x2
+        self.area_y2: int = area_y2
+        self.object_group: int = object_group
+        self.object_type: int = object_type
+        self.instruction_panel_position: int = instruction_panel_position
+        self.attack_stance: int = attack_stance
+        self.time_unit: int = time_unit
+        self.enabled: int = enabled
+        self.food: int = food
+        self.wood: int = wood
+        self.stone: int = stone
+        self.gold: int = gold
+        # self.item_id: int = item_id  # Unused (?)
+        self.flash_object: int = flash_object
+        self.force_research_technology: int = force_research_technology
+        self.visibility_state: int = visibility_state
+        self.scroll: int = scroll
+        self.operation: int = operation
+        self.object_list_unit_id_2: int = object_list_unit_id_2
+        self.button_location: int = button_location
+        self.ai_signal_value: int = ai_signal_value
+        self.object_attributes: int = object_attributes
+        self.variable: int = variable
+        self.timer: int = timer
+        self.facet: int = facet
+        self.play_sound: int = play_sound
+        self.player_color: int = player_color
+        self.color_mood: int = color_mood
+        self.reset_timer: int = reset_timer
+        self.object_state: int = object_state
+        self.action_type: int = action_type
+        self.resource_1: int = resource_1
+        self.resource_1_quantity: int = resource_1_quantity
+        self.resource_2: int = resource_2
+        self.resource_2_quantity: int = resource_2_quantity
+        self.resource_3: int = resource_3
+        self.resource_3_quantity: int = resource_3_quantity
+        self.message: str = message
+        self.sound_name: str = sound_name
+        self.selected_object_ids: List[int] = selected_object_ids
+        self.unused_string_1: str = unused_string_1
+        self.unused_string_2: str = unused_string_2
+
+    @property
+    def legacy_location_object_reference(self) -> int:
+        """Getter for legacy object_reference location. Always returns `-1`."""
+        return -1
+
+    @property
+    def player_color(self):
+        """Get the player color attribute"""
+        return self._player_color
+
+    @player_color.setter
+    def player_color(self, value):
+        if type(value) in [PlayerColorId, PlayerId]:
+            value -= 1
+        self._player_color = value
+
+    @property
+    def item_id(self):
+        """Get the currently selected item_id based on other attributes"""
+        if value_is_valid(self.object_list_unit_id):
+            return self.object_list_unit_id
+        if value_is_valid(self.technology):
+            return self.technology
+        if value_is_valid(self.tribute_list):
+            return self.tribute_list
+        return -1
+
+    @item_id.setter
+    def item_id(self, value):
+        raise ValueError("The `item_id` attribute is always equal to its corresponding attribute."
+                         "Please use that attribute (i.e. 'object_list_unit_id' or 'technology' or 'tribute_list').")
+
+    @property
+    def effect_type(self):
+        """The type of the effect (EffectId dataset)"""
+        return self._effect_type
+
+    @effect_type.setter
+    def effect_type(self, value):
+        self._effect_type = value
+        self._update_armour_attack_flag()
+
+    @property
+    def object_attributes(self):
+        return self._object_attributes
+
+    @object_attributes.setter
+    def object_attributes(self, value):
+        self._object_attributes = value
+        self._update_armour_attack_flag()
+
+    @property
+    def _armour_attack_flag(self):
+        return self._armour_attack_source is not None
+
+    @property
+    def armour_attack_quantity(self):
+        """Helper property for handling the armour_attack related values"""
+        return self._armour_attack_quantity
+
+    @armour_attack_quantity.setter
+    def armour_attack_quantity(self, value):
+        if value is not None and value != [] and not self._armour_attack_flag:
+            warn("Setting 'effect.armour_attack_quantity' when the effect doesn't use armour/attack attributes "
+                 "might result in unintended behaviour.", category=IncorrectArmorAttackUsageWarning)
+        self._armour_attack_quantity = value
+
+    @property
+    def armour_attack_class(self):
+        """Helper property for handling the armour_attack related values"""
+        return self._armour_attack_class
+
+    @armour_attack_class.setter
+    def armour_attack_class(self, value):
+        if value is not None and value != [] and not self._armour_attack_flag:
+            warn("Setting 'effect.armour_attack_class' when the effect doesn't use armour/attack attributes "
+                 "might result in unintended behaviour.", category=IncorrectArmorAttackUsageWarning)
+        self._armour_attack_class = value
+
+    @property
+    def quantity(self) -> int:
+        """Getter for quantity, even if it is combined with `armour_attack_quantity` and `armour_attack_class`"""
+        if self._armour_attack_source == 'quantity':
+            return self._merge_aa_values(self.armour_attack_class, self.armour_attack_quantity)
+        return self._quantity
+
+    @quantity.setter
+    def quantity(self, value):
+        # Quantity by default, when unused is [], or
+        if self._armour_attack_source == 'quantity' and value not in (None, []):
+            warn(
+                message="Setting 'effect.quantity' directly in an effect that uses armour/attack attributes "
+                        "might result in unintended behaviour.\nPlease use the 'effect.armour_attack_quantity' "
+                        "and 'effect.armour_attack_class' attributes instead.",
+                category=IncorrectArmorAttackUsageWarning
+            )
+            self.armour_attack_class, self.armour_attack_quantity = self._split_aa_value(value)
+        self._quantity = value
+
+    @property
+    def _variable_ref(self) -> int | None:
+        """Variable only used for byte retrieval of Effect"""
+        if self._armour_attack_source == 'variable':
+            return self._merge_aa_values(self.armour_attack_class, self.variable)
+        return self.variable
+
+    @property
+    def selected_object_ids(self) -> List[int]:
+        """Get the current selected objects"""
+        return self._selected_object_ids
+
+    @selected_object_ids.setter
+    def selected_object_ids(self, val: List[int]):
+        if type(val) is int:
+            val = [val]
+        self._selected_object_ids = val
+
+    def _should_be_displayed(self, attr: str, val: Any) -> bool:
+        # Ignore the quantity value in the print statement when flag is True.
+        if self._armour_attack_flag and attr == "quantity":
+            return False
+        if not self._armour_attack_flag and (attr == "armour_attack_quantity" or attr == "armour_attack_class"):
+            return False
+
+        return super()._should_be_displayed(attr, val)
+
+    def get_content_as_string(self, include_effect_definition: bool = False) -> str:
+        """
+        Create a human-readable string showcasing all content of this effect.
+
+        This is also the function that is called when doing: `print(effect)`
+
+        Args:
+            include_effect_definition: If the effect meta-data should be added by this function
+
+        Returns:
+            The created string
+        """
+        if self.effect_type not in effects.attributes:  # Unknown effect
+            attributes_list = effects.empty_attributes
+        else:
+            attributes_list = effects.attributes[self.effect_type]
+
+        return_string = ""
+        for attribute in attributes_list:
+            val = getattr(self, attribute)
+            if not self._should_be_displayed(attribute, val):
+                continue
+
+            value_string = transform_effect_attr_value(self.effect_type, attribute, val, self._uuid)
+            return_string += f"{attribute}: {value_string}\n"
+
+        if return_string == "":
+            return "<< No Attributes >>\n"
+
+        if include_effect_definition:
+            try:
+                effect_name = effects.effect_names[self.effect_type]
+            except KeyError:
+                effect_name = "Unknown"
+
+            return f"{effect_name}:\n{add_tabs(return_string, 1)}"
+        return return_string
+
+    def _update_armour_attack_flag(self):
+        self._armour_attack_source = _get_armour_attack_source(self.effect_type, self.object_attributes)
+
+    def _split_aa_value(self, quantity: int) -> Tuple[int, int]:
+        """
+        A function to convert the initial quantity value to the quantity and armor/attack values.
+        Unfortunately this problem has to be solved in the object due to how specific this was implemented in DE.
+
+        Args:
+            quantity: the initial quantity value
+
+        Returns:
+            The one byte armor/attack class as int and one byte armor/attack quantity as int
+
+        ----
+
+        **Trigger Version 2.4**::
+
+            Quantity value: (3, 5)
+            00000000 00000000 00000011 000000101
+                                aaq      aac
+
+        Final 2/4 bytes are aaq (1 byte), and aac (1 byte). First 2 are unused. Max value of both is 255.
+
+        **Trigger Version 2.5**::
+
+            Quantity value: (3, 5)
+            00000000 00000011 00000000 000000101
+              aaq      aaq      aac      aac
+
+        The 4/4 bytes are aaq (2 bytes), and aac (2 bytes). All are used. Max value of both is 65535.
+
+        ----
+        """
+        trigger_version = getters.get_trigger_version(self._uuid)
+        if trigger_version >= 2.5:
+            return quantity >> 16, quantity & 65535
+        return quantity >> 8, quantity & 255
+
+    def _merge_aa_values(self, aa_class: int, aa_quantity: int) -> int:
+        """
+        A function to convert the quantity and armor/attack field to a quantity value.
+        Unfortunately this problem has to be solved in the object due to how specific this was implemented in DE.
+
+        Args:
+            aa_quantity: the armor quantity value
+            aa_class: the armor/attack value
+
+        Returns:
+            The one byte quantity and one byte armor/attack value
+        """
+        trigger_version = getters.get_trigger_version(self._uuid)
+        if trigger_version >= 2.5:
+            return aa_class * 65536 + aa_quantity
+
+        # Would use `aa_class << 8` - but apparently multiplication is faster
+        return aa_class * 256 + aa_quantity
+
+    def __str__(self):
+        return f"[Effect] {self.get_content_as_string(include_effect_definition=True)}"
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+action_type: int = action_type + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +action_type
+ + + + + +
+
+ +
+ +
+ + + + +
+ai_script_goal: int = ai_script_goal + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +ai_script_goal
+ + + + + +
+
+ +
+ +
+ + + + +
+ai_signal_value: int = ai_signal_value + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +ai_signal_value
+ + + + + +
+
+ +
+ +
+ + + + +
+area_x1: int = area_x1 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_x1
+ + + + + +
+
+ +
+ +
+ + + + +
+area_x2: int = area_x2 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_x2
+ + + + + +
+
+ +
+ +
+ + + + +
+area_y1: int = area_y1 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_y1
+ + + + + +
+
+ +
+ +
+ + + + +
+area_y2: int = area_y2 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +area_y2
+ + + + + +
+
+ +
+ +
+ + + + +
+armour_attack_class + + + property + writable + + +
+ +
+ + +
+ + + + + +
+ +

Helper property for handling the armour_attack related values

+
+ +
+ +
+ + + + +
+armour_attack_quantity + + + property + writable + + +
+ +
+ + +
+ + + + + +
+ +

Helper property for handling the armour_attack related values

+
+ +
+ +
+ + + + +
+attack_stance: int = attack_stance + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +attack_stance
+ + + + + +
+
+ +
+ +
+ + + + +
+button_location: int = button_location + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +button_location
+ + + + + +
+
+ +
+ +
+ + + + +
+color_mood: int = color_mood + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +color_mood
+ + + + + +
+
+ +
+ +
+ + + + +
+diplomacy: int = diplomacy + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +diplomacy
+ + + + + +
+
+ +
+ +
+ + + + +
+display_time: int = display_time + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +display_time
+ + + + + +
+
+ +
+ +
+ + + + +
+effect_type + + + property + writable + + +
+ +
+ + +
+ + + + + +
+ +

The type of the effect (EffectId dataset)

+
+ +
+ +
+ + + + +
+enabled: int = enabled + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +enabled
+ + + + + +
+
+ +
+ +
+ + + + +
+facet: int = facet + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +facet
+ + + + + +
+
+ +
+ +
+ + + + +
+flash_object: int = flash_object + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +flash_object
+ + + + + +
+
+ +
+ +
+ + + + +
+food: int = food + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +food
+ + + + + +
+
+ +
+ +
+ + + + +
+force_research_technology: int = force_research_technology + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +force_research_technology
+ + + + + +
+
+ +
+ +
+ + + + +
+gold: int = gold + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +gold
+ + + + + +
+
+ +
+ +
+ + + + +
+hidden_attribute = 'effect_type' + + + class-attribute + instance-attribute + + +
+ +
+ + + Value: +'effect_type'
+ + + + + +
+
+ +
+ +
+ + + + +
+instruction_panel_position: int = instruction_panel_position + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +instruction_panel_position
+ + + + + +
+
+ +
+ +
+ + + + +
+item_id + + + property + writable + + +
+ +
+ + +
+ + + + + +
+ +

Get the currently selected item_id based on other attributes

+
+ +
+ +
+ + + + +
+legacy_location_object_reference: int + + + property + + +
+ +
+ Type: +int + +
+ + + + + +
+ +

Getter for legacy object_reference location. Always returns -1.

+
+ +
+ +
+ + + + +
+location_object_reference: int = location_object_reference + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +location_object_reference
+ + + + + +
+
+ +
+ +
+ + + + +
+location_x: int = location_x + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +location_x
+ + + + + +
+
+ +
+ +
+ + + + +
+location_y: int = location_y + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +location_y
+ + + + + +
+
+ +
+ +
+ + + + +
+message: str = message + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +message
+ + + + + +
+
+ +
+ +
+ + + + +
+object_attributes + + + property + writable + + +
+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +
+object_group: int = object_group + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_group
+ + + + + +
+
+ +
+ +
+ + + + +
+object_list_unit_id: int = object_list_unit_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_list_unit_id
+ + + + + +
+
+ +
+ +
+ + + + +
+object_list_unit_id_2: int = object_list_unit_id_2 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_list_unit_id_2
+ + + + + +
+
+ +
+ +
+ + + + +
+object_state: int = object_state + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_state
+ + + + + +
+
+ +
+ +
+ + + + +
+object_type: int = object_type + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +object_type
+ + + + + +
+
+ +
+ +
+ + + + +
+operation: int = operation + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +operation
+ + + + + +
+
+ +
+ +
+ + + + +
+play_sound: int = play_sound + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +play_sound
+ + + + + +
+
+ +
+ +
+ + + + +
+player_color + + + property + writable + + +
+ +
+ + +
+ + + + + +
+ +

Get the player color attribute

+
+ +
+ +
+ + + + +
+quantity: int + + + property + writable + + +
+ +
+ Type: +int + +
+ + + + + +
+ +

Getter for quantity, even if it is combined with armour_attack_quantity and armour_attack_class

+
+ +
+ +
+ + + + +
+reset_timer: int = reset_timer + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +reset_timer
+ + + + + +
+
+ +
+ +
+ + + + +
+resource_1: int = resource_1 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +resource_1
+ + + + + +
+
+ +
+ +
+ + + + +
+resource_1_quantity: int = resource_1_quantity + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +resource_1_quantity
+ + + + + +
+
+ +
+ +
+ + + + +
+resource_2: int = resource_2 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +resource_2
+ + + + + +
+
+ +
+ +
+ + + + +
+resource_2_quantity: int = resource_2_quantity + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +resource_2_quantity
+ + + + + +
+
+ +
+ +
+ + + + +
+resource_3: int = resource_3 + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +resource_3
+ + + + + +
+
+ +
+ +
+ + + + +
+resource_3_quantity: int = resource_3_quantity + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +resource_3_quantity
+ + + + + +
+
+ +
+ +
+ + + + +
+scroll: int = scroll + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +scroll
+ + + + + +
+
+ +
+ +
+ + + + +
+selected_object_ids: List[int] + + + property + writable + + +
+ +
+ Type: +List[int] + +
+ + + + + +
+ +

Get the current selected objects

+
+ +
+ +
+ + + + +
+sound_name: str = sound_name + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +sound_name
+ + + + + +
+
+ +
+ +
+ + + + +
+source_player: int = source_player + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +source_player
+ + + + + +
+
+ +
+ +
+ + + + +
+stone: int = stone + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +stone
+ + + + + +
+
+ +
+ +
+ + + + +
+string_id: int = string_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +string_id
+ + + + + +
+
+ +
+ +
+ + + + +
+target_player: int = target_player + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +target_player
+ + + + + +
+
+ +
+ +
+ + + + +
+technology: int = technology + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +technology
+ + + + + +
+
+ +
+ +
+ + + + +
+time_unit: int = time_unit + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +time_unit
+ + + + + +
+
+ +
+ +
+ + + + +
+timer: int = timer + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +timer
+ + + + + +
+
+ +
+ +
+ + + + +
+tribute_list: int = tribute_list + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +tribute_list
+ + + + + +
+
+ +
+ +
+ + + + +
+trigger_id: int = trigger_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +trigger_id
+ + + + + +
+
+ +
+ +
+ + + + +
+unused_string_1: str = unused_string_1 + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +unused_string_1
+ + + + + +
+
+ +
+ +
+ + + + +
+unused_string_2: str = unused_string_2 + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +unused_string_2
+ + + + + +
+
+ +
+ +
+ + + + +
+variable: int = variable + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +variable
+ + + + + +
+
+ +
+ +
+ + + + +
+visibility_state: int = visibility_state + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +visibility_state
+ + + + + +
+
+ +
+ +
+ + + + +
+wood: int = wood + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +wood
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
effect_type + int + - + None +
ai_script_goal + int + - + None +
armour_attack_quantity + int + - + None +
armour_attack_class + int + - + None +
quantity + int + - + None +
tribute_list + int + - + None +
diplomacy + int + - + None +
legacy_location_object_reference + int + - + None +
object_list_unit_id + int + - + None +
source_player + int + - + None +
target_player + int + - + None +
technology + int + - + None +
string_id + int + - + None +
display_time + int + - + None +
trigger_id + int + - + None +
location_x + int + - + None +
location_y + int + - + None +
location_object_reference + int + - + None +
area_x1 + int + - + None +
area_y1 + int + - + None +
area_x2 + int + - + None +
area_y2 + int + - + None +
object_group + int + - + None +
object_type + int + - + None +
instruction_panel_position + int + - + None +
attack_stance + int + - + None +
time_unit + int + - + None +
enabled + int + - + None +
food + int + - + None +
wood + int + - + None +
stone + int + - + None +
gold + int + - + None +
item_id + int + - + None +
flash_object + int + - + None +
force_research_technology + int + - + None +
visibility_state + int + - + None +
scroll + int + - + None +
operation + int + - + None +
object_list_unit_id_2 + int + - + None +
button_location + int + - + None +
ai_signal_value + int + - + None +
object_attributes + int + - + None +
variable + int + - + None +
timer + int + - + None +
facet + int + - + None +
play_sound + int + - + None +
player_color + int + - + None +
color_mood + int + - + None +
reset_timer + int + - + None +
object_state + int + - + None +
action_type + int + - + None +
resource_1 + int + - + None +
resource_1_quantity + int + - + None +
resource_2 + int + - + None +
resource_2_quantity + int + - + None +
resource_3 + int + - + None +
resource_3_quantity + int + - + None +
message + str + - + None +
sound_name + str + - + None +
selected_object_ids + List[int] + - + None +
unused_string_1 + str + - + None +
unused_string_2 + str + - + None +
_variable_ref + int + - + None +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/effect.py +
100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
def __init__(
+        self,
+        effect_type: int = None,
+        ai_script_goal: int = None,
+        armour_attack_quantity: int = None,
+        armour_attack_class: int = None,
+        quantity: int = None,
+        tribute_list: int = None,
+        diplomacy: int = None,
+        legacy_location_object_reference: int = None,
+        object_list_unit_id: int = None,
+        source_player: int = None,
+        target_player: int = None,
+        technology: int = None,
+        string_id: int = None,
+        display_time: int = None,
+        trigger_id: int = None,
+        location_x: int = None,
+        location_y: int = None,
+        location_object_reference: int = None,
+        area_x1: int = None,
+        area_y1: int = None,
+        area_x2: int = None,
+        area_y2: int = None,
+        object_group: int = None,
+        object_type: int = None,
+        instruction_panel_position: int = None,
+        attack_stance: int = None,
+        time_unit: int = None,
+        enabled: int = None,
+        food: int = None,
+        wood: int = None,
+        stone: int = None,
+        gold: int = None,
+        item_id: int = None,  # Unused (?)
+        flash_object: int = None,
+        force_research_technology: int = None,
+        visibility_state: int = None,
+        scroll: int = None,
+        operation: int = None,
+        object_list_unit_id_2: int = None,
+        button_location: int = None,
+        ai_signal_value: int = None,
+        object_attributes: int = None,
+        variable: int = None,
+        timer: int = None,
+        facet: int = None,
+        play_sound: int = None,
+        player_color: int = None,
+        color_mood: int = None,
+        reset_timer: int = None,
+        object_state: int = None,
+        action_type: int = None,
+        resource_1: int = None,
+        resource_1_quantity: int = None,
+        resource_2: int = None,
+        resource_2_quantity: int = None,
+        resource_3: int = None,
+        resource_3_quantity: int = None,
+        message: str = None,
+        sound_name: str = None,
+        selected_object_ids: List[int] = None,
+        unused_string_1: str = None,
+        unused_string_2: str = None,
+        # Used for variable retrieval in armour/attack effects (source=variable only)
+        _variable_ref: int = None,
+        **kwargs
+):
+    super().__init__(**kwargs)
+
+    raise_if_not_int_subclass([object_list_unit_id, technology, object_list_unit_id_2])
+
+    if selected_object_ids is None:
+        selected_object_ids = []
+
+    # Set armour/attack flags
+    self._armour_attack_source = _get_armour_attack_source(effect_type, object_attributes)
+
+    if self._armour_attack_source == 'variable':
+        # If effect created through reading scenario file
+        if _variable_ref is not None and variable is None and armour_attack_class is None:
+            armour_attack_class, variable = self._split_aa_value(_variable_ref)
+        # If created through new_effect with variable and armour_attack_class values
+        else:
+            armour_attack_class = armour_attack_class or 0
+    elif self._armour_attack_source == 'quantity':
+        # If effect created through reading scenario file
+        if quantity is not None and armour_attack_class is None and armour_attack_quantity is None:
+            armour_attack_class, armour_attack_quantity = self._split_aa_value(quantity)
+            quantity = None
+        # If effect created through new_effect with aa values defined
+        elif value_is_valid(armour_attack_class) or value_is_valid(armour_attack_quantity):
+            quantity = None
+        # If created through new_effect with quantity defined instead of the aa values. Handled by quantity property
+        else:
+            pass
+    else:
+        armour_attack_class = armour_attack_quantity = None
+
+    area_x1, area_y1, area_x2, area_y2 = validate_coords(area_x1, area_y1, area_x2, area_y2)
+
+    if value_is_valid(legacy_location_object_reference):
+        location_object_reference = legacy_location_object_reference
+
+    # Bypass the @property which causes: self._update_armour_attack_flag()
+    self._effect_type: int = effect_type
+    self.ai_script_goal: int = ai_script_goal
+    self.armour_attack_quantity: int = armour_attack_quantity
+    self.armour_attack_class: int = armour_attack_class
+    self.quantity: int = quantity
+    self.tribute_list: int = tribute_list
+    self.diplomacy: int = diplomacy
+    self.object_list_unit_id: int = object_list_unit_id
+    self.source_player: int = source_player
+    self.target_player: int = target_player
+    self.technology: int = technology
+    self.string_id: int = string_id
+    self.display_time: int = display_time
+    self.trigger_id: int = trigger_id
+    self.location_x: int = location_x
+    self.location_y: int = location_y
+    self.location_object_reference: int = location_object_reference
+    self.area_x1: int = area_x1
+    self.area_y1: int = area_y1
+    self.area_x2: int = area_x2
+    self.area_y2: int = area_y2
+    self.object_group: int = object_group
+    self.object_type: int = object_type
+    self.instruction_panel_position: int = instruction_panel_position
+    self.attack_stance: int = attack_stance
+    self.time_unit: int = time_unit
+    self.enabled: int = enabled
+    self.food: int = food
+    self.wood: int = wood
+    self.stone: int = stone
+    self.gold: int = gold
+    # self.item_id: int = item_id  # Unused (?)
+    self.flash_object: int = flash_object
+    self.force_research_technology: int = force_research_technology
+    self.visibility_state: int = visibility_state
+    self.scroll: int = scroll
+    self.operation: int = operation
+    self.object_list_unit_id_2: int = object_list_unit_id_2
+    self.button_location: int = button_location
+    self.ai_signal_value: int = ai_signal_value
+    self.object_attributes: int = object_attributes
+    self.variable: int = variable
+    self.timer: int = timer
+    self.facet: int = facet
+    self.play_sound: int = play_sound
+    self.player_color: int = player_color
+    self.color_mood: int = color_mood
+    self.reset_timer: int = reset_timer
+    self.object_state: int = object_state
+    self.action_type: int = action_type
+    self.resource_1: int = resource_1
+    self.resource_1_quantity: int = resource_1_quantity
+    self.resource_2: int = resource_2
+    self.resource_2_quantity: int = resource_2_quantity
+    self.resource_3: int = resource_3
+    self.resource_3_quantity: int = resource_3_quantity
+    self.message: str = message
+    self.sound_name: str = sound_name
+    self.selected_object_ids: List[int] = selected_object_ids
+    self.unused_string_1: str = unused_string_1
+    self.unused_string_2: str = unused_string_2
+
+
+
+ +
+ +
+ + +
+def __str__(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/effect.py +
489
+490
def __str__(self):
+    return f"[Effect] {self.get_content_as_string(include_effect_definition=True)}"
+
+
+
+ +
+ +
+ + +
+def get_content_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing all content of this effect.

+

This is also the function that is called when doing: print(effect)

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
include_effect_definition + bool + +
+

If the effect meta-data should be added by this function

+
+
+ False +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/effect.py +
393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
def get_content_as_string(self, include_effect_definition: bool = False) -> str:
+    """
+    Create a human-readable string showcasing all content of this effect.
+
+    This is also the function that is called when doing: `print(effect)`
+
+    Args:
+        include_effect_definition: If the effect meta-data should be added by this function
+
+    Returns:
+        The created string
+    """
+    if self.effect_type not in effects.attributes:  # Unknown effect
+        attributes_list = effects.empty_attributes
+    else:
+        attributes_list = effects.attributes[self.effect_type]
+
+    return_string = ""
+    for attribute in attributes_list:
+        val = getattr(self, attribute)
+        if not self._should_be_displayed(attribute, val):
+            continue
+
+        value_string = transform_effect_attr_value(self.effect_type, attribute, val, self._uuid)
+        return_string += f"{attribute}: {value_string}\n"
+
+    if return_string == "":
+        return "<< No Attributes >>\n"
+
+    if include_effect_definition:
+        try:
+            effect_name = effects.effect_names[self.effect_type]
+        except KeyError:
+            effect_name = "Unknown"
+
+        return f"{effect_name}:\n{add_tabs(return_string, 1)}"
+    return return_string
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/effects/new_effect/index.html b/api_docs/trigger/effects/new_effect/index.html new file mode 100644 index 00000000..defed713 --- /dev/null +++ b/api_docs/trigger/effects/new_effect/index.html @@ -0,0 +1,19213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + NewEffect - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

NewEffect

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ NewEffectSupport + + +

+ + +
+ + +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
  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
+ 124
+ 125
+ 126
+ 127
+ 128
+ 129
+ 130
+ 131
+ 132
+ 133
+ 134
+ 135
+ 136
+ 137
+ 138
+ 139
+ 140
+ 141
+ 142
+ 143
+ 144
+ 145
+ 146
+ 147
+ 148
+ 149
+ 150
+ 151
+ 152
+ 153
+ 154
+ 155
+ 156
+ 157
+ 158
+ 159
+ 160
+ 161
+ 162
+ 163
+ 164
+ 165
+ 166
+ 167
+ 168
+ 169
+ 170
+ 171
+ 172
+ 173
+ 174
+ 175
+ 176
+ 177
+ 178
+ 179
+ 180
+ 181
+ 182
+ 183
+ 184
+ 185
+ 186
+ 187
+ 188
+ 189
+ 190
+ 191
+ 192
+ 193
+ 194
+ 195
+ 196
+ 197
+ 198
+ 199
+ 200
+ 201
+ 202
+ 203
+ 204
+ 205
+ 206
+ 207
+ 208
+ 209
+ 210
+ 211
+ 212
+ 213
+ 214
+ 215
+ 216
+ 217
+ 218
+ 219
+ 220
+ 221
+ 222
+ 223
+ 224
+ 225
+ 226
+ 227
+ 228
+ 229
+ 230
+ 231
+ 232
+ 233
+ 234
+ 235
+ 236
+ 237
+ 238
+ 239
+ 240
+ 241
+ 242
+ 243
+ 244
+ 245
+ 246
+ 247
+ 248
+ 249
+ 250
+ 251
+ 252
+ 253
+ 254
+ 255
+ 256
+ 257
+ 258
+ 259
+ 260
+ 261
+ 262
+ 263
+ 264
+ 265
+ 266
+ 267
+ 268
+ 269
+ 270
+ 271
+ 272
+ 273
+ 274
+ 275
+ 276
+ 277
+ 278
+ 279
+ 280
+ 281
+ 282
+ 283
+ 284
+ 285
+ 286
+ 287
+ 288
+ 289
+ 290
+ 291
+ 292
+ 293
+ 294
+ 295
+ 296
+ 297
+ 298
+ 299
+ 300
+ 301
+ 302
+ 303
+ 304
+ 305
+ 306
+ 307
+ 308
+ 309
+ 310
+ 311
+ 312
+ 313
+ 314
+ 315
+ 316
+ 317
+ 318
+ 319
+ 320
+ 321
+ 322
+ 323
+ 324
+ 325
+ 326
+ 327
+ 328
+ 329
+ 330
+ 331
+ 332
+ 333
+ 334
+ 335
+ 336
+ 337
+ 338
+ 339
+ 340
+ 341
+ 342
+ 343
+ 344
+ 345
+ 346
+ 347
+ 348
+ 349
+ 350
+ 351
+ 352
+ 353
+ 354
+ 355
+ 356
+ 357
+ 358
+ 359
+ 360
+ 361
+ 362
+ 363
+ 364
+ 365
+ 366
+ 367
+ 368
+ 369
+ 370
+ 371
+ 372
+ 373
+ 374
+ 375
+ 376
+ 377
+ 378
+ 379
+ 380
+ 381
+ 382
+ 383
+ 384
+ 385
+ 386
+ 387
+ 388
+ 389
+ 390
+ 391
+ 392
+ 393
+ 394
+ 395
+ 396
+ 397
+ 398
+ 399
+ 400
+ 401
+ 402
+ 403
+ 404
+ 405
+ 406
+ 407
+ 408
+ 409
+ 410
+ 411
+ 412
+ 413
+ 414
+ 415
+ 416
+ 417
+ 418
+ 419
+ 420
+ 421
+ 422
+ 423
+ 424
+ 425
+ 426
+ 427
+ 428
+ 429
+ 430
+ 431
+ 432
+ 433
+ 434
+ 435
+ 436
+ 437
+ 438
+ 439
+ 440
+ 441
+ 442
+ 443
+ 444
+ 445
+ 446
+ 447
+ 448
+ 449
+ 450
+ 451
+ 452
+ 453
+ 454
+ 455
+ 456
+ 457
+ 458
+ 459
+ 460
+ 461
+ 462
+ 463
+ 464
+ 465
+ 466
+ 467
+ 468
+ 469
+ 470
+ 471
+ 472
+ 473
+ 474
+ 475
+ 476
+ 477
+ 478
+ 479
+ 480
+ 481
+ 482
+ 483
+ 484
+ 485
+ 486
+ 487
+ 488
+ 489
+ 490
+ 491
+ 492
+ 493
+ 494
+ 495
+ 496
+ 497
+ 498
+ 499
+ 500
+ 501
+ 502
+ 503
+ 504
+ 505
+ 506
+ 507
+ 508
+ 509
+ 510
+ 511
+ 512
+ 513
+ 514
+ 515
+ 516
+ 517
+ 518
+ 519
+ 520
+ 521
+ 522
+ 523
+ 524
+ 525
+ 526
+ 527
+ 528
+ 529
+ 530
+ 531
+ 532
+ 533
+ 534
+ 535
+ 536
+ 537
+ 538
+ 539
+ 540
+ 541
+ 542
+ 543
+ 544
+ 545
+ 546
+ 547
+ 548
+ 549
+ 550
+ 551
+ 552
+ 553
+ 554
+ 555
+ 556
+ 557
+ 558
+ 559
+ 560
+ 561
+ 562
+ 563
+ 564
+ 565
+ 566
+ 567
+ 568
+ 569
+ 570
+ 571
+ 572
+ 573
+ 574
+ 575
+ 576
+ 577
+ 578
+ 579
+ 580
+ 581
+ 582
+ 583
+ 584
+ 585
+ 586
+ 587
+ 588
+ 589
+ 590
+ 591
+ 592
+ 593
+ 594
+ 595
+ 596
+ 597
+ 598
+ 599
+ 600
+ 601
+ 602
+ 603
+ 604
+ 605
+ 606
+ 607
+ 608
+ 609
+ 610
+ 611
+ 612
+ 613
+ 614
+ 615
+ 616
+ 617
+ 618
+ 619
+ 620
+ 621
+ 622
+ 623
+ 624
+ 625
+ 626
+ 627
+ 628
+ 629
+ 630
+ 631
+ 632
+ 633
+ 634
+ 635
+ 636
+ 637
+ 638
+ 639
+ 640
+ 641
+ 642
+ 643
+ 644
+ 645
+ 646
+ 647
+ 648
+ 649
+ 650
+ 651
+ 652
+ 653
+ 654
+ 655
+ 656
+ 657
+ 658
+ 659
+ 660
+ 661
+ 662
+ 663
+ 664
+ 665
+ 666
+ 667
+ 668
+ 669
+ 670
+ 671
+ 672
+ 673
+ 674
+ 675
+ 676
+ 677
+ 678
+ 679
+ 680
+ 681
+ 682
+ 683
+ 684
+ 685
+ 686
+ 687
+ 688
+ 689
+ 690
+ 691
+ 692
+ 693
+ 694
+ 695
+ 696
+ 697
+ 698
+ 699
+ 700
+ 701
+ 702
+ 703
+ 704
+ 705
+ 706
+ 707
+ 708
+ 709
+ 710
+ 711
+ 712
+ 713
+ 714
+ 715
+ 716
+ 717
+ 718
+ 719
+ 720
+ 721
+ 722
+ 723
+ 724
+ 725
+ 726
+ 727
+ 728
+ 729
+ 730
+ 731
+ 732
+ 733
+ 734
+ 735
+ 736
+ 737
+ 738
+ 739
+ 740
+ 741
+ 742
+ 743
+ 744
+ 745
+ 746
+ 747
+ 748
+ 749
+ 750
+ 751
+ 752
+ 753
+ 754
+ 755
+ 756
+ 757
+ 758
+ 759
+ 760
+ 761
+ 762
+ 763
+ 764
+ 765
+ 766
+ 767
+ 768
+ 769
+ 770
+ 771
+ 772
+ 773
+ 774
+ 775
+ 776
+ 777
+ 778
+ 779
+ 780
+ 781
+ 782
+ 783
+ 784
+ 785
+ 786
+ 787
+ 788
+ 789
+ 790
+ 791
+ 792
+ 793
+ 794
+ 795
+ 796
+ 797
+ 798
+ 799
+ 800
+ 801
+ 802
+ 803
+ 804
+ 805
+ 806
+ 807
+ 808
+ 809
+ 810
+ 811
+ 812
+ 813
+ 814
+ 815
+ 816
+ 817
+ 818
+ 819
+ 820
+ 821
+ 822
+ 823
+ 824
+ 825
+ 826
+ 827
+ 828
+ 829
+ 830
+ 831
+ 832
+ 833
+ 834
+ 835
+ 836
+ 837
+ 838
+ 839
+ 840
+ 841
+ 842
+ 843
+ 844
+ 845
+ 846
+ 847
+ 848
+ 849
+ 850
+ 851
+ 852
+ 853
+ 854
+ 855
+ 856
+ 857
+ 858
+ 859
+ 860
+ 861
+ 862
+ 863
+ 864
+ 865
+ 866
+ 867
+ 868
+ 869
+ 870
+ 871
+ 872
+ 873
+ 874
+ 875
+ 876
+ 877
+ 878
+ 879
+ 880
+ 881
+ 882
+ 883
+ 884
+ 885
+ 886
+ 887
+ 888
+ 889
+ 890
+ 891
+ 892
+ 893
+ 894
+ 895
+ 896
+ 897
+ 898
+ 899
+ 900
+ 901
+ 902
+ 903
+ 904
+ 905
+ 906
+ 907
+ 908
+ 909
+ 910
+ 911
+ 912
+ 913
+ 914
+ 915
+ 916
+ 917
+ 918
+ 919
+ 920
+ 921
+ 922
+ 923
+ 924
+ 925
+ 926
+ 927
+ 928
+ 929
+ 930
+ 931
+ 932
+ 933
+ 934
+ 935
+ 936
+ 937
+ 938
+ 939
+ 940
+ 941
+ 942
+ 943
+ 944
+ 945
+ 946
+ 947
+ 948
+ 949
+ 950
+ 951
+ 952
+ 953
+ 954
+ 955
+ 956
+ 957
+ 958
+ 959
+ 960
+ 961
+ 962
+ 963
+ 964
+ 965
+ 966
+ 967
+ 968
+ 969
+ 970
+ 971
+ 972
+ 973
+ 974
+ 975
+ 976
+ 977
+ 978
+ 979
+ 980
+ 981
+ 982
+ 983
+ 984
+ 985
+ 986
+ 987
+ 988
+ 989
+ 990
+ 991
+ 992
+ 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
class NewEffectSupport:
+    def __init__(self, trigger_ref):
+        self._trigger_ref = trigger_ref
+
+    def none(
+            self,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.NONE,
+        )
+
+    def change_diplomacy(
+            self,
+            diplomacy: int | None = None,
+            source_player: int | None = None,
+            target_player: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_DIPLOMACY,
+            diplomacy=diplomacy,
+            source_player=source_player,
+            target_player=target_player,
+        )
+
+    def research_technology(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            force_research_technology: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.RESEARCH_TECHNOLOGY,
+            source_player=source_player,
+            technology=technology,
+            force_research_technology=force_research_technology,
+        )
+
+    def send_chat(
+            self,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            message: str | None = None,
+            sound_name: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.SEND_CHAT,
+            source_player=source_player,
+            string_id=string_id,
+            message=message,
+            sound_name=sound_name,
+        )
+
+    def play_sound(
+            self,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            location_object_reference: int | None = None,
+            sound_name: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.PLAY_SOUND,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            location_object_reference=location_object_reference,
+            sound_name=sound_name,
+        )
+
+    def tribute(
+            self,
+            quantity: int | None = None,
+            tribute_list: int | None = None,
+            source_player: int | None = None,
+            target_player: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.TRIBUTE,
+            quantity=quantity,
+            tribute_list=tribute_list,
+            source_player=source_player,
+            target_player=target_player,
+        )
+
+    def unlock_gate(
+            self,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.UNLOCK_GATE,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def lock_gate(
+            self,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.LOCK_GATE,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def activate_trigger(
+            self,
+            trigger_id: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ACTIVATE_TRIGGER,
+            trigger_id=trigger_id,
+        )
+
+    def deactivate_trigger(
+            self,
+            trigger_id: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DEACTIVATE_TRIGGER,
+            trigger_id=trigger_id,
+        )
+
+    def ai_script_goal(
+            self,
+            ai_script_goal: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.AI_SCRIPT_GOAL,
+            ai_script_goal=ai_script_goal,
+        )
+
+    def create_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            facet: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CREATE_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            facet=facet,
+        )
+
+    def task_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            location_object_reference: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            action_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.TASK_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            location_object_reference=location_object_reference,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            action_type=action_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def declare_victory(
+            self,
+            source_player: int | None = None,
+            enabled: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DECLARE_VICTORY,
+            source_player=source_player,
+            enabled=enabled,
+        )
+
+    def kill_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.KILL_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def remove_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            object_state: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.REMOVE_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            object_state=object_state,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_view(
+            self,
+            quantity: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            scroll: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_VIEW,
+            quantity=quantity,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            scroll=scroll,
+        )
+
+    def unload(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            location_object_reference: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.UNLOAD,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            location_object_reference=location_object_reference,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_ownership(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            target_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            flash_object: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OWNERSHIP,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            target_player=target_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            flash_object=flash_object,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def patrol(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.PATROL,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def display_instructions(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            display_time: int | None = None,
+            instruction_panel_position: int | None = None,
+            play_sound: int | None = None,
+            message: str | None = None,
+            sound_name: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DISPLAY_INSTRUCTIONS,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            string_id=string_id,
+            display_time=display_time,
+            instruction_panel_position=instruction_panel_position,
+            play_sound=play_sound,
+            message=message,
+            sound_name=sound_name,
+        )
+
+    def clear_instructions(
+            self,
+            instruction_panel_position: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CLEAR_INSTRUCTIONS,
+            instruction_panel_position=instruction_panel_position,
+        )
+
+    def freeze_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.FREEZE_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def use_advanced_buttons(
+            self,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.USE_ADVANCED_BUTTONS,
+        )
+
+    def damage_object(
+            self,
+            quantity: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DAMAGE_OBJECT,
+            quantity=quantity,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def place_foundation(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.PLACE_FOUNDATION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+        )
+
+    def change_object_name(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            message: str | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_NAME,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            string_id=string_id,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            message=message,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_hp(
+            self,
+            quantity: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            operation: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_HP,
+            quantity=quantity,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            operation=operation,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_attack(
+            self,
+            armour_attack_quantity: int | None = None,
+            armour_attack_class: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            operation: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_ATTACK,
+            armour_attack_quantity=armour_attack_quantity,
+            armour_attack_class=armour_attack_class,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            operation=operation,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def stop_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.STOP_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def attack_move(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            location_object_reference: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ATTACK_MOVE,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            location_object_reference=location_object_reference,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_armor(
+            self,
+            armour_attack_quantity: int | None = None,
+            armour_attack_class: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            operation: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_ARMOR,
+            armour_attack_quantity=armour_attack_quantity,
+            armour_attack_class=armour_attack_class,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            operation=operation,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_range(
+            self,
+            quantity: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            operation: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_RANGE,
+            quantity=quantity,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            operation=operation,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_speed(
+            self,
+            quantity: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_SPEED,
+            quantity=quantity,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def heal_object(
+            self,
+            quantity: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.HEAL_OBJECT,
+            quantity=quantity,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def teleport_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.TELEPORT_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_stance(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            attack_stance: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_STANCE,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            attack_stance=attack_stance,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def display_timer(
+            self,
+            string_id: int | None = None,
+            display_time: int | None = None,
+            time_unit: int | None = None,
+            timer: int | None = None,
+            reset_timer: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DISPLAY_TIMER,
+            string_id=string_id,
+            display_time=display_time,
+            time_unit=time_unit,
+            timer=timer,
+            reset_timer=reset_timer,
+            message=message,
+        )
+
+    def enable_disable_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            enabled: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ENABLE_DISABLE_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            enabled=enabled,
+        )
+
+    def enable_disable_technology(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            enabled: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ENABLE_DISABLE_TECHNOLOGY,
+            source_player=source_player,
+            technology=technology,
+            enabled=enabled,
+        )
+
+    def change_object_cost(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            resource_1: int | None = None,
+            resource_1_quantity: int | None = None,
+            resource_2: int | None = None,
+            resource_2_quantity: int | None = None,
+            resource_3: int | None = None,
+            resource_3_quantity: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_COST,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            resource_1=resource_1,
+            resource_1_quantity=resource_1_quantity,
+            resource_2=resource_2,
+            resource_2_quantity=resource_2_quantity,
+            resource_3=resource_3,
+            resource_3_quantity=resource_3_quantity,
+        )
+
+    def set_player_visibility(
+            self,
+            source_player: int | None = None,
+            target_player: int | None = None,
+            visibility_state: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.SET_PLAYER_VISIBILITY,
+            source_player=source_player,
+            target_player=target_player,
+            visibility_state=visibility_state,
+        )
+
+    def change_object_icon(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            object_list_unit_id_2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_ICON,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            object_list_unit_id_2=object_list_unit_id_2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def replace_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            target_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            object_list_unit_id_2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.REPLACE_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            target_player=target_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            object_list_unit_id_2=object_list_unit_id_2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_description(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_DESCRIPTION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            string_id=string_id,
+            message=message,
+        )
+
+    def change_player_name(
+            self,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_PLAYER_NAME,
+            source_player=source_player,
+            string_id=string_id,
+            message=message,
+        )
+
+    def change_train_location(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            object_list_unit_id_2: int | None = None,
+            button_location: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TRAIN_LOCATION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            object_list_unit_id_2=object_list_unit_id_2,
+            button_location=button_location,
+        )
+
+    @deprecated('Use `change_technology_location` instead')
+    def change_research_location(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            object_list_unit_id_2: int | None = None,
+            button_location: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_LOCATION,
+            source_player=source_player,
+            technology=technology,
+            object_list_unit_id_2=object_list_unit_id_2,
+            button_location=button_location,
+        )
+
+    def change_technology_location(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            object_list_unit_id_2: int | None = None,
+            button_location: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_LOCATION,
+            source_player=source_player,
+            technology=technology,
+            object_list_unit_id_2=object_list_unit_id_2,
+            button_location=button_location,
+        )
+
+    def change_civilization_name(
+            self,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_CIVILIZATION_NAME,
+            source_player=source_player,
+            string_id=string_id,
+            message=message,
+        )
+
+    def create_garrisoned_object(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_list_unit_id_2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CREATE_GARRISONED_OBJECT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_list_unit_id_2=object_list_unit_id_2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def acknowledge_ai_signal(
+            self,
+            ai_signal_value: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ACKNOWLEDGE_AI_SIGNAL,
+            ai_signal_value=ai_signal_value,
+        )
+
+    def modify_attribute(
+            self,
+            quantity: int | None = None,
+            armour_attack_quantity: int | None = None,
+            armour_attack_class: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            operation: int | None = None,
+            object_attributes: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        """
+        The parameters 'armour_attack_quantity' and 'armour_attack_class' are only used when object_attributes is Armor
+        or Attack (8 or 9). Use, 'quantity' otherwise.
+        """
+        if (armour_attack_quantity is not None or armour_attack_class is not None) and quantity is not None:
+            raise ValueError("Cannot use 'armour_attack' attributes together with the 'quantity' attribute.")
+
+        return self._trigger_ref._add_effect(
+            EffectId.MODIFY_ATTRIBUTE,
+            quantity=quantity,
+            armour_attack_quantity=armour_attack_quantity,
+            armour_attack_class=armour_attack_class,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            operation=operation,
+            object_attributes=object_attributes,
+            message=message
+        )
+
+    def modify_resource(
+            self,
+            quantity: int | None = None,
+            tribute_list: int | None = None,
+            source_player: int | None = None,
+            operation: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.MODIFY_RESOURCE,
+            quantity=quantity,
+            tribute_list=tribute_list,
+            source_player=source_player,
+            operation=operation,
+        )
+
+    def modify_resource_by_variable(
+            self,
+            tribute_list: int | None = None,
+            source_player: int | None = None,
+            operation: int | None = None,
+            variable: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.MODIFY_RESOURCE_BY_VARIABLE,
+            tribute_list=tribute_list,
+            source_player=source_player,
+            operation=operation,
+            variable=variable,
+        )
+
+    def set_building_gather_point(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.SET_BUILDING_GATHER_POINT,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def script_call(
+            self,
+            string_id: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.SCRIPT_CALL,
+            string_id=string_id,
+            message=message,
+        )
+
+    def change_variable(
+            self,
+            quantity: int | None = None,
+            operation: int | None = None,
+            variable: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_VARIABLE,
+            quantity=quantity,
+            operation=operation,
+            variable=variable,
+            message=message,
+        )
+
+    def clear_timer(
+            self,
+            timer: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CLEAR_TIMER,
+            timer=timer,
+        )
+
+    def change_object_player_color(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            player_color: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_PLAYER_COLOR,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            player_color=player_color,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_civilization_name(
+            self,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            message: str | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_CIVILIZATION_NAME,
+            source_player=source_player,
+            string_id=string_id,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            message=message,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_object_player_name(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            message: str | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_PLAYER_NAME,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            string_id=string_id,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            message=message,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def disable_unit_targeting(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DISABLE_UNIT_TARGETING,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def enable_unit_targeting(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ENABLE_UNIT_TARGETING,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_technology_cost(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            resource_1: int | None = None,
+            resource_1_quantity: int | None = None,
+            resource_2: int | None = None,
+            resource_2_quantity: int | None = None,
+            resource_3: int | None = None,
+            resource_3_quantity: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_COST,
+            source_player=source_player,
+            technology=technology,
+            resource_1=resource_1,
+            resource_1_quantity=resource_1_quantity,
+            resource_2=resource_2,
+            resource_2_quantity=resource_2_quantity,
+            resource_3=resource_3,
+            resource_3_quantity=resource_3_quantity,
+        )
+
+    def change_technology_research_time(
+            self,
+            quantity: int | None = None,
+            source_player: int | None = None,
+            technology: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_RESEARCH_TIME,
+            quantity=quantity,
+            source_player=source_player,
+            technology=technology,
+        )
+
+    def change_technology_name(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            string_id: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_NAME,
+            source_player=source_player,
+            technology=technology,
+            string_id=string_id,
+            message=message,
+        )
+
+    def change_technology_description(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            string_id: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_DESCRIPTION,
+            source_player=source_player,
+            technology=technology,
+            string_id=string_id,
+            message=message,
+        )
+
+    def enable_technology_stacking(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            quantity: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ENABLE_TECHNOLOGY_STACKING,
+            source_player=source_player,
+            technology=technology,
+            quantity=quantity,
+        )
+
+    def disable_technology_stacking(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DISABLE_TECHNOLOGY_STACKING,
+            source_player=source_player,
+            technology=technology,
+        )
+
+    def acknowledge_multiplayer_ai_signal(
+            self,
+            ai_signal_value: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ACKNOWLEDGE_MULTIPLAYER_AI_SIGNAL,
+            ai_signal_value=ai_signal_value,
+        )
+
+    def disable_object_selection(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DISABLE_OBJECT_SELECTION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def enable_object_selection(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.ENABLE_OBJECT_SELECTION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_color_mood(
+            self,
+            quantity: int | None = None,
+            color_mood: int | None = None):
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_COLOR_MOOD,
+            quantity=quantity,
+            color_mood=color_mood
+        )
+
+    def enable_object_deletion(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.ENABLE_OBJECT_DELETION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def disable_object_deletion(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.DISABLE_OBJECT_DELETION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def train_unit(
+            self,
+            quantity: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            location_x: int | None = None,
+            location_y: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.TRAIN_UNIT,
+            quantity=quantity,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            location_x=location_x,
+            location_y=location_y,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids
+        )
+
+    def initiate_research(
+            self,
+            source_player: int | None = None,
+            technology: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.INITIATE_RESEARCH,
+            source_player=source_player,
+            technology=technology,
+            selected_object_ids=selected_object_ids
+        )
+
+    def create_object_attack(
+            self,
+            armour_attack_quantity: int | None = None,
+            armour_attack_class: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            operation: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.CREATE_OBJECT_ATTACK,
+            armour_attack_quantity=armour_attack_quantity,
+            armour_attack_class=armour_attack_class,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            operation=operation,
+            selected_object_ids=selected_object_ids
+        )
+
+    def create_object_armor(
+            self,
+            armour_attack_quantity: int | None = None,
+            armour_attack_class: int | None = None,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            object_group: int | None = None,
+            object_type: int | None = None,
+            operation: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.CREATE_OBJECT_ARMOR,
+            armour_attack_quantity=armour_attack_quantity,
+            armour_attack_class=armour_attack_class,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            object_group=object_group,
+            object_type=object_type,
+            operation=operation,
+            selected_object_ids=selected_object_ids
+        )
+
+    def modify_attribute_by_variable(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            operation: int | None = None,
+            object_attributes: int | None = None,
+            variable: int | None = None,
+            message: str | None = None,
+            armour_attack_class: int | None = None,
+    ):
+        if armour_attack_class is not None and object_attributes not in (ObjectAttribute.ATTACK, ObjectAttribute.ARMOR):
+            raise ValueError("Cannot use 'armour_attack_class' for non attack/armor attributes.")
+
+        return self._trigger_ref._add_effect(
+            EffectId.MODIFY_ATTRIBUTE_BY_VARIABLE,
+            armour_attack_class=armour_attack_class,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            operation=operation,
+            object_attributes=object_attributes,
+            variable=variable,
+            message=message,
+        )
+
+    def set_object_cost(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            quantity: int | None = None,
+            tribute_list: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.SET_OBJECT_COST,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            quantity=quantity,
+            tribute_list=tribute_list,
+        )
+
+    def load_key_value(
+            self,
+            variable: int | None = None,
+            message: str | None = None,
+            quantity: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.LOAD_KEY_VALUE,
+            variable=variable,
+            message=message,
+            quantity=quantity,
+        )
+
+    def store_key_value(
+            self,
+            variable: int | None = None,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.STORE_KEY_VALUE,
+            variable=variable,
+            message=message,
+        )
+
+    def delete_key(
+            self,
+            message: str | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.DELETE_KEY,
+            message=message,
+        )
+
+    def change_technology_icon(
+            self,
+            technology: int | None = None,
+            source_player: int | None = None,
+            quantity: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_ICON,
+            technology=technology,
+            source_player=source_player,
+            quantity=quantity,
+        )
+
+    def change_technology_hotkey(
+            self,
+            technology: int | None = None,
+            source_player: int | None = None,
+            quantity: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_TECHNOLOGY_HOTKEY,
+            technology=technology,
+            source_player=source_player,
+            quantity=quantity,
+        )
+
+    def modify_variable_by_resource(
+            self,
+            tribute_list: int | None = None,
+            source_player: int | None = None,
+            operation: int | None = None,
+            variable: int | None = None,
+    ) -> Effect:
+        return self._trigger_ref._add_effect(
+            EffectId.MODIFY_VARIABLE_BY_RESOURCE,
+            tribute_list=tribute_list,
+            source_player=source_player,
+            operation=operation,
+            variable=variable,
+        )
+
+    def modify_variable_by_attribute(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            operation: int | None = None,
+            object_attributes: int | None = None,
+            variable: int | None = None,
+            message: str | None = None,
+            armour_attack_class: int | None = None,
+    ):
+        if armour_attack_class is not None and object_attributes not in (ObjectAttribute.ATTACK, ObjectAttribute.ARMOR):
+            raise ValueError("Cannot use 'armour_attack_class' for non attack/armor attributes.")
+
+        return self._trigger_ref._add_effect(
+            EffectId.MODIFY_VARIABLE_BY_ATTRIBUTE,
+            armour_attack_class=armour_attack_class,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            operation=operation,
+            object_attributes=object_attributes,
+            variable=variable,
+            message=message
+        )
+
+    def change_object_caption(
+            self,
+            object_list_unit_id: int | None = None,
+            source_player: int | None = None,
+            string_id: int | None = None,
+            message: str | None = None,
+            area_x1: int | None = None,
+            area_y1: int | None = None,
+            area_x2: int | None = None,
+            area_y2: int | None = None,
+            selected_object_ids: int | List[int] | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_OBJECT_CAPTION,
+            object_list_unit_id=object_list_unit_id,
+            source_player=source_player,
+            string_id=string_id,
+            message=message,
+            area_x1=area_x1,
+            area_y1=area_y1,
+            area_x2=area_x2,
+            area_y2=area_y2,
+            selected_object_ids=selected_object_ids,
+        )
+
+    def change_player_color(
+            self,
+            source_player: int | None = None,
+            player_color: int | None = None,
+    ):
+        return self._trigger_ref._add_effect(
+            EffectId.CHANGE_PLAYER_COLOR,
+            source_player=source_player,
+            player_color=player_color,
+        )
+
+
+ + + +
+ + + + + + + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_ref + ? + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
13
+14
def __init__(self, trigger_ref):
+    self._trigger_ref = trigger_ref
+
+
+
+ +
+ +
+ + +
+def acknowledge_ai_signal(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
ai_signal_value + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
def acknowledge_ai_signal(
+        self,
+        ai_signal_value: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ACKNOWLEDGE_AI_SIGNAL,
+        ai_signal_value=ai_signal_value,
+    )
+
+
+
+ +
+ +
+ + +
+def acknowledge_multiplayer_ai_signal(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
ai_signal_value + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
def acknowledge_multiplayer_ai_signal(
+        self,
+        ai_signal_value: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ACKNOWLEDGE_MULTIPLAYER_AI_SIGNAL,
+        ai_signal_value=ai_signal_value,
+    )
+
+
+
+ +
+ +
+ + +
+def activate_trigger(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_id + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
114
+115
+116
+117
+118
+119
+120
+121
def activate_trigger(
+        self,
+        trigger_id: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ACTIVATE_TRIGGER,
+        trigger_id=trigger_id,
+    )
+
+
+
+ +
+ +
+ + +
+def ai_script_goal(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
ai_script_goal + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
132
+133
+134
+135
+136
+137
+138
+139
def ai_script_goal(
+        self,
+        ai_script_goal: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.AI_SCRIPT_GOAL,
+        ai_script_goal=ai_script_goal,
+    )
+
+
+
+ +
+ +
+ + +
+def attack_move(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
location_object_reference + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
def attack_move(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        location_object_reference: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ATTACK_MOVE,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        location_object_reference=location_object_reference,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_civilization_name(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
string_id + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
def change_civilization_name(
+        self,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_CIVILIZATION_NAME,
+        source_player=source_player,
+        string_id=string_id,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def change_color_mood(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
color_mood + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
def change_color_mood(
+        self,
+        quantity: int | None = None,
+        color_mood: int | None = None):
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_COLOR_MOOD,
+        quantity=quantity,
+        color_mood=color_mood
+    )
+
+
+
+ +
+ +
+ + +
+def change_diplomacy(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
diplomacy + int | None + - + None +
source_player + int | None + - + None +
target_player + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
def change_diplomacy(
+        self,
+        diplomacy: int | None = None,
+        source_player: int | None = None,
+        target_player: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_DIPLOMACY,
+        diplomacy=diplomacy,
+        source_player=source_player,
+        target_player=target_player,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_armor(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
armour_attack_quantity + int | None + - + None +
armour_attack_class + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
operation + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
def change_object_armor(
+        self,
+        armour_attack_quantity: int | None = None,
+        armour_attack_class: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        operation: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_ARMOR,
+        armour_attack_quantity=armour_attack_quantity,
+        armour_attack_class=armour_attack_class,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        operation=operation,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_attack(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
armour_attack_quantity + int | None + - + None +
armour_attack_class + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
operation + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
def change_object_attack(
+        self,
+        armour_attack_quantity: int | None = None,
+        armour_attack_class: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        operation: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_ATTACK,
+        armour_attack_quantity=armour_attack_quantity,
+        armour_attack_class=armour_attack_class,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        operation=operation,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_caption(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
string_id + int | None + - + None +
message + str | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
def change_object_caption(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        message: str | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_CAPTION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        string_id=string_id,
+        message=message,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_civilization_name(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
string_id + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
message + str | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
def change_object_civilization_name(
+        self,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        message: str | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_CIVILIZATION_NAME,
+        source_player=source_player,
+        string_id=string_id,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        message=message,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_cost(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
resource_1 + int | None + - + None +
resource_1_quantity + int | None + - + None +
resource_2 + int | None + - + None +
resource_2_quantity + int | None + - + None +
resource_3 + int | None + - + None +
resource_3_quantity + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
def change_object_cost(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        resource_1: int | None = None,
+        resource_1_quantity: int | None = None,
+        resource_2: int | None = None,
+        resource_2_quantity: int | None = None,
+        resource_3: int | None = None,
+        resource_3_quantity: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_COST,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        resource_1=resource_1,
+        resource_1_quantity=resource_1_quantity,
+        resource_2=resource_2,
+        resource_2_quantity=resource_2_quantity,
+        resource_3=resource_3,
+        resource_3_quantity=resource_3_quantity,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_description(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
string_id + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
def change_object_description(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_DESCRIPTION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        string_id=string_id,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_hp(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
operation + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
def change_object_hp(
+        self,
+        quantity: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        operation: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_HP,
+        quantity=quantity,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        operation=operation,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_icon(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
object_list_unit_id_2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
def change_object_icon(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        object_list_unit_id_2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_ICON,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        object_list_unit_id_2=object_list_unit_id_2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_name(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
string_id + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
message + str | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
def change_object_name(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        message: str | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_NAME,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        string_id=string_id,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        message=message,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_player_color(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
player_color + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
def change_object_player_color(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        player_color: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_PLAYER_COLOR,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        player_color=player_color,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_player_name(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
string_id + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
message + str | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
def change_object_player_name(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        message: str | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_PLAYER_NAME,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        string_id=string_id,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        message=message,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_range(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
operation + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
def change_object_range(
+        self,
+        quantity: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        operation: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_RANGE,
+        quantity=quantity,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        operation=operation,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_speed(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
def change_object_speed(
+        self,
+        quantity: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_SPEED,
+        quantity=quantity,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_object_stance(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
attack_stance + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
def change_object_stance(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        attack_stance: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OBJECT_STANCE,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        attack_stance=attack_stance,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_ownership(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
target_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
flash_object + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
def change_ownership(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        target_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        flash_object: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_OWNERSHIP,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        target_player=target_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        flash_object=flash_object,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def change_player_color(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
player_color + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
def change_player_color(
+        self,
+        source_player: int | None = None,
+        player_color: int | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_PLAYER_COLOR,
+        source_player=source_player,
+        player_color=player_color,
+    )
+
+
+
+ +
+ +
+ + +
+def change_player_name(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
string_id + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
def change_player_name(
+        self,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_PLAYER_NAME,
+        source_player=source_player,
+        string_id=string_id,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def change_research_location(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
object_list_unit_id_2 + int | None + - + None +
button_location + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
@deprecated('Use `change_technology_location` instead')
+def change_research_location(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        object_list_unit_id_2: int | None = None,
+        button_location: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_LOCATION,
+        source_player=source_player,
+        technology=technology,
+        object_list_unit_id_2=object_list_unit_id_2,
+        button_location=button_location,
+    )
+
+
+
+ +
+ +
+ + +
+def change_technology_cost(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
resource_1 + int | None + - + None +
resource_1_quantity + int | None + - + None +
resource_2 + int | None + - + None +
resource_2_quantity + int | None + - + None +
resource_3 + int | None + - + None +
resource_3_quantity + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
def change_technology_cost(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        resource_1: int | None = None,
+        resource_1_quantity: int | None = None,
+        resource_2: int | None = None,
+        resource_2_quantity: int | None = None,
+        resource_3: int | None = None,
+        resource_3_quantity: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_COST,
+        source_player=source_player,
+        technology=technology,
+        resource_1=resource_1,
+        resource_1_quantity=resource_1_quantity,
+        resource_2=resource_2,
+        resource_2_quantity=resource_2_quantity,
+        resource_3=resource_3,
+        resource_3_quantity=resource_3_quantity,
+    )
+
+
+
+ +
+ +
+ + +
+def change_technology_description(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
string_id + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
def change_technology_description(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        string_id: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_DESCRIPTION,
+        source_player=source_player,
+        technology=technology,
+        string_id=string_id,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def change_technology_hotkey(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
technology + int | None + - + None +
source_player + int | None + - + None +
quantity + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
def change_technology_hotkey(
+        self,
+        technology: int | None = None,
+        source_player: int | None = None,
+        quantity: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_HOTKEY,
+        technology=technology,
+        source_player=source_player,
+        quantity=quantity,
+    )
+
+
+
+ +
+ +
+ + +
+def change_technology_icon(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
technology + int | None + - + None +
source_player + int | None + - + None +
quantity + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
def change_technology_icon(
+        self,
+        technology: int | None = None,
+        source_player: int | None = None,
+        quantity: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_ICON,
+        technology=technology,
+        source_player=source_player,
+        quantity=quantity,
+    )
+
+
+
+ +
+ +
+ + +
+def change_technology_location(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
object_list_unit_id_2 + int | None + - + None +
button_location + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
def change_technology_location(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        object_list_unit_id_2: int | None = None,
+        button_location: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_LOCATION,
+        source_player=source_player,
+        technology=technology,
+        object_list_unit_id_2=object_list_unit_id_2,
+        button_location=button_location,
+    )
+
+
+
+ +
+ +
+ + +
+def change_technology_name(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
string_id + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
def change_technology_name(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        string_id: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_NAME,
+        source_player=source_player,
+        technology=technology,
+        string_id=string_id,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def change_technology_research_time(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
source_player + int | None + - + None +
technology + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
def change_technology_research_time(
+        self,
+        quantity: int | None = None,
+        source_player: int | None = None,
+        technology: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TECHNOLOGY_RESEARCH_TIME,
+        quantity=quantity,
+        source_player=source_player,
+        technology=technology,
+    )
+
+
+
+ +
+ +
+ + +
+def change_train_location(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
object_list_unit_id_2 + int | None + - + None +
button_location + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
def change_train_location(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        object_list_unit_id_2: int | None = None,
+        button_location: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_TRAIN_LOCATION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        object_list_unit_id_2=object_list_unit_id_2,
+        button_location=button_location,
+    )
+
+
+
+ +
+ +
+ + +
+def change_variable(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
operation + int | None + - + None +
variable + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
def change_variable(
+        self,
+        quantity: int | None = None,
+        operation: int | None = None,
+        variable: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_VARIABLE,
+        quantity=quantity,
+        operation=operation,
+        variable=variable,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def change_view(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
scroll + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
def change_view(
+        self,
+        quantity: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        scroll: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CHANGE_VIEW,
+        quantity=quantity,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        scroll=scroll,
+    )
+
+
+
+ +
+ +
+ + +
+def clear_instructions(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
instruction_panel_position + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
383
+384
+385
+386
+387
+388
+389
+390
def clear_instructions(
+        self,
+        instruction_panel_position: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CLEAR_INSTRUCTIONS,
+        instruction_panel_position=instruction_panel_position,
+    )
+
+
+
+ +
+ +
+ + +
+def clear_timer(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
timer + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
def clear_timer(
+        self,
+        timer: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CLEAR_TIMER,
+        timer=timer,
+    )
+
+
+
+ +
+ +
+ + +
+def create_garrisoned_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_list_unit_id_2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
def create_garrisoned_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_list_unit_id_2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CREATE_GARRISONED_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_list_unit_id_2=object_list_unit_id_2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def create_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
facet + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
def create_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        facet: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.CREATE_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        facet=facet,
+    )
+
+
+
+ +
+ +
+ + +
+def create_object_armor(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
armour_attack_quantity + int | None + - + None +
armour_attack_class + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
operation + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
def create_object_armor(
+        self,
+        armour_attack_quantity: int | None = None,
+        armour_attack_class: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        operation: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.CREATE_OBJECT_ARMOR,
+        armour_attack_quantity=armour_attack_quantity,
+        armour_attack_class=armour_attack_class,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        operation=operation,
+        selected_object_ids=selected_object_ids
+    )
+
+
+
+ +
+ +
+ + +
+def create_object_attack(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
armour_attack_quantity + int | None + - + None +
armour_attack_class + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
operation + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
def create_object_attack(
+        self,
+        armour_attack_quantity: int | None = None,
+        armour_attack_class: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        operation: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.CREATE_OBJECT_ATTACK,
+        armour_attack_quantity=armour_attack_quantity,
+        armour_attack_class=armour_attack_class,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        operation=operation,
+        selected_object_ids=selected_object_ids
+    )
+
+
+
+ +
+ +
+ + +
+def damage_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
def damage_object(
+        self,
+        quantity: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DAMAGE_OBJECT,
+        quantity=quantity,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def deactivate_trigger(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_id + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
123
+124
+125
+126
+127
+128
+129
+130
def deactivate_trigger(
+        self,
+        trigger_id: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DEACTIVATE_TRIGGER,
+        trigger_id=trigger_id,
+    )
+
+
+
+ +
+ +
+ + +
+def declare_victory(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
enabled + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
191
+192
+193
+194
+195
+196
+197
+198
+199
+200
def declare_victory(
+        self,
+        source_player: int | None = None,
+        enabled: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DECLARE_VICTORY,
+        source_player=source_player,
+        enabled=enabled,
+    )
+
+
+
+ +
+ +
+ + +
+def delete_key(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
def delete_key(
+        self,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DELETE_KEY,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def disable_object_deletion(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
def disable_object_deletion(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.DISABLE_OBJECT_DELETION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def disable_object_selection(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
def disable_object_selection(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DISABLE_OBJECT_SELECTION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def disable_technology_stacking(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
def disable_technology_stacking(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DISABLE_TECHNOLOGY_STACKING,
+        source_player=source_player,
+        technology=technology,
+    )
+
+
+
+ +
+ +
+ + +
+def disable_unit_targeting(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
def disable_unit_targeting(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DISABLE_UNIT_TARGETING,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def display_instructions(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
string_id + int | None + - + None +
display_time + int | None + - + None +
instruction_panel_position + int | None + - + None +
play_sound + int | None + - + None +
message + str | None + - + None +
sound_name + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
def display_instructions(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        display_time: int | None = None,
+        instruction_panel_position: int | None = None,
+        play_sound: int | None = None,
+        message: str | None = None,
+        sound_name: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DISPLAY_INSTRUCTIONS,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        string_id=string_id,
+        display_time=display_time,
+        instruction_panel_position=instruction_panel_position,
+        play_sound=play_sound,
+        message=message,
+        sound_name=sound_name,
+    )
+
+
+
+ +
+ +
+ + +
+def display_timer(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
string_id + int | None + - + None +
display_time + int | None + - + None +
time_unit + int | None + - + None +
timer + int | None + - + None +
reset_timer + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
def display_timer(
+        self,
+        string_id: int | None = None,
+        display_time: int | None = None,
+        time_unit: int | None = None,
+        timer: int | None = None,
+        reset_timer: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.DISPLAY_TIMER,
+        string_id=string_id,
+        display_time=display_time,
+        time_unit=time_unit,
+        timer=timer,
+        reset_timer=reset_timer,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def enable_disable_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
enabled + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
def enable_disable_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        enabled: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ENABLE_DISABLE_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        enabled=enabled,
+    )
+
+
+
+ +
+ +
+ + +
+def enable_disable_technology(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
enabled + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
def enable_disable_technology(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        enabled: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ENABLE_DISABLE_TECHNOLOGY,
+        source_player=source_player,
+        technology=technology,
+        enabled=enabled,
+    )
+
+
+
+ +
+ +
+ + +
+def enable_object_deletion(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
def enable_object_deletion(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.ENABLE_OBJECT_DELETION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def enable_object_selection(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
def enable_object_selection(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ENABLE_OBJECT_SELECTION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def enable_technology_stacking(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
quantity + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
def enable_technology_stacking(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        quantity: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ENABLE_TECHNOLOGY_STACKING,
+        source_player=source_player,
+        technology=technology,
+        quantity=quantity,
+    )
+
+
+
+ +
+ +
+ + +
+def enable_unit_targeting(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
def enable_unit_targeting(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.ENABLE_UNIT_TARGETING,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def freeze_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
def freeze_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.FREEZE_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def heal_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
def heal_object(
+        self,
+        quantity: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.HEAL_OBJECT,
+        quantity=quantity,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def initiate_research(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
def initiate_research(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.INITIATE_RESEARCH,
+        source_player=source_player,
+        technology=technology,
+        selected_object_ids=selected_object_ids
+    )
+
+
+
+ +
+ +
+ + +
+def kill_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
def kill_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.KILL_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def load_key_value(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
variable + int | None + - + None +
message + str | None + - + None +
quantity + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
def load_key_value(
+        self,
+        variable: int | None = None,
+        message: str | None = None,
+        quantity: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.LOAD_KEY_VALUE,
+        variable=variable,
+        message=message,
+        quantity=quantity,
+    )
+
+
+
+ +
+ +
+ + +
+def lock_gate(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
105
+106
+107
+108
+109
+110
+111
+112
def lock_gate(
+        self,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.LOCK_GATE,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def modify_attribute(...) + +
+ + +
+ +

The parameters 'armour_attack_quantity' and 'armour_attack_class' are only used when object_attributes is Armor +or Attack (8 or 9). Use, 'quantity' otherwise.

+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
armour_attack_quantity + int | None + - + None +
armour_attack_class + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
operation + int | None + - + None +
object_attributes + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
def modify_attribute(
+        self,
+        quantity: int | None = None,
+        armour_attack_quantity: int | None = None,
+        armour_attack_class: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        operation: int | None = None,
+        object_attributes: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    """
+    The parameters 'armour_attack_quantity' and 'armour_attack_class' are only used when object_attributes is Armor
+    or Attack (8 or 9). Use, 'quantity' otherwise.
+    """
+    if (armour_attack_quantity is not None or armour_attack_class is not None) and quantity is not None:
+        raise ValueError("Cannot use 'armour_attack' attributes together with the 'quantity' attribute.")
+
+    return self._trigger_ref._add_effect(
+        EffectId.MODIFY_ATTRIBUTE,
+        quantity=quantity,
+        armour_attack_quantity=armour_attack_quantity,
+        armour_attack_class=armour_attack_class,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        operation=operation,
+        object_attributes=object_attributes,
+        message=message
+    )
+
+
+
+ +
+ +
+ + +
+def modify_attribute_by_variable(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
operation + int | None + - + None +
object_attributes + int | None + - + None +
variable + int | None + - + None +
message + str | None + - + None +
armour_attack_class + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
def modify_attribute_by_variable(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        operation: int | None = None,
+        object_attributes: int | None = None,
+        variable: int | None = None,
+        message: str | None = None,
+        armour_attack_class: int | None = None,
+):
+    if armour_attack_class is not None and object_attributes not in (ObjectAttribute.ATTACK, ObjectAttribute.ARMOR):
+        raise ValueError("Cannot use 'armour_attack_class' for non attack/armor attributes.")
+
+    return self._trigger_ref._add_effect(
+        EffectId.MODIFY_ATTRIBUTE_BY_VARIABLE,
+        armour_attack_class=armour_attack_class,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        operation=operation,
+        object_attributes=object_attributes,
+        variable=variable,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def modify_resource(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
tribute_list + int | None + - + None +
source_player + int | None + - + None +
operation + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
def modify_resource(
+        self,
+        quantity: int | None = None,
+        tribute_list: int | None = None,
+        source_player: int | None = None,
+        operation: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.MODIFY_RESOURCE,
+        quantity=quantity,
+        tribute_list=tribute_list,
+        source_player=source_player,
+        operation=operation,
+    )
+
+
+
+ +
+ +
+ + +
+def modify_resource_by_variable(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tribute_list + int | None + - + None +
source_player + int | None + - + None +
operation + int | None + - + None +
variable + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
def modify_resource_by_variable(
+        self,
+        tribute_list: int | None = None,
+        source_player: int | None = None,
+        operation: int | None = None,
+        variable: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.MODIFY_RESOURCE_BY_VARIABLE,
+        tribute_list=tribute_list,
+        source_player=source_player,
+        operation=operation,
+        variable=variable,
+    )
+
+
+
+ +
+ +
+ + +
+def modify_variable_by_attribute(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
operation + int | None + - + None +
object_attributes + int | None + - + None +
variable + int | None + - + None +
message + str | None + - + None +
armour_attack_class + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
def modify_variable_by_attribute(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        operation: int | None = None,
+        object_attributes: int | None = None,
+        variable: int | None = None,
+        message: str | None = None,
+        armour_attack_class: int | None = None,
+):
+    if armour_attack_class is not None and object_attributes not in (ObjectAttribute.ATTACK, ObjectAttribute.ARMOR):
+        raise ValueError("Cannot use 'armour_attack_class' for non attack/armor attributes.")
+
+    return self._trigger_ref._add_effect(
+        EffectId.MODIFY_VARIABLE_BY_ATTRIBUTE,
+        armour_attack_class=armour_attack_class,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        operation=operation,
+        object_attributes=object_attributes,
+        variable=variable,
+        message=message
+    )
+
+
+
+ +
+ +
+ + +
+def modify_variable_by_resource(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tribute_list + int | None + - + None +
source_player + int | None + - + None +
operation + int | None + - + None +
variable + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
def modify_variable_by_resource(
+        self,
+        tribute_list: int | None = None,
+        source_player: int | None = None,
+        operation: int | None = None,
+        variable: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.MODIFY_VARIABLE_BY_RESOURCE,
+        tribute_list=tribute_list,
+        source_player=source_player,
+        operation=operation,
+        variable=variable,
+    )
+
+
+
+ +
+ +
+ + +
+def none(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
16
+17
+18
+19
+20
+21
def none(
+        self,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.NONE,
+    )
+
+
+
+ +
+ +
+ + +
+def patrol(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
def patrol(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.PATROL,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def place_foundation(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
def place_foundation(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.PLACE_FOUNDATION,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+    )
+
+
+
+ +
+ +
+ + +
+def play_sound(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
location_object_reference + int | None + - + None +
sound_name + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
def play_sound(
+        self,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        location_object_reference: int | None = None,
+        sound_name: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.PLAY_SOUND,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        location_object_reference=location_object_reference,
+        sound_name=sound_name,
+    )
+
+
+
+ +
+ +
+ + +
+def remove_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
object_state + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
def remove_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        object_state: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.REMOVE_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        object_state=object_state,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def replace_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
target_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
object_list_unit_id_2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
def replace_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        target_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        object_list_unit_id_2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.REPLACE_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        target_player=target_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        object_list_unit_id_2=object_list_unit_id_2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def research_technology(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
technology + int | None + - + None +
force_research_technology + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
def research_technology(
+        self,
+        source_player: int | None = None,
+        technology: int | None = None,
+        force_research_technology: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.RESEARCH_TECHNOLOGY,
+        source_player=source_player,
+        technology=technology,
+        force_research_technology=force_research_technology,
+    )
+
+
+
+ +
+ +
+ + +
+def script_call(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
string_id + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
def script_call(
+        self,
+        string_id: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.SCRIPT_CALL,
+        string_id=string_id,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def send_chat(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
string_id + int | None + - + None +
message + str | None + - + None +
sound_name + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
def send_chat(
+        self,
+        source_player: int | None = None,
+        string_id: int | None = None,
+        message: str | None = None,
+        sound_name: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.SEND_CHAT,
+        source_player=source_player,
+        string_id=string_id,
+        message=message,
+        sound_name=sound_name,
+    )
+
+
+
+ +
+ +
+ + +
+def set_building_gather_point(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
def set_building_gather_point(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.SET_BUILDING_GATHER_POINT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def set_object_cost(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
quantity + int | None + - + None +
tribute_list + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
def set_object_cost(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        quantity: int | None = None,
+        tribute_list: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.SET_OBJECT_COST,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        quantity=quantity,
+        tribute_list=tribute_list,
+    )
+
+
+
+ +
+ +
+ + +
+def set_player_visibility(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
source_player + int | None + - + None +
target_player + int | None + - + None +
visibility_state + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
def set_player_visibility(
+        self,
+        source_player: int | None = None,
+        target_player: int | None = None,
+        visibility_state: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.SET_PLAYER_VISIBILITY,
+        source_player=source_player,
+        target_player=target_player,
+        visibility_state=visibility_state,
+    )
+
+
+
+ +
+ +
+ + +
+def stop_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
def stop_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.STOP_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def store_key_value(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
variable + int | None + - + None +
message + str | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
def store_key_value(
+        self,
+        variable: int | None = None,
+        message: str | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.STORE_KEY_VALUE,
+        variable=variable,
+        message=message,
+    )
+
+
+
+ +
+ +
+ + +
+def task_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
location_object_reference + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
action_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
def task_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        location_object_reference: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        action_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.TASK_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        location_object_reference=location_object_reference,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        action_type=action_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def teleport_object(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
def teleport_object(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.TELEPORT_OBJECT,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def train_unit(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
def train_unit(
+        self,
+        quantity: int | None = None,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+):
+    return self._trigger_ref._add_effect(
+        EffectId.TRAIN_UNIT,
+        quantity=quantity,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        selected_object_ids=selected_object_ids
+    )
+
+
+
+ +
+ +
+ + +
+def tribute(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
quantity + int | None + - + None +
tribute_list + int | None + - + None +
source_player + int | None + - + None +
target_player + int | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
def tribute(
+        self,
+        quantity: int | None = None,
+        tribute_list: int | None = None,
+        source_player: int | None = None,
+        target_player: int | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.TRIBUTE,
+        quantity=quantity,
+        tribute_list=tribute_list,
+        source_player=source_player,
+        target_player=target_player,
+    )
+
+
+
+ +
+ +
+ + +
+def unload(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
object_list_unit_id + int | None + - + None +
source_player + int | None + - + None +
location_x + int | None + - + None +
location_y + int | None + - + None +
location_object_reference + int | None + - + None +
area_x1 + int | None + - + None +
area_y1 + int | None + - + None +
area_x2 + int | None + - + None +
area_y2 + int | None + - + None +
object_group + int | None + - + None +
object_type + int | None + - + None +
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
def unload(
+        self,
+        object_list_unit_id: int | None = None,
+        source_player: int | None = None,
+        location_x: int | None = None,
+        location_y: int | None = None,
+        location_object_reference: int | None = None,
+        area_x1: int | None = None,
+        area_y1: int | None = None,
+        area_x2: int | None = None,
+        area_y2: int | None = None,
+        object_group: int | None = None,
+        object_type: int | None = None,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.UNLOAD,
+        object_list_unit_id=object_list_unit_id,
+        source_player=source_player,
+        location_x=location_x,
+        location_y=location_y,
+        location_object_reference=location_object_reference,
+        area_x1=area_x1,
+        area_y1=area_y1,
+        area_x2=area_x2,
+        area_y2=area_y2,
+        object_group=object_group,
+        object_type=object_type,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def unlock_gate(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
selected_object_ids + int | List[int] | None + - + None +
+ +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
def unlock_gate(
+        self,
+        selected_object_ids: int | List[int] | None = None,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.UNLOCK_GATE,
+        selected_object_ids=selected_object_ids,
+    )
+
+
+
+ +
+ +
+ + +
+def use_advanced_buttons(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/support/new_effect.py +
417
+418
+419
+420
+421
+422
def use_advanced_buttons(
+        self,
+) -> Effect:
+    return self._trigger_ref._add_effect(
+        EffectId.USE_ADVANCED_BUTTONS,
+    )
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/trigger/index.html b/api_docs/trigger/trigger/index.html new file mode 100644 index 00000000..89293006 --- /dev/null +++ b/api_docs/trigger/trigger/index.html @@ -0,0 +1,5383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Trigger - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Trigger

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ Trigger + + +

+ + +
+

+ Bases: AoE2Object, TriggerComponent

+ + +

Object for handling a trigger.

+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
class Trigger(AoE2Object, TriggerComponent):
+    """Object for handling a trigger."""
+
+    _link_list = [
+        RetrieverObjectLinkGroup("Triggers", "trigger_data[__index__]", group=[
+            RetrieverObjectLink("name", link="trigger_name"),
+            RetrieverObjectLink("description", link="trigger_description"),
+            RetrieverObjectLink("description_stid", link="description_string_table_id"),
+            RetrieverObjectLink("display_as_objective"),
+            RetrieverObjectLink("short_description"),
+            RetrieverObjectLink("short_description_stid", link="short_description_string_table_id"),
+            RetrieverObjectLink("display_on_screen"),
+            RetrieverObjectLink("description_order", link="objective_description_order"),
+            RetrieverObjectLink("enabled"),
+            RetrieverObjectLink("looping"),
+            RetrieverObjectLink("header", link="make_header"),
+            RetrieverObjectLink("mute_objectives"),
+            RetrieverObjectLink("conditions", link="condition_data", process_as_object=Condition),
+            RetrieverObjectLink("condition_order", link="condition_display_order_array"),
+            RetrieverObjectLink("effects", link="effect_data", process_as_object=Effect),
+            RetrieverObjectLink("effect_order", link="effect_display_order_array"),
+        ]),
+        RetrieverObjectLink("trigger_id", retrieve_history_number=0),
+    ]
+
+    def __init__(
+            self,
+            name: str,
+            description: str = "",
+            description_stid: int = -1,
+            display_as_objective: int = 0,
+            short_description: str = "",
+            short_description_stid: int = -1,
+            display_on_screen: int = 0,
+            description_order: int = 0,
+            enabled: int = 1,
+            looping: int = 0,
+            header: int = 0,
+            mute_objectives: int = 0,
+            conditions: List[Condition] = None,
+            condition_order: List[int] = None,
+            effects: List[Effect] = None,
+            effect_order: List[int] = None,
+            trigger_id: int = -1,
+            **kwargs
+    ):
+        super().__init__(**kwargs)
+
+        if conditions is None:
+            conditions = []
+        if condition_order is None:
+            condition_order = []
+        if effects is None:
+            effects = []
+        if effect_order is None:
+            effect_order = []
+
+        self.name: str = name
+        self.description: str = description
+        self.description_stid: int = description_stid
+        self.display_as_objective: int = display_as_objective
+        self.short_description: str = short_description
+        self.short_description_stid: int = short_description_stid
+        self.display_on_screen: int = display_on_screen
+        self.description_order: int = description_order
+        self.enabled: int = enabled
+        self.looping: int = looping
+        self.header: int = header
+        self.mute_objectives: int = mute_objectives
+        self._condition_hash = hash_list(conditions)
+        self.conditions: List[Condition] = conditions
+        self.condition_order: List[int] = condition_order
+        self._effect_hash = hash_list(effects)
+        self.effects: List[Effect] = effects
+        self.effect_order: List[int] = effect_order
+        self.trigger_id: int = trigger_id
+
+        self.new_effect: NewEffectSupport = NewEffectSupport(self)
+        self.new_condition: NewConditionSupport = NewConditionSupport(self)
+
+        self._assign_new_ce_support()
+
+    def _deepcopy_entry(self, k, v) -> Any:
+        if k in ['new_effect', 'new_condition']:
+            return None
+        else:
+            return super()._deepcopy_entry(k, v)
+
+    def _assign_new_ce_support(self):
+        """Assigns new `new_effect` and `new_condition` objects to this trigger"""
+        self.new_effect = NewEffectSupport(self)
+        self.new_condition = NewConditionSupport(self)
+
+    @property
+    def condition_order(self) -> List[int]:
+        """The condition display order """
+        if list_changed(self.conditions, self._condition_hash):
+            update_order_array(self._condition_order, len(self.conditions))
+            self._condition_hash = hash_list(self.conditions)
+        return self._condition_order
+
+    @condition_order.setter
+    def condition_order(self, val):
+        self._condition_order = val
+
+    @property
+    def effect_order(self) -> List[int]:
+        """The effect display order """
+        if list_changed(self.effects, self._effect_hash):
+            update_order_array(self._effect_order, len(self.effects))
+            self._effect_hash = hash_list(self.effects)
+        return self._effect_order
+
+    @effect_order.setter
+    def effect_order(self, val):
+        self._effect_order = val
+
+    @property
+    def conditions(self) -> List[Condition]:
+        """All conditions in this trigger"""
+        return self._conditions
+
+    @conditions.setter
+    def conditions(self, val: List[Condition]) -> None:
+        self._conditions = UuidList(self._uuid, val)
+        self.condition_order = list(range(0, len(val)))
+
+    @property
+    def effects(self) -> List[Effect]:
+        """All effects in this trigger"""
+        return self._effects
+
+    @effects.setter
+    def effects(self, val: List[Effect]) -> None:
+        self._effects = UuidList(self._uuid, val)
+        self.effect_order = list(range(0, len(val)))
+
+    def _add_effect(self, effect_type: EffectId, ai_script_goal=None, armour_attack_quantity=None,
+                    armour_attack_class=None, quantity=None, tribute_list=None, diplomacy=None,
+                    object_list_unit_id=None, source_player=None, target_player=None, technology=None, string_id=None,
+                    display_time=None, trigger_id=None, location_x=None, location_y=None,
+                    location_object_reference=None, area_x1=None, area_y1=None, area_x2=None, area_y2=None,
+                    object_group=None, object_type=None, instruction_panel_position=None, attack_stance=None,
+                    time_unit=None, enabled=None, food=None, wood=None, stone=None, gold=None, item_id=None,
+                    flash_object=None, force_research_technology=None, visibility_state=None, scroll=None,
+                    operation=None, object_list_unit_id_2=None, button_location=None, ai_signal_value=None,
+                    object_attributes=None, variable=None, timer=None, facet=None, play_sound=None, message=None,
+                    player_color=None, sound_name=None, selected_object_ids=None, color_mood=None, reset_timer=None,
+                    object_state=None, action_type=None, resource_1=None, resource_1_quantity=None, resource_2=None,
+                    resource_2_quantity=None, resource_3=None, resource_3_quantity=None, unused_string_1=None,
+                    unused_string_2=None, ) -> Effect:
+        """Used to add new effect to trigger. Please use trigger.new_effect.<effect_name> instead"""
+
+        def get_default_effect_attributes(eff_type):
+            """Gets the default effect attributes based on a certain effect type, with exception handling"""
+            sv = getters.get_scenario_version(self._uuid)
+            try:
+                # Get None defaults and overwrite them with effect specific defaults
+                none_default = effect_dataset.default_attributes[0]
+                effect_default = effect_dataset.default_attributes[eff_type]
+
+                return {**none_default, **effect_default}
+            except KeyError:
+                effect = EffectId(eff_type)
+                raise UnsupportedAttributeError(
+                    f"The effect {effect.name} is not supported in scenario version {sv}"
+                ) from None
+
+        effect_defaults = get_default_effect_attributes(effect_type)
+        effect_attr = {}
+        for key, value in effect_defaults.items():
+            effect_attr[key] = (locals()[key] if locals()[key] is not None else value)
+        new_effect = Effect(**effect_attr, uuid=self._uuid)
+        self.effects.append(new_effect)
+        return new_effect
+
+    def _add_condition(self, condition_type: ConditionId, quantity=None,
+                       attribute=None, unit_object=None, next_object=None, object_list=None,
+                       source_player=None, technology=None, timer=None, area_x1=None, area_y1=None, area_x2=None,
+                       area_y2=None, object_group=None, object_type=None, ai_signal=None, inverted=None, variable=None,
+                       comparison=None, target_player=None, unit_ai_action=None, xs_function=None, object_state=None,
+                       timer_id=None, victory_timer_type=None, include_changeable_weapon_objects=None,
+                       ) -> Condition:
+        """Used to add new condition to trigger. Please use trigger.new_condition.<condition_name> instead"""
+
+        def get_default_condition_attributes(cond_type):
+            """Gets the default condition attributes based on a certain condition type, with exception handling"""
+            sv = getters.get_scenario_version(self._uuid)
+            try:
+                # Get None defaults and overwrite them with condition specific defaults
+                none_default = condition_dataset.default_attributes[0]
+                condition_default = condition_dataset.default_attributes[cond_type]
+
+                return {**none_default, **condition_default}
+            except KeyError:
+                condition = ConditionId(cond_type)
+                raise UnsupportedAttributeError(
+                    f"The condition {condition.name} is not supported in scenario version {sv}"
+                ) from None
+
+        condition_defaults = get_default_condition_attributes(condition_type)
+        condition_attr = {}
+        for key, value in condition_defaults.items():
+            condition_attr[key] = (locals()[key] if locals()[key] is not None else value)
+        new_condition = Condition(**condition_attr, uuid=self._uuid)
+        self.conditions.append(new_condition)
+        return new_condition
+
+    def get_effect(self, effect_index: int = None, display_index: int = None) -> Effect:
+        """
+        Retrieve an effect based on its (display) index
+
+        Args:
+            effect_index: The effect index of the effect to retrieve
+            display_index: The display index of the effect to retrieve
+
+        Raises:
+            ValueError: If more than one parameter is used simultaneously
+            IndexError: If the effect could not be found
+
+        Returns:
+            The wanted effect
+        """
+        if not mutually_exclusive(effect_index is not None, display_index is not None):
+            raise ValueError(f"Please identify an effect using either effect_index or display_index.")
+
+        if effect_index is None:
+            effect_index = self.effect_order[display_index]
+
+        return self.effects[effect_index]
+
+    def get_condition(self, condition_index: int = None, display_index: int = None) -> Condition:
+        """
+        Retrieve a condition based on its (display) index
+
+        Args:
+            condition_index: The condition index of the condition to retrieve
+            display_index: The display index of the condition to retrieve
+
+        Raises:
+            ValueError: If more than one parameter is used simultaneously
+            IndexError: If the condition could not be found
+
+        Returns:
+            The wanted condition
+        """
+        if not mutually_exclusive(condition_index is not None, display_index is not None):
+            raise ValueError(f"Please identify a condition using either condition_index or display_index.")
+
+        if condition_index is None:
+            condition_index = self.condition_order[display_index]
+
+        return self.conditions[condition_index]
+
+    def remove_effect(self, effect_index: int = None, display_index: int = None, effect: Effect = None) -> None:
+        """
+        Remove an effect from this trigger
+
+        Args:
+            effect_index: The effect index of the condition to remove
+            display_index: The display index of the condition to remove
+            effect: A reference to the effect object to remove from this trigger
+
+        Raises:
+            ValueError: If more than one parameter is used simultaneously
+        """
+        if not mutually_exclusive(effect_index is not None, display_index is not None, effect is not None):
+            raise ValueError(f"Please identify an effect using either effect_index, display_index or effect.")
+
+        if effect is not None:
+            effect_index = self.effects.index(effect)
+        if effect_index is None:
+            effect_index = self.effect_order[display_index]
+
+        del self.effects[effect_index]
+
+    def remove_condition(self, condition_index: int = None, display_index: int = None, condition: Condition = None) \
+            -> None:
+        """
+        Remove a condition from this trigger
+
+        Args:
+            condition_index: The condition index of the condition to remove
+            display_index: The display index of the condition to remove
+            condition: A reference to the condition object to remove from this trigger
+
+        Raises:
+            ValueError: If more than one parameter is used simultaneously
+        """
+        if not mutually_exclusive(condition_index is not None, display_index is not None, condition is not None):
+            raise ValueError(f"Please identify a condition using either condition_index, display_index or condition.")
+
+        if condition is not None:
+            condition_index = self.conditions.index(condition)
+
+        if condition_index is None:
+            condition_index = self.condition_order[display_index]
+
+        del self.conditions[condition_index]
+
+    def get_content_as_string(self, include_trigger_definition: bool = False) -> str:
+        """
+        Create a human-readable string showcasing all content of this trigger.
+        This includes all the content within the conditions and effects of this trigger.
+
+        This is also the function that is called when doing: `print(trigger)`
+
+        Args:
+            include_trigger_definition: If the trigger meta-data should be added by this function
+
+        Returns:
+            The created string
+        """
+        return_string = ""
+
+        data_tba = {
+            'enabled': self.enabled != 0,
+            'looping': self.looping != 0
+        }
+
+        if self.description != "":
+            data_tba['description'] = f"'{self.description}'"
+        if self.description_stid != -1:
+            data_tba['description_stid'] = self.description_stid
+        if self.short_description != "":
+            data_tba['short_description'] = f"'{self.short_description}'"
+        if self.short_description_stid != -1:
+            data_tba['short_description_stid'] = self.short_description_stid
+        if self.display_as_objective != 0:
+            data_tba['display_as_objective'] = (self.display_as_objective != 0)
+        if self.display_on_screen != 0:
+            data_tba['display_on_screen'] = (self.display_on_screen != 0)
+        if self.description_order != 0:
+            data_tba['description_order'] = self.description_order
+        if self.header != 0:
+            data_tba['header'] = (self.header != 0)
+        if self.mute_objectives != 0:
+            data_tba['mute_objectives'] = (self.mute_objectives != 0)
+
+        for key, value in data_tba.items():
+            return_string += f"{key}: {value}\n"
+
+        if len(self.condition_order) > 0:
+            return_string += "conditions:\n"
+            for c_display_order, condition_id in enumerate(self.condition_order):
+                condition = self.conditions[condition_id]
+
+                name = f"Unknown ({condition.condition_type})"
+                if condition.condition_type in condition_dataset.condition_names:
+                    name = condition_dataset.condition_names[condition.condition_type]
+
+                return_string += f"\t{name} [Index: {condition_id}, Display: {c_display_order}]:\n"
+                return_string += add_tabs(condition.get_content_as_string(), 2)
+
+        if len(self.effect_order) > 0:
+            return_string += "effects:\n"
+            for e_display_order, effect_id in enumerate(self.effect_order):
+                effect = self.effects[effect_id]
+
+                name = f"Unknown ({effect.effect_type})"
+                if effect.effect_type in effect_dataset.effect_names:
+                    name = effect_dataset.effect_names[effect.effect_type]
+
+                return_string += f"\t{name} [Index: {effect_id}, Display: {e_display_order}]:\n"
+                return_string += add_tabs(effect.get_content_as_string(), 2)
+
+        if include_trigger_definition:
+            return f"\"{self.name}\" [Index: {self.trigger_id}]\n" + add_tabs(return_string, 1)
+        return return_string
+
+    def __str__(self) -> str:
+        return f"[Trigger] {self.get_content_as_string(include_trigger_definition=True)}"
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+condition_order: List[int] + + + property + writable + + +
+ +
+ Type: +List[int] + +
+ + + + + +
+ +

The condition display order

+
+ +
+ +
+ + + + +
+conditions: List[Condition] + + + property + writable + + +
+ +
+ Type: +List[Condition] + +
+ + + + + +
+ +

All conditions in this trigger

+
+ +
+ +
+ + + + +
+description: str = description + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +description
+ + + + + +
+
+ +
+ +
+ + + + +
+description_order: int = description_order + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +description_order
+ + + + + +
+
+ +
+ +
+ + + + +
+description_stid: int = description_stid + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +description_stid
+ + + + + +
+
+ +
+ +
+ + + + +
+display_as_objective: int = display_as_objective + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +display_as_objective
+ + + + + +
+
+ +
+ +
+ + + + +
+display_on_screen: int = display_on_screen + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +display_on_screen
+ + + + + +
+
+ +
+ +
+ + + + +
+effect_order: List[int] + + + property + writable + + +
+ +
+ Type: +List[int] + +
+ + + + + +
+ +

The effect display order

+
+ +
+ +
+ + + + +
+effects: List[Effect] + + + property + writable + + +
+ +
+ Type: +List[Effect] + +
+ + + + + +
+ +

All effects in this trigger

+
+ +
+ +
+ + + + +
+enabled: int = enabled + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +enabled
+ + + + + +
+
+ +
+ +
+ + + + +
+header: int = header + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +header
+ + + + + +
+
+ +
+ +
+ + + + +
+looping: int = looping + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +looping
+ + + + + +
+
+ +
+ +
+ + + + +
+mute_objectives: int = mute_objectives + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +mute_objectives
+ + + + + +
+
+ +
+ +
+ + + + +
+name: str = name + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +name
+ + + + + +
+
+ +
+ +
+ + + + +
+new_condition: NewConditionSupport = NewConditionSupport(self) + + + instance-attribute + + +
+ +
+ Type: +NewConditionSupport +
+ + Value: +NewConditionSupport(self)
+ + + + + +
+
+ +
+ +
+ + + + +
+new_effect: NewEffectSupport = NewEffectSupport(self) + + + instance-attribute + + +
+ +
+ Type: +NewEffectSupport +
+ + Value: +NewEffectSupport(self)
+ + + + + +
+
+ +
+ +
+ + + + +
+short_description: str = short_description + + + instance-attribute + + +
+ +
+ Type: +str +
+ + Value: +short_description
+ + + + + +
+
+ +
+ +
+ + + + +
+short_description_stid: int = short_description_stid + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +short_description_stid
+ + + + + +
+
+ +
+ +
+ + + + +
+trigger_id: int = trigger_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +trigger_id
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + - + required +
description + str + - + '' +
description_stid + int + - + -1 +
display_as_objective + int + - + 0 +
short_description + str + - + '' +
short_description_stid + int + - + -1 +
display_on_screen + int + - + 0 +
description_order + int + - + 0 +
enabled + int + - + 1 +
looping + int + - + 0 +
header + int + - + 0 +
mute_objectives + int + - + 0 +
conditions + List[Condition] + - + None +
condition_order + List[int] + - + None +
effects + List[Effect] + - + None +
effect_order + List[int] + - + None +
trigger_id + int + - + -1 +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
 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
def __init__(
+        self,
+        name: str,
+        description: str = "",
+        description_stid: int = -1,
+        display_as_objective: int = 0,
+        short_description: str = "",
+        short_description_stid: int = -1,
+        display_on_screen: int = 0,
+        description_order: int = 0,
+        enabled: int = 1,
+        looping: int = 0,
+        header: int = 0,
+        mute_objectives: int = 0,
+        conditions: List[Condition] = None,
+        condition_order: List[int] = None,
+        effects: List[Effect] = None,
+        effect_order: List[int] = None,
+        trigger_id: int = -1,
+        **kwargs
+):
+    super().__init__(**kwargs)
+
+    if conditions is None:
+        conditions = []
+    if condition_order is None:
+        condition_order = []
+    if effects is None:
+        effects = []
+    if effect_order is None:
+        effect_order = []
+
+    self.name: str = name
+    self.description: str = description
+    self.description_stid: int = description_stid
+    self.display_as_objective: int = display_as_objective
+    self.short_description: str = short_description
+    self.short_description_stid: int = short_description_stid
+    self.display_on_screen: int = display_on_screen
+    self.description_order: int = description_order
+    self.enabled: int = enabled
+    self.looping: int = looping
+    self.header: int = header
+    self.mute_objectives: int = mute_objectives
+    self._condition_hash = hash_list(conditions)
+    self.conditions: List[Condition] = conditions
+    self.condition_order: List[int] = condition_order
+    self._effect_hash = hash_list(effects)
+    self.effects: List[Effect] = effects
+    self.effect_order: List[int] = effect_order
+    self.trigger_id: int = trigger_id
+
+    self.new_effect: NewEffectSupport = NewEffectSupport(self)
+    self.new_condition: NewConditionSupport = NewConditionSupport(self)
+
+    self._assign_new_ce_support()
+
+
+
+ +
+ +
+ + +
+def __str__(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
394
+395
def __str__(self) -> str:
+    return f"[Trigger] {self.get_content_as_string(include_trigger_definition=True)}"
+
+
+
+ +
+ +
+ + +
+def get_condition(...) + +
+ + +
+ +

Retrieve a condition based on its (display) index

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
condition_index + int + +
+

The condition index of the condition to retrieve

+
+
+ None +
display_index + int + +
+

The display index of the condition to retrieve

+
+
+ None +
+ + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If more than one parameter is used simultaneously

+
+
+ IndexError + +
+

If the condition could not be found

+
+
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Condition + +
+

The wanted condition

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
def get_condition(self, condition_index: int = None, display_index: int = None) -> Condition:
+    """
+    Retrieve a condition based on its (display) index
+
+    Args:
+        condition_index: The condition index of the condition to retrieve
+        display_index: The display index of the condition to retrieve
+
+    Raises:
+        ValueError: If more than one parameter is used simultaneously
+        IndexError: If the condition could not be found
+
+    Returns:
+        The wanted condition
+    """
+    if not mutually_exclusive(condition_index is not None, display_index is not None):
+        raise ValueError(f"Please identify a condition using either condition_index or display_index.")
+
+    if condition_index is None:
+        condition_index = self.condition_order[display_index]
+
+    return self.conditions[condition_index]
+
+
+
+ +
+ +
+ + +
+def get_content_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing all content of this trigger. +This includes all the content within the conditions and effects of this trigger.

+

This is also the function that is called when doing: print(trigger)

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
include_trigger_definition + bool + +
+

If the trigger meta-data should be added by this function

+
+
+ False +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
def get_content_as_string(self, include_trigger_definition: bool = False) -> str:
+    """
+    Create a human-readable string showcasing all content of this trigger.
+    This includes all the content within the conditions and effects of this trigger.
+
+    This is also the function that is called when doing: `print(trigger)`
+
+    Args:
+        include_trigger_definition: If the trigger meta-data should be added by this function
+
+    Returns:
+        The created string
+    """
+    return_string = ""
+
+    data_tba = {
+        'enabled': self.enabled != 0,
+        'looping': self.looping != 0
+    }
+
+    if self.description != "":
+        data_tba['description'] = f"'{self.description}'"
+    if self.description_stid != -1:
+        data_tba['description_stid'] = self.description_stid
+    if self.short_description != "":
+        data_tba['short_description'] = f"'{self.short_description}'"
+    if self.short_description_stid != -1:
+        data_tba['short_description_stid'] = self.short_description_stid
+    if self.display_as_objective != 0:
+        data_tba['display_as_objective'] = (self.display_as_objective != 0)
+    if self.display_on_screen != 0:
+        data_tba['display_on_screen'] = (self.display_on_screen != 0)
+    if self.description_order != 0:
+        data_tba['description_order'] = self.description_order
+    if self.header != 0:
+        data_tba['header'] = (self.header != 0)
+    if self.mute_objectives != 0:
+        data_tba['mute_objectives'] = (self.mute_objectives != 0)
+
+    for key, value in data_tba.items():
+        return_string += f"{key}: {value}\n"
+
+    if len(self.condition_order) > 0:
+        return_string += "conditions:\n"
+        for c_display_order, condition_id in enumerate(self.condition_order):
+            condition = self.conditions[condition_id]
+
+            name = f"Unknown ({condition.condition_type})"
+            if condition.condition_type in condition_dataset.condition_names:
+                name = condition_dataset.condition_names[condition.condition_type]
+
+            return_string += f"\t{name} [Index: {condition_id}, Display: {c_display_order}]:\n"
+            return_string += add_tabs(condition.get_content_as_string(), 2)
+
+    if len(self.effect_order) > 0:
+        return_string += "effects:\n"
+        for e_display_order, effect_id in enumerate(self.effect_order):
+            effect = self.effects[effect_id]
+
+            name = f"Unknown ({effect.effect_type})"
+            if effect.effect_type in effect_dataset.effect_names:
+                name = effect_dataset.effect_names[effect.effect_type]
+
+            return_string += f"\t{name} [Index: {effect_id}, Display: {e_display_order}]:\n"
+            return_string += add_tabs(effect.get_content_as_string(), 2)
+
+    if include_trigger_definition:
+        return f"\"{self.name}\" [Index: {self.trigger_id}]\n" + add_tabs(return_string, 1)
+    return return_string
+
+
+
+ +
+ +
+ + +
+def get_effect(...) + +
+ + +
+ +

Retrieve an effect based on its (display) index

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
effect_index + int + +
+

The effect index of the effect to retrieve

+
+
+ None +
display_index + int + +
+

The display index of the effect to retrieve

+
+
+ None +
+ + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If more than one parameter is used simultaneously

+
+
+ IndexError + +
+

If the effect could not be found

+
+
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Effect + +
+

The wanted effect

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
def get_effect(self, effect_index: int = None, display_index: int = None) -> Effect:
+    """
+    Retrieve an effect based on its (display) index
+
+    Args:
+        effect_index: The effect index of the effect to retrieve
+        display_index: The display index of the effect to retrieve
+
+    Raises:
+        ValueError: If more than one parameter is used simultaneously
+        IndexError: If the effect could not be found
+
+    Returns:
+        The wanted effect
+    """
+    if not mutually_exclusive(effect_index is not None, display_index is not None):
+        raise ValueError(f"Please identify an effect using either effect_index or display_index.")
+
+    if effect_index is None:
+        effect_index = self.effect_order[display_index]
+
+    return self.effects[effect_index]
+
+
+
+ +
+ +
+ + +
+def remove_condition(...) + +
+ + +
+ +

Remove a condition from this trigger

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
condition_index + int + +
+

The condition index of the condition to remove

+
+
+ None +
display_index + int + +
+

The display index of the condition to remove

+
+
+ None +
condition + Condition + +
+

A reference to the condition object to remove from this trigger

+
+
+ None +
+ + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If more than one parameter is used simultaneously

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
def remove_condition(self, condition_index: int = None, display_index: int = None, condition: Condition = None) \
+        -> None:
+    """
+    Remove a condition from this trigger
+
+    Args:
+        condition_index: The condition index of the condition to remove
+        display_index: The display index of the condition to remove
+        condition: A reference to the condition object to remove from this trigger
+
+    Raises:
+        ValueError: If more than one parameter is used simultaneously
+    """
+    if not mutually_exclusive(condition_index is not None, display_index is not None, condition is not None):
+        raise ValueError(f"Please identify a condition using either condition_index, display_index or condition.")
+
+    if condition is not None:
+        condition_index = self.conditions.index(condition)
+
+    if condition_index is None:
+        condition_index = self.condition_order[display_index]
+
+    del self.conditions[condition_index]
+
+
+
+ +
+ +
+ + +
+def remove_effect(...) + +
+ + +
+ +

Remove an effect from this trigger

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
effect_index + int + +
+

The effect index of the condition to remove

+
+
+ None +
display_index + int + +
+

The display index of the condition to remove

+
+
+ None +
effect + Effect + +
+

A reference to the effect object to remove from this trigger

+
+
+ None +
+ + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If more than one parameter is used simultaneously

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/trigger.py +
278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
def remove_effect(self, effect_index: int = None, display_index: int = None, effect: Effect = None) -> None:
+    """
+    Remove an effect from this trigger
+
+    Args:
+        effect_index: The effect index of the condition to remove
+        display_index: The display index of the condition to remove
+        effect: A reference to the effect object to remove from this trigger
+
+    Raises:
+        ValueError: If more than one parameter is used simultaneously
+    """
+    if not mutually_exclusive(effect_index is not None, display_index is not None, effect is not None):
+        raise ValueError(f"Please identify an effect using either effect_index, display_index or effect.")
+
+    if effect is not None:
+        effect_index = self.effects.index(effect)
+    if effect_index is None:
+        effect_index = self.effect_order[display_index]
+
+    del self.effects[effect_index]
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/trigger_ce_lock/index.html b/api_docs/trigger/trigger_ce_lock/index.html new file mode 100644 index 00000000..c18d89a2 --- /dev/null +++ b/api_docs/trigger/trigger_ce_lock/index.html @@ -0,0 +1,2903 @@ + + + + + + + + + + + + + + + + + + + + + + + + + TriggerCeLock - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

TriggerCeLock

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ TriggerCELock + + +

+ + +
+ + +

Object used to identify which conditions and effects should be locked from change

+ +
+ Source code in AoE2ScenarioParser/objects/support/trigger_ce_lock.py +
 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
class TriggerCELock:
+    """Object used to identify which conditions and effects should be locked from change"""
+    def __init__(
+            self,
+            lock_conditions: bool = False,
+            lock_effects: bool = False,
+            lock_condition_type: List[int] | None = None,
+            lock_effect_type: List[int] | None = None,
+            lock_condition_ids: List[int] | None = None,
+            lock_effect_ids: List[int] | None = None
+    ):
+        """
+        Args:
+            lock_conditions: Lock all conditions
+            lock_effects: Lock all effects
+            lock_condition_type: Lock certain condition types. Example: `ConditionId.OWN_OBJECTS`
+            lock_effect_type: Lock certain effect types. Example: `EffectId.CREATE_OBJECT`
+            lock_condition_ids: Lock certain conditions by their id
+            lock_effect_ids: Lock certain effects by their id
+        """
+        if lock_condition_type is None:
+            lock_condition_type = []
+        if lock_effect_type is None:
+            lock_effect_type = []
+        if lock_condition_ids is None:
+            lock_condition_ids = []
+        if lock_effect_ids is None:
+            lock_effect_ids = []
+
+        self.lock_conditions = lock_conditions
+        self.lock_effects = lock_effects
+        self.lock_condition_type = lock_condition_type
+        self.lock_effect_type = lock_effect_type
+        self.lock_condition_ids = lock_condition_ids
+        self.lock_effect_ids = lock_effect_ids
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+lock_condition_ids = lock_condition_ids + + + instance-attribute + + +
+ +
+ + + Value: +lock_condition_ids
+ + + + + +
+
+ +
+ +
+ + + + +
+lock_condition_type = lock_condition_type + + + instance-attribute + + +
+ +
+ + + Value: +lock_condition_type
+ + + + + +
+
+ +
+ +
+ + + + +
+lock_conditions = lock_conditions + + + instance-attribute + + +
+ +
+ + + Value: +lock_conditions
+ + + + + +
+
+ +
+ +
+ + + + +
+lock_effect_ids = lock_effect_ids + + + instance-attribute + + +
+ +
+ + + Value: +lock_effect_ids
+ + + + + +
+
+ +
+ +
+ + + + +
+lock_effect_type = lock_effect_type + + + instance-attribute + + +
+ +
+ + + Value: +lock_effect_type
+ + + + + +
+
+ +
+ +
+ + + + +
+lock_effects = lock_effects + + + instance-attribute + + +
+ +
+ + + Value: +lock_effects
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
lock_conditions + bool + +
+

Lock all conditions

+
+
+ False +
lock_effects + bool + +
+

Lock all effects

+
+
+ False +
lock_condition_type + List[int] | None + +
+

Lock certain condition types. Example: ConditionId.OWN_OBJECTS

+
+
+ None +
lock_effect_type + List[int] | None + +
+

Lock certain effect types. Example: EffectId.CREATE_OBJECT

+
+
+ None +
lock_condition_ids + List[int] | None + +
+

Lock certain conditions by their id

+
+
+ None +
lock_effect_ids + List[int] | None + +
+

Lock certain effects by their id

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/trigger_ce_lock.py +
 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
def __init__(
+        self,
+        lock_conditions: bool = False,
+        lock_effects: bool = False,
+        lock_condition_type: List[int] | None = None,
+        lock_effect_type: List[int] | None = None,
+        lock_condition_ids: List[int] | None = None,
+        lock_effect_ids: List[int] | None = None
+):
+    """
+    Args:
+        lock_conditions: Lock all conditions
+        lock_effects: Lock all effects
+        lock_condition_type: Lock certain condition types. Example: `ConditionId.OWN_OBJECTS`
+        lock_effect_type: Lock certain effect types. Example: `EffectId.CREATE_OBJECT`
+        lock_condition_ids: Lock certain conditions by their id
+        lock_effect_ids: Lock certain effects by their id
+    """
+    if lock_condition_type is None:
+        lock_condition_type = []
+    if lock_effect_type is None:
+        lock_effect_type = []
+    if lock_condition_ids is None:
+        lock_condition_ids = []
+    if lock_effect_ids is None:
+        lock_effect_ids = []
+
+    self.lock_conditions = lock_conditions
+    self.lock_effects = lock_effects
+    self.lock_condition_type = lock_condition_type
+    self.lock_effect_type = lock_effect_type
+    self.lock_condition_ids = lock_condition_ids
+    self.lock_effect_ids = lock_effect_ids
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/trigger_manager/index.html b/api_docs/trigger/trigger_manager/index.html new file mode 100644 index 00000000..69a7dc9f --- /dev/null +++ b/api_docs/trigger/trigger_manager/index.html @@ -0,0 +1,7463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + TriggerManager - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

TriggerManager

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ TriggerManager + + +

+ + +
+

+ Bases: AoE2Object

+ + +

Manager of everything trigger related. +This class does not include the logic for DE specific features (e.g. Variable objects). +For those see: TriggerManagerDE

+ +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
class TriggerManager(AoE2Object):
+    """
+    Manager of everything trigger related.
+    This class does not include the logic for DE specific features (e.g. `Variable` objects).
+    For those see: `TriggerManagerDE`
+    """
+
+    _link_list = [
+        RetrieverObjectLink("triggers", "Triggers", "trigger_data", process_as_object=Trigger),
+        RetrieverObjectLink("trigger_display_order", "Triggers", "trigger_display_order_array"),
+    ]
+
+    def __init__(
+            self,
+            triggers: List[Trigger],
+            trigger_display_order: List[int],
+            **kwargs
+    ):
+        super().__init__(**kwargs)
+
+        self.triggers: List[Trigger] = triggers
+        self.trigger_display_order: List[int] = trigger_display_order
+        self._trigger_hash = hash_list(triggers)
+
+    @property
+    def triggers(self) -> UuidList[Trigger]:
+        """All triggers"""
+        return self._triggers
+
+    @triggers.setter
+    def triggers(self, value: List[Trigger]) -> None:
+        value = UuidList(self._uuid, value, on_update_execute_entry=self._update_triggers_uuid)
+
+        self._trigger_hash = hash_list(value)
+        self._triggers = value
+        self.trigger_display_order = list(range(len(value)))
+
+    def _update_triggers_uuid(self, trigger):
+        """Function to update inner UUIDs """
+        for effect in trigger.effects:
+            effect._uuid = self._uuid
+        for condition in trigger.conditions:
+            condition._uuid = self._uuid
+
+    @property
+    def trigger_display_order(self) -> List[int]:
+        """The display order. This is a list of trigger IDs in the display order. NOT execution order!"""
+        if list_changed(self.triggers, self._trigger_hash):
+            update_order_array(self._trigger_display_order, len(self.triggers))
+            self._trigger_hash = hash_list(self.triggers)
+        return self._trigger_display_order
+
+    @trigger_display_order.setter
+    def trigger_display_order(self, val):
+        self._trigger_display_order = val
+
+    def copy_trigger_per_player(
+            self,
+            from_player: IntEnum,
+            trigger_select: int | TriggerSelect,
+            change_from_player_only: bool = False,
+            include_player_source: bool = True,
+            include_player_target: bool = False,
+            trigger_ce_lock: TriggerCELock | None = None,
+            include_gaia: bool = False,
+            create_copy_for_players: List[IntEnum] = None
+    ) -> Dict[PlayerId, Trigger]:
+        """
+        Copies a trigger for all or a selection of players. Every copy will change desired player attributes with it.
+
+        Args:
+            from_player: The central player this trigger is created for. This is the player that will not get
+                a copy.
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+            change_from_player_only: If set to `True`,  only change player attributes in effects and conditions that
+                are equal to the player defined using the `from_player` parameter.
+            include_player_source: If set to `True`,  allow player source attributes to be changed while copying.
+                Player source attributes are attributes where a player is defined to perform an action such as create an
+                object. If set to `False` these attributes will remain unchanged.
+            include_player_target: If set to `True`,  allow player target attributes to be changed while copying.
+                Player target attributes are attributes where a player is defined as the target such as change ownership
+                or sending resources. If set to `False` these attributes will remain unchanged.
+            trigger_ce_lock: The TriggerCELock object. Used to lock certain (types) of conditions or
+                effects from being changed while copying.
+            include_gaia: If `True`,  GAIA is included in the copied list. (Also when `create_copy_for_players` is
+                defined)
+            create_copy_for_players: A list of Players to create a copy for. The `from_player` will be
+                excluded from this list.
+
+        Returns:
+            A dict with all the new created triggers. The key is the player for which the trigger is
+                created using the IntEnum associated with it. Example:
+                `{PlayerId.TWO: Trigger, PlayerId.FIVE: Trigger}`
+
+        Raises:
+            ValueError: if more than one trigger selection is used. Any of (trigger_index, display_index or trigger)
+                Or if Both `include_player_source` and `include_player_target` are `False`
+        """
+        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+        if create_copy_for_players is None:
+            create_copy_for_players = [
+                PlayerId.ONE, PlayerId.TWO, PlayerId.THREE, PlayerId.FOUR,
+                PlayerId.FIVE, PlayerId.SIX, PlayerId.SEVEN, PlayerId.EIGHT
+            ]
+        if include_gaia and PlayerId.GAIA not in create_copy_for_players:
+            create_copy_for_players.append(PlayerId.GAIA)
+
+        alter_conditions, alter_effects = TriggerManager._find_alterable_ce(trigger, trigger_ce_lock)
+
+        return_dict: Dict[PlayerId, Trigger] = {}
+        for player in create_copy_for_players:
+            if player == from_player:
+                continue
+
+            new_trigger = self.copy_trigger(TriggerSelect.trigger(trigger), append_after_source=False, add_suffix=False)
+            new_trigger.name += " (GAIA)" if player == PlayerId.GAIA else f" (p{player})"
+            return_dict[player] = new_trigger
+
+            for cond_x in alter_conditions:
+                cond = new_trigger.conditions[cond_x]
+                if cond.source_player == -1:
+                    continue
+
+                if include_player_source:
+                    if not change_from_player_only or (change_from_player_only and cond.source_player == from_player):
+                        cond.source_player = PlayerId(player)
+                if include_player_target:
+                    if not change_from_player_only or (change_from_player_only and cond.target_player == from_player):
+                        cond.target_player = PlayerId(player)
+
+            for effect_x in alter_effects:
+                effect = new_trigger.effects[effect_x]
+                if effect.source_player == -1:
+                    continue
+
+                if include_player_source:
+                    if not change_from_player_only or (change_from_player_only and effect.source_player == from_player):
+                        effect.source_player = PlayerId(player)
+                if include_player_target:
+                    if not change_from_player_only or (change_from_player_only and effect.target_player == from_player):
+                        effect.target_player = PlayerId(player)
+
+        # After copies have been made
+        trigger.name += f" (p{from_player})"
+
+        return return_dict
+
+    def copy_trigger(
+            self,
+            trigger_select: int | TriggerSelect,
+            append_after_source: bool = True,
+            add_suffix: bool = True
+    ) -> Trigger:
+        """
+        Creates an exact copy (deepcopy) of this trigger.
+
+        Args:
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+            append_after_source: If the new trigger should be appended below the source trigger
+            add_suffix: If the text ' (copy)' should be added after the trigger
+
+        Returns:
+            The newly copied trigger
+        """
+        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+        deepcopy_trigger = copy.deepcopy(trigger)
+        deepcopy_trigger.trigger_id = len(self.triggers)
+        if add_suffix:
+            deepcopy_trigger.name += " (copy)"
+
+        self.triggers.append(deepcopy_trigger)
+
+        if append_after_source:
+            self.move_triggers([trigger_index, deepcopy_trigger.trigger_id], trigger_index)
+
+        return deepcopy_trigger
+
+    def copy_trigger_tree_per_player(
+            self,
+            from_player: IntEnum,
+            trigger_select: int | TriggerSelect,
+            change_from_player_only: bool = False,
+            include_player_source: bool = True,
+            include_player_target: bool = False,
+            trigger_ce_lock: TriggerCELock | None = None,
+            include_gaia: bool = False,
+            create_copy_for_players: List[IntEnum] | None = None,
+            group_triggers_by: GroupBy | None = None
+    ) -> Dict[IntEnum, List[Trigger]]:
+        """
+        Copies an entire trigger tree for all or a selection of players. Every copy will change desired player
+        attributes with it. Trigger trees are triggers linked together using `(de)activate_trigger` effects.
+
+        Args:
+            from_player: The central player this trigger is created for. This is the player that will not get
+                a copy.
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+            change_from_player_only: If set to `True`,  only change player attributes in effects and conditions that
+                are equal to the player defined using the `from_player` parameter.
+            include_player_source: If set to `True`,  allow player source attributes to be changed while copying.
+                Player source attributes are attributes where a player is defined to perform an action such as create an
+                object. If set to `False` these attributes will remain unchanged.
+            include_player_target: If set to `True`,  allow player target attributes to be changed while copying.
+                Player target attributes are attributes where a player is defined as the target such as change ownership
+                or sending resources. If set to `False` these attributes will remain unchanged.
+            trigger_ce_lock: The TriggerCELock object. Used to lock certain (types) of conditions or
+                effects from being changed while copying.
+            include_gaia: If `True`,  GAIA is included in the copied list. (Also when `create_copy_for_players` is
+                defined)
+            create_copy_for_players: A list of Players to create a copy for. The `from_player` will be
+                excluded from this list.
+            group_triggers_by: How to group the newly added triggers.
+
+        Returns:
+            The newly created triggers in a dict using the Player as key and as value with a list of triggers
+        """
+        if group_triggers_by is None:
+            group_triggers_by = GroupBy.NONE
+
+        trigger_index, display_index, source_trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+        known_node_indexes = [trigger_index]
+        self._find_trigger_tree_nodes_recursively(source_trigger, known_node_indexes)
+
+        new_triggers: Dict[IntEnum, List[Trigger]] = {}
+        trigger_index_swap = {}
+
+        # Set values for from_player
+        new_triggers[from_player] = [self.triggers[i] for i in known_node_indexes]
+        for index in known_node_indexes:
+            trigger: Trigger = self.triggers[index]
+            trigger_index_swap.setdefault(index, {})[from_player] = trigger.trigger_id
+
+        # Copy for all other players
+        for index in known_node_indexes:
+            triggers = self.copy_trigger_per_player(
+                from_player,
+                index,
+                change_from_player_only,
+                include_player_source,
+                include_player_target,
+                trigger_ce_lock,
+                include_gaia,
+                create_copy_for_players,
+            )
+            for player, trigger in triggers.items():
+                trigger_index_swap.setdefault(index, {})[player] = trigger.trigger_id
+                new_triggers.setdefault(player, []).append(trigger)
+
+        # Set trigger_id's in activation effects to the new player copied trigger ID
+        for player, triggers in new_triggers.items():
+            for trigger in triggers:
+                for effect in get_activation_effects(trigger):
+                    effect.trigger_id = trigger_index_swap[effect.trigger_id][player]
+
+        # -------------- Group by logic -------------- #
+        new_trigger_ids = []
+        if group_triggers_by == GroupBy.TRIGGER:
+            for i in range(len(known_node_indexes)):
+                for player in PlayerId.all():
+                    if player == from_player:
+                        new_trigger_ids.append(known_node_indexes[i])
+                        continue
+                    if player not in new_triggers:
+                        continue
+                    new_trigger_ids.append(new_triggers[player][i].trigger_id)
+        elif group_triggers_by == GroupBy.PLAYER:
+            for player in PlayerId.all():
+                if player == from_player:
+                    new_trigger_ids.extend(known_node_indexes)
+                    continue
+                if player not in new_triggers:
+                    continue
+
+                new_trigger_ids.extend([trigger.trigger_id for trigger in new_triggers[player]])
+
+        if group_triggers_by != GroupBy.NONE:
+            self.move_triggers(new_trigger_ids, display_index)
+
+        return new_triggers
+
+    def move_triggers(self, trigger_ids: List[int], insert_index: int) -> None:
+        """
+        Moves the given IDs from anywhere to the split index. This function reorders triggers BUT keeps
+        ``(de)activate trigger`` effects linked properly!
+
+        As an example:
+
+        ```
+        [0,1,2,3,4,5,6,7,8]  # Current index order
+        # Let's move trigger 1, 4, 5 and 6 to location 2
+        self.move_triggers([1, 4, 5, 6], 2)  # << 2 is an INDEX, not the value
+        [0,1,4,5,6,2,3,7,8]  # New index order
+        ```
+
+        Args:
+            trigger_ids: The trigger IDs to move
+            insert_index: The index that defines where to insert the triggers
+        """
+        if min(trigger_ids) < 0:
+            raise ValueError(f"Trigger IDs cannot be negative")
+
+        if insert_index >= len(self.trigger_display_order):
+            # Add to the end of the list
+            new_trigger_id_order = [n for n in self.trigger_display_order if n not in trigger_ids]
+            new_trigger_id_order += trigger_ids
+        else:
+            insert_num = self.trigger_display_order[insert_index]
+            new_trigger_id_order = [n for n in self.trigger_display_order if n not in trigger_ids or n == insert_num]
+
+            split_index = new_trigger_id_order.index(insert_num)
+
+            if insert_num in trigger_ids:
+                new_trigger_id_order.remove(insert_num)
+
+            new_trigger_id_order = new_trigger_id_order[:split_index] + trigger_ids + new_trigger_id_order[split_index:]
+        self.reorder_triggers(new_trigger_id_order)
+
+    def reorder_triggers(self, new_id_order: List[int] = None, ) -> None:
+        """
+        Reorder all triggers to a given order of IDs. This function reorders triggers BUT keeps ``(de)activate trigger``
+        effects linked properly!
+
+        Examples:
+
+            Moving the 6th trigger to the end of the trigger list::
+
+                [0,1,2,3,4,5,6,7,8]  # Trigger IDs before
+                self.reorder_triggers([0,1,2,3,5,4,7,8,6])
+                [0,1,2,3,5,4,7,8,6]  # Trigger IDs after
+
+            Setting the trigger (execution) order to the current display order::
+
+                self.reorder_triggers(self.trigger_display_order)
+
+        Keep in mind that all trigger IDs will get remapped with this function. So ``trigger_manager.triggers[4]`` might
+        result in a different trigger after this function is called in comparison to before.
+
+        Args:
+            new_id_order: The new trigger order. Uses the current display order when left unused
+        """
+        if new_id_order is not None:
+            if min(new_id_order) < 0:
+                raise ValueError(f"Trigger IDs cannot be negative")
+            self.trigger_display_order = new_id_order
+
+        new_triggers_list = []
+        index_changes = {}
+        for new_index, index in enumerate(self.trigger_display_order):
+            try:
+                trigger = self.triggers[index]
+            except IndexError:
+                raise ValueError(f"The trigger ID {index} doesn't exist") from None
+            index_changes[trigger.trigger_id] = new_index
+
+            trigger.trigger_id = new_index
+            new_triggers_list.append(trigger)
+        self.triggers = new_triggers_list
+
+        # Find and update all (de)activation effect trigger references
+        for trigger in self.triggers:
+            for effect in get_activation_effects(trigger):
+                if effect.trigger_id in index_changes:
+                    effect.trigger_id = index_changes[effect.trigger_id]
+
+    def copy_trigger_tree(self, trigger_select: int | TriggerSelect) -> List[Trigger]:
+        """
+        Copies an entire trigger tree. Trigger trees are triggers linked together using `(de)activate_trigger` effects.
+
+        Args:
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select
+
+        Returns:
+            The newly created triggers in a list
+        """
+        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+        known_node_indexes = [trigger_index]
+        self._find_trigger_tree_nodes_recursively(trigger, known_node_indexes)
+
+        new_triggers = []
+        id_swap = {}
+        for index in known_node_indexes:
+            trigger = self.copy_trigger(index, append_after_source=False)
+            new_triggers.append(trigger)
+            id_swap[index] = trigger.trigger_id
+
+        for trigger in new_triggers:
+            for effect in get_activation_effects(trigger):
+                effect.trigger_id = id_swap[effect.trigger_id]
+
+        return new_triggers
+
+    def replace_player(
+            self,
+            trigger_select: int | TriggerSelect,
+            to_player: PlayerId,
+            only_change_from: PlayerId = None,
+            include_player_source: bool = True,
+            include_player_target: bool = False,
+            trigger_ce_lock: TriggerCELock = None
+    ) -> Trigger:
+        """
+        Replaces player attributes. Specifically useful if multiple players are used in the same trigger.
+
+        Args:
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+            to_player: The player the attributes are changed to.
+            only_change_from: Can only change player attributes if the player is equal to the given value
+            include_player_source: If set to `True`,  allow player source attributes to be changed while replacing.
+                Player source attributes are attributes where a player is defined to perform an action such as create an
+                object. If set to `False` these attributes will remain unchanged.
+            include_player_target: If set to `True`,  allow player target attributes to be changed while replacing.
+                Player target attributes are attributes where a player is defined as the target such as change ownership
+                or sending resources. If set to `False` these attributes will remain unchanged.
+            trigger_ce_lock: The TriggerCELock object. Used to lock certain (types) of conditions or
+                effects from being changed.
+
+        Returns:
+            The given trigger with the proper player attributes changed
+        """
+        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+        alter_conditions, alter_effects = TriggerManager._find_alterable_ce(trigger, trigger_ce_lock)
+
+        for cond_x in alter_conditions:
+            cond = trigger.conditions[cond_x]
+            if value_is_valid(cond.source_player) and include_player_source:
+                if only_change_from is not None and only_change_from != cond.source_player:
+                    continue
+                cond.source_player = PlayerId(to_player)
+            if value_is_valid(cond.target_player) and include_player_target:
+                if only_change_from is not None and only_change_from != cond.target_player:
+                    continue
+                cond.target_player = PlayerId(to_player)
+        for effect_x in alter_effects:
+            effect = trigger.effects[effect_x]
+            if value_is_valid(effect.source_player) and include_player_source:
+                if only_change_from is not None and only_change_from != effect.source_player:
+                    continue
+                effect.source_player = PlayerId(to_player)
+            if value_is_valid(effect.target_player) and include_player_target:
+                if only_change_from is not None and only_change_from != effect.target_player:
+                    continue
+                effect.target_player = PlayerId(to_player)
+
+        return trigger
+
+    def add_trigger(
+            self,
+            name: str,
+            description: str | None = None,
+            description_stid: int | None = None,
+            display_as_objective: bool | None = None,
+            short_description: str | None = None,
+            short_description_stid: int | None = None,
+            display_on_screen: bool | None = None,
+            description_order: int | None = None,
+            enabled: bool | None = None,
+            looping: bool | None = None,
+            header: bool | None = None,
+            mute_objectives: bool | None = None,
+            conditions: List | None = None,
+            effects: List | None = None
+    ) -> Trigger:
+        """
+        Adds a new trigger to the scenario. Everything that is left empty will be set to in-game editor defaults.
+
+        Args:
+            name: The name for the trigger
+            description: The trigger description
+            description_stid: The trigger description string table ID
+            display_as_objective: Display the trigger as objective
+            short_description: The short trigger description
+            short_description_stid: The short trigger description string table ID
+            display_on_screen: Display the trigger objective on screen
+            description_order: ?
+            enabled: If the trigger is enabled from the start.
+            looping: If the trigger loops.
+            header: Turn objective into header
+            mute_objectives: Mute objectives
+            conditions: A list of condition managers
+            effects: A list of effect managers
+
+        Returns:
+            The newly created trigger
+        """
+        keys = [
+            'description', 'description_stid', 'display_as_objective', 'short_description',
+            'short_description_stid', 'display_on_screen', 'description_order', 'enabled', 'looping', 'header',
+            'mute_objectives', 'conditions', 'effects'
+        ]
+        trigger_attr = {}
+        for key in keys:
+            if locals()[key] is not None:
+                trigger_attr[key] = locals()[key]
+        new_trigger = Trigger(name=name, trigger_id=len(self.triggers), **trigger_attr, uuid=self._uuid)
+        self.triggers.append(new_trigger)
+        return new_trigger
+
+    def import_triggers(self, triggers: List[Trigger], index: int = -1, deepcopy: bool = True) -> List[Trigger]:
+        """
+        Adds existing trigger objects (from another scenario) to this scenario. Keeping all ``(de)activate trigger``
+        effects linked!
+
+        Args:
+            triggers: The list of Trigger objects to be added
+            index: The index where to insert the new triggers, will be added at the end when left unused.
+            deepcopy: If the given triggers need to be deep copied or not when importing. Can be useful to keep the
+                reference alive between the source and target trigger the same when setting this to `False`.
+
+        Returns:
+            The newly added triggers (with the new IDs and activation links etc.)
+        """
+        if deepcopy:
+            triggers = copy.deepcopy(triggers)
+        index_changes = {}
+
+        for offset, trigger in enumerate(triggers):
+            new_index = len(self.triggers) + offset
+            index_changes[trigger.trigger_id] = trigger.trigger_id = new_index
+
+        for trigger in triggers:
+            for i, effect in enumerate(get_activation_effects(trigger)):
+                try:
+                    effect.trigger_id = index_changes[effect.trigger_id]
+                except KeyError:
+                    warn(f"(De)Activation effect {i} in trigger '{trigger.name}' refers to a trigger that wasn't "
+                         f"included in the imported triggers. Effect will be reset")
+                    effect.trigger_id = -1
+
+        self.triggers += triggers
+        if index != -1:
+            self.move_triggers([t.trigger_id for t in triggers], index)
+        return triggers
+
+    def get_trigger(self, trigger_select: int | TriggerSelect) -> Trigger:
+        """
+        Get a single trigger
+
+        Args:
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+
+        Returns:
+            The selected trigger
+        """
+        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+        return trigger
+
+    def remove_trigger(self, trigger_select: int | TriggerSelect) -> None:
+        """
+        Removes a trigger BUT keeps ``(de)activate trigger`` effects linked properly!
+
+        Args:
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+        """
+        self.remove_triggers([trigger_select])
+
+    def remove_triggers(self, trigger_selects: List[int | TriggerSelect]) -> None:
+        """
+        Removes a list of triggers BUT keeps ``(de)activate trigger`` effects linked properly!
+
+        Args:
+            trigger_selects: The list with trigger IDs
+        """
+        removing_trigger_ids = [
+            self._validate_and_retrieve_trigger_info(trigger_select)[0] for trigger_select in trigger_selects
+        ]
+        removing_trigger_ids.sort(reverse=True)
+
+        new_display_order = self.compute_updated_display_order(removing_trigger_ids)
+
+        for trigger_id in removing_trigger_ids:
+            del self.triggers[trigger_id]
+
+        index_changes = {}
+        for new_index, trigger in enumerate(self.triggers):
+            if new_index != trigger.trigger_id:
+                index_changes[trigger.trigger_id] = new_index
+                trigger.trigger_id = new_index
+
+        # Find and update all (de)activation effect trigger references
+        for trigger in self.triggers:
+            for effect in get_activation_effects(trigger):
+                if effect.trigger_id in index_changes:
+                    effect.trigger_id = index_changes[effect.trigger_id]
+
+        self.trigger_display_order = new_display_order
+
+    def _find_trigger_tree_nodes_recursively(self, trigger, known_node_indexes: List[int]) -> None:
+        found_node_indexes = TriggerManager._find_trigger_tree_nodes(trigger)
+        unknown_node_indexes = [i for i in found_node_indexes if i not in known_node_indexes]
+
+        if len(unknown_node_indexes) == 0:
+            return
+
+        known_node_indexes += unknown_node_indexes
+
+        for index in unknown_node_indexes:
+            self._find_trigger_tree_nodes_recursively(self.triggers[index], known_node_indexes)
+
+    def _validate_and_retrieve_trigger_info(self, trigger_select) -> (int, int, Trigger):
+        if type(trigger_select) is int:
+            trigger = display_index = None
+            trigger_index = trigger_select
+        else:
+            trigger = trigger_select.trigger
+            trigger_index = trigger_select.trigger_index
+            display_index = trigger_select.display_index
+
+        try:
+            if trigger is not None:
+                trigger_index = trigger.trigger_id
+                display_index = self.trigger_display_order.index(trigger_index)
+            elif trigger_index is not None:
+                trigger = self.triggers[trigger_index]
+                display_index = self.trigger_display_order.index(trigger_index)
+            elif display_index is not None:
+                trigger_index = self.trigger_display_order[display_index]
+                trigger = self.triggers[trigger_index]
+        except IndexError:
+            if trigger_index:
+                raise ValueError(f"No trigger with index {trigger_index}") from None
+            if display_index:
+                raise ValueError(f"No Trigger with display index {display_index}") from None
+
+        return trigger_index, display_index, trigger
+
+    def get_summary_as_string(self) -> str:
+        """
+        Create a human-readable string showcasing a summary of the content of the manager.
+        This includes all triggers and the amount of conditions and effects they hold.
+
+        Returns:
+            The created string
+        """
+        return_string = "\nTrigger Summary:\n"
+
+        triggers = self.triggers
+        display_order = self.trigger_display_order
+
+        if len(display_order) == 0:
+            return_string += "\t<< No Triggers >>"
+
+        longest_trigger_name = -1
+        longest_index_notation = -1
+        for display, trigger_index in enumerate(display_order):
+            trigger_name = triggers[trigger_index].name
+            longest_trigger_name = max(longest_trigger_name, len(trigger_name))
+
+            longest_index_notation = max(
+                longest_index_notation,
+                helper.get_int_len(display) + helper.get_int_len(trigger_index)
+            )
+
+        longest_trigger_name += 3
+        for display, trigger_index in enumerate(display_order):
+            trigger = triggers[trigger_index]
+            trigger_name = trigger.name
+
+            name_buffer = longest_trigger_name - len(trigger_name)
+            index_buffer = longest_index_notation - (helper.get_int_len(display) + helper.get_int_len(trigger_index))
+            return_string += "\t" + trigger_name + (" " * name_buffer)
+            return_string += f" [Index: {trigger_index}, Display: {display}] {' ' * index_buffer}"
+
+            return_string += "\t(conditions: " + str(len(trigger.conditions)) + ", "
+            return_string += " effects: " + str(len(trigger.effects)) + ")\n"
+
+        return return_string
+
+    def get_content_as_string(self) -> str:
+        """
+        Create a human-readable string showcasing all content of the manager.
+        This includes all triggers and their conditions and effects.
+
+        This is also the function that is called when doing: `print(trigger_manager)`
+
+        Returns:
+            The created string
+        """
+        return_string = "\nTriggers:\n"
+
+        if len(self.triggers) == 0:
+            return_string += "\t<<No triggers>>\n"
+
+        for trigger_index in self.trigger_display_order:
+            return_string += self.get_trigger_as_string(trigger_index) + "\n"
+
+        return return_string
+
+    def get_trigger_as_string(self, trigger_select: int | TriggerSelect) -> str:
+        """
+        Create a human-readable string showcasing trigger meta-data and content.
+
+        Args:
+            trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+
+        Returns:
+            The created string
+        """
+        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+        return_string = "\t'" + trigger.name + "'"
+        return_string += " [Index: " + str(trigger_index) + ", Display: " + str(display_index) + "]" + ":\n"
+
+        return_string += add_tabs(trigger.get_content_as_string(include_trigger_definition=False), 2)
+
+        return return_string
+
+    @staticmethod
+    def _find_alterable_ce(trigger: Trigger, trigger_ce_lock: TriggerCELock) -> (List[int], List[int]):
+        """Logic for selecting the proper conditions and effects based on a TriggerCELock"""
+        lock_conditions = trigger_ce_lock.lock_conditions if trigger_ce_lock is not None else False
+        lock_effects = trigger_ce_lock.lock_effects if trigger_ce_lock is not None else False
+        lock_condition_type = trigger_ce_lock.lock_condition_type if trigger_ce_lock is not None else []
+        lock_effect_type = trigger_ce_lock.lock_effect_type if trigger_ce_lock is not None else []
+        lock_condition_ids = trigger_ce_lock.lock_condition_ids if trigger_ce_lock is not None else []
+        lock_effect_ids = trigger_ce_lock.lock_effect_ids if trigger_ce_lock is not None else []
+
+        alter_conditions: List[int] = []
+        alter_effects: List[int] = []
+        if not lock_conditions:
+            for i, cond in enumerate(trigger.conditions):
+                if i not in lock_condition_ids and cond.condition_type not in lock_condition_type:
+                    alter_conditions.append(i)
+        if not lock_effects:
+            for i, effect in enumerate(trigger.effects):
+                if i not in lock_effect_ids and effect.effect_type not in lock_effect_type:
+                    alter_effects.append(i)
+
+        return alter_conditions, alter_effects
+
+    @staticmethod
+    def _find_trigger_tree_nodes(trigger: Trigger) -> List[int]:
+        """Get all linked trigger ids from all (de)activation effects in a trigger"""
+        return [
+            effect.trigger_id for effect in trigger.effects if
+            effect.effect_type in [EffectId.ACTIVATE_TRIGGER, EffectId.DEACTIVATE_TRIGGER]
+        ]
+
+    def __str__(self) -> str:
+        return self.get_content_as_string()
+
+    def compute_updated_display_order(self, removing_trigger_ids: List[int]):
+        new_display_order = []
+        for index in self.trigger_display_order:
+            subtract = 0
+            skip = False
+
+            for removing_index in removing_trigger_ids:
+                if removing_index == index:
+                    skip = True
+                    break
+
+                if index > removing_index:
+                    subtract += 1
+
+            if skip:
+                continue
+
+            new_display_order.append(index - subtract)
+
+        return new_display_order
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+trigger_display_order: List[int] + + + property + writable + + +
+ +
+ Type: +List[int] + +
+ + + + + +
+ +

The display order. This is a list of trigger IDs in the display order. NOT execution order!

+
+ +
+ +
+ + + + +
+triggers: UuidList[Trigger] + + + property + writable + + +
+ +
+ Type: +UuidList[Trigger] + +
+ + + + + +
+ +

All triggers

+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
triggers + List[Trigger] + - + required +
trigger_display_order + List[int] + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
def __init__(
+        self,
+        triggers: List[Trigger],
+        trigger_display_order: List[int],
+        **kwargs
+):
+    super().__init__(**kwargs)
+
+    self.triggers: List[Trigger] = triggers
+    self.trigger_display_order: List[int] = trigger_display_order
+    self._trigger_hash = hash_list(triggers)
+
+
+
+ +
+ +
+ + +
+def __str__(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
765
+766
def __str__(self) -> str:
+    return self.get_content_as_string()
+
+
+
+ +
+ +
+ + +
+def add_trigger(...) + +
+ + +
+ +

Adds a new trigger to the scenario. Everything that is left empty will be set to in-game editor defaults.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + +
+

The name for the trigger

+
+
+ required +
description + str | None + +
+

The trigger description

+
+
+ None +
description_stid + int | None + +
+

The trigger description string table ID

+
+
+ None +
display_as_objective + bool | None + +
+

Display the trigger as objective

+
+
+ None +
short_description + str | None + +
+

The short trigger description

+
+
+ None +
short_description_stid + int | None + +
+

The short trigger description string table ID

+
+
+ None +
display_on_screen + bool | None + +
+

Display the trigger objective on screen

+
+
+ None +
description_order + int | None + +
+

?

+
+
+ None +
enabled + bool | None + +
+

If the trigger is enabled from the start.

+
+
+ None +
looping + bool | None + +
+

If the trigger loops.

+
+
+ None +
header + bool | None + +
+

Turn objective into header

+
+
+ None +
mute_objectives + bool | None + +
+

Mute objectives

+
+
+ None +
conditions + List | None + +
+

A list of condition managers

+
+
+ None +
effects + List | None + +
+

A list of effect managers

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Trigger + +
+

The newly created trigger

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
def add_trigger(
+        self,
+        name: str,
+        description: str | None = None,
+        description_stid: int | None = None,
+        display_as_objective: bool | None = None,
+        short_description: str | None = None,
+        short_description_stid: int | None = None,
+        display_on_screen: bool | None = None,
+        description_order: int | None = None,
+        enabled: bool | None = None,
+        looping: bool | None = None,
+        header: bool | None = None,
+        mute_objectives: bool | None = None,
+        conditions: List | None = None,
+        effects: List | None = None
+) -> Trigger:
+    """
+    Adds a new trigger to the scenario. Everything that is left empty will be set to in-game editor defaults.
+
+    Args:
+        name: The name for the trigger
+        description: The trigger description
+        description_stid: The trigger description string table ID
+        display_as_objective: Display the trigger as objective
+        short_description: The short trigger description
+        short_description_stid: The short trigger description string table ID
+        display_on_screen: Display the trigger objective on screen
+        description_order: ?
+        enabled: If the trigger is enabled from the start.
+        looping: If the trigger loops.
+        header: Turn objective into header
+        mute_objectives: Mute objectives
+        conditions: A list of condition managers
+        effects: A list of effect managers
+
+    Returns:
+        The newly created trigger
+    """
+    keys = [
+        'description', 'description_stid', 'display_as_objective', 'short_description',
+        'short_description_stid', 'display_on_screen', 'description_order', 'enabled', 'looping', 'header',
+        'mute_objectives', 'conditions', 'effects'
+    ]
+    trigger_attr = {}
+    for key in keys:
+        if locals()[key] is not None:
+            trigger_attr[key] = locals()[key]
+    new_trigger = Trigger(name=name, trigger_id=len(self.triggers), **trigger_attr, uuid=self._uuid)
+    self.triggers.append(new_trigger)
+    return new_trigger
+
+
+
+ +
+ +
+ + +
+def compute_updated_display_order(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
removing_trigger_ids + List[int] + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
def compute_updated_display_order(self, removing_trigger_ids: List[int]):
+    new_display_order = []
+    for index in self.trigger_display_order:
+        subtract = 0
+        skip = False
+
+        for removing_index in removing_trigger_ids:
+            if removing_index == index:
+                skip = True
+                break
+
+            if index > removing_index:
+                subtract += 1
+
+        if skip:
+            continue
+
+        new_display_order.append(index - subtract)
+
+    return new_display_order
+
+
+
+ +
+ +
+ + +
+def copy_trigger(...) + +
+ + +
+ +

Creates an exact copy (deepcopy) of this trigger.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select.

+
+
+ required +
append_after_source + bool + +
+

If the new trigger should be appended below the source trigger

+
+
+ True +
add_suffix + bool + +
+

If the text ' (copy)' should be added after the trigger

+
+
+ True +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Trigger + +
+

The newly copied trigger

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
def copy_trigger(
+        self,
+        trigger_select: int | TriggerSelect,
+        append_after_source: bool = True,
+        add_suffix: bool = True
+) -> Trigger:
+    """
+    Creates an exact copy (deepcopy) of this trigger.
+
+    Args:
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+        append_after_source: If the new trigger should be appended below the source trigger
+        add_suffix: If the text ' (copy)' should be added after the trigger
+
+    Returns:
+        The newly copied trigger
+    """
+    trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+    deepcopy_trigger = copy.deepcopy(trigger)
+    deepcopy_trigger.trigger_id = len(self.triggers)
+    if add_suffix:
+        deepcopy_trigger.name += " (copy)"
+
+    self.triggers.append(deepcopy_trigger)
+
+    if append_after_source:
+        self.move_triggers([trigger_index, deepcopy_trigger.trigger_id], trigger_index)
+
+    return deepcopy_trigger
+
+
+
+ +
+ +
+ + +
+def copy_trigger_per_player(...) + +
+ + +
+ +

Copies a trigger for all or a selection of players. Every copy will change desired player attributes with it.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
from_player + IntEnum + +
+

The central player this trigger is created for. This is the player that will not get +a copy.

+
+
+ required +
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select.

+
+
+ required +
change_from_player_only + bool + +
+

If set to True, only change player attributes in effects and conditions that +are equal to the player defined using the from_player parameter.

+
+
+ False +
include_player_source + bool + +
+

If set to True, allow player source attributes to be changed while copying. +Player source attributes are attributes where a player is defined to perform an action such as create an +object. If set to False these attributes will remain unchanged.

+
+
+ True +
include_player_target + bool + +
+

If set to True, allow player target attributes to be changed while copying. +Player target attributes are attributes where a player is defined as the target such as change ownership +or sending resources. If set to False these attributes will remain unchanged.

+
+
+ False +
trigger_ce_lock + TriggerCELock | None + +
+

The TriggerCELock object. Used to lock certain (types) of conditions or +effects from being changed while copying.

+
+
+ None +
include_gaia + bool + +
+

If True, GAIA is included in the copied list. (Also when create_copy_for_players is +defined)

+
+
+ False +
create_copy_for_players + List[IntEnum] + +
+

A list of Players to create a copy for. The from_player will be +excluded from this list.

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Dict[PlayerId, Trigger] + +
+

A dict with all the new created triggers. The key is the player for which the trigger is +created using the IntEnum associated with it. Example: +{PlayerId.TWO: Trigger, PlayerId.FIVE: Trigger}

+
+
+ + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if more than one trigger selection is used. Any of (trigger_index, display_index or trigger) +Or if Both include_player_source and include_player_target are False

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
def copy_trigger_per_player(
+        self,
+        from_player: IntEnum,
+        trigger_select: int | TriggerSelect,
+        change_from_player_only: bool = False,
+        include_player_source: bool = True,
+        include_player_target: bool = False,
+        trigger_ce_lock: TriggerCELock | None = None,
+        include_gaia: bool = False,
+        create_copy_for_players: List[IntEnum] = None
+) -> Dict[PlayerId, Trigger]:
+    """
+    Copies a trigger for all or a selection of players. Every copy will change desired player attributes with it.
+
+    Args:
+        from_player: The central player this trigger is created for. This is the player that will not get
+            a copy.
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+        change_from_player_only: If set to `True`,  only change player attributes in effects and conditions that
+            are equal to the player defined using the `from_player` parameter.
+        include_player_source: If set to `True`,  allow player source attributes to be changed while copying.
+            Player source attributes are attributes where a player is defined to perform an action such as create an
+            object. If set to `False` these attributes will remain unchanged.
+        include_player_target: If set to `True`,  allow player target attributes to be changed while copying.
+            Player target attributes are attributes where a player is defined as the target such as change ownership
+            or sending resources. If set to `False` these attributes will remain unchanged.
+        trigger_ce_lock: The TriggerCELock object. Used to lock certain (types) of conditions or
+            effects from being changed while copying.
+        include_gaia: If `True`,  GAIA is included in the copied list. (Also when `create_copy_for_players` is
+            defined)
+        create_copy_for_players: A list of Players to create a copy for. The `from_player` will be
+            excluded from this list.
+
+    Returns:
+        A dict with all the new created triggers. The key is the player for which the trigger is
+            created using the IntEnum associated with it. Example:
+            `{PlayerId.TWO: Trigger, PlayerId.FIVE: Trigger}`
+
+    Raises:
+        ValueError: if more than one trigger selection is used. Any of (trigger_index, display_index or trigger)
+            Or if Both `include_player_source` and `include_player_target` are `False`
+    """
+    trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+    if create_copy_for_players is None:
+        create_copy_for_players = [
+            PlayerId.ONE, PlayerId.TWO, PlayerId.THREE, PlayerId.FOUR,
+            PlayerId.FIVE, PlayerId.SIX, PlayerId.SEVEN, PlayerId.EIGHT
+        ]
+    if include_gaia and PlayerId.GAIA not in create_copy_for_players:
+        create_copy_for_players.append(PlayerId.GAIA)
+
+    alter_conditions, alter_effects = TriggerManager._find_alterable_ce(trigger, trigger_ce_lock)
+
+    return_dict: Dict[PlayerId, Trigger] = {}
+    for player in create_copy_for_players:
+        if player == from_player:
+            continue
+
+        new_trigger = self.copy_trigger(TriggerSelect.trigger(trigger), append_after_source=False, add_suffix=False)
+        new_trigger.name += " (GAIA)" if player == PlayerId.GAIA else f" (p{player})"
+        return_dict[player] = new_trigger
+
+        for cond_x in alter_conditions:
+            cond = new_trigger.conditions[cond_x]
+            if cond.source_player == -1:
+                continue
+
+            if include_player_source:
+                if not change_from_player_only or (change_from_player_only and cond.source_player == from_player):
+                    cond.source_player = PlayerId(player)
+            if include_player_target:
+                if not change_from_player_only or (change_from_player_only and cond.target_player == from_player):
+                    cond.target_player = PlayerId(player)
+
+        for effect_x in alter_effects:
+            effect = new_trigger.effects[effect_x]
+            if effect.source_player == -1:
+                continue
+
+            if include_player_source:
+                if not change_from_player_only or (change_from_player_only and effect.source_player == from_player):
+                    effect.source_player = PlayerId(player)
+            if include_player_target:
+                if not change_from_player_only or (change_from_player_only and effect.target_player == from_player):
+                    effect.target_player = PlayerId(player)
+
+    # After copies have been made
+    trigger.name += f" (p{from_player})"
+
+    return return_dict
+
+
+
+ +
+ +
+ + +
+def copy_trigger_tree(...) + +
+ + +
+ +

Copies an entire trigger tree. Trigger trees are triggers linked together using (de)activate_trigger effects.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select

+
+
+ required +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Trigger] + +
+

The newly created triggers in a list

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
def copy_trigger_tree(self, trigger_select: int | TriggerSelect) -> List[Trigger]:
+    """
+    Copies an entire trigger tree. Trigger trees are triggers linked together using `(de)activate_trigger` effects.
+
+    Args:
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select
+
+    Returns:
+        The newly created triggers in a list
+    """
+    trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+    known_node_indexes = [trigger_index]
+    self._find_trigger_tree_nodes_recursively(trigger, known_node_indexes)
+
+    new_triggers = []
+    id_swap = {}
+    for index in known_node_indexes:
+        trigger = self.copy_trigger(index, append_after_source=False)
+        new_triggers.append(trigger)
+        id_swap[index] = trigger.trigger_id
+
+    for trigger in new_triggers:
+        for effect in get_activation_effects(trigger):
+            effect.trigger_id = id_swap[effect.trigger_id]
+
+    return new_triggers
+
+
+
+ +
+ +
+ + +
+def copy_trigger_tree_per_player(...) + +
+ + +
+ +

Copies an entire trigger tree for all or a selection of players. Every copy will change desired player +attributes with it. Trigger trees are triggers linked together using (de)activate_trigger effects.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
from_player + IntEnum + +
+

The central player this trigger is created for. This is the player that will not get +a copy.

+
+
+ required +
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select.

+
+
+ required +
change_from_player_only + bool + +
+

If set to True, only change player attributes in effects and conditions that +are equal to the player defined using the from_player parameter.

+
+
+ False +
include_player_source + bool + +
+

If set to True, allow player source attributes to be changed while copying. +Player source attributes are attributes where a player is defined to perform an action such as create an +object. If set to False these attributes will remain unchanged.

+
+
+ True +
include_player_target + bool + +
+

If set to True, allow player target attributes to be changed while copying. +Player target attributes are attributes where a player is defined as the target such as change ownership +or sending resources. If set to False these attributes will remain unchanged.

+
+
+ False +
trigger_ce_lock + TriggerCELock | None + +
+

The TriggerCELock object. Used to lock certain (types) of conditions or +effects from being changed while copying.

+
+
+ None +
include_gaia + bool + +
+

If True, GAIA is included in the copied list. (Also when create_copy_for_players is +defined)

+
+
+ False +
create_copy_for_players + List[IntEnum] | None + +
+

A list of Players to create a copy for. The from_player will be +excluded from this list.

+
+
+ None +
group_triggers_by + GroupBy | None + +
+

How to group the newly added triggers.

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Dict[IntEnum, List[Trigger]] + +
+

The newly created triggers in a dict using the Player as key and as value with a list of triggers

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
def copy_trigger_tree_per_player(
+        self,
+        from_player: IntEnum,
+        trigger_select: int | TriggerSelect,
+        change_from_player_only: bool = False,
+        include_player_source: bool = True,
+        include_player_target: bool = False,
+        trigger_ce_lock: TriggerCELock | None = None,
+        include_gaia: bool = False,
+        create_copy_for_players: List[IntEnum] | None = None,
+        group_triggers_by: GroupBy | None = None
+) -> Dict[IntEnum, List[Trigger]]:
+    """
+    Copies an entire trigger tree for all or a selection of players. Every copy will change desired player
+    attributes with it. Trigger trees are triggers linked together using `(de)activate_trigger` effects.
+
+    Args:
+        from_player: The central player this trigger is created for. This is the player that will not get
+            a copy.
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+        change_from_player_only: If set to `True`,  only change player attributes in effects and conditions that
+            are equal to the player defined using the `from_player` parameter.
+        include_player_source: If set to `True`,  allow player source attributes to be changed while copying.
+            Player source attributes are attributes where a player is defined to perform an action such as create an
+            object. If set to `False` these attributes will remain unchanged.
+        include_player_target: If set to `True`,  allow player target attributes to be changed while copying.
+            Player target attributes are attributes where a player is defined as the target such as change ownership
+            or sending resources. If set to `False` these attributes will remain unchanged.
+        trigger_ce_lock: The TriggerCELock object. Used to lock certain (types) of conditions or
+            effects from being changed while copying.
+        include_gaia: If `True`,  GAIA is included in the copied list. (Also when `create_copy_for_players` is
+            defined)
+        create_copy_for_players: A list of Players to create a copy for. The `from_player` will be
+            excluded from this list.
+        group_triggers_by: How to group the newly added triggers.
+
+    Returns:
+        The newly created triggers in a dict using the Player as key and as value with a list of triggers
+    """
+    if group_triggers_by is None:
+        group_triggers_by = GroupBy.NONE
+
+    trigger_index, display_index, source_trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+    known_node_indexes = [trigger_index]
+    self._find_trigger_tree_nodes_recursively(source_trigger, known_node_indexes)
+
+    new_triggers: Dict[IntEnum, List[Trigger]] = {}
+    trigger_index_swap = {}
+
+    # Set values for from_player
+    new_triggers[from_player] = [self.triggers[i] for i in known_node_indexes]
+    for index in known_node_indexes:
+        trigger: Trigger = self.triggers[index]
+        trigger_index_swap.setdefault(index, {})[from_player] = trigger.trigger_id
+
+    # Copy for all other players
+    for index in known_node_indexes:
+        triggers = self.copy_trigger_per_player(
+            from_player,
+            index,
+            change_from_player_only,
+            include_player_source,
+            include_player_target,
+            trigger_ce_lock,
+            include_gaia,
+            create_copy_for_players,
+        )
+        for player, trigger in triggers.items():
+            trigger_index_swap.setdefault(index, {})[player] = trigger.trigger_id
+            new_triggers.setdefault(player, []).append(trigger)
+
+    # Set trigger_id's in activation effects to the new player copied trigger ID
+    for player, triggers in new_triggers.items():
+        for trigger in triggers:
+            for effect in get_activation_effects(trigger):
+                effect.trigger_id = trigger_index_swap[effect.trigger_id][player]
+
+    # -------------- Group by logic -------------- #
+    new_trigger_ids = []
+    if group_triggers_by == GroupBy.TRIGGER:
+        for i in range(len(known_node_indexes)):
+            for player in PlayerId.all():
+                if player == from_player:
+                    new_trigger_ids.append(known_node_indexes[i])
+                    continue
+                if player not in new_triggers:
+                    continue
+                new_trigger_ids.append(new_triggers[player][i].trigger_id)
+    elif group_triggers_by == GroupBy.PLAYER:
+        for player in PlayerId.all():
+            if player == from_player:
+                new_trigger_ids.extend(known_node_indexes)
+                continue
+            if player not in new_triggers:
+                continue
+
+            new_trigger_ids.extend([trigger.trigger_id for trigger in new_triggers[player]])
+
+    if group_triggers_by != GroupBy.NONE:
+        self.move_triggers(new_trigger_ids, display_index)
+
+    return new_triggers
+
+
+
+ +
+ +
+ + +
+def get_content_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing all content of the manager. +This includes all triggers and their conditions and effects.

+

This is also the function that is called when doing: print(trigger_manager)

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
def get_content_as_string(self) -> str:
+    """
+    Create a human-readable string showcasing all content of the manager.
+    This includes all triggers and their conditions and effects.
+
+    This is also the function that is called when doing: `print(trigger_manager)`
+
+    Returns:
+        The created string
+    """
+    return_string = "\nTriggers:\n"
+
+    if len(self.triggers) == 0:
+        return_string += "\t<<No triggers>>\n"
+
+    for trigger_index in self.trigger_display_order:
+        return_string += self.get_trigger_as_string(trigger_index) + "\n"
+
+    return return_string
+
+
+
+ +
+ +
+ + +
+def get_summary_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing a summary of the content of the manager. +This includes all triggers and the amount of conditions and effects they hold.

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
def get_summary_as_string(self) -> str:
+    """
+    Create a human-readable string showcasing a summary of the content of the manager.
+    This includes all triggers and the amount of conditions and effects they hold.
+
+    Returns:
+        The created string
+    """
+    return_string = "\nTrigger Summary:\n"
+
+    triggers = self.triggers
+    display_order = self.trigger_display_order
+
+    if len(display_order) == 0:
+        return_string += "\t<< No Triggers >>"
+
+    longest_trigger_name = -1
+    longest_index_notation = -1
+    for display, trigger_index in enumerate(display_order):
+        trigger_name = triggers[trigger_index].name
+        longest_trigger_name = max(longest_trigger_name, len(trigger_name))
+
+        longest_index_notation = max(
+            longest_index_notation,
+            helper.get_int_len(display) + helper.get_int_len(trigger_index)
+        )
+
+    longest_trigger_name += 3
+    for display, trigger_index in enumerate(display_order):
+        trigger = triggers[trigger_index]
+        trigger_name = trigger.name
+
+        name_buffer = longest_trigger_name - len(trigger_name)
+        index_buffer = longest_index_notation - (helper.get_int_len(display) + helper.get_int_len(trigger_index))
+        return_string += "\t" + trigger_name + (" " * name_buffer)
+        return_string += f" [Index: {trigger_index}, Display: {display}] {' ' * index_buffer}"
+
+        return_string += "\t(conditions: " + str(len(trigger.conditions)) + ", "
+        return_string += " effects: " + str(len(trigger.effects)) + ")\n"
+
+    return return_string
+
+
+
+ +
+ +
+ + +
+def get_trigger(...) + +
+ + +
+ +

Get a single trigger

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select.

+
+
+ required +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Trigger + +
+

The selected trigger

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
def get_trigger(self, trigger_select: int | TriggerSelect) -> Trigger:
+    """
+    Get a single trigger
+
+    Args:
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+
+    Returns:
+        The selected trigger
+    """
+    trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+    return trigger
+
+
+
+ +
+ +
+ + +
+def get_trigger_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing trigger meta-data and content.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select.

+
+
+ required +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
def get_trigger_as_string(self, trigger_select: int | TriggerSelect) -> str:
+    """
+    Create a human-readable string showcasing trigger meta-data and content.
+
+    Args:
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+
+    Returns:
+        The created string
+    """
+    trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+
+    return_string = "\t'" + trigger.name + "'"
+    return_string += " [Index: " + str(trigger_index) + ", Display: " + str(display_index) + "]" + ":\n"
+
+    return_string += add_tabs(trigger.get_content_as_string(include_trigger_definition=False), 2)
+
+    return return_string
+
+
+
+ +
+ +
+ + +
+def import_triggers(...) + +
+ + +
+ +

Adds existing trigger objects (from another scenario) to this scenario. Keeping all (de)activate trigger +effects linked!

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
triggers + List[Trigger] + +
+

The list of Trigger objects to be added

+
+
+ required +
index + int + +
+

The index where to insert the new triggers, will be added at the end when left unused.

+
+
+ -1 +
deepcopy + bool + +
+

If the given triggers need to be deep copied or not when importing. Can be useful to keep the +reference alive between the source and target trigger the same when setting this to False.

+
+
+ True +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Trigger] + +
+

The newly added triggers (with the new IDs and activation links etc.)

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
def import_triggers(self, triggers: List[Trigger], index: int = -1, deepcopy: bool = True) -> List[Trigger]:
+    """
+    Adds existing trigger objects (from another scenario) to this scenario. Keeping all ``(de)activate trigger``
+    effects linked!
+
+    Args:
+        triggers: The list of Trigger objects to be added
+        index: The index where to insert the new triggers, will be added at the end when left unused.
+        deepcopy: If the given triggers need to be deep copied or not when importing. Can be useful to keep the
+            reference alive between the source and target trigger the same when setting this to `False`.
+
+    Returns:
+        The newly added triggers (with the new IDs and activation links etc.)
+    """
+    if deepcopy:
+        triggers = copy.deepcopy(triggers)
+    index_changes = {}
+
+    for offset, trigger in enumerate(triggers):
+        new_index = len(self.triggers) + offset
+        index_changes[trigger.trigger_id] = trigger.trigger_id = new_index
+
+    for trigger in triggers:
+        for i, effect in enumerate(get_activation_effects(trigger)):
+            try:
+                effect.trigger_id = index_changes[effect.trigger_id]
+            except KeyError:
+                warn(f"(De)Activation effect {i} in trigger '{trigger.name}' refers to a trigger that wasn't "
+                     f"included in the imported triggers. Effect will be reset")
+                effect.trigger_id = -1
+
+    self.triggers += triggers
+    if index != -1:
+        self.move_triggers([t.trigger_id for t in triggers], index)
+    return triggers
+
+
+
+ +
+ +
+ + +
+def move_triggers(...) + +
+ + +
+ +

Moves the given IDs from anywhere to the split index. This function reorders triggers BUT keeps +(de)activate trigger effects linked properly!

+

As an example:

+
1
+2
+3
+4
[0,1,2,3,4,5,6,7,8]  # Current index order
+# Let's move trigger 1, 4, 5 and 6 to location 2
+self.move_triggers([1, 4, 5, 6], 2)  # << 2 is an INDEX, not the value
+[0,1,4,5,6,2,3,7,8]  # New index order
+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_ids + List[int] + +
+

The trigger IDs to move

+
+
+ required +
insert_index + int + +
+

The index that defines where to insert the triggers

+
+
+ required +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
def move_triggers(self, trigger_ids: List[int], insert_index: int) -> None:
+    """
+    Moves the given IDs from anywhere to the split index. This function reorders triggers BUT keeps
+    ``(de)activate trigger`` effects linked properly!
+
+    As an example:
+
+    ```
+    [0,1,2,3,4,5,6,7,8]  # Current index order
+    # Let's move trigger 1, 4, 5 and 6 to location 2
+    self.move_triggers([1, 4, 5, 6], 2)  # << 2 is an INDEX, not the value
+    [0,1,4,5,6,2,3,7,8]  # New index order
+    ```
+
+    Args:
+        trigger_ids: The trigger IDs to move
+        insert_index: The index that defines where to insert the triggers
+    """
+    if min(trigger_ids) < 0:
+        raise ValueError(f"Trigger IDs cannot be negative")
+
+    if insert_index >= len(self.trigger_display_order):
+        # Add to the end of the list
+        new_trigger_id_order = [n for n in self.trigger_display_order if n not in trigger_ids]
+        new_trigger_id_order += trigger_ids
+    else:
+        insert_num = self.trigger_display_order[insert_index]
+        new_trigger_id_order = [n for n in self.trigger_display_order if n not in trigger_ids or n == insert_num]
+
+        split_index = new_trigger_id_order.index(insert_num)
+
+        if insert_num in trigger_ids:
+            new_trigger_id_order.remove(insert_num)
+
+        new_trigger_id_order = new_trigger_id_order[:split_index] + trigger_ids + new_trigger_id_order[split_index:]
+    self.reorder_triggers(new_trigger_id_order)
+
+
+
+ +
+ +
+ + +
+def remove_trigger(...) + +
+ + +
+ +

Removes a trigger BUT keeps (de)activate trigger effects linked properly!

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select.

+
+
+ required +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
574
+575
+576
+577
+578
+579
+580
+581
def remove_trigger(self, trigger_select: int | TriggerSelect) -> None:
+    """
+    Removes a trigger BUT keeps ``(de)activate trigger`` effects linked properly!
+
+    Args:
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+    """
+    self.remove_triggers([trigger_select])
+
+
+
+ +
+ +
+ + +
+def remove_triggers(...) + +
+ + +
+ +

Removes a list of triggers BUT keeps (de)activate trigger effects linked properly!

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_selects + List[int | TriggerSelect] + +
+

The list with trigger IDs

+
+
+ required +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
def remove_triggers(self, trigger_selects: List[int | TriggerSelect]) -> None:
+    """
+    Removes a list of triggers BUT keeps ``(de)activate trigger`` effects linked properly!
+
+    Args:
+        trigger_selects: The list with trigger IDs
+    """
+    removing_trigger_ids = [
+        self._validate_and_retrieve_trigger_info(trigger_select)[0] for trigger_select in trigger_selects
+    ]
+    removing_trigger_ids.sort(reverse=True)
+
+    new_display_order = self.compute_updated_display_order(removing_trigger_ids)
+
+    for trigger_id in removing_trigger_ids:
+        del self.triggers[trigger_id]
+
+    index_changes = {}
+    for new_index, trigger in enumerate(self.triggers):
+        if new_index != trigger.trigger_id:
+            index_changes[trigger.trigger_id] = new_index
+            trigger.trigger_id = new_index
+
+    # Find and update all (de)activation effect trigger references
+    for trigger in self.triggers:
+        for effect in get_activation_effects(trigger):
+            if effect.trigger_id in index_changes:
+                effect.trigger_id = index_changes[effect.trigger_id]
+
+    self.trigger_display_order = new_display_order
+
+
+
+ +
+ +
+ + +
+def reorder_triggers(...) + +
+ + +
+ +

Reorder all triggers to a given order of IDs. This function reorders triggers BUT keeps (de)activate trigger +effects linked properly!

+

Examples:

+
1
+2
+3
+4
+5
+6
+7
+8
+9
Moving the 6th trigger to the end of the trigger list::
+
+    [0,1,2,3,4,5,6,7,8]  # Trigger IDs before
+    self.reorder_triggers([0,1,2,3,5,4,7,8,6])
+    [0,1,2,3,5,4,7,8,6]  # Trigger IDs after
+
+Setting the trigger (execution) order to the current display order::
+
+    self.reorder_triggers(self.trigger_display_order)
+
+

Keep in mind that all trigger IDs will get remapped with this function. So trigger_manager.triggers[4] might +result in a different trigger after this function is called in comparison to before.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
new_id_order + List[int] + +
+

The new trigger order. Uses the current display order when left unused

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
def reorder_triggers(self, new_id_order: List[int] = None, ) -> None:
+    """
+    Reorder all triggers to a given order of IDs. This function reorders triggers BUT keeps ``(de)activate trigger``
+    effects linked properly!
+
+    Examples:
+
+        Moving the 6th trigger to the end of the trigger list::
+
+            [0,1,2,3,4,5,6,7,8]  # Trigger IDs before
+            self.reorder_triggers([0,1,2,3,5,4,7,8,6])
+            [0,1,2,3,5,4,7,8,6]  # Trigger IDs after
+
+        Setting the trigger (execution) order to the current display order::
+
+            self.reorder_triggers(self.trigger_display_order)
+
+    Keep in mind that all trigger IDs will get remapped with this function. So ``trigger_manager.triggers[4]`` might
+    result in a different trigger after this function is called in comparison to before.
+
+    Args:
+        new_id_order: The new trigger order. Uses the current display order when left unused
+    """
+    if new_id_order is not None:
+        if min(new_id_order) < 0:
+            raise ValueError(f"Trigger IDs cannot be negative")
+        self.trigger_display_order = new_id_order
+
+    new_triggers_list = []
+    index_changes = {}
+    for new_index, index in enumerate(self.trigger_display_order):
+        try:
+            trigger = self.triggers[index]
+        except IndexError:
+            raise ValueError(f"The trigger ID {index} doesn't exist") from None
+        index_changes[trigger.trigger_id] = new_index
+
+        trigger.trigger_id = new_index
+        new_triggers_list.append(trigger)
+    self.triggers = new_triggers_list
+
+    # Find and update all (de)activation effect trigger references
+    for trigger in self.triggers:
+        for effect in get_activation_effects(trigger):
+            if effect.trigger_id in index_changes:
+                effect.trigger_id = index_changes[effect.trigger_id]
+
+
+
+ +
+ +
+ + +
+def replace_player(...) + +
+ + +
+ +

Replaces player attributes. Specifically useful if multiple players are used in the same trigger.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_select + int | TriggerSelect + +
+

The ID of the trigger or an object used to identify which trigger to select.

+
+
+ required +
to_player + PlayerId + +
+

The player the attributes are changed to.

+
+
+ required +
only_change_from + PlayerId + +
+

Can only change player attributes if the player is equal to the given value

+
+
+ None +
include_player_source + bool + +
+

If set to True, allow player source attributes to be changed while replacing. +Player source attributes are attributes where a player is defined to perform an action such as create an +object. If set to False these attributes will remain unchanged.

+
+
+ True +
include_player_target + bool + +
+

If set to True, allow player target attributes to be changed while replacing. +Player target attributes are attributes where a player is defined as the target such as change ownership +or sending resources. If set to False these attributes will remain unchanged.

+
+
+ False +
trigger_ce_lock + TriggerCELock + +
+

The TriggerCELock object. Used to lock certain (types) of conditions or +effects from being changed.

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Trigger + +
+

The given trigger with the proper player attributes changed

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
def replace_player(
+        self,
+        trigger_select: int | TriggerSelect,
+        to_player: PlayerId,
+        only_change_from: PlayerId = None,
+        include_player_source: bool = True,
+        include_player_target: bool = False,
+        trigger_ce_lock: TriggerCELock = None
+) -> Trigger:
+    """
+    Replaces player attributes. Specifically useful if multiple players are used in the same trigger.
+
+    Args:
+        trigger_select: The ID of the trigger or an object used to identify which trigger to select.
+        to_player: The player the attributes are changed to.
+        only_change_from: Can only change player attributes if the player is equal to the given value
+        include_player_source: If set to `True`,  allow player source attributes to be changed while replacing.
+            Player source attributes are attributes where a player is defined to perform an action such as create an
+            object. If set to `False` these attributes will remain unchanged.
+        include_player_target: If set to `True`,  allow player target attributes to be changed while replacing.
+            Player target attributes are attributes where a player is defined as the target such as change ownership
+            or sending resources. If set to `False` these attributes will remain unchanged.
+        trigger_ce_lock: The TriggerCELock object. Used to lock certain (types) of conditions or
+            effects from being changed.
+
+    Returns:
+        The given trigger with the proper player attributes changed
+    """
+    trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
+    alter_conditions, alter_effects = TriggerManager._find_alterable_ce(trigger, trigger_ce_lock)
+
+    for cond_x in alter_conditions:
+        cond = trigger.conditions[cond_x]
+        if value_is_valid(cond.source_player) and include_player_source:
+            if only_change_from is not None and only_change_from != cond.source_player:
+                continue
+            cond.source_player = PlayerId(to_player)
+        if value_is_valid(cond.target_player) and include_player_target:
+            if only_change_from is not None and only_change_from != cond.target_player:
+                continue
+            cond.target_player = PlayerId(to_player)
+    for effect_x in alter_effects:
+        effect = trigger.effects[effect_x]
+        if value_is_valid(effect.source_player) and include_player_source:
+            if only_change_from is not None and only_change_from != effect.source_player:
+                continue
+            effect.source_player = PlayerId(to_player)
+        if value_is_valid(effect.target_player) and include_player_target:
+            if only_change_from is not None and only_change_from != effect.target_player:
+                continue
+            effect.target_player = PlayerId(to_player)
+
+    return trigger
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+ +
+ + +

+def get_activation_effects(...) + +

+ + +
+ +

Get all activation effects in a Trigger

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger + Trigger + +
+

The trigger object

+
+
+ required +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Effect] + +
+

A list with (de)activation effects

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/trigger_manager.py +
790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
def get_activation_effects(trigger: Trigger) -> List[Effect]:
+    """
+    Get all activation effects in a Trigger
+
+    Args:
+        trigger: The trigger object
+
+    Returns:
+        A list with (de)activation effects
+    """
+    return [eff for eff in trigger.effects if eff.effect_type in [
+        EffectId.ACTIVATE_TRIGGER, EffectId.DEACTIVATE_TRIGGER
+    ]]
+
+
+
+ +
+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/trigger_manager_de/index.html b/api_docs/trigger/trigger_manager_de/index.html new file mode 100644 index 00000000..b865f343 --- /dev/null +++ b/api_docs/trigger/trigger_manager_de/index.html @@ -0,0 +1,3278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + TriggerManagerDE - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

TriggerManagerDE

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ TriggerManagerDE + + +

+ + +
+

+ Bases: TriggerManager

+ + +

Manager of all DE trigger related features

+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/trigger_manager_de.py +
 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
+124
+125
+126
+127
+128
class TriggerManagerDE(TriggerManager):
+    """Manager of all DE trigger related features"""
+    _link_list = [
+        RetrieverObjectLinkGroup("Triggers", group=[
+            RetrieverObjectLink("triggers", link="trigger_data", process_as_object=Trigger),
+            RetrieverObjectLink("trigger_display_order", link="trigger_display_order_array"),
+            RetrieverObjectLink("variables", link="variable_data", process_as_object=Variable),
+        ])
+    ]
+
+    def __init__(self, triggers: List[Trigger], trigger_display_order: List[int], variables: List[Variable], **kwargs):
+        super().__init__(triggers, trigger_display_order, **kwargs)
+
+        self.variables: List[Variable] = variables
+
+    @property
+    def variables(self) -> List[Variable]:
+        """All currently renamed variables"""
+        return self._variables
+
+    @variables.setter
+    def variables(self, value: List[Variable]):
+        self._variables = UuidList(self._uuid, value)
+
+    def add_variable(self, name: str, variable_id: int = -1) -> Variable:
+        """
+        Adds a variable.
+
+        Args:
+            name: The name for the variable
+            variable_id: The ID of the variable. If left empty lowest available value will be used
+
+        Returns:
+            The newly renamed Variable
+        """
+        list_of_var_ids = [var.variable_id for var in self.variables]
+        if variable_id == -1:
+            for i in range(256):
+                if i not in list_of_var_ids:
+                    variable_id = i
+                    break
+            if variable_id == -1:
+                raise IndexError(f"No variable ID available. All in use? In use: ({list_of_var_ids}/256)")
+        if not (0 <= variable_id <= 255):
+            raise ValueError("Variable ID has to fall between 0 and 255 (incl).")
+        if variable_id in list_of_var_ids:
+            raise ValueError("Variable ID already in use.")
+
+        new_variable = Variable(variable_id=variable_id, name=name, uuid=self._uuid)
+        self.variables.append(new_variable)
+        return new_variable
+
+    def get_variable(self, variable_id: int = None, variable_name: str = None) -> Optional[Variable]:
+        """
+        Get a specific variable
+
+        Args:
+            variable_id: The ID of the variable you want
+            variable_name: The name of the variable you want
+
+        Returns:
+            The `Variable` object or None if it couldn't be found
+        """
+        if not mutually_exclusive(variable_id is not None, variable_name is not None):
+            raise ValueError("Select a variable using either the variable_id or variable_name parameters.")
+        for variable in self.variables:
+            if variable.variable_id == variable_id or variable.name == variable_name:
+                return variable
+        return None
+
+    def get_summary_as_string(self) -> str:
+        """
+        Create a human-readable string showcasing a summary of the content of the manager.
+        This includes all triggers and the amount of conditions and effects they hold and also all renamed variables.
+
+        Returns:
+            The created string
+        """
+        return_string = super().get_summary_as_string()
+
+        return_string += "\nVariables Summary:\n"
+        if len(self.variables) == 0:
+            return_string += "\t<< No Variables >>"
+
+        longest_variable_name = -1
+        for variable in self.variables:
+            longest_variable_name = max(longest_variable_name, len(variable.name))
+
+        longest_variable_name += 3
+        for index, variable in enumerate(self.variables):
+            var_name = variable.name
+            name_buffer = " " * (longest_variable_name - len(var_name))
+            return_string += f"\t{var_name}{name_buffer}[Index: {variable.variable_id}]\n"
+
+        return return_string
+
+    def get_content_as_string(self) -> str:
+        """
+        Create a human-readable string showcasing all content of the manager.
+        This includes all triggers and their conditions and effects and also all renamed variables.
+
+        This is also the function that is called when doing: `print(trigger_manager)`
+
+        Returns:
+            The created string
+        """
+        return_string = super().get_content_as_string()
+
+        return_string += "Variables:\n"
+
+        if len(self.variables) == 0:
+            return_string += "\t<<No Variables>>\n"
+
+        for variable in self.variables:
+            return_string += f"\t'{variable.name}' [Index: {variable.variable_id}] ({variable._uuid})\n"
+
+        return return_string
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+variables: List[Variable] + + + property + writable + + +
+ +
+ Type: +List[Variable] + +
+ + + + + +
+ +

All currently renamed variables

+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
triggers + List[Trigger] + - + required +
trigger_display_order + List[int] + - + required +
variables + List[Variable] + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/trigger_manager_de.py +
22
+23
+24
+25
def __init__(self, triggers: List[Trigger], trigger_display_order: List[int], variables: List[Variable], **kwargs):
+    super().__init__(triggers, trigger_display_order, **kwargs)
+
+    self.variables: List[Variable] = variables
+
+
+
+ +
+ +
+ + +
+def add_variable(...) + +
+ + +
+ +

Adds a variable.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + +
+

The name for the variable

+
+
+ required +
variable_id + int + +
+

The ID of the variable. If left empty lowest available value will be used

+
+
+ -1 +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Variable + +
+

The newly renamed Variable

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/de/trigger_manager_de.py +
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
def add_variable(self, name: str, variable_id: int = -1) -> Variable:
+    """
+    Adds a variable.
+
+    Args:
+        name: The name for the variable
+        variable_id: The ID of the variable. If left empty lowest available value will be used
+
+    Returns:
+        The newly renamed Variable
+    """
+    list_of_var_ids = [var.variable_id for var in self.variables]
+    if variable_id == -1:
+        for i in range(256):
+            if i not in list_of_var_ids:
+                variable_id = i
+                break
+        if variable_id == -1:
+            raise IndexError(f"No variable ID available. All in use? In use: ({list_of_var_ids}/256)")
+    if not (0 <= variable_id <= 255):
+        raise ValueError("Variable ID has to fall between 0 and 255 (incl).")
+    if variable_id in list_of_var_ids:
+        raise ValueError("Variable ID already in use.")
+
+    new_variable = Variable(variable_id=variable_id, name=name, uuid=self._uuid)
+    self.variables.append(new_variable)
+    return new_variable
+
+
+
+ +
+ +
+ + +
+def get_content_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing all content of the manager. +This includes all triggers and their conditions and effects and also all renamed variables.

+

This is also the function that is called when doing: print(trigger_manager)

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/de/trigger_manager_de.py +
108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
def get_content_as_string(self) -> str:
+    """
+    Create a human-readable string showcasing all content of the manager.
+    This includes all triggers and their conditions and effects and also all renamed variables.
+
+    This is also the function that is called when doing: `print(trigger_manager)`
+
+    Returns:
+        The created string
+    """
+    return_string = super().get_content_as_string()
+
+    return_string += "Variables:\n"
+
+    if len(self.variables) == 0:
+        return_string += "\t<<No Variables>>\n"
+
+    for variable in self.variables:
+        return_string += f"\t'{variable.name}' [Index: {variable.variable_id}] ({variable._uuid})\n"
+
+    return return_string
+
+
+
+ +
+ +
+ + +
+def get_summary_as_string(...) + +
+ + +
+ +

Create a human-readable string showcasing a summary of the content of the manager. +This includes all triggers and the amount of conditions and effects they hold and also all renamed variables.

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

The created string

+
+
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/de/trigger_manager_de.py +
 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
def get_summary_as_string(self) -> str:
+    """
+    Create a human-readable string showcasing a summary of the content of the manager.
+    This includes all triggers and the amount of conditions and effects they hold and also all renamed variables.
+
+    Returns:
+        The created string
+    """
+    return_string = super().get_summary_as_string()
+
+    return_string += "\nVariables Summary:\n"
+    if len(self.variables) == 0:
+        return_string += "\t<< No Variables >>"
+
+    longest_variable_name = -1
+    for variable in self.variables:
+        longest_variable_name = max(longest_variable_name, len(variable.name))
+
+    longest_variable_name += 3
+    for index, variable in enumerate(self.variables):
+        var_name = variable.name
+        name_buffer = " " * (longest_variable_name - len(var_name))
+        return_string += f"\t{var_name}{name_buffer}[Index: {variable.variable_id}]\n"
+
+    return return_string
+
+
+
+ +
+ +
+ + +
+def get_variable(...) + +
+ + +
+ +

Get a specific variable

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
variable_id + int + +
+

The ID of the variable you want

+
+
+ None +
variable_name + str + +
+

The name of the variable you want

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Optional[Variable] + +
+

The Variable object or None if it couldn't be found

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/de/trigger_manager_de.py +
64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
def get_variable(self, variable_id: int = None, variable_name: str = None) -> Optional[Variable]:
+    """
+    Get a specific variable
+
+    Args:
+        variable_id: The ID of the variable you want
+        variable_name: The name of the variable you want
+
+    Returns:
+        The `Variable` object or None if it couldn't be found
+    """
+    if not mutually_exclusive(variable_id is not None, variable_name is not None):
+        raise ValueError("Select a variable using either the variable_id or variable_name parameters.")
+    for variable in self.variables:
+        if variable.variable_id == variable_id or variable.name == variable_name:
+            return variable
+    return None
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/trigger_select/index.html b/api_docs/trigger/trigger_select/index.html new file mode 100644 index 00000000..f7306bb5 --- /dev/null +++ b/api_docs/trigger/trigger_select/index.html @@ -0,0 +1,2947 @@ + + + + + + + + + + + + + + + + + + + + + + + + + TriggerSelect - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

TriggerSelect

+ +
+ + + + +
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +

+TS: Type[TriggerSelect] = TriggerSelect + + + module-attribute + + +

+ +
+ Type: +Type[TriggerSelect] +
+ + Value: +TriggerSelect
+ + + + + +
+ +

Alias for TriggerSelect

+
+ +
+ + +

Classes

+ +
+ + + +

+ TriggerSelect + + +

+ + +
+ + +

Object used to select a trigger in many trigger related functions. For ease of use, the alias TS can be +called. You can also use those in combination with the class methods (factory methods). Like so:

+ + +
+ Selecting a trigger by its ID doesn't require this object +

Most functions allow you to just input 4 into the trigger_select parameter (instead of: TS.index(4))

+
+ +

Examples:

+

TS.index(4) To select the trigger with index 4

+

TS.display(4) Trigger with display index 4

+

TS.trigger(trigger) Well... The trigger object given...

+ +
+ Source code in AoE2ScenarioParser/objects/support/trigger_select.py +
 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
class TriggerSelect:
+    """
+    Object used to select a trigger in many trigger related functions. For ease of use, the alias `TS` can be
+    called. You can also use those in combination with the class methods (factory methods). Like so:
+
+    Tip: Selecting a trigger by its ID doesn't require this object
+        Most functions allow you to just input `4` into the trigger_select parameter (instead of: `TS.index(4)`)
+
+    Examples:
+        `TS.index(4)`  To select the trigger with index 4
+
+        `TS.display(4)`  Trigger with display index 4
+
+        `TS.trigger(trigger)`  Well... The trigger object given...
+    """
+    def __init__(self, trigger_index: int = None, display_index: int = None, trigger: Trigger = None):
+        """
+        Args:
+            trigger_index: The index of the trigger. Starting from 0, based on creation time
+            display_index: The display index of a trigger. Starting from 0, based on display order in the editor
+            trigger: The trigger object itself.
+        """
+        self.trigger = trigger
+        self.display_index = display_index
+        self.trigger_index = trigger_index
+
+    @classmethod
+    def index(cls, index: int):
+        return cls(trigger_index=index)
+
+    @classmethod
+    def display(cls, display_index: int):
+        return cls(display_index=display_index)
+
+    @classmethod
+    def trigger(cls, trigger: Trigger):
+        return cls(trigger=trigger)
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+display_index = display_index + + + instance-attribute + + +
+ +
+ + + Value: +display_index
+ + + + + +
+
+ +
+ +
+ + + + +
+trigger_index = trigger_index + + + instance-attribute + + +
+ +
+ + + Value: +trigger_index
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger_index + int + +
+

The index of the trigger. Starting from 0, based on creation time

+
+
+ None +
display_index + int + +
+

The display index of a trigger. Starting from 0, based on display order in the editor

+
+
+ None +
trigger + Trigger + +
+

The trigger object itself.

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/support/trigger_select.py +
21
+22
+23
+24
+25
+26
+27
+28
+29
+30
def __init__(self, trigger_index: int = None, display_index: int = None, trigger: Trigger = None):
+    """
+    Args:
+        trigger_index: The index of the trigger. Starting from 0, based on creation time
+        display_index: The display index of a trigger. Starting from 0, based on display order in the editor
+        trigger: The trigger object itself.
+    """
+    self.trigger = trigger
+    self.display_index = display_index
+    self.trigger_index = trigger_index
+
+
+
+ +
+ +
+ + +
+def display(...) + + + classmethod + + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
display_index + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/trigger_select.py +
36
+37
+38
@classmethod
+def display(cls, display_index: int):
+    return cls(display_index=display_index)
+
+
+
+ +
+ +
+ + +
+def index(...) + + + classmethod + + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
index + int + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/trigger_select.py +
32
+33
+34
@classmethod
+def index(cls, index: int):
+    return cls(trigger_index=index)
+
+
+
+ +
+ +
+ + +
+def trigger(...) + + + classmethod + + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
trigger + Trigger + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/support/trigger_select.py +
40
+41
+42
@classmethod
+def trigger(cls, trigger: Trigger):
+    return cls(trigger=trigger)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/trigger/variable/index.html b/api_docs/trigger/variable/index.html new file mode 100644 index 00000000..ff7dee5d --- /dev/null +++ b/api_docs/trigger/variable/index.html @@ -0,0 +1,2565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Variable - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Variable

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ Variable + + +

+ + +
+

+ Bases: AoE2Object

+ + +

Object for handling a variable.

+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/variable.py +
 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
class Variable(AoE2Object):
+    """Object for handling a variable."""
+
+    _link_list = [
+        RetrieverObjectLinkGroup("Triggers", "variable_data[__index__]", group=[
+            RetrieverObjectLink("variable_id"),
+            RetrieverObjectLink("name", link="variable_name"),
+        ])
+    ]
+
+    def __init__(self, variable_id: int, name: str, **kwargs):
+        self.variable_id = variable_id
+        self.name = name
+
+        super().__init__(**kwargs)
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+name = name + + + instance-attribute + + +
+ +
+ + + Value: +name
+ + + + + +
+
+ +
+ +
+ + + + +
+variable_id = variable_id + + + instance-attribute + + +
+ +
+ + + Value: +variable_id
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
variable_id + int + - + required +
name + str + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/variable.py +
18
+19
+20
+21
+22
def __init__(self, variable_id: int, name: str, **kwargs):
+    self.variable_id = variable_id
+    self.name = name
+
+    super().__init__(**kwargs)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/units/unit/index.html b/api_docs/units/unit/index.html new file mode 100644 index 00000000..bc28ae55 --- /dev/null +++ b/api_docs/units/unit/index.html @@ -0,0 +1,3525 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Unit - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Unit

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ Unit + + +

+ + +
+

+ Bases: AoE2Object

+ + +

A class representing a single unit on the map. +This can be an archer, a gold mine, a house or even a tree.

+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/unit.py +
 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
class Unit(AoE2Object):
+    """
+    A class representing a single unit on the map.
+    This can be an archer, a gold mine, a house or even a tree.
+    """
+    _link_list = [
+        RetrieverObjectLink("player", retrieve_history_number=0),
+        RetrieverObjectLinkGroup("Units", "players_units[__index__].units[__index__]", group=[
+            RetrieverObjectLink("x"),
+            RetrieverObjectLink("y"),
+            RetrieverObjectLink("z"),
+            RetrieverObjectLink("reference_id"),
+            RetrieverObjectLink("unit_const"),
+            RetrieverObjectLink("status"),
+            RetrieverObjectLink("rotation"),
+            RetrieverObjectLink("initial_animation_frame"),
+            RetrieverObjectLink("garrisoned_in_id"),
+            RetrieverObjectLink("caption_string_id", support=Support(since=1.54)),
+        ])
+    ]
+
+    def __init__(
+            self,
+            player: int | PlayerId,
+            x: float,
+            y: float,
+            z: float,
+            reference_id: int,
+            unit_const: int,
+            status: int,
+            rotation: float,
+            initial_animation_frame: int,
+            garrisoned_in_id: int = -1,
+            caption_string_id: int = -1,
+            **kwargs
+    ):
+        raise_if_not_int_subclass([unit_const])
+
+        super().__init__(**kwargs)
+
+        self._player: PlayerId = PlayerId(player)
+        self.x: float = x
+        self.y: float = y
+        self.z: float = z
+        self.reference_id: int = reference_id
+        self.unit_const: int = unit_const
+        self.status: int = status
+        self.rotation: float = rotation
+        self.initial_animation_frame: int = initial_animation_frame
+        self.garrisoned_in_id: int = garrisoned_in_id
+        self.caption_string_id: int = caption_string_id
+
+    @property
+    def player(self) -> PlayerId:
+        """The player that owns this unit"""
+        return self._player
+
+    @player.setter
+    def player(self, player: int | PlayerId):
+        actions.unit_change_ownership(self._uuid, player, self)
+        self._player = player
+
+    @property
+    def tile(self) -> Tile:
+        """The tile where the unit is located"""
+        return Tile(math.floor(self.x), math.floor(self.y))
+        # Floor x and y as location (0.9, 0.9) is still Tile[x=0, y=0]
+
+    @tile.setter
+    def tile(self, tile: Tile) -> None:
+        self.x = tile.x
+        self.y = tile.y
+
+    @property
+    def name(self) -> str:
+        """The name of the unit, nicely formatted"""
+        unit_enum = helper.get_enum_from_unit_const(self.unit_const)
+        if unit_enum:
+            return pretty_format_name(unit_enum.name)
+        else:
+            return f"Unknown{self.unit_const}"  # e.g. "Unknown411"
+
+    def __repr__(self):
+        arguments = [
+            'player=' + str(PlayerId(self.player)),
+            'x=' + str(self.x),
+            'y=' + str(self.y),
+            ('z=' + str(self.z)) if self.z else None,
+            'reference_id=' + str(self.reference_id),
+            'unit_const=' + str(self.unit_const),
+            'status=' + str(self.status),
+            'rotation=' + str(self.rotation),
+            'initial_animation_frame=' + str(self.initial_animation_frame),
+            ('garrisoned_in_id=' + str(self.garrisoned_in_id)) if self.garrisoned_in_id != -1 else None,
+            ('caption_string_id=' + str(self.caption_string_id)) if self.caption_string_id != -1 else None,
+        ]
+
+        return 'Unit(' + ', '.join(filter(None, arguments)) + ')'
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+caption_string_id: int = caption_string_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +caption_string_id
+ + + + + +
+
+ +
+ +
+ + + + +
+garrisoned_in_id: int = garrisoned_in_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +garrisoned_in_id
+ + + + + +
+
+ +
+ +
+ + + + +
+initial_animation_frame: int = initial_animation_frame + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +initial_animation_frame
+ + + + + +
+
+ +
+ +
+ + + + +
+name: str + + + property + + +
+ +
+ Type: +str + +
+ + + + + +
+ +

The name of the unit, nicely formatted

+
+ +
+ +
+ + + + +
+player: PlayerId + + + property + writable + + +
+ +
+ Type: +PlayerId + +
+ + + + + +
+ +

The player that owns this unit

+
+ +
+ +
+ + + + +
+reference_id: int = reference_id + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +reference_id
+ + + + + +
+
+ +
+ +
+ + + + +
+rotation: float = rotation + + + instance-attribute + + +
+ +
+ Type: +float +
+ + Value: +rotation
+ + + + + +
+
+ +
+ +
+ + + + +
+status: int = status + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +status
+ + + + + +
+
+ +
+ +
+ + + + +
+tile: Tile + + + property + writable + + +
+ +
+ Type: +Tile + +
+ + + + + +
+ +

The tile where the unit is located

+
+ +
+ +
+ + + + +
+unit_const: int = unit_const + + + instance-attribute + + +
+ +
+ Type: +int +
+ + Value: +unit_const
+ + + + + +
+
+ +
+ +
+ + + + +
+x: float = x + + + instance-attribute + + +
+ +
+ Type: +float +
+ + Value: +x
+ + + + + +
+
+ +
+ +
+ + + + +
+y: float = y + + + instance-attribute + + +
+ +
+ Type: +float +
+ + Value: +y
+ + + + + +
+
+ +
+ +
+ + + + +
+z: float = z + + + instance-attribute + + +
+ +
+ Type: +float +
+ + Value: +z
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
player + int | PlayerId + - + required +
x + float + - + required +
y + float + - + required +
z + float + - + required +
reference_id + int + - + required +
unit_const + int + - + required +
status + int + - + required +
rotation + float + - + required +
initial_animation_frame + int + - + required +
garrisoned_in_id + int + - + -1 +
caption_string_id + int + - + -1 +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/data_objects/unit.py +
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
def __init__(
+        self,
+        player: int | PlayerId,
+        x: float,
+        y: float,
+        z: float,
+        reference_id: int,
+        unit_const: int,
+        status: int,
+        rotation: float,
+        initial_animation_frame: int,
+        garrisoned_in_id: int = -1,
+        caption_string_id: int = -1,
+        **kwargs
+):
+    raise_if_not_int_subclass([unit_const])
+
+    super().__init__(**kwargs)
+
+    self._player: PlayerId = PlayerId(player)
+    self.x: float = x
+    self.y: float = y
+    self.z: float = z
+    self.reference_id: int = reference_id
+    self.unit_const: int = unit_const
+    self.status: int = status
+    self.rotation: float = rotation
+    self.initial_animation_frame: int = initial_animation_frame
+    self.garrisoned_in_id: int = garrisoned_in_id
+    self.caption_string_id: int = caption_string_id
+
+
+
+ +
+ +
+ + +
+def __repr__(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/data_objects/unit.py +
 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
def __repr__(self):
+    arguments = [
+        'player=' + str(PlayerId(self.player)),
+        'x=' + str(self.x),
+        'y=' + str(self.y),
+        ('z=' + str(self.z)) if self.z else None,
+        'reference_id=' + str(self.reference_id),
+        'unit_const=' + str(self.unit_const),
+        'status=' + str(self.status),
+        'rotation=' + str(self.rotation),
+        'initial_animation_frame=' + str(self.initial_animation_frame),
+        ('garrisoned_in_id=' + str(self.garrisoned_in_id)) if self.garrisoned_in_id != -1 else None,
+        ('caption_string_id=' + str(self.caption_string_id)) if self.caption_string_id != -1 else None,
+    ]
+
+    return 'Unit(' + ', '.join(filter(None, arguments)) + ')'
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/units/unit_manager/index.html b/api_docs/units/unit_manager/index.html new file mode 100644 index 00000000..a865b72a --- /dev/null +++ b/api_docs/units/unit_manager/index.html @@ -0,0 +1,5873 @@ + + + + + + + + + + + + + + + + + + + + + + + + + UnitManager - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

UnitManager

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ UnitManager + + +

+ + +
+

+ Bases: AoE2Object

+ + +

Manager of everything unit related.

+ +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
 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
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
class UnitManager(AoE2Object):
+    """Manager of everything unit related."""
+
+    _link_list = [
+        RetrieverObjectLink("_player_units", "Units", "players_units", process_as_object=PlayerUnits),
+        RetrieverObjectLink("next_unit_id", "DataHeader", "next_unit_id_to_place")
+    ]
+
+    def __init__(
+            self,
+            _player_units: List[PlayerUnits],
+            next_unit_id: int,
+            **kwargs
+    ):
+        super().__init__(**kwargs)
+
+        self.units: List[List[Unit]] = [pu.units for pu in _player_units]
+        self.reference_id_generator: Generator[int] = create_id_generator(next_unit_id)
+
+    @property
+    def next_unit_id(self):
+        return self.get_new_reference_id()
+
+    @property
+    def units(self):
+        return self._units
+
+    @units.setter
+    def units(self, value: List[List[Unit]]):
+        def _raise():
+            raise ValueError("Units should be list with a maximum of 9 sub lists, example: [[Unit], [Unit, Unit], ...]")
+
+        if len(value) > 9:
+            _raise()
+        elif len(value) < 9:
+            value.extend([[] for _ in range(9 - len(value))])
+
+        self._units = UuidList(self._uuid, value)
+
+    def update_unit_player_values(self):
+        """Function to update all player values in all units. Useful when units are moved manually (in mass)."""
+        for player in PlayerId.all():
+            for unit in self.units[player]:
+                unit._player = player
+
+    def clone_unit(
+            self,
+            unit: Unit,
+            player: int | PlayerId = None,
+            unit_const: int = None,
+            x: float = None,
+            y: float = None,
+            z: float = None,
+            rotation: float = None,
+            garrisoned_in_id: int = None,
+            animation_frame: int = None,
+            status: int = None,
+            reference_id: int = None,
+            tile: Tile | Tuple[int, int] = None,
+    ) -> Unit:
+        """
+        Clones an existing unit with the adjusted variables. Everything except the initial unit is optional.
+        When arguments are provided, they will override the corresponding values in the cloned unit.
+
+        Args:
+            unit: The unit to clone
+            player: The player to set the cloned unit to (If not provided, the original player will be used)
+            unit_const: The unit you're placing (If not provided, the original unit constant will be used)
+            x: The X coordinate of the cloned unit (If not provided, the original x coordinate will be used)
+            y: The Y coordinate of the cloned unit (If not provided, the original y coordinate will be used)
+            z: The Z coordinate of the cloned unit (If not provided, the original z coordinate will be used)
+            rotation: The rotation of the cloned unit (If not provided, the original rotation will be used)
+            garrisoned_in_id: The id of the garrisoned unit (If not provided, the original garrisoned id will be used)
+            animation_frame: The animation frame of the cloned unit (If not provided, the original animation frame will be used)
+            status: The status of the cloned unit (If not provided, the original status will be used)
+            reference_id: Reference id of the cloned unit (If not provided, a new reference id will be generated)
+            tile: The tile of the cloned unit (If not provided, the original x,y coordinates will be used)
+
+        Returns:
+            The cloned unit
+        """
+
+        if (x is not None or y is not None) and tile is not None:
+            raise ValueError("Cannot use both x,y notation and tile notation at the same time")
+
+        return self.add_unit(
+            player=player or unit.player,
+            unit_const=unit_const or unit.unit_const,
+            x=x or unit.x,
+            y=y or unit.y,
+            z=z or unit.z,
+            rotation=rotation or unit.rotation,
+            garrisoned_in_id=garrisoned_in_id or unit.garrisoned_in_id,
+            animation_frame=animation_frame or unit.initial_animation_frame,
+            status=status or unit.status,
+            reference_id=reference_id,
+            tile=tile,
+        )
+
+    def add_unit(
+            self,
+            player: int | PlayerId,
+            unit_const: int,
+            x: float = 0,
+            y: float = 0,
+            z: float = 0,
+            rotation: float = 0,
+            garrisoned_in_id: int = -1,
+            animation_frame: int = 0,
+            status: int = 2,
+            reference_id: int = None,
+            caption_string_id: int = -1,
+            tile: Tile | Tuple[int, int] = None,
+    ) -> Unit:
+        """
+        Adds a unit to the scenario.
+
+        Args:
+            player: The player the unit belongs to.
+            unit_const: Defines what unit you're placing. The IDs used in the unit/buildings dataset.
+            x: The x location in the scenario.
+            y: The y location in the scenario.
+            z: The z (height) location in the scenario.
+            rotation: The rotation of the unit.
+            garrisoned_in_id: The reference_id of another unit this unit is garrisoned in.
+            animation_frame: The animation frame of the unit.
+            status: Unknown - Always 2. 0-6 no difference (?) | 7-255 makes it disappear. (Except from the mini-map)
+            reference_id: The reference ID of this unit. Normally added automatically. Used for garrisoning or reference
+                in triggers
+            caption_string_id: A string ID for the caption of a unit
+            tile: An object that represents a tile on the map. Replaces parameters x and y. Also, automatically adds
+                .5 to both ints to place the unit centered on the tile.
+
+        Returns:
+            The Unit created
+        """
+        if reference_id is None:
+            reference_id = self.get_new_reference_id()
+
+        unit = Unit(
+            player=player,
+            x=x if tile is None else (tile[0] + .5),
+            y=y if tile is None else (tile[1] + .5),
+            z=z,
+            reference_id=reference_id,
+            unit_const=unit_const,
+            status=status,
+            rotation=rotation,
+            initial_animation_frame=animation_frame,
+            garrisoned_in_id=garrisoned_in_id,
+            caption_string_id=caption_string_id,
+            uuid=self._uuid
+        )
+
+        self.units[player].append(unit)
+        return unit
+
+    def get_player_units(self, player: int | PlayerId) -> List[Unit]:
+        """
+        Returns a list of UnitObjects for the given player.
+
+        Raises:
+            ValueError: If player is not between 0 (GAIA) and 8 (EIGHT)
+        """
+        if not 0 <= player <= 8:
+            raise ValueError("Player must have a value between 0 and 8")
+        return self.units[player]
+
+    def get_all_units(self) -> List[Unit]:
+        units = []
+        for player_units in self.units:
+            units += player_units
+        return units
+
+    def filter_units_by(
+            self,
+            attr: str,
+            unit_attrs: List[int],
+            blacklist: bool = False,
+            player_list: List[Union[int, PlayerId]] = None,
+            unit_list: List[Unit] = None
+    ) -> List[Unit]:
+        """
+        Filter units based on a given attribute of units
+
+        Args:
+            attr: The attribute to filter by
+            unit_attrs: The values for the attributes to filter with
+            blacklist: Use the given constant list as blacklist instead of whitelist
+            player_list: A list of players to filter from. If not used, all players are used.
+            unit_list: A set of units to filter from. If not used, all units are used.
+
+        Returns:
+            A list of units
+
+        Raises:
+            AttributeError: If the provided attr does not exist on objects of the Unit class
+        """
+
+        if unit_list is None:
+            unit_list = self.get_all_units()
+        if player_list is not None:
+            unit_list = [unit for unit in unit_list if unit.player in player_list]
+
+        if len(unit_list) == 0:
+            return []
+
+        unit = unit_list[0]
+        if not hasattr(unit, attr):
+            raise AttributeError(f"Cannot filter Unit objects by {attr}")
+
+        # Both return statements can be combined using: ((unit.unit_const in unit_consts) != blacklist)
+        # But splitting them helps performance (not checking against blacklist for each entry)
+        if not blacklist:
+            return [unit for unit_attr in unit_attrs for unit in unit_list if getattr(unit, attr) == unit_attr]
+        return [unit for unit_attr in unit_attrs for unit in unit_list if getattr(unit, attr) != unit_attr]
+
+    def filter_units_by_const(
+            self,
+            unit_consts: List[int],
+            blacklist: bool = False,
+            player_list: List[Union[int, PlayerId]] = None,
+            unit_list: List[Unit] = None
+    ) -> List[Unit]:
+        """
+        Filter unit on their unit_const value.
+
+        Args:
+            unit_consts: The constants to filter with
+            blacklist: Use the given constant list as blacklist instead of whitelist
+            player_list: A list of players to filter from. If not used, all players are used.
+            unit_list: A set of units to filter from. If not used, all units are used.
+
+        Returns:
+            A list of units
+        """
+        return self.filter_units_by("unit_const", unit_consts, blacklist, player_list, unit_list)
+
+    def filter_units_by_reference_id(
+            self,
+            unit_reference_ids: List[int],
+            blacklist: bool = False,
+            player_list: List[Union[int, PlayerId]] = None,
+            unit_list: List[Unit] = None
+    ) -> List[Unit]:
+        """
+        Filter unit on their unit_const value.
+
+        Args:
+            unit_reference_ids (List[int]): The reference_ids to filter with
+            blacklist (bool): Use the given constant list as blacklist instead of whitelist
+            player_list (List[int]): A list of players to filter from. If not used, all players are used.
+            unit_list (List[Unit]): A set of units to filter from. If not used, all units are used.
+
+        Returns:
+            A list of units
+        """
+        return self.filter_units_by("reference_id", unit_reference_ids, blacklist, player_list, unit_list)
+
+    def get_units_in_area(
+            self,
+            x1: float = None,
+            y1: float = None,
+            x2: float = None,
+            y2: float = None,
+            tile1: Tile = None,
+            tile2: Tile = None,
+            unit_list: List[Unit] = None,
+            players: List[Union[int, PlayerId]] = None,
+            ignore_players: List[PlayerId] = None
+    ) -> List[Unit]:
+        """
+        Returns all units in the square with left corner (x1, y1) and right corner (x2, y2). Both corners inclusive.
+
+        Args:
+            x1: The X location of the left corner
+            y1: The Y location of the left corner
+            x2: The X location of the right corner
+            y2: The Y location of the right corner
+            tile1: The x,y location of the 1st corner as Tile Object
+            tile2: The x,y location of the 2nd corner as Tile Object
+            unit_list: (Optional) A list of units (Defaults to all units in the map, including GAIA (Trees etc.)
+            players: (Optional) A list of Players which units need to be selected from the selected area
+            ignore_players: (Optional) A list of Players which units need to be ignored from the selected area
+
+        Raises:
+            ValueError: if not all 4 (x1, y1, x2 and y2) are used simultaneously.
+                Or if both (tile1 and tile2) are not used simultaneously.
+                Or if any of the 4 (x1, y1, x2, y2) is used together with any of (tile1, tile2). Use one or the other.
+                Or if players and ignore_players are used simultaneously.
+        """
+        if (x1 is not None or y1 is not None or x2 is not None or y2 is not None) and any([tile1, tile2]):
+            raise ValueError("Cannot use both x1,y1,x2,y2 notation and tile1,tile2 notation at the same time")
+        if (x1 is not None or y1 is not None or x2 is not None or y2 is not None) and \
+                (x1 is None or y1 is None or x2 is None or y2 is None):
+            raise ValueError("Cannot use some but not all from x1,y1,x2,y2.")
+        if (not all([tile1, tile2])) and any([tile1, tile2]):
+            raise ValueError("Cannot use one from tile1, tile2. Use both.")
+        if players is not None and ignore_players is not None:
+            raise ValueError("Cannot use both whitelist (players) and blacklist (ignore_players) at the same time")
+
+        if tile1:
+            x1 = tile1.x
+            y1 = tile1.y
+            x2 = tile2.x
+            y2 = tile2.y
+        else:
+            # Inclusive selection
+            x2 += 1
+            y2 += 1
+
+        if players is not None:
+            players = players
+        elif ignore_players is not None:
+            players = [p for p in PlayerId if p not in ignore_players]
+        else:
+            players = [p for p in PlayerId]
+
+        if unit_list is None:
+            unit_list = self.get_all_units()
+
+        return [unit for unit in unit_list if x1 <= unit.x <= x2 and y1 <= unit.y <= y2 and unit.player in players]
+
+    @staticmethod
+    def change_ownership(unit: Unit | List[Unit], to_player: int | PlayerId) -> None:
+        """
+        Changes a unit's ownership to the given player.
+
+        Args:
+            unit: The unit object which ownership will be changed
+            to_player: The player that'll get ownership over the unit (using PlayerId enum)
+        """
+        if isinstance(unit, list):
+            for u in unit:
+                u.player = to_player
+        else:
+            unit.player = to_player
+
+    def get_new_reference_id(self) -> int:
+        """
+        Get a new ID each time the function is called. Starting from the current highest ID.
+
+        Returns:
+            The newly generated ID
+        """
+        return next(self.reference_id_generator)
+
+    def find_highest_reference_id(self) -> int:
+        """
+        Find the highest ID in the map. Searches through all units for the highest ID.
+
+        Returns:
+            The highest ID in the map
+        """
+        highest_id = 0  # If no units, default to 0
+        for player in PlayerId.all():
+            for unit in self.units[player]:
+                highest_id = max(highest_id, unit.reference_id)
+        return highest_id
+
+    def remove_unit(self, reference_id: int = None, unit: Unit = None) -> None:
+        """
+        Removes a unit. Please note that `unit=...` is a lot faster than `reference_id=...` due to reference_id having
+        to search through all units on the map. And unit has an ownership (player) attribute which is used for knowing
+        which list to remove the unit from.
+
+        Args:
+            reference_id: The id of the unit. Note that this is NOT a unit constant (So NOT: UnitInfo.ARCHER)
+            unit: The Unit object to be removed.
+        """
+        if reference_id is not None and unit is not None:
+            raise ValueError("Cannot use both unit_ref_id and unit arguments. Use one or the other.")
+        if reference_id is None and unit is None:
+            raise ValueError("Both unit_ref_id and unit arguments were unused. Use one.")
+
+        if reference_id is not None:
+            for player in range(0, 9):
+                for i, unit in enumerate(self.units[player]):
+                    if unit.reference_id == reference_id:
+                        del self.units[player][i]
+                        return
+        elif unit is not None:
+            self.units[unit.player].remove(unit)
+
+    def remove_eye_candy(self) -> None:
+        eye_candy_ids = [1351, 1352, 1353, 1354, 1355, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, 1366]
+        self.units[0] = [gaia_unit for gaia_unit in self.units[0] if gaia_unit.unit_const not in eye_candy_ids]
+
+    # ###############################################################################################
+    # ################################# Functions for reconstruction ################################
+    # ###############################################################################################
+
+    @property
+    def _player_units(self):
+        player_units = []
+        for i in range(9):
+            units = self.get_player_units(i)
+            player_units.append(PlayerUnits(unit_count=len(units), units=units))
+
+        return UuidList(self._uuid, player_units)
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+next_unit_id + + + property + + +
+ +
+ + +
+ + + + + +
+
+ +
+ +
+ + + + +
+reference_id_generator: Generator[int] = create_id_generator(next_unit_id) + + + instance-attribute + + +
+ +
+ Type: +Generator[int] +
+ + Value: +create_id_generator(next_unit_id)
+ + + + + +
+
+ +
+ +
+ + + + +
+units + + + property + writable + + +
+ +
+ + +
+ + + + + +
+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
_player_units + List[PlayerUnits] + - + required +
next_unit_id + int + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
22
+23
+24
+25
+26
+27
+28
+29
+30
+31
def __init__(
+        self,
+        _player_units: List[PlayerUnits],
+        next_unit_id: int,
+        **kwargs
+):
+    super().__init__(**kwargs)
+
+    self.units: List[List[Unit]] = [pu.units for pu in _player_units]
+    self.reference_id_generator: Generator[int] = create_id_generator(next_unit_id)
+
+
+
+ +
+ +
+ + +
+def add_unit(...) + +
+ + +
+ +

Adds a unit to the scenario.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
player + int | PlayerId + +
+

The player the unit belongs to.

+
+
+ required +
unit_const + int + +
+

Defines what unit you're placing. The IDs used in the unit/buildings dataset.

+
+
+ required +
x + float + +
+

The x location in the scenario.

+
+
+ 0 +
y + float + +
+

The y location in the scenario.

+
+
+ 0 +
z + float + +
+

The z (height) location in the scenario.

+
+
+ 0 +
rotation + float + +
+

The rotation of the unit.

+
+
+ 0 +
garrisoned_in_id + int + +
+

The reference_id of another unit this unit is garrisoned in.

+
+
+ -1 +
animation_frame + int + +
+

The animation frame of the unit.

+
+
+ 0 +
status + int + +
+

Unknown - Always 2. 0-6 no difference (?) | 7-255 makes it disappear. (Except from the mini-map)

+
+
+ 2 +
reference_id + int + +
+

The reference ID of this unit. Normally added automatically. Used for garrisoning or reference +in triggers

+
+
+ None +
caption_string_id + int + +
+

A string ID for the caption of a unit

+
+
+ -1 +
tile + Tile | Tuple[int, int] + +
+

An object that represents a tile on the map. Replaces parameters x and y. Also, automatically adds +.5 to both ints to place the unit centered on the tile.

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Unit + +
+

The Unit created

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
def add_unit(
+        self,
+        player: int | PlayerId,
+        unit_const: int,
+        x: float = 0,
+        y: float = 0,
+        z: float = 0,
+        rotation: float = 0,
+        garrisoned_in_id: int = -1,
+        animation_frame: int = 0,
+        status: int = 2,
+        reference_id: int = None,
+        caption_string_id: int = -1,
+        tile: Tile | Tuple[int, int] = None,
+) -> Unit:
+    """
+    Adds a unit to the scenario.
+
+    Args:
+        player: The player the unit belongs to.
+        unit_const: Defines what unit you're placing. The IDs used in the unit/buildings dataset.
+        x: The x location in the scenario.
+        y: The y location in the scenario.
+        z: The z (height) location in the scenario.
+        rotation: The rotation of the unit.
+        garrisoned_in_id: The reference_id of another unit this unit is garrisoned in.
+        animation_frame: The animation frame of the unit.
+        status: Unknown - Always 2. 0-6 no difference (?) | 7-255 makes it disappear. (Except from the mini-map)
+        reference_id: The reference ID of this unit. Normally added automatically. Used for garrisoning or reference
+            in triggers
+        caption_string_id: A string ID for the caption of a unit
+        tile: An object that represents a tile on the map. Replaces parameters x and y. Also, automatically adds
+            .5 to both ints to place the unit centered on the tile.
+
+    Returns:
+        The Unit created
+    """
+    if reference_id is None:
+        reference_id = self.get_new_reference_id()
+
+    unit = Unit(
+        player=player,
+        x=x if tile is None else (tile[0] + .5),
+        y=y if tile is None else (tile[1] + .5),
+        z=z,
+        reference_id=reference_id,
+        unit_const=unit_const,
+        status=status,
+        rotation=rotation,
+        initial_animation_frame=animation_frame,
+        garrisoned_in_id=garrisoned_in_id,
+        caption_string_id=caption_string_id,
+        uuid=self._uuid
+    )
+
+    self.units[player].append(unit)
+    return unit
+
+
+
+ +
+ +
+ + +
+def change_ownership(...) + + + staticmethod + + +
+ + +
+ +

Changes a unit's ownership to the given player.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit + Unit | List[Unit] + +
+

The unit object which ownership will be changed

+
+
+ required +
to_player + int | PlayerId + +
+

The player that'll get ownership over the unit (using PlayerId enum)

+
+
+ required +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
@staticmethod
+def change_ownership(unit: Unit | List[Unit], to_player: int | PlayerId) -> None:
+    """
+    Changes a unit's ownership to the given player.
+
+    Args:
+        unit: The unit object which ownership will be changed
+        to_player: The player that'll get ownership over the unit (using PlayerId enum)
+    """
+    if isinstance(unit, list):
+        for u in unit:
+            u.player = to_player
+    else:
+        unit.player = to_player
+
+
+
+ +
+ +
+ + +
+def clone_unit(...) + +
+ + +
+ +

Clones an existing unit with the adjusted variables. Everything except the initial unit is optional. +When arguments are provided, they will override the corresponding values in the cloned unit.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit + Unit + +
+

The unit to clone

+
+
+ required +
player + int | PlayerId + +
+

The player to set the cloned unit to (If not provided, the original player will be used)

+
+
+ None +
unit_const + int + +
+

The unit you're placing (If not provided, the original unit constant will be used)

+
+
+ None +
x + float + +
+

The X coordinate of the cloned unit (If not provided, the original x coordinate will be used)

+
+
+ None +
y + float + +
+

The Y coordinate of the cloned unit (If not provided, the original y coordinate will be used)

+
+
+ None +
z + float + +
+

The Z coordinate of the cloned unit (If not provided, the original z coordinate will be used)

+
+
+ None +
rotation + float + +
+

The rotation of the cloned unit (If not provided, the original rotation will be used)

+
+
+ None +
garrisoned_in_id + int + +
+

The id of the garrisoned unit (If not provided, the original garrisoned id will be used)

+
+
+ None +
animation_frame + int + +
+

The animation frame of the cloned unit (If not provided, the original animation frame will be used)

+
+
+ None +
status + int + +
+

The status of the cloned unit (If not provided, the original status will be used)

+
+
+ None +
reference_id + int + +
+

Reference id of the cloned unit (If not provided, a new reference id will be generated)

+
+
+ None +
tile + Tile | Tuple[int, int] + +
+

The tile of the cloned unit (If not provided, the original x,y coordinates will be used)

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Unit + +
+

The cloned unit

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
 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
def clone_unit(
+        self,
+        unit: Unit,
+        player: int | PlayerId = None,
+        unit_const: int = None,
+        x: float = None,
+        y: float = None,
+        z: float = None,
+        rotation: float = None,
+        garrisoned_in_id: int = None,
+        animation_frame: int = None,
+        status: int = None,
+        reference_id: int = None,
+        tile: Tile | Tuple[int, int] = None,
+) -> Unit:
+    """
+    Clones an existing unit with the adjusted variables. Everything except the initial unit is optional.
+    When arguments are provided, they will override the corresponding values in the cloned unit.
+
+    Args:
+        unit: The unit to clone
+        player: The player to set the cloned unit to (If not provided, the original player will be used)
+        unit_const: The unit you're placing (If not provided, the original unit constant will be used)
+        x: The X coordinate of the cloned unit (If not provided, the original x coordinate will be used)
+        y: The Y coordinate of the cloned unit (If not provided, the original y coordinate will be used)
+        z: The Z coordinate of the cloned unit (If not provided, the original z coordinate will be used)
+        rotation: The rotation of the cloned unit (If not provided, the original rotation will be used)
+        garrisoned_in_id: The id of the garrisoned unit (If not provided, the original garrisoned id will be used)
+        animation_frame: The animation frame of the cloned unit (If not provided, the original animation frame will be used)
+        status: The status of the cloned unit (If not provided, the original status will be used)
+        reference_id: Reference id of the cloned unit (If not provided, a new reference id will be generated)
+        tile: The tile of the cloned unit (If not provided, the original x,y coordinates will be used)
+
+    Returns:
+        The cloned unit
+    """
+
+    if (x is not None or y is not None) and tile is not None:
+        raise ValueError("Cannot use both x,y notation and tile notation at the same time")
+
+    return self.add_unit(
+        player=player or unit.player,
+        unit_const=unit_const or unit.unit_const,
+        x=x or unit.x,
+        y=y or unit.y,
+        z=z or unit.z,
+        rotation=rotation or unit.rotation,
+        garrisoned_in_id=garrisoned_in_id or unit.garrisoned_in_id,
+        animation_frame=animation_frame or unit.initial_animation_frame,
+        status=status or unit.status,
+        reference_id=reference_id,
+        tile=tile,
+    )
+
+
+
+ +
+ +
+ + +
+def filter_units_by(...) + +
+ + +
+ +

Filter units based on a given attribute of units

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
attr + str + +
+

The attribute to filter by

+
+
+ required +
unit_attrs + List[int] + +
+

The values for the attributes to filter with

+
+
+ required +
blacklist + bool + +
+

Use the given constant list as blacklist instead of whitelist

+
+
+ False +
player_list + List[Union[int, PlayerId]] + +
+

A list of players to filter from. If not used, all players are used.

+
+
+ None +
unit_list + List[Unit] + +
+

A set of units to filter from. If not used, all units are used.

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Unit] + +
+

A list of units

+
+
+ + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ AttributeError + +
+

If the provided attr does not exist on objects of the Unit class

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
def filter_units_by(
+        self,
+        attr: str,
+        unit_attrs: List[int],
+        blacklist: bool = False,
+        player_list: List[Union[int, PlayerId]] = None,
+        unit_list: List[Unit] = None
+) -> List[Unit]:
+    """
+    Filter units based on a given attribute of units
+
+    Args:
+        attr: The attribute to filter by
+        unit_attrs: The values for the attributes to filter with
+        blacklist: Use the given constant list as blacklist instead of whitelist
+        player_list: A list of players to filter from. If not used, all players are used.
+        unit_list: A set of units to filter from. If not used, all units are used.
+
+    Returns:
+        A list of units
+
+    Raises:
+        AttributeError: If the provided attr does not exist on objects of the Unit class
+    """
+
+    if unit_list is None:
+        unit_list = self.get_all_units()
+    if player_list is not None:
+        unit_list = [unit for unit in unit_list if unit.player in player_list]
+
+    if len(unit_list) == 0:
+        return []
+
+    unit = unit_list[0]
+    if not hasattr(unit, attr):
+        raise AttributeError(f"Cannot filter Unit objects by {attr}")
+
+    # Both return statements can be combined using: ((unit.unit_const in unit_consts) != blacklist)
+    # But splitting them helps performance (not checking against blacklist for each entry)
+    if not blacklist:
+        return [unit for unit_attr in unit_attrs for unit in unit_list if getattr(unit, attr) == unit_attr]
+    return [unit for unit_attr in unit_attrs for unit in unit_list if getattr(unit, attr) != unit_attr]
+
+
+
+ +
+ +
+ + +
+def filter_units_by_const(...) + +
+ + +
+ +

Filter unit on their unit_const value.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_consts + List[int] + +
+

The constants to filter with

+
+
+ required +
blacklist + bool + +
+

Use the given constant list as blacklist instead of whitelist

+
+
+ False +
player_list + List[Union[int, PlayerId]] + +
+

A list of players to filter from. If not used, all players are used.

+
+
+ None +
unit_list + List[Unit] + +
+

A set of units to filter from. If not used, all units are used.

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Unit] + +
+

A list of units

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
def filter_units_by_const(
+        self,
+        unit_consts: List[int],
+        blacklist: bool = False,
+        player_list: List[Union[int, PlayerId]] = None,
+        unit_list: List[Unit] = None
+) -> List[Unit]:
+    """
+    Filter unit on their unit_const value.
+
+    Args:
+        unit_consts: The constants to filter with
+        blacklist: Use the given constant list as blacklist instead of whitelist
+        player_list: A list of players to filter from. If not used, all players are used.
+        unit_list: A set of units to filter from. If not used, all units are used.
+
+    Returns:
+        A list of units
+    """
+    return self.filter_units_by("unit_const", unit_consts, blacklist, player_list, unit_list)
+
+
+
+ +
+ +
+ + +
+def filter_units_by_reference_id(...) + +
+ + +
+ +

Filter unit on their unit_const value.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
unit_reference_ids + List[int] + +
+

The reference_ids to filter with

+
+
+ required +
blacklist + bool + +
+

Use the given constant list as blacklist instead of whitelist

+
+
+ False +
player_list + List[int] + +
+

A list of players to filter from. If not used, all players are used.

+
+
+ None +
unit_list + List[Unit] + +
+

A set of units to filter from. If not used, all units are used.

+
+
+ None +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ List[Unit] + +
+

A list of units

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
def filter_units_by_reference_id(
+        self,
+        unit_reference_ids: List[int],
+        blacklist: bool = False,
+        player_list: List[Union[int, PlayerId]] = None,
+        unit_list: List[Unit] = None
+) -> List[Unit]:
+    """
+    Filter unit on their unit_const value.
+
+    Args:
+        unit_reference_ids (List[int]): The reference_ids to filter with
+        blacklist (bool): Use the given constant list as blacklist instead of whitelist
+        player_list (List[int]): A list of players to filter from. If not used, all players are used.
+        unit_list (List[Unit]): A set of units to filter from. If not used, all units are used.
+
+    Returns:
+        A list of units
+    """
+    return self.filter_units_by("reference_id", unit_reference_ids, blacklist, player_list, unit_list)
+
+
+
+ +
+ +
+ + +
+def find_highest_reference_id(...) + +
+ + +
+ +

Find the highest ID in the map. Searches through all units for the highest ID.

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

The highest ID in the map

+
+
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
def find_highest_reference_id(self) -> int:
+    """
+    Find the highest ID in the map. Searches through all units for the highest ID.
+
+    Returns:
+        The highest ID in the map
+    """
+    highest_id = 0  # If no units, default to 0
+    for player in PlayerId.all():
+        for unit in self.units[player]:
+            highest_id = max(highest_id, unit.reference_id)
+    return highest_id
+
+
+
+ +
+ +
+ + +
+def get_all_units(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
182
+183
+184
+185
+186
def get_all_units(self) -> List[Unit]:
+    units = []
+    for player_units in self.units:
+        units += player_units
+    return units
+
+
+
+ +
+ +
+ + +
+def get_new_reference_id(...) + +
+ + +
+ +

Get a new ID each time the function is called. Starting from the current highest ID.

+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

The newly generated ID

+
+
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
352
+353
+354
+355
+356
+357
+358
+359
def get_new_reference_id(self) -> int:
+    """
+    Get a new ID each time the function is called. Starting from the current highest ID.
+
+    Returns:
+        The newly generated ID
+    """
+    return next(self.reference_id_generator)
+
+
+
+ +
+ +
+ + +
+def get_player_units(...) + +
+ + +
+ +

Returns a list of UnitObjects for the given player.

+ + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If player is not between 0 (GAIA) and 8 (EIGHT)

+
+
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
player + int | PlayerId + - + required +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
171
+172
+173
+174
+175
+176
+177
+178
+179
+180
def get_player_units(self, player: int | PlayerId) -> List[Unit]:
+    """
+    Returns a list of UnitObjects for the given player.
+
+    Raises:
+        ValueError: If player is not between 0 (GAIA) and 8 (EIGHT)
+    """
+    if not 0 <= player <= 8:
+        raise ValueError("Player must have a value between 0 and 8")
+    return self.units[player]
+
+
+
+ +
+ +
+ + +
+def get_units_in_area(...) + +
+ + +
+ +

Returns all units in the square with left corner (x1, y1) and right corner (x2, y2). Both corners inclusive.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
x1 + float + +
+

The X location of the left corner

+
+
+ None +
y1 + float + +
+

The Y location of the left corner

+
+
+ None +
x2 + float + +
+

The X location of the right corner

+
+
+ None +
y2 + float + +
+

The Y location of the right corner

+
+
+ None +
tile1 + Tile + +
+

The x,y location of the 1st corner as Tile Object

+
+
+ None +
tile2 + Tile + +
+

The x,y location of the 2nd corner as Tile Object

+
+
+ None +
unit_list + List[Unit] + +
+

(Optional) A list of units (Defaults to all units in the map, including GAIA (Trees etc.)

+
+
+ None +
players + List[Union[int, PlayerId]] + +
+

(Optional) A list of Players which units need to be selected from the selected area

+
+
+ None +
ignore_players + List[PlayerId] + +
+

(Optional) A list of Players which units need to be ignored from the selected area

+
+
+ None +
+ + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if not all 4 (x1, y1, x2 and y2) are used simultaneously. +Or if both (tile1 and tile2) are not used simultaneously. +Or if any of the 4 (x1, y1, x2, y2) is used together with any of (tile1, tile2). Use one or the other. +Or if players and ignore_players are used simultaneously.

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
def get_units_in_area(
+        self,
+        x1: float = None,
+        y1: float = None,
+        x2: float = None,
+        y2: float = None,
+        tile1: Tile = None,
+        tile2: Tile = None,
+        unit_list: List[Unit] = None,
+        players: List[Union[int, PlayerId]] = None,
+        ignore_players: List[PlayerId] = None
+) -> List[Unit]:
+    """
+    Returns all units in the square with left corner (x1, y1) and right corner (x2, y2). Both corners inclusive.
+
+    Args:
+        x1: The X location of the left corner
+        y1: The Y location of the left corner
+        x2: The X location of the right corner
+        y2: The Y location of the right corner
+        tile1: The x,y location of the 1st corner as Tile Object
+        tile2: The x,y location of the 2nd corner as Tile Object
+        unit_list: (Optional) A list of units (Defaults to all units in the map, including GAIA (Trees etc.)
+        players: (Optional) A list of Players which units need to be selected from the selected area
+        ignore_players: (Optional) A list of Players which units need to be ignored from the selected area
+
+    Raises:
+        ValueError: if not all 4 (x1, y1, x2 and y2) are used simultaneously.
+            Or if both (tile1 and tile2) are not used simultaneously.
+            Or if any of the 4 (x1, y1, x2, y2) is used together with any of (tile1, tile2). Use one or the other.
+            Or if players and ignore_players are used simultaneously.
+    """
+    if (x1 is not None or y1 is not None or x2 is not None or y2 is not None) and any([tile1, tile2]):
+        raise ValueError("Cannot use both x1,y1,x2,y2 notation and tile1,tile2 notation at the same time")
+    if (x1 is not None or y1 is not None or x2 is not None or y2 is not None) and \
+            (x1 is None or y1 is None or x2 is None or y2 is None):
+        raise ValueError("Cannot use some but not all from x1,y1,x2,y2.")
+    if (not all([tile1, tile2])) and any([tile1, tile2]):
+        raise ValueError("Cannot use one from tile1, tile2. Use both.")
+    if players is not None and ignore_players is not None:
+        raise ValueError("Cannot use both whitelist (players) and blacklist (ignore_players) at the same time")
+
+    if tile1:
+        x1 = tile1.x
+        y1 = tile1.y
+        x2 = tile2.x
+        y2 = tile2.y
+    else:
+        # Inclusive selection
+        x2 += 1
+        y2 += 1
+
+    if players is not None:
+        players = players
+    elif ignore_players is not None:
+        players = [p for p in PlayerId if p not in ignore_players]
+    else:
+        players = [p for p in PlayerId]
+
+    if unit_list is None:
+        unit_list = self.get_all_units()
+
+    return [unit for unit in unit_list if x1 <= unit.x <= x2 and y1 <= unit.y <= y2 and unit.player in players]
+
+
+
+ +
+ +
+ + +
+def remove_eye_candy(...) + +
+ + +
+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
398
+399
+400
def remove_eye_candy(self) -> None:
+    eye_candy_ids = [1351, 1352, 1353, 1354, 1355, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, 1366]
+    self.units[0] = [gaia_unit for gaia_unit in self.units[0] if gaia_unit.unit_const not in eye_candy_ids]
+
+
+
+ +
+ +
+ + +
+def remove_unit(...) + +
+ + +
+ +

Removes a unit. Please note that unit=... is a lot faster than reference_id=... due to reference_id having +to search through all units on the map. And unit has an ownership (player) attribute which is used for knowing +which list to remove the unit from.

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
reference_id + int + +
+

The id of the unit. Note that this is NOT a unit constant (So NOT: UnitInfo.ARCHER)

+
+
+ None +
unit + Unit + +
+

The Unit object to be removed.

+
+
+ None +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
def remove_unit(self, reference_id: int = None, unit: Unit = None) -> None:
+    """
+    Removes a unit. Please note that `unit=...` is a lot faster than `reference_id=...` due to reference_id having
+    to search through all units on the map. And unit has an ownership (player) attribute which is used for knowing
+    which list to remove the unit from.
+
+    Args:
+        reference_id: The id of the unit. Note that this is NOT a unit constant (So NOT: UnitInfo.ARCHER)
+        unit: The Unit object to be removed.
+    """
+    if reference_id is not None and unit is not None:
+        raise ValueError("Cannot use both unit_ref_id and unit arguments. Use one or the other.")
+    if reference_id is None and unit is None:
+        raise ValueError("Both unit_ref_id and unit arguments were unused. Use one.")
+
+    if reference_id is not None:
+        for player in range(0, 9):
+            for i, unit in enumerate(self.units[player]):
+                if unit.reference_id == reference_id:
+                    del self.units[player][i]
+                    return
+    elif unit is not None:
+        self.units[unit.player].remove(unit)
+
+
+
+ +
+ +
+ + +
+def update_unit_player_values(...) + +
+ + +
+ +

Function to update all player values in all units. Useful when units are moved manually (in mass).

+ + + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
53
+54
+55
+56
+57
def update_unit_player_values(self):
+    """Function to update all player values in all units. Useful when units are moved manually (in mass)."""
+    for player in PlayerId.all():
+        for unit in self.units[player]:
+            unit._player = player
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+ +
+ + +

+def create_id_generator(...) + +

+ + +
+ +

Create generator for increasing value

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
start_id + int + +
+

The id to start returning

+
+
+ required +
+ + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ Generator[int] + +
+

A generator which will return a +1 ID value for each time called with next.

+
+
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/unit_manager.py +
416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
def create_id_generator(start_id: int) -> Generator[int]:
+    """
+    Create generator for increasing value
+
+    Args:
+        start_id: The id to start returning
+
+    Returns:
+        A generator which will return a +1 ID value for each time called with next.
+    """
+    while True:
+        yield start_id
+        start_id += 1
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/units/unit_manager_de/index.html b/api_docs/units/unit_manager_de/index.html new file mode 100644 index 00000000..67fd5f13 --- /dev/null +++ b/api_docs/units/unit_manager_de/index.html @@ -0,0 +1,2401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + UnitManagerDE - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

UnitManagerDE

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ UnitManagerDE + + +

+ + +
+

+ Bases: UnitManager

+ + +

Manager of all DE unit related features

+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/unit_manager_de.py +
 7
+ 8
+ 9
+10
+11
class UnitManagerDE(UnitManager):
+    """Manager of all DE unit related features"""
+
+    def __init__(self, _player_units: List[PlayerUnits], **kwargs):
+        super().__init__(_player_units, **kwargs)
+
+
+ + + +
+ + + + + + + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
_player_units + List[PlayerUnits] + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/unit_manager_de.py +
10
+11
def __init__(self, _player_units: List[PlayerUnits], **kwargs):
+    super().__init__(_player_units, **kwargs)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/api_docs/xs/xs_manager_de/index.html b/api_docs/xs/xs_manager_de/index.html new file mode 100644 index 00000000..db345cbb --- /dev/null +++ b/api_docs/xs/xs_manager_de/index.html @@ -0,0 +1,3022 @@ + + + + + + + + + + + + + + + + + + + + + + + + + XsManager - AoE2 Scenario Parser Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

XsManager

+ +
+ + + + +
+ + + +
+ + + + + + + +

Classes

+ +
+ + + +

+ XsManagerDE + + +

+ + +
+

+ Bases: AoE2Object

+ + +

Manager of everything XS related.

+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/xs_manager_de.py +
 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
class XsManagerDE(AoE2Object):
+    """Manager of everything XS related."""
+
+    _link_list = [
+        RetrieverObjectLink("script_name", "Map", "script_name", Support(since=1.40)),
+    ]
+
+    def __init__(self, script_name: str, **kwargs):
+        super().__init__(**kwargs)
+
+        self._script_name = script_name
+        """
+        Using script files (added through this attribute) will NOT work for spectators.
+        You can work around this issue by using: ```xs_manager.add_script(xs_file_path='path/to/script.xs')```
+        For more information check out: https://ksneijders.github.io/AoE2ScenarioParser/cheatsheets/xs/
+        """
+
+        # --- XS Script Call Trigger ---
+        self._initialized = False
+        self._xs_trigger: Optional[Trigger] = Trigger(
+            name="XS SCRIPT",
+            enabled=False,
+            description="Due to the lack of support for transferring XS files between systems in Age of Empires II:DE, "
+                        "this trigger adds the entire script to an effect script call. This will add the script to"
+                        "each system once the game starts in the default0.xs file. -- Created using AoE2ScenarioParser",
+        )
+
+    @property
+    def script_name(self):
+        """The XS script name to include in the scenario"""
+        return self._script_name
+
+    @script_name.setter
+    def script_name(self, value):
+        self._script_name = value
+
+    @property
+    def xs_trigger(self):
+        """The trigger holding the script call effect holding all the XS"""
+        if not self._initialized:
+            self.initialise_xs_trigger()
+        return self._xs_trigger
+
+    @xs_trigger.setter
+    def xs_trigger(self, value):
+        self._xs_trigger = value
+
+    def initialise_xs_trigger(self, insert_index: int = -1) -> None:
+        """
+        Creates the XS trigger on a desired location. If you don't care about the location, the `add_script()` function
+        adds the trigger when calling it the first time too.
+
+        If you want the trigger to be (almost) at the top of the list, and you're reading a scenario with barely any to
+        no triggers, it is recommended to call this somewhere at the start of the script.
+
+        Insert index is used to move this trigger to a desired index.
+        Keep in mind that moving triggers like this might take some time when you have a lot of triggers (thousands).
+
+        Args:
+            insert_index: The index where the xs trigger is added. Will be added at the end of the list if left empty
+        """
+        if self._initialized:
+            return
+
+        try:
+            self._xs_trigger.new_effect.script_call(message="")
+        except UnsupportedAttributeError:
+            raise UnsupportedVersionError(
+                f"The scenario version ({get_scenario_version(self._uuid)}) does not support XS. "
+                f"Save the scenario in the editor to update the scenario to allow for XS."
+            ) from None
+        self._initialized = True
+        actions.import_triggers(self._uuid, [self.xs_trigger], insert_index, deepcopy=False)
+
+    def _append_to_xs(self, title, string) -> None:
+        self.xs_trigger.effects[0].message += f"// {'-' * 25} {title} {'-' * 25}\n{string}\n\n"
+
+    def add_script(self, xs_file_path: str = "", xs_string: str = ""):
+        """
+        Add a script to the script call effect in the XS trigger
+
+        Args:
+            xs_file_path: Path to an XS file
+            xs_string: Raw XS
+        """
+        if xs_file_path:
+            path = Path(xs_file_path)
+            with path.open() as xs_file:
+                self._append_to_xs(path.name, xs_file.read())
+        if xs_string:
+            self._append_to_xs(f"XS string", xs_string)
+
+    def _debug_write_script_to_file(self, filename: str = "xs.txt"):
+        with open(filename, 'w') as file:
+            file.write(self.xs_trigger.effects[0].message)
+
+
+ + + +
+ + + + + + +

Attributes

+ + + +
+ + + + +
+script_name + + + property + writable + + +
+ +
+ + +
+ + + + + +
+ +

The XS script name to include in the scenario

+
+ +
+ +
+ + + + +
+xs_trigger + + + property + writable + + +
+ +
+ + +
+ + + + + +
+ +

The trigger holding the script call effect holding all the XS

+
+ +
+ + + +

Functions

+ +
+ + +
+def __init__(...) + +
+ + +
+ + + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
script_name + str + - + required +
kwargs + ? + - + {} +
+ +
+ Source code in AoE2ScenarioParser/objects/managers/de/xs_manager_de.py +
21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
def __init__(self, script_name: str, **kwargs):
+    super().__init__(**kwargs)
+
+    self._script_name = script_name
+    """
+    Using script files (added through this attribute) will NOT work for spectators.
+    You can work around this issue by using: ```xs_manager.add_script(xs_file_path='path/to/script.xs')```
+    For more information check out: https://ksneijders.github.io/AoE2ScenarioParser/cheatsheets/xs/
+    """
+
+    # --- XS Script Call Trigger ---
+    self._initialized = False
+    self._xs_trigger: Optional[Trigger] = Trigger(
+        name="XS SCRIPT",
+        enabled=False,
+        description="Due to the lack of support for transferring XS files between systems in Age of Empires II:DE, "
+                    "this trigger adds the entire script to an effect script call. This will add the script to"
+                    "each system once the game starts in the default0.xs file. -- Created using AoE2ScenarioParser",
+    )
+
+
+
+ +
+ +
+ + +
+def add_script(...) + +
+ + +
+ +

Add a script to the script call effect in the XS trigger

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
xs_file_path + str + +
+

Path to an XS file

+
+
+ '' +
xs_string + str + +
+

Raw XS

+
+
+ '' +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/de/xs_manager_de.py +
 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
def add_script(self, xs_file_path: str = "", xs_string: str = ""):
+    """
+    Add a script to the script call effect in the XS trigger
+
+    Args:
+        xs_file_path: Path to an XS file
+        xs_string: Raw XS
+    """
+    if xs_file_path:
+        path = Path(xs_file_path)
+        with path.open() as xs_file:
+            self._append_to_xs(path.name, xs_file.read())
+    if xs_string:
+        self._append_to_xs(f"XS string", xs_string)
+
+
+
+ +
+ +
+ + +
+def initialise_xs_trigger(...) + +
+ + +
+ +

Creates the XS trigger on a desired location. If you don't care about the location, the add_script() function +adds the trigger when calling it the first time too.

+

If you want the trigger to be (almost) at the top of the list, and you're reading a scenario with barely any to +no triggers, it is recommended to call this somewhere at the start of the script.

+

Insert index is used to move this trigger to a desired index. +Keep in mind that moving triggers like this might take some time when you have a lot of triggers (thousands).

+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
insert_index + int + +
+

The index where the xs trigger is added. Will be added at the end of the list if left empty

+
+
+ -1 +
+ + + + +
+ Source code in AoE2ScenarioParser/objects/managers/de/xs_manager_de.py +
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
def initialise_xs_trigger(self, insert_index: int = -1) -> None:
+    """
+    Creates the XS trigger on a desired location. If you don't care about the location, the `add_script()` function
+    adds the trigger when calling it the first time too.
+
+    If you want the trigger to be (almost) at the top of the list, and you're reading a scenario with barely any to
+    no triggers, it is recommended to call this somewhere at the start of the script.
+
+    Insert index is used to move this trigger to a desired index.
+    Keep in mind that moving triggers like this might take some time when you have a lot of triggers (thousands).
+
+    Args:
+        insert_index: The index where the xs trigger is added. Will be added at the end of the list if left empty
+    """
+    if self._initialized:
+        return
+
+    try:
+        self._xs_trigger.new_effect.script_call(message="")
+    except UnsupportedAttributeError:
+        raise UnsupportedVersionError(
+            f"The scenario version ({get_scenario_version(self._uuid)}) does not support XS. "
+            f"Save the scenario in the editor to update the scenario to allow for XS."
+        ) from None
+    self._initialized = True
+    actions.import_triggers(self._uuid, [self.xs_trigger], insert_index, deepcopy=False)
+
+
+
+ +
+ + + +
+ +
+ +
+

Functions

+

Modules

+ + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/_mkdocstrings.css b/assets/_mkdocstrings.css new file mode 100644 index 00000000..85449ec7 --- /dev/null +++ b/assets/_mkdocstrings.css @@ -0,0 +1,119 @@ + +/* Avoid breaking parameter names, etc. in table cells. */ +.doc-contents td code { + word-break: normal !important; +} + +/* No line break before first paragraph of descriptions. */ +.doc-md-description, +.doc-md-description>p:first-child { + display: inline; +} + +/* Max width for docstring sections tables. */ +.doc .md-typeset__table, +.doc .md-typeset__table table { + display: table !important; + width: 100%; +} + +.doc .md-typeset__table tr { + display: table-row; +} + +/* Defaults in Spacy table style. */ +.doc-param-default { + float: right; +} + +/* Backward-compatibility: docstring section titles in bold. */ +.doc-section-title { + font-weight: bold; +} + +/* Symbols in Navigation and ToC. */ +:root, +[data-md-color-scheme="default"] { + --doc-symbol-attribute-fg-color: #953800; + --doc-symbol-function-fg-color: #8250df; + --doc-symbol-method-fg-color: #8250df; + --doc-symbol-class-fg-color: #0550ae; + --doc-symbol-module-fg-color: #5cad0f; + + --doc-symbol-attribute-bg-color: #9538001a; + --doc-symbol-function-bg-color: #8250df1a; + --doc-symbol-method-bg-color: #8250df1a; + --doc-symbol-class-bg-color: #0550ae1a; + --doc-symbol-module-bg-color: #5cad0f1a; +} + +[data-md-color-scheme="slate"] { + --doc-symbol-attribute-fg-color: #ffa657; + --doc-symbol-function-fg-color: #d2a8ff; + --doc-symbol-method-fg-color: #d2a8ff; + --doc-symbol-class-fg-color: #79c0ff; + --doc-symbol-module-fg-color: #baff79; + + --doc-symbol-attribute-bg-color: #ffa6571a; + --doc-symbol-function-bg-color: #d2a8ff1a; + --doc-symbol-method-bg-color: #d2a8ff1a; + --doc-symbol-class-bg-color: #79c0ff1a; + --doc-symbol-module-bg-color: #baff791a; +} + +code.doc-symbol { + border-radius: .1rem; + font-size: .85em; + padding: 0 .3em; + font-weight: bold; +} + +code.doc-symbol-attribute { + color: var(--doc-symbol-attribute-fg-color); + background-color: var(--doc-symbol-attribute-bg-color); +} + +code.doc-symbol-attribute::after { + content: "attr"; +} + +code.doc-symbol-function { + color: var(--doc-symbol-function-fg-color); + background-color: var(--doc-symbol-function-bg-color); +} + +code.doc-symbol-function::after { + content: "func"; +} + +code.doc-symbol-method { + color: var(--doc-symbol-method-fg-color); + background-color: var(--doc-symbol-method-bg-color); +} + +code.doc-symbol-method::after { + content: "meth"; +} + +code.doc-symbol-class { + color: var(--doc-symbol-class-fg-color); + background-color: var(--doc-symbol-class-bg-color); +} + +code.doc-symbol-class::after { + content: "class"; +} + +code.doc-symbol-module { + color: var(--doc-symbol-module-fg-color); + background-color: var(--doc-symbol-module-bg-color); +} + +code.doc-symbol-module::after { + content: "mod"; +} + +.doc-signature .autorefs { + color: inherit; + border-bottom: 1px dotted currentcolor; +} diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.56dfad97.min.js b/assets/javascripts/bundle.56dfad97.min.js new file mode 100644 index 00000000..1df62cd7 --- /dev/null +++ b/assets/javascripts/bundle.56dfad97.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Fi=Object.create;var gr=Object.defineProperty;var Wi=Object.getOwnPropertyDescriptor;var Ui=Object.getOwnPropertyNames,Vt=Object.getOwnPropertySymbols,Di=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,io=Object.prototype.propertyIsEnumerable;var no=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,$=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&no(e,r,t[r]);if(Vt)for(var r of Vt(t))io.call(t,r)&&no(e,r,t[r]);return e};var ao=(e,t)=>{var r={};for(var o in e)yr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Vt)for(var o of Vt(e))t.indexOf(o)<0&&io.call(e,o)&&(r[o]=e[o]);return r};var xr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Vi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ui(t))!yr.call(e,n)&&n!==r&&gr(e,n,{get:()=>t[n],enumerable:!(o=Wi(t,n))||o.enumerable});return e};var Lt=(e,t,r)=>(r=e!=null?Fi(Di(e)):{},Vi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var so=(e,t,r)=>new Promise((o,n)=>{var i=p=>{try{s(r.next(p))}catch(c){n(c)}},a=p=>{try{s(r.throw(p))}catch(c){n(c)}},s=p=>p.done?o(p.value):Promise.resolve(p.value).then(i,a);s((r=r.apply(e,t)).next())});var po=xr((Er,co)=>{(function(e,t){typeof Er=="object"&&typeof co!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(Er,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function p(k){var ft=k.type,qe=k.tagName;return!!(qe==="INPUT"&&a[ft]&&!k.readOnly||qe==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function c(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(s(r.activeElement)&&c(r.activeElement),o=!0)}function u(k){o=!1}function d(k){s(k.target)&&(o||p(k.target))&&c(k.target)}function y(k){s(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function M(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",M,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",y,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var qr=xr((ly,Sn)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var ka=/["'&<>]/;Sn.exports=Ha;function Ha(e){var t=""+e,r=ka.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof It=="object"&&typeof Yr=="object"?Yr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof It=="object"?It.ClipboardJS=r():t.ClipboardJS=r()})(It,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return ji}});var a=i(279),s=i.n(a),p=i(370),c=i.n(p),l=i(817),f=i.n(l);function u(V){try{return document.execCommand(V)}catch(A){return!1}}var d=function(A){var L=f()(A);return u("cut"),L},y=d;function M(V){var A=document.documentElement.getAttribute("dir")==="rtl",L=document.createElement("textarea");L.style.fontSize="12pt",L.style.border="0",L.style.padding="0",L.style.margin="0",L.style.position="absolute",L.style[A?"right":"left"]="-9999px";var F=window.pageYOffset||document.documentElement.scrollTop;return L.style.top="".concat(F,"px"),L.setAttribute("readonly",""),L.value=V,L}var X=function(A,L){var F=M(A);L.container.appendChild(F);var D=f()(F);return u("copy"),F.remove(),D},te=function(A){var L=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},F="";return typeof A=="string"?F=X(A,L):A instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(A==null?void 0:A.type)?F=X(A.value,L):(F=f()(A),u("copy")),F},J=te;function k(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(L){return typeof L}:k=function(L){return L&&typeof Symbol=="function"&&L.constructor===Symbol&&L!==Symbol.prototype?"symbol":typeof L},k(V)}var ft=function(){var A=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},L=A.action,F=L===void 0?"copy":L,D=A.container,Y=A.target,$e=A.text;if(F!=="copy"&&F!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Y!==void 0)if(Y&&k(Y)==="object"&&Y.nodeType===1){if(F==="copy"&&Y.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(F==="cut"&&(Y.hasAttribute("readonly")||Y.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if($e)return J($e,{container:D});if(Y)return F==="cut"?y(Y):J(Y,{container:D})},qe=ft;function Fe(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Fe=function(L){return typeof L}:Fe=function(L){return L&&typeof Symbol=="function"&&L.constructor===Symbol&&L!==Symbol.prototype?"symbol":typeof L},Fe(V)}function Ai(V,A){if(!(V instanceof A))throw new TypeError("Cannot call a class as a function")}function oo(V,A){for(var L=0;L0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof D.action=="function"?D.action:this.defaultAction,this.target=typeof D.target=="function"?D.target:this.defaultTarget,this.text=typeof D.text=="function"?D.text:this.defaultText,this.container=Fe(D.container)==="object"?D.container:document.body}},{key:"listenClick",value:function(D){var Y=this;this.listener=c()(D,"click",function($e){return Y.onClick($e)})}},{key:"onClick",value:function(D){var Y=D.delegateTarget||D.currentTarget,$e=this.action(Y)||"copy",Dt=qe({action:$e,container:this.container,target:this.target(Y),text:this.text(Y)});this.emit(Dt?"success":"error",{action:$e,text:Dt,trigger:Y,clearSelection:function(){Y&&Y.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(D){return vr("action",D)}},{key:"defaultTarget",value:function(D){var Y=vr("target",D);if(Y)return document.querySelector(Y)}},{key:"defaultText",value:function(D){return vr("text",D)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(D){var Y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(D,Y)}},{key:"cut",value:function(D){return y(D)}},{key:"isSupported",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Y=typeof D=="string"?[D]:D,$e=!!document.queryCommandSupported;return Y.forEach(function(Dt){$e=$e&&!!document.queryCommandSupported(Dt)}),$e}}]),L}(s()),ji=Ii},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,p){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(p))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(l,f,u,d,y){var M=c.apply(this,arguments);return l.addEventListener(u,M,y),{destroy:function(){l.removeEventListener(u,M,y)}}}function p(l,f,u,d,y){return typeof l.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(M){return s(M,f,u,d,y)}))}function c(l,f,u,d){return function(y){y.delegateTarget=a(y.target,f),y.delegateTarget&&d.call(l,y)}}o.exports=p},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function p(u,d,y){if(!u&&!d&&!y)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(y))throw new TypeError("Third argument must be a Function");if(a.node(u))return c(u,d,y);if(a.nodeList(u))return l(u,d,y);if(a.string(u))return f(u,d,y);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(u,d,y){return u.addEventListener(d,y),{destroy:function(){u.removeEventListener(d,y)}}}function l(u,d,y){return Array.prototype.forEach.call(u,function(M){M.addEventListener(d,y)}),{destroy:function(){Array.prototype.forEach.call(u,function(M){M.removeEventListener(d,y)})}}}function f(u,d,y){return s(document.body,u,d,y)}o.exports=p},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var p=window.getSelection(),c=document.createRange();c.selectNodeContents(i),p.removeAllRanges(),p.addRange(c),a=p.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var p=this.e||(this.e={});return(p[i]||(p[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var p=this;function c(){p.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),p=0,c=s.length;for(p;p0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function N(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],a;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(s){a={error:s}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(a)throw a.error}}return i}function q(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||p(d,M)})},y&&(n[d]=y(n[d])))}function p(d,y){try{c(o[d](y))}catch(M){u(i[0][3],M)}}function c(d){d.value instanceof nt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){p("next",d)}function f(d){p("throw",d)}function u(d,y){d(y),i.shift(),i.length&&p(i[0][0],i[0][1])}}function fo(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof he=="function"?he(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(a){return new Promise(function(s,p){a=e[i](a),n(s,p,a.done,a.value)})}}function n(i,a,s,p){Promise.resolve(p).then(function(c){i({value:c,done:s})},a)}}function H(e){return typeof e=="function"}function ut(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var zt=ut(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Qe(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var We=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=he(a),p=s.next();!p.done;p=s.next()){var c=p.value;c.remove(this)}}catch(M){t={error:M}}finally{try{p&&!p.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var l=this.initialTeardown;if(H(l))try{l()}catch(M){i=M instanceof zt?M.errors:[M]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=he(f),d=u.next();!d.done;d=u.next()){var y=d.value;try{uo(y)}catch(M){i=i!=null?i:[],M instanceof zt?i=q(q([],N(i)),N(M.errors)):i.push(M)}}}catch(M){o={error:M}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new zt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)uo(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Qe(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Qe(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Tr=We.EMPTY;function qt(e){return e instanceof We||e&&"closed"in e&&H(e.remove)&&H(e.add)&&H(e.unsubscribe)}function uo(e){H(e)?e():e.unsubscribe()}var Pe={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var dt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,a=n.isStopped,s=n.observers;return i||a?Tr:(this.currentObservers=null,s.push(r),new We(function(){o.currentObservers=null,Qe(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new wo(r,o)},t}(j);var wo=function(e){oe(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Tr},t}(g);var _r=function(e){oe(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(g);var At={now:function(){return(At.delegate||Date).now()},delegate:void 0};var Ct=function(e){oe(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=At);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,p=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+p)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),p=0;p0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(gt);var Oo=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(yt);var kr=new Oo(So);var Mo=function(e){oe(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=vt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var a=r.actions;o!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==o&&(vt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(gt);var Lo=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(yt);var me=new Lo(Mo);var S=new j(function(e){return e.complete()});function Yt(e){return e&&H(e.schedule)}function Hr(e){return e[e.length-1]}function Xe(e){return H(Hr(e))?e.pop():void 0}function ke(e){return Yt(Hr(e))?e.pop():void 0}function Bt(e,t){return typeof Hr(e)=="number"?e.pop():t}var xt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Gt(e){return H(e==null?void 0:e.then)}function Jt(e){return H(e[bt])}function Xt(e){return Symbol.asyncIterator&&H(e==null?void 0:e[Symbol.asyncIterator])}function Zt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ji(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var er=Ji();function tr(e){return H(e==null?void 0:e[er])}function rr(e){return mo(this,arguments,function(){var r,o,n,i;return Nt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,nt(r.read())];case 3:return o=a.sent(),n=o.value,i=o.done,i?[4,nt(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,nt(n)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function or(e){return H(e==null?void 0:e.getReader)}function W(e){if(e instanceof j)return e;if(e!=null){if(Jt(e))return Xi(e);if(xt(e))return Zi(e);if(Gt(e))return ea(e);if(Xt(e))return _o(e);if(tr(e))return ta(e);if(or(e))return ra(e)}throw Zt(e)}function Xi(e){return new j(function(t){var r=e[bt]();if(H(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Zi(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?b(function(n,i){return e(n,i,o)}):le,Te(1),r?De(t):qo(function(){return new ir}))}}function jr(e){return e<=0?function(){return S}:E(function(t,r){var o=[];t.subscribe(T(r,function(n){o.push(n),e=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new g}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,p=s===void 0?!0:s;return function(c){var l,f,u,d=0,y=!1,M=!1,X=function(){f==null||f.unsubscribe(),f=void 0},te=function(){X(),l=u=void 0,y=M=!1},J=function(){var k=l;te(),k==null||k.unsubscribe()};return E(function(k,ft){d++,!M&&!y&&X();var qe=u=u!=null?u:r();ft.add(function(){d--,d===0&&!M&&!y&&(f=Wr(J,p))}),qe.subscribe(ft),!l&&d>0&&(l=new at({next:function(Fe){return qe.next(Fe)},error:function(Fe){M=!0,X(),f=Wr(te,n,Fe),qe.error(Fe)},complete:function(){y=!0,X(),f=Wr(te,a),qe.complete()}}),W(k).subscribe(l))})(c)}}function Wr(e,t){for(var r=[],o=2;oe.next(document)),e}function P(e,t=document){return Array.from(t.querySelectorAll(e))}function R(e,t=document){let r=fe(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function fe(e,t=document){return t.querySelector(e)||void 0}function Ie(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var xa=O(h(document.body,"focusin"),h(document.body,"focusout")).pipe(_e(1),Q(void 0),m(()=>Ie()||document.body),G(1));function et(e){return xa.pipe(m(t=>e.contains(t)),K())}function $t(e,t){return C(()=>O(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?Ht(r=>Me(+!r*t)):le,Q(e.matches(":hover"))))}function Go(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Go(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Go(o,n);return o}function sr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function Tt(e){let t=x("script",{src:e});return C(()=>(document.head.appendChild(t),O(h(t,"load"),h(t,"error").pipe(v(()=>$r(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),Te(1))))}var Jo=new g,Ea=C(()=>typeof ResizeObserver=="undefined"?Tt("https://unpkg.com/resize-observer-polyfill"):I(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>Jo.next(t)))),v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function ce(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ea.pipe(w(r=>r.observe(t)),v(r=>Jo.pipe(b(o=>o.target===t),_(()=>r.unobserve(t)))),m(()=>ce(e)),Q(ce(e)))}function St(e){return{width:e.scrollWidth,height:e.scrollHeight}}function cr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function Xo(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Ve(e){return{x:e.offsetLeft,y:e.offsetTop}}function Zo(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function en(e){return O(h(window,"load"),h(window,"resize")).pipe(Le(0,me),m(()=>Ve(e)),Q(Ve(e)))}function pr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ne(e){return O(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe(Le(0,me),m(()=>pr(e)),Q(pr(e)))}var tn=new g,wa=C(()=>I(new IntersectionObserver(e=>{for(let t of e)tn.next(t)},{threshold:0}))).pipe(v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function tt(e){return wa.pipe(w(t=>t.observe(e)),v(t=>tn.pipe(b(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function rn(e,t=16){return Ne(e).pipe(m(({y:r})=>{let o=ce(e),n=St(e);return r>=n.height-o.height-t}),K())}var lr={drawer:R("[data-md-toggle=drawer]"),search:R("[data-md-toggle=search]")};function on(e){return lr[e].checked}function Je(e,t){lr[e].checked!==t&&lr[e].click()}function ze(e){let t=lr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function Ta(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Sa(){return O(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function nn(){let e=h(window,"keydown").pipe(b(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:on("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),b(({mode:t,type:r})=>{if(t==="global"){let o=Ie();if(typeof o!="undefined")return!Ta(o,r)}return!0}),pe());return Sa().pipe(v(t=>t?S:e))}function ye(){return new URL(location.href)}function lt(e,t=!1){if(B("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function an(){return new g}function sn(){return location.hash.slice(1)}function cn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Oa(e){return O(h(window,"hashchange"),e).pipe(m(sn),Q(sn()),b(t=>t.length>0),G(1))}function pn(e){return Oa(e).pipe(m(t=>fe(`[id="${t}"]`)),b(t=>typeof t!="undefined"))}function Pt(e){let t=matchMedia(e);return ar(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function ln(){let e=matchMedia("print");return O(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function Nr(e,t){return e.pipe(v(r=>r?t():S))}function zr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let a=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+a*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function je(e,t){return zr(e,t).pipe(v(r=>r.text()),m(r=>JSON.parse(r)),G(1))}function mn(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),G(1))}function fn(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),G(1))}function un(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function dn(){return O(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(un),Q(un()))}function hn(){return{width:innerWidth,height:innerHeight}}function bn(){return h(window,"resize",{passive:!0}).pipe(m(hn),Q(hn()))}function vn(){return z([dn(),bn()]).pipe(m(([e,t])=>({offset:e,size:t})),G(1))}function mr(e,{viewport$:t,header$:r}){let o=t.pipe(ee("size")),n=z([o,r]).pipe(m(()=>Ve(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:a,size:s},{x:p,y:c}])=>({offset:{x:a.x-p,y:a.y-c+i},size:s})))}function Ma(e){return h(e,"message",t=>t.data)}function La(e){let t=new g;return t.subscribe(r=>e.postMessage(r)),t}function gn(e,t=new Worker(e)){let r=Ma(t),o=La(t),n=new g;n.subscribe(o);let i=o.pipe(Z(),ie(!0));return n.pipe(Z(),Re(r.pipe(U(i))),pe())}var _a=R("#__config"),Ot=JSON.parse(_a.textContent);Ot.base=`${new URL(Ot.base,ye())}`;function xe(){return Ot}function B(e){return Ot.features.includes(e)}function Ee(e,t){return typeof t!="undefined"?Ot.translations[e].replace("#",t.toString()):Ot.translations[e]}function Se(e,t=document){return R(`[data-md-component=${e}]`,t)}function ae(e,t=document){return P(`[data-md-component=${e}]`,t)}function Aa(e){let t=R(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>R(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function yn(e){if(!B("announce.dismiss")||!e.childElementCount)return S;if(!e.hidden){let t=R(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return C(()=>{let t=new g;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Aa(e).pipe(w(r=>t.next(r)),_(()=>t.complete()),m(r=>$({ref:e},r)))})}function Ca(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function xn(e,t){let r=new g;return r.subscribe(({hidden:o})=>{e.hidden=o}),Ca(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))}function Rt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function En(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function wn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Tn(e){return x("button",{class:"md-clipboard md-icon",title:Ee("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}var On=Lt(qr());function Qr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(p=>!e.terms[p]).reduce((p,c)=>[...p,x("del",null,(0,On.default)(c))," "],[]).slice(0,-1),i=xe(),a=new URL(e.location,i.base);B("search.highlight")&&a.searchParams.set("h",Object.entries(e.terms).filter(([,p])=>p).reduce((p,[c])=>`${p} ${c}`.trim(),""));let{tags:s}=xe();return x("a",{href:`${a}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(p=>{let c=s?p in s?`md-tag-icon md-tag--${s[p]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${c}`},p)}),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Ee("search.result.term.missing"),": ",...n)))}function Mn(e){let t=e[0].score,r=[...e],o=xe(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),a=r.findIndex(l=>l.scoreQr(l,1)),...p.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,p.length>0&&p.length===1?Ee("search.result.more.one"):Ee("search.result.more.other",p.length))),...p.map(l=>Qr(l,1)))]:[]];return x("li",{class:"md-search-result__item"},c)}function Ln(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?sr(r):r)))}function Kr(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function _n(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function $a(e){var o;let t=xe(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function An(e,t){var o;let r=xe();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Ee("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map($a)))}var Pa=0;function Ra(e){let t=z([et(e),$t(e)]).pipe(m(([o,n])=>o||n),K()),r=C(()=>Xo(e)).pipe(ne(Ne),pt(1),He(t),m(()=>Zo(e)));return t.pipe(Ae(o=>o),v(()=>z([t,r])),m(([o,n])=>({active:o,offset:n})),pe())}function Ia(e,t){let{content$:r,viewport$:o}=t,n=`__tooltip2_${Pa++}`;return C(()=>{let i=new g,a=new _r(!1);i.pipe(Z(),ie(!1)).subscribe(a);let s=a.pipe(Ht(c=>Me(+!c*250,kr)),K(),v(c=>c?r:S),w(c=>c.id=n),pe());z([i.pipe(m(({active:c})=>c)),s.pipe(v(c=>$t(c,250)),Q(!1))]).pipe(m(c=>c.some(l=>l))).subscribe(a);let p=a.pipe(b(c=>c),re(s,o),m(([c,l,{size:f}])=>{let u=e.getBoundingClientRect(),d=u.width/2;if(l.role==="tooltip")return{x:d,y:8+u.height};if(u.y>=f.height/2){let{height:y}=ce(l);return{x:d,y:-16-y}}else return{x:d,y:16+u.height}}));return z([s,i,p]).subscribe(([c,{offset:l},f])=>{c.style.setProperty("--md-tooltip-host-x",`${l.x}px`),c.style.setProperty("--md-tooltip-host-y",`${l.y}px`),c.style.setProperty("--md-tooltip-x",`${f.x}px`),c.style.setProperty("--md-tooltip-y",`${f.y}px`),c.classList.toggle("md-tooltip2--top",f.y<0),c.classList.toggle("md-tooltip2--bottom",f.y>=0)}),a.pipe(b(c=>c),re(s,(c,l)=>l),b(c=>c.role==="tooltip")).subscribe(c=>{let l=ce(R(":scope > *",c));c.style.setProperty("--md-tooltip-width",`${l.width}px`),c.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(K(),ve(me),re(s)).subscribe(([c,l])=>{l.classList.toggle("md-tooltip2--active",c)}),z([a.pipe(b(c=>c)),s]).subscribe(([c,l])=>{l.role==="dialog"?(e.setAttribute("aria-controls",n),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",n)}),a.pipe(b(c=>!c)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ra(e).pipe(w(c=>i.next(c)),_(()=>i.complete()),m(c=>$({ref:e},c)))})}function mt(e,{viewport$:t},r=document.body){return Ia(e,{content$:new j(o=>{let n=e.title,i=En(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t})}function ja(e,t){let r=C(()=>z([en(e),Ne(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:a,height:s}=ce(e);return{x:o-i.x+a/2,y:n-i.y+s/2}}));return et(e).pipe(v(o=>r.pipe(m(n=>({active:o,offset:n})),Te(+!o||1/0))))}function Cn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return C(()=>{let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({offset:s}){e.style.setProperty("--md-tooltip-x",`${s.x}px`),e.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),tt(e).pipe(U(a)).subscribe(s=>{e.toggleAttribute("data-md-visible",s)}),O(i.pipe(b(({active:s})=>s)),i.pipe(_e(250),b(({active:s})=>!s))).subscribe({next({active:s}){s?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Le(16,me)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?e.style.setProperty("--md-tooltip-0",`${-s}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(U(a),b(s=>!(s.metaKey||s.ctrlKey))).subscribe(s=>{s.stopPropagation(),s.preventDefault()}),h(n,"mousedown").pipe(U(a),re(i)).subscribe(([s,{active:p}])=>{var c;if(s.button!==0||s.metaKey||s.ctrlKey)s.preventDefault();else if(p){s.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(c=Ie())==null||c.blur()}}),r.pipe(U(a),b(s=>s===o),Ge(125)).subscribe(()=>e.focus()),ja(e,t).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function Fa(e){return e.tagName==="CODE"?P(".c, .c1, .cm",e):[e]}function Wa(e){let t=[];for(let r of Fa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let a;for(;a=/(\(\d+\))(!)?/.exec(i.textContent);){let[,s,p]=a;if(typeof p=="undefined"){let c=i.splitText(a.index);i=c.splitText(s.length),t.push(c)}else{i.textContent=s,t.push(i);break}}}}return t}function kn(e,t){t.append(...Array.from(e.childNodes))}function fr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,a=new Map;for(let s of Wa(t)){let[,p]=s.textContent.match(/\((\d+)\)/);fe(`:scope > li:nth-child(${p})`,e)&&(a.set(p,wn(p,i)),s.replaceWith(a.get(p)))}return a.size===0?S:C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=[];for(let[l,f]of a)c.push([R(".md-typeset",f),R(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(p)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of c)l?kn(f,u):kn(u,f)}),O(...[...a].map(([,l])=>Cn(l,t,{target$:r}))).pipe(_(()=>s.complete()),pe())})}function Hn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Hn(t)}}function $n(e,t){return C(()=>{let r=Hn(e);return typeof r!="undefined"?fr(r,e,t):S})}var Pn=Lt(Br());var Ua=0;function Rn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Rn(t)}}function Da(e){return ge(e).pipe(m(({width:t})=>({scrollable:St(e).width>t})),ee("scrollable"))}function In(e,t){let{matches:r}=matchMedia("(hover)"),o=C(()=>{let n=new g,i=n.pipe(jr(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let a=[];if(Pn.default.isSupported()&&(e.closest(".copy")||B("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${Ua++}`;let l=Tn(c.id);c.insertBefore(l,e),B("content.tooltips")&&a.push(mt(l,{viewport$}))}let s=e.closest(".highlight");if(s instanceof HTMLElement){let c=Rn(s);if(typeof c!="undefined"&&(s.classList.contains("annotate")||B("content.code.annotate"))){let l=fr(c,e,t);a.push(ge(s).pipe(U(i),m(({width:f,height:u})=>f&&u),K(),v(f=>f?l:S)))}}return P(":scope > span[id]",e).length&&e.classList.add("md-code__content"),Da(e).pipe(w(c=>n.next(c)),_(()=>n.complete()),m(c=>$({ref:e},c)),Re(...a))});return B("content.lazy")?tt(e).pipe(b(n=>n),Te(1),v(()=>o)):o}function Va(e,{target$:t,print$:r}){let o=!0;return O(t.pipe(m(n=>n.closest("details:not([open])")),b(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(b(n=>n||!o),w(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function jn(e,t){return C(()=>{let r=new g;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Va(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}var Fn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var Gr,za=0;function qa(){return typeof mermaid=="undefined"||mermaid instanceof Element?Tt("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):I(void 0)}function Wn(e){return e.classList.remove("mermaid"),Gr||(Gr=qa().pipe(w(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Fn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),G(1))),Gr.subscribe(()=>so(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${za++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),a=r.attachShadow({mode:"closed"});a.innerHTML=n,e.replaceWith(r),i==null||i(a)})),Gr.pipe(m(()=>({ref:e})))}var Un=x("table");function Dn(e){return e.replaceWith(Un),Un.replaceWith(_n(e)),I({ref:e})}function Qa(e){let t=e.find(r=>r.checked)||e[0];return O(...e.map(r=>h(r,"change").pipe(m(()=>R(`label[for="${r.id}"]`))))).pipe(Q(R(`label[for="${t.id}"]`)),m(r=>({active:r})))}function Vn(e,{viewport$:t,target$:r}){let o=R(".tabbed-labels",e),n=P(":scope > input",e),i=Kr("prev");e.append(i);let a=Kr("next");return e.append(a),C(()=>{let s=new g,p=s.pipe(Z(),ie(!0));z([s,ge(e),tt(e)]).pipe(U(p),Le(1,me)).subscribe({next([{active:c},l]){let f=Ve(c),{width:u}=ce(c);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=pr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ne(o),ge(o)]).pipe(U(p)).subscribe(([c,l])=>{let f=St(o);i.hidden=c.x<16,a.hidden=c.x>f.width-l.width-16}),O(h(i,"click").pipe(m(()=>-1)),h(a,"click").pipe(m(()=>1))).pipe(U(p)).subscribe(c=>{let{width:l}=ce(o);o.scrollBy({left:l*c,behavior:"smooth"})}),r.pipe(U(p),b(c=>n.includes(c))).subscribe(c=>c.click()),o.classList.add("tabbed-labels--linked");for(let c of n){let l=R(`label[for="${c.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(U(p),b(f=>!(f.metaKey||f.ctrlKey)),w(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return B("content.tabs.link")&&s.pipe(Ce(1),re(t)).subscribe(([{active:c},{offset:l}])=>{let f=c.innerText.trim();if(c.hasAttribute("data-md-switching"))c.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let y of P("[data-tabs]"))for(let M of P(":scope > input",y)){let X=R(`label[for="${M.id}"]`);if(X!==c&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),M.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),s.pipe(U(p)).subscribe(()=>{for(let c of P("audio, video",e))c.pause()}),Qa(n).pipe(w(c=>s.next(c)),_(()=>s.complete()),m(c=>$({ref:e},c)))}).pipe(Ke(se))}function Nn(e,{viewport$:t,target$:r,print$:o}){return O(...P(".annotate:not(.highlight)",e).map(n=>$n(n,{target$:r,print$:o})),...P("pre:not(.mermaid) > code",e).map(n=>In(n,{target$:r,print$:o})),...P("pre.mermaid",e).map(n=>Wn(n)),...P("table:not([class])",e).map(n=>Dn(n)),...P("details",e).map(n=>jn(n,{target$:r,print$:o})),...P("[data-tabs]",e).map(n=>Vn(n,{viewport$:t,target$:r})),...P("[title]",e).filter(()=>B("content.tooltips")).map(n=>mt(n,{viewport$:t})))}function Ka(e,{alert$:t}){return t.pipe(v(r=>O(I(!0),I(!1).pipe(Ge(2e3))).pipe(m(o=>({message:r,active:o})))))}function zn(e,t){let r=R(".md-typeset",e);return C(()=>{let o=new g;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ka(e,t).pipe(w(n=>o.next(n)),_(()=>o.complete()),m(n=>$({ref:e},n)))})}var Ya=0;function Ba(e,t){document.body.append(e);let{width:r}=ce(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=cr(t),n=typeof o!="undefined"?Ne(o):I({x:0,y:0}),i=O(et(t),$t(t)).pipe(K());return z([i,n]).pipe(m(([a,s])=>{let{x:p,y:c}=Ve(t),l=ce(t),f=t.closest("table");return f&&t.parentElement&&(p+=f.offsetLeft+t.parentElement.offsetLeft,c+=f.offsetTop+t.parentElement.offsetTop),{active:a,offset:{x:p-s.x+l.width/2-r/2,y:c-s.y+l.height+8}}}))}function qn(e){let t=e.title;if(!t.length)return S;let r=`__tooltip_${Ya++}`,o=Rt(r,"inline"),n=R(".md-typeset",o);return n.innerHTML=t,C(()=>{let i=new g;return i.subscribe({next({offset:a}){o.style.setProperty("--md-tooltip-x",`${a.x}px`),o.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),O(i.pipe(b(({active:a})=>a)),i.pipe(_e(250),b(({active:a})=>!a))).subscribe({next({active:a}){a?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Le(16,me)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?o.style.setProperty("--md-tooltip-0",`${-a}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ba(o,e).pipe(w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))}).pipe(Ke(se))}function Ga({viewport$:e}){if(!B("header.autohide"))return I(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Be(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),K()),o=ze("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),K(),v(n=>n?r:I(!1)),Q(!1))}function Qn(e,t){return C(()=>z([ge(e),Ga(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),K((r,o)=>r.height===o.height&&r.hidden===o.hidden),G(1))}function Kn(e,{header$:t,main$:r}){return C(()=>{let o=new g,n=o.pipe(Z(),ie(!0));o.pipe(ee("active"),He(t)).subscribe(([{active:a},{hidden:s}])=>{e.classList.toggle("md-header--shadow",a&&!s),e.hidden=s});let i=ue(P("[title]",e)).pipe(b(()=>B("content.tooltips")),ne(a=>qn(a)));return r.subscribe(o),t.pipe(U(n),m(a=>$({ref:e},a)),Re(i.pipe(U(n))))})}function Ja(e,{viewport$:t,header$:r}){return mr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=ce(e);return{active:o>=n}}),ee("active"))}function Yn(e,t){return C(()=>{let r=new g;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=fe(".md-content h1");return typeof o=="undefined"?S:Ja(o,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))})}function Bn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),K()),n=o.pipe(v(()=>ge(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ee("bottom"))));return z([o,n,t]).pipe(m(([i,{top:a,bottom:s},{offset:{y:p},size:{height:c}}])=>(c=Math.max(0,c-Math.max(0,a-p,i)-Math.max(0,c+p-s)),{offset:a-i,height:c,active:a-i<=p})),K((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function Xa(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return I(...e).pipe(ne(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),G(1))}function Gn(e){let t=P("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Pt("(prefers-color-scheme: light)");return C(()=>{let i=new g;return i.subscribe(a=>{if(document.body.setAttribute("data-md-color-switching",""),a.color.media==="(prefers-color-scheme)"){let s=matchMedia("(prefers-color-scheme: light)"),p=document.querySelector(s.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");a.color.scheme=p.getAttribute("data-md-color-scheme"),a.color.primary=p.getAttribute("data-md-color-primary"),a.color.accent=p.getAttribute("data-md-color-accent")}for(let[s,p]of Object.entries(a.color))document.body.setAttribute(`data-md-color-${s}`,p);for(let s=0;sa.key==="Enter"),re(i,(a,s)=>s)).subscribe(({index:a})=>{a=(a+1)%t.length,t[a].click(),t[a].focus()}),i.pipe(m(()=>{let a=Se("header"),s=window.getComputedStyle(a);return o.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(p=>(+p).toString(16).padStart(2,"0")).join("")})).subscribe(a=>r.content=`#${a}`),i.pipe(ve(se)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Xa(t).pipe(U(n.pipe(Ce(1))),ct(),w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))})}function Jn(e,{progress$:t}){return C(()=>{let r=new g;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(w(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Jr=Lt(Br());function Za(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Xn({alert$:e}){Jr.default.isSupported()&&new j(t=>{new Jr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||Za(R(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(w(t=>{t.trigger.focus()}),m(()=>Ee("clipboard.copied"))).subscribe(e)}function Zn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function es(e,t){let r=new Map;for(let o of P("url",e)){let n=R("loc",o),i=[Zn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let a of P("[rel=alternate]",o)){let s=a.getAttribute("href");s!=null&&i.push(Zn(new URL(s),t))}}return r}function ur(e){return fn(new URL("sitemap.xml",e)).pipe(m(t=>es(t,new URL(e))),de(()=>I(new Map)))}function ts(e,t){if(!(e.target instanceof Element))return S;let r=e.target.closest("a");if(r===null)return S;if(r.target||e.metaKey||e.ctrlKey)return S;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),I(new URL(r.href))):S}function ei(e){let t=new Map;for(let r of P(":scope > *",e.head))t.set(r.outerHTML,r);return t}function ti(e){for(let t of P("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return I(e)}function rs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...B("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=fe(o),i=fe(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=ei(document);for(let[o,n]of ei(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Se("container");return Ue(P("script",r)).pipe(v(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),S}),Z(),ie(document))}function ri({location$:e,viewport$:t,progress$:r}){let o=xe();if(location.protocol==="file:")return S;let n=ur(o.base);I(document).subscribe(ti);let i=h(document.body,"click").pipe(He(n),v(([p,c])=>ts(p,c)),pe()),a=h(window,"popstate").pipe(m(ye),pe());i.pipe(re(t)).subscribe(([p,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",p)}),O(i,a).subscribe(e);let s=e.pipe(ee("pathname"),v(p=>mn(p,{progress$:r}).pipe(de(()=>(lt(p,!0),S)))),v(ti),v(rs),pe());return O(s.pipe(re(e,(p,c)=>c)),s.pipe(v(()=>e),ee("pathname"),v(()=>e),ee("hash")),e.pipe(K((p,c)=>p.pathname===c.pathname&&p.hash===c.hash),v(()=>i),w(()=>history.back()))).subscribe(p=>{var c,l;history.state!==null||!p.hash?window.scrollTo(0,(l=(c=history.state)==null?void 0:c.y)!=null?l:0):(history.scrollRestoration="auto",cn(p.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(ee("offset"),_e(100)).subscribe(({offset:p})=>{history.replaceState(p,"")}),s}var oi=Lt(qr());function ni(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(0,oi.default)(a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function jt(e){return e.type===1}function dr(e){return e.type===3}function ii(e,t){let r=gn(e);return O(I(location.protocol!=="file:"),ze("search")).pipe(Ae(o=>o),v(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:B("search.suggest")}}})),r}function ai({document$:e}){let t=xe(),r=je(new URL("../versions.json",t.base)).pipe(de(()=>S)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:a,aliases:s})=>a===i||s.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),v(n=>h(document.body,"click").pipe(b(i=>!i.metaKey&&!i.ctrlKey),re(o),v(([i,a])=>{if(i.target instanceof Element){let s=i.target.closest("a");if(s&&!s.target&&n.has(s.href)){let p=s.href;return!i.target.closest(".md-version")&&n.get(p)===a?S:(i.preventDefault(),I(p))}}return S}),v(i=>ur(new URL(i)).pipe(m(a=>{let p=ye().href.replace(t.base,i);return a.has(p.split("#")[0])?new URL(p):new URL(i)})))))).subscribe(n=>lt(n,!0)),z([r,o]).subscribe(([n,i])=>{R(".md-header__topic").appendChild(An(n,i))}),e.pipe(v(()=>o)).subscribe(n=>{var a;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let s=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(s)||(s=[s]);e:for(let p of s)for(let c of n.aliases.concat(n.version))if(new RegExp(p,"i").test(c)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let s of ae("outdated"))s.hidden=!1})}function is(e,{worker$:t}){let{searchParams:r}=ye();r.has("q")&&(Je("search",!0),e.value=r.get("q"),e.focus(),ze("search").pipe(Ae(i=>!i)).subscribe(()=>{let i=ye();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=et(e),n=O(t.pipe(Ae(jt)),h(e,"keyup"),o).pipe(m(()=>e.value),K());return z([n,o]).pipe(m(([i,a])=>({value:i,focus:a})),G(1))}function si(e,{worker$:t}){let r=new g,o=r.pipe(Z(),ie(!0));z([t.pipe(Ae(jt)),r],(i,a)=>a).pipe(ee("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ee("focus")).subscribe(({focus:i})=>{i&&Je("search",i)}),h(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=R("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),is(e,{worker$:t}).pipe(w(i=>r.next(i)),_(()=>r.complete()),m(i=>$({ref:e},i)),G(1))}function ci(e,{worker$:t,query$:r}){let o=new g,n=rn(e.parentElement).pipe(b(Boolean)),i=e.parentElement,a=R(":scope > :first-child",e),s=R(":scope > :last-child",e);ze("search").subscribe(l=>s.setAttribute("role",l?"list":"presentation")),o.pipe(re(r),Ur(t.pipe(Ae(jt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:a.textContent=f.length?Ee("search.result.none"):Ee("search.result.placeholder");break;case 1:a.textContent=Ee("search.result.one");break;default:let u=sr(l.length);a.textContent=Ee("search.result.other",u)}});let p=o.pipe(w(()=>s.innerHTML=""),v(({items:l})=>O(I(...l.slice(0,10)),I(...l.slice(10)).pipe(Be(4),Vr(n),v(([f])=>f)))),m(Mn),pe());return p.subscribe(l=>s.appendChild(l)),p.pipe(ne(l=>{let f=fe("details",l);return typeof f=="undefined"?S:h(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(b(dr),m(({data:l})=>l)).pipe(w(l=>o.next(l)),_(()=>o.complete()),m(l=>$({ref:e},l)))}function as(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ye();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function pi(e,t){let r=new g,o=r.pipe(Z(),ie(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),as(e,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))}function li(e,{worker$:t,keyboard$:r}){let o=new g,n=Se("search-query"),i=O(h(n,"keydown"),h(n,"focus")).pipe(ve(se),m(()=>n.value),K());return o.pipe(He(i),m(([{suggest:s},p])=>{let c=p.split(/([\s-]+)/);if(s!=null&&s.length&&c[c.length-1]){let l=s[s.length-1];l.startsWith(c[c.length-1])&&(c[c.length-1]=l)}else c.length=0;return c})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(b(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(b(dr),m(({data:s})=>s)).pipe(w(s=>o.next(s)),_(()=>o.complete()),m(()=>({ref:e})))}function mi(e,{index$:t,keyboard$:r}){let o=xe();try{let n=ii(o.search,t),i=Se("search-query",e),a=Se("search-result",e);h(e,"click").pipe(b(({target:p})=>p instanceof Element&&!!p.closest("a"))).subscribe(()=>Je("search",!1)),r.pipe(b(({mode:p})=>p==="search")).subscribe(p=>{let c=Ie();switch(p.type){case"Enter":if(c===i){let l=new Map;for(let f of P(":first-child [href]",a)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}p.claim()}break;case"Escape":case"Tab":Je("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof c=="undefined")i.focus();else{let l=[i,...P(":not(details) > [href], summary, details[open] [href]",a)],f=Math.max(0,(Math.max(0,l.indexOf(c))+l.length+(p.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}p.claim();break;default:i!==Ie()&&i.focus()}}),r.pipe(b(({mode:p})=>p==="global")).subscribe(p=>{switch(p.type){case"f":case"s":case"/":i.focus(),i.select(),p.claim();break}});let s=si(i,{worker$:n});return O(s,ci(a,{worker$:n,query$:s})).pipe(Re(...ae("search-share",e).map(p=>pi(p,{query$:s})),...ae("search-suggest",e).map(p=>li(p,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ye}}function fi(e,{index$:t,location$:r}){return z([t,r.pipe(Q(ye()),b(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>ni(o.config)(n.searchParams.get("h"))),m(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)!=null&&a.offsetHeight){let p=s.textContent,c=o(p);c.length>p.length&&n.set(s,c)}for(let[s,p]of n){let{childNodes:c}=x("span",null,p);s.replaceWith(...Array.from(c))}return{ref:e,nodes:n}}))}function ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:a},{offset:{y:s}}])=>(a=a+Math.min(n,Math.max(0,s-i))-n,{height:a,locked:s>=i+n})),K((i,a)=>i.height===a.height&&i.locked===a.locked))}function Xr(e,o){var n=o,{header$:t}=n,r=ao(n,["header$"]);let i=R(".md-sidebar__scrollwrap",e),{y:a}=Ve(i);return C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=s.pipe(Le(0,me));return c.pipe(re(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*a}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),c.pipe(Ae()).subscribe(()=>{for(let l of P(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2})}}}),ue(P("label[tabindex]",e)).pipe(ne(l=>h(l,"click").pipe(ve(se),m(()=>l),U(p)))).subscribe(l=>{let f=R(`[id="${l.htmlFor}"]`);R(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),ss(e,r).pipe(w(l=>s.next(l)),_(()=>s.complete()),m(l=>$({ref:e},l)))})}function ui(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return st(je(`${r}/releases/latest`).pipe(de(()=>S),m(o=>({version:o.tag_name})),De({})),je(r).pipe(de(()=>S),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return je(r).pipe(m(o=>({repositories:o.public_repos})),De({}))}}function di(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return st(je(`${r}/releases/permalink/latest`).pipe(de(()=>S),m(({tag_name:o})=>({version:o})),De({})),je(r).pipe(de(()=>S),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}function hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return ui(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return di(r,o)}return S}var cs;function ps(e){return cs||(cs=C(()=>{let t=__md_get("__source",sessionStorage);if(t)return I(t);if(ae("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return S}return hi(e.href).pipe(w(o=>__md_set("__source",o,sessionStorage)))}).pipe(de(()=>S),b(t=>Object.keys(t).length>0),m(t=>({facts:t})),G(1)))}function bi(e){let t=R(":scope > :last-child",e);return C(()=>{let r=new g;return r.subscribe(({facts:o})=>{t.appendChild(Ln(o)),t.classList.add("md-source__repository--active")}),ps(e).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function ls(e,{viewport$:t,header$:r}){return ge(document.body).pipe(v(()=>mr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ee("hidden"))}function vi(e,t){return C(()=>{let r=new g;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(B("navigation.tabs.sticky")?I({hidden:!1}):ls(e,t)).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function ms(e,{viewport$:t,header$:r}){let o=new Map,n=P(".md-nav__link",e);for(let s of n){let p=decodeURIComponent(s.hash.substring(1)),c=fe(`[id="${p}"]`);typeof c!="undefined"&&o.set(s,c)}let i=r.pipe(ee("height"),m(({height:s})=>{let p=Se("main"),c=R(":scope > :first-child",p);return s+.8*(c.offsetTop-p.offsetTop)}),pe());return ge(document.body).pipe(ee("height"),v(s=>C(()=>{let p=[];return I([...o].reduce((c,[l,f])=>{for(;p.length&&o.get(p[p.length-1]).tagName>=f.tagName;)p.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return c.set([...p=[...p,l]].reverse(),u)},new Map))}).pipe(m(p=>new Map([...p].sort(([,c],[,l])=>c-l))),He(i),v(([p,c])=>t.pipe(Fr(([l,f],{offset:{y:u},size:d})=>{let y=u+d.height>=Math.floor(s.height);for(;f.length;){let[,M]=f[0];if(M-c=u&&!y)f=[l.pop(),...f];else break}return[l,f]},[[],[...p]]),K((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([s,p])=>({prev:s.map(([c])=>c),next:p.map(([c])=>c)})),Q({prev:[],next:[]}),Be(2,1),m(([s,p])=>s.prev.length{let i=new g,a=i.pipe(Z(),ie(!0));if(i.subscribe(({prev:s,next:p})=>{for(let[c]of p)c.classList.remove("md-nav__link--passed"),c.classList.remove("md-nav__link--active");for(let[c,[l]]of s.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",c===s.length-1)}),B("toc.follow")){let s=O(t.pipe(_e(1),m(()=>{})),t.pipe(_e(250),m(()=>"smooth")));i.pipe(b(({prev:p})=>p.length>0),He(o.pipe(ve(se))),re(s)).subscribe(([[{prev:p}],c])=>{let[l]=p[p.length-1];if(l.offsetHeight){let f=cr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2,behavior:c})}}})}return B("navigation.tracking")&&t.pipe(U(a),ee("offset"),_e(250),Ce(1),U(n.pipe(Ce(1))),ct({delay:250}),re(i)).subscribe(([,{prev:s}])=>{let p=ye(),c=s[s.length-1];if(c&&c.length){let[l]=c,{hash:f}=new URL(l.href);p.hash!==f&&(p.hash=f,history.replaceState({},"",`${p}`))}else p.hash="",history.replaceState({},"",`${p}`)}),ms(e,{viewport$:t,header$:r}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function fs(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:a}})=>a),Be(2,1),m(([a,s])=>a>s&&s>0),K()),i=r.pipe(m(({active:a})=>a));return z([i,n]).pipe(m(([a,s])=>!(a&&s)),K(),U(o.pipe(Ce(1))),ie(!0),ct({delay:250}),m(a=>({hidden:a})))}function yi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({hidden:s}){e.hidden=s,s?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(a),ee("height")).subscribe(({height:s})=>{e.style.top=`${s+16}px`}),h(e,"click").subscribe(s=>{s.preventDefault(),window.scrollTo({top:0})}),fs(e,{viewport$:t,main$:o,target$:n}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))}function xi({document$:e,viewport$:t}){e.pipe(v(()=>P(".md-ellipsis")),ne(r=>tt(r).pipe(U(e.pipe(Ce(1))),b(o=>o),m(()=>r),Te(1))),b(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,B("content.tooltips")?mt(n,{viewport$:t}).pipe(U(e.pipe(Ce(1))),_(()=>n.removeAttribute("title"))):S})).subscribe(),B("content.tooltips")&&e.pipe(v(()=>P(".md-status")),ne(r=>mt(r,{viewport$:t}))).subscribe()}function Ei({document$:e,tablet$:t}){e.pipe(v(()=>P(".md-toggle--indeterminate")),w(r=>{r.indeterminate=!0,r.checked=!1}),ne(r=>h(r,"change").pipe(Dr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),re(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function us(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function wi({document$:e}){e.pipe(v(()=>P("[data-md-scrollfix]")),w(t=>t.removeAttribute("data-md-scrollfix")),b(us),ne(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Ti({viewport$:e,tablet$:t}){z([ze("search"),t]).pipe(m(([r,o])=>r&&!o),v(r=>I(r).pipe(Ge(r?400:100))),re(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ds(){return location.protocol==="file:"?Tt(`${new URL("search/search_index.js",Zr.base)}`).pipe(m(()=>__index),G(1)):je(new URL("search/search_index.json",Zr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ot=Bo(),Wt=an(),Mt=pn(Wt),eo=nn(),Oe=vn(),hr=Pt("(min-width: 960px)"),Oi=Pt("(min-width: 1220px)"),Mi=ln(),Zr=xe(),Li=document.forms.namedItem("search")?ds():Ye,to=new g;Xn({alert$:to});var ro=new g;B("navigation.instant")&&ri({location$:Wt,viewport$:Oe,progress$:ro}).subscribe(ot);var Si;((Si=Zr.version)==null?void 0:Si.provider)==="mike"&&ai({document$:ot});O(Wt,Mt).pipe(Ge(125)).subscribe(()=>{Je("drawer",!1),Je("search",!1)});eo.pipe(b(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=fe("link[rel=prev]");typeof t!="undefined"&<(t);break;case"n":case".":let r=fe("link[rel=next]");typeof r!="undefined"&<(r);break;case"Enter":let o=Ie();o instanceof HTMLLabelElement&&o.click()}});xi({viewport$:Oe,document$:ot});Ei({document$:ot,tablet$:hr});wi({document$:ot});Ti({viewport$:Oe,tablet$:hr});var rt=Qn(Se("header"),{viewport$:Oe}),Ft=ot.pipe(m(()=>Se("main")),v(e=>Bn(e,{viewport$:Oe,header$:rt})),G(1)),hs=O(...ae("consent").map(e=>xn(e,{target$:Mt})),...ae("dialog").map(e=>zn(e,{alert$:to})),...ae("header").map(e=>Kn(e,{viewport$:Oe,header$:rt,main$:Ft})),...ae("palette").map(e=>Gn(e)),...ae("progress").map(e=>Jn(e,{progress$:ro})),...ae("search").map(e=>mi(e,{index$:Li,keyboard$:eo})),...ae("source").map(e=>bi(e))),bs=C(()=>O(...ae("announce").map(e=>yn(e)),...ae("content").map(e=>Nn(e,{viewport$:Oe,target$:Mt,print$:Mi})),...ae("content").map(e=>B("search.highlight")?fi(e,{index$:Li,location$:Wt}):S),...ae("header-title").map(e=>Yn(e,{viewport$:Oe,header$:rt})),...ae("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Nr(Oi,()=>Xr(e,{viewport$:Oe,header$:rt,main$:Ft})):Nr(hr,()=>Xr(e,{viewport$:Oe,header$:rt,main$:Ft}))),...ae("tabs").map(e=>vi(e,{viewport$:Oe,header$:rt})),...ae("toc").map(e=>gi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Mt})),...ae("top").map(e=>yi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Mt})))),_i=ot.pipe(v(()=>bs),Re(hs),G(1));_i.subscribe();window.document$=ot;window.location$=Wt;window.target$=Mt;window.keyboard$=eo;window.viewport$=Oe;window.tablet$=hr;window.screen$=Oi;window.print$=Mi;window.alert$=to;window.progress$=ro;window.component$=_i;})(); +//# sourceMappingURL=bundle.56dfad97.min.js.map + diff --git a/assets/javascripts/bundle.56dfad97.min.js.map b/assets/javascripts/bundle.56dfad97.min.js.map new file mode 100644 index 00000000..eb83bdb3 --- /dev/null +++ b/assets/javascripts/bundle.56dfad97.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n *\n * @class BehaviorSubject\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an